Skip to content

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 your PATH
  • patchelf — used to embed runtime library paths into the shared libraries so that LD_LIBRARY_PATH does not need to be set manually:
    sudo apt install patchelf   # Debian/Ubuntu
    pip install patchelf        # alternative without sudo
    

Build and install

Clone the repository and run:

git clone https://gitlab.com/thomas.biekoetter/pyevortran
cd pyevortran
make pyevortran

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

cd python/test
python test_rastrigin.py

Interface

Fitness function

The fitness function receives a NumPy array x of length ndim and must return a scalar float. pyevortran minimizes the function.

import numpy as np

def my_func(x):
    return np.sum((x - 1.0)**2)  # minimum at x = [1, 1, ..., 1]

evolve_population

from pyevortran.evolutions import evolve_population

xmin = evolve_population(func, ndim, ...)

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

from pyevortran.migrations import evolve_migration

xmin = evolve_migration(func, ndim, ...)

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))