pynetics package

Submodules

pynetics.algorithm module

Implementation of different Evolutionary Computation algorithms.

Currently only one implementation (Genetic Algorithm) is provided.

class pynetics.algorithm.GeneticAlgorithm(*, population_size: int, initializer: api.Initializer, stop_condition: api.StopCondition, fitness: api.Fitness, selection: api.Selection, replacement: api.Replacement, replacement_ratio: float, recombination: Optional[api.Recombination] = None, recombination_probability: float = None, mutation: Optional[api.Mutation] = None, mutation_probability: Optional[float] = None, callbacks: Sequence[callback.Callback] = None)

Bases: pynetics.api.EvolutiveAlgorithm

Modular implementation of a canonical genetic algorithm.

Each step the algorithm will generate a new offspring population from the previous one, and then invoke the replacement schema to combine the old and new genotypes in a new population ready to use in a new algorithm step.

The step consists in a loop where the selection, replacement and mutation operators take part. This loop will end when the offspring population is filled.

best() → pynetics.api.Genotype

The best genotype obtained so far.

Returns:The best genotype obtained at the moment of calling.
Raise:NotInitialized if the population hasn’t be created yet.
fitness

The fitness used by the genotypes during the algorithm

Returns:A recombination schema.
mutation

TODO TBD

mutation_probability

TODO TBD

offspring_size

The size of the offspring prior to perform the replacement.

It is dynamically computed based on the replacement ratio and the current population size.

Returns:The offspring size. It’ll be always a value greater than one, that is, the offspring will be composed at least of one genotype.
on_finalize()

It does nothing

on_initialize()

Subclass specific initialization method

recombination

The recombination schema used in this instance.

Returns:A recombination schema.
recombination_probability

The recombination probability for this algorithm instance.

Returns:A float with the recombination probability.
selection_size

The number of genotypes to select when selecting parents.

It is dynamically computed based on the recombination argument, so it should not be modified directly.

Returns:The number of genotypes needed to breed a new progeny.
step()

The particular implementation of this genetic algorithm.

pynetics.api module

Definition of the generic high level API for the library.

The rest of modules and packages will either inherit from these or use them ignoring their implementations.

class pynetics.api.EvolutiveAlgorithm(stop_condition: Callable[[pynetics.api.EvolutiveAlgorithm], bool], callbacks: Optional[Sequence[pynetics.callback.Callback]] = None)

Bases: object

Base class which defines how a genetic algorithm works.

More than one algorithm may exist so a base class is created to specify the required contract to be implemented by the other classes to work properly.

class EventType

Bases: enum.Enum

The event type fired.

It is used to differentiate which event should be called in the algorithm listeners subscribed to the class.

ALGORITHM_BEGIN = 'on_algorithm_begins'
ALGORITHM_END = 'on_algorithm_ends'
STEP_BEGIN = 'on_step_begins'
STEP_END = 'on_step_ends'
best() → pynetics.api.Genotype

Returns the best genotype in this current generation.

Returns:The best genotype.
callbacks
finalize()

Performs the finalization operations.

generation

The current generation in the algorithm.

initialize()

TODO: TBD…

on_finalize()

Subclass specific finalization method

on_initialize()

Subclass specific initialization method

run()

Runs the algorithm.

running()

TODO TBD…

step()

Called on every iteration of the algorithm.

stop()

TODO TBD…

class pynetics.api.Genotype

Bases: object

A possible codification of a solution to a problem.

fitness() → float

Returns the fitness of this particular genotype.

It relies on the parameter fitness_function, which is filled by the Population object that manages each population inside a genetic algorithm.

Returns:The fitness of this particular genotype
phenotype() → Any

The phenotype resulting from this genotype.

Returns:An object representing the phenotype of this genotype.
class pynetics.api.Initializer

Bases: object

Responsible for new genotypes generation.

This class is used for both initialization phase (just before the first genetic algorithm step) and genotype creation when needed.

create() → pynetics.api.Genotype

Creates a new genotype.

This method should be overwritten, as it depends completely on the problem codification.

Returns:A newly created genotype.
fill(population: pynetics.api.Population) → pynetics.api.Population

Fills the population with newly created genotypes.

The creation of those genotypes will be delegated to the create method.

The population passed as argument will be modified. The returned population is nothing but a reference to the same population for fluent API purposes.

Parameters:population – The population to be filled. Cannot be None.
Returns:The same population, but with the new genotypes in it.
class pynetics.api.Population(size: int, fitness: Callable[[Any], float])

Bases: collections.abc.MutableSequence, typing.Generic

Manages the pool of genotypes that are solutions of a G.A.

It’s a subclass of list, but with some additions.

append(genotype: pynetics.api.Genotype) → None

Add a new genotype to this population.

The implementation is delegated to the super class (list) but the population is marked as not sorted and it raises an error in case the maximum size (specified at initialization time) is reached.

Notice that, after calling this method, the genotype object attribute fitness_function will be overriden by the one set in this population object.

Parameters:genotype – The genotype to add into this population.
Raises:FullError – in case the population has already reached its full size.
clear()

Removes all the population genotypes.

empty() → bool

Points out if the population is empty or not.

Returns:True if the population is empty or false otherwise.
extend(genotypes: Iterable[pynetics.api.Genotype]) → None

Add a new bunch of genotypes to this population.

The implementation is delegated to the super class (list) but the population is marked as not sorted and it raises an error in case the maximum size (specified at initialization time) is reached.

Notice that, after calling this method, the genotype object attribute fitness_function will be overriden by the one set in this population object.

Parameters:genotypes – The genotypes to add into this population.
Raises:FullError – in case the population has already reached its full size.
fitness

The fitness used to evaluate the genotypes of the population.

Returns:This population fitness function.
full() → bool

Points out if the population is full or not.

Returns:True if the population is complete or false otherwise.
insert(index: int, genotype: pynetics.api.Genotype) → None

Add a new genotype to this population.

The implementation is delegated to the super class (list) but the population is marked as not sorted and it raises an error in case the maximum size (specified at initialization time) is reached.

Notice that, after calling this method, the genotype object attribute fitness_function will be overriden by the one set in this population object.

Parameters:
  • index – The genotype to add into this population.
  • genotype – The genotype to add into this population.
Raise:

FullError in case the population has already reached its full size.

pop(i: int = 0) → pynetics.api.Genotype

Extracts the object in the specified position.

Parameters:i – The position. If not specified, it will be position 0.
reverse() → None

Reverse the elements of the list in place.

The implementation is delegated to the super class (list) but the population is marked as not sorted.

sort(**kwargs) → None

Sorts the genotypes of this population by a function.

After calling this method, the order of the population will be in ascending order, i.e. from lower to higher fitness.

pynetics.callback module

General behaviour of the callbacks called while training.

class pynetics.callback.Callback

Bases: object

Base class to listen Genetic Algorithm events during training.

It is useful not only to monitor how the whole genetic algorithm is evolving during training, but also to modify it in real time (i.e. the problem constraints change in real time).

on_algorithm_begins(ga: pynetics.api.EvolutiveAlgorithm)

The method to be called when the genetic algorithm starts.

It will be called AFTER initialization but BEFORE the first iteration, including the check against the stop condition.

Parameters:ga – The genetic algorithm that caused the event.
on_algorithm_ends(ga: pynetics.api.EvolutiveAlgorithm)

The method to be called when the genetic algorithm ends.

It will be called AFTER the stop condition has been met.

Parameters:ga – The genetic algorithm that caused the event.
on_step_begins(ga: pynetics.api.EvolutiveAlgorithm)

The method to be called when an iteration step starts.

It will be called AFTER the stop condition has been checked and proved to be false) and BEFORE the new step is computed.

Parameters:ga – The genetic algorithm that caused the event.
on_step_ends(ga: pynetics.api.EvolutiveAlgorithm)

The method to be called when an iteration step ends.

It will be called AFTER an step of the algorithm has been computed and BEFORE a new check against the stop condition is going to be made.

Parameters:ga – The genetic algorithm that caused the event.
class pynetics.callback.History

Bases: pynetics.callback.Callback

Keep track of some of the indicators of the algorithm.

This callback is automatically added by the GeneticAlgorithm base class and is returned by its run method.

on_algorithm_begins(ga: pynetics.api.EvolutiveAlgorithm)

When the algorithm starts, the parameters are reset.

Parameters:ga – The genetic algorithm that caused the event.
on_step_ends(ga: pynetics.api.EvolutiveAlgorithm)

Once a step is finished, some indicators are recorded.

Those indicators include the best genotype instance and the best fitness achieved.

Parameters:ga – The genetic algorithm that caused the event.

pynetics.exception module

Definition of library specific errors.

exception pynetics.exception.AllelesError

Bases: pynetics.exception.PyneticsError

exception pynetics.exception.BoundsCannotBeTheSame(value)

Bases: pynetics.exception.PyneticsError

The lower and upper bounds are equal and cannot be.

exception pynetics.exception.CannotSelectThatMany(size: int, n: int)

Bases: pynetics.exception.SelectionError

The population has fewer chromosomes than the required.

exception pynetics.exception.ElementNotFound

Bases: pynetics.exception.PyneticsError

The element was not found where it was supposed to be.

exception pynetics.exception.EmptyPopulation(population_name: str = None)

Bases: pynetics.exception.PopulationError

The population has no individuals.

exception pynetics.exception.FullPopulationError(size: int)

Bases: pynetics.exception.PopulationError

We are trying to add more genotypes than the allowed.

exception pynetics.exception.GeneticAlgorithmError

Bases: pynetics.exception.PyneticsError

exception pynetics.exception.MoreGenesRequiredThanExisting(req_size: int, pool_size: int)

Bases: pynetics.exception.AllelesError

exception pynetics.exception.NotInitialized

Bases: pynetics.exception.GeneticAlgorithmError

The algorithm hasn’t been initialized before start.

exception pynetics.exception.PopulationError

Bases: pynetics.exception.PyneticsError

exception pynetics.exception.PyneticsError

Bases: Exception

Generic class for the errors raised from inside the library.

exception pynetics.exception.RecombinationError

Bases: pynetics.exception.PyneticsError

Errors related to mating issues.

exception pynetics.exception.SelectionError

Bases: pynetics.exception.PyneticsError

Errors related with selection algorithms

exception pynetics.exception.StopConditionError

Bases: pynetics.exception.PyneticsError

Any error related to stop conditions

exception pynetics.exception.WrongSelectionSize(n: int)

Bases: pynetics.exception.SelectionError

Tried to extract zero or negative genotypes.

pynetics.replacement module

Replacement algorithms.

class pynetics.replacement.HighElitism

Bases: pynetics.replacement.ReplacementSchema

Replacement with the fittest among both population and offspring.

Only those best genotypes among both populations will be selected, thus discarding those less fit. This makes this operator extremely elitist.

do(*, population: pynetics.api.Population, offspring: pynetics.api.Population, max_size: Optional[int] = None) → pynetics.api.Population

Executes this replacement.

Parameters:
  • population – The original population.
  • offspring – The population to replace the original one.
  • max_size – The size of the new population. It is guaranteed that the value will be a valid int value.
Returns:

A new Population instance.

class pynetics.replacement.LowElitism

Bases: pynetics.replacement.ReplacementSchema

Replaces the less fit of the population with the fittest of the offspring.

The method will replace the less fit genotypes by the best ones of the offspring. This makes this operator elitist, but at least not much. Moreover, if the offspring size equals to the population size then it’s a full replacement (i.e. a generational scheme).

do(*, population: pynetics.api.Population, offspring: pynetics.api.Population, max_size: Optional[int] = None) → pynetics.api.Population

Executes this replacement.

Parameters:
  • population – The original population.
  • offspring – The population to replace the original one.
  • max_size – The size of the new population. It is guaranteed that the value will be a valid int value.
Returns:

A new Population instance.

class pynetics.replacement.ReplacementSchema

Bases: object

Groups common behavior across all the replacement schemas.

The replacement schema is defined as a class. However, it is enough to implement it a replacement method, i.e. a function that receives two populations (original and offspring) and returns a population resulting from the combination of the previous two.

do(*, population: pynetics.api.Population, offspring: pynetics.api.Population, max_size: Optional[int] = None) → pynetics.api.Population

Executes this particular implementation of selection.

This method is not called by the base algorithms implemented, but from __call__() instead. It should contain the logic of the specific selection schema.

Parameters:
  • population – The original population.
  • offspring – The population to replace the original one.
  • max_size – The size of the new population. It is guaranteed that the value will be a valid int value.
Returns:

A new Population instance.

pynetics.selection module

Different implementations for some well known selection schemas.

class pynetics.selection.ExponentialRank(*, alpha: float = 1, replacement: bool = False)

Bases: pynetics.selection.WeightedSelectionSchema

Selection based on the fitness rank exponentially.

It’s somewhat similar to the roulette wheel method but instead of assigning the probability to be selected proportionally to their fitness, their probability to be selected is exponentially to their position in a rank where the genotypes are ordered accordingly to their fitnesses.

If the beta parameter is 0, the schema is equivalent to the monte carlo one, i.e. every chromosome has the same probability to be chosen.

get_weights(*, genotypes: Iterable[pynetics.api.Genotype]) → Tuple[float, ...]

Weights will be arranged according to the genotypes position.

Because the fittest genotype is the last one, the weights will be in ascending order.

Parameters:genotypes – The genotypes (only used to get the number of them).
Returns:A tuple with as many frequencies as genotypes in the population.
class pynetics.selection.LinearRank(*, alpha: float = 1, replacement: bool = False)

Bases: pynetics.selection.WeightedSelectionSchema

Selection based on the fitness rank proportionally.

It’s somewhat similar to the roulette wheel method but instead of assigning the probability to be selected proportionally to their fitness, their probability to be selected is proportional to their position in a rank where the genotypes are ordered accordingly to their fitnesses.

If the alpha parameter is 0, the schema is equivalent to the monte carlo one, i.e. every chromosome has the same probability to be chosen.

get_weights(*, genotypes: Iterable[pynetics.api.Genotype]) → Tuple[float, ...]

Weights will be arranged according to the genotypes position.

Because the fittest genotype is the last one, the weights will be in ascending order.

Parameters:genotypes – The genotypes (only used to get the number of them).
Returns:A tuple with as many frequencies as genotypes in the population.
class pynetics.selection.MonteCarlo(replacement: bool = False)

Bases: pynetics.selection.SelectionSchema

Selects the genotypes uniformly of the whole population.

It doesn’t provide any means to increase the selective pressure across the generations. It’s rather a way of measuring how well other selection schemes behave

do(population: pynetics.api.Population, n: int) → Iterable[pynetics.api.Genotype]

Implementation of the selection schema.

Parameters:
  • population – The population where genotypes are extracted.
  • n – The number of genotypes to return.
Returns:

A sequence of genotypes.

class pynetics.selection.RouletteWheel(replacement: bool = False)

Bases: pynetics.selection.WeightedSelectionSchema

Selection by associating the fitness to a probabilities.

Also called “fitness proportionate selection”, the function selects the genotypes weighting proportionally their fitness.

get_weights(*, genotypes: Iterable[pynetics.api.Genotype]) → Tuple[float, ...]

The frequency is directly each fitness.

Parameters:genotypes – The genotypes to select from.
Returns:A tuple with as many frequencies as genotypes in the population.
class pynetics.selection.SelectionSchema(replacement: bool = False)

Bases: object

Groups common behavior across all the selection schemas.

The selection schema is defined as a class. However, it is enough to implement it as a selection method, i.e. a function that receives a sequence and a number of individuals, and returns a sample of individuals of that size from the given population.

do(population: pynetics.api.Population, n: int) → Iterable[pynetics.api.Genotype]

Executes this particular implementation of selection.

This method is not called by the base algorithms implemented, but from __call__() instead. It should contain the logic of the specific selection schema.

Parameters:
  • population – The population where genotypes are extracted.
  • n – The number of genotypes to return.
Returns:

A sequence of genotypes.

class pynetics.selection.Tournament(m: int, replacement: bool = False)

Bases: pynetics.selection.SelectionSchema

Selects the best genotypes after random tournaments of genotypes.

The idea is as follows: If n genotypes are required, then n rounds (tournaments) of the following process are executed:

  1. Select m genotypes uniformly across the whole population.
  2. Select the best genotypes of those m genotypes.

After those n rounds are executed, the n resulting genotypes are returned.

As m is a meta-parameter of the genetic algorithm, it is necessary to create a new object with the desired value. For example, if a selection schema with m=3 is needed, the selection functor should be created as follows:

>>> tournament_m3 = Tournament(m=3)

After that, tournamet_m3 will be a tournament selection functor with an m of 3.

do(population: pynetics.api.Population, n: int) → Iterable[pynetics.api.Genotype]

Implementation of the selection schema.

Parameters:
  • population – The population where genotypes are extracted.
  • n – The number of genotypes to return.
Returns:

A sequence of genotypes.

class pynetics.selection.Truncation(replacement: bool = False)

Bases: pynetics.selection.SelectionSchema

Selects the best genotypes after sorting the population.

This selector simply selects the best n individuals from the population.

do(population: pynetics.api.Population, n: int) → Iterable[pynetics.api.Genotype]

Implementation of the selection schema.

Parameters:
  • population – The population where genotypes are extracted.
  • n – The number of genotypes to return.
Returns:

A sequence of genotypes.

class pynetics.selection.WeightedSelectionSchema(replacement: bool = False)

Bases: pynetics.selection.SelectionSchema

Generic behaviour to all the weight based schemas.

do(population: pynetics.api.Population, n: int) → Iterable[pynetics.api.Genotype]

Implementation of the selection schema.

Parameters:
  • population – The population where genotypes are extracted.
  • n – The number of genotypes to return.
Returns:

A sequence of genotypes.

get_weights(*, genotypes: Iterable[pynetics.api.Genotype]) → Tuple[float, ...]

Computes as many probabilities as genotypes.

The probabilities depend completely of the implementing class.

Parameters:genotypes – The genotypes to select from.
Returns:A tuple with as many frequencies as genotypes in the population.

pynetics.stop module

Different implementations of stop conditions for algorithms.

class pynetics.stop.FitnessBound(bound: float)

Bases: object

Stop based on a fitness bound.

class pynetics.stop.NumSteps(steps: int)

Bases: object

Stop based on the number of iterations.

The condition is met once the number of iterations has reached an specified limit.

pynetics.stop.never(genetic_algorithm: pynetics.api.EvolutiveAlgorithm) → bool

Stop condition that, ironically, never stops.

It doesn’t care about the argument, its state, its parameters, properties, … Nothing. It just doesn’t considers it is necessary to stop because it isn’t. And you’ll never be able to change it, no matter how much effort. Unless you’re a developer, of course; if so, then you’re closest thing to a god that will ever exist, because you bend reality to your will. Nice thing.

Parameters:genetic_algorithm – The genetic algorithm where this stop condition belongs.
Returns:True if criteria is met, false otherwise.

pynetics.util module

Utilities to be used across the project.

pynetics.util.take_chances(probability: float = 0.5) → bool

Random bool value given a probability p.

Parameters:probability – The value of the probability to beat. Defaults to 0.5.
Returns:A true value with a probability of p (thus, a False value with a (1 - p) probability).

Module contents

Import core names of pynetics.

This library provides the means to create genetic algorithms in a simple yet fast way. Specifically:

1. A generic and modular algorithm to be composed by the different parts that build a whole algorithm. 2. Simple algorithm implementations with some defaults implemented. 3. Different generic operators (e.g. crossover or mutation) to work with different genotypes (e.g. binary list, real list).

The fastest way to work with this library is by importing this file directly:

>>> import pynetics as pyn