Packages

A multi-representation genetic algorithm library.

Current section

Files

Jump to
petri README.md
Raw

README.md

<div align="center">
<img src="https://raw.githubusercontent.com/sylvesterroos/petri/master/assets/hero.svg" alt="Petri" width="600">
</div>
# Petri
[![Hex.pm](https://img.shields.io/hexpm/v/petri.svg)](https://hex.pm/packages/petri)
[![Docs](https://img.shields.io/badge/docs-hexdocs-purple.svg)](https://hexdocs.pm/petri)
[![License](https://img.shields.io/badge/license-LGPL--3.0--or--later-blue.svg)](LICENSE)
A genetic algorithm library for Elixir.
## What's a genetic algorithm?
A genetic algorithm is a form of evolutionary optimization for problems with too many combinations to brute force. For example: the shortest route through 52 cities, the best hyperparameters for a model, or the most useful features in a dataset. A dataset of 50 features has a quadrillion combinations. At a million checks per second you're waiting 36 years. A GA gets a good answer before you've dropped programming and taken up farming.
A GA works like natural selection. Generate a population of candidate solutions, score them with a fitness function you write, pick the best, cross them to create new candidates, mutate a few to explore. Repeat for a few hundred generations. The result is rarely the mathematical optimum, but it gets there while brute force is just getting started.
Petri handles the selection, crossover, mutation, and generational loop. You pick a chromosome encoding that fits your problem, write a fitness function, and run it.
## Quick start
Below is the traveling salesman problem on Berlin52: 52 cities, find the shortest tour that visits each once.
```elixir
alias Petri.Chromosome.Permutation
# Fitness: shorter tours score higher (a GA maximizes, so invert distance)
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
```
> See [`examples/tsp.exs`](examples/tsp.exs) for the full runnable script with city coordinates and distance calculation.
## Capabilities
Four chromosome encodings, each with operators tuned for that representation.
| Encoding | Shape |
|---|---|
| `:real` | `[0.001, 0.5, 120.0]` |
| `:integer` | `[3, 17, 255]` |
| `:permutation` | `[4, 0, 7, 2, 5, 1, 3, 6]` |
| `:binary` | `[1, 0, 1, 1, 0]` |
Config validation catches operator/encoding mismatches up front so you won't get surprises mid-run.
Engine features:
- Parallel evaluation with per-task timeout
- Constraint handling: soft penalty functions or death-penalty retries on invalid offspring
- Adaptive crossover/mutation rates with built-in decay strategies
- Generational or steady-state replacement
- Termination by generation count, fitness threshold, stagnation, time budget, or evaluation budget
## Running the examples
Standalone scripts that pull in Petri via `Mix.install`. Run from the repo root with `elixir` (not `mix`):
```
elixir examples/tsp.exs
elixir examples/ml_hyperparams.exs
elixir examples/feature_selection.exs
elixir examples/ring_inscription.exs
```
| Example | Encoding | What it does |
|---|---|---|
| `tsp.exs` | permutation | Find the shortest tour through the 52 cities of Berlin52 |
| `ml_hyperparams.exs` | real | Tune learning rate, regularization, and epochs for a regression model |
| `feature_selection.exs` | binary | Pick the 4 predictive features out of 20 before training |
| `ring_inscription.exs` | integer | Evolve an arbitrary string toward a target quote character by character |
## Documentation
Full API docs at [hexdocs.pm/petri](https://hexdocs.pm/petri).
## License
LGPL-3.0-or-later. See [LICENSE](LICENSE).