1 | import argparse |
---|
2 | import os |
---|
3 | import sys |
---|
4 | import numpy as np |
---|
5 | |
---|
6 | #TODO add new example: steadystate.py (analogous to standard.py) OR include steadysteate as a mode in this example or in niching_novelty.py |
---|
7 | #TODO extend both standard.py and steadystate.py to support >1 criteria (using DEAP's selNSGA2() and selSPEA2()) |
---|
8 | #TODO add comments to all examples in this directory |
---|
9 | #TODO add to standard.py and steadystate.py evaluating each genotype in HOF N (configurable, default 10) times when the evolution ends instead of evaluating the last population as it is now in niching_novelty.py |
---|
10 | #TODO protect all examples against invalid genotypes (fill population until all genotypes are conrrectly evaluated). And maybe remove invalid.py if it overlaps with (is a subset of) other examples |
---|
11 | #TODO "debug" mode, displaying Step-based class names and their arguments so it is easy to see what happens during evolution |
---|
12 | |
---|
13 | from evolalg.base.lambda_step import LambdaStep |
---|
14 | from evolalg.experiment import Experiment |
---|
15 | from evolalg.fitness.fitness_step import FitnessStep |
---|
16 | from evolalg.mutation_cross.frams_cross_and_mutate import FramsCrossAndMutate |
---|
17 | from evolalg.population.frams_population import FramsPopulation |
---|
18 | from evolalg.repair.multistep import MultistepRepair |
---|
19 | from evolalg.repair.remove.field import FieldRemove |
---|
20 | from evolalg.selection.tournament import TournamentSelection |
---|
21 | from evolalg.statistics.halloffame_stats import HallOfFameStatistics |
---|
22 | from evolalg.statistics.statistics_deap import StatisticsDeap |
---|
23 | from evolalg.base.union_step import UnionStep |
---|
24 | from evolalg.utils.population_save import PopulationSave |
---|
25 | from evolalg.utils.stable_generation import StableGeneration |
---|
26 | from FramsticksLib import FramsticksLib |
---|
27 | |
---|
28 | |
---|
29 | |
---|
30 | EVAL_LIFESPAN_BEHAVIOR = False # if False, standard evaluation criteria can be used as fitness as defined by the -opt parameter. If True, it is assumed that the expdef provides custom dictionary fields in evaluation and they need to be handled specifically in python source code below (this could be parametrized in command-line too, but the syntax would be complex) |
---|
31 | |
---|
32 | |
---|
33 | def ensureDir(string): |
---|
34 | if os.path.isdir(string): |
---|
35 | return string |
---|
36 | else: |
---|
37 | raise NotADirectoryError(string) |
---|
38 | |
---|
39 | |
---|
40 | def parseArguments(): |
---|
41 | parser = argparse.ArgumentParser( |
---|
42 | description='Run this program with "python -u %s" if you want to disable buffering of its output.' % sys.argv[ |
---|
43 | 0]) |
---|
44 | parser.add_argument('-path', type=ensureDir, required=True, help='Path to the Framsticks library without trailing slash.') |
---|
45 | parser.add_argument('-opt', required=True, |
---|
46 | help='optimization criteria : vertpos, velocity, distance, vertvel, lifespan, numjoints, numparts, numneurons, numconnections (or other as long as it is provided by the .sim file and its .expdef). Single or multiple criteria.') |
---|
47 | parser.add_argument('-lib', required=False, help="Filename of .so or .dll with the Framsticks library") |
---|
48 | parser.add_argument('-genformat', required=False, default="1", |
---|
49 | help='Genetic format for the demo run, for example 4, 9, or B. If not given, f1 is assumed.') |
---|
50 | parser.add_argument('-sim', required=False, default="eval-allcriteria.sim", help="Name of the .sim file with all parameter values") |
---|
51 | parser.add_argument("-popsize", type=int, default=50, help="Population size, default 50.") |
---|
52 | parser.add_argument('-generations', type=int, default=5, help="Number of generations, default 5.") |
---|
53 | parser.add_argument('-tournament', type=int, default=5, help="Tournament size, default 5.") |
---|
54 | return parser.parse_args() |
---|
55 | |
---|
56 | |
---|
57 | def extract_fitness(ind): |
---|
58 | return ind.fitness |
---|
59 | |
---|
60 | |
---|
61 | def print_population_count(pop): |
---|
62 | print("Current:", len(pop)) |
---|
63 | return pop # Each step must return a population |
---|
64 | |
---|
65 | |
---|
66 | def main(): |
---|
67 | parsed_args = parseArguments() |
---|
68 | frams_lib = FramsticksLib(parsed_args.path, parsed_args.lib, parsed_args.sim) |
---|
69 | |
---|
70 | hall_of_fame = HallOfFameStatistics(100, "fitness") |
---|
71 | statistics_union = UnionStep([ |
---|
72 | hall_of_fame, |
---|
73 | StatisticsDeap([ |
---|
74 | ("avg", np.mean), |
---|
75 | ("stddev", np.std), |
---|
76 | ("min", np.min), |
---|
77 | ("max", np.max), |
---|
78 | ("count", len) |
---|
79 | ], extract_fitness) |
---|
80 | ]) |
---|
81 | |
---|
82 | fitness_remove = UnionStep( |
---|
83 | [ |
---|
84 | FitnessStep(frams_lib, fields={"velocity": "fitness", "data->recording": "recording"}, |
---|
85 | fields_defaults={"velocity": None, "data->recording": None}) # custom definitions and handling |
---|
86 | if EVAL_LIFESPAN_BEHAVIOR else |
---|
87 | FitnessStep(frams_lib, fields={parsed_args.opt: "fitness", }, fields_defaults={}) |
---|
88 | ] |
---|
89 | + |
---|
90 | ([FieldRemove("recording", None)] if EVAL_LIFESPAN_BEHAVIOR else []) |
---|
91 | + |
---|
92 | [print_population_count] # Stages can also be any Callable |
---|
93 | ) |
---|
94 | |
---|
95 | selection = TournamentSelection(parsed_args.tournament, copy=True, fit_attr="fitness") |
---|
96 | new_generation_steps = [ |
---|
97 | FramsCrossAndMutate(frams_lib, cross_prob=0.2, mutate_prob=0.9), |
---|
98 | fitness_remove |
---|
99 | ] |
---|
100 | |
---|
101 | generation_modifications = [ |
---|
102 | statistics_union # Or niching, novelty |
---|
103 | ] |
---|
104 | |
---|
105 | init_stages = [FramsPopulation(frams_lib, parsed_args.genformat, parsed_args.popsize), |
---|
106 | fitness_remove, # It is possible to create smaller population |
---|
107 | statistics_union] |
---|
108 | |
---|
109 | end_steps = [PopulationSave("halloffame.gen", provider=hall_of_fame.halloffame, |
---|
110 | fields={"genotype": "genotype", "fitness": "fitness", "custom": "recording"} |
---|
111 | if EVAL_LIFESPAN_BEHAVIOR |
---|
112 | else {"genotype": "genotype", "fitness": "fitness"} |
---|
113 | )] |
---|
114 | |
---|
115 | experiment = Experiment(init_population=init_stages, |
---|
116 | selection=selection, |
---|
117 | new_generation_steps=new_generation_steps, |
---|
118 | generation_modification=generation_modifications, |
---|
119 | end_steps=end_steps, |
---|
120 | population_size=parsed_args.popsize |
---|
121 | ) |
---|
122 | experiment.init() |
---|
123 | experiment.run(parsed_args.generations) |
---|
124 | for ind in hall_of_fame.halloffame: |
---|
125 | print("%g\t%s" % (ind.fitness, ind.genotype)) |
---|
126 | |
---|
127 | |
---|
128 | if __name__ == '__main__': |
---|
129 | main() |
---|