11
11
_options = dict (
12
12
popsize = ("population size" , 20 ),
13
13
maxiter = ("maximum number of generations" , 100 ),
14
- constraint_aware = ("constraint-aware optimization (True/False)" , False ),
14
+ constraint_aware = ("constraint-aware optimization (True/False)" , True ),
15
15
method = ("crossover method to use, choose any from single_point, two_point, uniform, disruptive_uniform" , "uniform" ),
16
16
mutation_chance = ("chance to mutate is 1 in mutation_chance" , 10 ),
17
17
)
@@ -36,7 +36,8 @@ def tune(searchspace: Searchspace, runner, tuning_options):
36
36
weighted_population = []
37
37
for dna in population :
38
38
try :
39
- time = cost_func (dna , check_restrictions = False )
39
+ # if we are not constraint-aware we should check restrictions upon evaluation
40
+ time = cost_func (dna , check_restrictions = not constraint_aware )
40
41
except util .StopCriterionReached as e :
41
42
if tuning_options .verbose :
42
43
print (e )
@@ -84,13 +85,24 @@ class GeneticAlgorithm:
84
85
def __init__ (self , pop_size , searchspace , constraint_aware = False , method = "uniform" , mutation_chance = 10 ):
85
86
self .pop_size = pop_size
86
87
self .searchspace = searchspace
88
+ self .tune_params = searchspace .tune_params .copy ()
87
89
self .constraint_aware = constraint_aware
88
90
self .crossover_method = supported_methods [method ]
89
91
self .mutation_chance = mutation_chance
90
92
91
93
def generate_population (self ):
92
94
""" Constraint-aware population creation method """
93
- return list (list (p ) for p in self .searchspace .get_random_sample (self .pop_size ))
95
+ if self .constraint_aware :
96
+ pop = list (list (p ) for p in self .searchspace .get_random_sample (self .pop_size ))
97
+ else :
98
+ pop = []
99
+ dna_size = len (self .tune_params )
100
+ for _ in range (self .pop_size ):
101
+ dna = []
102
+ for key in self .tune_params :
103
+ dna .append (random .choice (self .tune_params [key ]))
104
+ pop .append (dna )
105
+ return pop
94
106
95
107
def crossover (self , dna1 , dna2 ):
96
108
""" Apply selected crossover method, repair dna if constraint-aware """
@@ -135,12 +147,24 @@ def mutate(self, dna, cache=False):
135
147
"""Mutate DNA with 1/mutation_chance chance."""
136
148
# this is actually a neighbors problem with Hamming distance, choose randomly from returned searchspace list
137
149
if int (random .random () * self .mutation_chance ) == 0 :
138
- if cache :
139
- neighbors = self .searchspace .get_neighbors (tuple (dna ), neighbor_method = "Hamming" )
150
+ if self .constraint_aware :
151
+ if cache :
152
+ neighbors = self .searchspace .get_neighbors (tuple (dna ), neighbor_method = "Hamming" )
153
+ else :
154
+ neighbors = self .searchspace .get_neighbors_no_cache (tuple (dna ), neighbor_method = "Hamming" )
155
+ if len (neighbors ) > 0 :
156
+ return list (random .choice (neighbors ))
140
157
else :
141
- neighbors = self .searchspace .get_neighbors_no_cache (tuple (dna ), neighbor_method = "Hamming" )
142
- if len (neighbors ) > 0 :
143
- return list (random .choice (neighbors ))
158
+ # select a tunable parameter at random
159
+ mutate_index = random .randint (0 , len (self .tune_params )- 1 )
160
+ mutate_key = list (self .tune_params .keys ())[mutate_index ]
161
+ # get all possible values for this parameter and remove current value
162
+ new_val_options = self .tune_params [mutate_key ].copy ()
163
+ new_val_options .remove (dna [mutate_index ])
164
+ # pick new value at random
165
+ if len (new_val_options ) > 0 :
166
+ new_val = random .choice (new_val_options )
167
+ dna [mutate_index ] = new_val
144
168
return dna
145
169
146
170
0 commit comments