Release notes¶
What each release contained, what broke, and how to upgrade. The short version lives in the changelog.
0.3.0 (2026-10-05)¶
Removes everything deprecated in 0.2 and changes the operator and
Problem.generate contracts, so operators draw from the run's rng.
Breaking changes
Problem.generatetakes the run'srng.- GA operators take
rngand selection gets oriented scores; extra knobs are bound withfunctools.partial, there is no**operator_kwargs. - Removed the deprecated names listed below.
New in 0.3.0: artificial_bee_colony() and the gaussian_neighbor move.
Upgrading from 0.2¶
Problem generation:
# 0.2
problem = Problem(generate=lambda: rng.sample(range(5), 5), evaluate=f)
# 0.3
problem = Problem(generate=lambda rng: rng.sample(range(5), 5), evaluate=f)
Operators (rng is required and positional; selection receives scores
oriented so that lower is always better, whatever the direction):
# 0.2
def mutation(genome, rng=None, **kwargs): ...
def crossover(parent1, parent2, rng=None, **kwargs): ...
def selection(population, fitness_function, k=2, rng=None,
direction=MINIMIZE, **kwargs): ...
genetic_algorithm(problem, stop=stop, mutation=inter_mutation, q=3)
# 0.3
def mutation(genome, rng, ...): ...
def crossover(parent1, parent2, rng): ...
def selection(population, scores, rng, k): ... # the GA asks for k=2
genetic_algorithm(problem, stop=stop,
mutation=partial(inter_mutation, num_swaps=3))
Benchmark factories no longer take an rng/seed: tsp(cities, 0) becomes
tsp(cities), likewise knapsack(...) and continuous(...). Seed the
heuristic instead (rng=).
inter_mutation is now inter_mutation(genome, rng, num_swaps=2,
probability=0.75).
Removed names:
| 0.2 (deprecated) | 0.3 |
|---|---|
GeneticAlgorithm(...), .train() |
genetic_algorithm(problem, stop=..., ...) |
GeneticAlgorithmHistory, LoadHistoryException |
result.history (a list of dicts) |
genetic_algorithm.steps.multations |
genetic_algorithm.steps.mutations |
utils.distances.euclidian_distance |
utils.distances.euclidean_distance (or math.dist) |
inter_mutation(genome, q=...) |
inter_mutation(genome, rng, num_swaps=...) |
| neighborhoods imported from the simulated annealing module | pymetaheuristics.neighborhoods |
0.2.0 (2026-10-04)¶
A rewrite of the core around plain functions. Python 3.12+.
Breaking changes
Nothing from 0.1 was removed, but these changed behaviour:
- Python 3.12+ is required (0.1.x supported 3.7+).
- Operators no longer modify their inputs. Crossover, mutation and selection return new genomes. Code that relied on in-place changes must use the return value.
- Selection weights changed. 0.1 used
-fitnessas the weight, which only worked for negative fitness values. Weights are now scaled by the value range, so any objective scale works and the worst genome is still selectable. Runs differ from 0.1 even with the same seed. - The GA breeds the whole population every generation. In 0.1 most of each generation was refilled with random genomes, so results are much better, and different.
- The GA respects
Directionin selection and best tracking. In 0.1 it always minimized, and you negated profits yourself. - Exceptions changed base class.
CrossOverExceptionis now aValueErrorandLoadHistoryExceptionanException. In 0.1 both wereBaseException, so a plainexcept Exceptiondid not catch them and now does. euclidean_distanceismath.dist, so it raisesValueErrorinstead ofAssertionErroron a length mismatch.
Deprecated, removal planned for 0.3
Tracked in #50.
These still work but emit a DeprecationWarning:
- the
GeneticAlgorithmclass (now a wrapper overgenetic_algorithm()), genetic_algorithm.steps.multations(usemutations),utils.distances.euclidian_distance(useeuclidean_distance),inter_mutation(q=...)(usenum_swaps=).
New in 0.2.0:
Problem,DirectionandOptimizationResult, and the reference loopcore.runwith stopping criteria (max_iterations,max_evaluations,max_time,target_value,any_of).genetic_algorithm()andsimulated_annealing()as plain functions with pluggable operators (swap_neighbor,two_opt_neighbor,bit_flip_neighbor,geometric_cooling,linear_cooling).- A single
feasiblepredicate, withreject,repairandpenaltyhelpers. - Reproducible runs through
rng=. - The benchmark suite, the experiments, runnable extension examples and this documentation site.
- A release workflow that publishes to PyPI and attaches the sdist and wheel.
Upgrading from 0.1¶
| 0.1 | Now |
|---|---|
GeneticAlgorithm(fitness_function, genome_generator, constraints, direction=...) |
Problem(generate=..., evaluate=..., feasible=..., direction=...) |
ga.add_constraint(c) / constraints=[...] |
one feasible predicate: lambda s: all(c(s) for c in constraints) |
ga.train(epochs, pop_size, ...) returns (genome, fitness) |
genetic_algorithm(problem, stop=max_iterations(epochs), population_size=pop_size, ...) returns an OptimizationResult |
ga.history keyed by timestamp |
result.history, one dict per generation (see Results and history) |
verbose=True |
loop over result.history after the run |
genetic_algorithm.steps.multations |
genetic_algorithm.steps.mutations |
inter_mutation(genome, q=...) |
inter_mutation(genome, num_swaps=...) |
utils.distances.euclidian_distance |
utils.distances.euclidean_distance (or math.dist) |
Before and after:
# 0.1
ga = GeneticAlgorithm(fitness, generate, constraints=[fits])
genome, value = ga.train(epochs=15, pop_size=10, rng=42)
from pymetaheuristics.core import Problem, max_iterations
from pymetaheuristics.genetic_algorithm import genetic_algorithm
problem = Problem(generate=lambda rng: rng.sample(range(5), 5),
evaluate=lambda s: sum(abs(g - i) for i, g in enumerate(s)))
result = genetic_algorithm(problem, stop=max_iterations(15), rng=42,
population_size=10)
genome, value = result.best_solution, result.best_value
0.1.1 (2021-06-05)¶
A small feature release on the same GeneticAlgorithm class.
- Added
GeneticAlgorithm.load_history(history), to load a previously saved history. It validates the pattern and raisesLoadHistoryExceptionif keys such asargs,runs,bestorelapsedare missing. - Added the
GeneticAlgorithmHistorytype. - Changed each history entry to also hold
bestandelapsed. - Changed the supported Python to 3.7+ (was 3.8+), and added a PyPI badge and a "Requires" section to the README.
No breaking changes.
0.1.0 (2021-06-03)¶
The first release: a Genetic Algorithm, with no dependencies.
GeneticAlgorithm(fitness_function, genome_generator, constraints)withtrain(epochs, pop_size, selection, crossover, mutation, verbose, **kwargs)returning(genome, fitness).add_constraint()adds a constraint, andhistoryrecords runs by timestamp.- Step functions:
random_weighted_selection,single_point_crossover,pmx_single_point(for TSP) andinter_mutation. euclidian_distance.- The GA only minimized: to maximize, you returned the negated value.
- Integration tests on Knapsack and TSP, CI and delivery to PyPI.
- Python 3.8+.