evokit.core package

Submodules

evokit.core.accountant module

class evokit.core.accountant.AccountantRecord[source]

Bases: NamedTuple, Generic[C]

A value collected by an Accountant; also contains the context in which that value is collected.

event: str

Event that triggers the handler.

generation: int

Generation count when the event event occurs.

value: Any

Data collected in generation generation after event event.

class evokit.core.accountant.Accountant[source]

Bases: Generic[C]

Monitor and collect data from a running Algorithm.

Maintain a dictionary of event : handler mappings. Each time event fires, handler collects data from the Algorithm.

Call Algorithm.register() to register an Accountant to a Algorithm.

Example:

ctrl = SimpleLinearAlgorithm(...)
acc1 = Accountant(handlers={"POST_EVALUATION":
                            lambda x: len(x.population)})
ctrl.register(acc1)

for _ in range(...):
    ctrl.step()

report = acc1.publish()

Tutorial: Collect Runtime Statistics with Accountant.

__init__(handlers: Dict[str, Callable[[C], Any]])[source]
Parameters:

handlers – a dictionary of event : handler mappings. Each handler should have the signature Algorithm -> Any:

records: List[AccountantRecord]

Records collected by the Accountant

handlers: Dict[str, Callable[[C], Any]]

Event - handler pairs of the Accountant

subject: C | None

The attached Algorithm

publish() List[AccountantRecord][source]

Report collected data.

Each time an event fires in the attached :class`.Algorithm`, if that event is registered in handlers, supply the :class`.Algorithm` to the handler as argument then collect the result in an AccountantRecord. This method returns a list of all collected records.

is_registered() bool[source]

Return if this accountant is attached to an :class:Algorithm.

evokit.core.algorithm module

class evokit.core.algorithm.Algorithm[source]

Bases: ABC

Base class for all evolutionary algorithms.

Derive this class to create custom algorithms.

Tutorial: Creating a Custom Controller.

static __new__(cls, *_: Any, **__: Any) Self[source]

Machinery.

Implement managed attributes.

abstract __init__() None[source]

Subclasses should override this method.

The initialiser should create (or accept as argument) operators used in the algorithm.

generation: int

Generation counter, automatically increments wit step.

accountants: List[Accountant]

Registered Accountant objects.

events: List[str]

Events that can be reported by this algorithm.

abstract step() None[source]

Advance the population by one generation.

Subclasses should override this method. Use operators to update the population (or populations). Call update() to fire events.

Example:

self.population = self.variator.vary_population(self.population)
self.update("POST_VARIATION")

self.evaluator.evaluate_population(self.population)
self.update("POST_EVALUATION")

self.population = \
    self.selector.select_to_population(self.population)

Note

Do not manually increment generation. This property is automatically managed.

register(accountant: Accountant) None[source]

Attach an Accountant to this algorithm.

Parameters:

accountant – An Accountant that observes and collects data from this Algorithm.

update(event: str) None[source]

Report an event to all attached Accountant objects.

If the event is not in events, raise an exception.

Parameters:

event – The event to report.

Raises:

ValueError – if an reported event is not registered.

class evokit.core.algorithm.SimpleLinearAlgorithm[source]

Bases: Algorithm

A very simple evolutionary algorithm.

An evolutionary algorithm that maintains one population and does not take advantage of parallelism. The algorithm applies its operators in the following order:

  1. fire event GENERATION_BEGIN

  2. evaluate for selection

  3. fire event POST_VARIATION

  4. select for survivors

  5. update population

  6. fire event POST_EVALUATION

  7. vary parents

  8. update population

  9. fire event POST_SELECTION

__init__(population: Population[T], evaluator: Evaluator[T], selector: Selector[T], variator: Variator[T]) None[source]
step() None[source]
class evokit.core.algorithm.LinearAlgorithm[source]

Bases: Algorithm

A simple evolutionary algorithm.

An evolutionary algorithm that maintains one population and does not take advantage of parallelism. The algorithm applies its operators in the following order:

  1. fire event "GENERATION_BEGIN"

  2. evaluate for parent selection

  3. fire event POST_PARENT_EVALUATION

  4. select for parents

  5. update population

  6. fire event POST_PARENT_SELECTION

  7. vary parents

  8. fire event POST_VARIATION

  9. evaluate for survivor selection

  10. fire event POST_SURVIVOR_EVALUATION

  11. select for survivors

  12. update population

  13. fire event POST_SURVIVOR_SELECTION

__init__(population: Population[T], parent_evaluator: Evaluator[T], parent_selector: Selector[T], variator: Variator[T], survivor_evaluator: Evaluator[T], survivor_selector: Selector[T]) None[source]
step() None[source]

evokit.core.evaluator module

class evokit.core.evaluator.Evaluator[source]

Bases: ABC, Generic[D]

Base class for all evaluators.

Derive this class to create custom evaluators.

Tutorial: Getting Started with OneMax.

static __new__(cls, *args: Any, **kwargs: Any) Self[source]

Machinery. Implement managed attributes.

__init__() None[source]
retain_fitness: bool

If this evaluator should re-evaluate an Individual whose fitness is already set.

abstract evaluate(individual: D) tuple[float, ...][source]

Evaluation strategy. Return the fitness of an individual.

Subclasses should override this method.

Note

The implementation should assign higher fitness to better individuals.

Parameters:

individual – individual to evaluate

evaluate_population(pop: Population[D]) None[source]

Context of evaluate().

Iterate individuals in a population. For each individual, compute a fitness with evaluate(), then assign that value to the individual.

A subclass may override this method to implement behaviours that require access to the entire population.

Effect:

For each item in pop, set its fitness .Individual.fitness.

Note

This method must never return a value. It must assign to fitness for each Individual in the Population. The result must be sorted, so that the earliest item has the highest fitness.

class evokit.core.evaluator.NullEvaluator[source]

Bases: Evaluator[Any]

evaluate(_: Any) Tuple[float][source]

evokit.core.population module

class evokit.core.population.Individual[source]

Bases: ABC, Generic[R]

Base class for all individuals.

Derive this class to create custom representations.

Note

An implementation should store the genotype in genome.

The individual can information outside of the genotype, such as a .fitness, a reference to the parent, and strategy parameter(s).

Tutorial: Getting Started with OneMax.

static __new__(cls: Type[Self], *args: Any, **kwargs: Any) Self[source]

Machinery. Implement managed attributes.

abstract __init__() None[source]
genome: R

Genotype of the individual.

property fitness: tuple[float, ...]

Fitness of an individual.

Writing to this property changes the fitness of the individual. If this individual has yet to be assigned a fitness, reading from this property raises an exception.

To determine if the individual has a fitness, call has_fitness().

Returns:

Fitness of the individual

Raises:

ValueError – if the current fitness is None.

reset_fitness() None[source]

Reset the fitness of the individual.

Set the fitness of the individual to None.

Effect:

The .fitness of this individual becomes None.

has_fitness() bool[source]

Return if the individual has a fitness value.

abstract copy() Self[source]

Return an identical copy of the individual.

Subclasses should override this method.

Operations on in this individual should not affect the new individual. In addition to duplicating genome, the implementation should decide whether to retain other fields such as fitness.

Note

Ensure that changes made to the returned value do not affect the original value.

class evokit.core.population.AbstractCollection[source]

Bases: ABC, Generic[R], Sequence[R], Iterable[R]

Machinery.

__init__(*args: R)[source]
append(value: R) None[source]

Append an item to this collection.

Parameters:

value – The item to add to this item

extend(values: Iterable[R]) None[source]

Append all items from another collection to this collection

Parameters:

values – Collection whose values are appended to this collection.

populate(new_data: Iterable[R]) None[source]

Replace items in this population with items in new_data.

Parameters:

new_data – Collection whose items replace items in this population.

Effect:

Replace all items in this population with those in new_data.

draw(key: R | None = None, pos: int | None = None) R[source]

Remove an item from the population.

Identify an item either by value (in key) or by position (in pos). Remove that item from the collection, then return that item.

Returns:

The Individual that is removed from the population

Raises:

TypeError – If neither key nor pos is given.

class evokit.core.population.Population[source]

Bases: AbstractCollection[D]

A flat collection of individuals.

__init__(*args: D)[source]
Parameters:

*args – Initial items in the population

copy() Self[source]

Return an independent population.

Changes made to items in the new population should not affect items in this population. This behaviour depends on correct implementation of Individual.copy() in each item.

Call Individual.copy() for each Individual in this population. Collect the results, then create a new population with these values.

sort(ranker: Callable[[D], SupportsRichComparison] = <function Population.<lambda>>) None[source]

Rearrange items by fitness, highest-first.

If individuals have multiple fitnesses, sort lexi … what?.

Parameters:

ranker – Sort key, called on each item prior to sorting.

Effect:

Rearrange items in this population.

reset_fitness() None[source]

Remove fitness values of all Individuals in the population.

Effect:

For each item in this population, set its fitness Individual.fitness to None.

best() D[source]

Return the highest-fitness individual in this population.

evokit.core.selector module

class evokit.core.selector.Selector[source]

Bases: ABC, Generic[D]

Base class for all selectors.

Derive this class to create custom selectors.

Tutorial: Creating a Custom Selector.

__init__(budget: int)[source]
Parameters:

budget – Number of individuals in the output.

select_to_population(population: Population[D]) Population[D][source]

Select from a population to a population.

Invoke select_to_many(), then shape the result into a Population.

Parameters:

population – population to select from.

Returns:

A new population with selected individuals.

Effect:

Remove all items from the original population (from select_to_many()).

select_to_many(population: Population[D]) Tuple[D, ...][source]

Context of select.

Repeatedly apply select() to create a collection of solutions. Each application removes an item in the original population.

A subclass may override this method to implement behaviours that require access to the entire selection process.

Parameters:

population – population to select from.

Returns:

A tuple of selected individuals.

Effect:

Remove all items from population.

abstract select(population: Population[D]) Tuple[D, ...][source]

Selection strategy.

All subclasses should override this method. The implementation should return a tuple of individuals. Each item in the tuple should also be a member of population.

Parameters:

population – population to select from.

Returns:

A tuple of selected individuals.

class evokit.core.selector.NullSelector[source]

Bases: Selector[D]

Selector that does nothing.

__init__()[source]
select(*_: Any, **__: Any) Any[source]
select_to_many(population: Population[D]) Tuple[D, ...][source]

Select every item in the population.

class evokit.core.selector.SimpleSelector[source]

Bases: Selector[D]

Simple selector that select the highest-fitness individual.

__init__(budget: int)[source]
select(population: Population[D]) Tuple[D][source]

Greedy selection.

Select the item in the population with highest fitness.

class evokit.core.selector.ElitistSimpleSelector[source]

Bases: SimpleSelector[D]

Elitist selector that select the highest-fitness individual.

Example for overriding select_to_many. Just overriding select

is not enough, because elitism requires the highest-fitness individual of a _population_.

__init__(budget: int)[source]
select_to_many(population: Population[D]) Tuple[D, ...][source]

Context that implements elitism.

Preserve and update an elite. Each time the selector is used, insert the current elite to the results.

class evokit.core.selector.TournamentSelector[source]

Bases: Selector[D]

Tournament selector.

__init__(budget: int, bracket_size: int = 2, probability: float = 1)[source]
select(population: Population[D]) Tuple[D][source]

Tournament selection.

Select a uniform sample, then select the best member in that sample.

evokit.core.selector.Elitist(sel: Selector[D]) Selector[source]

Decorator that adds elitism to a selector.

Retain and update the highest-fitness individual encountered so far. Each time the selector is called, append that individual to the end of the output population.

Modify select_to_many of sel to use elitism. If sel already

overrides select_to_many, that implementation is destroyed.

Parameters:

sel – A selector

Returns:

A selector

evokit.core.variator module

class evokit.core.variator.Variator[source]

Bases: ABC, Generic[D]

static __new__(cls: Type[Self], *args: Any, **kwargs: Any) Self[source]

Machinery. Implement managed attributes.

__init__() None[source]
abstract vary(parents: Sequence[D]) Tuple[D, ...][source]

Apply the variator to a tuple of parents

Produce a tuple of individuals from a tuple of individuals. The input and output tuple sizes should match the arity and coarity of this selector, respectively.

Note

If the result could have a different fitness .Individual.reset_fitness than the original individual, call Individual.reset_fitness() to clear its fitness.

vary_population(population: Population[D]) Population[D][source]

Vary the population.

Separate population into groups of size .arity. For each group, call .vary with that group as argument, then collect the result.

At the end, return a Population of collected results.

class evokit.core.variator.NullVariator[source]

Bases: Variator[D]

Variator that does not change anything

__init__() None[source]
vary(parents: Sequence[D]) Tuple[D, ...][source]

Module contents

Export modules from core.