-
Notifications
You must be signed in to change notification settings - Fork 10
/
population.py
executable file
·137 lines (112 loc) · 4.54 KB
/
population.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
from typing import Callable, List, Optional, Union
import numpy as np
from .genome import Genome
from .individual import IndividualBase, IndividualMultiGenome, IndividualSingleGenome
class Population:
"""
A population of individuals.
"""
def __init__(
self,
n_parents: int = 4,
seed: int = 1234,
genome_params: Optional[Union[dict, List[dict]]] = None,
individual_init: Optional[Callable[[IndividualBase], IndividualBase]] = None,
) -> None:
"""Init function.
Parameters
----------
n_parents : int, optional
Number of parent individuals. Defaults to 4.
seed : int, optional
Seed for internal random number generator. Defaults to 1234.
genome_params : dict
Parameters for the genomes of the population's individuals. Defaults to None.
If None, default parameters defined in Genome are used.
individual_init: callable, optional
If not None, called for each individual of the initial
parent population, for example, to set the dna of parents. Defaults to None.
"""
self.n_parents = n_parents # number of individuals in parent population
self.seed = seed
self.rng = np.random.RandomState(self.seed)
self._genome_params = genome_params
self._parents: List[IndividualBase] = [] # list of parent individuals
# keeps track of the number of generations, increases with
# every new offspring generation
self.generation = 0
self._max_idx = 0 # keeps track of maximal idx in population used to label individuals
if individual_init is not None and not callable(individual_init):
raise TypeError("individual_init must be a callable")
self._generate_random_parent_population(individual_init)
@property
def champion(self) -> IndividualBase:
"""Return parent with the highest fitness.
"""
def key(ind: IndividualBase) -> float:
return ind.fitness
return max(self._parents, key=key)
@property
def parents(self) -> List[IndividualBase]:
return self._parents
@parents.setter
def parents(self, new_parents: List[IndividualBase]) -> None:
self.generation += 1
self._parents = new_parents
def __getitem__(self, idx: int) -> IndividualBase:
return self._parents[idx]
def _generate_random_parent_population(
self, individual_init: Optional[Callable[[IndividualBase], IndividualBase]] = None
) -> None:
parents: List[IndividualBase] = []
for _ in range(self.n_parents):
ind = self.generate_random_individual()
if individual_init is not None:
ind = individual_init(ind)
parents.append(ind)
self._parents = parents
def get_idx_for_new_individual(self) -> int:
idx = self._max_idx
self._max_idx += 1
return idx
def generate_random_individual(self) -> IndividualBase:
if self._genome_params is None or isinstance(self._genome_params, dict):
genome: Genome
if self._genome_params is None:
genome = Genome()
else:
genome = Genome(**self._genome_params)
individual_s = IndividualSingleGenome(
genome=genome
) # type: IndividualBase # indicates to mypy that
# individual_s is instance of a child class of
# IndividualBase
ind = individual_s
else:
genomes: List[Genome] = [Genome(**gd) for gd in self._genome_params]
individual_m = IndividualMultiGenome(
genome=genomes
) # type: IndividualBase # indicates to mypy that
# individual_m is an instance of a child class of
# IndividualBase
ind = individual_m
ind.randomize_genome(self.rng)
ind.idx = self.get_idx_for_new_individual()
ind.parent_idx = -1
return ind
def fitness_parents(self) -> List[Optional[float]]:
"""Return fitness for all parents of the population.
Returns
----------
List[float]
List of fitness values for all parents.
"""
return [ind.fitness for ind in self._parents]
def reorder_genome(self) -> None:
""" Reorders the genome for all parents in the population
Returns
---------
None
"""
for parent in self.parents:
parent.reorder_genome(self.rng)