Examples¶
The app/ directory contains ready-to-run example programs.
Build and run any example with:
Quick start: Rosenbrock function¶
The Rosenbrock function is a classic test problem with a narrow curved valley leading to a global minimum at \((x, y) = (1, 1)\), where \(f = 0\):
This example demonstrates evolve_population with blend crossover and
Gaussian mutation, and shows how to retrieve the best individual from each
generation.
program rosenbrock_example
use evortran__util_kinds, only : wp
use evortran__individuals_float, only : individual
use evortran__evolutions_float, only : evolve_population
implicit none
type(individual) :: best_ind
type(individual), allocatable :: bests(:)
real(wp), parameter :: xmin = -2.0_wp, xmax = 2.0_wp
real(wp), parameter :: ymin = -1.0_wp, ymax = 3.0_wp
best_ind = evolve_population( &
300, 2, rosenbrock, &
mating='blend', &
elite_size=3, &
max_generations=1000, &
fitness_target=1.0e-7_wp, &
mutate='gaussian', &
mutate_prob=0.2_wp, &
mutate_gene_prob=0.4_wp, &
mutate_gaussian_sigma=0.5_wp, &
verbose=.true., &
fittest_inds_from_gen=bests)
write(*,*) 'Minimum at:'
write(*,*) ' x =', xmin + best_ind%genes(1) * (xmax - xmin)
write(*,*) ' y =', ymin + best_ind%genes(2) * (ymax - ymin)
write(*,*) ' f =', best_ind%get_fitness()
contains
pure subroutine rosenbrock(ind, f)
class(individual), intent(in) :: ind
real(wp), intent(out) :: f
real(wp) :: x, y
x = xmin + ind%genes(1) * (xmax - xmin)
y = ymin + ind%genes(2) * (ymax - ymin)
f = (1.0_wp - x)**2 + 100.0_wp * (y - x**2)**2
end subroutine
end program
Run the packaged version with:
Key points:
- Genes are in
[0, 1](default); the mapping to \((x, y)\) is done inside the fitness function. fittest_inds_from_gencollects the best individual after every generation, useful for convergence plots.verbose=.true.shows a live display of the current generation and fitness.
Multiple minima: Himmelblau function with migration¶
Himmelblau's function has four equal global minima at different locations in \([-5, 5]^2\):
A single population tends to converge to one minimum. Using
evolve_migration with multiple populations allows simultaneous discovery
of all minima.
program himmelblau_example
use evortran__util_kinds, only : wp
use evortran__individuals_float, only : individual
use evortran__migrations_float, only : evolve_migration
implicit none
type(individual) :: best_ind
type(individual), allocatable :: fittest_per_pop(:)
integer :: i
best_ind = evolve_migration( &
pop_number=20, &
epoches=1, &
pop_size=50, &
gene_length=2, &
fit_func=himmelblau, &
mating='sbx', &
elite_size=1, &
lower_lim=-5.0_wp, &
upper_lim=5.0_wp, &
max_generations=100, &
fittest_inds_final_pops=fittest_per_pop)
write(*,*) 'Global best: f =', best_ind%get_fitness()
write(*,*) 'At: x =', best_ind%genes(1), ' y =', best_ind%genes(2)
write(*,*) 'Best individual per population:'
do i = 1, 20
write(*,'(i3,3es14.5)') i, &
fittest_per_pop(i)%genes(1), fittest_per_pop(i)%genes(2), &
fittest_per_pop(i)%get_fitness()
end do
contains
pure subroutine himmelblau(ind, f)
class(individual), intent(in) :: ind
real(wp), intent(out) :: f
real(wp) :: x, y
x = ind%genes(1)
y = ind%genes(2)
f = (x**2 + y - 11.0_wp)**2 + (x + y**2 - 7.0_wp)**2
end subroutine
end program
Run the packaged version:
Key points:
lower_lim/upper_limare set, soind%genesdirectly holds \((x, y)\).- 20 populations of 50 individuals each run in parallel (OpenMP), then exchange their best individual after each epoch.
fittest_inds_final_popsreturns the best individual from each population at the end, giving multiple candidate minima.
High-dimensional problem: Rastrigin function¶
The Rastrigin function is highly multi-modal with many local minima:
with global minimum \(f = 0\) at \(\mathbf{x} = \mathbf{0}\).
program rastrigin_example
use evortran__util_kinds, only : wp
use evortran__individuals_float, only : individual
use evortran__evolutions_float, only : evolve_population
implicit none
type(individual) :: best_ind
integer, parameter :: ndim = 10
real(wp), parameter :: pi = 4.0_wp * atan(1.0_wp)
real(wp), parameter :: A = 10.0_wp
best_ind = evolve_population( &
10000, ndim, rastrigin, &
lower_lim=-5.12_wp, &
upper_lim=5.12_wp, &
mating='blend', &
selection='rank', &
selection_size=100, &
elite_size=100, &
fitness_target=1.0e-10_wp, &
mutate_prob=0.1_wp, &
mutate_gene_prob=0.1_wp)
write(*,*) 'Minimum: f =', best_ind%get_fitness()
write(*,*) 'At: x =', best_ind%genes
contains
pure subroutine rastrigin(ind, f)
class(individual), intent(in) :: ind
real(wp), intent(out) :: f
integer :: i
f = A * size(ind%genes)
do i = 1, size(ind%genes)
f = f + ind%genes(i)**2 - A * cos(2.0_wp * pi * ind%genes(i))
end do
end subroutine
end program
Run the packaged multi-dimensional scan:
Key points:
lower_lim/upper_limmap genes directly to \([-5.12, 5.12]\).- Large
elite_size(100 out of 10000) and rank selection help retain good solutions in the heavily multi-modal landscape. - The packaged
rastriginexample runs dimensions 2–20 and logs function call counts to a CSV file.