1
+
2
+
3
+ #[ macro_export]
4
+ macro_rules! build_concrete_genetic_algorithm_base {
5
+ ( $me_base_name: ident, $individual_variant: ident, $score_type: ty) => {
6
+ #[ pyclass]
7
+ pub struct $me_base_name {
8
+
9
+ pub population_size: usize ,
10
+ pub half_population_size: usize ,
11
+ pub crossover_probability: f64 ,
12
+ pub mutation_rate_multiplier: f64 ,
13
+ pub p_best_rate: f64 ,
14
+ pub tabu_entity_rate: f64 ,
15
+
16
+ pub metaheuristic_kind: String ,
17
+ pub metaheuristic_name: String ,
18
+
19
+ pub group_mutation_rates_map: HashMap <String , f64 >,
20
+ pub discrete_ids: Option <Vec <usize >>,
21
+ pub mover: Mover ,
22
+ pub variables_manager: VariablesManager ,
23
+ }
24
+
25
+ #[ pymethods]
26
+ impl $me_base_name {
27
+
28
+ #[ new]
29
+ #[ pyo3( signature = ( variables_manager_py, population_size,
30
+ crossover_probability, p_best_rate, tabu_entity_rate,
31
+ semantic_groups_dict,
32
+ mutation_rate_multiplier=None , move_probas=None , discrete_ids=None ) ) ]
33
+ pub fn new(
34
+ variables_manager_py: VariablesManagerPy ,
35
+ population_size: usize ,
36
+ crossover_probability: f64 ,
37
+ p_best_rate: f64 ,
38
+ tabu_entity_rate: f64 ,
39
+ semantic_groups_dict: HashMap <String , Vec <usize >>,
40
+ mutation_rate_multiplier: Option <f64 >,
41
+ move_probas: Option <Vec <f64 >>,
42
+ discrete_ids: Option <Vec <usize >>,
43
+ ) -> Self {
44
+
45
+ let half_population_size = ( 0.5 * ( population_size as f64 ) ) . ceil( ) as usize ;
46
+ let current_mutation_rate_multiplier;
47
+ match mutation_rate_multiplier {
48
+ Some ( x) => current_mutation_rate_multiplier = mutation_rate_multiplier. unwrap( ) ,
49
+ None => current_mutation_rate_multiplier = 0.0 // 0.0 - always use minimal possible move size, 1.0 - is more intuitive,
50
+ }
51
+ let mut group_mutation_rates_map: HashMap <String , f64 > = HashMap :: new( ) ;
52
+ for group_name in semantic_groups_dict. keys( ) {
53
+ let group_size = semantic_groups_dict[ group_name] . len( ) ;
54
+ let current_group_mutation_rate = current_mutation_rate_multiplier * ( 1.0 / ( group_size as f64 ) ) ;
55
+ group_mutation_rates_map. insert( group_name. clone( ) , current_group_mutation_rate) ;
56
+ }
57
+
58
+ Self {
59
+ population_size: population_size,
60
+ half_population_size: half_population_size,
61
+ crossover_probability: crossover_probability,
62
+ mutation_rate_multiplier: current_mutation_rate_multiplier,
63
+ p_best_rate: p_best_rate,
64
+ tabu_entity_rate: tabu_entity_rate,
65
+
66
+ metaheuristic_kind: "Population" . to_string( ) ,
67
+ metaheuristic_name: "GeneticAlgorithm" . to_string( ) ,
68
+
69
+ group_mutation_rates_map: group_mutation_rates_map. clone( ) ,
70
+ discrete_ids: discrete_ids. clone( ) ,
71
+ mover: Mover :: new( tabu_entity_rate, HashMap :: new( ) , HashMap :: new( ) , HashMap :: new( ) , group_mutation_rates_map. clone( ) , move_probas) ,
72
+ variables_manager: VariablesManager :: new( variables_manager_py. variables_vec. clone( ) ) ,
73
+ }
74
+ }
75
+
76
+ fn select_p_best_id( & mut self ) -> usize {
77
+
78
+ let p_best_proba = Uniform :: new( 0.000001 , self . p_best_rate) . sample( & mut StdRng :: from_entropy( ) ) ;
79
+ let last_top_id = ( p_best_proba * ( self . population_size as f64 ) ) . ceil( ) as usize ;
80
+ let chosen_id: usize = Uniform :: new( 0 , last_top_id) . sample( & mut StdRng :: from_entropy( ) ) ;
81
+
82
+ return chosen_id;
83
+ }
84
+
85
+ fn select_p_worst_id( & mut self ) -> usize {
86
+
87
+ let p_best_proba = Uniform :: new( 0.000001 , self . p_best_rate) . sample( & mut StdRng :: from_entropy( ) ) ;
88
+ let last_top_id = ( p_best_proba * ( self . population_size as f64 ) ) . ceil( ) as usize ;
89
+ let chosen_id: usize = Uniform :: new( self . population_size - last_top_id, self . population_size) . sample( & mut StdRng :: from_entropy( ) ) ;
90
+
91
+ return chosen_id;
92
+ }
93
+
94
+ fn cross( & mut self , candidate_1: Vec <f64 >, candidate_2: Vec <f64 >) -> ( Vec <f64 >, Vec <f64 >) {
95
+
96
+ let variables_count = candidate_1. len( ) ;
97
+ let mut weights = vec![ Uniform :: new_inclusive( 0.0 , 1.0 ) . sample( & mut StdRng :: from_entropy( ) ) ; variables_count] ;
98
+
99
+ match & self . discrete_ids {
100
+ None => ( ) ,
101
+ Some ( discrete_ids) => discrete_ids. into_iter( ) . for_each( |i| weights[ * i] = math_utils:: rint( weights[ * i] ) )
102
+ }
103
+
104
+ let new_candidate_1: Vec <f64 > =
105
+ weights. iter( )
106
+ . zip( candidate_1. iter( ) )
107
+ . zip( candidate_2. iter( ) )
108
+ . map( |( ( w, c_1) , c_2) | {
109
+ c_1 * w + c_2 * ( 1.0 - w)
110
+ } )
111
+ . collect( ) ;
112
+
113
+ let new_candidate_2: Vec <f64 > =
114
+ weights. iter( )
115
+ . zip( candidate_1. iter( ) )
116
+ . zip( candidate_2. iter( ) )
117
+ . map( |( ( w, c_1) , c_2) | {
118
+ c_2 * w + c_1 * ( 1.0 - w)
119
+ } )
120
+ . collect( ) ;
121
+
122
+ return ( new_candidate_1, new_candidate_2) ;
123
+ }
124
+
125
+ fn sample_candidates_plain(
126
+ & mut self ,
127
+ population: Vec <$individual_variant>,
128
+ current_top_individual: $individual_variant,
129
+ ) -> Vec <Vec <f64 >> {
130
+
131
+ if self . mover. tabu_entity_size_map. len( ) == 0 {
132
+ let semantic_groups_map = self . variables_manager. semantic_groups_map. clone( ) ;
133
+ for ( group_name, group_ids) in semantic_groups_map {
134
+ self . mover. tabu_ids_sets_map. insert( group_name. clone( ) , HashSet :: new( ) ) ;
135
+ self . mover. tabu_entity_size_map. insert( group_name. clone( ) , max( ( self . tabu_entity_rate * ( group_ids. len( ) as f64 ) ) . ceil( ) as usize , 1 ) ) ;
136
+ self . mover. tabu_ids_vecdeque_map. insert( group_name. clone( ) , VecDeque :: new( ) ) ;
137
+ }
138
+ }
139
+
140
+ let mut population = population;
141
+ population. sort( ) ;
142
+
143
+ let mut candidates: Vec <Vec <f64 >> = Vec :: new( ) ;
144
+ for i in 0 ..self . half_population_size {
145
+ let mut candidate_1 = population[ self . select_p_best_id( ) ] . variable_values. clone( ) ;
146
+ let mut candidate_2 = population[ self . select_p_best_id( ) ] . variable_values. clone( ) ;
147
+
148
+ if Uniform :: new_inclusive( 0.0 , 1.0 ) . sample( & mut StdRng :: from_entropy( ) ) <= self . crossover_probability {
149
+ ( candidate_1, candidate_2) = self . cross( candidate_1, candidate_2) ;
150
+ }
151
+
152
+ let ( changed_candidate_1, changed_columns_1, _) = self . mover. do_move( & mut candidate_1, & self . variables_manager, false ) ;
153
+ let ( changed_candidate_2, changed_columns_2, _) = self . mover. do_move( & mut candidate_2, & self . variables_manager, false ) ;
154
+
155
+ candidate_1 = changed_candidate_1. unwrap( ) ;
156
+ candidate_2 = changed_candidate_2. unwrap( ) ;
157
+
158
+
159
+ // for crossover with rint() one doesn't need for fixing the whole candidate vector
160
+ // float values are crossed without rint, but due to the convex sum they will be still into the bounds
161
+ // all sampled values are always in the bounds
162
+ // problems can occur only by swap mutations, so fix all changed by a move columns
163
+ self . variables_manager. fix_variables( & mut candidate_1, changed_columns_1) ;
164
+ self . variables_manager. fix_variables( & mut candidate_2, changed_columns_2) ;
165
+
166
+ candidates. push( candidate_1) ;
167
+ candidates. push( candidate_2) ;
168
+ }
169
+
170
+ return candidates;
171
+ }
172
+
173
+ fn sample_candidates_incremental(
174
+ & mut self ,
175
+ population: Vec <$individual_variant>,
176
+ current_top_individual: $individual_variant,
177
+ ) -> ( Vec <f64 >, Vec <Vec <( usize , f64 ) >>) {
178
+ panic!( "Incremental candidates sampling is available only for local search approaches (TabuSearch, LateAcceptance, etc)." )
179
+ }
180
+
181
+ fn build_updated_population(
182
+ & mut self ,
183
+ current_population: Vec <$individual_variant>,
184
+ candidates: Vec <$individual_variant>
185
+ ) -> Vec <$individual_variant> {
186
+
187
+ let mut winners: Vec <$individual_variant> = Vec :: new( ) ;
188
+ for i in 0 ..self . population_size {
189
+ let chosen_id = self . select_p_worst_id( ) ;
190
+ let weak_native = current_population[ chosen_id] . clone( ) ;
191
+ let candidate = & candidates[ i] ;
192
+ let winner = if & candidate. score <= & weak_native. score { candidate. clone( ) } else { weak_native. clone( ) } ;
193
+ winners. push( winner) ;
194
+ }
195
+
196
+ return winners;
197
+ }
198
+
199
+ fn build_updated_population_incremental(
200
+ & mut self ,
201
+ current_population: Vec <$individual_variant>,
202
+ sample: Vec <f64 >,
203
+ deltas: Vec <Vec <( usize , f64 ) >>,
204
+ scores: Vec <$score_type>,
205
+ ) -> Vec <$individual_variant> {
206
+
207
+ panic!( "Incremental candidates sampling is available only for local search approaches (TabuSearch, LateAcceptance, etc)." )
208
+ }
209
+
210
+ #[ getter]
211
+ pub fn metaheuristic_kind( & self ) -> String {
212
+ self . metaheuristic_kind. clone( )
213
+ }
214
+
215
+ #[ getter]
216
+ pub fn metaheuristic_name( & self ) -> String {
217
+ self . metaheuristic_name. clone( )
218
+ }
219
+
220
+ }
221
+ } ;
222
+ }
0 commit comments