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
//! Surrogate-assisted fitness prescreening for offspring evaluation.
//!
//! A [`SurrogateModel`] is a cheap approximation of the true fitness function.
//! Before offspring are passed to the (expensive) true evaluator, the engine
//! uses the surrogate to predict fitness and retains only the most promising
//! fraction. This reduces the number of true fitness evaluations per generation
//! without changing the population size or selection pressure.
//!
//! # Prescreening contract (D-04, D-08)
//!
//! 1. The surrogate runs **before** [`FitnessCache`] and [`BatchFitnessEvaluator`]
//! in the engine's evaluation pipeline. Only offspring that survive
//! prescreening are passed to the true evaluator.
//! 2. Rejected offspring are **dropped permanently**. They are never evaluated
//! with the true fitness function and never inserted into the population.
//! 3. The surviving offspring fraction is controlled by
//! `GaConfiguration::surrogate_fraction` (Plan 02). The engine always retains
//! at least `max(1, floor(n * fraction))` offspring.
//!
//! # Tie-breaking
//!
//! When multiple offspring have the same predicted score, the engine sorts them
//! using an **unstable sort**. The relative order of equal-scored offspring is
//! not guaranteed. Users must not rely on tie-breaking order for determinism
//! across platforms or compiler versions.
//!
//! [`FitnessCache`]: crate::fitness::FitnessCache
//! [`BatchFitnessEvaluator`]: crate::fitness::BatchFitnessEvaluator
use crateChromosomeT;
/// Surrogate model for cheap offspring prescreening before true fitness evaluation.
///
/// Implementors provide a single `predict` method that returns an approximate
/// fitness score for a chromosome. The engine uses these scores to rank offspring
/// and discard the least-promising fraction before calling the (expensive) true
/// fitness function.
///
/// # Required implementation
///
/// ```text
/// fn predict(&self, chromosome: &U) -> f64
/// ```
///
/// The returned value is a **relative score** used only for ordering: higher
/// values are treated as better (maximization-consistent). The engine normalises
/// nothing — if your true fitness is minimization-based, return the negated
/// approximation so that better chromosomes still score higher.
///
/// # Thread safety
///
/// Implementors must be `Send + Sync` so that the model can be stored in an
/// `Arc<dyn SurrogateModel<U>>` and accessed across rayon threads.
///
/// # Tie-breaking
///
/// The prescreening sort is unstable. Equal-scored offspring are retained or
/// rejected in an unspecified order. Do not rely on tie-breaking for
/// deterministic behaviour across platforms or compiler versions.
///
/// # Example
///
/// ```rust,no_run
/// // no_run: stub implementation — not a runnable example
/// use genetic_algorithms::SurrogateModel;
/// use genetic_algorithms::chromosomes::Range as RangeChromosome;
///
/// struct LinearSurrogate;
///
/// impl SurrogateModel<RangeChromosome<f64>> for LinearSurrogate {
/// fn predict(&self, _chromosome: &RangeChromosome<f64>) -> f64 {
/// // Cheap approximation: sum of gene values
/// // chromosome.dna().iter().map(|g| g.value()).sum()
/// todo!()
/// }
/// }
/// ```