-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconfig.py
175 lines (141 loc) · 6.35 KB
/
config.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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
"""
"""
import random
import functools
from functools import partial
from collections import namedtuple
import utils
import mutator
import scoring
import crossing
import stepping
import creation
import selection
import reproduction
class Configuration:
"""A Configuration object is a set of functions for each role
in the genetic algorithm.
It is basically a group of callables that will be used
by a genetic algorithm.
In other word, a Configuration instance yield different functions
at each access to the same attribute (score, select,…), because
choosing them randomly in the set given to the constructor.
It provides the specialize method, that return a new configuration
with only one choice of function, allowing one to generate a configuration
"""
def __init__(self, score:callable=None, select:callable=None,
mutate:callable=None, reproduce:callable=None,
cross: callable=None, create:callable=None, step:callable=None):
score = score or scoring.default_functions()
select = select or selection.default_functions()
mutate = mutate or mutator.default_functions()
reproduce = reproduce or reproduction.default_functions()
cross = cross or crossing.default_functions()
create = create or creation.default_functions()
step = step or stepping.default_functions()
self._score = (score,) if callable(score) else tuple(score)
self._select = (select,) if callable(select) else tuple(select)
self._mutate = (mutate,) if callable(mutate) else tuple(mutate)
self._reproduce = (reproduce,) if callable(reproduce) else tuple(reproduce)
self._cross = (cross,) if callable(cross) else tuple(cross)
self._create = (create,) if callable(create) else tuple(create)
self._step = (step,) if callable(step) else tuple(step)
def specialize(self) -> 'Configuration':
"""Return a new Configuration where all sets of functions are fixed to a
unique function.
This is a great way to get a specific Configuration, in order to
get the same GA implementation for multiple iterations or simulations.
Note that functions themselves are not modified ; for instance the
mutate method will not be fixed to a specific mutation, as the mutate
methods sent to the Configuration object usually are functions calling
randomly one among many mutators.
"""
return Configuration(score=self.score, select=self.select,
mutate=self.mutate, reproduce=self.reproduce,
cross=self.cross, create=self.create, step=self.step)
@property
def score(self) -> callable:
return random.choice(self._score)
@property
def select(self) -> callable:
return random.choice(self._select)
@property
def mutate(self) -> callable:
return random.choice(self._mutate)
@property
def reproduce(self) -> callable:
return random.choice(self._reproduce)
@property
def cross(self) -> callable:
return random.choice(self._cross)
@property
def create(self) -> callable:
return random.choice(self._create)
@property
def step(self) -> callable:
return random.choice(self._step)
def __str__(self):
out = ''
for funcs, module, name in ((self._score, scoring, 'SCORE'),
(self._select, selection, 'SELECT'),
(self._mutate, mutator, 'MUTATE'),
(self._reproduce, reproduction, 'REPRODUCE'),
(self._cross, crossing, 'CROSS'),
(self._create, creation, 'CREATE'),
(self._step, stepping, 'STEP')):
out += '{}:\n'.format(name)
for func in funcs:
code = utils.key_of(func, module.named_functions(), '')
out += '\t{} {}\n'.format(utils.pretty_func(func), code)
return out
@staticmethod
def from_codes(score:str=None, select:str=None, mutate:str=None,
reproduce:str=None, create:str=None, step:str=None):
"""Create configuration from given codes of named functions.
Code can be either None, a valid codename, or an iterable of
valid codename.
if a code is None, the default will be used.
"""
kwargs = {}
for param, value, module in (('score', score, scoring),
('select', select, selection),
('mutate', mutate, mutator),
('reproduce', reproduce, reproduction),
('cross', cross, crossing),
('create', create, creation),
('step', step, stepping)):
if isinstance(value, str): # it's a code name
kwargs[param] = module.named_functions(value)
elif value is None: # user wants default
kwargs[param] = module.default_functions()
else: # it's an iterable of code name
kwargs[param] = tuple(module.named_functions(name) for name in value)
return Configuration(**kwargs)
@staticmethod
def recipe_showing_elistism():
"""Reproduction of the initial configuration that shows
the side-effect of elitism
"""
return Configuration(
score=scoring.io_comparison,
select=partial(selection.ranking_slices, pattern=[(0, 40)])
)
@staticmethod
def recipe_using_standard_methods():
"""Reproduction of the typical genetic algorithm implementation.
"""
return Configuration(
score=scoring.named_functions('IOCBM'),
select=selection.named_functions('RSD'),
mutate=mutator.all_mutators(),
reproduce=reproduction.named_functions('PRPW'),
create=creation.all_methods_equally,
)
@staticmethod
def recipe_best_solution_so_far():
"""Reproduction of the best known configuration.
"""
return Configuration(
score=scoring.io_comparison,
select=partial(selection.poolling, pool_size=10, selection_size=0.4)
)