genetic_algorithms 3.0.0

Library for solving genetic algorithm problems
Documentation
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
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
# Core Traits

> Fundamental traits for genes, chromosomes, and genetic algorithm configuration.

## Overview

The core traits define the essential abstractions for building genetic algorithms in this library. They provide the interfaces for representing genes, chromosomes, and the overall configuration of a genetic algorithm. By implementing these traits, users can customize the genetic representation, fitness evaluation, and algorithm behavior to suit a wide variety of optimization problems.

The `GeneT` trait represents the smallest unit of genetic information, allowing for unique identification and cloning. The `ChromosomeT` trait models a sequence of genes (the DNA), along with metadata such as fitness and age, and provides efficient methods for manipulating genetic material using Rust's `Cow` for zero-copy or minimal-copy operations. The `ConfigurationT` trait exposes a builder-style API for specifying all aspects of the genetic algorithm, including population size, selection and crossover methods, mutation parameters, and stopping criteria.

These traits are the foundation for extending and customizing the library. Users typically implement their own gene and chromosome types to encode problem-specific information, and use the configuration trait to tune the algorithm for their optimization task.

## Key Concepts

### GeneT

| Method         | Signature                                 | Description                                  |
|----------------|-------------------------------------------|----------------------------------------------|
| `new`          | `fn new() -> Self`                        | Creates a new gene instance.                 |
| `default`      | `fn default(mut self) -> Self`            | Resets gene to its default state.            |
| `get_id`       | `fn get_id(&self) -> i32`                 | Retrieves the unique identifier of the gene. |
| `set_id`       | `fn set_id(&mut self, id: i32) -> &mut Self` | Sets the unique identifier for the gene.     |

### ChromosomeT

| Method               | Signature                                                                 | Description                                               |
|----------------------|---------------------------------------------------------------------------|-----------------------------------------------------------|
| `new`                | `fn new() -> Self`                                                        | Creates a new chromosome instance.                        |
| `default`            | `fn default(mut self) -> Self`                                            | Resets chromosome state (fitness, age, DNA).              |
| `new_gene`           | `fn new_gene() -> Self::Gene`                                             | Creates a new default-initialized gene.                   |
| `get_dna`            | `fn get_dna(&self) -> &[Self::Gene]`                                      | Returns the chromosome's DNA as a slice.                  |
| `set_dna`            | `fn set_dna<'a>(&mut self, dna: Cow<'a, [Self::Gene]>) -> &mut Self`      | Sets DNA efficiently using `Cow` for zero/minimal copy.   |
| `set_gene`           | `fn set_gene(&mut self, gene_index: usize, gene: Self::Gene) -> &mut Self`| Replaces a gene at a specific index.                      |
| `set_fitness_fn`     | `fn set_fitness_fn<F>(&mut self, fitness_fn: F) -> &mut Self`             | Installs a fitness function.                              |
| `calculate_fitness`  | `fn calculate_fitness(&mut self)`                                         | Computes and sets fitness using the installed function.   |
| `get_fitness`        | `fn get_fitness(&self) -> f64`                                            | Retrieves the current fitness value.                      |
| `set_fitness`        | `fn set_fitness(&mut self, fitness: f64) -> &mut Self`                    | Sets the fitness value.                                   |
| `set_age`            | `fn set_age(&mut self, age: i32) -> &mut Self`                            | Sets the age of the chromosome.                           |
| `get_age`            | `fn get_age(&self) -> i32`                                                | Gets the age of the chromosome.                           |
| `get_fitness_distance`| `fn get_fitness_distance(&self, fitness_target: &f64) -> f64`            | Computes distance to a target fitness.                    |

### ConfigurationT

| Method                          | Signature                                                        | Description                                              |
|----------------------------------|------------------------------------------------------------------|----------------------------------------------------------|
| `new`                           | `fn new() -> Self`                                               | Creates a new configuration instance.                    |
| `with_adaptive_ga`              | `fn with_adaptive_ga(&mut self, adaptive_ga: bool) -> &mut Self` | Enables adaptive GA features.                            |
| `with_threads`                  | `fn with_threads(&mut self, number_of_threads: i32) -> &mut Self`| Sets the number of threads for parallel execution.        |
| `with_logs`                     | `fn with_logs(&mut self, log_level: LogLevel) -> &mut Self`      | Sets the logging level.                                  |
| `with_survivor_method`          | `fn with_survivor_method(&mut self, method: Survivor) -> &mut Self`| Sets survivor selection method.                        |
| `with_problem_solving`          | `fn with_problem_solving(&mut self, problem_solving: ProblemSolving) -> &mut Self`| Sets problem-solving mode.          |
| `with_max_generations`          | `fn with_max_generations(&mut self, max_generations: i32) -> &mut Self`| Sets max number of generations.                     |
| `with_fitness_target`           | `fn with_fitness_target(&mut self, fitness_target: f64) -> &mut Self`| Sets target fitness value.                          |
| `with_population_size`          | `fn with_population_size(&mut self, population_size: i32) -> &mut Self`| Sets population size.                              |
| `with_genes_per_chromosome`     | `fn with_genes_per_chromosome(&mut self, genes_per_chromosome: i32) -> &mut Self`| Sets number of genes per chromosome.         |
| `with_needs_unique_ids`         | `fn with_needs_unique_ids(&mut self, needs_unique_ids: bool) -> &mut Self`| Requires unique gene IDs.                        |
| `with_alleles_can_be_repeated`  | `fn with_alleles_can_be_repeated(&mut self, alleles_can_be_repeated: bool) -> &mut Self`| Allows repeated alleles.          |
| `with_number_of_couples`        | `fn with_number_of_couples(&mut self, number_of_couples: i32) -> &mut Self`| Sets number of mating couples.                   |
| `with_selection_method`         | `fn with_selection_method(&mut self, selection_method: Selection) -> &mut Self`| Sets selection method.                      |
| `with_crossover_number_of_points`| `fn with_crossover_number_of_points(&mut self, number_of_points: i32) -> &mut Self`| Sets crossover points.                 |
| `with_crossover_probability_max`| `fn with_crossover_probability_max(&mut self, probability_max: f64) -> &mut Self`| Sets max crossover probability.         |
| `with_crossover_probability_min`| `fn with_crossover_probability_min(&mut self, probability_min: f64) -> &mut Self`| Sets min crossover probability.         |
| `with_crossover_method`         | `fn with_crossover_method(&mut self, method: Crossover) -> &mut Self`| Sets crossover method.                        |
| `with_sbx_eta`                  | `fn with_sbx_eta(&mut self, eta: f64) -> &mut Self`              | Sets SBX distribution index.                         |
| `with_blend_alpha`              | `fn with_blend_alpha(&mut self, alpha: f64) -> &mut Self`        | Sets BLX-α crossover alpha parameter.                 |
| `with_mutation_probability_max` | `fn with_mutation_probability_max(&mut self, probability_max: f64) -> &mut Self`| Sets max mutation probability.         |
| `with_mutation_probability_min` | `fn with_mutation_probability_min(&mut self, probability_min: f64) -> &mut Self`| Sets min mutation probability.         |
| `with_mutation_method`          | `fn with_mutation_method(&mut self, method: Mutation) -> &mut Self`| Sets mutation method.                          |
| `with_mutation_step`            | `fn with_mutation_step(&mut self, step: f64) -> &mut Self`       | Sets step size for Creep mutation.                   |
| `with_mutation_sigma`           | `fn with_mutation_sigma(&mut self, sigma: f64) -> &mut Self`     | Sets sigma for Gaussian mutation.                    |
| `with_save_progress`            | `fn with_save_progress(&mut self, save_progress: bool) -> &mut Self`| Enables saving progress.                       |
| `with_save_progress_interval`   | `fn with_save_progress_interval(&mut self, save_progress_interval: i32) -> &mut Self`| Sets save interval.           |
| `with_save_progress_path`       | `fn with_save_progress_path(&mut self, save_progress_path: String) -> &mut Self`| Sets save path.                       |
| `with_elitism`                  | `fn with_elitism(&mut self, elitism_count: usize) -> &mut Self`  | Sets number of elite chromosomes.                    |
| `with_stopping_criteria`        | `fn with_stopping_criteria(&mut self, criteria: StoppingCriteria) -> &mut Self`| Sets compound stopping criteria.           |

## Usage

### Basic Example

Implementing a custom gene and chromosome:

```rust
use std::borrow::Cow;
use my_ga_lib::traits::{GeneT, ChromosomeT, ConfigurationT};

// Custom gene type
#[derive(Default, Clone)]
pub struct MyGene {
    id: i32,
    value: f64,
}

impl GeneT for MyGene {
    fn new() -> Self {
        MyGene { id: 0, value: 0.0 }
    }
    fn default(mut self) -> Self {
        self.id = 0;
        self.value = 0.0;
        self
    }
    fn get_id(&self) -> i32 {
        self.id
    }
    fn set_id(&mut self, id: i32) -> &mut Self {
        self.id = id;
        self
    }
}

// Custom chromosome type
#[derive(Default, Clone)]
pub struct MyChromosome {
    dna: Vec<MyGene>,
    fitness: f64,
    age: i32,
    fitness_fn: Option<Box<dyn Fn(&[MyGene]) -> f64 + Send + Sync>>,
}

impl ChromosomeT for MyChromosome {
    type Gene = MyGene;

    fn new() -> Self {
        MyChromosome::default()
    }

    fn get_dna(&self) -> &[Self::Gene] {
        &self.dna
    }

    fn set_dna<'a>(&mut self, dna: Cow<'a, [Self::Gene]>) -> &mut Self {
        match dna {
            Cow::Borrowed(slice) => self.dna = slice.to_vec(),
            Cow::Owned(vec) => self.dna = vec,
        }
        self
    }

    fn set_fitness_fn<F>(&mut self, fitness_fn: F) -> &mut Self
    where
        F: Fn(&[Self::Gene]) -> f64 + Send + Sync + 'static,
    {
        self.fitness_fn = Some(Box::new(fitness_fn));
        self
    }

    fn calculate_fitness(&mut self) {
        if let Some(ref f) = self.fitness_fn {
            self.fitness = f(&self.dna);
        }
    }

    fn get_fitness(&self) -> f64 {
        self.fitness
    }

    fn set_fitness(&mut self, fitness: f64) -> &mut Self {
        self.fitness = fitness;
        self
    }

    fn set_age(&mut self, age: i32) -> &mut Self {
        self.age = age;
        self
    }

    fn get_age(&self) -> i32 {
        self.age
    }
}
```

### Advanced Example

Configuring the genetic algorithm using the builder pattern and manipulating DNA efficiently:

```rust
use std::borrow::Cow;
use my_ga_lib::traits::{GeneT, ChromosomeT, ConfigurationT};
use my_ga_lib::operators::{Selection, Crossover, Mutation, Survivor};
use my_ga_lib::stopping::StoppingCriteria;
use my_ga_lib::logging::LogLevel;

// Assume MyGene and MyChromosome are implemented as above

// Example: Efficient DNA manipulation using Cow
let mut chromosome = MyChromosome::new();
let dna_vec = vec![MyGene::new(); 10];
chromosome.set_dna(Cow::Owned(dna_vec)); // Zero-copy if you own the Vec

// Example: Setting a fitness function and calculating fitness
chromosome.set_fitness_fn(|dna| {
    dna.iter().map(|gene| gene.value).sum()
});
chromosome.calculate_fitness();
println!("Fitness: {}", chromosome.get_fitness());

// Example: Using the configuration builder
let mut config = MyConfiguration::new()
    .with_population_size(100)
    .with_genes_per_chromosome(10)
    .with_selection_method(Selection::Tournament)
    .with_crossover_method(Crossover::Uniform)
    .with_mutation_method(Mutation::Swap)
    .with_mutation_probability_max(0.1)
    .with_mutation_probability_min(0.01)
    .with_survivor_method(Survivor::AgeBased)
    .with_max_generations(1000)
    .with_fitness_target(1.0)
    .with_threads(4)
    .with_logs(LogLevel::Info)
    .with_stopping_criteria(StoppingCriteria::AnyReached)
    .with_elitism(2);

// Now pass `config` to your genetic algorithm runner.
```

## API Reference

### `GeneT`

Trait for gene representation.

**Methods:**

| Name     | Signature                                         | Description                                  |
|----------|---------------------------------------------------|----------------------------------------------|
| `new`    | `fn new() -> Self`                                | Creates a new gene instance.                 |
| `default`| `fn default(mut self) -> Self`                    | Resets gene to default state.                |
| `get_id` | `fn get_id(&self) -> i32`                         | Gets the gene's unique identifier.           |
| `set_id` | `fn set_id(&mut self, id: i32) -> &mut Self`      | Sets the gene's unique identifier.           |

### `ChromosomeT`

Trait for chromosome representation.

**Associated Types:**

| Name     | Description                                  |
|----------|----------------------------------------------|
| `Gene`   | The gene type used by the chromosome.        |

**Methods:**

| Name                  | Signature                                                                 | Description                                               |
|-----------------------|---------------------------------------------------------------------------|-----------------------------------------------------------|
| `new`                 | `fn new() -> Self`                                                        | Creates a new chromosome instance.                        |
| `default`             | `fn default(mut self) -> Self`                                            | Resets chromosome state.                                  |
| `new_gene`            | `fn new_gene() -> Self::Gene`                                             | Creates a new gene.                                       |
| `get_dna`             | `fn get_dna(&self) -> &[Self::Gene]`                                      | Returns DNA as slice.                                     |
| `set_dna`             | `fn set_dna<'a>(&mut self, dna: Cow<'a, [Self::Gene]>) -> &mut Self`      | Sets DNA efficiently using `Cow`.                         |
| `set_gene`            | `fn set_gene(&mut self, gene_index: usize, gene: Self::Gene) -> &mut Self`| Replaces gene at index.                                   |
| `set_fitness_fn`      | `fn set_fitness_fn<F>(&mut self, fitness_fn: F) -> &mut Self`             | Installs fitness function.                                |
| `calculate_fitness`   | `fn calculate_fitness(&mut self)`                                         | Computes and sets fitness.                                |
| `get_fitness`         | `fn get_fitness(&self) -> f64`                                            | Gets current fitness value.                               |
| `set_fitness`         | `fn set_fitness(&mut self, fitness: f64) -> &mut Self`                    | Sets fitness value.                                       |
| `set_age`             | `fn set_age(&mut self, age: i32) -> &mut Self`                            | Sets chromosome age.                                      |
| `get_age`             | `fn get_age(&self) -> i32`                                                | Gets chromosome age.                                      |
| `get_fitness_distance`| `fn get_fitness_distance(&self, fitness_target: &f64) -> f64`             | Computes distance to target fitness.                      |

### `TreeChromosome`

Supertrait of [`ChromosomeT`] for tree-structured chromosomes used by the Genetic Programming engine (`GpGa<N>`). Implementors expose the root expression tree and tree-level statistics. This trait does **not** extend `LinearChromosome` — tree chromosomes have no flat DNA slice (`dna()`, `dna_mut()`, `set_dna()` all panic on `GpChromosome<N>`).

**Associated Types:**

| Name | Description |
|------|-------------|
| `GpNodeType: GpNode` | The primitive set enum stored in this chromosome's expression tree. |

**Methods:**

| Name | Signature | Description |
|------|-----------|-------------|
| `tree` | `fn tree(&self) -> &Node<Self::GpNodeType>` | Returns a reference to the root node of the expression tree. |
| `tree_mut` | `fn tree_mut(&mut self) -> &mut Node<Self::GpNodeType>` | Returns a mutable reference to the root node. |
| `depth` | `fn depth(&self) -> usize` | Returns the depth of the expression tree. |
| `node_count` | `fn node_count(&self) -> usize` | Returns the total number of nodes in the tree. |

The only built-in implementor is [`GpChromosome<N>`](../src/engines/gp/chromosome.rs). See [Genetic Programming](gp.md) for the full guide.

### `GpNode`

User-implemented trait that defines the function and terminal set for Genetic Programming. Implement on your own enum and use as the type parameter `N` in `GpChromosome<N>` and `GpGa<N>`.

**Required methods:**

| Name | Signature | Description |
|------|-----------|-------------|
| `arity` | `fn arity(&self) -> usize` | Number of child arguments this node consumes (0 = terminal). |
| `evaluate` | `fn evaluate(&self, args: &[f64]) -> f64` | Evaluate this node given pre-evaluated child values. |
| `sample_random_terminal` | `fn sample_random_terminal(rng: &mut impl Rng) -> Self` | Produce a fresh terminal node (used during init and subtree mutation). |
| `all_functions` | `fn all_functions() -> Vec<Self>` | Enumerate all non-terminal variants (used by point mutation). |

**Provided methods:**

| Name | Signature | Description |
|------|-----------|-------------|
| `is_terminal` | `fn is_terminal(&self) -> bool` | Default impl returns `self.arity() == 0`. |

### `ConfigurationT`

Trait for genetic algorithm configuration.

**Methods:**

| Name                          | Signature                                                        | Description                                              |
|-------------------------------|------------------------------------------------------------------|----------------------------------------------------------|
| `new`                         | `fn new() -> Self`                                               | Creates a new configuration.                             |
| `with_adaptive_ga`            | `fn with_adaptive_ga(&mut self, adaptive_ga: bool) -> &mut Self` | Enables adaptive GA features.                            |
| `with_threads`                | `fn with_threads(&mut self, number_of_threads: i32) -> &mut Self`| Sets number of threads.                                  |
| `with_logs`                   | `fn with_logs(&mut self, log_level: LogLevel) -> &mut Self`      | Sets logging level.                                      |
| `with_survivor_method`        | `fn with_survivor_method(&mut self, method: Survivor) -> &mut Self`| Sets survivor selection method.                        |
| `with_problem_solving`        | `fn with_problem_solving(&mut self, problem_solving: ProblemSolving) -> &mut Self`| Sets problem-solving mode.          |
| `with_max_generations`        | `fn with_max_generations(&mut self, max_generations: i32) -> &mut Self`| Sets max generations.                               |
| `with_fitness_target`         | `fn with_fitness_target(&mut self, fitness_target: f64) -> &mut Self`| Sets target fitness.                                |
| `with_population_size`        | `fn with_population_size(&mut self, population_size: i32) -> &mut Self`| Sets population size.                              |
| `with_genes_per_chromosome`   | `fn with_genes_per_chromosome(&mut self, genes_per_chromosome: i32) -> &mut Self`| Sets genes per chromosome.                 |
| `with_needs_unique_ids`       | `fn with_needs_unique_ids(&mut self, needs_unique_ids: bool) -> &mut Self`| Requires unique gene IDs.                        |
| `with_alleles_can_be_repeated`| `fn with_alleles_can_be_repeated(&mut self, alleles_can_be_repeated: bool) -> &mut Self`| Allows repeated alleles.          |
| `with_number_of_couples`      | `fn with_number_of_couples(&mut self, number_of_couples: i32) -> &mut Self`| Sets number of couples.                         |
| `with_selection_method`       | `fn with_selection_method(&mut self, selection_method: Selection) -> &mut Self`| Sets selection method.                      |
| `with_crossover_number_of_points`| `fn with_crossover_number_of_points(&mut self, number_of_points: i32) -> &mut Self`| Sets crossover points.                 |
| `with_crossover_probability_max`| `fn with_crossover_probability_max(&mut self, probability_max: f64) -> &mut Self`| Sets max crossover probability.         |
| `with_crossover_probability_min`| `fn with_crossover_probability_min(&mut self, probability_min: f64) -> &mut Self`| Sets min crossover probability.         |
| `with_crossover_method`       | `fn with_crossover_method(&mut self, method: Crossover) -> &mut Self`| Sets crossover method.                        |
| `with_sbx_eta`                | `fn with_sbx_eta(&mut self, eta: f64) -> &mut Self`              | Sets SBX distribution index.                         |
| `with_blend_alpha`            | `fn with_blend_alpha(&mut self, alpha: f64) -> &mut Self`        | Sets BLX-α crossover alpha parameter.                 |
| `with_mutation_probability_max`| `fn with_mutation_probability_max(&mut self, probability_max: f64) -> &mut Self`| Sets max mutation probability.         |
| `with_mutation_probability_min`| `fn with_mutation_probability_min(&mut self, probability_min: f64) -> &mut Self`| Sets min mutation probability.         |
| `with_mutation_method`        | `fn with_mutation_method(&mut self, method: Mutation) -> &mut Self`| Sets mutation method.                          |
| `with_mutation_step`          | `fn with_mutation_step(&mut self, step: f64) -> &mut Self`       | Sets step size for Creep mutation.                   |
| `with_mutation_sigma`         | `fn with_mutation_sigma(&mut self, sigma: f64) -> &mut Self`     | Sets sigma for Gaussian mutation.                    |
| `with_save_progress`          | `fn with_save_progress(&mut self, save_progress: bool) -> &mut Self`| Enables saving progress.                       |
| `with_save_progress_interval` | `fn with_save_progress_interval(&mut self, save_progress_interval: i32) -> &mut Self`| Sets save interval.           |
| `with_save_progress_path`     | `fn with_save_progress_path(&mut self, save_progress_path: String) -> &mut Self`| Sets save path.                       |
| `with_elitism`                | `fn with_elitism(&mut self, elitism_count: usize) -> &mut Self`  | Sets number of elite chromosomes.                    |
| `with_stopping_criteria`      | `fn with_stopping_criteria(&mut self, criteria: StoppingCriteria) -> &mut Self`| Sets compound stopping criteria.           |
| `with_chromosome_length`      | `fn with_chromosome_length(&mut self, len: ChromosomeLength) -> &mut Self`| Configures `Fixed`/`Variable` length policy. Required for `Mutation::Insertion`/`Deletion`. |
| `with_length_penalty`         | `fn with_length_penalty(&mut self, penalty: f64) -> &mut Self`   | Enables parsimony pressure during survivor selection. |

## Observer Traits

The observer system provides structured, lifecycle-aware hooks into the GA run loop. Unlike
the legacy `Reporter` trait, observers are stored as `Arc<dyn GaObserver + Send + Sync>` and
receive `&self` references, enabling interior mutability and safe sharing across island threads.

### `GaObserver<U>`

Defined in `src/observe/observer/mod.rs`. All methods have default no-op implementations.

| Hook                             | When it fires                                           |
|----------------------------------|---------------------------------------------------------|
| `on_run_start`                   | Once before the first generation                        |
| `on_generation_start`            | Start of each generation, before any operators          |
| `on_selection_complete`          | After parent selection                                  |
| `on_crossover_complete`          | After crossover produces offspring                      |
| `on_mutation_complete`           | After mutation is applied                               |
| `on_fitness_evaluation_complete` | After fitness evaluation of the new population          |
| `on_survivor_selection_complete` | After survivor selection prunes the population          |
| `on_new_best`                    | When the population's best fitness improves             |
| `on_stagnation`                  | Each time the stagnation counter increments             |
| `on_extension_triggered`         | When an extension strategy fires due to low diversity   |
| `on_generation_end`              | End of each generation, after statistics collected      |
| `on_run_end`                     | Once after the GA loop exits                            |

Attach via `.with_observer(Arc::new(my_observer))` on the `Ga` builder.

### `IslandGaObserver<U>`

Island-model-specific hooks. Implement alongside `GaObserver<U>` to observe island
runs and migration events.

| Hook                        | When it fires                                  |
|-----------------------------|------------------------------------------------|
| `on_island_run_start`       | When an island's run starts                    |
| `on_island_run_end`         | When an island's run ends                      |
| `on_island_generation_end`  | At the end of each generation per island       |
| `on_migration_triggered`    | When migration between islands fires           |

### `Nsga2Observer<U>`

NSGA-II-specific hooks for observing Pareto front assignment and sort timing.

| Hook                           | When it fires                                       |
|--------------------------------|-----------------------------------------------------|
| `on_pareto_front_assigned`     | After Pareto fronts are assigned                    |
| `on_non_dominated_sort_complete` | After non-dominated sorting completes             |
| `on_crowding_distance_calculated` | After crowding distance calculation completes    |

### `AllObserver<U>`

Supertrait that combines `GaObserver<U>`, `IslandGaObserver<U>`, and `Nsga2Observer<U>`.
A blanket `impl` automatically applies to any type implementing all three. Used internally
by `CompositeObserver`.

### Built-in Observer Implementations

| Type                  | Feature flag        | Description                                                              |
|-----------------------|---------------------|--------------------------------------------------------------------------|
| `NoopObserver`        | always available    | Zero-sized no-op observer. Useful as a placeholder.                      |
| `LogObserver`         | always available    | Logs generation statistics using the `log` crate.                        |
| `CompositeObserver<U>` | always available   | Fan-out dispatch: forwards every hook to a list of inner observers.      |
| `TracingObserver`     | `observer-tracing`  | Emits structured `tracing` spans and events per hook.                    |
| `MetricsObserver`     | `observer-metrics`  | Collects numeric metrics into a thread-safe `MetricsStore`.              |

### Comparison: `Reporter` vs `GaObserver`

| Aspect          | `Reporter`                         | `GaObserver`                               |
|-----------------|------------------------------------|--------------------------------------------|
| Storage         | `Box<dyn Reporter + Send>`         | `Arc<dyn GaObserver + Send + Sync>`        |
| Mutability      | `&mut self`                        | `&self` (interior mutability)              |
| Hooks           | 4 lifecycle hooks                  | 12 hooks (lifecycle + operator + special)  |
| Thread safety   | `Send` only                        | `Send + Sync`                              |
| Island support  | No                                 | Yes (via `IslandGaObserver`)               |
| NSGA-II support | No                                 | Yes (via `Nsga2Observer`)                  |

## Related

- [chromosomes.md](chromosomes.md)
- [genotypes.md](genotypes.md)
- [configuration.md](configuration.md)
- [operators/selection.md](operators/selection.md)
- [operators/crossover.md](operators/crossover.md)
- [operators/mutation.md](operators/mutation.md)
- [operators/survivor.md](operators/survivor.md)
- [fitness.md](fitness.md)
- [population.md](population.md)
- [src/traits.rs](../src/traits.rs)
- [src/traits/gene.rs](../src/traits/gene.rs)
- [src/traits/chromosome.rs](../src/traits/chromosome.rs)
- [src/traits/configuration.rs](../src/traits/configuration.rs)