1use crate::logdet_bounds::{LogdetEnclosure, MarginVerdict};
21use crate::model_types::OuterCriterionCertificate;
22use crate::row_sampling_measure::{CoresetCertificate, CoresetMarginVerdict};
23use crate::structure_search::{CollapseAction, CollapseEvent};
24use gam_problem::topology_certificates::{Certificate, Claim, Evidence, Verdict};
25
26fn put_finite(evidence: &mut Evidence, key: &'static str, value: f64) {
29 if value.is_finite() {
30 evidence.insert(key, value.into());
31 } else {
32 evidence.insert(key, "n/a".into());
33 }
34}
35
36impl Certificate for OuterCriterionCertificate {
39 fn claim(&self) -> Claim {
40 Claim::new(
41 "outer-optimality",
42 concat!(
43 "the returned outer optimum is analytically KKT-stationary and ",
44 "its available exact curvature is not indefinite",
45 ),
46 )
47 }
48
49 fn evidence(&self) -> Evidence {
50 let mut e = Evidence::new();
51 put_finite(
52 &mut e,
53 "stationarity_raw_norm",
54 self.stationarity.raw_norm(),
55 );
56 put_finite(
57 &mut e,
58 "stationarity_projected_norm",
59 self.stationarity.projected_norm(),
60 );
61 put_finite(&mut e, "stationarity_bound", self.stationarity.bound());
62 e.insert(
63 "stationarity_kind",
64 if self.stationarity.is_fixed_point() {
65 "fixed_point"
66 } else {
67 "analytic_gradient"
68 }
69 .into(),
70 );
71 e.insert(
72 "hessian_psd",
73 match self.hessian_psd {
74 Some(psd) => psd.into(),
75 None => "n/a".into(),
76 },
77 );
78 if let Some(clearance) = self.curvature_floor {
82 e.insert("curvature_floor_cleared", clearance.cleared.into());
83 put_finite(
84 &mut e,
85 "curvature_interior_min_eigenvalue",
86 clearance.interior_min_eigenvalue,
87 );
88 put_finite(&mut e, "curvature_gradient_floor", clearance.gradient_floor);
89 }
90 e.insert("lambdas_railed_count", self.lambdas_railed.len().into());
91 e.insert("stationary", self.is_stationary().into());
92 e.insert("curvature_admissible", self.curvature_admissible().into());
93 e.insert("summary", self.summary().into());
94 e
95 }
96
97 fn verdict(&self) -> Verdict {
98 if self.certifies() {
99 Verdict::Certified
100 } else {
101 Verdict::Insufficient
102 }
103 }
104}
105
106impl Certificate for CoresetCertificate {
109 fn claim(&self) -> Claim {
110 Claim::new(
111 "coreset-budget",
112 "the selected row coreset reproduces the full-corpus evidence within \
113 a certified spectral + likelihood error budget; a race decision \
114 inherits the full-corpus verdict only when its margin clears this \
115 budget",
116 )
117 }
118
119 fn evidence(&self) -> Evidence {
120 let mut e = Evidence::new();
121 put_finite(&mut e, "eps_spectral", self.eps_spectral);
122 put_finite(&mut e, "eps_likelihood", self.eps_likelihood);
123 e.insert("dim_effective", self.dim_effective.into());
124 e.insert("n_selected", self.n_selected.into());
125 put_finite(&mut e, "logdet_error_bound", self.logdet_error_bound());
126 put_finite(&mut e, "race_transfer_margin", self.race_transfer_margin());
127 e
128 }
129
130 fn verdict(&self) -> Verdict {
131 if self.race_transfer_margin().is_finite() {
138 Verdict::Insufficient
139 } else {
140 Verdict::Unavailable
141 }
142 }
143}
144
145pub fn coreset_race_verdict(verdict: CoresetMarginVerdict) -> Verdict {
152 match verdict {
153 CoresetMarginVerdict::Certified { .. } => Verdict::Certified,
154 CoresetMarginVerdict::InsufficientMargin { .. } => Verdict::Insufficient,
155 }
156}
157
158pub fn enclosure_margin_verdict(enclosure: &LogdetEnclosure, decision_margin: f64) -> Verdict {
161 match enclosure.decide_within_margin(decision_margin) {
162 MarginVerdict::Decided { .. } => Verdict::Certified,
163 MarginVerdict::InsufficientMargin { .. } => Verdict::Insufficient,
164 }
165}
166
167impl Certificate for LogdetEnclosure {
170 fn claim(&self) -> Claim {
171 Claim::new(
172 "logdet-enclosure",
173 "the log-determinant is enclosed in a certified [lower, upper] \
174 interval whose midpoint is interchangeable with the exact value for \
175 any decision whose margin exceeds the enclosure gap",
176 )
177 }
178
179 fn evidence(&self) -> Evidence {
180 let mut e = Evidence::new();
181 put_finite(&mut e, "block_diag_logdet", self.block_diag_logdet);
182 put_finite(&mut e, "lower", self.lower);
183 put_finite(&mut e, "upper", self.upper);
184 put_finite(&mut e, "gap", self.gap());
185 put_finite(&mut e, "rho", self.rho);
186 put_finite(&mut e, "p2", self.p2);
187 match self.p3 {
188 Some(p3) => put_finite(&mut e, "p3", p3),
189 None => {
190 e.insert("p3", "n/a".into());
191 }
192 }
193 e
194 }
195
196 fn verdict(&self) -> Verdict {
197 if self.lower.is_finite() && self.upper.is_finite() && self.gap().is_finite() {
202 Verdict::Insufficient
203 } else {
204 Verdict::Unavailable
205 }
206 }
207}
208
209impl Certificate for CollapseEvent {
212 fn claim(&self) -> Claim {
213 Claim::new(
214 "structure-collapse",
215 "an atom's active mass fell below the collapse floor during the joint \
216 fit; the guard either reseeded it from a fresh basin or, once the \
217 reseed budget was exhausted, recorded the collapse as the objective's \
218 terminal verdict",
219 )
220 }
221
222 fn evidence(&self) -> Evidence {
223 let mut e = Evidence::new();
224 e.insert("iteration", self.iteration.into());
225 e.insert("atom", self.atom.into());
226 put_finite(&mut e, "max_active_mass", self.max_active_mass);
227 put_finite(&mut e, "floor", self.floor);
228 e.insert(
229 "action",
230 match self.action {
231 CollapseAction::Reseeded => "reseeded",
232 CollapseAction::Terminal => "terminal",
233 }
234 .into(),
235 );
236 e
237 }
238
239 fn verdict(&self) -> Verdict {
240 match self.action {
246 CollapseAction::Reseeded => Verdict::Insufficient,
247 CollapseAction::Terminal => Verdict::Unavailable,
248 }
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255 use gam_problem::topology_certificates::CertificateLedger;
256
257 #[test]
258 fn criterion_stationarity_and_curvature_control_verdict() {
259 let clean = OuterCriterionCertificate {
260 stationarity: crate::model_types::OuterStationarityCertificate::AnalyticGradient {
261 grad_norm: 1e-8,
262 projected_grad_norm: 1e-8,
263 bound: 1e-6,
264 },
265 hessian_psd: Some(true),
266 lambdas_railed: Vec::new(),
267 curvature_floor: None,
268 };
269 assert_eq!(clean.verdict(), Verdict::Certified);
270 assert!(clean.verdict().is_certified());
271
272 let nonstationary = OuterCriterionCertificate {
273 stationarity: crate::model_types::OuterStationarityCertificate::AnalyticGradient {
274 grad_norm: 1e-2,
275 projected_grad_norm: 1e-2,
276 bound: 1e-6,
277 },
278 ..clean
279 };
280 assert_eq!(nonstationary.verdict(), Verdict::Insufficient);
281 assert!(!nonstationary.verdict().is_certified());
282 assert_eq!(nonstationary.claim().id, "outer-optimality");
284 assert!(nonstationary.evidence().contains_key("summary"));
285 }
286
287 #[test]
288 fn coreset_budget_alone_is_insufficient_but_decides_with_margin() {
289 let cert = CoresetCertificate::new(0.1, 0.0, 4, 32).expect("coreset cert");
290 assert_eq!(cert.verdict(), Verdict::Insufficient);
291 let req = cert.race_transfer_margin();
293 assert_eq!(
294 coreset_race_verdict(cert.certify_margin(req * 0.5)),
295 Verdict::Insufficient
296 );
297 assert_eq!(
298 coreset_race_verdict(cert.certify_margin(req * 2.0 + 1.0)),
299 Verdict::Certified
300 );
301 }
302
303 #[test]
304 fn enclosure_certifies_only_when_margin_clears_gap() {
305 let enc = LogdetEnclosure {
306 block_diag_logdet: 10.0,
307 lower: 9.9,
308 upper: 10.1,
309 rho: 0.3,
310 p2: 0.01,
311 p3: None,
312 };
313 assert_eq!(enc.verdict(), Verdict::Insufficient);
314 assert_eq!(enclosure_margin_verdict(&enc, 0.5), Verdict::Certified);
316 assert_eq!(enclosure_margin_verdict(&enc, 0.1), Verdict::Insufficient);
317 }
318
319 #[test]
320 fn collapse_terminal_is_unavailable_reseeded_is_insufficient() {
321 let reseeded = CollapseEvent {
322 iteration: 3,
323 atom: 1,
324 max_active_mass: 1e-4,
325 floor: 1e-3,
326 action: CollapseAction::Reseeded,
327 };
328 assert_eq!(reseeded.verdict(), Verdict::Insufficient);
329 let terminal = CollapseEvent {
330 action: CollapseAction::Terminal,
331 ..reseeded
332 };
333 assert_eq!(terminal.verdict(), Verdict::Unavailable);
334 }
335
336 #[test]
337 fn ledger_rolls_up_to_weakest_member() {
338 let mut ledger = CertificateLedger::new();
339 let clean = OuterCriterionCertificate {
340 stationarity: crate::model_types::OuterStationarityCertificate::AnalyticGradient {
341 grad_norm: 1e-8,
342 projected_grad_norm: 1e-8,
343 bound: 1e-6,
344 },
345 hessian_psd: Some(true),
346 lambdas_railed: Vec::new(),
347 curvature_floor: None,
348 };
349 let cert = CoresetCertificate::new(0.1, 0.0, 4, 32).expect("coreset");
350 ledger.record(&clean); ledger.record(&cert); assert_eq!(ledger.overall(), Verdict::Insufficient);
353 assert_eq!(ledger.verdict_of("outer-optimality"), Verdict::Certified);
354 assert_eq!(ledger.verdict_of("coreset-budget"), Verdict::Insufficient);
355 }
356}