pyevortran — Python interface¶
pyevortran is a Python wrapper for evortran that provides a Python interface
to evolve_population() and evolve_migration(). It lets you define fitness
functions in pure Python (using NumPy) while the genetic algorithm itself runs
as compiled Fortran code.
Repository: gitlab.com/thomas.biekoetter/pyevortran
Installation¶
Prerequisites¶
- gfortran — Fortran compiler
- Python 3 with
pip - fpm ≥ 0.13.0 — the version on PyPI (
pip install fpm) is outdated; download the latest binary from the fpm releases page and place it in yourPATH - patchelf — used to embed runtime library paths into the shared libraries
so that
LD_LIBRARY_PATHdoes not need to be set manually:
Build and install¶
Clone the repository and run:
This builds the Fortran shared library with fpm, patches its RPATH with
patchelf, and installs the pyevortran Python package into your environment
with pip.
Verify the installation¶
Interface¶
Fitness function¶
The fitness function receives a NumPy array x of length ndim and must return
a scalar float. pyevortran minimizes the function.
evolve_population¶
Returns a NumPy array of length ndim with the best parameters found.
Required arguments¶
| Argument | Description |
|---|---|
func |
Fitness function f(x) -> float. |
ndim |
Number of parameters (dimensions). |
Optional arguments¶
| Argument | Default | Description |
|---|---|---|
pop_size |
100 |
Population size. |
lower_lim |
0.0 |
Lower bound for all parameters. |
upper_lim |
1.0 |
Upper bound for all parameters. |
max_generations |
100 |
Maximum number of generations. |
fitness_target |
-1e99 |
Stop early when fitness falls below this value. |
x0 |
None |
Starting point as a list or array of length ndim. Must lie within [lower_lim, upper_lim]. |
verbose |
False |
Print progress to the terminal. |
selection |
'tournament' |
Selection method: 'tournament', 'rank', 'roulette'. |
selection_size |
100 |
Number of individuals selected as parents. |
tourn_size |
2 |
Tournament size (for selection='tournament'). |
wheele_size |
3 |
Wheel size (for selection='roulette'). |
elite_size |
1 |
Number of elite individuals carried over each generation. |
mating |
'blend' |
Crossover operator: 'one-point', 'two-point', 'uniform', 'blend', 'sbx'. |
mating_prob |
0.95 |
Probability that two parents mate. |
mutate |
'uniform' |
Mutation operator: 'uniform', 'gaussian', 'shuffle', 'none'. |
mutate_prob |
0.1 |
Probability that an individual is mutated. |
mutate_gene_prob |
0.1 |
Probability that each gene of a selected individual is mutated. |
evolve_migration¶
Returns a NumPy array of length ndim with the best parameters found across
all populations.
Required arguments¶
| Argument | Description |
|---|---|
func |
Fitness function f(x) -> float. |
ndim |
Number of parameters (dimensions). |
Optional arguments¶
All optional arguments from evolve_population are supported, plus:
| Argument | Default | Description |
|---|---|---|
pop_number |
4 |
Number of independent populations. |
epoches |
10 |
Number of migration epochs. |
pop_size |
100 |
Population size. |
lower_lim |
0.0 |
Lower bound for all parameters. |
upper_lim |
1.0 |
Upper bound for all parameters. |
max_generations |
100 |
Maximum number of generations. |
fitness_target |
-1e99 |
Stop early when fitness falls below this value. |
x0 |
None |
Starting point as a list or array of length ndim. Must lie within [lower_lim, upper_lim]. |
verbose |
False |
Print progress to the terminal. |
migration_size |
1 |
Number of individuals migrating between populations per epoch. |
migration_order |
'random' |
Migration order: 'random', 'LR', 'RL'. |
selection |
'tournament' |
Selection method: 'tournament', 'rank', 'roulette'. |
selection_size |
100 |
Number of individuals selected as parents. |
tourn_size |
2 |
Tournament size (for selection='tournament'). |
wheele_size |
3 |
Wheel size (for selection='roulette'). |
elite_size |
1 |
Number of elite individuals carried over each generation. |
mating |
'blend' |
Crossover operator: 'one-point', 'two-point', 'uniform', 'blend', 'sbx'. |
mating_prob |
0.95 |
Probability that two parents mate. |
mutate |
'uniform' |
Mutation operator: 'uniform', 'gaussian', 'shuffle', 'none'. |
mutate_prob |
0.1 |
Probability that an individual is mutated. |
mutate_gene_prob |
0.1 |
Probability that each gene of a selected individual is mutated. |
Examples¶
Minimizing a simple function¶
import numpy as np
from pyevortran.evolutions import evolve_population
a = np.array([0.2, -3.4, 0.3, 0.8])
def my_func(x):
return np.sum((x - a)**2)
xmin = evolve_population(
my_func, 4,
pop_size=100,
lower_lim=-100, upper_lim=100,
max_generations=100,
fitness_target=1e-9,
selection='roulette', selection_size=50,
elite_size=1,
mating='blend', mating_prob=0.9,
mutate='uniform', mutate_prob=0.1, mutate_gene_prob=0.2)
print("Minimum at xmin =", xmin)
print(" f(xmin) =", my_func(xmin))
Rastrigin function¶
import numpy as np
from pyevortran.evolutions import evolve_population
A = 10
def rastrigin(x):
return A * len(x) + np.sum(x**2 - A * np.cos(2 * np.pi * x))
xmin = evolve_population(
rastrigin, 5,
pop_size=1000,
lower_lim=-5.12, upper_lim=5.12,
max_generations=1000,
fitness_target=1e-9,
selection='rank', selection_size=100,
elite_size=100)
print("Minimum at xmin =", xmin)
print(" f(xmin) =", rastrigin(xmin))
Using evolve_migration for a high-dimensional problem¶
from pyevortran.migrations import evolve_migration
a = [-2.2, 9.4, 88.3, -66.8, -500.5, 800.0]
def my_func(x):
return (x[0]-a[0])**2 + (x[1]-a[1])**4 + (x[2]-a[2])**6 + \
(x[3]-a[3])**2 + (x[4]-a[4])**6 + (x[5]-a[5])**8
xmin = evolve_migration(
my_func, 6,
pop_number=4, epoches=5, pop_size=100,
migration_size=2, migration_order='LR',
lower_lim=-10000, upper_lim=10000,
max_generations=100,
fitness_target=1e-9,
elite_size=10,
mating='blend', mating_prob=0.9,
mutate='shuffle', mutate_prob=0.1, mutate_gene_prob=0.5)
print("Minimum at xmin =", xmin)
print(" f(xmin) =", my_func(xmin))