1use serde::Serialize;
2
3use crate::config::SimulationConfig;
4use crate::kernel::evaluate_kernel;
5use crate::observer::{ObserverSeries, ObserverSpec};
6use crate::scenario::ScenarioDefinition;
7
8#[derive(Debug, Clone, Serialize)]
9pub struct CorrectionEvent {
10 pub scenario: String,
11 pub source_level: usize,
12 pub target_level: usize,
13 pub anchor_time: usize,
14 pub corrected_time: usize,
15 pub delta_window: usize,
16 pub trust_weight: f64,
17 pub kernel_weight: f64,
18 pub compatibility: f64,
19 pub correction_magnitude: f64,
20 pub recursion_depth: usize,
21 pub iteration: usize,
22}
23
24#[derive(Debug, Clone, Serialize)]
25pub struct RecursionStats {
26 pub total_correction_events: usize,
27 pub max_recursion_depth: usize,
28 pub mean_recursion_depth: f64,
29 pub convergence_iterations: usize,
30 pub average_correction_magnitude: f64,
31 pub average_correction_trust_weight: f64,
32 pub monotonicity_violations: usize,
33}
34
35#[derive(Debug, Clone, Serialize)]
36pub struct TmtrResult {
37 pub observers: Vec<ObserverSeries>,
38 pub correction_events: Vec<CorrectionEvent>,
39 pub recursion_stats: RecursionStats,
40}
41
42pub fn apply_tmtr(
43 definition: &ScenarioDefinition,
44 config: &SimulationConfig,
45 specs: &[ObserverSpec],
46 baseline: &[ObserverSeries],
47 truth: &[f64],
48) -> TmtrResult {
49 let mut observers = baseline.to_vec();
50 let mut events = Vec::new();
51 let mut convergence_iterations = 0;
52
53 for iteration in 0..config.max_iterations {
54 convergence_iterations = iteration + 1;
55 let mut iteration_change = 0.0;
56 let mut depth_reached = vec![0usize; observers.len()];
57
58 for source_index in (1..observers.len()).rev() {
59 let target_index = source_index - 1;
60 let recursion_depth = 1 + depth_reached[source_index];
61 if recursion_depth > config.max_recursion_depth {
62 continue;
63 }
64
65 let mut pair_changed = false;
66 let source = observers[source_index].clone();
67 let target = &mut observers[target_index];
68 let eta = definition.eta[target_index];
69 let delta = definition.delta.min(config.delta);
70
71 for anchor_time in 0..truth.len() {
72 let source_trust = source.trust[anchor_time];
73 let target_trust = target.trust[anchor_time];
74 if !source.available[anchor_time] {
75 continue;
76 }
77 if source_trust < config.trust_threshold
78 || source_trust <= target_trust + config.min_trust_gap
79 {
80 continue;
81 }
82
83 let window_start = anchor_time.saturating_sub(delta);
84 for corrected_time in window_start..=anchor_time {
85 let evaluation = evaluate_kernel(
86 config.kernel,
87 &source,
88 target,
89 corrected_time,
90 anchor_time,
91 delta,
92 definition.resonance_threshold,
93 );
94 let retro_weight =
95 1.0 - (anchor_time - corrected_time) as f64 / (delta.max(1) as f64 + 1.0);
96 let correction =
97 eta * source_trust * retro_weight.max(0.05) * evaluation.signal;
98 if correction.abs() < config.convergence_tolerance * 0.1 {
99 continue;
100 }
101 target.estimate[corrected_time] += correction;
102 iteration_change += correction.abs();
103 pair_changed = true;
104 events.push(CorrectionEvent {
105 scenario: definition.name.clone(),
106 source_level: source.level,
107 target_level: target.level,
108 anchor_time,
109 corrected_time,
110 delta_window: delta,
111 trust_weight: source_trust,
112 kernel_weight: evaluation.weight,
113 compatibility: evaluation.compatibility,
114 correction_magnitude: correction,
115 recursion_depth,
116 iteration: iteration + 1,
117 });
118 }
119 }
120
121 if pair_changed {
122 depth_reached[target_index] = recursion_depth;
123 target.recompute_after_estimate_update(truth, &specs[target_index]);
124 }
125 }
126
127 if should_stop(iteration_change, config.convergence_tolerance) {
128 break;
129 }
130 }
131
132 let total_events = events.len();
133 let depth_sum = events
134 .iter()
135 .map(|event| event.recursion_depth as f64)
136 .sum::<f64>();
137 let magnitude_sum = events
138 .iter()
139 .map(|event| event.correction_magnitude.abs())
140 .sum::<f64>();
141 let trust_sum = events.iter().map(|event| event.trust_weight).sum::<f64>();
142 let max_depth = events
143 .iter()
144 .map(|event| event.recursion_depth)
145 .max()
146 .unwrap_or(0);
147
148 TmtrResult {
149 observers,
150 correction_events: events,
151 recursion_stats: RecursionStats {
152 total_correction_events: total_events,
153 max_recursion_depth: max_depth,
154 mean_recursion_depth: if total_events == 0 {
155 0.0
156 } else {
157 depth_sum / total_events as f64
158 },
159 convergence_iterations,
160 average_correction_magnitude: if total_events == 0 {
161 0.0
162 } else {
163 magnitude_sum / total_events as f64
164 },
165 average_correction_trust_weight: if total_events == 0 {
166 0.0
167 } else {
168 trust_sum / total_events as f64
169 },
170 monotonicity_violations: 0,
171 },
172 }
173}
174
175pub fn should_stop(total_change: f64, tolerance: f64) -> bool {
176 total_change <= tolerance
177}
178
179#[cfg(test)]
180mod tests {
181 use super::should_stop;
182
183 #[test]
184 fn bounded_recursion_stop_condition_triggers() {
185 assert!(should_stop(0.0005, 0.001));
186 assert!(!should_stop(0.01, 0.001));
187 }
188}