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
//! Per-generation observer surface for [`EvolutionaryHarness`].
//!
//! The harness emits a scalar tracing event per generation
//! (`best_fitness`, `mean_fitness`, …) which feeds the on-disk metric
//! stream. Population-level reporting — the full fitness vector, the
//! best-individual digest, parent lineage — does not fit through
//! `tracing::info!` cleanly: events are string-keyed and scalar-valued.
//! [`PopulationObserver`] is the structured callback the EA-population
//! recorder (`rlevo_benchmarks::record::PopulationReporter`) attaches
//! to to capture that shape.
//!
//! The trait is intentionally narrow — the snapshot fields mirror the
//! report-tier `PopulationSample` schema 1:1 minus the `inner_rl_returns`
//! field, which is the hybrid driver's responsibility.
//!
//! [`EvolutionaryHarness`]: crate::strategy::EvolutionaryHarness
use Arc;
use Mutex;
/// Per-generation population snapshot delivered to a
/// [`PopulationObserver`].
///
/// Fields mirror the on-disk `PopulationSample` schema; the harness
/// emits one of these after every successful
/// [`Strategy::tell`](crate::Strategy::tell) call when an observer is
/// attached.
///
/// **Field semantics**:
///
/// - `fitnesses` is the full per-individual fitness vector in **natural
/// (user-sense)** space — the values are recorded before the harness
/// canonicalises, so they read in the objective's declared
/// [`ObjectiveSense`](rlevo_core::objective::ObjectiveSense) (a `Minimize`
/// landscape's costs, a `Maximize` objective's rewards). `best_index` points
/// at the best individual in that sense.
/// - `diversity` is currently `None` — the harness has no
/// strategy-agnostic geometry over the population tensor. A future
/// `Strategy::diversity` extension fills it in.
/// - `best_genome_digest` and `parents_of_best` are emitted empty /
/// `None` until per-strategy digest + parent-tracking lands (will
/// feed a lineage DAG panel).
/// Callback the harness invokes once per generation, after
/// [`Strategy::tell`](crate::Strategy::tell) has returned and the
/// canonical `tracing::info!` aggregate event has been emitted.
///
/// `Send + 'static` so observers can sit behind
/// [`Arc<Mutex<dyn PopulationObserver>>`](SharedPopulationObserver) and
/// be shared across rayon worker threads — same shape as the
/// `RecordSink` trait in `rlevo-benchmarks`.
/// Shared handle to a [`PopulationObserver`]. Backed by
/// [`parking_lot::Mutex`] so the observer handle and the recording-tier
/// sinks in `rlevo-benchmarks` share one lock type (ADR 0010); aliased so
/// call sites do not have to spell out the `Arc<Mutex<…>>` shape every time.
pub type SharedPopulationObserver = ;