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 e.insert("lambdas_railed_count", self.lambdas_railed.len().into());
79 e.insert("stationary", self.is_stationary().into());
80 e.insert("curvature_admissible", self.curvature_admissible().into());
81 e.insert("summary", self.summary().into());
82 e
83 }
84
85 fn verdict(&self) -> Verdict {
86 if self.certifies() {
87 Verdict::Certified
88 } else {
89 Verdict::Insufficient
90 }
91 }
92}
93
94impl Certificate for CoresetCertificate {
97 fn claim(&self) -> Claim {
98 Claim::new(
99 "coreset-budget",
100 "the selected row coreset reproduces the full-corpus evidence within \
101 a certified spectral + likelihood error budget; a race decision \
102 inherits the full-corpus verdict only when its margin clears this \
103 budget",
104 )
105 }
106
107 fn evidence(&self) -> Evidence {
108 let mut e = Evidence::new();
109 put_finite(&mut e, "eps_spectral", self.eps_spectral);
110 put_finite(&mut e, "eps_likelihood", self.eps_likelihood);
111 e.insert("dim_effective", self.dim_effective.into());
112 e.insert("n_selected", self.n_selected.into());
113 put_finite(&mut e, "logdet_error_bound", self.logdet_error_bound());
114 put_finite(&mut e, "race_transfer_margin", self.race_transfer_margin());
115 e
116 }
117
118 fn verdict(&self) -> Verdict {
119 if self.race_transfer_margin().is_finite() {
126 Verdict::Insufficient
127 } else {
128 Verdict::Unavailable
129 }
130 }
131}
132
133pub fn coreset_race_verdict(verdict: CoresetMarginVerdict) -> Verdict {
140 match verdict {
141 CoresetMarginVerdict::Certified { .. } => Verdict::Certified,
142 CoresetMarginVerdict::InsufficientMargin { .. } => Verdict::Insufficient,
143 }
144}
145
146pub fn enclosure_margin_verdict(enclosure: &LogdetEnclosure, decision_margin: f64) -> Verdict {
149 match enclosure.decide_within_margin(decision_margin) {
150 MarginVerdict::Decided { .. } => Verdict::Certified,
151 MarginVerdict::InsufficientMargin { .. } => Verdict::Insufficient,
152 }
153}
154
155impl Certificate for LogdetEnclosure {
158 fn claim(&self) -> Claim {
159 Claim::new(
160 "logdet-enclosure",
161 "the log-determinant is enclosed in a certified [lower, upper] \
162 interval whose midpoint is interchangeable with the exact value for \
163 any decision whose margin exceeds the enclosure gap",
164 )
165 }
166
167 fn evidence(&self) -> Evidence {
168 let mut e = Evidence::new();
169 put_finite(&mut e, "block_diag_logdet", self.block_diag_logdet);
170 put_finite(&mut e, "lower", self.lower);
171 put_finite(&mut e, "upper", self.upper);
172 put_finite(&mut e, "gap", self.gap());
173 put_finite(&mut e, "rho", self.rho);
174 put_finite(&mut e, "p2", self.p2);
175 match self.p3 {
176 Some(p3) => put_finite(&mut e, "p3", p3),
177 None => {
178 e.insert("p3", "n/a".into());
179 }
180 }
181 e
182 }
183
184 fn verdict(&self) -> Verdict {
185 if self.lower.is_finite() && self.upper.is_finite() && self.gap().is_finite() {
190 Verdict::Insufficient
191 } else {
192 Verdict::Unavailable
193 }
194 }
195}
196
197impl Certificate for CollapseEvent {
200 fn claim(&self) -> Claim {
201 Claim::new(
202 "structure-collapse",
203 "an atom's active mass fell below the collapse floor during the joint \
204 fit; the guard either reseeded it from a fresh basin or, once the \
205 reseed budget was exhausted, recorded the collapse as the objective's \
206 terminal verdict",
207 )
208 }
209
210 fn evidence(&self) -> Evidence {
211 let mut e = Evidence::new();
212 e.insert("iteration", self.iteration.into());
213 e.insert("atom", self.atom.into());
214 put_finite(&mut e, "max_active_mass", self.max_active_mass);
215 put_finite(&mut e, "floor", self.floor);
216 e.insert(
217 "action",
218 match self.action {
219 CollapseAction::Reseeded => "reseeded",
220 CollapseAction::Terminal => "terminal",
221 }
222 .into(),
223 );
224 e
225 }
226
227 fn verdict(&self) -> Verdict {
228 match self.action {
234 CollapseAction::Reseeded => Verdict::Insufficient,
235 CollapseAction::Terminal => Verdict::Unavailable,
236 }
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243 use gam_problem::topology_certificates::CertificateLedger;
244
245 #[test]
246 fn criterion_stationarity_and_curvature_control_verdict() {
247 let clean = OuterCriterionCertificate {
248 stationarity: crate::model_types::OuterStationarityCertificate::AnalyticGradient {
249 grad_norm: 1e-8,
250 projected_grad_norm: 1e-8,
251 bound: 1e-6,
252 },
253 hessian_psd: Some(true),
254 lambdas_railed: Vec::new(),
255 };
256 assert_eq!(clean.verdict(), Verdict::Certified);
257 assert!(clean.verdict().is_certified());
258
259 let nonstationary = OuterCriterionCertificate {
260 stationarity: crate::model_types::OuterStationarityCertificate::AnalyticGradient {
261 grad_norm: 1e-2,
262 projected_grad_norm: 1e-2,
263 bound: 1e-6,
264 },
265 ..clean
266 };
267 assert_eq!(nonstationary.verdict(), Verdict::Insufficient);
268 assert!(!nonstationary.verdict().is_certified());
269 assert_eq!(nonstationary.claim().id, "outer-optimality");
271 assert!(nonstationary.evidence().contains_key("summary"));
272 }
273
274 #[test]
275 fn coreset_budget_alone_is_insufficient_but_decides_with_margin() {
276 let cert = CoresetCertificate::new(0.1, 0.0, 4, 32).expect("coreset cert");
277 assert_eq!(cert.verdict(), Verdict::Insufficient);
278 let req = cert.race_transfer_margin();
280 assert_eq!(
281 coreset_race_verdict(cert.certify_margin(req * 0.5)),
282 Verdict::Insufficient
283 );
284 assert_eq!(
285 coreset_race_verdict(cert.certify_margin(req * 2.0 + 1.0)),
286 Verdict::Certified
287 );
288 }
289
290 #[test]
291 fn enclosure_certifies_only_when_margin_clears_gap() {
292 let enc = LogdetEnclosure {
293 block_diag_logdet: 10.0,
294 lower: 9.9,
295 upper: 10.1,
296 rho: 0.3,
297 p2: 0.01,
298 p3: None,
299 };
300 assert_eq!(enc.verdict(), Verdict::Insufficient);
301 assert_eq!(enclosure_margin_verdict(&enc, 0.5), Verdict::Certified);
303 assert_eq!(enclosure_margin_verdict(&enc, 0.1), Verdict::Insufficient);
304 }
305
306 #[test]
307 fn collapse_terminal_is_unavailable_reseeded_is_insufficient() {
308 let reseeded = CollapseEvent {
309 iteration: 3,
310 atom: 1,
311 max_active_mass: 1e-4,
312 floor: 1e-3,
313 action: CollapseAction::Reseeded,
314 };
315 assert_eq!(reseeded.verdict(), Verdict::Insufficient);
316 let terminal = CollapseEvent {
317 action: CollapseAction::Terminal,
318 ..reseeded
319 };
320 assert_eq!(terminal.verdict(), Verdict::Unavailable);
321 }
322
323 #[test]
324 fn ledger_rolls_up_to_weakest_member() {
325 let mut ledger = CertificateLedger::new();
326 let clean = OuterCriterionCertificate {
327 stationarity: crate::model_types::OuterStationarityCertificate::AnalyticGradient {
328 grad_norm: 1e-8,
329 projected_grad_norm: 1e-8,
330 bound: 1e-6,
331 },
332 hessian_psd: Some(true),
333 lambdas_railed: Vec::new(),
334 };
335 let cert = CoresetCertificate::new(0.1, 0.0, 4, 32).expect("coreset");
336 ledger.record(&clean); ledger.record(&cert); assert_eq!(ledger.overall(), Verdict::Insufficient);
339 assert_eq!(ledger.verdict_of("outer-optimality"), Verdict::Certified);
340 assert_eq!(ledger.verdict_of("coreset-budget"), Verdict::Insufficient);
341 }
342}