12
12
_options = dict (
13
13
popsize = ("population size" , 30 ),
14
14
maxiter = ("maximum number of generations" , 30 ),
15
- constraint_aware = ("constraint-aware optimization (True/False)" , True ),
16
15
method = ("crossover method to use, choose any from single_point, two_point, uniform, disruptive_uniform" , "uniform" ),
17
16
mutation_chance = ("chance to mutate is 1 in mutation_chance" , 20 ),
17
+ constraint_aware = ("constraint-aware optimization (True/False)" , True ),
18
18
)
19
19
20
20
21
21
def tune (searchspace : Searchspace , runner , tuning_options ):
22
22
23
23
options = tuning_options .strategy_options
24
- pop_size , generations , constraint_aware , method , mutation_chance = common .get_options (options , _options )
24
+ pop_size , generations , method , mutation_chance , constraint_aware = common .get_options (options , _options )
25
25
26
- GA = GeneticAlgorithm (pop_size , searchspace , constraint_aware , method , mutation_chance )
26
+ # if necessary adjust the popsize to a sensible value based on search space size
27
+ pop_size = min (round ((searchspace .size / generations ) * 3 ), pop_size )
27
28
28
- # if left to the default, adjust the popsize to a sensible value for small search spaces
29
- if pop_size == _options ["popsize" ][1 ]:
30
- pop_size = min (round (searchspace .size / 2 ), pop_size )
31
- else :
32
- # otherwise, just make sure it doesn't exceed the search space size
33
- pop_size = min (searchspace .size , pop_size )
29
+ GA = GeneticAlgorithm (pop_size , searchspace , method , mutation_chance , constraint_aware )
34
30
35
31
best_score = 1e20
36
32
cost_func = CostFunc (searchspace , tuning_options , runner )
33
+ num_evaluated = 0
37
34
38
35
population = GA .generate_population ()
39
36
40
37
for generation in range (generations ):
38
+ if any ([not searchspace .is_param_config_valid (tuple (dna )) for dna in population ]):
39
+ raise ValueError (f"Generation { generation } /{ generations } , population validity: { [searchspace .is_param_config_valid (tuple (dna )) for dna in population ]} " )
41
40
42
41
# determine fitness of population members
43
42
weighted_population = []
44
43
for dna in population :
45
44
try :
46
45
# if we are not constraint-aware we should check restrictions upon evaluation
47
46
time = cost_func (dna , check_restrictions = not constraint_aware )
48
- except util .StopCriterionReached as e :
47
+ num_evaluated += 1
48
+ except StopCriterionReached as e :
49
49
if tuning_options .verbose :
50
50
print (e )
51
51
return cost_func .results
@@ -68,15 +68,15 @@ def tune(searchspace: Searchspace, runner, tuning_options):
68
68
population = []
69
69
70
70
# crossover and mutate
71
- while len (population ) < pop_size :
71
+ while len (population ) < pop_size and searchspace . size > num_evaluated + len ( population ) :
72
72
dna1 , dna2 = GA .weighted_choice (weighted_population , 2 )
73
73
74
74
children = GA .crossover (dna1 , dna2 )
75
75
76
76
for child in children :
77
77
child = GA .mutate (child )
78
78
79
- if child not in population :
79
+ if child not in population and searchspace . is_param_config_valid ( tuple ( child )) :
80
80
population .append (child )
81
81
82
82
if len (population ) >= pop_size :
@@ -91,13 +91,13 @@ def tune(searchspace: Searchspace, runner, tuning_options):
91
91
92
92
class GeneticAlgorithm :
93
93
94
- def __init__ (self , pop_size , searchspace , constraint_aware = False , method = "uniform" , mutation_chance = 10 ):
94
+ def __init__ (self , pop_size , searchspace , method = "uniform" , mutation_chance = 10 , constraint_aware = True ):
95
95
self .pop_size = pop_size
96
96
self .searchspace = searchspace
97
97
self .tune_params = searchspace .tune_params .copy ()
98
- self .constraint_aware = constraint_aware
99
98
self .crossover_method = supported_methods [method ]
100
99
self .mutation_chance = mutation_chance
100
+ self .constraint_aware = constraint_aware
101
101
102
102
def generate_population (self ):
103
103
""" Constraint-aware population creation method """
0 commit comments