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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
//! # Fitness Evaluators
//!
//! This module provides fitness evaluation strategies for genetic algorithms, offering
//! both individual and batch evaluation approaches. Evaluators are responsible for
//! computing fitness scores for individuals in the population using the provided problem.
//!
//! The module provides two main evaluation strategies:
//! - **FitnessEvaluator**: Evaluates individuals one at a time
//! - **BatchFitnessEvaluator**: Evaluates individuals in batches for better performance
//!
//! Both evaluators support parallel execution through the executor system and integrate
//! seamlessly with the ecosystem and problem abstractions.
use crateResult;
use crate::;
use Arc;
/// A trait for evaluating fitness of individuals in the ecosystem.
///
/// The [Evaluator] trait defines the interface for fitness evaluation strategies.
/// Implementors can define different approaches to computing fitness scores,
/// such as individual evaluation, batch evaluation, or specialized evaluation
/// strategies for specific problem domains.
///
/// The two main implementations provided are:
/// - [FitnessEvaluator]: Evaluates individuals one at a time
/// - [BatchFitnessEvaluator]: Evaluates individuals in batches
/// Custom evaluators can be created and used, however, take special note on how the
/// members of the ecosystem are accessed and modified, (taken out of the phenotype then restored).
/// This is important to ensure memory safety and avoid unnecessary cloning of genotypes.
///
/// # Generic Parameters
/// - `C`: The chromosome type that represents the genetic material
/// - `T`: The target type that the problem operates on
/// A fitness evaluator that evaluates individuals one at a time.
///
/// `FitnessEvaluator` processes individuals individually, which is suitable for
/// problems where individual evaluation is the most efficient approach or when
/// you need fine-grained control over the evaluation process.
///
/// # Performance Characteristics
///
/// - **Individual Processing**: Each individual is evaluated separately
/// - **Parallelization**: Can utilize multiple threads through the executor
/// - **Flexibility**: Easy to implement custom evaluation logic
/// Implementation of [Evaluator] for [FitnessEvaluator].
///
/// This implementation evaluates individuals one at a time, collecting unevaluated
/// individuals and processing them through the executor system.
///
/// # Algorithm
///
/// 1. **Collect Unevaluated Individuals**: Find individuals without fitness scores
/// 2. **Create Evaluation Jobs**: Package genotypes and indices into jobs
/// 3. **Execute Jobs**: Run evaluations through the executor
/// 4. **Update Ecosystem**: Store computed scores and restore genotypes
///
/// # Performance Optimizations
///
/// - **Efficient Job Creation**: Jobs are created with minimal allocation
/// - **Batch Execution**: Multiple jobs are submitted to the executor at once
/// - **Memory Reuse**: Genotypes are restored to avoid unnecessary cloning
/// Default implementation for [FitnessEvaluator].
///
/// Creates a fitness evaluator with a serial executor, which is suitable for
/// single-threaded applications or when parallel execution is not needed.
///
/// # Note
///
/// The default executor is [Executor::Serial], which processes evaluations
/// sequentially. For better performance with large populations, consider using
/// [variant@Executor::ThreadPool(n)] with an appropriate number of worker threads
/// or [Executor::WorkerPool] (rayon feature required).
/// A fitness evaluator that evaluates individuals in batches for improved performance or
/// for when you need access to parts or the whole of an unevaluated population in order
/// to compute fitness.
///
/// [BatchFitnessEvaluator] groups individuals into batches and evaluates them
/// together, which can be more efficient than individual evaluation, especially
/// when the problem supports batch evaluation or when there are significant
/// overhead costs per evaluation.
///
/// # Performance Characteristics
///
/// - **Batch Processing**: Multiple individuals evaluated together
/// - **Reduced Overhead**: Sometimes lower per-individual evaluation cost
///
/// # Use Cases
///
/// Use [BatchFitnessEvaluator] when:
/// - The problem supports efficient batch evaluation
/// - You have a large population that benefits from parallelization
/// - Individual evaluation overhead is significant
/// - You need access to parts or the whole of an unevaluated population in order to compute fitness.
///
/// # Batch Size Calculation
///
/// In order to ensure the optimal distribution of work across available threads,
/// batch size is automatically calculated based on the number of workers
/// in the executor and the total number of individuals to evaluate:
///
/// ```text
/// batch_size = (total_individuals + num_workers - 1) / num_workers
/// ```
/// So, for example, if you have 100 individuals and 4 workers, the batch size would be:
///
/// ```text
/// batch_size = (100 + 4 - 1) / 4 = 25
/// ```
///
/// But, if your executor is [Executor::Serial], then the batch size will simply be the total number of
/// individuals to evaluate, resulting in a single batch.
/// Implementation of [Evaluator] for [BatchFitnessEvaluator].
///
/// This implementation groups individuals into batches and evaluates them
/// together, which can significantly improve performance for large populations
/// or when the problem supports efficient batch evaluation.
///
/// # Algorithm
///
/// The algorithm largely follows the same steps as `FitnessEvaluator`:
/// 1. **Collect Unevaluated Individuals**: Find individuals without fitness scores
/// 2. **Calculate Batch Size**: Determine optimal batch size based on worker count
/// 3. **Create Batches**: Group individuals into batches for parallel processing
/// 4. **Execute Batches**: Run batch evaluations through the executor
/// 5. **Update Ecosystem**: Store computed scores and restore genotypes
///
/// # Batch Size Optimization
///
/// The batch size is calculated to ensure optimal work distribution:
/// - **Small Batches**: Ensure all workers have work to do
/// - **Large Batches**: Minimize overhead from job creation and distribution
/// - **Balanced Distribution**: Work is evenly distributed across available threads
///
/// # Performance Optimizations
///
/// - **Efficient Batching**: Batches are created with minimal memory allocation
/// - **Parallel Execution**: Multiple batches are processed simultaneously
/// - **Memory Management**: Genotypes are efficiently restored after evaluation