Petri

A multi-representation genetic algorithm library for Elixir.

Supports three chromosome encodings: real (continuous), permutation (ordering), and binary. Each has its own crossover and mutation operators. Selection, termination, and the generational engine are shared across all three.

Usage

Three snippets from examples/.

Permutation: TSP on Berlin52

alias Petri.Chromosome.Permutation
fitness = fn %Permutation{genes: tour} ->
1.0 / tour_distance(tour)
end
result =
Petri.run(fitness, %{
encoding: :permutation,
n: 52,
population_size: 400,
max_generations: 1000,
seed: 67,
selection: :tournament,
tournament_size: 5,
elite_count: 8,
crossover: :ox,
mutation: :inversion
})
{best_tour, best_fitness} = result.best
best_distance = 1.0 / best_fitness

From examples/tsp.exs.

Real: ML hyperparameter tuning

alias Petri.Chromosome.Real
fitness = fn %Real{genes: [lr, lambda, epochs]} ->
epochs_int = max(1, round(epochs))
weights = train(train_x, train_y, lr, lambda, epochs_int)
r2(test_y, Nx.dot(test_x, weights))
end
result =
Petri.run(fitness, %{
encoding: :real,
bounds: [{1.0e-4, 1.0e-1}, {1.0e-6, 1.0e-1}, {10.0, 200.0}],
population_size: 50,
max_generations: 40,
seed: 42,
selection: :tournament,
tournament_size: 3,
elite_count: 3,
crossover: :blx_alpha,
blx_alpha_param: 0.5,
mutation: :gaussian,
gaussian_sigma: 0.15,
mutation_rate: 0.3
})
{best_chromosome, best_fitness} = result.best
[lr, lambda, epochs] = best_chromosome.genes

From examples/ml_hyperparams.exs.

Binary: feature subset selection

alias Petri.Chromosome.Binary
fitness = fn %Binary{genes: mask} ->
predictions =
Enum.map(data, fn row ->
row |> Enum.zip(mask)
|> Enum.reduce(0.0, fn {v, m}, acc -> if m == 1, do: acc + v, else: acc end)
end)
mse =
Enum.zip(predictions, targets)
|> Enum.reduce(0.0, fn {p, t}, acc -> acc + (p - t) ** 2 end)
|> Kernel./(@n_samples)
1.0 / (1.0 + mse)
end
result =
Petri.run(fitness, %{
encoding: :binary,
length: 20,
population_size: 80,
max_generations: 80,
seed: 17,
selection: :tournament,
tournament_size: 3,
elite_count: 3,
crossover: :uniform,
mutation: :bit_flip,
mutation_rate: 0.4,
mutation_per_gene_rate: 0.05
})
{best_chromosome, best_fitness} = result.best

From examples/feature_selection.exs.

Running the examples

Standalone .exs scripts that use Mix.install to pull in dependencies. Run from the repo root with elixir (not mix):

elixir examples/tsp.exs
elixir examples/ml_hyperparams.exs
elixir examples/feature_selection.exs
ExampleEncodingWhat it does
tsp.exspermutationOrder crossover + swap mutation on Berlin52
ml_hyperparams.exsrealBLX-α + Gaussian mutation tuning a linear regression
feature_selection.exsbinaryUniform crossover + bit-flip for feature subset selection

Installation

def deps do
[
{:petri, "~> 0.1.0"}
]
end