{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Getting Started with OneMax\n",
"\n",
"This tutorial explains how to implement custom representations\n",
"with the following components:\n",
"\n",
"- [Individual](#Representation) as the\n",
" _binary string_ representation.\n",
"\n",
"- [Variator](#Variator) as the _random bit mutator_\n",
" which modifies each bit of the binary string with a fixed probability.\n",
"\n",
"- [Evaluator](#Evaluator) as the _bit distance\n",
" evaluator_ which sums all bits in a bit string.\n",
"\n",
"- [Put Everything Together](#Put-Everything-Together) runs\n",
" an evolutionary algorithm using the operators defined above,\n",
" in addition to a stock selector.\n",
"\n",
"Typically, these components are required to solve an optimisation\n",
"problem: (a) the individual represents solutions, (b) the variator\n",
"must know the representation to change it, and (c) the evaluator\n",
"assesses how the individual solves the problem.\n",
"\n",
"
"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Individual\n",
"\n",
"All custom representations must derive `evokit.core.population.Individual`.\n",
"\n",
"At minimum, the implementation must do the following:\n",
" \n",
"1. Initialise `.genome` to store the representation.\n",
"\n",
"3. Override `.copy(..)` to return an independent copy.\n",
"\n",
"The implementation must decide how to initialise new individuals.\n",
"In this example, the class `BinaryString` implements a factory\n",
"method `BinaryString.random(..)`, which creates a uniformly\n",
"random bit string of the given length."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from __future__ import annotations\n",
"from typing import Self\n",
"from typing import override"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"from random import getrandbits\n",
"from evokit.core import Individual\n",
"\n",
"class BinaryString(Individual[list[int]]):\n",
" @override\n",
" def __init__(self, value: list[int]) -> None:\n",
" \"\"\"Required.\n",
"\n",
" Initialise the genome.\n",
" \"\"\"\n",
" self.genome: list[int] = value\n",
"\n",
" @override\n",
" def copy(self: Self) -> Self:\n",
" \"\"\"Required.\n",
" \n",
" Ensure that operations performed on\n",
" a copy do not affect to the original individual.\n",
" \"\"\"\n",
" return type(self)(self.genome.copy())\n",
"\n",
" @staticmethod\n",
" def random(len: int) -> BinaryString:\n",
" \"\"\"Not required.\n",
" \n",
" Initialise a random individual.\n",
" \"\"\"\n",
" return BinaryString(\n",
" (len * [0] +\n",
" [int(digit)\n",
" for digit in bin(getrandbits(len))[2:]])[-len:]\n",
" )\n",
"\n",
" @override\n",
" def __str__(self: Self) -> str:\n",
" \"\"\"Not required.\n",
" \n",
" Return a text representation of the individual.\n",
" \"\"\"\n",
" return str(self.genome)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Variator\n",
"\n",
"All custom variators must derive `evokit.core.variator.Variator`.\n",
"\n",
"At minimum, the implementation must do the following:\n",
" \n",
"1. Initialise `.arity` to specify how many parents are passed to `.vary`.\n",
"\n",
"2. Override `.vary(..)` method to specify how to create new individuals from existing ones.\n",
" Be sure to call `.copy` before making changes to an existing individual.\n",
"\n",
"In this example, `.vary` receives an 1-tuple because `.arity=2`."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"from random import random\n",
"from typing import Sequence\n",
"\n",
"from evokit.core import Variator\n",
"\n",
"class RandomBitMutator(Variator[BinaryString]):\n",
" @override\n",
" def __init__(self, mutation_rate: float):\n",
" self.arity = 1\n",
"\n",
" # Not required. Raise an error if the mutation rate is too big or small.\n",
" if (mutation_rate < 0 or mutation_rate > 1):\n",
" raise ValueError(f\"Mutation rate must be within {0} and {1}.\"\n",
" f\"Got: {mutation_rate}\")\n",
" \n",
" # The mutation rates is a parameter of the variator.\n",
" self.mutation_rate = mutation_rate\n",
" \n",
" @override\n",
" def vary(self, parents: Sequence[BinaryString]) -> tuple[BinaryString,\n",
" BinaryString]:\n",
" \"\"\"Required.\n",
"\n",
" Produce new individuals from existing ones.\n",
" \n",
" Because `.arity=1` in the initialiser, `parents` will be a 1-tuple at runtime.\n",
" \"\"\"\n",
" offspring = parents[0].copy()\n",
"\n",
" for i in range(0, len(offspring.genome)):\n",
" if (random() < self.mutation_rate):\n",
" offspring.genome[i] = 1 if offspring.genome[i] == 0 else 1\n",
"\n",
" return (offspring, parents[0].copy())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Evaluator\n",
"\n",
"All custom evaluators must derive `evokit.core.evaluator.Evaluator`.\n",
"\n",
"At minimum, the implementation must override the `.evaluate(..)` method. This method should take one individual of the correct type, then return its fitness(es) in a tuple.\n",
"\n",
"The `.evaluate_population(..)` method, which is not covered here, applies `.evaluate(..)` to each \n",
"\n",
"In this example, `.evaluate(..)` returns a 1-tuple because it uses only one measure of fitness. The method sums all bits in the `.genome` of a `BinaryString`."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"from evokit.core import Evaluator\n",
"from typing import override\n",
"\n",
"class BitDistanceEvaluator(Evaluator[BinaryString]):\n",
" @override\n",
" def evaluate(self, individual: BinaryString) -> tuple[float]:\n",
" return (sum(individual.genome),)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Put Everything Together\n",
"\n",
"Construct an evolutionary algorithm with components defined\n",
"above, in addition to a stock selector\n",
"`evolvables.selectors.TruncationSelector`\n",
"(see the [Selector Tutorial](./selector.ipynb) for how to construct it)\n",
"and a stock algorithm `evolvables.algorithms.SimpleLinearAlgorithm`\n",
"(see the [Algorithm Tutorial](./algorithm.ipynb) for\n",
"how to make your own). Selectors are algorithms are independent\n",
"to problem and solution representations.\n",
"\n",
"This example also use an elitist (hall-of-fame) truncation selector.\n",
"This is done by wrapping the selector with `Elitist` (see the\n",
"[Interceptor Tutorial](./interceptor.ipynb) for more information).\n",
"\n",
"Each step of the algorithm includes the following operations:\n",
"\n",
"1. `evaluate` the `population`\n",
"2. `select` from `population`\n",
" * then, `update` `population` with the result\n",
"3. `vary` `population`\n",
" * then, `update` the `population` with the result\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"from evokit.evolvables.selectors import Elitist, TruncationSelector\n",
"from evokit.evolvables.algorithms import SimpleLinearAlgorithm"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Initialise Population\n",
"\n",
"The algorithm should begin with a pool of candidate solutions.\n",
"Create a number of `BinaryString` instances, then store them in\n",
"a `Population`. This is the initial population."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"from evokit.core import Population\n",
"\n",
"BINSTRING_LENGTH: int = 1000\n",
"POPULATION_SIZE: int = 20\n",
"GENERATION_COUNT: int = 100\n",
"init_pop = Population[BinaryString]()\n",
"\n",
"for i in range(0, POPULATION_SIZE):\n",
" init_pop.append(BinaryString.random(BINSTRING_LENGTH))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Define the Algorithm\n",
"\n",
"Initialise operators, then initialise the algorithm with these operators.\n",
"Everything has already been defined or imported, so using them is easy."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"ctrl: SimpleLinearAlgorithm[BinaryString] =\\\n",
" SimpleLinearAlgorithm[BinaryString](\n",
" population=init_pop,\n",
" variator=RandomBitMutator(0.1),\n",
" selector=Elitist(TruncationSelector[BinaryString](POPULATION_SIZE)),\n",
" evaluator = BitDistanceEvaluator()\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Run the Algorithm\n",
"\n",
"Run the algorithm, then record the best individual of\n",
"each generation. Be sure call `copy`, so that the retained individual\n",
"is not affected by operations on the original."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"bests: list[BinaryString] = []\n",
"\n",
"for _ in range(GENERATION_COUNT):\n",
" ctrl.step()\n",
" bests.append(ctrl.population.best().copy())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Analyse Results\n",
"\n",
"As `bests` records highest-fitness individuals of each generation,\n",
"its last item should have higher fitness than the first.\n",
"Observe the difference:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Best individual of the first generation is [0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]\n",
" with fitness 577\n",
"Best individual of the last generation is [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n",
" with fitness 1000\n"
]
}
],
"source": [
"print(f\"Best individual of the first generation is {bests[0]}\")\n",
"print(f\" with fitness {sum(bests[0].genome)}\")\n",
"print(f\"Best individual of the last generation is {bests[-1]}\")\n",
"print(f\" with fitness {sum(bests[-1].genome)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Plot the training curve, note that the increase in\n",
"fitness slows down near convergence.\n",
"\n",
"For EvoKit's specialised\n",
"data collection and reporting module, see the\n",
"[Accountant Tutorial](./accountant.ipynb)."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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iMh4ZMLAVRZfffVVbNy4Ee7u7qaqyeyoVCq4uroiLS1N8vFEQbOicD8jD1s/aIWGvLErERHRExny+9vgWWZ79+4tdWH0fB6dcl/Dk5fMiIiIjEWvQDRu3DjMnDkTjo6OOtPRS7Jo0SKjFEbFXb/PKfdERESmoFcgOnnyJPLz87X/fhKpFmy0FPGcYUZERGQSegWivXv34tq1a3B1deUlMwnd+GcNIj/ew4yIiMio9F6HqHbt2rh37572ef/+/YsteEim9W8PEccPERERGZPegejxyWjbtm1DZmam0QuiJ7vOHiIiIiKTMHilapJO0X3MOIaIiIjIuPQORDKZrNigaQ6iLjuqnHw8yCy6yz0DERERkTHpvQ6REALvvPOO9nYXOTk5eP/99+HoqPvLeePGjcatkAD8e7mskpMCTgqDl48iIiKip9D7N2tERITO87feesvoxdCTXX/Ae5gRERGZit6BaOXKlaasg55BO6Ca44eIiIiMjoOqywnOMCMiIjIdBqJygqtUExERmQ4DUTmhnXLPMURERERGx0BUDqRl5yMls+gu9+whIiIiMjYGonKgaPxQZWdOuSciIjIFBqJy4Po/44f82TtERERkEgxE5cD1+xw/REREZEoMROVAUQ8Rxw8RERGZBgNROaC9ZMY1iIiIiEyCgagc4CrVREREpsVAZObSsvLxMCsfAFDDk2OIiIiITIGByMzdSPl3yr0jp9wTERGZBAORmSu6y70fe4eIiIhMhoHIzCVwhhkREZHJMRCZOfYQERERmR4DkZm78U8PUXX2EBEREZkMA5GZYw8RERGR6TEQmbHM3ALcS88FANTwYA8RERGRqTAQmbEb//QOuTvYwNXBRuJqiIiIKi4GIjOWkMIZZkRERGWBgciMFY0f4grVREREpsVAZMZucA0iIiKiMsFAZMau3+cMMyIiorLAQGTG2ENERERUNhiIzFROvhp3VDkA2ENERERkagxEZurWwywIATgrrOHhaCt1OURERBUaA5GZKho/VN3TATKZTOJqiIiIKjYGIjN1/Z/xQ34cP0RERGRyDERm6gbXICIiIiozDERmij1EREREZYeByEwlpLCHiIiIqKwwEJmhfLUGtx5mA+AaRERERGWBgcgM3X6YDbVGwM5GDi9nhdTlEBERVXgMRGaoaPxQDQ9HyOWcck9ERGRqDERmiDPMiIiI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",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# type: ignore[reportUnknownMemberType]\n",
"from matplotlib import pyplot as plt\n",
"\n",
"plt.title('Training Curve of the Custom Algorithm') \n",
"plt.xlabel('Generation')\n",
"plt.ylabel('Fitness of Best Individual')\n",
"plt.plot([i for i in range(len(bests))], [sum(x.genome) for x in bests])\n",
"plt.show()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}