use std::fmt::Write;
use crate::families::custom_family::KktRefusalDiagnosis;
use crate::families::inner_status::{InnerFailure, classify_inner_error};
#[derive(Clone, Debug)]
pub(crate) struct SeedRejection {
pub seed_idx: usize,
pub phase: &'static str,
pub failure: InnerFailure,
}
impl SeedRejection {
pub(crate) fn from_message(seed_idx: usize, phase: &'static str, message: String) -> Self {
Self {
seed_idx,
phase,
failure: classify_inner_error(message),
}
}
}
#[derive(Clone, Debug, Default)]
pub(crate) struct StartupStats {
pub generated: usize,
pub screened: usize,
pub exact_validated: usize,
pub solver_started: usize,
pub rejected_by_kkt: usize,
pub rejected_by_domain: usize,
pub rejected_by_objective: usize,
pub rejected_by_budget: usize,
pub rejected_other: usize,
}
impl StartupStats {
pub(crate) fn from_rejections(
generated: usize,
screened: usize,
exact_validated: usize,
solver_started: usize,
rejections: &[SeedRejection],
) -> Self {
let mut stats = Self {
generated,
screened,
exact_validated,
solver_started,
..Self::default()
};
for rej in rejections {
match &rej.failure {
InnerFailure::CertRefused { .. } => stats.rejected_by_kkt += 1,
InnerFailure::LikelihoodFailure(_) => stats.rejected_by_domain += 1,
InnerFailure::BudgetExhausted { .. } | InnerFailure::TrustRegionFloor { .. } => {
stats.rejected_by_budget += 1
}
InnerFailure::IdentifiabilityFailure { .. } => stats.rejected_by_kkt += 1,
InnerFailure::Other(msg) => {
if msg.contains("non-finite")
|| msg.contains("not finite")
|| msg.contains("Infinity")
|| msg.contains("inf")
{
stats.rejected_by_objective += 1;
} else {
stats.rejected_other += 1;
}
}
}
}
stats
}
pub(crate) fn total_rejected(&self) -> usize {
self.rejected_by_kkt
+ self.rejected_by_domain
+ self.rejected_by_objective
+ self.rejected_by_budget
+ self.rejected_other
}
}
pub(crate) type StructuralKey = (KktRefusalDiagnosis, Option<String>);
pub(crate) fn structural_key(failure: &InnerFailure) -> Option<StructuralKey> {
match failure {
InnerFailure::CertRefused {
diagnosis,
carrying_block,
..
} => Some((*diagnosis, carrying_block.clone())),
_ => None,
}
}
pub(crate) fn uniform_structural_key(
rejections: &[SeedRejection],
min_count: usize,
) -> Option<StructuralKey> {
if rejections.len() < min_count {
return None;
}
let mut iter = rejections.iter();
let key = structural_key(&iter.next()?.failure)?;
for rej in iter {
let candidate = structural_key(&rej.failure)?;
if candidate != key {
return None;
}
}
Some(key)
}
pub(crate) fn structural_diagnosis_hint(key: &StructuralKey) -> String {
let (diagnosis, carrying) = key;
let carrying_label = carrying
.as_deref()
.map(|name| format!("smooth '{name}'"))
.unwrap_or_else(|| "the smooth carrying the dominant KKT residual".to_string());
match diagnosis {
KktRefusalDiagnosis::RankDeficientHPen => format!(
"structural rank deficiency in {carrying_label} — no seed is solvable. \
Either reduce the smooth's knot count, increase its smoothing parameter, \
or rely on the smooth-construction null-space absorption pass once it lands."
),
KktRefusalDiagnosis::PhantomMultiplierWithWellConditionedH => format!(
"every seed terminates at a phantom multiplier in {carrying_label} while H_pen \
is well-conditioned — the active-set projection captures part but not all of \
the gradient. Likely an incomplete inequality-constraint set or a basis whose \
range still hides a near-null direction the data does not constrain."
),
KktRefusalDiagnosis::ActiveSetIncomplete => format!(
"every seed exits with an incomplete active set on {carrying_label}. The \
outer cascade cannot grow the active set further without changing the \
smooth's constraint family."
),
KktRefusalDiagnosis::AliasingDetectedAtFit => format!(
"cross-block identifiability aliasing surfaced at {carrying_label} during the \
inner solve — a binding active set or λ-dependent direction created an alias \
the pre-fit audit could not see. Structural fix only: drop or reparameterise \
the aliased block; no rho-anneal will recover."
),
}
}
pub(crate) fn format_no_seeds_passed(
context: &str,
stats: &StartupStats,
rejections: &[SeedRejection],
structural: Option<&StructuralKey>,
early_exit_note: &str,
) -> String {
let mut out = String::new();
writeln!(
&mut out,
"no candidate seeds passed outer startup validation ({context}):"
)
.expect("writing to String cannot fail");
writeln!(
&mut out,
" generated={}, screened={}, exact_validated={}, solver_started={}",
stats.generated, stats.screened, stats.exact_validated, stats.solver_started,
)
.expect("writing to String cannot fail");
writeln!(
&mut out,
" rejection breakdown: rejected_by_kkt={}, rejected_by_domain={}, \
rejected_by_objective={}, rejected_by_budget={}, rejected_other={} (total={})",
stats.rejected_by_kkt,
stats.rejected_by_domain,
stats.rejected_by_objective,
stats.rejected_by_budget,
stats.rejected_other,
stats.total_rejected(),
)
.expect("writing to String cannot fail");
if let Some(key) = structural {
writeln!(
&mut out,
" uniform CertRefused: diagnosis={}, carrying-block={}",
key.0.as_str(),
key.1.as_deref().unwrap_or("<unknown>"),
)
.expect("writing to String cannot fail");
writeln!(&mut out, " diagnosis: {}", structural_diagnosis_hint(key))
.expect("writing to String cannot fail");
}
if !early_exit_note.is_empty() {
writeln!(&mut out, " {early_exit_note}").expect("writing to String cannot fail");
}
if !rejections.is_empty() {
writeln!(&mut out, " per-seed reasons:").expect("writing to String cannot fail");
for rej in rejections {
writeln!(
&mut out,
" seed {} ({}): {}",
rej.seed_idx,
rej.phase,
rej.failure.message(),
)
.expect("writing to String cannot fail");
}
}
while out.ends_with('\n') {
out.pop();
}
out
}
#[cfg(test)]
mod tests {
use super::*;
fn cert_refused(seed_idx: usize, block: &str) -> SeedRejection {
SeedRejection::from_message(
seed_idx,
"validation",
format!(
"cycle=7 cert REFUSED: residual=5.0e+05 > 4·tol=4.0e+03; \
carrying-block: {block} (idx=0, |g|=5.0e+05, |Sβ|=1.0e-03, \
|∇L-Sβ|=5.0e+05, |β|=1.0e+00, width=12); diagnosis: rank_deficient_H_pen; \
reduce knots"
),
)
}
#[test]
fn structural_key_extracts_diagnosis_only_for_cert_refused() {
let cert = cert_refused(0, "time_surface").failure;
let key = structural_key(&cert).expect("CertRefused must yield a structural key");
assert_eq!(key.0, KktRefusalDiagnosis::RankDeficientHPen);
assert_eq!(key.1.as_deref(), Some("time_surface"));
let domain = SeedRejection::from_message(
0,
"validation",
"likelihood evaluation failed: NaN response".to_string(),
)
.failure;
assert!(
structural_key(&domain).is_none(),
"non-cert-refused failures must not present a structural key"
);
}
#[test]
fn startup_stats_categorises_cert_refused() {
let rejections = vec![
cert_refused(0, "time_surface"),
cert_refused(1, "time_surface"),
];
let stats = StartupStats::from_rejections(5, 5, 5, 0, &rejections);
assert_eq!(stats.generated, 5);
assert_eq!(stats.solver_started, 0);
assert_eq!(stats.rejected_by_kkt, 2);
assert_eq!(stats.rejected_by_domain, 0);
assert_eq!(stats.total_rejected(), 2);
}
#[test]
fn uniform_structural_key_detects_repeating_cert_refused() {
let rejections = vec![
cert_refused(0, "time_surface"),
cert_refused(1, "time_surface"),
cert_refused(2, "time_surface"),
];
let key = uniform_structural_key(&rejections, 2).expect("uniform key");
assert_eq!(key.0, KktRefusalDiagnosis::RankDeficientHPen);
assert_eq!(key.1.as_deref(), Some("time_surface"));
}
#[test]
fn uniform_structural_key_rejects_mixed_blocks() {
let rejections = vec![cert_refused(0, "time_surface"), cert_refused(1, "marginal")];
assert!(uniform_structural_key(&rejections, 2).is_none());
}
#[test]
fn uniform_structural_key_rejects_mixed_failure_kinds() {
let cert = cert_refused(0, "time_surface");
let domain = SeedRejection::from_message(
1,
"validation",
"likelihood evaluation failed: NaN response".to_string(),
);
assert!(uniform_structural_key(&[cert, domain], 2).is_none());
}
#[test]
fn iterative_loop_triggers_early_exit_at_second_uniform_failure() {
const MIN_COUNT: usize = 2;
let mut rejections: Vec<SeedRejection> = Vec::new();
rejections.push(cert_refused(0, "time_surface"));
assert!(
uniform_structural_key(&rejections, MIN_COUNT).is_none(),
"single failure must not trigger early-exit; threshold guards \
against transient one-off CertRefused at exploration seeds"
);
rejections.push(cert_refused(1, "time_surface"));
let key = uniform_structural_key(&rejections, MIN_COUNT)
.expect("second matching failure must trigger early-exit");
assert_eq!(key.0, KktRefusalDiagnosis::RankDeficientHPen);
assert_eq!(key.1.as_deref(), Some("time_surface"));
rejections.push(cert_refused(2, "marginal"));
assert!(
uniform_structural_key(&rejections, MIN_COUNT).is_none(),
"structural key must be invalidated when a sibling block \
carries the residual at a later seed"
);
}
#[test]
fn structural_diagnosis_hint_names_next_action_per_diagnosis() {
let rank = structural_diagnosis_hint(&(
KktRefusalDiagnosis::RankDeficientHPen,
Some("time_surface".to_string()),
));
assert!(rank.contains("structural rank deficiency"));
assert!(rank.contains("time_surface"));
assert!(rank.contains("reduce the smooth's knot count"));
let phantom = structural_diagnosis_hint(&(
KktRefusalDiagnosis::PhantomMultiplierWithWellConditionedH,
None,
));
assert!(phantom.contains("phantom multiplier"));
assert!(phantom.contains("the smooth carrying the dominant KKT residual"));
let active = structural_diagnosis_hint(&(
KktRefusalDiagnosis::ActiveSetIncomplete,
Some("constraint_block".to_string()),
));
assert!(active.contains("incomplete active set"));
assert!(active.contains("constraint_block"));
}
#[test]
fn format_no_seeds_passed_payload_carries_full_triage_surface() {
let rejections = vec![
cert_refused(0, "time_surface"),
cert_refused(1, "time_surface"),
cert_refused(2, "time_surface"),
];
let stats = StartupStats::from_rejections(5, 5, 3, 0, &rejections);
let key = uniform_structural_key(&rejections, 2);
let msg = format_no_seeds_passed(
"custom family",
&stats,
&rejections,
key.as_ref(),
"early-exit triggered: every observed seed reported the same structural CertRefused",
);
assert!(msg.contains("generated=5"));
assert!(msg.contains("exact_validated=3"));
assert!(msg.contains("solver_started=0"));
assert!(msg.contains("rejected_by_kkt=3"));
assert!(msg.contains("diagnosis=rank_deficient_H_pen"));
assert!(msg.contains("carrying-block=time_surface"));
assert!(msg.contains("structural rank deficiency"));
assert!(msg.contains("early-exit triggered"));
assert!(msg.contains("seed 0 (validation)"));
assert!(msg.contains("seed 2 (validation)"));
}
#[test]
fn format_no_seeds_passed_emits_structured_payload() {
let rejections = vec![
cert_refused(0, "time_surface"),
cert_refused(1, "time_surface"),
];
let stats = StartupStats::from_rejections(5, 5, 5, 0, &rejections);
let key = uniform_structural_key(&rejections, 2);
let msg = format_no_seeds_passed("custom family", &stats, &rejections, key.as_ref(), "");
assert!(msg.contains("generated=5"));
assert!(msg.contains("solver_started=0"));
assert!(msg.contains("rejected_by_kkt=2"));
assert!(msg.contains("diagnosis=rank_deficient_H_pen"));
assert!(msg.contains("carrying-block=time_surface"));
assert!(msg.contains("structural rank deficiency"));
}
}