11"""A simple Different Evolution for parameter search."""
2-
2+ import random
33import re
44import numpy as np
55
1414 F = ("mutation factor (differential weight)" , 0.8 ),
1515 CR = ("crossover rate" , 0.9 ),
1616 method = ("method" , "best1bin" ),
17+ constraint_aware = ("constraint-aware optimization (True/False)" , True ),
1718)
1819
1920supported_methods = [
@@ -37,13 +38,13 @@ def tune(searchspace: Searchspace, runner, tuning_options):
3738 bounds = cost_func .get_bounds ()
3839
3940 options = tuning_options .strategy_options
40- popsize , maxiter , F , CR , method = common .get_options (options , _options )
41+ popsize , maxiter , F , CR , method , constraint_aware = common .get_options (options , _options )
4142
4243 if method not in supported_methods :
4344 raise ValueError (f"Error { method } not supported, { supported_methods = } " )
4445
4546 try :
46- differential_evolution (searchspace , cost_func , bounds , popsize , maxiter , F , CR , method , tuning_options .verbose )
47+ differential_evolution (searchspace , cost_func , bounds , popsize , maxiter , F , CR , method , constraint_aware , tuning_options .verbose )
4748 except util .StopCriterionReached as e :
4849 if tuning_options .verbose :
4950 print (e )
@@ -96,7 +97,7 @@ def random_draw(idxs, mutate, best):
9697 return np .random .choice (idxs , draw , replace = draw >= len (idxs ))
9798
9899
99- def differential_evolution (searchspace , cost_func , bounds , popsize , maxiter , F , CR , method , verbose ):
100+ def differential_evolution (searchspace , cost_func , bounds , popsize , maxiter , F , CR , method , constraint_aware , verbose ):
100101 """
101102 A basic implementation of the Differential Evolution algorithm.
102103
@@ -114,7 +115,18 @@ def differential_evolution(searchspace, cost_func, bounds, popsize, maxiter, F,
114115 bounds = np .array (bounds )
115116
116117 # Initialize the population with random individuals within the bounds
117- population = np .array (list (list (p ) for p in searchspace .get_random_sample (popsize )))
118+ if constraint_aware :
119+ population = np .array (list (list (p ) for p in searchspace .get_random_sample (popsize )))
120+ else :
121+ population = []
122+ dna_size = len (self .tune_params )
123+ for _ in range (self .pop_size ):
124+ dna = []
125+ for key in self .tune_params :
126+ dna .append (random .choice (self .tune_params [key ]))
127+ population .append (dna )
128+ population = np .array (population )
129+
118130 population [0 ] = cost_func .get_start_pos ()
119131
120132 # Calculate the initial cost for each individual in the population
@@ -155,6 +167,10 @@ def differential_evolution(searchspace, cost_func, bounds, popsize, maxiter, F,
155167 # --- b. Crossover ---
156168 trial_vector = crossover_method (donor_vector , population [i ], CR )
157169
170+ # Repair if constraint_aware
171+ if constraint_aware :
172+ trial_vector = repair (trial_vector , searchspace )
173+
158174 # Store for selection
159175 trial_population .append (trial_vector )
160176
@@ -319,6 +335,25 @@ def exponential_crossover(donor_vector, target, CR):
319335 return trial_idx
320336
321337
338+ def repair (trial_vector , searchspace ):
339+ """
340+ Attempts to repair trial_vector if trial_vector is invalid
341+ """
342+ if not searchspace .is_param_config_valid (tuple (trial_vector )):
343+ # search for valid configurations neighboring trial_vector
344+ # start from strictly-adjacent to increasingly allowing more neighbors
345+ for neighbor_method in ["strictly-adjacent" , "adjacent" , "Hamming" ]:
346+ neighbors = searchspace .get_neighbors_no_cache (tuple (trial_vector ), neighbor_method = neighbor_method )
347+
348+ # if we have found valid neighboring configurations, select one at random
349+ if len (neighbors ) > 0 :
350+ new_trial_vector = np .array (list (random .choice (neighbors )))
351+ print (f"Differential evolution resulted in invalid config { trial_vector = } , repaired dna to { new_trial_vector = } " )
352+ return new_trial_vector
353+
354+ return trial_vector
355+
356+
322357mutation = {
323358 "1" : mutate_de_1 ,
324359 "2" : mutate_de_2 ,
0 commit comments