From 4d913250e358f3fab4db0084904b6d8fdbcd2895 Mon Sep 17 00:00:00 2001 From: DanyPaisDaSilva Date: Wed, 19 Feb 2025 15:04:26 +0100 Subject: [PATCH 1/7] added .individuals to offspring and parents where relevant in example 4b --- .../4b_simple_ea_xor_database/main.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/4_example_experiment_setups/4b_simple_ea_xor_database/main.py b/examples/4_example_experiment_setups/4b_simple_ea_xor_database/main.py index 811456ba5..ffc576cb3 100644 --- a/examples/4_example_experiment_setups/4b_simple_ea_xor_database/main.py +++ b/examples/4_example_experiment_setups/4b_simple_ea_xor_database/main.py @@ -91,8 +91,8 @@ def select( original_survivors, offspring_survivors = population_management.steady_state( [i.genotype for i in population.individuals], [i.fitness for i in population.individuals], - [i.genotype for i in offspring], - [i.fitness for i in offspring], + [i.genotype for i in offspring.individuals], + [i.fitness for i in offspring.individuals], lambda n, genotypes, fitnesses: selection.multiple_unique( n, genotypes, @@ -112,8 +112,8 @@ def select( ] + [ Individual( - genotype=offspring[i].genotype, - fitness=offspring[i].fitness, + genotype=offspring.individuals[i].genotype, + fitness=offspring.individuals[i].fitness, ) for i in offspring_survivors ] @@ -145,10 +145,11 @@ def reproduce(self, population: NDArray[np.int_], **kwargs: Any) -> list[Genotyp ) # We select the population of parents that were passed in KWArgs of the parent selector object. if parents is None: raise KeyError("No children passed.") + #print(type(parents)) offspring = [ Genotype.crossover( - parents[parent1_i].genotype, - parents[parent2_i].genotype, + parents.individuals[parent1_i].genotype, + parents.individuals[parent2_i].genotype, self._rng, num_parameters=config.NUM_PARAMETERS, ).mutate( From 96bb70360fac15f08b024c6661086b660e6b212d Mon Sep 17 00:00:00 2001 From: DanyPaisDaSilva Date: Wed, 19 Feb 2025 15:30:49 +0100 Subject: [PATCH 2/7] further abstracted isinstance assertion (np.int_ to np.integer) in _body_develop.py so that example 4c works --- .../genotypes/cppnwin/modular_robot/v2/_body_develop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/revolve2/standards/genotypes/cppnwin/modular_robot/v2/_body_develop.py b/standards/revolve2/standards/genotypes/cppnwin/modular_robot/v2/_body_develop.py index f65a92c96..2b8996479 100644 --- a/standards/revolve2/standards/genotypes/cppnwin/modular_robot/v2/_body_develop.py +++ b/standards/revolve2/standards/genotypes/cppnwin/modular_robot/v2/_body_develop.py @@ -101,7 +101,7 @@ def __evaluate_cppn( """ x, y, z = position assert isinstance( - x, np.int_ + x, np.integer ), f"Error: The position is not of type int. Type: {type(x)}." body_net.Input([1.0, x, y, z, chain_length]) # 1.0 is the bias input body_net.ActivateAllLayers() From f909f97af98b9d8be1120395baf64c8f779618ca Mon Sep 17 00:00:00 2001 From: DanyPaisDaSilva Date: Fri, 14 Mar 2025 19:18:00 +0100 Subject: [PATCH 3/7] added prototype project --- prototype/config.py | 6 ++ prototype/ea.py | 160 ++++++++++++++++++++++++++++++++++++++++ prototype/genotype.py | 88 ++++++++++++++++++++++ prototype/individual.py | 13 ++++ prototype/main.py | 104 ++++++++++++++++++++++++++ prototype/rerun.py | 28 +++++++ prototype/tools.py | 18 +++++ 7 files changed, 417 insertions(+) create mode 100644 prototype/config.py create mode 100644 prototype/ea.py create mode 100644 prototype/genotype.py create mode 100644 prototype/individual.py create mode 100644 prototype/main.py create mode 100644 prototype/rerun.py create mode 100644 prototype/tools.py diff --git a/prototype/config.py b/prototype/config.py new file mode 100644 index 000000000..59ff95885 --- /dev/null +++ b/prototype/config.py @@ -0,0 +1,6 @@ +"""Configuration parameters for this example.""" + +NUM_SIMULATORS = 2 # should be number of cores +POPULATION_SIZE = 100 +OFFSPRING_SIZE = 50 +NUM_GENERATIONS = 100 diff --git a/prototype/ea.py b/prototype/ea.py new file mode 100644 index 000000000..aad8ac67a --- /dev/null +++ b/prototype/ea.py @@ -0,0 +1,160 @@ +from typing import Any +import multineat +import numpy as np +import numpy.typing as npt +from genotype import Genotype +from individual import Individual +from revolve2.experimentation.evolution.abstract_elements import Reproducer, Selector +from revolve2.experimentation.optimization.ea import population_management, selection + + +class ParentSelector(Selector): + """Selector class for parent selection.""" + + rng: np.random.Generator + offspring_size: int + + def __init__(self, offspring_size: int, rng: np.random.Generator) -> None: + """ + Initialize the parent selector. + + :param offspring_size: The offspring size. + :param rng: The rng generator. + """ + self.offspring_size = offspring_size + self.rng = rng + + def select( + self, population: list[Individual], **kwargs: Any + ) -> tuple[npt.NDArray[np.int_], dict[str, list[Individual]]]: + """ + Select the parents. + + :param population: The population of robots. + :param kwargs: Other parameters. + :return: The parent pairs. + """ + return np.array( + [ + selection.multiple_unique( + selection_size=2, + population=[individual.genotype for individual in population], + fitnesses=[individual.fitness for individual in population], + selection_function=lambda _, fitnesses: selection.tournament( + rng=self.rng, fitnesses=fitnesses, k=1 + ), + ) + for _ in range(self.offspring_size) + ], + ), {"parent_population": population} + + +class SurvivorSelector(Selector): + """Selector class for survivor selection.""" + + rng: np.random.Generator + + def __init__(self, rng: np.random.Generator) -> None: + """ + Initialize the parent selector. + + :param rng: The rng generator. + """ + self.rng = rng + + def select( + self, population: list[Individual], **kwargs: Any + ) -> tuple[list[Individual], dict[str, Any]]: + """ + Select survivors using a tournament. + + :param population: The population the parents come from. + :param kwargs: The offspring, with key 'offspring_population'. + :returns: A newly created population. + :raises ValueError: If the population is empty. + """ + offspring = kwargs.get("children") + offspring_fitness = kwargs.get("child_task_performance") + if offspring is None or offspring_fitness is None: + raise ValueError( + "No offspring was passed with positional argument 'children' and / or 'child_task_performance'." + ) + + original_survivors, offspring_survivors = population_management.steady_state( + old_genotypes=[i.genotype for i in population], + old_fitnesses=[i.fitness for i in population], + new_genotypes=offspring, + new_fitnesses=offspring_fitness, + selection_function=lambda n, genotypes, fitnesses: selection.multiple_unique( + selection_size=n, + population=genotypes, + fitnesses=fitnesses, + selection_function=lambda _, fitnesses: selection.tournament( + rng=self.rng, fitnesses=fitnesses, k=2 + ), + ), + ) + + return [ + Individual( + population[i].genotype, + population[i].fitness, + ) + for i in original_survivors + ] + [ + Individual( + offspring[i], + offspring_fitness[i], + ) + for i in offspring_survivors + ], {} + + +class CrossoverReproducer(Reproducer): + """A simple crossover reproducer using multineat.""" + + rng: np.random.Generator + innov_db_body: multineat.InnovationDatabase + innov_db_brain: multineat.InnovationDatabase + + def __init__( + self, + rng: np.random.Generator, + innov_db_body: multineat.InnovationDatabase, + innov_db_brain: multineat.InnovationDatabase, + ): + """ + Initialize the reproducer. + + :param rng: The ranfom generator. + :param innov_db_body: The innovation database for the body. + :param innov_db_brain: The innovation database for the brain. + """ + self.rng = rng + self.innov_db_body = innov_db_body + self.innov_db_brain = innov_db_brain + + def reproduce( + self, population: npt.NDArray[np.int_], **kwargs: Any + ) -> list[Genotype]: + """ + Reproduce the population by crossover. + + :param population: The parent pairs. + :param kwargs: Additional keyword arguments. + :return: The genotypes of the children. + :raises ValueError: If the parent population is not passed as a kwarg `parent_population`. + """ + parent_population: list[Individual] | None = kwargs.get("parent_population") + if parent_population is None: + raise ValueError("No parent population given.") + + offspring_genotypes = [ + Genotype.crossover( + parent_population[parent1_i].genotype, + parent_population[parent2_i].genotype, + self.rng, + ).mutate(self.innov_db_body, self.innov_db_brain, self.rng) + for parent1_i, parent2_i in population + ] + return offspring_genotypes \ No newline at end of file diff --git a/prototype/genotype.py b/prototype/genotype.py new file mode 100644 index 000000000..38c9abfff --- /dev/null +++ b/prototype/genotype.py @@ -0,0 +1,88 @@ +"""Genotype class.""" + +from __future__ import annotations + +from dataclasses import dataclass + +import multineat +import numpy as np + +from revolve2.modular_robot import ModularRobot +from revolve2.standards.genotypes.cppnwin.modular_robot import BrainGenotypeCpg +from revolve2.standards.genotypes.cppnwin.modular_robot.v2 import BodyGenotypeV2 + + +@dataclass +class Genotype(BodyGenotypeV2, BrainGenotypeCpg): + """A genotype for a body and brain using CPPN.""" + + @classmethod + def random( + cls, + innov_db_body: multineat.InnovationDatabase, + innov_db_brain: multineat.InnovationDatabase, + rng: np.random.Generator, + ) -> Genotype: + """ + Create a random genotype. + + :param innov_db_body: Multineat innovation database for the body. See Multineat library. + :param innov_db_brain: Multineat innovation database for the brain. See Multineat library. + :param rng: Random number generator. + :returns: The created genotype. + """ + body = cls.random_body(innov_db_body, rng) + brain = cls.random_brain(innov_db_brain, rng) + + return Genotype(body=body.body, brain=brain.brain) + + def mutate( + self, + innov_db_body: multineat.InnovationDatabase, + innov_db_brain: multineat.InnovationDatabase, + rng: np.random.Generator, + ) -> Genotype: + """ + Mutate this genotype. + + This genotype will not be changed; a mutated copy will be returned. + + :param innov_db_body: Multineat innovation database for the body. See Multineat library. + :param innov_db_brain: Multineat innovation database for the brain. See Multineat library. + :param rng: Random number generator. + :returns: A mutated copy of the provided genotype. + """ + body = self.mutate_body(innov_db_body, rng) + brain = self.mutate_brain(innov_db_brain, rng) + + return Genotype(body=body.body, brain=brain.brain) + + @classmethod + def crossover( + cls, + parent1: Genotype, + parent2: Genotype, + rng: np.random.Generator, + ) -> Genotype: + """ + Perform crossover between two genotypes. + + :param parent1: The first genotype. + :param parent2: The second genotype. + :param rng: Random number generator. + :returns: A newly created genotype. + """ + body = cls.crossover_body(parent1, parent2, rng) + brain = cls.crossover_brain(parent1, parent2, rng) + + return Genotype(body=body.body, brain=brain.brain) + + def develop(self) -> ModularRobot: + """ + Develop the genotype into a modular robot. + + :returns: The created robot. + """ + body = self.develop_body() + brain = self.develop_brain(body=body) + return ModularRobot(body=body, brain=brain) diff --git a/prototype/individual.py b/prototype/individual.py new file mode 100644 index 000000000..cb254b57f --- /dev/null +++ b/prototype/individual.py @@ -0,0 +1,13 @@ +"""Individual class.""" + +from dataclasses import dataclass + +from genotype import Genotype + + +@dataclass +class Individual: + """An individual in a population.""" + + genotype: Genotype + fitness: float diff --git a/prototype/main.py b/prototype/main.py new file mode 100644 index 000000000..1e9094c51 --- /dev/null +++ b/prototype/main.py @@ -0,0 +1,104 @@ +import logging +import pickle +from tqdm import tqdm + +import config +import multineat +from evaluator import Evaluator +from genotype import Genotype +from individual import Individual + +from revolve2.experimentation.evolution import ModularRobotEvolution +from revolve2.experimentation.logging import setup_logging +from revolve2.experimentation.rng import make_rng_time_seed +from ea import ParentSelector, SurvivorSelector, CrossoverReproducer +from tools import find_best_robot + + +def run() -> None: + # Set up logging. + setup_logging(file_name="log.txt") + + rng = make_rng_time_seed() + + # initialize CPPN innovation databases (brain & body) + innov_db_body = multineat.InnovationDatabase() + innov_db_brain = multineat.InnovationDatabase() + + """ + - evaluator: Allows us to evaluate a population of modular robots. + - parent_selector: Allows us to select parents from a population of modular robots. + - survivor_selector: Allows us to select survivors from a population. + - crossover_reproducer: Allows us to generate offspring from parents. + - modular_robot_evolution: The evolutionary process as a object that can be iterated. + """ + # initialize evolutionary process components + evaluator = Evaluator(headless=True, num_simulators=config.NUM_SIMULATORS) + parent_selector = ParentSelector(offspring_size=config.OFFSPRING_SIZE, rng=rng) + survivor_selector = SurvivorSelector(rng=rng) + crossover_reproducer = CrossoverReproducer( + rng=rng, innov_db_body=innov_db_body, innov_db_brain=innov_db_brain + ) + + modular_robot_evolution = ModularRobotEvolution( + parent_selection=parent_selector, + survivor_selection=survivor_selector, + evaluator=evaluator, + reproducer=crossover_reproducer, + ) + + # Create an initial population as we cant start from nothing. + logging.info("Generating initial population.") + initial_genotypes = [ + Genotype.random( + innov_db_body=innov_db_body, + innov_db_brain=innov_db_brain, + rng=rng, + ) + for _ in range(config.POPULATION_SIZE) + ] + + # Evaluate the initial population. + logging.info("Evaluating initial population.") + initial_fitnesses = evaluator.evaluate(initial_genotypes) + + # Create a population of individuals, combining genotype with fitness. + population = [ + Individual(genotype, fitness) + for genotype, fitness in zip(initial_genotypes, initial_fitnesses, strict=True) + ] + + # Save the best robot + best_robot = find_best_robot(None, population) + + # Set the current generation to 0. + generation_index = 0 + + # Start the actual optimization process. + logging.info("Start optimization process.") + while generation_index < config.NUM_GENERATIONS: + logging.info(f"Generation {generation_index + 1} / {config.NUM_GENERATIONS}.") + + """ + In contrast to the previous example we do not explicitly stat the order of operations here, but let the ModularRobotEvolution object do the scheduling. + This does not give a performance boost, but is more readable and less prone to errors due to mixing up the order. + + Not that you are not restricted to the classical ModularRobotEvolution object, since you can adjust the step function as you want. + """ + population = modular_robot_evolution.step( + population + ) # Step the evolution forward. + + # Find the new best robot + best_robot = find_best_robot(best_robot, population) + + logging.info(f"Best robot until now: {best_robot.fitness}") + logging.info(f"Genotype pickle: {pickle.dumps(best_robot)!r}") + + # Increase the generation index counter. + generation_index += 1 + +if __name__ == "__main__": + run() + + diff --git a/prototype/rerun.py b/prototype/rerun.py new file mode 100644 index 000000000..e1858e26e --- /dev/null +++ b/prototype/rerun.py @@ -0,0 +1,28 @@ +"""Rerun a robot with given body and parameters.""" + +import logging +import pickle + +from evaluator import Evaluator +from individual import Individual + +from revolve2.experimentation.logging import setup_logging + +# This is a pickled genotype we optimized. +# You can copy your own parameters from the optimization output log. +PICKLED_GENOTYPE = b'\x80\x04\x95\x84\x13\x00\x00\x00\x00\x00\x00\x8c\nindividual\x94\x8c\nIndividual\x94\x93\x94)\x81\x94}\x94(\x8c\x08genotype\x94h\x05\x8c\x08Genotype\x94\x93\x94)\x81\x94}\x94(\x8c\x05brain\x94\x8cGrevolve2.standards.genotypes.cppnwin._multineat_genotype_pickle_wrapper\x94\x8c\x1eMultineatGenotypePickleWrapper\x94\x93\x94)\x81\x94X\xf1\x08\x00\x00{\n"value0":{\n"value0":0,\n"value1":[\n{\n"value0":{\n"value0":[]\n},\n"value1":1,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":2,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":3,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":4,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":5,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":6,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":7,\n"value2":2,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":8,\n"value2":4,\n"value3":3.025,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":9,\n"value10":1.0\n}\n],\n"value2":[\n{\n"value0":{\n"value0":[]\n},\n"value1":1,\n"value2":8,\n"value3":1,\n"value4":false,\n"value5":-0.4544869952184381\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":2,\n"value2":8,\n"value3":2,\n"value4":false,\n"value5":0.676048083864304\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":3,\n"value2":8,\n"value3":3,\n"value4":false,\n"value5":-0.5953685570925378\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":4,\n"value2":8,\n"value3":4,\n"value4":false,\n"value5":0.009840582764092401\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":5,\n"value2":8,\n"value3":5,\n"value4":false,\n"value5":0.5120174982690214\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":6,\n"value2":8,\n"value3":6,\n"value4":false,\n"value5":-0.7356904567062617\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":7,\n"value2":8,\n"value3":7,\n"value4":false,\n"value5":-0.5692049290039457\n}\n],\n"value3":7,\n"value4":1,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0.0,\n"value9":false,\n"value10":16384,\n"value11":{\n"value0":[]\n},\n"value12":8,\n"value13":7\n}\n}\x94b\x8c\x04body\x94h\r)\x81\x94X\xa7\t\x00\x00{\n"value0":{\n"value0":0,\n"value1":[\n{\n"value0":{\n"value0":[]\n},\n"value1":1,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":2,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":3,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":4,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":5,\n"value2":2,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":6,\n"value2":4,\n"value3":3.025,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":10,\n"value10":1.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":7,\n"value2":4,\n"value3":3.025,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":10,\n"value10":1.0\n}\n],\n"value2":[\n{\n"value0":{\n"value0":[]\n},\n"value1":1,\n"value2":6,\n"value3":1,\n"value4":false,\n"value5":0.5456837443101523\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":2,\n"value2":6,\n"value3":2,\n"value4":false,\n"value5":0.2225766151110867\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":3,\n"value2":6,\n"value3":3,\n"value4":false,\n"value5":0.13869819784354954\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":4,\n"value2":6,\n"value3":4,\n"value4":false,\n"value5":0.16110704268670845\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":5,\n"value2":6,\n"value3":5,\n"value4":false,\n"value5":0.044524845315174147\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":1,\n"value2":7,\n"value3":6,\n"value4":false,\n"value5":0.26762550975363866\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":2,\n"value2":7,\n"value3":7,\n"value4":false,\n"value5":0.3432962942367168\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":3,\n"value2":7,\n"value3":8,\n"value4":false,\n"value5":-0.8363667190793456\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":4,\n"value2":7,\n"value3":9,\n"value4":false,\n"value5":-0.18570450336414635\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":5,\n"value2":7,\n"value3":10,\n"value4":false,\n"value5":-0.9900463485351121\n}\n],\n"value3":5,\n"value4":2,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0.0,\n"value9":false,\n"value10":16384,\n"value11":{\n"value0":[]\n},\n"value12":7,\n"value13":10\n}\n}\x94bub\x8c\x07fitness\x94G?\xe3\'\x1c\x0e\xc9`\x8aub.' + + +if __name__ == "__main__": + setup_logging() + + individual: Individual = pickle.loads(PICKLED_GENOTYPE) + + logging.info(f"Fitness from pickle: {individual.fitness}") + + evaluator = Evaluator( + headless=False, + num_simulators=1, + ) + fitness = evaluator.evaluate([individual.genotype])[0] + logging.info(f"Rerun fitness: {fitness}") \ No newline at end of file diff --git a/prototype/tools.py b/prototype/tools.py new file mode 100644 index 000000000..b52083b64 --- /dev/null +++ b/prototype/tools.py @@ -0,0 +1,18 @@ +from individual import Individual + +#contains functionality + +def find_best_robot( + current_best: Individual | None, population: list[Individual] +) -> Individual: + """ + Return the best robot between the population and the current best individual. + + :param current_best: The current best individual. + :param population: The population. + :returns: The best individual. + """ + return max( + population if current_best is None else [current_best] + population, + key=lambda x: x.fitness, + ) \ No newline at end of file From 458343024af1fd7b925c39baceda5b95ead566df Mon Sep 17 00:00:00 2001 From: DanyPaisDaSilva Date: Fri, 14 Mar 2025 19:18:16 +0100 Subject: [PATCH 4/7] added evaluator with new x_displacement fitness function --- prototype/evaluator.py | 93 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 prototype/evaluator.py diff --git a/prototype/evaluator.py b/prototype/evaluator.py new file mode 100644 index 000000000..0566a022c --- /dev/null +++ b/prototype/evaluator.py @@ -0,0 +1,93 @@ +"""Evaluator class.""" + +from genotype import Genotype + +from revolve2.experimentation.evolution.abstract_elements import Evaluator as Eval +from revolve2.modular_robot_simulation import ( + ModularRobotScene, + Terrain, + simulate_scenes, +) +from revolve2.simulators.mujoco_simulator import LocalSimulator +from revolve2.standards import terrains +from revolve2.standards.simulation_parameters import make_standard_batch_parameters +import math +from revolve2.modular_robot_simulation import ModularRobotSimulationState + + +def x_displacement( + begin_state: ModularRobotSimulationState, end_state: ModularRobotSimulationState +) -> float: + """ + Calculate the distance traveled on the x-plane by a single modular robot. + + :param begin_state: Begin state of the robot. + :param end_state: End state of the robot. + :returns: The calculated fitness. + """ + begin_position = begin_state.get_pose().position + end_position = end_state.get_pose().position + return math.sqrt( + (begin_position.x - end_position.x) ** 2 + ) + + +class Evaluator(Eval): + """Provides evaluation of robots.""" + + _simulator: LocalSimulator + _terrain: Terrain + + def __init__( + self, + headless: bool, + num_simulators: int, + ) -> None: + """ + Initialize this object. + + :param headless: `headless` parameter for the physics simulator. + :param num_simulators: `num_simulators` parameter for the physics simulator. + """ + self._simulator = LocalSimulator( + headless=headless, num_simulators=num_simulators + ) + self._terrain = terrains.flat() + + def evaluate( + self, + population: list[Genotype], + ) -> list[float]: + """ + Evaluate multiple robots. + + Fitness is the distance traveled on the xy plane. + + :param population: The robots to simulate. + :returns: Fitnesses of the robots. + """ + robots = [genotype.develop() for genotype in population] + # Create the scenes. + scenes = [] + for robot in robots: + scene = ModularRobotScene(terrain=self._terrain) + scene.add_robot(robot) + scenes.append(scene) + + # Simulate all scenes. + scene_states = simulate_scenes( + simulator=self._simulator, + batch_parameters=make_standard_batch_parameters(), + scenes=scenes, + ) + + # Calculate the x displacement (only one direction) + x_displacements = [ + x_displacement( + states[0].get_modular_robot_simulation_state(robot), + states[-1].get_modular_robot_simulation_state(robot), + ) + for robot, states in zip(robots, scene_states) + ] + + return x_displacements From e0bbb38708443d6c5791db616b2d89d301cf8cb1 Mon Sep 17 00:00:00 2001 From: DanyPaisDaSilva Date: Fri, 14 Mar 2025 19:18:36 +0100 Subject: [PATCH 5/7] fixed typos --- .../experimentation/optimization/ea/selection/_tournament.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/experimentation/revolve2/experimentation/optimization/ea/selection/_tournament.py b/experimentation/revolve2/experimentation/optimization/ea/selection/_tournament.py index 42750461f..d842d48a0 100644 --- a/experimentation/revolve2/experimentation/optimization/ea/selection/_tournament.py +++ b/experimentation/revolve2/experimentation/optimization/ea/selection/_tournament.py @@ -10,9 +10,9 @@ def tournament(rng: np.random.Generator, fitnesses: list[Fitness], k: int) -> in Perform tournament selection and return the index of the best individual. :param rng: Random number generator. - :param fitnesses: List of finesses of individuals that joint the tournamente. + :param fitnesses: List of finesses of individuals that joint the tournament. :param k: Amount of individuals to participate in tournament. - :returns: The index of te individual that won the tournament. + :returns: The index of the individual that won the tournament. """ assert len(fitnesses) >= k From aebba4873c213aec57c606b0adf6fbcf7d4e9b36 Mon Sep 17 00:00:00 2001 From: DanyPaisDaSilva Date: Fri, 14 Mar 2025 19:22:02 +0100 Subject: [PATCH 6/7] fixed typo and corrected return type annotation to int for indices (was float previously) --- .../optimization/ea/selection/_multiple_unique.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/experimentation/revolve2/experimentation/optimization/ea/selection/_multiple_unique.py b/experimentation/revolve2/experimentation/optimization/ea/selection/_multiple_unique.py index 95b19074b..4af074d4a 100644 --- a/experimentation/revolve2/experimentation/optimization/ea/selection/_multiple_unique.py +++ b/experimentation/revolve2/experimentation/optimization/ea/selection/_multiple_unique.py @@ -12,11 +12,11 @@ def multiple_unique( population: list[TIndividual], fitnesses: list[TFitness], selection_function: Callable[[list[TIndividual], list[TFitness]], int], -) -> npt.NDArray[np.float_]: +) -> npt.NDArray[np.int_]: """ Select multiple distinct individuals from a population using the provided selection function. - :param selection_size: Amount of of individuals to select. + :param selection_size: Amount of individuals to select. :param population: List of individuals to select from. :param fitnesses: Fitnesses of the population. :param selection_function: Function that select a single individual from a population. ([TIndividual], [TFitness]) -> index. From 0b1a395cebd17d36b79d7248bbc2f303af2d96d3 Mon Sep 17 00:00:00 2001 From: DanyPaisDaSilva Date: Fri, 14 Mar 2025 19:49:38 +0100 Subject: [PATCH 7/7] mostly changed some text --- prototype/evaluator.py | 2 +- prototype/main.py | 49 +++++++++++++++++++----------------------- prototype/rerun.py | 3 +-- prototype/tools.py | 2 +- 4 files changed, 25 insertions(+), 31 deletions(-) diff --git a/prototype/evaluator.py b/prototype/evaluator.py index 0566a022c..58859e28c 100644 --- a/prototype/evaluator.py +++ b/prototype/evaluator.py @@ -61,7 +61,7 @@ def evaluate( """ Evaluate multiple robots. - Fitness is the distance traveled on the xy plane. + Fitness is the distance traveled on the x plane. :param population: The robots to simulate. :returns: Fitnesses of the robots. diff --git a/prototype/main.py b/prototype/main.py index 1e9094c51..e903a29bb 100644 --- a/prototype/main.py +++ b/prototype/main.py @@ -1,6 +1,5 @@ import logging import pickle -from tqdm import tqdm import config import multineat @@ -16,22 +15,19 @@ def run() -> None: - # Set up logging. - setup_logging(file_name="log.txt") + ######### + # SETUP # + ######### + + # initialize logging and rng + setup_logging(file_name="log.txt") rng = make_rng_time_seed() # initialize CPPN innovation databases (brain & body) innov_db_body = multineat.InnovationDatabase() innov_db_brain = multineat.InnovationDatabase() - """ - - evaluator: Allows us to evaluate a population of modular robots. - - parent_selector: Allows us to select parents from a population of modular robots. - - survivor_selector: Allows us to select survivors from a population. - - crossover_reproducer: Allows us to generate offspring from parents. - - modular_robot_evolution: The evolutionary process as a object that can be iterated. - """ # initialize evolutionary process components evaluator = Evaluator(headless=True, num_simulators=config.NUM_SIMULATORS) parent_selector = ParentSelector(offspring_size=config.OFFSPRING_SIZE, rng=rng) @@ -40,14 +36,15 @@ def run() -> None: rng=rng, innov_db_body=innov_db_body, innov_db_brain=innov_db_brain ) - modular_robot_evolution = ModularRobotEvolution( + # initialize ea + evolver = ModularRobotEvolution( parent_selection=parent_selector, survivor_selection=survivor_selector, evaluator=evaluator, reproducer=crossover_reproducer, ) - # Create an initial population as we cant start from nothing. + # generate initial genotypes logging.info("Generating initial population.") initial_genotypes = [ Genotype.random( @@ -58,38 +55,36 @@ def run() -> None: for _ in range(config.POPULATION_SIZE) ] - # Evaluate the initial population. + # evaluate the initial genotypes logging.info("Evaluating initial population.") initial_fitnesses = evaluator.evaluate(initial_genotypes) - # Create a population of individuals, combining genotype with fitness. + # create population of individuals with fitness. population = [ Individual(genotype, fitness) for genotype, fitness in zip(initial_genotypes, initial_fitnesses, strict=True) ] - # Save the best robot + # save best robot best_robot = find_best_robot(None, population) - # Set the current generation to 0. + # set the current generation to 0 generation_index = 0 - # Start the actual optimization process. + + ########################### + # START EVOLUTIONARY LOOP # + ########################### + + # start optimization process. logging.info("Start optimization process.") while generation_index < config.NUM_GENERATIONS: logging.info(f"Generation {generation_index + 1} / {config.NUM_GENERATIONS}.") - """ - In contrast to the previous example we do not explicitly stat the order of operations here, but let the ModularRobotEvolution object do the scheduling. - This does not give a performance boost, but is more readable and less prone to errors due to mixing up the order. - - Not that you are not restricted to the classical ModularRobotEvolution object, since you can adjust the step function as you want. - """ - population = modular_robot_evolution.step( - population - ) # Step the evolution forward. + # step the evolution forward + population = evolver.step(population) - # Find the new best robot + # find the new best robot best_robot = find_best_robot(best_robot, population) logging.info(f"Best robot until now: {best_robot.fitness}") diff --git a/prototype/rerun.py b/prototype/rerun.py index e1858e26e..4a151d58e 100644 --- a/prototype/rerun.py +++ b/prototype/rerun.py @@ -10,14 +10,13 @@ # This is a pickled genotype we optimized. # You can copy your own parameters from the optimization output log. -PICKLED_GENOTYPE = b'\x80\x04\x95\x84\x13\x00\x00\x00\x00\x00\x00\x8c\nindividual\x94\x8c\nIndividual\x94\x93\x94)\x81\x94}\x94(\x8c\x08genotype\x94h\x05\x8c\x08Genotype\x94\x93\x94)\x81\x94}\x94(\x8c\x05brain\x94\x8cGrevolve2.standards.genotypes.cppnwin._multineat_genotype_pickle_wrapper\x94\x8c\x1eMultineatGenotypePickleWrapper\x94\x93\x94)\x81\x94X\xf1\x08\x00\x00{\n"value0":{\n"value0":0,\n"value1":[\n{\n"value0":{\n"value0":[]\n},\n"value1":1,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":2,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":3,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":4,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":5,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":6,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":7,\n"value2":2,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":8,\n"value2":4,\n"value3":3.025,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":9,\n"value10":1.0\n}\n],\n"value2":[\n{\n"value0":{\n"value0":[]\n},\n"value1":1,\n"value2":8,\n"value3":1,\n"value4":false,\n"value5":-0.4544869952184381\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":2,\n"value2":8,\n"value3":2,\n"value4":false,\n"value5":0.676048083864304\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":3,\n"value2":8,\n"value3":3,\n"value4":false,\n"value5":-0.5953685570925378\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":4,\n"value2":8,\n"value3":4,\n"value4":false,\n"value5":0.009840582764092401\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":5,\n"value2":8,\n"value3":5,\n"value4":false,\n"value5":0.5120174982690214\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":6,\n"value2":8,\n"value3":6,\n"value4":false,\n"value5":-0.7356904567062617\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":7,\n"value2":8,\n"value3":7,\n"value4":false,\n"value5":-0.5692049290039457\n}\n],\n"value3":7,\n"value4":1,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0.0,\n"value9":false,\n"value10":16384,\n"value11":{\n"value0":[]\n},\n"value12":8,\n"value13":7\n}\n}\x94b\x8c\x04body\x94h\r)\x81\x94X\xa7\t\x00\x00{\n"value0":{\n"value0":0,\n"value1":[\n{\n"value0":{\n"value0":[]\n},\n"value1":1,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":2,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":3,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":4,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":5,\n"value2":2,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":6,\n"value2":4,\n"value3":3.025,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":10,\n"value10":1.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":7,\n"value2":4,\n"value3":3.025,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":10,\n"value10":1.0\n}\n],\n"value2":[\n{\n"value0":{\n"value0":[]\n},\n"value1":1,\n"value2":6,\n"value3":1,\n"value4":false,\n"value5":0.5456837443101523\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":2,\n"value2":6,\n"value3":2,\n"value4":false,\n"value5":0.2225766151110867\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":3,\n"value2":6,\n"value3":3,\n"value4":false,\n"value5":0.13869819784354954\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":4,\n"value2":6,\n"value3":4,\n"value4":false,\n"value5":0.16110704268670845\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":5,\n"value2":6,\n"value3":5,\n"value4":false,\n"value5":0.044524845315174147\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":1,\n"value2":7,\n"value3":6,\n"value4":false,\n"value5":0.26762550975363866\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":2,\n"value2":7,\n"value3":7,\n"value4":false,\n"value5":0.3432962942367168\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":3,\n"value2":7,\n"value3":8,\n"value4":false,\n"value5":-0.8363667190793456\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":4,\n"value2":7,\n"value3":9,\n"value4":false,\n"value5":-0.18570450336414635\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":5,\n"value2":7,\n"value3":10,\n"value4":false,\n"value5":-0.9900463485351121\n}\n],\n"value3":5,\n"value4":2,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0.0,\n"value9":false,\n"value10":16384,\n"value11":{\n"value0":[]\n},\n"value12":7,\n"value13":10\n}\n}\x94bub\x8c\x07fitness\x94G?\xe3\'\x1c\x0e\xc9`\x8aub.' +PICKLED_GENOTYPE = b'\x80\x04\x95\x88\x13\x00\x00\x00\x00\x00\x00\x8c\nindividual\x94\x8c\nIndividual\x94\x93\x94)\x81\x94}\x94(\x8c\x08genotype\x94h\x05\x8c\x08Genotype\x94\x93\x94)\x81\x94}\x94(\x8c\x05brain\x94\x8cGrevolve2.standards.genotypes.cppnwin._multineat_genotype_pickle_wrapper\x94\x8c\x1eMultineatGenotypePickleWrapper\x94\x93\x94)\x81\x94X\xf4\x08\x00\x00{\n"value0":{\n"value0":0,\n"value1":[\n{\n"value0":{\n"value0":[]\n},\n"value1":1,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":2,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":3,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":4,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":5,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":6,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":7,\n"value2":2,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":8,\n"value2":4,\n"value3":3.025,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":9,\n"value10":1.0\n}\n],\n"value2":[\n{\n"value0":{\n"value0":[]\n},\n"value1":1,\n"value2":8,\n"value3":1,\n"value4":false,\n"value5":-0.06794864201394263\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":2,\n"value2":8,\n"value3":2,\n"value4":false,\n"value5":0.17108417988846415\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":3,\n"value2":8,\n"value3":3,\n"value4":false,\n"value5":0.24011150499138546\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":4,\n"value2":8,\n"value3":4,\n"value4":false,\n"value5":-0.16464075583241637\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":5,\n"value2":8,\n"value3":5,\n"value4":false,\n"value5":0.22977633480239496\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":6,\n"value2":8,\n"value3":6,\n"value4":false,\n"value5":0.3248419928837503\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":7,\n"value2":8,\n"value3":7,\n"value4":false,\n"value5":-0.7515674507248752\n}\n],\n"value3":7,\n"value4":1,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0.0,\n"value9":false,\n"value10":16384,\n"value11":{\n"value0":[]\n},\n"value12":8,\n"value13":7\n}\n}\x94b\x8c\x04body\x94h\r)\x81\x94X\xa8\t\x00\x00{\n"value0":{\n"value0":0,\n"value1":[\n{\n"value0":{\n"value0":[]\n},\n"value1":1,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":2,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":3,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":4,\n"value2":1,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":5,\n"value2":2,\n"value3":0.0,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":1,\n"value10":0.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":6,\n"value2":4,\n"value3":3.025,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":10,\n"value10":1.0\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":7,\n"value2":4,\n"value3":3.025,\n"value4":0.0,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0,\n"value9":10,\n"value10":1.0\n}\n],\n"value2":[\n{\n"value0":{\n"value0":[]\n},\n"value1":1,\n"value2":6,\n"value3":1,\n"value4":false,\n"value5":0.7574132188798952\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":2,\n"value2":6,\n"value3":2,\n"value4":false,\n"value5":0.6307925509290383\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":3,\n"value2":6,\n"value3":3,\n"value4":false,\n"value5":0.01637625737534451\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":4,\n"value2":6,\n"value3":4,\n"value4":false,\n"value5":-0.37919500097307026\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":5,\n"value2":6,\n"value3":5,\n"value4":false,\n"value5":-0.6202562022299705\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":1,\n"value2":7,\n"value3":6,\n"value4":false,\n"value5":0.26930546284060899\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":2,\n"value2":7,\n"value3":7,\n"value4":false,\n"value5":-0.08117748536134356\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":3,\n"value2":7,\n"value3":8,\n"value4":false,\n"value5":0.8408206873082067\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":4,\n"value2":7,\n"value3":9,\n"value4":false,\n"value5":-0.19255428862439867\n},\n{\n"value0":{\n"value0":[]\n},\n"value1":5,\n"value2":7,\n"value3":10,\n"value4":false,\n"value5":-0.5920272155347908\n}\n],\n"value3":5,\n"value4":2,\n"value5":0.0,\n"value6":0.0,\n"value7":0,\n"value8":0.0,\n"value9":false,\n"value10":16384,\n"value11":{\n"value0":[]\n},\n"value12":7,\n"value13":10\n}\n}\x94bub\x8c\x07fitness\x94G?\xec$M\x9fy\x1a2ub.' if __name__ == "__main__": setup_logging() individual: Individual = pickle.loads(PICKLED_GENOTYPE) - logging.info(f"Fitness from pickle: {individual.fitness}") evaluator = Evaluator( diff --git a/prototype/tools.py b/prototype/tools.py index b52083b64..89688e666 100644 --- a/prototype/tools.py +++ b/prototype/tools.py @@ -1,6 +1,6 @@ from individual import Individual -#contains functionality +# contains functionality def find_best_robot( current_best: Individual | None, population: list[Individual]