Why EvoKit?

Operators are Easy to Make

Define only what matters, let the framework automate wht it can.

The stock OneMax evaluator is written just in 3 lines! [1]

class CountBits(Evaluator[BinaryString]):
    def evaluate(self, individual: BinaryString) -> tuple[float,]:
        return (individual.genome.bit_count(),)

Operators are Interchangeable

Operators of the same type are interchangeable. That is:

  • All evaluators and variators of the same representation are interchangeable.

  • All selectors are interchangeable.

  • All algorithms work with all configurations of compatible operators.

Completely Documented

All public members are documented (see [evokit] for the API documentation).

All private members (except for well-known dunders) are documented. You can find examples like this in the source code:

def _get_arity(fun: Callable[..., Any]
               | Expression[Any]
               | Symbol
               | Any) -> int:
    """Return the arity of an object.

    If the argument is callable, return the length of its signature.
    Otherwise, return 0.

    Does not work with built-in functions and other objects that do not
    work with :meth:`.inspect.signature`.

    Args:
        fun: An object
    """

Well Described

EvoKit describes exactly what it does.

  • All methods (public or private) have type hints:

    def __init__(self: Self,
                 population: Population[T],
                 evaluator: Evaluator[T],
                 selector: Selector[T],
                 variator: Variator[T]) -> None:
  • All return values are explained:

    def has_fitness(self) -> bool:
        """Return `True` if :attr:`.fitness` is not None.
            Otherwise, return `False`.
        """
        return self._fitness is not None
  • All effects are documented:

    def reset_fitness(self) -> None:
        """Reset the fitness of the individual.

        Effect:
            The :attr:`.fitness` of this individual becomes ``None``.
        """
        self._fitness = None
  • All managed attributes are explained:

    def step(self: Self, *args: Any, **kwargs: Any) -> None:
        """Advance the population by one generation.
            nothing prevents you from firing them inside :meth:`step`.
            The :attr:`automatic_events` class attribute reports these
            events, like how :attr:`events` reports regular events.
        """

Transparent

Core modules (in core) do not depend on any external module.

Reproducible

All randomness in existing modules come from random and can be reproduced by setting the same random.seed().