Skip to main content

gam_solve/rho_optimizer/
run.rs

1use super::*;
2use gam_problem::{StationarityRung, StationarityStandard};
3
4use super::asymptote_certificate::{
5    AsymptoteSample, AsymptoteSide, AsymptoteTolerances, AsymptoteVerdict, AsymptoteWindow,
6    MIN_TAIL_SAMPLES, assess_coordinate,
7};
8use super::rail_face::{
9    RailFaceLimit, RailFaceLimitOutcome, RailFaceProof, RailFaceVerdict, certify_rail_face,
10};
11
12pub(crate) const OPERATOR_TRUST_RESTART_RADIUS_FLOOR: f64 = 1.0e-6;
13
14/// Inner coefficient state bound to one exact outer seed.
15///
16/// A cached coefficient vector is only a valid initialization for the outer
17/// coordinate that produced it.  Keeping the coordinate beside the vector
18/// prevents a multi-start run from silently reusing one basin's coefficients
19/// at a different seed.
20#[derive(Clone, Debug)]
21pub(crate) struct BoundInnerSeed {
22    pub(crate) theta: Array1<f64>,
23    pub(crate) beta: Array1<f64>,
24}
25
26pub(crate) fn outer_theta_bitwise_eq(left: &Array1<f64>, right: &Array1<f64>) -> bool {
27    left.len() == right.len()
28        && left
29            .iter()
30            .zip(right.iter())
31            .all(|(left, right)| left.to_bits() == right.to_bits())
32}
33
34/// Install cached inner state only at the exact outer coordinate that owns it.
35///
36/// This function is called after a seed-attempt reset and immediately before
37/// the literal seed can be evaluated. Generated multistart candidates never
38/// inherit coefficients from another outer point.
39pub(crate) fn install_matching_initial_inner_seed(
40    obj: &mut dyn OuterObjective,
41    config: &OuterConfig,
42    seed: &Array1<f64>,
43    context: &str,
44) -> Result<(), EstimationError> {
45    let Some(bound) = config.initial_inner_seed.as_ref() else {
46        return Ok(());
47    };
48    if !outer_theta_bitwise_eq(&bound.theta, seed) {
49        return Ok(());
50    }
51    match obj.seed_inner_state(&bound.beta)? {
52        SeedOutcome::Installed => log::info!(
53            "[CACHE] beta-warm context={} theta_dim={} beta_dim={} action=installed",
54            context,
55            bound.theta.len(),
56            bound.beta.len(),
57        ),
58        SeedOutcome::NoSlot => log::warn!(
59            "[CACHE] beta-warm context={} theta_dim={} beta_dim={} action=skip \
60             reason=objective_has_no_inner_beta_slot",
61            context,
62            bound.theta.len(),
63            bound.beta.len(),
64        ),
65        SeedOutcome::Incompatible => log::info!(
66            "[CACHE] beta-warm context={} theta_dim={} beta_dim={} action=rho-only \
67             reason=seed_beta_incompatible_with_inner_state",
68            context,
69            bound.theta.len(),
70            bound.beta.len(),
71        ),
72    }
73    Ok(())
74}
75
76/// Temporarily require a complete inner solve.
77///
78/// Search-time REML evaluations may deliberately cap P-IRLS, but a sample
79/// used as mathematical evidence about the true profiled objective cannot.
80/// Seed samples, terminal certificates, and final state installation therefore
81/// lift the cap for the duration of their evaluation and restore the scheduler's
82/// value afterward. Callers that may hold capped cached state reset the
83/// objective before making the full-fidelity request.
84pub(crate) struct FullFidelityInnerCapGuard<'a> {
85    cap: &'a AtomicUsize,
86    previous: usize,
87}
88
89impl<'a> FullFidelityInnerCapGuard<'a> {
90    pub(crate) fn lift(feedback: &'a InnerProgressFeedback) -> Self {
91        let cap = feedback.cap.as_ref();
92        let previous = cap.swap(0, Ordering::Relaxed);
93        Self { cap, previous }
94    }
95}
96
97impl Drop for FullFidelityInnerCapGuard<'_> {
98    fn drop(&mut self) {
99        self.cap.store(self.previous, Ordering::Relaxed);
100    }
101}
102
103/// Configuration for the outer optimization runner.
104#[derive(Clone, Debug)]
105pub(crate) struct OuterConfig {
106    pub(crate) tolerance: f64,
107    /// Optional override for the *relative-cost-decrease* convergence stop,
108    /// decoupled from `tolerance`. `outer_gradient_tolerance` normally derives
109    /// BOTH the absolute projected-gradient floor
110    /// (`max(tolerance, scale·√ε_machine)`)
111    /// AND the relative-cost stop (`rel_cost = tolerance`) from the single
112    /// `tolerance`. That conflation forces a caller who needs a *tight absolute
113    /// floor* (to resolve λ to the genuine REML optimum at large `n`, where the
114    /// floor is `scale·√ε_machine`) to also accept a *tight rel-cost stop*,
115    /// which on a flat REML ridge never trips and grinds the optimizer to `max_iter` —
116    /// dozens of surplus O(D·p³) Laplace-derivative outer iterations (the #1082
117    /// multinomial smooth-by-factor wall-clock blow-up). When `Some(r)`, the
118    /// rel-cost stop uses `r` while the absolute floor keeps using `tolerance`
119    /// via `objective_scale`, so accuracy (absolute floor) and perf (loose
120    /// rel-cost) are selected independently. `None` preserves the legacy coupling
121    /// (`rel_cost = tolerance`) for every existing path byte-for-byte.
122    pub(crate) rel_cost_tolerance: Option<f64>,
123    pub(crate) max_iter: usize,
124    /// The model's canonical feasible outer domain. Every stationarity
125    /// certificate and rail report reasons against this box.
126    pub(crate) model_domain_bounds: Option<(Array1<f64>, Array1<f64>)>,
127    /// A temporary algorithmic subspace used only by an active-set polish.
128    /// It may narrow the model domain but never changes the feasible cone that
129    /// screening or mint is allowed to certify.
130    pub(crate) search_bounds_override: Option<(Array1<f64>, Array1<f64>)>,
131    pub(crate) seed_config: gam_problem::SeedConfig,
132    pub(crate) rho_bound: f64,
133    pub(crate) heuristic_lambdas: Option<Vec<f64>>,
134    pub(crate) initial_rho: Option<Array1<f64>>,
135    /// Additional explicit, model-derived starts. Unlike the generic seed
136    /// lattice these are supplied by the objective owner and survive the
137    /// `max_seeds` truncation; the certified keep-best loop optimizes each one.
138    pub(crate) initial_rho_candidates: Vec<Array1<f64>>,
139    /// Seed points an EARLIER round of this same [`run_outer`] call already
140    /// started and whose certification it already refused (#2569).
141    ///
142    /// Set only by the certify-resume loop, from
143    /// [`OuterResult::refused_seed_points`]. The plan runner drops these from
144    /// its cascade (never the caller's own `initial_rho`, which is the reseed
145    /// point the resume exists to explore), because re-running them from the
146    /// reset state they were refused in reproduces the recorded verdict digit
147    /// for digit — the same argument #2080 makes for replaying a recorded
148    /// cold-entry-leg refusal. Empty on every non-resume path, so the ordinary
149    /// cascade is unchanged.
150    pub(crate) previously_refused_seed_points: Vec<Array1<f64>>,
151    pub(crate) initial_inner_seed: Option<BoundInnerSeed>,
152    pub(crate) fallback_policy: FallbackPolicy,
153    pub(crate) screening_cap: Option<Arc<AtomicUsize>>,
154    pub(crate) screen_initial_rho: bool,
155    /// `initial_rho` came from a PRIOR FIT'S TERMINAL CERTIFICATE, not from a
156    /// heuristic, a mid-run checkpoint, or a caller's guess.
157    ///
158    /// The distinction is not where the number was stored, it is what is known
159    /// about it: a terminal certificate is a rho a previous outer run already
160    /// certified as stationary, so re-deriving it can only move it.
161    pub(crate) initial_rho_is_prior_terminal_certificate: bool,
162    /// Outer-aware inner-PIRLS iteration cap (sibling of `screening_cap`).
163    /// When set, the BFGS bridge drives this atomic on every accepted
164    /// gradient eval to coarsen the inner Newton solve at early outer iters
165    /// (when ρ is far from converged) and lift it back to full as
166    /// convergence approaches. Distinct from `screening_cap` in that it
167    /// does NOT suppress cache writes / warm-start updates / KKT
168    /// enforcement; it is purely a budget. See
169    /// `RemlObjectiveState::outer_inner_cap` for dual-cap semantics.
170    pub(crate) outer_inner_cap: Option<InnerProgressFeedback>,
171    pub(crate) operator_initial_trust_radius: Option<f64>,
172    pub(crate) arc_initial_regularization: Option<f64>,
173    /// Optional scale factor for the objective's natural magnitude.
174    /// Used to widen the absolute gradient-norm floor on objectives whose
175    /// gradient lives on a non-unit scale (e.g. Gaussian-identity REML at
176    /// large `n`, whose ∂/∂logλ inherits the O(n) likelihood constant).
177    /// `None` falls back to the bare `tolerance` floor.
178    pub(crate) objective_scale: Option<f64>,
179    /// BFGS line-search infinity-norm cap applied to the leading `rho_dim`
180    /// outer parameters (log-λ axes). Documented natural step for
181    /// `log(lambda)` is ≈ 5 (`e^5 ≈ 148`-fold smoothing-parameter change
182    /// per accepted outer iter — matches typical quasi-Newton direction
183    /// magnitude on flat REML surfaces). Setting this `None` disables the
184    /// rho-axis cap entirely.
185    pub(crate) bfgs_step_cap: Option<f64>,
186    /// BFGS line-search infinity-norm cap applied to the trailing `psi_dim`
187    /// outer parameters (kappa / aniso-log-scale axes). Required because
188    /// the kernel scale axes need much tighter control (`e^1 ≈ 2.7`-fold
189    /// per iter is plenty) — using the rho-axis cap here lets the optimizer
190    /// jump kappa by orders of magnitude per step and oscillate. Setting
191    /// this `None` disables the psi-axis cap.
192    pub(crate) bfgs_step_cap_psi: Option<f64>,
193    /// Optional persistent-cache session. When `Some`, every finite objective
194    /// evaluation is written through to disk (rate-limited, atomic-rename)
195    /// and the best on-disk rho is prepended as a seed at the start of each
196    /// plan attempt. Defaulted off so test-only paths skip filesystem I/O.
197    pub(crate) cache_session: Option<Arc<CacheSession>>,
198    /// Optional mirror cache sessions. Checkpoints and successful finalize
199    /// writes are also written to each of these sessions (different keys,
200    /// shared store). Used for hierarchical broadcast: the current best ρ is
201    /// written to the exact-key (primary) AND the data-independent
202    /// seed-prefix key so the next fit with related structure can warm-start
203    /// from this one, even after an interrupted run.
204    pub(crate) cache_mirror_sessions: Vec<Arc<CacheSession>>,
205    pub(crate) rho_uncertainty_problem_size: crate::rho_uncertainty::RhoUncertaintyProblemSize,
206    /// Converged exact outer Hessian `H(θ̂)` transferred from a prior
207    /// structurally-matching fit via the persistent cache (a warm-start *hit*),
208    /// in the full θ layout. When present and SPD, the BFGS host path seeds its
209    /// iter-0 metric with `InitialMetric::DenseInverseHessian(H⁻¹)` so the first
210    /// outer step is quasi-Newton instead of unscaled steepest descent — the
211    /// dominant LOSO line-search-bracketing cost (each bracketing probe is a
212    /// full inner joint-Newton re-solve). Strictly stronger than the scalar
213    /// `1/‖g₀‖` metric: it carries the full anisotropic curvature, which across
214    /// folds (one held-out point) is nearly identical to this fold's. Never
215    /// changes the converged optimum — BFGS reaches `∇V=0` under any SPD initial
216    /// metric. `None` on every cold-start / no-cache / pre-Hessian-schema path,
217    /// which falls back to the scalar warm metric byte-for-byte.
218    pub(crate) warm_start_outer_hessian: Option<Array2<f64>>,
219    /// Per-ρ-coordinate structural keys, in the objective's NATIVE (formula)
220    /// coordinate order, used to make the outer smoothing-parameter search
221    /// invariant to the order the user wrote the smooth terms / tensor margins
222    /// (#1538/#1539).
223    ///
224    /// When `Some` and the keys induce a non-identity canonical permutation,
225    /// [`run_outer`] reorders the coordinate layout the optimizer sees into a
226    /// stable canonical order (derived purely from the keys, never from the
227    /// native position) before seeding/optimizing, and inverts the permutation
228    /// on the returned ρ / gradient / Hessian so the caller still receives the
229    /// native layout. Seeding, multistart and tie-breaking then all operate on
230    /// the identical canonical layout for every term order, so both orders
231    /// reach the same λ̂ and the same fitted surface. `None` (or an identity
232    /// permutation) leaves the legacy native-order path byte-for-byte unchanged.
233    pub(crate) rho_canonical_keys: Option<Vec<u64>>,
234    /// A CALLER'S absolute requirement on the projected outer gradient norm,
235    /// honoured by the search and not merely checked at the end (#2568).
236    ///
237    /// The engine's own stationarity bound is a function of the FIT, not a
238    /// constant of the engine: it is a widening ladder anchored on the
239    /// criterion's magnitude and the optimizer's terminal state. That is right
240    /// for minting a model, and it means two fits on the same data in the same
241    /// call can be certified against bounds four orders apart -- measured on
242    /// #2568 at `2.0708e-4` for one and **exactly `1.000e0`** for its
243    /// companion, the latter being the saturated score-relative flat-valley
244    /// value -- a rung since deleted (#2458), because it was a constant
245    /// selected by an exit reason and it won precisely where the probe-noise
246    /// MEASUREMENT had declined to license any bound. A consumer
247    /// whose accuracy requirement is stricter than whatever the ladder happens
248    /// to compute had no supported way to impose it.
249    ///
250    /// Reading `|Pg|` off the summary and thresholding it afterwards is not the
251    /// same thing, and is the shape SPEC-20 exists to prevent: it mints a
252    /// replayable, diagnostics-clean fit the consumer must then reject. Nothing
253    /// made the optimizer *work harder* to reach the stricter standard.
254    ///
255    /// So this number enters in BOTH places that decide the outcome:
256    ///
257    /// * [`outer_gradient_tolerance`] floors the solver's convergence band at
258    ///   it, so the outer loop keeps going instead of stopping at a looser
259    ///   sealed bound -- the load-bearing half;
260    /// * the certificate's bound ladder is CAPPED by it, so a point the engine
261    ///   would have certified against a wider rung is refused, reporting
262    ///   [`StationarityBoundSource::CallerRequirement`] and naming the engine's
263    ///   own bound alongside it.
264    ///
265    /// The cap is applied to the ladder's TOP, after every widening rung, which
266    /// is why it cannot be defeated by a rung that fires later. It never
267    /// loosens: a requirement weaker than the engine's own bound leaves the
268    /// engine's in force, because a caller asking for less accuracy than the
269    /// engine already guarantees is not asking for anything.
270    ///
271    /// `None` reproduces today's behaviour byte-for-byte on every path.
272    pub(crate) required_projected_gradient_norm: Option<f64>,
273    /// Require the terminal mint to carry a measured, raw PSD Hessian.
274    ///
275    /// The general certificate may admit a tiny assembled negative direction
276    /// when the gradient-residue floor proves it unresolved. Profiled
277    /// nonconvex coefficient families cannot use that weaker answer: their
278    /// selected branch is a local minimum only when the analytic outer Hessian
279    /// itself is measured PSD. Declaring that requirement here lets the outer
280    /// recovery loop escape/re-optimize instead of contradicting the certificate
281    /// later during fit assembly.
282    pub(crate) require_measured_psd: bool,
283}
284
285impl Default for OuterConfig {
286    fn default() -> Self {
287        Self {
288            tolerance: 1e-5,
289            rel_cost_tolerance: None,
290            required_projected_gradient_norm: None,
291            require_measured_psd: false,
292            max_iter: 200,
293            model_domain_bounds: None,
294            search_bounds_override: None,
295            seed_config: gam_problem::SeedConfig::default(),
296            rho_bound: 30.0,
297            heuristic_lambdas: None,
298            initial_rho: None,
299            initial_rho_candidates: Vec::new(),
300            previously_refused_seed_points: Vec::new(),
301            initial_inner_seed: None,
302            fallback_policy: FallbackPolicy::Automatic,
303            screening_cap: None,
304            screen_initial_rho: false,
305            initial_rho_is_prior_terminal_certificate: false,
306            outer_inner_cap: None,
307            operator_initial_trust_radius: None,
308            arc_initial_regularization: None,
309            objective_scale: None,
310            bfgs_step_cap: None,
311            bfgs_step_cap_psi: None,
312            cache_session: None,
313            cache_mirror_sessions: Vec::new(),
314            rho_uncertainty_problem_size:
315                crate::rho_uncertainty::RhoUncertaintyProblemSize::default(),
316            warm_start_outer_hessian: None,
317            rho_canonical_keys: None,
318        }
319    }
320}
321
322// ─── OuterProblem builder ─────────────────────────────────────────────
323//
324// Declarative builder for outer optimization problems.  Derives
325// OuterCapability flags from high-level inputs (gradient/hessian
326// availability, psi dimension, EFS eligibility) so call sites never
327// hand-copy capability flags.
328
329/// Declarative outer-problem builder.  Produces both the
330/// [`OuterCapability`] (what the objective can provide) and the
331/// `OuterConfig` (how the runner should behave) from a small set
332/// of high-level declarations.
333pub struct OuterProblem {
334    n_params: usize,
335    gradient: Derivative,
336    hessian: DeclaredHessianForm,
337    prefer_gradient_only: bool,
338    disable_fixed_point: bool,
339    psi_dim: usize,
340    barrier_config: Option<BarrierConfig>,
341    tolerance: f64,
342    rel_cost_tolerance: Option<f64>,
343    /// See [`OuterConfig::required_projected_gradient_norm`] (#2568).
344    required_projected_gradient_norm: Option<f64>,
345    require_measured_psd: bool,
346    max_iter: usize,
347    bounds: Option<(Array1<f64>, Array1<f64>)>,
348    rho_bound: f64,
349    seed_config: gam_problem::SeedConfig,
350    heuristic_lambdas: Option<Vec<f64>>,
351    initial_rho: Option<Array1<f64>>,
352    initial_rho_candidates: Vec<Array1<f64>>,
353    fallback_policy: FallbackPolicy,
354    screening_cap: Option<Arc<AtomicUsize>>,
355    screen_initial_rho: bool,
356    outer_inner_cap: Option<InnerProgressFeedback>,
357    operator_initial_trust_radius: Option<f64>,
358    arc_initial_regularization: Option<f64>,
359    objective_scale: Option<f64>,
360    bfgs_step_cap: Option<f64>,
361    bfgs_step_cap_psi: Option<f64>,
362    cache_session: Option<Arc<CacheSession>>,
363    cache_mirror_sessions: Vec<Arc<CacheSession>>,
364    rho_uncertainty_problem_size: crate::rho_uncertainty::RhoUncertaintyProblemSize,
365    rho_canonical_keys: Option<Vec<u64>>,
366}
367
368impl OuterProblem {
369    pub fn new(n_params: usize) -> Self {
370        Self {
371            n_params,
372            gradient: Derivative::Unavailable,
373            hessian: DeclaredHessianForm::Unavailable,
374            // Closed-form/test objectives preserve explicit ARC availability;
375            // production family-ladder entry points opt into #2359's
376            // optimize-3/certify-4 protocol with
377            // `with_prefer_gradient_only(true)`.
378            prefer_gradient_only: false,
379            disable_fixed_point: false,
380            psi_dim: 0,
381            barrier_config: None,
382            tolerance: 1e-5,
383            rel_cost_tolerance: None,
384            required_projected_gradient_norm: None,
385            require_measured_psd: false,
386            max_iter: 200,
387            bounds: None,
388            rho_bound: 30.0,
389            seed_config: gam_problem::SeedConfig::default(),
390            heuristic_lambdas: None,
391            initial_rho: None,
392            initial_rho_candidates: Vec::new(),
393            fallback_policy: FallbackPolicy::Automatic,
394            screening_cap: None,
395            screen_initial_rho: false,
396            outer_inner_cap: None,
397            operator_initial_trust_radius: None,
398            arc_initial_regularization: None,
399            objective_scale: None,
400            bfgs_step_cap: None,
401            bfgs_step_cap_psi: None,
402            cache_session: None,
403            cache_mirror_sessions: Vec::new(),
404            rho_uncertainty_problem_size:
405                crate::rho_uncertainty::RhoUncertaintyProblemSize::default(),
406            rho_canonical_keys: None,
407        }
408    }
409
410    /// Supply per-ρ-coordinate structural keys (native/formula order) so the
411    /// outer search is canonicalized to be invariant to the order the smooth
412    /// terms / tensor margins were written (#1538/#1539). See
413    /// `OuterConfig::rho_canonical_keys`.
414    pub fn with_rho_canonical_keys(mut self, keys: Option<Vec<u64>>) -> Self {
415        self.rho_canonical_keys = keys;
416        self
417    }
418
419    pub fn with_gradient(mut self, d: Derivative) -> Self {
420        self.gradient = d;
421        self
422    }
423    pub fn with_hessian(mut self, form: DeclaredHessianForm) -> Self {
424        self.hessian = form;
425        self
426    }
427    /// Choose whether analytic Hessian work is reserved for terminal
428    /// certification (`true`, the generic derivative-ladder protocol) or may
429    /// also be consumed by the search (`false`, for closed-form objectives).
430    pub fn with_prefer_gradient_only(mut self, prefer_gradient_only: bool) -> Self {
431        self.prefer_gradient_only = prefer_gradient_only;
432        self
433    }
434    /// Forbid the planner from selecting EFS/HybridEfs, even when the
435    /// objective implements `eval_efs()` and the coordinate structure would
436    /// otherwise make pure/hybrid EFS eligible.
437    ///
438    /// Callers use this for families where the Wood-Fasiolo structural
439    /// property is known not to hold (e.g. GAMLSS/location-scale with
440    /// β-dependent joint Hessian), so EFS would stagnate and burn budget
441    /// before the automatic cascade falls back to gradient-based BFGS.
442    pub fn with_disable_fixed_point(mut self, disable: bool) -> Self {
443        self.disable_fixed_point = disable;
444        self
445    }
446    // MEASURE-JET ψ REGISTRATION: the engine below is already complete for a
447    // 3-coordinate measure-jet ψ group (s, α, ln τ) — `psi_dim` is generic,
448    // `with_bounds` carries the s ∈ (0, 2) box (the same convention matern κ
449    // uses for its log-κ window; no logistic reparameterization exists or is
450    // needed in-house), `with_bfgs_step_cap_psi` caps per-iteration ψ moves,
451    // and `DirectionalHyperParam::new_compact` (solver/reml/mod.rs) carries
452    // penalty-only first/second/cross jets with `is_penalty_like`
453    // auto-derived from the identically-zero design drift (∂X/∂ψ ≡ 0).
454    // Every remaining registration arm is formula-layer dispatch in
455    // src/terms/smooth.rs (eligibility in
456    // `spatial_term_supports_hyper_optimization`, dims in
457    // `spatial_dims_per_term`, seed/bounds/write-back on
458    // `SpatialLogKappaCoords`, the per-trial rebuild in
459    // `apply_log_kappa_to_term`, and the derivative bundle in
460    // `try_build_spatial_term_log_kappa_derivative`, which currently returns
461    // `Ok(None)` for `SmoothBasisSpec::MeasureJet`) plus the
462    // `build_measure_jet_basis_psi_derivatives` producer in
463    // src/terms/basis/measure_jet_smooth.rs; both are owned by the
464    // measure-jet terms actor. Registration stays gated on those arms — do
465    // NOT add measure-jet-specific branches to this engine.
466    pub fn with_psi_dim(mut self, dim: usize) -> Self {
467        self.psi_dim = dim;
468        self
469    }
470    pub fn with_barrier(mut self, cfg: Option<BarrierConfig>) -> Self {
471        self.barrier_config = cfg;
472        self
473    }
474    pub fn with_tolerance(mut self, tol: f64) -> Self {
475        self.tolerance = tol;
476        self
477    }
478    pub fn with_max_iter(mut self, n: usize) -> Self {
479        self.max_iter = n;
480        self
481    }
482    pub fn with_bounds(mut self, lo: Array1<f64>, hi: Array1<f64>) -> Self {
483        self.bounds = Some((lo, hi));
484        self
485    }
486    pub fn with_rho_bound(mut self, b: f64) -> Self {
487        self.rho_bound = b;
488        self
489    }
490    pub fn with_seed_config(mut self, sc: gam_problem::SeedConfig) -> Self {
491        self.seed_config = sc;
492        self
493    }
494    pub fn with_heuristic_lambdas(mut self, h: Vec<f64>) -> Self {
495        self.heuristic_lambdas = Some(h);
496        self
497    }
498    pub fn with_initial_rho(mut self, rho: Array1<f64>) -> Self {
499        self.initial_rho = Some(rho);
500        self
501    }
502    pub fn with_initial_rho_candidates(mut self, candidates: Vec<Array1<f64>>) -> Self {
503        self.initial_rho_candidates = candidates;
504        self
505    }
506    pub fn with_screening_cap(mut self, screening_cap: Arc<AtomicUsize>) -> Self {
507        self.screening_cap = Some(screening_cap);
508        self
509    }
510    /// Allow seed screening to rank the explicit initial rho against generated
511    /// candidates even when the effective seed budget is one. The default keeps
512    /// a user-provided initial point authoritative and avoids a separate
513    /// screening pass.
514    pub fn with_screen_initial_rho(mut self, screen_initial_rho: bool) -> Self {
515        self.screen_initial_rho = screen_initial_rho;
516        self
517    }
518    /// Wire the bidirectional inner-PIRLS feedback channel.
519    ///
520    /// The outer bridge writes a coarsened iteration cap into
521    /// `feedback.cap` on every accepted gradient/Hessian eval; the inner
522    /// solver writes back into `feedback.last_iters` /
523    /// `feedback.last_converged` after each non-screening solve so the
524    /// next outer iter's schedule can adapt to the inner solver's
525    /// actual convergence behavior. Typical caller passes
526    /// `InnerProgressFeedback {
527    ///     cap: Arc::clone(&reml_state.outer_inner_cap),
528    ///     last_iters: Arc::clone(&reml_state.last_inner_iters),
529    ///     last_converged: Arc::clone(&reml_state.last_inner_converged),
530    /// }` so the inner and outer observe the same atomics.
531    pub fn with_outer_inner_cap(mut self, feedback: InnerProgressFeedback) -> Self {
532        self.outer_inner_cap = Some(feedback);
533        self
534    }
535
536    /// Wire a one-shot "re-evaluate the inner solve COLD" signal that the outer
537    /// cost-stall guard raises when it grants a STUCK-stall escape (#2349).
538    ///
539    /// A profiled objective whose inner solve is warm-started along the outer
540    /// trajectory can carry value HYSTERESIS on a near-flat inner ridge — the
541    /// multinomial simplex-boundary regime where the softmax Fisher weight
542    /// `diag(p) − ppᵀ` collapses is the motivating case: two warm starts
543    /// converge to different ridge points whose Laplace `½log|H(β)|`, hence the
544    /// profiled objective, differ by more than the outer descent resolution, so
545    /// the optimizer's step-acceptance cannot separate real descent from that
546    /// hysteresis and grinds to `max_iter` at a non-stationary point. Uncapping
547    /// the inner cycle budget does not cure it (a fully converged warm solve
548    /// still lands on the warm-biased ridge point); the objective must re-solve
549    /// COLD to see a consistent surface.
550    ///
551    /// The caller shares this `Arc<AtomicBool>` with its objective closure and
552    /// consults it there, re-solving the inner problem from a canonical seed
553    /// (dropping the warm cache) whenever the flag is raised. The signal rides
554    /// the internal inner-cap feedback channel, but its `cap` slot is a private
555    /// throwaway so wiring the signal never perturbs the caller's own inner-cap
556    /// scheduling (custom families hold their real inner cap separately).
557    /// Objectives that do not warm-start, or never near-separate, simply never
558    /// observe the flag raised.
559    pub fn with_stuck_stall_cold_reeval_signal(self, signal: Arc<AtomicBool>) -> Self {
560        self.with_outer_inner_cap(InnerProgressFeedback {
561            cap: Arc::new(AtomicUsize::new(0)),
562            accepted_iter: Arc::new(AtomicUsize::new(0)),
563            // `last_iters == 0` ⇒ `snapshot()` returns `None` ⇒ no cap-schedule
564            // adaptation is derived from this dummy; `last_converged == true`
565            // matches the `None` default of `inner_solve_converged`, so
566            // terminal-fidelity gating is byte-for-byte unchanged.
567            last_iters: Arc::new(AtomicUsize::new(0)),
568            last_converged: Arc::new(AtomicBool::new(true)),
569            ift_residual: Arc::new(AtomicU64::new(f64::NAN.to_bits())),
570            accept_rho: Arc::new(AtomicU64::new(f64::NAN.to_bits())),
571            force_cold: signal,
572        })
573    }
574
575    /// Set the objective's natural magnitude scale, used to derive an
576    /// `n`-aware absolute gradient-norm floor. When set to `Some(s)`,
577    /// the runner uses `abs_floor = max(tol, s * √ε_machine)` for the
578    /// projected-gradient convergence check.
579    ///
580    /// Rationale: a fixed `abs = tol` (e.g. 1e-6) is appropriate when the
581    /// objective and its gradient live on a unit scale, but Gaussian-
582    /// identity REML carries an O(n) likelihood constant that flows into
583    /// ∂/∂logλ. At large-scale n the floor becomes binding even when the
584    /// relative-from-seed component (`rel_initial_grad * ‖g0‖`) declared
585    /// convergence iters earlier — chasing sub-ULP changes in log-λ at
586    /// the cost of repeated k²·n·p² analytic-Hessian assemblies.
587    pub fn with_objective_scale(mut self, scale: Option<f64>) -> Self {
588        self.objective_scale = scale.filter(|v| v.is_finite() && *v > 0.0);
589        self
590    }
591
592    /// Decouple the *relative-cost-decrease* convergence stop from the
593    /// absolute projected-gradient floor. By default both are derived from the
594    /// single `with_tolerance` value (`abs = max(tol, scale·√ε_machine)`,
595    /// `rel_cost = tol`). Supplying `Some(r)` here makes the rel-cost stop use
596    /// `r` while the absolute floor keeps using `tolerance` (so a caller can
597    /// keep a tight absolute floor for accuracy at large `n` AND a loose
598    /// rel-cost stop for perf on a flat REML ridge — see #1082). `None` keeps
599    /// the legacy coupling.
600    pub fn with_rel_cost_tolerance(mut self, rel_cost: Option<f64>) -> Self {
601        self.rel_cost_tolerance = rel_cost.filter(|v| v.is_finite() && *v > 0.0);
602        self
603    }
604
605    /// Require the returned fit's projected outer gradient norm to satisfy
606    /// `|Pg| <= requirement`, and make the SEARCH pursue it (#2568).
607    ///
608    /// This is the caller-side floor the engine's own stationarity bound could
609    /// not previously express. Without it the engine's data-scaled ladder is the
610    /// only standard applied, and because that ladder is a function of the fit,
611    /// a fit can be certified at `|Pg| = 5.564e-1` against a bound that
612    /// saturated to exactly `1.000e0` while its companion on the same data in
613    /// the same call was held to `2.0708e-4`.
614    ///
615    /// Two consequences, and the first is the point:
616    ///
617    /// 1. the outer loop's convergence band is floored at `requirement`, so it
618    ///    keeps optimizing rather than stopping at a looser sealed bound;
619    /// 2. the certificate cannot mint above `requirement` -- the bound ladder is
620    ///    capped at it, so an unmet requirement is a typed refusal naming both
621    ///    `requirement` and the engine's own bound.
622    ///
623    /// A refusal is the CORRECT outcome when the requirement is unreachable on
624    /// the design: `|Pg| = 5.564e-1` may be a genuine floor of the criterion
625    /// there, and saying so beats certifying against `1.0`. Callers who want the
626    /// engine's judgement unmodified pass `None`, which is the default and is
627    /// byte-for-byte today's behaviour.
628    ///
629    /// Non-finite and non-positive values are rejected rather than silently
630    /// clamped: a requirement of `0.0` or `NaN` is not a stricter standard, it
631    /// is an unsatisfiable one, and honouring it would grind the outer loop to
632    /// `max_iter` and then refuse every fit.
633    pub fn with_required_projected_gradient_norm(mut self, requirement: Option<f64>) -> Self {
634        self.required_projected_gradient_norm = requirement.filter(|v| v.is_finite() && *v > 0.0);
635        self
636    }
637
638    /// Cap the infinity-norm displacement of BFGS cost-only line-search probes
639    /// on the **rho axes** (the first `n_params - psi_dim` outer parameters,
640    /// = log-λ). Also scales the initial inverse metric so the first trial
641    /// direction respects the same local budget coordinate-wise. Documented
642    /// natural step on log-λ is ≈ 5; tighter values throttle BFGS and starve
643    /// convergence on flat REML valleys.
644    pub fn with_bfgs_step_cap(mut self, cap: Option<f64>) -> Self {
645        self.bfgs_step_cap = cap.filter(|v| v.is_finite() && *v > 0.0);
646        self
647    }
648
649    /// Cap the infinity-norm displacement of BFGS cost-only line-search probes
650    /// on the **psi axes** (the trailing `psi_dim` outer parameters, = kappa
651    /// or anisotropic log-scales). Mirrors [`Self::with_bfgs_step_cap`] but
652    /// scoped to kernel-scale parameters whose natural step is much smaller
653    /// than log-λ (≈ ln 2 per iter keeps kappa from oscillating). Without
654    /// this split, a uniform rho-scale cap lets psi explode while a uniform
655    /// psi-scale cap throttles rho — both fail the survival-marginal-slope
656    /// path at large scale, where rho needs |d|≈5 while psi wants |d|≤1.
657    pub fn with_bfgs_step_cap_psi(mut self, cap: Option<f64>) -> Self {
658        self.bfgs_step_cap_psi = cap.filter(|v| v.is_finite() && *v > 0.0);
659        self
660    }
661
662    pub fn with_cache_session(mut self, session: Arc<CacheSession>) -> Self {
663        self.cache_session = Some(session);
664        self
665    }
666
667    /// Attach mirror cache sessions that receive a broadcast copy of
668    /// the final-result finalize write. See
669    /// `OuterConfig::cache_mirror_sessions`.
670    pub fn with_cache_mirror_sessions(mut self, sessions: Vec<Arc<CacheSession>>) -> Self {
671        self.cache_mirror_sessions = sessions;
672        self
673    }
674
675    pub fn with_problem_size(mut self, n_obs: usize, p_coefficients: usize) -> Self {
676        self.rho_uncertainty_problem_size = crate::rho_uncertainty::RhoUncertaintyProblemSize {
677            n_obs: Some(n_obs),
678            p_coefficients: Some(p_coefficients),
679        };
680        self
681    }
682
683    /// Override the fallback policy. Default is [`FallbackPolicy::Automatic`].
684    ///
685    /// Set [`FallbackPolicy::Disabled`] when the caller requires the primary
686    /// plan to stand on its own. Exact-Hessian objectives use this to ensure
687    /// failures surface on the analytic geometry instead of being reinterpreted
688    /// by a different optimizer class.
689    pub fn with_fallback_policy(mut self, policy: FallbackPolicy) -> Self {
690        self.fallback_policy = policy;
691        self
692    }
693
694    /// Demand a measured PSD analytic Hessian at the terminal mint.
695    ///
696    /// Use this when a downstream coefficient-mode selection is only defined
697    /// for a certified local minimum, rather than for a merely stationary point
698    /// whose tiny negative curvature was cleared by the gradient-residue floor.
699    pub fn with_require_measured_psd(mut self, required: bool) -> Self {
700        self.require_measured_psd = required;
701        self
702    }
703
704    /// Derive the capability flags from the builder state.
705    /// `fixed_point_available` is set to `false` here; `build_objective`
706    /// overrides it based on whether an EFS closure is actually provided.
707    fn capability(&self) -> OuterCapability {
708        OuterCapability {
709            gradient: self.gradient,
710            hessian: self.hessian,
711            prefer_gradient_only: self.prefer_gradient_only,
712            disable_fixed_point: self.disable_fixed_point,
713            n_params: self.n_params,
714            psi_dim: self.psi_dim,
715            fixed_point_available: false,
716            barrier_config: self.barrier_config.clone(),
717        }
718    }
719
720    /// Derive the runner configuration from the builder state.
721    pub(crate) fn config(&self) -> OuterConfig {
722        OuterConfig {
723            tolerance: self.tolerance,
724            rel_cost_tolerance: self.rel_cost_tolerance,
725            required_projected_gradient_norm: self.required_projected_gradient_norm,
726            require_measured_psd: self.require_measured_psd,
727            max_iter: self.max_iter,
728            model_domain_bounds: self.bounds.clone(),
729            search_bounds_override: None,
730            seed_config: self.seed_config,
731            rho_bound: self.rho_bound,
732            heuristic_lambdas: self.heuristic_lambdas.clone(),
733            initial_rho: self.initial_rho.clone(),
734            initial_rho_candidates: self.initial_rho_candidates.clone(),
735            previously_refused_seed_points: Vec::new(),
736            initial_inner_seed: None,
737            fallback_policy: self.fallback_policy,
738            screening_cap: self.screening_cap.clone(),
739            screen_initial_rho: self.screen_initial_rho,
740            // Only the cache's final-hit path can establish this, and it says
741            // so where it sets `initial_rho`.
742            initial_rho_is_prior_terminal_certificate: false,
743            outer_inner_cap: self.outer_inner_cap.clone(),
744            operator_initial_trust_radius: self.operator_initial_trust_radius,
745            arc_initial_regularization: self.arc_initial_regularization,
746            objective_scale: self.objective_scale,
747            bfgs_step_cap: self.bfgs_step_cap,
748            bfgs_step_cap_psi: self.bfgs_step_cap_psi,
749            cache_session: self.cache_session.clone(),
750            cache_mirror_sessions: self.cache_mirror_sessions.clone(),
751            rho_uncertainty_problem_size: self.rho_uncertainty_problem_size,
752            // Populated only by the persistent-cache resume path in `run` after
753            // a warm-start hit decodes a converged outer Hessian.
754            warm_start_outer_hessian: None,
755            rho_canonical_keys: self.rho_canonical_keys.clone(),
756        }
757    }
758
759    /// Construct a [`ClosureObjective`] with capability flags derived from the
760    /// builder state **and** the closures actually provided.
761    ///
762    /// `fixed_point_available` is set to `true` when `efs_fn` is `Some`,
763    /// regardless of whether `.with_efs()` was called.  This is the canonical
764    /// way to create production objectives — it eliminates the drift risk of
765    /// manually entering capability flags.
766    pub fn build_objective<S, Fc, Fe, Fr, Fefs>(
767        &self,
768        state: S,
769        cost_fn: Fc,
770        eval_fn: Fe,
771        reset_fn: Option<Fr>,
772        efs_fn: Option<Fefs>,
773    ) -> ClosureObjective<S, Fc, Fe, Fr, Fefs>
774    where
775        Fc: FnMut(&mut S, &Array1<f64>) -> Result<f64, EstimationError>,
776        Fe: FnMut(&mut S, &Array1<f64>) -> Result<OuterEval, EstimationError>,
777        Fr: FnMut(&mut S),
778        Fefs: FnMut(&mut S, &Array1<f64>) -> Result<EfsEval, EstimationError>,
779    {
780        let mut cap = self.capability();
781        // Derive fixed_point_available from whether the caller actually
782        // provided an EFS hook, rather than relying on manual flags.
783        cap.fixed_point_available = efs_fn.is_some();
784        ClosureObjective {
785            state,
786            cap,
787            cost_fn,
788            eval_fn,
789            eval_order_fn: None,
790            reset_fn,
791            efs_fn,
792            fixed_point_certificate_fn: None,
793            exact_polish_fn: None,
794            rail_face_limit_fn: None,
795            soft_rho_guard_gradient_fn: None,
796            criterion_invariance_fn: None,
797            screening_proxy_fn: None::<fn(&mut S, &Array1<f64>) -> Result<f64, EstimationError>>,
798            seed_fn: None::<fn(&mut S, &Array1<f64>) -> Result<SeedOutcome, EstimationError>>,
799            terminal_eval_order: None,
800        }
801    }
802
803    /// Construct a [`ClosureObjective`] with an order-aware evaluation hook.
804    ///
805    /// This lets the runner request first-order vs second-order work based on
806    /// the active outer plan while preserving the legacy eager `eval_fn`.
807    pub fn build_objective_with_eval_order<S, Fc, Fe, Feo, Fr, Fefs>(
808        &self,
809        state: S,
810        cost_fn: Fc,
811        eval_fn: Fe,
812        eval_order_fn: Feo,
813        reset_fn: Option<Fr>,
814        efs_fn: Option<Fefs>,
815    ) -> ClosureObjective<S, Fc, Fe, Fr, Fefs, Feo>
816    where
817        Fc: FnMut(&mut S, &Array1<f64>) -> Result<f64, EstimationError>,
818        Fe: FnMut(&mut S, &Array1<f64>) -> Result<OuterEval, EstimationError>,
819        Feo: FnMut(&mut S, &Array1<f64>, OuterEvalOrder) -> Result<OuterEval, EstimationError>,
820        Fr: FnMut(&mut S),
821        Fefs: FnMut(&mut S, &Array1<f64>) -> Result<EfsEval, EstimationError>,
822    {
823        let mut cap = self.capability();
824        cap.fixed_point_available = efs_fn.is_some();
825        ClosureObjective {
826            state,
827            cap,
828            cost_fn,
829            eval_fn,
830            eval_order_fn: Some(eval_order_fn),
831            reset_fn,
832            efs_fn,
833            fixed_point_certificate_fn: None,
834            exact_polish_fn: None,
835            rail_face_limit_fn: None,
836            soft_rho_guard_gradient_fn: None,
837            criterion_invariance_fn: None,
838            screening_proxy_fn: None::<fn(&mut S, &Array1<f64>) -> Result<f64, EstimationError>>,
839            seed_fn: None::<fn(&mut S, &Array1<f64>) -> Result<SeedOutcome, EstimationError>>,
840            terminal_eval_order: None,
841        }
842    }
843
844    /// Construct a [`ClosureObjective`] with both an order-aware evaluation
845    /// hook and a custom seed-screening ranking proxy. The proxy fires only
846    /// when the cascade in `rank_seeds_with_screening` calls it; outside
847    /// screening the regular cost path is unaffected.
848    pub fn build_objective_with_screening_proxy<S, Fc, Fe, Feo, Fr, Fefs, Fsp>(
849        &self,
850        state: S,
851        cost_fn: Fc,
852        eval_fn: Fe,
853        eval_order_fn: Feo,
854        reset_fn: Option<Fr>,
855        efs_fn: Option<Fefs>,
856        screening_proxy_fn: Fsp,
857    ) -> ClosureObjective<S, Fc, Fe, Fr, Fefs, Feo, Fsp>
858    where
859        Fc: FnMut(&mut S, &Array1<f64>) -> Result<f64, EstimationError>,
860        Fe: FnMut(&mut S, &Array1<f64>) -> Result<OuterEval, EstimationError>,
861        Feo: FnMut(&mut S, &Array1<f64>, OuterEvalOrder) -> Result<OuterEval, EstimationError>,
862        Fr: FnMut(&mut S),
863        Fefs: FnMut(&mut S, &Array1<f64>) -> Result<EfsEval, EstimationError>,
864        Fsp: FnMut(&mut S, &Array1<f64>) -> Result<f64, EstimationError>,
865    {
866        let mut cap = self.capability();
867        cap.fixed_point_available = efs_fn.is_some();
868        ClosureObjective {
869            state,
870            cap,
871            cost_fn,
872            eval_fn,
873            eval_order_fn: Some(eval_order_fn),
874            reset_fn,
875            efs_fn,
876            fixed_point_certificate_fn: None,
877            exact_polish_fn: None,
878            rail_face_limit_fn: None,
879            soft_rho_guard_gradient_fn: None,
880            criterion_invariance_fn: None,
881            screening_proxy_fn: Some(screening_proxy_fn),
882            seed_fn: None::<fn(&mut S, &Array1<f64>) -> Result<SeedOutcome, EstimationError>>,
883            terminal_eval_order: None,
884        }
885    }
886
887    /// Run the outer optimization with a given objective.
888    pub fn run(
889        &self,
890        obj: &mut dyn OuterObjective,
891        context: &str,
892    ) -> Result<OuterResult, EstimationError> {
893        let mut config = self.config();
894        let objective_lower = obj.outer_domain_lower_bound()?;
895        let objective_upper = obj.outer_domain_upper_bound()?;
896        if objective_lower.is_some() || objective_upper.is_some() {
897            install_objective_domain(&mut config, self.n_params, objective_lower, objective_upper)?;
898        }
899        let Some(session) = config.cache_session.clone() else {
900            return run_outer(obj, &config, context);
901        };
902        let key_hex = session.key().to_hex();
903        let short_key = &key_hex[..8.min(key_hex.len())];
904        let mut had_hit = false;
905        let mut cached_inner_seed: Option<BoundInnerSeed> = None;
906        if let Some(loaded) = session.try_load_with_source() {
907            match classify_cache_entry_for_outer(&loaded, self.n_params) {
908                CacheSeedDecision::ExactFinal {
909                    rho,
910                    beta,
911                    iterations,
912                    prior_obj_display,
913                } => {
914                    log::info!(
915                        "[CACHE] final-hit key={}.. context={} rho_dim={} prior_obj={:.6e} iter={} action=resume-and-recertify",
916                        short_key,
917                        context,
918                        rho.len(),
919                        prior_obj_display,
920                        iterations,
921                    );
922                    config.initial_rho = Some(rho.clone());
923                    config.screen_initial_rho = false;
924                    config.initial_rho_is_prior_terminal_certificate = true;
925                    if !beta.is_empty() {
926                        cached_inner_seed = Some(BoundInnerSeed {
927                            theta: rho,
928                            beta: Array1::from_vec(beta),
929                        });
930                    }
931                    had_hit = true;
932                }
933                CacheSeedDecision::Seed {
934                    rho,
935                    beta,
936                    hessian,
937                    prior_obj_display,
938                    iteration,
939                } => {
940                    let beta_len = beta.len();
941                    let beta_arr = if beta.is_empty() {
942                        None
943                    } else {
944                        Some(Array1::from_vec(beta))
945                    };
946                    // Adopt the transferred converged outer Hessian only when it
947                    // matches this fit's full-θ dimension; a dimension drift
948                    // (structural change the cache key did not capture) falls
949                    // back to the scalar warm metric in run_plan.
950                    config.warm_start_outer_hessian = if self.hessian.is_analytic() {
951                        hessian.and_then(|(dim, flat)| {
952                            if dim == self.n_params && flat.len() == dim * dim {
953                                Array2::from_shape_vec((dim, dim), flat).ok()
954                            } else {
955                                None
956                            }
957                        })
958                    } else {
959                        None
960                    };
961                    if config
962                        .initial_rho
963                        .as_ref()
964                        .is_none_or(|initial| initial != rho)
965                    {
966                        log::info!(
967                            "[CACHE] hit  key={}.. context={} rho_dim={} beta_dim={} prior_obj={:.6e} iter={}",
968                            short_key,
969                            context,
970                            rho.len(),
971                            beta_len,
972                            prior_obj_display,
973                            iteration,
974                        );
975                        config.initial_rho = Some(rho.clone());
976                        config.screen_initial_rho = false;
977                        had_hit = true;
978                    } else {
979                        log::info!(
980                            "[CACHE] hit  key={}.. context={} rho_dim={} beta_dim={} already-aligned prior_obj={:.6e}",
981                            short_key,
982                            context,
983                            rho.len(),
984                            beta_len,
985                            prior_obj_display,
986                        );
987                        had_hit = true;
988                    }
989                    if let Some(beta) = beta_arr {
990                        cached_inner_seed = Some(BoundInnerSeed { theta: rho, beta });
991                    }
992                }
993                CacheSeedDecision::Discard {
994                    reason: "payload-shape-mismatch",
995                    ..
996                } => {
997                    log::info!(
998                        "[CACHE] skip key={}.. context={} reason=payload-shape-mismatch n_params={}",
999                        short_key,
1000                        context,
1001                        self.n_params,
1002                    );
1003                }
1004                CacheSeedDecision::Discard {
1005                    reason,
1006                    prior_obj_display,
1007                    all_rho_finite,
1008                } => {
1009                    log::info!(
1010                        "[CACHE] skip key={}.. context={} reason={} prior_obj={:.6e} all_rho_finite={}",
1011                        short_key,
1012                        context,
1013                        reason,
1014                        prior_obj_display,
1015                        all_rho_finite.unwrap_or(false),
1016                    );
1017                }
1018            }
1019        } else {
1020            log::info!(
1021                "[CACHE] miss key={}.. context={} reason=fresh-fingerprint n_params={}",
1022                short_key,
1023                context,
1024                self.n_params,
1025            );
1026        }
1027        // Preserve the ownership relation between a cached coefficient vector
1028        // and the exact outer coordinate that produced it. The runner installs
1029        // this seed only after resetting for that bitwise-matching candidate;
1030        // it is never replayed at another generated seed.
1031        config.initial_inner_seed = cached_inner_seed;
1032        let mut checkpointing = CheckpointingObjective::new(
1033            obj,
1034            Arc::clone(&session),
1035            config.cache_mirror_sessions.clone(),
1036        );
1037        let result = run_outer(&mut checkpointing, &config, context);
1038        // Attach β only when a beta-bearing evaluation surfaced it at this
1039        // exact final ρ. Scalar terminal audits carry no β and may follow an
1040        // evaluation elsewhere; a bare "last β" would manufacture a false
1041        // (ρ, β) pair and let machine history select the next fit's basin
1042        // (#2486). A rho-only payload is slower to resume but remains honest.
1043        let final_beta = result
1044            .as_ref()
1045            .ok()
1046            .and_then(|result| checkpointing.inner_beta_for(&result.rho));
1047        if let Ok(result) = result.as_ref()
1048            && result.final_value.is_finite()
1049            && result.converged()
1050            && result
1051                .criterion_certificate
1052                .as_ref()
1053                .is_some_and(OuterCriterionCertificate::certifies)
1054            && let Some(bytes) = encode_iterate(
1055                &result.rho,
1056                final_beta.as_ref(),
1057                result.final_hessian.as_ref(),
1058                result.final_value,
1059                result.iterations as u64,
1060            )
1061        {
1062            let saved = session.finalize(
1063                &bytes,
1064                Some(result.final_value),
1065                Some(result.iterations as u64),
1066            );
1067            if saved {
1068                log::info!(
1069                    "[CACHE] save key={}.. context={} final_obj={:.6e} iter={} resumed={}",
1070                    short_key,
1071                    context,
1072                    result.final_value,
1073                    result.iterations,
1074                    had_hit,
1075                );
1076            }
1077            // Broadcast finalize to mirror keys. The seed-prefix mirror
1078            // exists so future fits with related-but-not-identical
1079            // structure can warm-start from this run via the dispatcher's
1080            // prefix lookup.
1081            for mirror in &config.cache_mirror_sessions {
1082                let mirror_saved = mirror.finalize(
1083                    &bytes,
1084                    Some(result.final_value),
1085                    Some(result.iterations as u64),
1086                );
1087                if mirror_saved {
1088                    let mirror_hex = mirror.key().to_hex();
1089                    log::info!(
1090                        "[CACHE] save key={}.. context={} mirror final_obj={:.6e} iter={}",
1091                        &mirror_hex[..8.min(mirror_hex.len())],
1092                        context,
1093                        result.final_value,
1094                        result.iterations,
1095                    );
1096                }
1097            }
1098        }
1099        result
1100    }
1101
1102    /// Run the outer optimization and return an unforgeable certified-result
1103    /// carrier.  Callers that only need checkpoints or diagnostics should use
1104    /// [`Self::run`]; fit assembly after an optimized outer coordinate must use
1105    /// this boundary so a caller-constructed [`OuterResult`] cannot mint
1106    /// convergence provenance.
1107    pub fn run_certified(
1108        &self,
1109        obj: &mut dyn OuterObjective,
1110        context: &str,
1111    ) -> Result<CertifiedOuterResult, EstimationError> {
1112        let result = self.run(obj, context)?;
1113        CertifiedOuterResult::from_optimizer_result(result).map_err(|reason| {
1114            EstimationError::RemlOptimizationFailed(format!(
1115                "{context}: outer result failed certified-fit validation: {reason}"
1116            ))
1117        })
1118    }
1119}
1120
1121/// Internal outcome of one planned solver/multistart attempt.
1122///
1123/// Exhausted checkpoints carry resumable work only. They never pass through
1124/// finalization, cache promotion, uncertainty diagnostics, or fitted-model
1125/// construction.
1126pub(crate) enum PlanRunOutcome {
1127    Converged(OuterResult),
1128    Exhausted(OuterResult),
1129    FirstOrderFallbackRequested(FirstOrderFallbackRequest),
1130    FixedPointContinuationRequested(FixedPointContinuationRequest),
1131}
1132
1133/// Which certificate concluded a CONVERGED outer run (#2235/#2241).
1134///
1135/// `OuterResult.converged == true` bundles genuinely different endings, each
1136/// with its own certificate. Distinguishing them is pure evidence for the
1137/// caller's termination report — every variant is a converged fit. There is
1138/// deliberately no "budget/freeze" variant: exhaustion is a typed error
1139/// carrying the resume checkpoint, never a minted fit (SPEC 20; the #2235
1140/// forcing-function redesign deleted the freeze lanes).
1141#[derive(Clone, Copy, Debug, PartialEq)]
1142pub enum OuterConvergedVia {
1143    /// The bound-projected analytic gradient at the returned point cleared the
1144    /// solver's absolute/score-scaled stationarity tolerance.
1145    GradientStationary,
1146    /// Criterion-flat certificate (#2241/#2253): the criterion stalled over the
1147    /// cost-stall window and the residual projected gradient sits inside the
1148    /// flat certificate band — the score-relative stationarity bound
1149    /// (`flat_valley_converged_grad_bound`), the probe-noise-floor bound
1150    /// measured from the stall window's own value scatter, and/or the
1151    /// curvature-scaled Newton-decrement bound (`newton_predicted_decrease`),
1152    /// under which a residual above the gradient-magnitude bands is still
1153    /// stationary when the second-order-predicted improvement `½·gᵀH⁻¹g` is below
1154    /// the outer objective tolerance. `certificate_bound` is the operative
1155    /// (widened) bound the residual actually cleared.
1156    CriterionFlat {
1157        residual_grad_norm: f64,
1158        certificate_bound: f64,
1159    },
1160    /// Every optimized coordinate carried an explicit analytic fixed-point
1161    /// equation and the KKT-projected residual cleared the solver tolerance.
1162    FixedPointStationary {
1163        projected_residual_inf_norm: f64,
1164        certificate_bound: f64,
1165    },
1166    /// Fellner–Schall model-state fixed point (#2235 verdict 2): two
1167    /// consecutive outer evaluations restored the same banked incumbent, so a
1168    /// further outer update provably does not change the fitted state. The
1169    /// analytic first-order certificate is still taken at the incumbent.
1170    RecurrentIncumbent { consecutive_restores: usize },
1171    /// Stationary-at-asymptote (#2348 Inc 1 / #2299 layer 3): the interior
1172    /// (non-railed) coordinates are gradient-stationary, and every coordinate
1173    /// railed at the infinite-/zero-smoothing box bound is certified on a
1174    /// confirmed exponential tail (Thm 2.1) whose fitted model has reached the
1175    /// rail limit to within the estimand tolerance. The typed rail supersedes
1176    /// the generic gradient/criterion-flat verdict for a railed optimum.
1177    AsymptoteStationary { rails: usize },
1178}
1179
1180impl OuterConvergedVia {
1181    /// Stable wire name for termination reports; the enum owns the vocabulary
1182    /// so bindings marshal instead of mapping.
1183    pub fn as_str(&self) -> &'static str {
1184        match self {
1185            Self::GradientStationary => "converged_stationary",
1186            Self::CriterionFlat { .. } => "converged_criterion_flat",
1187            Self::FixedPointStationary { .. } => "converged_fixed_point",
1188            Self::RecurrentIncumbent { .. } => "incumbent_stationary",
1189            Self::AsymptoteStationary { .. } => "converged_asymptote_rail",
1190        }
1191    }
1192}
1193
1194/// Typed lifecycle of an outer optimization result.
1195///
1196/// A solver claim and an analytic certificate are different stages, but they
1197/// belong to one state machine. Encoding them in one enum makes it impossible
1198/// to retain a certified success verdict after a later certificate refusal.
1199#[derive(Clone, Copy, Debug, PartialEq)]
1200enum OuterTermination {
1201    /// The solver exhausted or refused without claiming convergence.
1202    Exhausted,
1203    /// The solver claimed convergence, but no terminal analytic certificate
1204    /// currently authorizes the point. `proposed_via` is reserved for the
1205    /// recurrent-incumbent fixed-point signal that certification must corroborate.
1206    SolverClaimed {
1207        proposed_via: Option<OuterConvergedVia>,
1208    },
1209    /// A terminal analytic certificate authorizes the point and records why.
1210    Certified(OuterConvergedVia),
1211}
1212
1213impl OuterTermination {
1214    fn from_solver_claim(claimed: bool) -> Self {
1215        if claimed {
1216            Self::SolverClaimed { proposed_via: None }
1217        } else {
1218            Self::Exhausted
1219        }
1220    }
1221
1222    fn solver_claimed_convergence(self) -> bool {
1223        !matches!(self, Self::Exhausted)
1224    }
1225
1226    fn is_certified(self) -> bool {
1227        matches!(self, Self::Certified(_))
1228    }
1229
1230    fn certified_via(self) -> Option<OuterConvergedVia> {
1231        match self {
1232            Self::Certified(via) => Some(via),
1233            Self::Exhausted | Self::SolverClaimed { .. } => None,
1234        }
1235    }
1236
1237    fn proposed_via(self) -> Option<OuterConvergedVia> {
1238        match self {
1239            Self::SolverClaimed { proposed_via } => proposed_via,
1240            Self::Exhausted | Self::Certified(_) => None,
1241        }
1242    }
1243
1244    /// Revoke any earlier screening certificate before measuring a new
1245    /// screening/mint verdict. Only the model-state recurrent-incumbent signal
1246    /// survives as a proposal; ordinary gradient/rail verdicts must be re-earned.
1247    fn begin_certification(&mut self) {
1248        let proposed_via = match *self {
1249            Self::SolverClaimed {
1250                proposed_via: Some(via @ OuterConvergedVia::RecurrentIncumbent { .. }),
1251            }
1252            | Self::Certified(via @ OuterConvergedVia::RecurrentIncumbent { .. }) => Some(via),
1253            Self::Exhausted
1254            | Self::SolverClaimed { .. }
1255            | Self::Certified(_) => None,
1256        };
1257        if !matches!(*self, Self::Exhausted) {
1258            *self = Self::SolverClaimed { proposed_via };
1259        }
1260    }
1261
1262    fn certify(&mut self, via: OuterConvergedVia) {
1263        *self = Self::Certified(via);
1264    }
1265
1266    /// A refused analytic pass may retain the factual solver claim for resume
1267    /// policy, but never a proposed or certified success verdict.
1268    fn refuse_certificate(&mut self) {
1269        if !matches!(*self, Self::Exhausted) {
1270            *self = Self::SolverClaimed { proposed_via: None };
1271        }
1272    }
1273}
1274
1275/// Which lane actually produced an [`OuterResult`].
1276///
1277/// `OuterResult::solver_termination` is `None` whenever no `opt` solver
1278/// produced the result, and its own doc names three ways that happens — a
1279/// cache short-circuit, a synthesized checkpoint, the per-atom Fellner–Schall
1280/// lane — without recording WHICH. A refusal then reads
1281/// `termination=<no opt solver produced this result>` and the reader is left to
1282/// infer the lane from iteration counts, which is the #2465 shape: the decision
1283/// is made, and the basis for it is dropped by the emitter that holds it. On
1284/// the #1575 binomial fixture that absence is the whole question — a result at
1285/// `|Pg| = 5.991e-1` after 13 of 300 permitted outer iterations, PSD Hessian,
1286/// nothing railed, is a search that stopped with descent still available, and
1287/// "which lane stopped it" is the first thing anyone needs.
1288///
1289/// [`OuterResult::new`] defaults to [`Self::Solver`]; the gam-side lanes that
1290/// synthesize a result overwrite it at their construction site.
1291#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1292pub enum OuterResultOrigin {
1293    /// An `opt` solver ran and its solution was translated into this result.
1294    Solver,
1295    /// A seed was accepted as its own optimum with zero outer iterations.
1296    SeedAcceptedWithoutIteration,
1297    /// ARC exhausted its budget on a last iterate WORSE than the best feasible
1298    /// iterate it had seen, so the best iterate was substituted (#1371/#1476).
1299    ArcBestIterateSubstitution,
1300    /// ARC hit a run of infeasible probes with no synchronized Hessian, so a
1301    /// checkpoint was rebuilt from the stored best iterate.
1302    ArcInfeasibleStallCheckpoint,
1303    /// ARC was stopped at a point its own terminal certificate accepts: the
1304    /// Newton decrement ½gᵀH⁻¹g of the rail-projected gradient sat at or below
1305    /// the criterion's resolution under a PSD reduced Hessian (#2817).
1306    ArcCurvatureStationaryStop,
1307    /// The BFGS cost-stall guard halted the search and published its best
1308    /// iterate, which was rebuilt into this result.
1309    BfgsCostStallExit,
1310    /// The per-atom Fellner–Schall frontier lane.
1311    PerAtomFellnerSchall,
1312    /// The parameter space is empty; there was nothing to optimize.
1313    EmptyParameterSpace,
1314    /// A caller-supplied point audited without running any optimizer.
1315    StationaryPointAudit,
1316}
1317
1318/// Result of a completed outer optimization.
1319#[derive(Clone, Debug)]
1320pub struct OuterResult {
1321    /// Optimized log-smoothing parameters.
1322    pub rho: Array1<f64>,
1323    /// Final objective value.
1324    pub final_value: f64,
1325    /// Total outer iterations across all solver restarts.
1326    pub iterations: usize,
1327    /// Final gradient norm, when the solver computed an actual gradient.
1328    pub final_grad_norm: Option<f64>,
1329    /// Final gradient when the solver is gradient-based.
1330    pub final_gradient: Option<Array1<f64>>,
1331    /// Final Hessian when the solver tracks one.
1332    pub final_hessian: Option<Array2<f64>>,
1333    /// Single authoritative termination lifecycle. Private so downstream
1334    /// callers cannot manufacture convergence without the optimizer transition.
1335    termination: OuterTermination,
1336    /// Which plan was actually used (may differ from initial if fallback fired).
1337    pub plan_used: OuterPlan,
1338    /// Final trust radius for the internal operator trust-region solver.
1339    ///
1340    /// A non-converged operator-ARC attempt may be restarted by the budget
1341    /// ladder. Restarting only from the last θ but resetting the trust radius
1342    /// is not a warm start: it replays the same rejected large trial steps.
1343    /// Carry this globalization state so retries resume from the scale the
1344    /// previous attempt already learned.
1345    pub operator_trust_radius: Option<f64>,
1346    /// Why the internal operator trust-region solver stopped.
1347    ///
1348    /// Derived from `Self::termination` in
1349    /// `bridges::solution_into_outer_result`; do not set it independently
1350    /// or the two can disagree.
1351    pub operator_stop_reason: Option<OperatorTrustRegionStopReason>,
1352    /// Which test the underlying `opt` solver stopped on, and the
1353    /// quantity it was decided against.
1354    ///
1355    /// Distinct from `OuterTermination`, which is gam's *certification*
1356    /// state machine (did a terminal analytic certificate authorize this
1357    /// point). This is the solver's own account of why it stopped
1358    /// searching, and the two answer different questions: a run can stop
1359    /// on a satisfied gradient test and still fail certification, or
1360    /// exhaust its budget and be certified by a later re-measurement.
1361    ///
1362    /// Carried from the solver rather than reconstructed here. Before
1363    /// `opt` reported this, `operator_stop_reason` was hand-populated on
1364    /// the matrix-free branch alone by matching the coarse
1365    /// `OptimizationStatus`, so every other route left it `None` — and
1366    /// `None` rendered identically to "there was nothing to say", which
1367    /// made #2547's "WHY it stopped is unrecorded" unanswerable without a
1368    /// new probe.
1369    ///
1370    /// `None` here means no `opt` solver produced this result at all (a
1371    /// cache short-circuit, a synthesized checkpoint, the per-atom
1372    /// Fellner–Schall lane) — an honest absence, not a dropped verdict.
1373    pub solver_termination: Option<TerminationReason>,
1374    /// First-order optimality self-audit at the returned point (#934).
1375    ///
1376    /// `None` when no analytic gradient was measured at termination
1377    /// (gradient-free solvers, cache-hit short-circuits, per-atom EFS) or
1378    /// when an audit probe failed to evaluate. Populated once by
1379    /// `run_outer` after the solver ladder returns, outside all hot loops.
1380    pub criterion_certificate: Option<OuterCriterionCertificate>,
1381    /// Probe-noise-floor gradient bound measured by the cost-stall guard at a
1382    /// halted stall (#2241): σ̂/Δ, the criterion's evaluation-noise floor over
1383    /// the stall window divided by the radius the accepted steps actually
1384    /// probed. Present only on results rebuilt from a cost-stall exit;
1385    /// `certify_outer_optimality` folds it into the stationarity bound so the
1386    /// final re-measured gradient is judged against the same flat certificate
1387    /// the guard granted.
1388    /// Why the solver's line search gave up, when it did (#2465).
1389    ///
1390    /// `opt` reports `LineSearchFailureReason` plus the attempt count on
1391    /// `BfgsError::LineSearchFailed`, and the two variants have opposite
1392    /// causes: `StepSizeTooSmall` means the direction WAS a descent direction
1393    /// and no usable step decreased the objective — an objective/gradient
1394    /// inconsistency or evaluation noise — while `MaxAttempts` means the
1395    /// bracketing never closed, a pathological landscape. The bridge used to
1396    /// drop both on the floor: a line-search failure whose last iterate is
1397    /// finite is returned as `Ok(non-converged)`, so the caller never sees the
1398    /// `Err` that carries them, and the certificate could say only
1399    /// `termination=line_search_failed(|g|=…)` — the verdict without the
1400    /// quantity it was decided against.
1401    ///
1402    /// `None` means no line search failed (or no `opt` solver produced this
1403    /// result at all).
1404    pub line_search_failure: Option<(LineSearchFailureReason, usize)>,
1405    pub flat_noise_grad_bound: Option<f64>,
1406    /// Post-fit PSIS diagnostic for whether sampled smoothing-parameter weights
1407    /// show evidence that plug-in REML/LAML intervals are unreliable. Populated
1408    /// once by `run_outer` when the exact rho Hessian is cheap enough to use.
1409    pub rho_uncertainty_diagnostic: Option<crate::rho_uncertainty::RhoUncertaintyDiagnostic>,
1410    /// Reseed point minted by a refused certification whose tail snap CONFIRMED
1411    /// an exponential tail (#2348 Inc 2b). A snap is a waypoint, never a
1412    /// candidate optimum: even an interior coordinate stationary before the
1413    /// snap can move when it is coupled to the tail coordinate (#2358). The
1414    /// plan runner retries ONCE from this point, allowing every coordinate to
1415    /// re-descend or remain on the rail before the natural certificate judges
1416    /// the result.
1417    pub tail_snap_reseed: Option<Array1<f64>>,
1418    /// Saddle-escape reseed point minted by a refused certification whose
1419    /// interior reduced Hessian is a certified strict saddle — small projected
1420    /// gradient, `hessian_psd = Some(false)`, no railed coordinate (#2357). A
1421    /// gradient-only convergence gate (ARC's, or the cost-stall guard's) can
1422    /// ARRIVE at such a saddle with its gradient already below tolerance and
1423    /// stop, even though the certified negative-curvature eigendirection is a
1424    /// strict descent direction the optimizer never took. This point is
1425    /// `ρ + α·v` for the most-negative-curvature eigenvector `v`, stepped off
1426    /// the saddle ridge to a strictly-lower objective; the plan runner reseeds
1427    /// the outer search ONCE from it (reseed gate closed so it cannot recurse),
1428    /// which lets the optimizer descend to the true PSD minimum exactly as an
1429    /// identical warm-started resume does by hand.
1430    pub saddle_escape_reseed: Option<Array1<f64>>,
1431    /// What the criterion measured about this result's own analytic Hessian,
1432    /// when the certificate adjudicated a disputed negative curvature (#2748).
1433    ///
1434    /// Set exactly where `SaddleAdjudication::Contradicted` is reached with a
1435    /// determined ladder fit. It is a certified lower bound on `‖δH‖₂` for the
1436    /// assembly — the measurement `gam_linalg::curvature_resolution`'s Law 2
1437    /// requires and supplies no value for — and it exists on the result so the
1438    /// downstream ρ-curvature gates judge by the same evidence this certificate
1439    /// did instead of re-deciding from the matrix alone (#2428).
1440    pub criterion_hessian_error: Option<CriterionCurvatureDisagreement>,
1441    /// Wrong-rail pull-back reseed point minted by a refused certification whose
1442    /// coordinate sits AT the ρ box bound but whose clean-band probes prove the
1443    /// objective DECREASES as the coordinate moves INWARD (#2392). The outer
1444    /// search drove the coordinate to the wrong bound — its terminal gradient is
1445    /// deep-λ instrument noise, so the trust region never proposed the large
1446    /// inward move — while a drift-band-clean, above-noise-floor run of probes a
1447    /// few e-folds inside carries a pencil constant of the sign OPPOSITE the rail
1448    /// (descent points away from the bound, `∂V/∂ρ > 0` at an upper rail). This
1449    /// point moves that coordinate to its clean-band interior scale, where the
1450    /// gradient is informative again; the plan runner reseeds ONCE (gate closed)
1451    /// and the optimizer descends to the true interior optimum. Gated strictly on
1452    /// the opposite-sign clean-tail proof, so a GENUINE rail (descent toward the
1453    /// bound) never mints it and no real λ→∞ optimum is pulled off its rail.
1454    pub wrong_rail_reseed: Option<Array1<f64>>,
1455    /// Active-set reduction reseed minted by a refused certification whose
1456    /// INTERIOR is not stationary while a coordinate is railed at the ρ box with
1457    /// a deep-λ noise-floor gradient (#2392). The railed coordinate's
1458    /// ill-conditioned Hessian row poisons the joint Newton/ARC steps, so the
1459    /// interior cannot polish; freezing that coordinate at its bound and
1460    /// re-running lets the optimizer converge the interior in the well-conditioned
1461    /// REDUCED space. The reseed carries the frozen box (`bounds`, with
1462    /// `lower[k]==upper[k]==rail` for each frozen coordinate); the plan runner's
1463    /// re-certification under the ORIGINAL bounds then judges every pinned
1464    /// coordinate's KKT sign at the reduced optimum (an inward-feasible-descent
1465    /// gradient unfreezes it — no silent clamping of a coordinate that stops
1466    /// wanting the rail).
1467    pub active_set_reseed: Option<ActiveSetReseed>,
1468    /// `(noise_floor σ̂, probe_radius Δ)` the cost-stall guard measured over its
1469    /// stall window, when a stall produced this result. Present whether or not
1470    /// their ratio licensed a `flat_noise_grad_bound`: σ̂ is the per-step
1471    /// objective change the no-improvement window judged and Δ is the radius the
1472    /// accepted steps moved, and a window that filled because the search took
1473    /// microscopic steps is told from one that filled because the surface is
1474    /// flat by Δ, not by the verdict.
1475    pub cost_stall_probe_scale: Option<(f64, f64)>,
1476    /// Which lane produced this result. See [`OuterResultOrigin`].
1477    pub origin: OuterResultOrigin,
1478    /// Seed start points this plan run STARTED and whose mandatory analytic
1479    /// certificate then REFUSED (#2569).
1480    ///
1481    /// The certify-resume loop in `run_outer` re-runs the outer search seeded
1482    /// at the refused checkpoint, and the seed cascade it re-enters is allowed
1483    /// to fall through its `seed_budget` while nothing has certified
1484    /// (`should_start_next_seed`). The fall-through lands on the SAME generated
1485    /// lattice seed every round — a point that does not depend on the
1486    /// checkpoint, reached from a state `obj.reset()` has restored — so the
1487    /// cascade re-derives a verdict it already recorded. Measured on the #2569
1488    /// grouped-binomial design: one cold seed re-run 17 times per fit, each
1489    /// repetition terminating at the identical `|g|` after the identical outer
1490    /// iteration count, for 18-48% of the fit's wall clock.
1491    ///
1492    /// Carrying the points forward lets the next resume skip exactly those
1493    /// seeds, on the same grounds #2080's `cold_entry_leg_refusal` replays a
1494    /// recorded cold-entry verdict: re-running them would reproduce the
1495    /// recorded refusal digit for digit. A seed that has NOT been started and
1496    /// refused is never suppressed, so no rescue path is closed.
1497    pub refused_seed_points: Vec<Array1<f64>>,
1498}
1499
1500/// An active-set reduction reseed (#2392): re-run the outer search with a set of
1501/// railed coordinates FROZEN at their box bounds so the optimizer polishes the
1502/// interior in the reduced space.
1503#[derive(Clone, Debug)]
1504pub struct ActiveSetReseed {
1505    /// The reseed point: the refused checkpoint with the frozen coordinates
1506    /// pinned at their bounds (`rho[k] == bounds.0[k] == bounds.1[k]`).
1507    pub rho: Array1<f64>,
1508    /// The reduced-space box: `lower[k] == upper[k] == rail` for every frozen
1509    /// coordinate `k`, the original bounds elsewhere.
1510    pub bounds: (Array1<f64>, Array1<f64>),
1511}
1512
1513impl OuterResult {
1514    pub fn new(
1515        rho: Array1<f64>,
1516        final_value: f64,
1517        iterations: usize,
1518        solver_claimed_convergence: bool,
1519        plan_used: OuterPlan,
1520    ) -> Self {
1521        Self {
1522            rho,
1523            final_value,
1524            iterations,
1525            final_grad_norm: None,
1526            final_gradient: None,
1527            final_hessian: None,
1528            termination: OuterTermination::from_solver_claim(solver_claimed_convergence),
1529            plan_used,
1530            operator_trust_radius: None,
1531            operator_stop_reason: None,
1532            solver_termination: None,
1533            criterion_certificate: None,
1534            line_search_failure: None,
1535            flat_noise_grad_bound: None,
1536            rho_uncertainty_diagnostic: None,
1537            tail_snap_reseed: None,
1538            saddle_escape_reseed: None,
1539            criterion_hessian_error: None,
1540            wrong_rail_reseed: None,
1541            active_set_reseed: None,
1542            cost_stall_probe_scale: None,
1543            origin: OuterResultOrigin::Solver,
1544            refused_seed_points: Vec::new(),
1545        }
1546    }
1547
1548    /// Whether this result owns a terminal analytic convergence certificate.
1549    pub fn converged(&self) -> bool {
1550        self.termination.is_certified()
1551    }
1552
1553    /// Which analytic certificate concluded this run.
1554    pub fn converged_via(&self) -> Option<OuterConvergedVia> {
1555        self.termination.certified_via()
1556    }
1557
1558    /// Whether the underlying solver claimed convergence before analytic
1559    /// certification. Certified results necessarily originated from a claim or
1560    /// an explicit stationary-point audit.
1561    pub(crate) fn solver_claimed_convergence(&self) -> bool {
1562        self.termination.solver_claimed_convergence()
1563    }
1564
1565    /// Human-readable rendering of `final_grad_norm` for diagnostics. Returns
1566    /// `"n/a"` when no gradient was measured (gradient-free / cache-hit paths).
1567    pub fn final_grad_norm_report(&self) -> String {
1568        match self.final_grad_norm {
1569            Some(g) => format!("{g:.3e}"),
1570            None => "n/a".to_string(),
1571        }
1572    }
1573}
1574
1575/// Validated evidence that an outer optimization terminated at a finite,
1576/// analytically certified optimum.
1577///
1578/// The inner [`OuterResult`] is private so downstream fit assembly cannot turn
1579/// a status boolean into convergence provenance. Construction consumes the
1580/// optimizer result and revalidates the certificate at the ownership boundary.
1581#[derive(Clone, Debug)]
1582pub struct CertifiedOuterResult {
1583    result: OuterResult,
1584}
1585
1586impl CertifiedOuterResult {
1587    /// The sole constructor is reached from [`OuterProblem::run_certified`]
1588    /// after the optimizer has produced the result.  Keeping this private is
1589    /// load-bearing: `OuterResult` is also a public diagnostic/checkpoint
1590    /// payload, so a public conversion would let downstream code fabricate a
1591    /// certificate-shaped result without ever running an objective.
1592    fn from_optimizer_result(result: OuterResult) -> Result<Self, String> {
1593        if !result.converged() {
1594            return Err(format!(
1595                "outer optimization did not converge after {} iterations",
1596                result.iterations
1597            ));
1598        }
1599        if !result.final_value.is_finite() {
1600            return Err(format!(
1601                "outer optimization returned a non-finite objective: {}",
1602                result.final_value
1603            ));
1604        }
1605        if result.rho.iter().any(|value| !value.is_finite()) {
1606            return Err("outer optimization returned non-finite hyperparameters".to_string());
1607        }
1608        if result
1609            .final_grad_norm
1610            .is_some_and(|value| !value.is_finite() || value < 0.0)
1611        {
1612            return Err(format!(
1613                "outer optimization returned an invalid gradient norm: {:?}",
1614                result.final_grad_norm
1615            ));
1616        }
1617        let certificate = result
1618            .criterion_certificate
1619            .as_ref()
1620            .ok_or_else(|| "outer optimization returned no analytic certificate".to_string())?;
1621        if !certificate.certifies() {
1622            return Err(format!(
1623                "outer optimization certificate does not certify: {}",
1624                certificate.summary()
1625            ));
1626        }
1627        Ok(Self { result })
1628    }
1629
1630    /// Exact optimizer-owned hyperparameter vector covered by the certificate.
1631    pub fn rho(&self) -> &Array1<f64> {
1632        &self.result.rho
1633    }
1634
1635    pub fn iterations(&self) -> usize {
1636        self.result.iterations
1637    }
1638
1639    pub fn final_value(&self) -> f64 {
1640        self.result.final_value
1641    }
1642
1643    pub fn final_grad_norm(&self) -> Option<f64> {
1644        self.result.final_grad_norm
1645    }
1646
1647    /// Exact analytic gradient re-measured by the optimizer-owned terminal
1648    /// certificate. Downstream selected-profile finalizers use this to prove
1649    /// that a retained objective payload is the one certified at `rho()`.
1650    pub fn final_gradient(&self) -> Option<&Array1<f64>> {
1651        self.result.final_gradient.as_ref()
1652    }
1653
1654    pub fn criterion_certificate(&self) -> &OuterCriterionCertificate {
1655        self.result
1656            .criterion_certificate
1657            .as_ref()
1658            .expect("CertifiedOuterResult always owns a validated certificate")
1659    }
1660
1661    /// The analytic outer ρ-Hessian measured at the certified point, when the
1662    /// certification retained one. This is the curvature evidence behind the
1663    /// certificate's PSD verdict — and the `V_ρ = H_ρ⁻¹` input to first-order
1664    /// smoothing-correction inflation (#2346).
1665    pub fn final_hessian(&self) -> Option<&Array2<f64>> {
1666        self.result.final_hessian.as_ref()
1667    }
1668}
1669
1670#[cfg(test)]
1671#[path = "certified_outer_result_tests.rs"]
1672mod certified_outer_result_tests;
1673
1674/// Typed refusal from [`audit_stationary_point`]. The rejected point and every
1675/// analytic certificate field measured before refusal remain available to the
1676/// caller; `source` records why those measurements did not certify.
1677#[derive(Debug)]
1678pub struct OuterStationaryPointRejection {
1679    pub result: OuterResult,
1680    pub source: EstimationError,
1681}
1682
1683impl std::fmt::Display for OuterStationaryPointRejection {
1684    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1685        std::fmt::Display::fmt(&self.source, f)
1686    }
1687}
1688
1689impl std::error::Error for OuterStationaryPointRejection {
1690    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1691        Some(&self.source)
1692    }
1693}
1694
1695/// Apply the shared analytic outer-optimality authority to one caller-supplied
1696/// point without running an optimizer or taking a step.
1697///
1698/// The objective controls whether evaluating that point mutates its profiled
1699/// state. Callers auditing an already-installed inner state must put their
1700/// objective in a frozen evaluation mode before calling this function.
1701/// `iterations == 0` in the returned result is structural: no optimization loop
1702/// exists on this path.
1703pub fn audit_stationary_point(
1704    obj: &mut dyn OuterObjective,
1705    rho: Array1<f64>,
1706    context: &str,
1707) -> Result<OuterResult, OuterStationaryPointRejection> {
1708    let config = OuterConfig::default();
1709    let selected_plan = plan(&obj.capability());
1710    // There is intentionally no independent value-only probe. The analytic
1711    // sample is the authority being audited, and infinity records that no
1712    // optimizer-produced terminal value exists to compare against it.
1713    let mut result = OuterResult::new(rho, f64::INFINITY, 0, false, selected_plan);
1714    result.origin = OuterResultOrigin::StationaryPointAudit;
1715    match certify_outer_optimality(obj, &config, context, &mut result) {
1716        Ok(certificate) => {
1717            result.criterion_certificate = Some(certificate);
1718            Ok(result)
1719        }
1720        Err(source) => Err(OuterStationaryPointRejection { result, source }),
1721    }
1722}
1723
1724// ─── First-order optimality certificate (#934) ────────────────────────
1725//
1726// The objective↔gradient desync bug genus (#748, #752, #808, #901, …) has a
1727// universal signature: at the returned "optimum" the optimizer claims
1728// convergence while the criterion is not actually stationary there (or the
1729// optimizer stalls and rails λ). The certificate makes the engine check
1730// itself, once, at θ̂, on every generic outer fit — purely from the ANALYTIC
1731// objective, per SPEC rule 2 (finite differences never run outside tests;
1732// the FD gradient oracle now lives in the test-only `fd_audit` module): the
1733// KKT-projected analytic gradient norm against the same score-relative
1734// stationarity bound the outer loop already uses to accept flat-valley
1735// stalls (#1690), a scaled PSD probe of the tracked outer Hessian, and the
1736// λ-rail facts every desync postmortem asks for. It is the runtime
1737// enforcement layer for the criterion-atom architecture (#931).
1738//
1739// A failed certificate REJECTS the fit as typed non-convergence — never a
1740// warn-and-continue diagnostic — so a nonstationary point can never be
1741// minted into a fit (SPEC rule 20).
1742
1743/// Cholesky positive-SEMIdefiniteness probe for the (small, outer-dim) final
1744/// Hessian, with a roundoff-scale diagonal shift. Returns `None` when the
1745/// matrix is empty, non-square, or non-finite; `Some(false)` when the shifted
1746/// matrix has a non-positive pivot — i.e. the curvature is genuinely
1747/// indefinite, not merely semidefinite-within-noise.
1748///
1749/// The shift is `√ε · max(1, max|H_ii|)`: eigenvalues assembled through
1750/// O(‖H‖)-scaled arithmetic carry O(ε·‖H‖) roundoff, so a `√ε`-relative
1751/// margin cleanly separates a true negative direction from accumulated
1752/// floating-point noise on a flat (near-semidefinite) valley.
1753pub(crate) fn certificate_hessian_is_psd(hessian: &Array2<f64>) -> Option<bool> {
1754    certificate_hessian_is_psd_at_resolution(hessian, 0.0)
1755}
1756
1757/// [`certificate_hessian_is_psd`] with an explicit **measured** curvature
1758/// resolution, which the shift is raised to when it is the larger (#2748).
1759///
1760/// The `√ε·max(1, max|H_ii|)` shift above is a statement about the *arithmetic*
1761/// that assembled `H` — accumulated round-off at the matrix's own scale. It is
1762/// not, and does not claim to be, a statement about the *assembly*: an outer
1763/// criterion Hessian is a derivative of a quantity computed through an inner
1764/// solve, a log-determinant and a trace contraction, and its error is not
1765/// bounded by `√ε·‖H‖`. `gam_linalg::curvature_resolution` calls the second
1766/// quantity `‖δH‖₂` and requires it to be MEASURED, per site, from an identity
1767/// that is exactly zero in exact arithmetic.
1768///
1769/// `measured_resolution` is that measurement. `0.0` reproduces the historical
1770/// shift bit for bit, and since the two are combined by `max` this can only
1771/// ever admit a point the previous rule refused — never refuse one it admitted.
1772/// The shift the certificate's definiteness verdict is ACTUALLY taken at, and
1773/// the single owner of that number (#2748).
1774///
1775/// It is the larger of the measured `‖δH‖₂` and the arithmetic shift
1776/// `√ε·max(max|H_ii|, 1)`. The second is what decides whenever no identity
1777/// could be measured, and it is not small: on a ρ-Hessian whose largest
1778/// diagonal is under 1 it is a flat `√ε = 1.49e-8`.
1779///
1780/// It is `pub(crate)` and separate because the verdict travels. The
1781/// smoothing-correction re-judges the same direction at the same point against
1782/// a resolution built from its own eigensolver's backward error — `2.19e-16` on
1783/// the measured #2748 `geo_disease` k=12 cell — and refused a direction the
1784/// certificate had cleared at `1.49e-8`, eight orders wider. A verdict and the
1785/// standard it was taken at have to travel together, or the second layer
1786/// applies a strictly stronger test than the first and the fit dies between
1787/// them.
1788pub(crate) fn certificate_curvature_shift(hessian: &Array2<f64>, measured_resolution: f64) -> f64 {
1789    let n = hessian.nrows();
1790    let max_diag = (0..n).fold(0.0_f64, |acc, j| acc.max(hessian[[j, j]].abs()));
1791    let arithmetic_shift = f64::EPSILON.sqrt() * max_diag.max(1.0);
1792    if measured_resolution.is_finite() && measured_resolution > arithmetic_shift {
1793        measured_resolution
1794    } else {
1795        arithmetic_shift
1796    }
1797}
1798
1799pub(crate) fn certificate_hessian_is_psd_at_resolution(
1800    hessian: &Array2<f64>,
1801    measured_resolution: f64,
1802) -> Option<bool> {
1803    let n = hessian.nrows();
1804    if n == 0 || hessian.ncols() != n || hessian.iter().any(|v| !v.is_finite()) {
1805        return None;
1806    }
1807    let shift = certificate_curvature_shift(hessian, measured_resolution);
1808    let mut chol = hessian.clone();
1809    for j in 0..n {
1810        chol[[j, j]] += shift;
1811    }
1812    for j in 0..n {
1813        for k in 0..j {
1814            let l_jk = chol[[j, k]];
1815            for i in j..n {
1816                chol[[i, j]] -= chol[[i, k]] * l_jk;
1817            }
1818        }
1819        let pivot = chol[[j, j]];
1820        if !(pivot > 0.0) || !pivot.is_finite() {
1821            return Some(false);
1822        }
1823        let inv_sqrt = 1.0 / pivot.sqrt();
1824        for i in j..n {
1825            chol[[i, j]] *= inv_sqrt;
1826        }
1827    }
1828    Some(true)
1829}
1830
1831/// PSD verdict of the outer Hessian restricted to its UN-RAILED coordinates
1832/// (#2299 box-KKT reduced-Hessian / critical-cone gate).
1833///
1834/// A coordinate railed at ±`rho_bound` with an outward gradient is at the box-KKT
1835/// constrained optimum: its curvature direction is the flat/indefinite
1836/// infinite-smoothing plateau of a fully-saturated penalty (λ ~ 1e13), carrying
1837/// no feasible descent. Including it makes the FULL Hessian indefinite and used to
1838/// disable the very flatness certificate that exists to handle rails, so an
1839/// honest railed optimum ground to `max_iter` and refused. Judging PSD on the
1840/// INTERIOR (un-railed) sub-block is the standard reduced-Hessian condition: a
1841/// genuinely indefinite *interior* direction still keeps the sub-block non-PSD,
1842/// so this can never over-certify a real saddle. When every coordinate is railed
1843/// the interior is empty — there is no feasible curvature to certify and the rail
1844/// KKT signs are the whole certificate — so the empty sub-block is trivially PSD.
1845/// With no railed coordinate and no declared invariance it is exactly
1846/// [`certificate_hessian_is_psd`].
1847///
1848/// # The invariance argument (#2676)
1849///
1850/// `invariance` carries the directions along which the criterion is EXACTLY
1851/// constant by construction of its penalty map (see
1852/// [`crate::penalty_invariance`]). They are removed from the judged subspace
1853/// alongside the railed coordinates, for the same reason: a coordinate railed
1854/// at its bound and a direction the criterion does not vary along are both
1855/// places where "is the curvature positive?" has no answer that is about the
1856/// fit. On such a direction `t' H_rho t = sum_k g_k t_k^2` identically, so its
1857/// sign is the sign of the disagreement between the gradient code and the
1858/// Hessian code — measured at `|sigma|/floor = 0.99925` on `geo_disease_matern`.
1859///
1860/// `None` reproduces the pre-#2676 sub-block extraction bit for bit; that is
1861/// what every objective declaring no invariance gets, which is nearly all of
1862/// them.
1863pub(crate) fn certificate_hessian_is_psd_off_railed(
1864    hessian: &Array2<f64>,
1865    railed: &[usize],
1866    invariance: Option<&Array2<f64>>,
1867) -> Option<bool> {
1868    certificate_hessian_is_psd_off_railed_at_resolution(hessian, railed, invariance, 0.0)
1869}
1870
1871/// [`certificate_hessian_is_psd_off_railed`] carrying a **measured** curvature
1872/// resolution down to the definiteness test (#2748).
1873pub(crate) fn certificate_hessian_is_psd_off_railed_at_resolution(
1874    hessian: &Array2<f64>,
1875    railed: &[usize],
1876    invariance: Option<&Array2<f64>>,
1877    measured_resolution: f64,
1878) -> Option<bool> {
1879    let n = hessian.nrows();
1880    let deflate = invariance.filter(|basis| basis.nrows() == n && basis.ncols() > 0);
1881    if railed.is_empty() && deflate.is_none() {
1882        return certificate_hessian_is_psd_at_resolution(hessian, measured_resolution);
1883    }
1884    let judged = crate::penalty_invariance::judged_subspace_basis(n, railed, deflate);
1885    let Some(judged) = judged else {
1886        // Nothing left to judge: every coordinate is railed, or the criterion is
1887        // flat in every direction. There is no feasible curvature to certify and
1888        // the rail KKT signs are the whole certificate.
1889        return Some(true);
1890    };
1891    if deflate.is_none() {
1892        // Exactly the historical sub-block extraction: `judged` is the interior
1893        // indicator basis, so build the sub-block directly rather than through a
1894        // matrix product, keeping this path bit-identical.
1895        let railed_set: std::collections::BTreeSet<usize> = railed.iter().copied().collect();
1896        let interior: Vec<usize> = (0..n).filter(|k| !railed_set.contains(k)).collect();
1897        let mut sub = Array2::<f64>::zeros((interior.len(), interior.len()));
1898        for (i, &ri) in interior.iter().enumerate() {
1899            for (j, &rj) in interior.iter().enumerate() {
1900                sub[[i, j]] = hessian[[ri, rj]];
1901            }
1902        }
1903        return certificate_hessian_is_psd_at_resolution(&sub, measured_resolution);
1904    }
1905    let compressed = crate::penalty_invariance::compress_to_judged_subspace(hessian, &judged);
1906    certificate_hessian_is_psd_at_resolution(&compressed, measured_resolution)
1907}
1908
1909/// The curvature resolution this site is entitled to: `‖δH‖₂` MEASURED from the
1910/// identities that are exactly zero in exact arithmetic (#2748), never assumed.
1911///
1912/// Two are available here and both are free:
1913///
1914/// * the **symmetrization defect** `‖(H − Hᵀ)/2‖₂`. A Hessian is symmetric for
1915///   any twice-continuously-differentiable criterion, and `H[i,j]` and `H[j,i]`
1916///   are separate accumulations of the same mixed partial, so whatever survives
1917///   is the assembly's error;
1918/// * the **penalty-map invariance residual** `‖T'(H − diag(g))T‖₂` on the
1919///   directions this certificate deflates. Along them the criterion is exactly
1920///   constant in λ, so `t'H t = Σ_k g_k t_k²` identically and the residual is
1921///   error and only error — in exactly the currency a `H + diag(|g|)` gate
1922///   spends.
1923///
1924/// Both are certified LOWER bounds on `‖δH‖₂`, so their maximum is the strongest
1925/// available fact. Returns `0.0` when neither can be taken, which leaves the
1926/// historical `√ε` shift in force unchanged.
1927pub(crate) fn measured_outer_curvature_resolution(
1928    hessian: &Array2<f64>,
1929    railed: &[usize],
1930    gradient: &Array1<f64>,
1931    invariance: Option<&Array2<f64>>,
1932) -> f64 {
1933    let n = hessian.nrows();
1934    if n == 0 || hessian.ncols() != n {
1935        return 0.0;
1936    }
1937    let symmetrization = gam_linalg::matrix::symmetrization_defect_2norm(hessian);
1938    let deflate = invariance.filter(|basis| basis.nrows() == n && basis.ncols() > 0);
1939    let invariance_residual = deflate
1940        .and_then(|basis| crate::penalty_invariance::judged_subspace_basis(n, railed, Some(basis)))
1941        .and_then(|judged| crate::penalty_invariance::deflated_directions(n, &judged))
1942        .and_then(|removed| {
1943            crate::penalty_invariance::invariance_residual_2norm(hessian, gradient, &removed)
1944        })
1945        .unwrap_or(0.0);
1946    symmetrization.max(invariance_residual).max(0.0)
1947}
1948
1949/// Interior-PSD verdict judged ABOVE the per-coordinate gradient-residue noise
1950/// floor (#2349): PSD of `H + diag(|g|)` restricted to the un-excluded
1951/// coordinates.
1952///
1953/// The assembled ρ-Hessian's tail entries carry the #2298 trace-pair
1954/// cancellation residue: when the `λ²V_λλ` pair cancels to roundoff, the
1955/// surviving diagonal entry is `λV_λ = g_k` — gradient magnitude, corrupted
1956/// sign (the same tie signature the tail-snap candidate band keys on).
1957/// Measured on the #2349 multinomial checkpoint: the sole interior coordinate
1958/// had `g₁ = −1.0228e-3`, `H₁₁ = −1.0216e-3` (ratio 0.999), and that single
1959/// sub-resolution entry was the entire `interior Hessian sub-block not PSD`
1960/// refusal — the full 6×6 spectrum was `[−1.02e-3, 0.135, …, 0.904]`.
1961///
1962/// # Why the floor is safe — instrument resolution, not step economics
1963///
1964/// The residue is `O(|g_k|)`, and the sub-block is extracted AFTER flooring, so
1965/// only the *judged* (un-excluded) coordinates' gradients ever enter: a railed
1966/// coordinate's large `|g|` (measured 1.40 on the #2349 checkpoint) cannot
1967/// inflate the floor. Weyl bounds what remains exactly:
1968///
1969/// ```text
1970///     λ_min(H) + min|g| ≤ λ_min(H + diag|g|) ≤ λ_min(H) + max|g|
1971/// ```
1972///
1973/// so the floor absorbs **at most `max_k |g_k|` over the judged coordinates**.
1974/// Where this verdict can mint, those coordinates have passed gradient
1975/// stationarity, so that is at most the stationarity bound. A negative
1976/// eigenvalue smaller than that is not distinguishable from zero *by the
1977/// instrument that produced it* — the assembled tail entry IS `λV_λ = g`
1978/// (ratio 0.999 as measured) — while a genuine saddle survives untouched: the
1979/// #2357 trace's `λ_min ≈ −0.5` against `|g| ≈ 1e-3` floors to `−0.4973`, and a
1980/// `−1.50e-2` direction still refuses at a `1e-2` bound.
1981///
1982/// ## Do not widen this floor beyond `diag(|g|)`
1983///
1984/// Earlier revisions of this argument (and #2349 rounds 6/7) justified the
1985/// floor by an "exploitable improvement `≲ g²/2|H| ≈ 5e-7`". Both halves are
1986/// wrong and the pair is misleading in the permissive direction:
1987///
1988/// * the quoted number is `g²/2 = 5.23e-7`; the quoted formula evaluates to
1989///   `g²/(2|H|) = 5.12e-4` on the same measured `g = 1.0228e-3`,
1990///   `|H| = 1.0216e-3` — a 979× discrepancy. Since `|H| ≈ g` at exactly the
1991///   point this floor serves, that formula collapses to `≈ g/2`;
1992/// * and `g²/2|H|` is the Newton decrement, which bounds the improvement along
1993///   a direction of POSITIVE curvature. Along curvature `−ε` the model
1994///   `−g·t − ½εt²` is unbounded below, so the quantity it purports to bound
1995///   does not exist in the regime this floor exists for; the step is limited by
1996///   the trust radius, not the curvature.
1997///
1998/// The Weyl bound above is the correct and checkable statement. Anything that
1999/// widens the floor must re-derive against it, not against `g²/2|H|`.
2000pub(crate) fn certificate_meets_curvature_requirement(
2001    certificate: &OuterCriterionCertificate,
2002    require_measured_psd: bool,
2003    fidelity: CertificationFidelity,
2004) -> bool {
2005    matches!(fidelity, CertificationFidelity::Screening)
2006        || !require_measured_psd
2007        || certificate.hessian_psd() == Some(true)
2008        // #2612: a caller asking for a certified local minimum is asking for the
2009        // strongest statement the curvature evidence can support. When that
2010        // evidence has been CONTRADICTED by the criterion — every feasible step
2011        // along its reported negative eigenvector, over the whole range in which
2012        // the claim predicts a decrease the criterion can represent, failed to
2013        // lower the objective — the strongest supportable statement is that no
2014        // descent along it exists. Refusing here instead would refuse for the
2015        // ABSENCE of a measurement the route has just shown it cannot make,
2016        // which is the failure mode this flag's own doc block at
2017        // `with_require_measured_psd` warns about one case earlier.
2018        || matches!(
2019            certificate.curvature,
2020            CurvatureEvidence::CriterionContradicted
2021        )
2022}
2023
2024pub(crate) fn certificate_hessian_is_psd_off_railed_above_gradient_floor(
2025    hessian: &Array2<f64>,
2026    excluded: &[usize],
2027    gradient: &Array1<f64>,
2028    invariance: Option<&Array2<f64>>,
2029) -> Option<bool> {
2030    let n = hessian.nrows();
2031    if gradient.len() != n {
2032        return certificate_hessian_is_psd_off_railed(hessian, excluded, invariance);
2033    }
2034    let mut floored = hessian.clone();
2035    for k in 0..n {
2036        floored[[k, k]] += gradient[k].abs();
2037    }
2038    // The resolution is measured on the UNFLOORED `H` against the UNFLOORED
2039    // gradient: `diag(|g|)` is a deliberate softening of the test, not part of
2040    // the assembly, and the identities that measure `‖δH‖₂` are identities
2041    // about `(H, g)` as evaluated (#2748).
2042    let measured_resolution =
2043        measured_outer_curvature_resolution(hessian, excluded, gradient, invariance);
2044    certificate_hessian_is_psd_off_railed_at_resolution(
2045        &floored,
2046        excluded,
2047        invariance,
2048        measured_resolution,
2049    )
2050}
2051
2052/// Measure the gradient-residue floor's clearance on the interior sub-block:
2053/// the sub-block's smallest eigenvalue as assembled, the floor it is judged
2054/// against (`max_k |g_k|` over exactly those coordinates), and whether
2055/// `H + diag(|g|)` is PSD there.
2056///
2057/// This RECORDS the verdict; it does not replace the raw measurement. See
2058/// [`certificate_hessian_is_psd_off_railed_above_gradient_floor`] for why the
2059/// Weyl bound makes the floor safe where it can mint, and
2060/// [`crate::model_types::CurvatureFloorClearance`] for why the two facts are
2061/// kept apart.
2062pub(crate) fn interior_curvature_floor_clearance(
2063    hessian: &Array2<f64>,
2064    excluded: &[usize],
2065    gradient: &Array1<f64>,
2066    invariance: Option<&Array2<f64>>,
2067) -> Option<CurvatureFloorClearance> {
2068    use faer::Side;
2069    use gam_linalg::faer_ndarray::FaerEigh;
2070
2071    let n = hessian.nrows();
2072    if n == 0 || hessian.ncols() != n || gradient.len() != n {
2073        return None;
2074    }
2075    let excluded_set: std::collections::BTreeSet<usize> = excluded.iter().copied().collect();
2076    let interior: Vec<usize> = (0..n).filter(|k| !excluded_set.contains(k)).collect();
2077    if interior.is_empty() {
2078        return None;
2079    }
2080    let m = interior.len();
2081    let mut sub = Array2::<f64>::zeros((m, m));
2082    for (i, &ri) in interior.iter().enumerate() {
2083        for (j, &rj) in interior.iter().enumerate() {
2084            sub[[i, j]] = 0.5 * (hessian[[ri, rj]] + hessian[[rj, ri]]);
2085        }
2086    }
2087    if sub.iter().any(|v| !v.is_finite()) {
2088        return None;
2089    }
2090    // #2676: report the minimum of the block the verdict was actually reached
2091    // on. Reporting the raw interior minimum beside a verdict taken on the
2092    // deflated complement is what made every historical `[INDEF-HESS]` line
2093    // ambiguous: the number named a direction the decision no longer involved.
2094    let deflate = invariance.filter(|basis| basis.nrows() == n && basis.ncols() > 0);
2095    let sub = match deflate
2096        .and_then(|basis| crate::penalty_invariance::judged_subspace_basis(n, excluded, Some(basis)))
2097    {
2098        Some(judged) => crate::penalty_invariance::compress_to_judged_subspace(hessian, &judged),
2099        None => sub,
2100    };
2101    if sub.nrows() == 0 {
2102        return None;
2103    }
2104    let interior_min_eigenvalue = sub
2105        .eigh(Side::Lower)
2106        .ok()?
2107        .0
2108        .iter()
2109        .fold(f64::INFINITY, |acc, v| acc.min(*v));
2110    if !interior_min_eigenvalue.is_finite() {
2111        return None;
2112    }
2113    // The floor is the largest gradient among EXACTLY the judged coordinates —
2114    // the excluded ones never enter, so a railed coordinate's large |g| cannot
2115    // inflate it.
2116    let gradient_floor = interior
2117        .iter()
2118        .fold(0.0_f64, |acc, &k| acc.max(gradient[k].abs()));
2119    let cleared = certificate_hessian_is_psd_off_railed_above_gradient_floor(
2120        hessian, excluded, gradient, invariance,
2121    ) == Some(true);
2122    // The eigenvalue the verdict was ACTUALLY taken on: the same
2123    // `H + diag(|g|)`, on the same judged subspace, that
2124    // `certificate_hessian_is_psd_off_railed_above_gradient_floor` tests
2125    // (#2748). The two fields above are the ends of the Weyl sandwich
2126    // `λ_min(H) + min|g| ≤ λ_min(H + diag|g|) ≤ λ_min(H) + max|g|`, and a
2127    // reader given only the ends cannot tell why a curvature well inside
2128    // `max_k|g_k|` refused.
2129    let mut floored = hessian.clone();
2130    for k in 0..n {
2131        floored[[k, k]] += gradient[k].abs();
2132    }
2133    let floored_block = match invariance
2134        .filter(|basis| basis.nrows() == n && basis.ncols() > 0)
2135        .and_then(|basis| {
2136            crate::penalty_invariance::judged_subspace_basis(n, excluded, Some(basis))
2137        }) {
2138        Some(judged) => crate::penalty_invariance::compress_to_judged_subspace(&floored, &judged),
2139        None => {
2140            let mut block = Array2::<f64>::zeros((m, m));
2141            for (i, &ri) in interior.iter().enumerate() {
2142                for (j, &rj) in interior.iter().enumerate() {
2143                    block[[i, j]] = 0.5 * (floored[[ri, rj]] + floored[[rj, ri]]);
2144                }
2145            }
2146            block
2147        }
2148    };
2149    let floored_min_eigenvalue = if floored_block.nrows() == 0 {
2150        0.0
2151    } else {
2152        floored_block
2153            .eigh(Side::Lower)
2154            .ok()?
2155            .0
2156            .iter()
2157            .fold(f64::INFINITY, |acc, v| acc.min(*v))
2158    };
2159    let measured_resolution =
2160        measured_outer_curvature_resolution(hessian, excluded, gradient, invariance);
2161    Some(CurvatureFloorClearance {
2162        interior_min_eigenvalue,
2163        gradient_floor,
2164        floored_min_eigenvalue,
2165        measured_resolution,
2166        // The shift the verdict was decided at, from the SAME owner the PSD
2167        // test calls and on the SAME block it tested, so the number recorded
2168        // and the number applied cannot drift apart (#2748).
2169        decided_at_resolution: certificate_curvature_shift(&floored_block, measured_resolution),
2170        cleared,
2171    })
2172}
2173
2174/// The **measured** disagreement between an analytic outer Hessian and the
2175/// criterion it claims to be the curvature of, along one direction (#2748).
2176///
2177/// # Why this is a measurement of the MATRIX
2178///
2179/// For a unit direction `v`, `vᵀHv` and `d²/dα² V(θ̂ + αv)|₀` are the same
2180/// number written two ways. Their difference is exactly zero in exact
2181/// arithmetic, which is what makes it admissible as a
2182/// [`gam_linalg::curvature_resolution::MeasuredHessianError`], and by Weyl
2183/// `|vᵀ(δH)v| ≤ ‖δH‖₂`, so the difference is a certified LOWER BOUND on the
2184/// assembly's own error. That is the *"how wrong is this matrix?"* quantity
2185/// `curvature_resolution`'s Law 2 supplies no value for, and which every
2186/// downstream ρ-curvature gate has been substituting an eigensolver's
2187/// *"given this matrix, how wrong is σ?"* for.
2188///
2189/// # Why it is carried rather than consumed here
2190///
2191/// The outer certificate is not the only subsystem that judges this matrix's
2192/// definiteness. `estimate::smoothing_correction::invert_identified_rho_hessian`
2193/// judges it again, later, from the matrix alone — and #2428 is precisely the
2194/// two reaching opposite verdicts on one matrix at one point. Handing the
2195/// measurement forward is what stops the second site from re-deciding a
2196/// question the first one measured.
2197#[derive(Clone, Debug)]
2198pub struct CriterionCurvatureDisagreement {
2199    /// The unit direction probed, in the FULL outer coordinate vector.
2200    ///
2201    /// Carried so a consumer whose Hessian is a coordinate sub-block can check
2202    /// that the direction lives inside its own block before reading the bound:
2203    /// `‖δH‖₂` measured on a wider matrix does not bound a sub-block's error in
2204    /// general, but a direction supported entirely on the sub-block does
2205    /// measure exactly that sub-block.
2206    pub direction: Array1<f64>,
2207    /// `vᵀHv` as the analytic outer Hessian reported it — the number in dispute.
2208    pub analytic_curvature: f64,
2209    /// What the criterion's own symmetric probe ladder measured there.
2210    pub ladder: gam_linalg::curvature_resolution::LadderCurvature,
2211}
2212
2213impl CriterionCurvatureDisagreement {
2214    /// The certified lower bound on `‖δH‖₂`, i.e. the disagreement net of the
2215    /// ladder's own uncertainty. Exactly `0.0` when the two agree.
2216    pub fn hessian_error_2norm(&self) -> f64 {
2217        self.ladder.hessian_error_against(self.analytic_curvature)
2218    }
2219
2220    /// The probed direction restricted to the leading `dimension` coordinates,
2221    /// but only when every coordinate beyond them is exactly zero and the
2222    /// restriction is still a unit vector to orthonormality round-off.
2223    ///
2224    /// Both conditions are required for the bound to transfer: the identity
2225    /// `|vᵀ(δH)v| ≤ ‖δH‖₂` is about the sub-block's own `δH` only if `v` has no
2226    /// component outside it, and the Rayleigh quotient is a curvature only if
2227    /// `v` is normalised.
2228    pub fn restricted_to_leading(&self, dimension: usize) -> Option<Array1<f64>> {
2229        if dimension == 0 || self.direction.len() < dimension {
2230            return None;
2231        }
2232        if self.direction.iter().skip(dimension).any(|value| *value != 0.0) {
2233            return None;
2234        }
2235        let head = self.direction.slice(ndarray::s![..dimension]).to_owned();
2236        let norm = head.dot(&head).sqrt();
2237        let round_off = 64.0 * (self.direction.len() as f64) * f64::EPSILON;
2238        ((norm - 1.0).abs() <= round_off).then_some(head)
2239    }
2240}
2241
2242/// What the CRITERION said about a Hessian's reported negative direction
2243/// (#2357/#2155/#2612).
2244///
2245/// The escape has always been able to distinguish "the saddle is real, here is
2246/// the descending reseed" from "no descending trial was found", but only the
2247/// first was reported to the caller: the second reached the refusal as `None`,
2248/// where it was indistinguishable from "the escape was never runnable", and the
2249/// curvature refusal proceeded on the matrix's word alone.
2250///
2251/// Those are three different states and one of them is a MEASUREMENT of the
2252/// criterion, so they are three variants.
2253#[derive(Debug)]
2254pub(crate) enum SaddleAdjudication {
2255    /// A strictly-descending feasible point exists along the reported direction:
2256    /// the point is not a minimum, and this is the one-shot reseed.
2257    Descended(Array1<f64>),
2258    /// Every feasible step along the reported direction — both signs, from one
2259    /// e-fold down to the step at which the quadratic model's own predicted
2260    /// decrease reaches the criterion's resolution — failed to lower the
2261    /// objective. The claim has been falsified over its whole falsifiable
2262    /// range.
2263    Contradicted {
2264        /// Trials actually evaluated (finite cost, not clamped back onto ρ).
2265        probed: usize,
2266        /// Smallest step the ladder reached.
2267        smallest_step: f64,
2268        /// `½|λ_min|·α_min²` — what the claim predicted at that step, against
2269        /// which the criterion's resolution was the stopping standard.
2270        predicted_at_smallest: f64,
2271        /// The criterion's own resolution, i.e. the standard that bounded the
2272        /// ladder.
2273        objective_resolution: f64,
2274        /// Best objective seen, against the baseline it had to beat.
2275        best_seen_cost: f64,
2276        /// What the ladder MEASURED about the analytic Hessian, when it could
2277        /// be determined (#2748). `None` is an absent measurement — the fit was
2278        /// undetermined, or the baseline could not be restored — and must never
2279        /// be read as a zero error.
2280        criterion_curvature: Option<CriterionCurvatureDisagreement>,
2281    },
2282    /// The adjudication could not be run: no eigen-resolvable negative
2283    /// direction, nothing left to search after rails and invariance, an
2284    /// eigensolver failure, or trials that could not be evaluated at all.
2285    /// Nothing has been established about the point either way.
2286    Declined(String),
2287}
2288
2289/// Escape point off a certified strict saddle in the free (un-railed) subspace
2290/// (#2357, generalised to the box-constrained case in #2155), and — when no
2291/// escape exists — the verdict that the criterion has CONTRADICTED the matrix
2292/// (#2612).
2293///
2294/// A gradient-only outer convergence gate — ARC's own, or the cost-stall guard's
2295/// — can ARRIVE at a point that is first-order stationary (`‖Pg‖ ≤ bound`) yet
2296/// sits on genuinely indefinite curvature in its INTERIOR (un-railed) directions,
2297/// and stop there because its gradient already cleared tolerance. The mandatory
2298/// analytic certificate then refuses the point as `INDEFINITE CURVATURE AT
2299/// INTERIOR OPTIMUM` — a verdict `certificate_hessian_is_psd_off_railed` reaches
2300/// on the reduced Hessian restricted to the un-railed coordinates, so it fires
2301/// whether or not some other coordinate happens to be railed. Such a point is a
2302/// saddle, not a minimum: the most-negative-curvature eigenvector `v` of that
2303/// reduced Hessian is a strict, box-feasible descent direction the optimizer
2304/// never took. An
2305/// identical warm-started resume escapes it trivially (its fresh cubic step moves
2306/// off the ridge, which is why the resume converges where the cold run refuses);
2307/// this reproduces that escape deterministically by stepping `ρ ± α·v` to a
2308/// strictly-lower objective and handing the point back as a one-shot reseed.
2309///
2310/// Termination is guaranteed: along a direction of negative curvature
2311/// `vᵀHv = λ_min < 0` at a near-stationary gradient,
2312/// `f(ρ ± αv) = f(ρ) ± α(g·v) + ½α²λ_min + o(α²)` strictly decreases for small
2313/// enough `α` once the sign is chosen so the first-order term is non-positive, so
2314/// the finite backtracking below always finds a descending feasible point when
2315/// one exists inside the box.
2316///
2317/// Returns `None` (no reseed; the ordinary refusal proceeds) when the Hessian
2318/// carries no eigen-resolvable negative direction, or no bounded step along it
2319/// clears the box projection with a strict objective decrease. Restores the
2320/// objective's profiled inner state to `rho` before returning either way, so the
2321/// refusal path that follows measures the checkpoint rather than the last probe.
2322fn adjudicate_negative_curvature(
2323    obj: &mut dyn OuterObjective,
2324    rho: &Array1<f64>,
2325    gradient: &Array1<f64>,
2326    hessian: &Array2<f64>,
2327    railed: &[usize],
2328    invariance: Option<&Array2<f64>>,
2329    baseline_cost: f64,
2330    objective_resolution: f64,
2331    bounds: &(Array1<f64>, Array1<f64>),
2332    context: &str,
2333) -> SaddleAdjudication {
2334    use faer::Side;
2335    use gam_linalg::faer_ndarray::FaerEigh;
2336
2337    let n = hessian.nrows();
2338    if n == 0 || hessian.ncols() != n || hessian.iter().any(|v| !v.is_finite()) {
2339        // #2665: every `None` in this function is a DIFFERENT reason the escape
2340        // did not fire, and the caller records none of them -- the refusal that
2341        // follows says only that the curvature floor did not clear. On the
2342        // SAS/mixture cluster the escape is silently absent (measured: no mint
2343        // line at all, and four resumes at a bitwise-identical rho), and the
2344        // exits cannot be told apart from the run record. The sibling
2345        // NOT ATTEMPTED warning above covers the case where this function is
2346        // never called; these cover the case where it is called and declines.
2347        return SaddleAdjudication::Declined(format!(
2348            "the analytic Hessian is not a usable square finite matrix (rows={}, cols={}, \
2349             all_finite={})",
2350            n,
2351            hessian.ncols(),
2352            hessian.iter().all(|v| v.is_finite()),
2353        ));
2354    }
2355    // The escape direction lives in the INTERIOR (un-railed) subspace — the exact
2356    // reduced Hessian / critical cone that `certificate_hessian_is_psd_off_railed`
2357    // judges for the PSD verdict. A coordinate railed at a box bound with an
2358    // outward KKT gradient is already at its constrained optimum; its curvature is
2359    // the flat/indefinite infinite-smoothing plateau (λ ~ 1e13) and carries no
2360    // feasible descent. Including it would let the step chase that spurious
2361    // direction and simply re-rail. Restricting to the un-railed block yields a
2362    // feasible descent that holds every rail fixed, so the escape generalises from
2363    // the fully-interior saddle to a box-constrained one whose free-direction
2364    // reduced Hessian is indefinite (#2357 → #2155). With no rail this is exactly
2365    // the full-Hessian eigenproblem as before.
2366    let railed_set: std::collections::BTreeSet<usize> = railed.iter().copied().collect();
2367    let interior: Vec<usize> = (0..n).filter(|k| !railed_set.contains(k)).collect();
2368    if interior.is_empty() {
2369        // Every coordinate is railed: there is no feasible interior direction and
2370        // the rail KKT signs are the whole certificate.
2371        return SaddleAdjudication::Declined(format!(
2372            "every one of the {n} outer coordinates is railed, so there is no feasible interior \
2373             direction to descend"
2374        ));
2375    }
2376    // #2676: the escape must search the SAME subspace the certificate judged.
2377    // `judged_subspace_basis` returns the interior indicator basis when there is
2378    // no invariance, so `sub` and the lift below are bit-identical on that path;
2379    // with one, the escape stops being able to pick the criterion-invariant
2380    // direction — where the only "negative curvature" available is the
2381    // chain-rule term `sum_k g_k t_k^2`, i.e. the residual gradient wearing a
2382    // curvature's clothes — instead of the genuine saddle direction that
2383    // refused.
2384    let deflate = invariance.filter(|basis| basis.nrows() == n && basis.ncols() > 0);
2385    let Some(judged) = crate::penalty_invariance::judged_subspace_basis(n, railed, deflate) else {
2386        return SaddleAdjudication::Declined(
2387            "after removing the railed coordinates and the criterion's own invariance there is \
2388             no direction left to search"
2389                .to_string(),
2390        );
2391    };
2392    let m = judged.ncols();
2393    let sub = match deflate {
2394        Some(_) => crate::penalty_invariance::compress_to_judged_subspace(hessian, &judged),
2395        None => {
2396            let mut sub = Array2::<f64>::zeros((m, m));
2397            for (i, &ri) in interior.iter().enumerate() {
2398                for (j, &rj) in interior.iter().enumerate() {
2399                    sub[[i, j]] = hessian[[ri, rj]];
2400                }
2401            }
2402            sub
2403        }
2404    };
2405    let (eigenvalues, eigenvectors) = match sub.eigh(Side::Lower) {
2406        Ok(pair) => pair,
2407        Err(err) => {
2408            return SaddleAdjudication::Declined(format!(
2409                "the interior sub-block's eigendecomposition failed ({err})"
2410            ));
2411        }
2412    };
2413    // The SAME √ε·‖H‖ margin `certificate_hessian_is_psd` uses to separate a
2414    // genuine negative eigenvalue from O(ε·‖H‖) assembly roundoff: only a truly
2415    // negative direction — not a flat / near-semidefinite one — carries a descent
2416    // the reseed can exploit. Measured on the interior sub-block's diagonal so the
2417    // threshold matches the reduced PSD verdict exactly.
2418    let max_diag = interior
2419        .iter()
2420        .fold(0.0_f64, |acc, &j| acc.max(hessian[[j, j]].abs()));
2421    let neg_margin = f64::EPSILON.sqrt() * max_diag.max(1.0);
2422    let mut min_idx = 0usize;
2423    for k in 1..eigenvalues.len() {
2424        if eigenvalues[k] < eigenvalues[min_idx] {
2425            min_idx = k;
2426        }
2427    }
2428    if !(eigenvalues[min_idx] < -neg_margin) {
2429        // The certificate refuses on the FLOOR (`H + diag(|g|)` not PSD); this
2430        // gate admits on a sqrt(EPSILON)*||H|| ROUNDOFF margin. They are
2431        // different numbers, so a point can be refused for curvature AND
2432        // declined for escape, with no record of either bound. Print both so
2433        // the gap is measurable rather than inferred (#2665).
2434        return SaddleAdjudication::Declined(format!(
2435            "the interior sub-block's most negative eigenvalue does not clear the roundoff \
2436             margin: lambda_min={:.6e}, neg_margin={:.6e} (= sqrt(EPSILON) * max(1, max_k \
2437             |H_kk|) with max_diag={:.6e}), interior_dim={}",
2438            eigenvalues[min_idx], neg_margin, max_diag, m,
2439        ));
2440    }
2441    let v_sub = eigenvectors.column(min_idx);
2442    let dir_norm = v_sub.dot(&v_sub).sqrt();
2443    if !(dir_norm > 0.0) || !dir_norm.is_finite() {
2444        return SaddleAdjudication::Declined(format!(
2445            "the lambda_min={:.6e} eigenvector has an unusable norm {dir_norm:.6e}",
2446            eigenvalues[min_idx],
2447        ));
2448    }
2449    // Lift the judged eigenvector into the full ρ space through the same basis
2450    // the sub-block was taken in. Its rows are exactly zero on every railed
2451    // coordinate, so the backtracking step below still holds all rails fixed;
2452    // with no invariance the basis is the interior indicator matrix and this is
2453    // the historical scatter, multiplication by exact zeros and ones.
2454    let direction = judged.dot(&v_sub.mapv(|value| value / dir_norm));
2455    // First-order-consistent sign: move against the (tiny) gradient's projection
2456    // onto `v` so the linear term never opposes the curvature descent. With a
2457    // stationary gradient the tie is arbitrary; the opposite sign is tried below
2458    // regardless, which also covers a `v` that projects straight out of the box.
2459    let primary_sign = if gradient.dot(&direction) > 0.0 {
2460        -1.0
2461    } else {
2462        1.0
2463    };
2464    // The step ladder is DERIVED from what the claim predicts, not chosen
2465    // (#2612).
2466    //
2467    // One e-fold in log-λ is a macroscopic step across the saddle ridge, and
2468    // ARC refines from wherever this lands, so the largest step stays `1`. What
2469    // the old fixed ladder could not say is where to STOP: it halted at
2470    // `0.0625` because five entries had been written down, so a claim whose
2471    // descent only appears below that step was reported the same way as a claim
2472    // with no descent at all — and the refusal then proceeded on the matrix's
2473    // word either way.
2474    //
2475    // At a stationary point the quadratic model of the claim itself is
2476    //
2477    // ```text
2478    //     V(ρ ± αv) − V(ρ) ≈ ½ λ_min α²,     λ_min < 0
2479    // ```
2480    //
2481    // so the claim predicts a decrease of `½|λ_min|α²`. Once that falls to the
2482    // criterion's own resolution the claim predicts nothing the criterion can
2483    // represent, and no smaller step can falsify it. That step,
2484    //
2485    // ```text
2486    //     α_min = sqrt(2 · objective_resolution / |λ_min|),
2487    // ```
2488    //
2489    // is therefore the exact end of the claim's FALSIFIABLE RANGE — derived
2490    // from the eigenvalue in dispute and the same `rel_cost_tolerance`-anchored
2491    // resolution the rail and cost-stall machinery already use, with no
2492    // constant chosen here. Probing from `1` down to it and finding no descent
2493    // in either sign is a measurement of the criterion that contradicts the
2494    // matrix; stopping earlier would only have been a statement about the
2495    // ladder.
2496    let lambda_min = eigenvalues[min_idx];
2497    let alpha_min = if objective_resolution.is_finite() && objective_resolution > 0.0 {
2498        (2.0 * objective_resolution / lambda_min.abs()).sqrt().min(1.0)
2499    } else {
2500        // No usable resolution: keep the historical five-rung ladder's reach.
2501        0.0625
2502    };
2503    let mut escape_step_scales: Vec<f64> = Vec::new();
2504    let mut alpha = 1.0_f64;
2505    loop {
2506        escape_step_scales.push(alpha);
2507        // `f64::EPSILON` is where halving stops changing `ρ + αv` at all — a
2508        // property of the arithmetic, not a budget.
2509        if alpha <= alpha_min || alpha <= f64::EPSILON {
2510            break;
2511        }
2512        alpha *= 0.5;
2513    }
2514    // The strict-decrease floor is the CRITERION's resolution, not the
2515    // arithmetic's (#2612).
2516    //
2517    // The ladder above stops at `α_min = sqrt(2·objective_resolution/|λ_min|)`
2518    // on the stated ground that below it "the claim predicts nothing the
2519    // criterion can represent". A trial's MEASURED decrease is the same kind of
2520    // quantity as the claim's predicted one, so it has to be judged against the
2521    // same resolution: a step that lowers the objective by less than the
2522    // criterion can resolve has not descended, it has reproduced the noise the
2523    // ladder's own stopping rule was derived from. Accepting it as an escape
2524    // spends the one-shot reseed on a number the criterion cannot distinguish
2525    // from zero, and the retry — which cannot adjudicate again — then refuses on
2526    // the matrix's word, which is the state this whole block exists to prevent.
2527    //
2528    // Measured before this changed, with the floor at `16ε|V|` (roundoff) while
2529    // the ladder's limit used `objective_resolution`, i.e. the same function
2530    // holding two notions of "a decrease the criterion can represent" ten orders
2531    // apart:
2532    //
2533    // ```text
2534    //   penguins stride-3, unbiased probe: λ_min = −6.35e−7 … −1.99e−6,
2535    //     four reseeds minted on decreases 2e−6 … 4e−6 of an objective ≈ 2.158,
2536    //     against objective_resolution = 1.228e−3 — three orders BELOW it;
2537    //   banded quasi-separated, armed refit: λ_min = −9.19e−3 … −1.12e−2,
2538    //     three reseeds on decreases 3.4e−4, 1.4e−4, 5.0e−5 of ≈ 53.66,
2539    //     against a measured cost-stall noise floor of 1.91e−4.
2540    // ```
2541    //
2542    // Both fits then refused for lack of a certified optimum, and the fit that
2543    // shipped was the Firth/Jeffreys-armed one.
2544    //
2545    // Roundoff remains the hard lower limit — where `objective_resolution` is
2546    // absent or non-positive there is nothing derived to use, and a decrease
2547    // under `16ε|V|` is not a decrease under any reading.
2548    let roundoff_floor = baseline_cost.abs().max(1.0) * (16.0 * f64::EPSILON);
2549    let strict_floor = if objective_resolution.is_finite() && objective_resolution > 0.0 {
2550        objective_resolution.max(roundoff_floor)
2551    } else {
2552        roundoff_floor
2553    };
2554    // `(cost, point, sign, alpha)`. The step that produced the point is carried
2555    // because the ladder ANSWERS a different question from the one the reseed
2556    // asks (#2612): see [`expand_confirmed_descent`].
2557    let mut best: Option<(f64, Array1<f64>, f64, f64)> = None;
2558    // #2665 bookkeeping: "no descending trial", "every trial clamped back onto
2559    // rho" and "every trial evaluated non-finite" are three different failures
2560    // that all leave `best == None`. Count them so the declined exit below says
2561    // which one happened, and carry the best cost actually SEEN so the
2562    // shortfall against `baseline_cost - strict_floor` is a number.
2563    let mut probed = 0usize;
2564    let mut clamped_onto_rho = 0usize;
2565    let mut eval_failed = 0usize;
2566    let mut nonfinite = 0usize;
2567    let mut best_seen_cost = f64::INFINITY;
2568    // Every evaluation the descent search makes, kept so the ladder below can
2569    // pair the two signs at a common step instead of paying for them twice
2570    // (#2748). Recording costs nothing and changes no verdict: the loop's
2571    // order, its break and its counters are untouched.
2572    let mut evaluations: Vec<(f64, f64, f64)> = Vec::new();
2573    for sign in [primary_sign, -primary_sign] {
2574        for &alpha in escape_step_scales.iter() {
2575            let mut exact = rho.clone();
2576            for i in 0..n {
2577                exact[i] += sign * alpha * direction[i];
2578            }
2579            let trial = project_to_bounds(&exact, Some(bounds));
2580            // A fully box-clamped trial that lands back on ρ probes nothing.
2581            if outer_theta_bitwise_eq(&trial, rho) {
2582                clamped_onto_rho += 1;
2583                continue;
2584            }
2585            // A PARTIALLY clamped trial is not `ρ ± αv` either, so it cannot
2586            // enter the symmetric average — the second difference would be
2587            // taken between two different directions. It still probes descent,
2588            // which is what this loop is for, so only the ladder skips it.
2589            let unclamped = outer_theta_bitwise_eq(&trial, &exact);
2590            probed += 1;
2591            match obj.eval_cost(&trial) {
2592                Ok(cost) if cost.is_finite() => {
2593                    if unclamped {
2594                        evaluations.push((sign, alpha, cost));
2595                    }
2596                    best_seen_cost = best_seen_cost.min(cost);
2597                    if cost < baseline_cost - strict_floor {
2598                        best = Some((cost, trial, sign, alpha));
2599                        // The ladder descends in α and the reseed only has to
2600                        // LEAVE the ridge — ARC refines from wherever it lands
2601                        // — so the first (largest) descending step is the
2602                        // escape. Continuing would spend the rest of the
2603                        // falsifiability ladder confirming a claim already
2604                        // confirmed, and that ladder is now derived rather than
2605                        // five rungs long.
2606                        break;
2607                    }
2608                }
2609                Ok(_) => nonfinite += 1,
2610                Err(_) => eval_failed += 1,
2611            }
2612        }
2613        if best.is_some() {
2614            break;
2615        }
2616    }
2617    // ── The ladder, extended until it can MEASURE rather than only falsify ──
2618    //
2619    // The loop above has just evaluated the criterion on both sides of `ρ̂`
2620    // along `v`, which is a symmetric probe ladder — the exact instrument
2621    // `gam_linalg::curvature_resolution`'s header says `ε_f` and `M₄` "come
2622    // free from". It was being spent on one boolean and discarded.
2623    //
2624    // It is spent on the boolean because the falsifiability ladder stops at
2625    // `α_min = sqrt(2·objective_resolution/|λ_min|)`, and when the claim is
2626    // small that is `≥ 1` and the ladder is ONE rung. One rung cannot fit two
2627    // parameters, so the extension below is not an optional refinement: without
2628    // it there is no measurement at all. It runs only where the descent search
2629    // has already failed, i.e. on the path that is about to hand a curvature
2630    // verdict to a gate that would otherwise judge it against an eigensolver's
2631    // backward error (#2748).
2632    if best.is_none() {
2633        LadderExtension {
2634            rho,
2635            direction: &direction,
2636            lambda_min,
2637            roundoff_floor,
2638            smallest_escape_step: escape_step_scales.last().copied().unwrap_or(1.0),
2639            bounds,
2640            context,
2641        }
2642        .run(obj, &mut evaluations);
2643    }
2644    // Restore the profiled inner state to the checkpoint ρ so the refusal path
2645    // that follows measures the checkpoint, not the last probe.
2646    //
2647    // Its RETURNED value is the ladder's baseline: `f(x)` has to come from the
2648    // same instrument, in the same state, as `f(x ± αv)`, and any drift between
2649    // this evaluation and the `baseline_cost` the caller passed in is part of
2650    // the `ε_f` the ladder is measuring rather than something to hide from it.
2651    let restored_baseline = match obj.eval_cost(rho) {
2652        Ok(cost) => Some(cost),
2653        Err(err) => {
2654            log::warn!(
2655                "[CERTIFICATE] {context}: failed to restore the objective to the checkpoint \
2656                 after saddle-escape probing: {err}"
2657            );
2658            None
2659        }
2660    };
2661    if let Some((cost, _, sign, alpha)) = best {
2662        let descent = expand_confirmed_descent(
2663            obj,
2664            rho,
2665            &direction,
2666            LadderConfirmedStep {
2667                sign,
2668                alpha,
2669                cost,
2670                strict_floor,
2671            },
2672            bounds,
2673            context,
2674        );
2675        log::info!(
2676            "[CERTIFICATE] {context}: interior strict saddle (λ_min={lambda_min:.3e} < 0, |Pg| \
2677             within band); minting a negative-curvature escape reseed (objective {:.6e} → \
2678             {:.6e}) for one retry (#2357)",
2679            baseline_cost,
2680            descent.cost,
2681        );
2682        return SaddleAdjudication::Descended(descent.point);
2683    }
2684    let smallest_step = escape_step_scales
2685        .last()
2686        .copied()
2687        .unwrap_or(f64::INFINITY);
2688    let predicted_at_smallest = 0.5 * lambda_min.abs() * smallest_step * smallest_step;
2689    // `probed == 0` is not a contradiction: nothing was evaluated, so nothing
2690    // was falsified. The three ways that happens are counted separately for
2691    // exactly this reason (#2665).
2692    if probed == 0 {
2693        return SaddleAdjudication::Declined(format!(
2694            "a certified strict saddle (lambda_min={lambda_min:.6e}, neg_margin={neg_margin:.6e}) \
2695             produced no EVALUABLE trial at all: clamped_back_onto_rho={clamped_onto_rho}, \
2696             eval_failed={eval_failed}, non_finite={nonfinite} over {} step(s)",
2697            escape_step_scales.len(),
2698        ));
2699    }
2700    // The ladder's verdict on the very number in dispute. `v` has unit norm in
2701    // the outer coordinates, so `v'Hv` and `d²/dα² V(ρ̂+αv)|₀` are the same
2702    // quantity computed two ways -- one analytically, one from the criterion's
2703    // own values -- and their difference is exactly zero in exact arithmetic.
2704    let criterion_curvature = restored_baseline.and_then(|baseline| {
2705        let mut by_step: std::collections::BTreeMap<u64, (Option<f64>, Option<f64>)> =
2706            std::collections::BTreeMap::new();
2707        for &(sign, alpha, cost) in &evaluations {
2708            let slot = by_step.entry(alpha.to_bits()).or_insert((None, None));
2709            if sign > 0.0 {
2710                slot.0 = Some(cost);
2711            } else {
2712                slot.1 = Some(cost);
2713            }
2714        }
2715        let probes: Vec<gam_linalg::curvature_resolution::SymmetricProbe> = by_step
2716            .into_iter()
2717            .filter_map(|(bits, (forward, backward))| {
2718                Some(gam_linalg::curvature_resolution::SymmetricProbe::new(
2719                    f64::from_bits(bits),
2720                    forward?,
2721                    backward?,
2722                ))
2723            })
2724            .collect();
2725        gam_linalg::curvature_resolution::measure_symmetric_ladder(baseline, &probes).map(
2726            |ladder| CriterionCurvatureDisagreement {
2727                direction: direction.clone(),
2728                analytic_curvature: lambda_min,
2729                ladder,
2730            },
2731        )
2732    });
2733    // ── The escape declined on a BORROWED tolerance; the ladder measured the
2734    // ── criterion's own, and it re-tests the descent against that ─────────
2735    //
2736    // The strict-decrease floor above is `objective_resolution` — the
2737    // optimizer's DECLARED tolerance, `rel_cost_tolerance * |V|`. #2690's
2738    // standing rule is that `eps_f` is a property of a fixture, measured on the
2739    // fixture in hand, and never borrowed; a declared tolerance is the most
2740    // borrowed quantity available. Measured on `papuan_oce4_matern_k24` the two
2741    // are `2.178e-4` (declared) and `2.549e-11` (the ladder's) — SEVEN orders
2742    // apart — and the descent the escape declined there was `~2.3e-7`, four
2743    // orders above the criterion's actual noise. The claim was falsifiable all
2744    // along; the floor could not see it.
2745    //
2746    // Three conditions, all measured on this fixture at this point, and the
2747    // conjunction is what keeps this from being a looser escape:
2748    //
2749    //   1. the criterion's own curvature along `v` is NEGATIVE — the ladder
2750    //      agrees with the matrix about the sign, rather than the matrix being
2751    //      believed;
2752    //   2. that curvature is RESOLVED by the ladder's own Law 1 floor
2753    //      `(2/sqrt(3))*sqrt(eps_f*|M4|)` — the finest curvature ANY central
2754    //      second difference of this criterion could reach — so the descent is
2755    //      a property of the criterion and not of the ladder's noise;
2756    //   3. some already-evaluated trial is lower than the baseline by more than
2757    //      the ARITHMETIC's own `roundoff_floor`, which is all that is left to
2758    //      check once (1) and (2) hold: a stationary point with a resolved
2759    //      negative curvature descends, and the reseed only has to leave the
2760    //      ridge for ARC to refine from where it lands.
2761    //
2762    // ⚠ (3) is deliberately NOT "some trial beat the measured `2*eps_f`".
2763    // A criterion with evaluation error of amplitude `A` produces individual
2764    // trials that dip to `-A` by luck, while the ladder's `eps_f` estimate is
2765    // that amplitude's RMS over the rungs — so a single-trial test against
2766    // `2*eps_f` accepts noise about as often as signal. The
2767    // `unresolvable-well #2612` fixture demonstrates it: planted with an
2768    // evaluation error of `5e-8` and a well only `3.1e-8` deep, one rung dips
2769    // to `-7.6e-8` and beats `2*eps_f ~ 3.5e-8` while carrying no descent at
2770    // all. Condition (2) is the one that decides it correctly there —
2771    // `|c| = 1e-4` against a Law 1 floor of `1.07e-4`, unresolved — because a
2772    // curvature is a property of the WHOLE ladder and averages the noise the
2773    // way a single trial cannot.
2774    //
2775    // Where any of the three fails, nothing changes: on `geo_disease_matern`
2776    // the measured curvature is POSITIVE (`+8.15e-5` against a claimed
2777    // `-6.4e-6`), so (1) fails and the measurement flows on to the smoothing
2778    // correction as a `||dH||_2` instead.
2779    let measured_escape = criterion_curvature.as_ref().and_then(|measured| {
2780        let curvature = measured.ladder.curvature;
2781        if !(curvature < 0.0) {
2782            return None;
2783        }
2784        if !measured
2785            .ladder
2786            .finite_difference_resolution()
2787            .is_ok_and(|resolution| resolution.resolves(curvature))
2788        {
2789            return None;
2790        }
2791        let measured_floor = roundoff_floor;
2792        if !(measured_floor.is_finite() && measured_floor > 0.0) {
2793            return None;
2794        }
2795        let baseline = restored_baseline?;
2796        let mut descent: Option<(f64, f64, f64)> = None;
2797        for &(sign, alpha, cost) in &evaluations {
2798            if cost < baseline - measured_floor
2799                && descent.is_none_or(|(_, _, best): (f64, f64, f64)| cost < best)
2800            {
2801                descent = Some((sign, alpha, cost));
2802            }
2803        }
2804        descent.map(|(sign, alpha, cost)| (sign, alpha, cost, measured_floor, baseline))
2805    });
2806    if let Some((sign, alpha, cost, measured_floor, baseline)) = measured_escape {
2807        let descent = expand_confirmed_descent(
2808            obj,
2809            rho,
2810            &direction,
2811            LadderConfirmedStep {
2812                sign,
2813                alpha,
2814                cost,
2815                strict_floor: measured_floor,
2816            },
2817            bounds,
2818            context,
2819        );
2820        let measured = criterion_curvature
2821            .as_ref()
2822            .expect("a measured escape implies a determined ladder");
2823        log::info!(
2824            "[CERTIFICATE] {context}: the criterion CONFIRMS the reported negative curvature              once its OWN evaluation error is measured rather than declared.              c_criterion={:.6e} (resolved against this fixture's Law 1 floor from the measured              eps_f={:.6e} and M4={:.6e}) against the analytic lambda_min={lambda_min:.6e}; a              trial at step {alpha:.3e} lowered the objective {baseline:.9e} -> {cost:.9e}, a              decrease of {:.6e} against the MEASURED floor {measured_floor:.6e} where the              DECLARED one was {objective_resolution:.6e}. Minting the negative-curvature              escape reseed the declared tolerance was hiding (#2748, #2690, #2357).",
2825            measured.ladder.curvature,
2826            measured.ladder.evaluation_error,
2827            measured.ladder.fourth_derivative,
2828            baseline - cost,
2829        );
2830        return SaddleAdjudication::Descended(descent.point);
2831    }
2832    match criterion_curvature.as_ref() {
2833        Some(measured) => log::warn!(
2834            "[CERTIFICATE] {context}: the criterion's OWN curvature along the disputed \
2835             eigenvector, from the symmetric ladder this adjudication already runs: \
2836             c_criterion={:.6e} +/- {:.6e} against the analytic lambda_min={lambda_min:.6e}, \
2837             over {} rung(s); measured eps_f={:.6e} and M4={:.6e} give this fixture's Law 1 \
2838             floor {} -- the finest curvature ANY central second difference of this criterion \
2839             could resolve. Measured ||dH||_2 from the disagreement = {:.6e} (#2748, #2690).",
2840            measured.ladder.curvature,
2841            measured.ladder.curvature_uncertainty,
2842            measured.ladder.rungs,
2843            measured.ladder.evaluation_error,
2844            measured.ladder.fourth_derivative,
2845            measured
2846                .ladder
2847                .finite_difference_resolution()
2848                .map(|resolution| format!("{resolution}"))
2849                .unwrap_or_else(|error| format!("unavailable ({error})")),
2850            measured.hessian_error_2norm(),
2851        ),
2852        None => log::info!(
2853            "[CERTIFICATE] {context}: the symmetric ladder did not determine a fit (baseline \
2854             restored: {}), so no criterion curvature and no measured ||dH||_2 are available \
2855             here. An absent measurement stays absent (#2748).",
2856            restored_baseline.is_some(),
2857        ),
2858    }
2859    log::warn!(
2860        "[CERTIFICATE] {context}: the criterion CONTRADICTS the reported negative curvature. \
2861         lambda_min={lambda_min:.6e} on the judged sub-block, and {probed} feasible trial(s) \
2862         along its eigenvector — both signs, steps {:.3e} down to {smallest_step:.3e} — lowered \
2863         the objective nowhere. The ladder ends where the claim's own predicted decrease \
2864         (½|λ_min|α² = {predicted_at_smallest:.3e}) reaches the criterion's resolution \
2865         ({objective_resolution:.3e}), so that is the WHOLE range in which the claim could have \
2866         been falsified. best cost seen={best_seen_cost:.9e} against baseline={:.9e} (needed \
2867         < {:.9e}); clamped_back_onto_rho={clamped_onto_rho}, eval_failed={eval_failed}, \
2868         non_finite={nonfinite}. The negative direction is a property of this matrix, not of \
2869         this point (#2612).",
2870        escape_step_scales.first().copied().unwrap_or(1.0),
2871        baseline_cost,
2872        baseline_cost - strict_floor,
2873    );
2874    SaddleAdjudication::Contradicted {
2875        probed,
2876        smallest_step,
2877        predicted_at_smallest,
2878        objective_resolution,
2879        best_seen_cost,
2880        criterion_curvature,
2881    }
2882}
2883
2884/// The falsifiability ladder's own confirmed step, as handed to the expansion.
2885///
2886/// These four travel together — they are one measurement (a signed step along
2887/// the negative-curvature direction, the objective there, and the floor that
2888/// decision was strict against) — so they are one argument. Splitting them into
2889/// four positional `f64`s is what pushed `expand_confirmed_descent` over the
2890/// argument count and produced an `#[allow(clippy::too_many_arguments)]`, which
2891/// this repo bans outright: the lint is naming a real thing, and four adjacent
2892/// same-typed scalars at a call site are a transposition waiting to happen.
2893#[derive(Clone, Copy, Debug)]
2894struct LadderConfirmedStep {
2895    /// Which way along `direction` the ladder confirmed the descent.
2896    sign: f64,
2897    /// The step the ladder confirmed it at.
2898    alpha: f64,
2899    /// The objective there, in the ladder's instrument state.
2900    cost: f64,
2901    /// The decrease the acceptance was strict against — the ladder's measured
2902    /// evaluation floor at the confirming site, never a declared tolerance.
2903    strict_floor: f64,
2904}
2905
2906/// The escape point a CONFIRMED negative-curvature descent actually supports
2907/// (#2612), after the step has been extended past the falsifiability ladder.
2908#[derive(Clone, Debug)]
2909struct ConfirmedDescent {
2910    /// Reseed point, already projected into the box.
2911    point: Array1<f64>,
2912    /// Step along `sign · direction` the point sits at.
2913    alpha: f64,
2914    /// Objective there, as measured in the expansion's own instrument state.
2915    cost: f64,
2916    /// Doublings evaluated. `0` means the ladder's own step stood — either
2917    /// nothing beyond it improved, or it was already the box intersection.
2918    expansions: usize,
2919    /// Whether the accepted step IS the box intersection along the ray, i.e.
2920    /// the descent ran to the constraint face rather than stopping inside it.
2921    on_box_face: bool,
2922}
2923
2924/// The largest `α ≥ 0` for which `ρ + α·d` stays inside the box, exactly.
2925///
2926/// `f64::INFINITY` when no coordinate the ray moves is bounded in the direction
2927/// it moves. Coordinates with `d_i == 0` never bind — the ray does not move
2928/// them — which is what lets the railed block (where `direction` is exactly
2929/// zero by construction) sit at its bounds without capping the step at `0`.
2930fn max_feasible_step_along(
2931    rho: &Array1<f64>,
2932    ray: &Array1<f64>,
2933    bounds: &(Array1<f64>, Array1<f64>),
2934) -> f64 {
2935    let (lower, upper) = bounds;
2936    let mut alpha = f64::INFINITY;
2937    for i in 0..rho.len() {
2938        let step = ray[i];
2939        if step > 0.0 {
2940            if let Some(&limit) = upper.get(i) {
2941                alpha = alpha.min((limit - rho[i]) / step);
2942            }
2943        } else if step < 0.0 {
2944            if let Some(&limit) = lower.get(i) {
2945                alpha = alpha.min((limit - rho[i]) / step);
2946            }
2947        }
2948    }
2949    alpha.max(0.0)
2950}
2951
2952/// Extend a confirmed negative-curvature descent to the step the criterion
2953/// actually supports, instead of the step the falsifiability ladder happened to
2954/// stop at (#2612).
2955///
2956/// # The two questions one ladder was answering
2957///
2958/// [`adjudicate_negative_curvature`] builds a single step ladder `α = 1, ½, ¼,
2959/// …` down to `α_min = sqrt(2·objective_resolution/|λ_min|)` and uses it twice.
2960/// As a falsifier it is exactly right: the smallest step at which the claim
2961/// `½|λ_min|α²` still predicts something the criterion can represent is the end
2962/// of the range in which the claim could be refuted, so probing DOWN from one
2963/// e-fold in log-λ is the whole falsifiable range and finding no descent in it
2964/// contradicts the matrix.
2965///
2966/// As a step rule it is wrong, and wrong in a direction the mathematics names.
2967/// Along a direction of negative curvature the quadratic model
2968///
2969/// ```text
2970///     V(ρ + αv) − V(ρ) ≈ α(g·v) + ½λ_min α²,    λ_min < 0
2971/// ```
2972///
2973/// decreases WITHOUT BOUND in `α` once the sign is chosen so the linear term is
2974/// non-positive. A model with no interior minimiser cannot supply a step length;
2975/// the step has to come from the objective itself and from the feasible box —
2976/// which is the standard treatment of a negative-curvature direction and is
2977/// exactly what a trust region does when its solution lands on the boundary.
2978/// Capping the reseed at the falsifier's largest rung silently asserts the
2979/// opposite: that one e-fold is as far as any such descent ever runs.
2980///
2981/// # What it cost, measured
2982///
2983/// On the `#2612` banded quasi-separated fixture the escape direction is `−e₁`
2984/// to six digits and the criterion falls monotonically along it all the way to
2985/// the box wall:
2986///
2987/// ```text
2988///   baseline        1.786314898942e1
2989///   ladder  α=1     1.786314894043e1
2990///   ladder  α=½     1.786314883184e1   <- the ladder's pick, decrease 1.6e-7
2991///   α=1             1.786314862766e1
2992///   α=2             1.786314814710e1
2993///   α=4             1.786314708132e1
2994///   α=8             1.786314488769e1   <- box intersection, decrease 4.1e-6
2995/// ```
2996///
2997/// so the wall step is worth **26×** the ladder's, and the BFGS resume seeded at
2998/// the ladder's point makes no progress at all (reseed and next refused point
2999/// bit-identical), leaving the escape as the only thing moving ρ — one e-fold
3000/// per escape, against `OUTER_SADDLE_ESCAPE_BUDGET = 3`, on a ridge six e-folds
3001/// long. The fit refused.
3002///
3003/// # The rule, and why it needs no constant
3004///
3005/// Double the confirmed step while the criterion strictly improves, clamped to
3006/// the exact box intersection `max_feasible_step_along`, and keep the best point
3007/// seen. Termination is structural: the box intersection is finite whenever the
3008/// ray moves any bounded coordinate, doubling reaches it in `⌈log₂(α_box/α)⌉`
3009/// steps, and any non-improving trial stops the sweep immediately. The accepted
3010/// point is always the lowest measured, so it is never worse than the ladder's.
3011///
3012/// # One evaluation is spent making the comparison honest
3013///
3014/// The incumbent's cost came from the falsifiability ladder, which ran before
3015/// the symmetric extension and before the checkpoint restore, so it was measured
3016/// in a different profiled-inner state. Measured on the same fixture, the SAME
3017/// point (`sign = −1, α = 1`) evaluated in the ladder and again afterwards
3018/// differs by `3.1e-7` — larger than the descent being adjudicated — because the
3019/// profiled criterion carries warm-start hysteresis well above the `ε_f` the
3020/// symmetric ladder measures on itself. Re-evaluating the incumbent here puts
3021/// the whole comparison chain in one instrument state, for the same reason
3022/// `restored_baseline` is re-measured rather than reused.
3023fn expand_confirmed_descent(
3024    obj: &mut dyn OuterObjective,
3025    rho: &Array1<f64>,
3026    direction: &Array1<f64>,
3027    seed: LadderConfirmedStep,
3028    bounds: &(Array1<f64>, Array1<f64>),
3029    context: &str,
3030) -> ConfirmedDescent {
3031    let LadderConfirmedStep {
3032        sign,
3033        alpha,
3034        cost,
3035        strict_floor,
3036    } = seed;
3037    /// Runaway bound on the doubling sweep. Not a modelling choice — the sweep's
3038    /// END is the box intersection — but a bound on what a pathologically small
3039    /// confirmed step could ask for. Binding it is logged rather than silently
3040    /// truncating the range the escape claims to have searched.
3041    const MAX_EXPANSIONS: usize = 64;
3042
3043    let n = rho.len();
3044    let ray = direction.mapv(|value| sign * value);
3045    let point_at = |alpha: f64| -> Array1<f64> {
3046        let mut point = rho.clone();
3047        for i in 0..n {
3048            point[i] += alpha * ray[i];
3049        }
3050        project_to_bounds(&point, Some(bounds))
3051    };
3052    let alpha_box = max_feasible_step_along(rho, &ray, bounds);
3053    let mut best = ConfirmedDescent {
3054        point: point_at(alpha),
3055        alpha,
3056        cost,
3057        expansions: 0,
3058        on_box_face: alpha_box.is_finite() && alpha >= alpha_box,
3059    };
3060    if !(alpha.is_finite() && alpha > 0.0) || !strict_floor.is_finite() || strict_floor < 0.0 {
3061        return best;
3062    }
3063    // Nothing to extend into: the confirmed step already reaches (or was clamped
3064    // at) the box intersection, so the ray has no room left. Returning before the
3065    // re-measure below keeps this case exactly as cheap as it was.
3066    if !(alpha < alpha_box) {
3067        return best;
3068    }
3069    // The incumbent, re-measured in THIS instrument state so every comparison
3070    // below is between values the same profiled inner solve produced.
3071    if let Ok(reference) = obj.eval_cost(&best.point)
3072        && reference.is_finite()
3073    {
3074        best.cost = reference;
3075    }
3076    let mut expansions = 0usize;
3077    let mut truncated = false;
3078    let mut current = alpha;
3079    while current < alpha_box {
3080        if expansions >= MAX_EXPANSIONS {
3081            truncated = true;
3082            break;
3083        }
3084        let next = (2.0 * current).min(alpha_box);
3085        if !(next > current) || !next.is_finite() {
3086            break;
3087        }
3088        expansions += 1;
3089        let trial = point_at(next);
3090        // A doubling that lands back on ρ (the whole ray clamped away) probes
3091        // nothing and cannot be a reseed.
3092        if outer_theta_bitwise_eq(&trial, rho) {
3093            break;
3094        }
3095        match obj.eval_cost(&trial) {
3096            Ok(trial_cost) if trial_cost.is_finite() && trial_cost < best.cost - strict_floor => {
3097                best = ConfirmedDescent {
3098                    point: trial,
3099                    alpha: next,
3100                    cost: trial_cost,
3101                    expansions,
3102                    on_box_face: next >= alpha_box,
3103                };
3104                current = next;
3105            }
3106            _ => break,
3107        }
3108    }
3109    if best.expansions > 0 || truncated {
3110        log::info!(
3111            "[CERTIFICATE] {context}: the confirmed negative-curvature descent was extended past \
3112             the falsifiability ladder's step alpha={alpha:.6e} to alpha={:.6e} over {} \
3113             doubling(s) ({} evaluated), objective {:.9e} -> {:.9e}; box intersection along the \
3114             ray is alpha_box={alpha_box:.6e} and the accepted step {} it (#2612).{}",
3115            best.alpha,
3116            best.expansions,
3117            expansions,
3118            cost,
3119            best.cost,
3120            if best.on_box_face { "IS" } else { "is inside" },
3121            if truncated {
3122                format!(" -- TRUNCATED at the {MAX_EXPANSIONS}-doubling budget, so the ray was not searched to the box")
3123            } else {
3124                String::new()
3125            },
3126        );
3127    }
3128    // Same contract as the adjudication's own checkpoint restore: leave the
3129    // profiled inner state at ρ, not at the last probe.
3130    if let Err(err) = obj.eval_cost(rho) {
3131        log::warn!(
3132            "[CERTIFICATE] {context}: failed to restore the objective to the checkpoint after \
3133             extending the negative-curvature descent: {err}"
3134        );
3135    }
3136    best
3137}
3138
3139/// Extend an adjudication's symmetric probe ladder until it can determine a
3140/// curvature, not merely fail to falsify one (#2748).
3141///
3142/// # Why an extension is needed at all
3143///
3144/// The falsifiability ladder stops at
3145/// `α_min = sqrt(2·objective_resolution/|λ_min|)`, which is the right place to
3146/// stop *asking whether the claim descends*: below it the claim predicts less
3147/// than the criterion can represent. But for a small claim that bound is `≥ 1`,
3148/// so the ladder is a SINGLE rung — and one rung cannot fit the two parameters
3149/// of `N(α) = c·α² + (M₄/12)·α⁴`. The falsification and the measurement need
3150/// different ranges, and only one of them was being run.
3151///
3152/// # Where the extension ends, and why that is derived
3153///
3154/// The claim's own predicted numerator at step `α` is `|λ_min|·α²`. It stops
3155/// being distinguishable from the objective's own arithmetic when it reaches
3156/// `roundoff_floor = 16ε·max(1,|V|)` — the same floor the escape's strict
3157/// decrease test uses — so
3158///
3159/// ```text
3160///     α_end = sqrt(roundoff_floor / |λ_min|)
3161/// ```
3162///
3163/// is where the ladder passes out of the signal and into the plateau. The
3164/// plateau is not waste: it is exactly where `ε_f` is read off, per this
3165/// module's `curvature_resolution` header, so the ladder runs TWO halvings past
3166/// `α_end` — the smallest number of plateau rungs that makes the residual a
3167/// scatter rather than a single point.
3168///
3169/// # Cost, and the refusal to hide it
3170///
3171/// Two objective evaluations per rung, on a path that has already failed to
3172/// find a descent and is about to hand a curvature verdict to a gate that can
3173/// abort the whole fit. `MAX_LADDER_RUNGS` bounds it for a pathological
3174/// `λ_min`; when it binds, that is logged rather than silently truncating the
3175/// range the measurement claims to cover.
3176struct LadderExtension<'a> {
3177    /// The certified point the ladder is centred on.
3178    rho: &'a Array1<f64>,
3179    /// Unit direction whose curvature is in dispute.
3180    direction: &'a Array1<f64>,
3181    /// The analytic claim, `v'Hv`, whose predicted numerator sets where the
3182    /// ladder passes out of signal and into plateau.
3183    lambda_min: f64,
3184    /// `16 eps * max(1, |V|)`, the objective's own arithmetic floor.
3185    roundoff_floor: f64,
3186    /// Smallest step the falsifiability ladder already reached; the extension
3187    /// starts one halving below it.
3188    smallest_escape_step: f64,
3189    /// The rho box; a clamped trial is not `rho +- alpha v` and cannot enter a
3190    /// symmetric average.
3191    bounds: &'a (Array1<f64>, Array1<f64>),
3192    /// Diagnostic label of the calling certificate.
3193    context: &'a str,
3194}
3195
3196impl LadderExtension<'_> {
3197    fn run(
3198        &self,
3199        obj: &mut dyn OuterObjective,
3200        evaluations: &mut Vec<(f64, f64, f64)>,
3201    ) {
3202        let Self {
3203            rho,
3204            direction,
3205            lambda_min,
3206            roundoff_floor,
3207            smallest_escape_step,
3208            bounds,
3209            context,
3210        } = *self;
3211        /// Rung budget. Not a statistical choice — the ladder's END is derived
3212        /// above — but a bound on the evaluations a pathological `λ_min` could
3213        /// ask for. Binding it is reported rather than silently truncating.
3214        const MAX_LADDER_RUNGS: usize = 32;
3215
3216        let n = rho.len();
3217        if !lambda_min.is_finite() || lambda_min == 0.0 || !roundoff_floor.is_finite() {
3218            return;
3219        }
3220        let alpha_end = (roundoff_floor / lambda_min.abs()).sqrt();
3221        let mut alpha = smallest_escape_step;
3222        if !alpha.is_finite() || alpha <= 0.0 {
3223            return;
3224        }
3225        let mut plateau_rungs = 0usize;
3226        let mut added = 0usize;
3227        let mut truncated = false;
3228        loop {
3229            alpha *= 0.5;
3230            // `f64::EPSILON` is where halving stops changing `ρ + αv` at all — the
3231            // same arithmetic limit the escape ladder uses.
3232            if !(alpha > f64::EPSILON) {
3233                break;
3234            }
3235            if alpha <= alpha_end {
3236                plateau_rungs += 1;
3237            }
3238            if plateau_rungs > 2 {
3239                break;
3240            }
3241            if added >= MAX_LADDER_RUNGS {
3242                truncated = true;
3243                break;
3244            }
3245            added += 1;
3246            for sign in [1.0_f64, -1.0_f64] {
3247                let mut trial = rho.clone();
3248                for i in 0..n {
3249                    trial[i] += sign * alpha * direction[i];
3250                }
3251                let projected = project_to_bounds(&trial, Some(bounds));
3252                // Only an UNCLAMPED pair is `ρ ± αv`; a clamped one is a different
3253                // direction and would corrupt the symmetric average rather than
3254                // add to it.
3255                if !outer_theta_bitwise_eq(&projected, &trial) {
3256                    continue;
3257                }
3258                if let Ok(cost) = obj.eval_cost(&projected)
3259                    && cost.is_finite()
3260                {
3261                    evaluations.push((sign, alpha, cost));
3262                }
3263            }
3264        }
3265        log::debug!(
3266            "[CERTIFICATE] {context}: extended the symmetric ladder by {added} rung(s) \
3267             ({} evaluations) down to alpha={alpha:.6e}, past the derived plateau entry \
3268             alpha_end=sqrt(roundoff_floor/|lambda_min|)={alpha_end:.6e}{}",
3269            2 * added,
3270            if truncated {
3271                format!(" -- TRUNCATED at the {MAX_LADDER_RUNGS}-rung budget, so the plateau may not have been reached")
3272            } else {
3273                String::new()
3274            },
3275        );
3276    }
3277}
3278
3279/// Second-order predicted objective decrease of a safeguarded Newton step at a
3280/// flat-valley cost-stall exit (#2253/#2249/#2015).
3281///
3282/// On a flat-valley cost-stall exit the outer criterion has provably stopped
3283/// improving (the cost-stall window fired), yet the re-measured projected
3284/// gradient can sit modestly above the score-relative flat band on a
3285/// weakly-identified small-n fit (measured: |Pg| ≈ 0.072 vs a score-relative
3286/// band ≈ 0.053 on an n=84/p=64 K=1 circle). Whether that residual is genuine
3287/// available descent is a SECOND-ORDER question. The improvement a safeguarded
3288/// Newton step buys is the Newton decrement over two:
3289///
3290/// ```text
3291/// Δpred = ½ · gᵀ H⁻¹ g,
3292/// ```
3293///
3294/// the textbook Newton stopping quantity (Boyd–Vandenberghe §9.5). When `Δpred`
3295/// is below the outer objective tolerance, no step can reduce the criterion by
3296/// more than that tolerance and the point is stationary at the resolution the
3297/// criterion can be optimized — the mathematically correct "no further descent
3298/// possible" criterion.
3299///
3300/// This is curvature-scaled, not a constant: because `H⁻¹` weights each gradient
3301/// component by the inverse eigenvalue, a residual aligned with a NEAR-FLAT
3302/// Hessian eigenvector (a linear ramp that DOES carry real descent) inflates
3303/// `gᵀ H⁻¹ g` toward the roundoff-regularized `|g_flat|² / shift` and is
3304/// REJECTED; only a residual that is small along the well-curved directions and
3305/// nearly orthogonal to the flat ones certifies. An indefinite Hessian never
3306/// reaches here — the certificate's curvature gate (`certificate_hessian_is_psd`)
3307/// rejects a genuinely indefinite point independently, and this factorization
3308/// returns `None` on a non-PSD shifted factor so the caller falls back to the
3309/// gradient-only bound.
3310///
3311/// `hessian` and `grad` are the analytic outer Hessian and the KKT-PROJECTED
3312/// gradient at the certified point. The shift `√ε · max|H_jj|` matches
3313/// [`certificate_hessian_is_psd`] so the definiteness verdict and this decrement
3314/// agree on the same regularized operator. Returns `None` when the shapes are
3315/// malformed, an entry is non-finite, the shifted factor is not PD, or the
3316/// resulting quadratic form is negative (which a PD factor rules out; retained
3317/// as a roundoff guard).
3318pub(crate) fn newton_predicted_decrease(hessian: &Array2<f64>, grad: &Array1<f64>) -> Option<f64> {
3319    let n = hessian.nrows();
3320    if n == 0 || hessian.ncols() != n || grad.len() != n {
3321        return None;
3322    }
3323    if hessian.iter().any(|v| !v.is_finite()) || grad.iter().any(|v| !v.is_finite()) {
3324        return None;
3325    }
3326    let max_diag = (0..n).fold(0.0_f64, |acc, j| acc.max(hessian[[j, j]].abs()));
3327    let shift = f64::EPSILON.sqrt() * max_diag.max(1.0);
3328    // Lower Cholesky factor L of H + shift·I (same regularization the PSD probe
3329    // uses), computed in place.
3330    let mut l = hessian.clone();
3331    for j in 0..n {
3332        l[[j, j]] += shift;
3333    }
3334    for j in 0..n {
3335        for k in 0..j {
3336            let l_jk = l[[j, k]];
3337            for i in j..n {
3338                l[[i, j]] -= l[[i, k]] * l_jk;
3339            }
3340        }
3341        let pivot = l[[j, j]];
3342        if !(pivot > 0.0) || !pivot.is_finite() {
3343            return None;
3344        }
3345        let inv_sqrt = 1.0 / pivot.sqrt();
3346        for i in j..n {
3347            l[[i, j]] *= inv_sqrt;
3348        }
3349    }
3350    // Solve (L Lᵀ) d = g for d = H_s⁻¹ g: forward-substitute L y = g, then
3351    // back-substitute Lᵀ d = y.
3352    let mut y = grad.clone();
3353    for j in 0..n {
3354        let mut s = y[j];
3355        for k in 0..j {
3356            s -= l[[j, k]] * y[k];
3357        }
3358        y[j] = s / l[[j, j]];
3359    }
3360    let mut d = y;
3361    for j in (0..n).rev() {
3362        let mut s = d[j];
3363        for k in (j + 1)..n {
3364            s -= l[[k, j]] * d[k];
3365        }
3366        d[j] = s / l[[j, j]];
3367    }
3368    let quad = grad.dot(&d); // gᵀ H_s⁻¹ g ≥ 0 for a PD factor.
3369    if !quad.is_finite() || quad < 0.0 {
3370        return None;
3371    }
3372    Some(0.5 * quad)
3373}
3374
3375/// Is outer coordinate `k` pinned within [`coordinate_rail_margin`] of either
3376/// of its own box bounds?
3377///
3378/// Factored out so the λ-block REPORT and the θ-wide certificate FACE below
3379/// cannot drift apart about what "railed" means for the same coordinate: they
3380/// differ only in which coordinates they scan, never in the test.
3381///
3382/// The margin is the shared width-capped one, so this is the exact-bound test on
3383/// [`rail_relaxed_bounds`]' relaxed endpoints. It used to be a flat
3384/// [`CERTIFICATE_RAIL_MARGIN`] while the residual projector capped the same
3385/// constant at a quarter-width, and a box narrower than `2 ×
3386/// CERTIFICATE_RAIL_MARGIN` — the raw-κ chart window on any standardised
3387/// feature set — was then covered end to end by its own two margin bands: every
3388/// κ read railed, flat κ = 0 included, and the certificate's reduced Hessian
3389/// lost every row it was supposed to judge (#2462).
3390///
3391/// Comparing against the relaxed endpoints rather than `|θ_k − bound|` also
3392/// keeps an infeasible coordinate railed. Under the absolute-value form a point
3393/// *outside* the box by more than the margin reported interior, which is the one
3394/// reading that can never be right.
3395fn outer_coordinate_is_railed(theta: &Array1<f64>, k: usize, config: &OuterConfig) -> bool {
3396    RailTest::evaluate(theta, k, config).is_railed()
3397}
3398
3399/// One coordinate's rail test: the verdict together with the interval and the
3400/// margin it was decided against (#2465).
3401///
3402/// The predicate above computed `(lo, hi)`, derived a margin from them, compared
3403/// against the relaxed endpoints, and returned a bare `bool` — so `railed=[3]`
3404/// reached the reader with everything that produced it already destroyed, and
3405/// recovering the interval on #2462 took a thirteen-point seeding sweep.
3406///
3407/// #2462 made carrying it necessary rather than merely useful: the margin is now
3408/// [`coordinate_rail_margin`], **width-capped per coordinate**, so two
3409/// coordinates in the same fit can be judged railed against different margins.
3410/// `railed=[1, 3]` is no longer even one statement, and the flag alone cannot
3411/// say which band either coordinate met.
3412#[derive(Debug, Clone, Copy)]
3413pub(crate) struct RailTest {
3414    /// Index into the θ vector.
3415    pub(crate) index: usize,
3416    /// The coordinate's value at the judged point.
3417    pub(crate) theta: f64,
3418    /// The interval it was tested against. `None` means the configured box does
3419    /// not cover this coordinate at all — which is itself the reason the verdict
3420    /// is `false`, a distinction the bare bool erased.
3421    pub(crate) box_bounds: Option<(f64, f64)>,
3422    /// The width-capped margin in force for THIS coordinate, from
3423    /// [`coordinate_rail_margin`]. Zero when the box does not cover it.
3424    pub(crate) margin: f64,
3425}
3426
3427impl RailTest {
3428    fn evaluate(theta: &Array1<f64>, k: usize, config: &OuterConfig) -> Self {
3429        let box_bounds = match config.model_domain_bounds.as_ref() {
3430            Some((lo, hi)) if k < lo.len() && k < hi.len() => Some((lo[k], hi[k])),
3431            Some(_) => None,
3432            None => Some((-config.rho_bound, config.rho_bound)),
3433        };
3434        Self {
3435            // Indexed, not `get`-ed: every caller scans an index range derived
3436            // from `theta.len()`, so an out-of-range k is a caller bug and the
3437            // predicate this replaced panicked on it. Softening that to a silent
3438            // `false` would turn a bug into a coordinate quietly reported
3439            // un-railed.
3440            theta: theta[k],
3441            index: k,
3442            margin: box_bounds.map_or(0.0, |(lo, hi)| coordinate_rail_margin(lo, hi)),
3443            box_bounds,
3444        }
3445    }
3446
3447    /// Pinned at or past either relaxed endpoint. This is the ONLY definition of
3448    /// railed in this file; both the λ-block report and the θ-wide certificate
3449    /// face route through it, and the relaxed-endpoint form (rather than
3450    /// `|θ_k − bound|`) is what keeps an infeasible coordinate railed (#2462).
3451    pub(crate) fn is_railed(self) -> bool {
3452        match self.box_bounds {
3453            Some((lo, hi)) => self.theta <= lo + self.margin || self.theta >= hi - self.margin,
3454            None => false,
3455        }
3456    }
3457}
3458
3459impl std::fmt::Display for RailTest {
3460    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3461        match self.box_bounds {
3462            Some((lo, hi)) => write!(
3463                f,
3464                "#{} theta={:.6e} box=[{:.6e}, {:.6e}] margin={:.3e} railed_at=(<={:.6e} or >={:.6e})",
3465                self.index,
3466                self.theta,
3467                lo,
3468                hi,
3469                self.margin,
3470                lo + self.margin,
3471                hi - self.margin,
3472            ),
3473            None => write!(
3474                f,
3475                "#{} theta={:.6e} box=NOT-COVERED-BY-CONFIGURED-BOUNDS",
3476                self.index, self.theta,
3477            ),
3478        }
3479    }
3480}
3481
3482/// The certificate-facing facts for `indices`: the interval and margin each
3483/// railed coordinate was judged against (#2530).
3484///
3485/// Built from [`RailTest`], which is the single definition of railed, so the
3486/// certificate reports what the predicate actually decided rather than a second
3487/// derivation of it. A coordinate the configured box does not cover contributes
3488/// nothing: it was not judged against an interval, so there is no interval to
3489/// report.
3490pub(crate) fn railed_coordinate_facts(
3491    theta: &Array1<f64>,
3492    indices: &[usize],
3493    config: &OuterConfig,
3494) -> Vec<RailedCoordinateFact> {
3495    indices
3496        .iter()
3497        .filter_map(|&k| {
3498            let test = RailTest::evaluate(theta, k, config);
3499            test.box_bounds.map(|(lower, upper)| RailedCoordinateFact {
3500                index: test.index,
3501                theta: test.theta,
3502                lower,
3503                upper,
3504                margin: test.margin,
3505            })
3506        })
3507        .collect()
3508}
3509
3510/// Render the rail tests for `indices`, so a refusal naming railed coordinates
3511/// also states the interval and margin each was judged against (#2465).
3512pub(crate) fn rail_test_summary(
3513    theta: &Array1<f64>,
3514    indices: &[usize],
3515    config: &OuterConfig,
3516) -> String {
3517    indices
3518        .iter()
3519        .map(|&k| RailTest::evaluate(theta, k, config).to_string())
3520        .collect::<Vec<_>>()
3521        .join(", ")
3522}
3523
3524/// Smoothing coordinates (leading ρ block) railed against the outer box.
3525///
3526/// This is the **report**. `OuterCriterionCertificate::lambdas_railed` indexes
3527/// *smoothing parameters*, and every consumer reads it that way — gam-report's
3528/// "λ railed" warning, the pyffi certificate surface, `is_clean`. It is
3529/// deliberately NOT the set the certificate reasons with; see
3530/// [`certificate_railed_coordinates`].
3531pub(crate) fn certificate_railed_lambdas(
3532    rho: &Array1<f64>,
3533    rho_dim: usize,
3534    config: &OuterConfig,
3535) -> Vec<usize> {
3536    (0..rho_dim.min(rho.len()))
3537        .filter(|&k| outer_coordinate_is_railed(rho, k, config))
3538        .collect()
3539}
3540
3541/// **Every** outer coordinate railed against its own box bound — the ρ block
3542/// *and* the trailing non-ρ blocks that a joint search carries in the same θ
3543/// vector under the same box: the spatial log-κ ψ coordinates and the
3544/// auxiliary coordinates.
3545///
3546/// This is the set every *decision* in [`certify_outer_optimality`] must use,
3547/// and using the λ-only report there instead was a real defect. The active-set
3548/// reasoning at a box-constrained optimum is a statement about coordinates, not
3549/// about what a coordinate happens to parameterize:
3550///
3551/// * the second-order condition only has to hold on the **feasible tangent
3552///   subspace**, so `certificate_hessian_is_psd_off_railed` deletes the railed
3553///   rows and columns. `layout.rho_dim()` is `n_params − psi_dim`, so a ψ
3554///   coordinate pinned on its data-derived κ window stayed *inside* that
3555///   sub-block and its saturated (and, near a window edge, routinely negative)
3556///   curvature row decided `hessian_psd = false` for the whole fit. That is
3557///   precisely the failure the block's own comment says must not happen — "a
3558///   rail-caused indefiniteness would disable the very certificate that exists
3559///   to certify a railed optimum (#2299)" — it was simply never extended past
3560///   the ρ block;
3561/// * the same omission hid the coordinate from the asymptote-rail mint
3562///   (#2348 Inc 1), from tail-snap, from `interior_curvature_floor_clearance`,
3563///   from the #2155 saddle escape, and from the #2392 wrong-rail pull-back and
3564///   active-set reduction. A κ search whose rail is a ψ coordinate could
3565///   therefore never be certified *by construction*, no matter how correct its
3566///   gradient was;
3567/// * and it made the refusal message actively misleading, because
3568///   `project_gradient_vector` DOES project every coordinate against its own
3569///   bound. So `|g|` could collapse to a small `|Pg|` while `railed=[]`
3570///   reported nothing responsible for the collapse — the exact disagreement
3571///   #979 measured from the other side and could not explain.
3572///
3573/// Relaxing nothing: a coordinate near a bound whose gradient still points
3574/// *into* the box keeps its feasible-descent component in `|Pg|`
3575/// ([`project_gradient_vector`] zeros only the outward half), so a genuinely
3576/// unconverged interior direction is still refused.
3577pub(crate) fn certificate_railed_coordinates(
3578    theta: &Array1<f64>,
3579    config: &OuterConfig,
3580) -> Vec<usize> {
3581    (0..theta.len())
3582        .filter(|&k| outer_coordinate_is_railed(theta, k, config))
3583        .collect()
3584}
3585
3586/// Which term of the stationarity bound's `max` chain actually set it.
3587///
3588/// `certify_outer_optimality` does not compute *a* bound; it takes the maximum of
3589/// up to five independently-derived quantities, and until #2458 nothing recorded
3590/// which one won. That is why a `bound=1.000e0` and a `bound=5.68e-6` came out of
3591/// the same message shape and telling them apart required reading this file --
3592/// the emitted number is a DERIVATIVE of a construction-site value, computed
3593/// later, so grepping the construction sites cannot match an observed bound.
3594///
3595/// The rungs are not interchangeable calibrations of one quantity. Only
3596/// [`Self::CurvatureResolvability`] is derived from what a gradient of that size
3597/// does to the criterion: `|Pg|·√(τ/Δpred)` simplifies to `√(2·h·τ)` -- the `|Pg|`
3598/// cancels -- i.e. the gradient magnitude below which the criterion cannot resolve
3599/// descent at all. The others are gradient-magnitude tests, blind to how curvature
3600/// maps a gradient to an objective change, which is exactly what this file's own
3601/// comment at the curvature block already says. Recording the rung is the
3602/// prerequisite for holding every route to the derived one: it makes "which
3603/// machinery did this route happen to have" an observable rather than an
3604/// inference.
3605#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3606pub(crate) enum StationarityBoundSource {
3607    /// The band the ENGINE declared before it saw the judged point:
3608    /// `outer_engine_gradient_band(config)` -- the arithmetic floor
3609    /// `max(tolerance, scale·√ε)`, widened by the DECLARED-scale rung
3610    /// `τ·(1 + |scale|)`. A function of the declared problem and nothing else.
3611    SolverBand,
3612    /// The CERTIFICATE's point-anchored widening `τ·(1 + |cost_at_point|)`,
3613    /// reported when it strictly exceeds [`Self::SolverBand`] (#2688). A
3614    /// different anchor and therefore a different quantity: the engine band is
3615    /// sealed at run start, this one moves with wherever the search stopped
3616    /// (#2613). Both were `solver-band` until #2688, so a `N× over bound` ratio
3617    /// could not be read as a ratio against a resolution standard.
3618    CertificateScoreRelative,
3619    /// The cost-stall guard's measured probe-noise floor `σ̂/Δ` (#2241). Diverges
3620    /// as the step collapses, which is the regime it fires in.
3621    ProbeNoiseFloor,
3622    /// `|Pg|·√(τ/Δpred)` = `√(2·h·τ)` (#2253/#2249/#2015/#2091) -- the only rung
3623    /// with a derivation from the criterion's own resolution.
3624    CurvatureResolvability,
3625    /// Twice the same-ρ spread between the run-recorded and certificate-time
3626    /// gradients (#2299): the measuring instrument's demonstrated noise.
3627    GradientReproducibility,
3628    /// `config.tolerance` judged against the EFS/fixed-point route's
3629    /// normalized residual `‖(θ⁺−θ)/scale‖_∞` -- not against a gradient norm
3630    /// at all. The route has no ladder: it exposes no analytic gradient, so
3631    /// none of the gradient-magnitude rungs above is even computable on it,
3632    /// and the certificate it mints (`OuterStationarityCertificate::FixedPoint`)
3633    /// already declares `bound: config.tolerance`. Recording it names the one
3634    /// thing the shared `bound=` field could not previously say -- that the
3635    /// number is a residual tolerance and the quantity beside it is a residual.
3636    ///
3637    /// Only the ONE refusal on that route which has actually formed the
3638    /// residual carries this. Its eight early exits formed none, and now say so.
3639    FixedPointResidual,
3640    /// The CALLER's `|Pg|` requirement (#2568), capping every rung above.
3641    ///
3642    /// The only member of this enum that is not the engine's own judgement, and
3643    /// the only one that ever TIGHTENS the bound. Reported when the caller's
3644    /// requirement is stricter than the ladder the engine would have applied, so
3645    /// a reader can tell "the engine refused this" from "the engine would have
3646    /// certified this and the caller would not" -- a distinction that matters
3647    /// because the second is not a defect in the fit.
3648    CallerRequirement,
3649}
3650
3651impl StationarityBoundSource {
3652    pub(crate) fn label(self) -> &'static str {
3653        match self {
3654            Self::SolverBand => "solver-band",
3655            Self::CertificateScoreRelative => "certificate-score-relative",
3656            Self::ProbeNoiseFloor => "probe-noise-floor",
3657            Self::CurvatureResolvability => "curvature-resolvability",
3658            Self::GradientReproducibility => "gradient-reproducibility",
3659            Self::FixedPointResidual => "fixed-point-residual",
3660            Self::CallerRequirement => "caller-requirement",
3661        }
3662    }
3663
3664    /// Whether this rung is the derived resolvability standard rather than a
3665    /// gradient-magnitude substitute adopted where that standard was unavailable.
3666    pub(crate) fn is_derived_standard(self) -> bool {
3667        // The standard is the decrement test, and #2458's whole point is that
3668        // WHICH derivative machinery produced the curvature must stop deciding
3669        // which standard a fit is held to. The answer is that every route
3670        // supplies exact curvature or none: there is no second, approximate
3671        // curvature rung, because a bound estimated by the code doing the
3672        // judging is not the same evidence and pretending otherwise is what
3673        // made the tiering invisible in the first place.
3674        matches!(self, Self::CurvatureResolvability)
3675    }
3676
3677    /// The neutral projection carried on the refusal, so a red states its own
3678    /// rung instead of leaving it to be inferred from the numbers.
3679    pub(crate) fn provenance(self) -> StationarityRung {
3680        StationarityRung {
3681            label: self.label(),
3682            derived_standard: self.is_derived_standard(),
3683        }
3684    }
3685}
3686
3687/// A stationarity bound bundled with the rung that produced it (#2458).
3688///
3689/// The bound and its provenance travelled separately before this: the bound was
3690/// a bare `f64` argument and the rung stayed a local in the certificate block,
3691/// so every refusal outside that block reported a number with no way to say
3692/// which standard it came from. Bundling them makes it impossible to pass one
3693/// without deciding the other.
3694///
3695/// The source is NOT optional, and there is no constructor for "a number with
3696/// no standard". An earlier revision carried an `unrecorded` escape that 22 of
3697/// the 30 refusal paths took; naming those 22 was the first repair, and it was
3698/// half right. Twenty of them do not have an unclassified bound — they have NO
3699/// bound. They refuse before any stationarity residual exists (a failed
3700/// terminal evaluation, a malformed gradient, a non-converged inner state), and
3701/// the configured constant they used to report was never weighed against the
3702/// point. Those now carry [`StationarityStandard::NoComparison`] and print no
3703/// bound at all; only a route that formed a residual constructs one of these.
3704#[derive(Debug, Clone, Copy)]
3705pub(crate) struct StationarityBound {
3706    value: f64,
3707    source: StationarityBoundSource,
3708}
3709
3710impl StationarityBound {
3711    /// A bound the rung ladder produced, carrying the rung it came from.
3712    pub(crate) fn from_ladder(value: f64, source: StationarityBoundSource) -> Self {
3713        Self { value, source }
3714    }
3715
3716    /// The EFS/fixed-point route's standard: `config.tolerance` against a
3717    /// normalized fixed-point residual
3718    /// ([`StationarityBoundSource::FixedPointResidual`]).
3719    ///
3720    /// This is the bound `certify_fixed_point_optimality` mints its
3721    /// `OuterStationarityCertificate::FixedPoint` against, so the refusal that
3722    /// weighs a formed residual reports the route's own declared standard
3723    /// rather than a gradient tolerance it never used.
3724    pub(crate) fn fixed_point_residual(config: &OuterConfig) -> Self {
3725        Self::from_ladder(
3726            config.tolerance,
3727            StationarityBoundSource::FixedPointResidual,
3728        )
3729    }
3730
3731    pub(crate) fn value(self) -> f64 {
3732        self.value
3733    }
3734
3735    /// Whether this value was derived by mapping the caller's residual through
3736    /// the caller's Hessian. Such a value cannot cross into a reduced face:
3737    /// the face must mint its own bound from its own Hessian and residual.
3738    pub(crate) fn requires_face_local_derivation(self) -> bool {
3739        self.source.is_derived_standard()
3740    }
3741
3742    pub(crate) fn rung(self) -> StationarityRung {
3743        self.source.provenance()
3744    }
3745}
3746
3747impl From<StationarityBound> for StationarityStandard {
3748    fn from(bound: StationarityBound) -> Self {
3749        Self::Measured {
3750            bound: bound.value(),
3751            rung: bound.rung(),
3752        }
3753    }
3754}
3755
3756fn outer_nonconvergence_error(
3757    context: &str,
3758    reason: &str,
3759    result: &OuterResult,
3760    projected_grad_norm: Option<f64>,
3761    stationarity_standard: impl Into<StationarityStandard>,
3762) -> EstimationError {
3763    // Solver provenance, appended to every outer non-convergence.
3764    //
3765    // The certificate answers "is this point stationary" — and when it is not,
3766    // the next question is always "then why did the search STOP here", which
3767    // the message did not answer. A binomial/logit P-spline (#1575/#1561)
3768    // refuses with `|Pg|=7.5e-1` against a `1.0e-2` bound, PSD Hessian, nothing
3769    // railed, all coordinates 18 e-folds inside the box, "after 7 outer
3770    // iteration(s)" — and the 7 is the whole mystery. `converged` distinguishes
3771    // a solver that CLAIMED convergence (a tolerance desync, and the only case
3772    // the #2273/#2374 resume will retry) from one that ran out of budget;
3773    // `operator_stop_reason` separates a trust-region reject floor from a
3774    // flat-valley cost stall from an iteration budget. All three are already on
3775    // the result and cost nothing to print.
3776    let reason = format!(
3777        "{reason}; solver provenance: origin={:?}, plan={}, claimed_converged={}{}{}{}",
3778        // #2465 at a fifth site. `termination=<no opt solver produced this
3779        // result>` says an `opt` solver did not decide this, and stops there;
3780        // `origin` says WHICH lane did. Both are already on the result.
3781        result.origin,
3782        result.plan_used,
3783        result.solver_claimed_convergence(),
3784        // #2547: `stop_reason` is the coarse projection. `termination` is
3785        // the test the solver actually applied plus the quantity it was
3786        // judged against, so a reader gets "the L-infinity window fired at
3787        // a threshold of 8.9e-1" rather than "it stopped". A stop that
3788        // made no stationarity claim at all (an iteration budget, a
3789        // collapsed trust region) says so, instead of leaving that to be
3790        // inferred from a bare gradient norm nothing compared it to.
3791        result
3792            .solver_termination
3793            .map(|t| {
3794                let evidence = match t.stationarity_evidence() {
3795                    Some(e) => format!(
3796                        " [measured={:.6e} vs threshold={:.6e}, {:?}, {:?}]",
3797                        e.measured, e.threshold, e.norm, e.scaling
3798                    ),
3799                    None => " [made no stationarity claim]".to_string(),
3800                };
3801                format!(", termination={t}{evidence}")
3802            })
3803            .unwrap_or_else(|| ", termination=<no opt solver produced this result>".to_string()),
3804        result
3805            .operator_stop_reason
3806            .map(|stop| format!(", stop_reason={stop:?}"))
3807            .unwrap_or_default(),
3808        result
3809            .converged_via()
3810            .map(|via| format!(", converged_via={via:?}"))
3811            .unwrap_or_default(),
3812    );
3813    // #2465: the line search's own verdict, when one failed. `StepSizeTooSmall`
3814    // and `MaxAttempts` are different defects with different repairs, and
3815    // "line_search_failed" alone distinguishes neither.
3816    // The cost-stall guard's measured probe-noise floor, when it published one.
3817    // A stall halted against a noise bound and a stall halted against the
3818    // score-relative flat band are different verdicts, and only the first
3819    // carries this.
3820    let reason = match result.cost_stall_probe_scale {
3821        Some((noise_floor, probe_radius)) => format!(
3822            "{reason}, cost_stall_window=[noise_floor={noise_floor:.6e}, \
3823             probe_radius={probe_radius:.6e}, ratio={:.6e}]",
3824            noise_floor / probe_radius
3825        ),
3826        None => reason,
3827    };
3828    let reason = match result.flat_noise_grad_bound {
3829        Some(bound) => format!("{reason}, flat_noise_grad_bound={bound:.6e}"),
3830        None => reason,
3831    };
3832    let reason = match result.line_search_failure {
3833        Some((failure_reason, max_attempts)) => format!(
3834            "{reason}, line_search={failure_reason:?} after {max_attempts} attempt(s) \
3835             [StepSizeTooSmall = the direction descended but no step improved the \
3836             objective; MaxAttempts = the bracket never closed]"
3837        ),
3838        None => reason,
3839    };
3840    EstimationError::RemlDidNotConverge {
3841        context: context.to_string(),
3842        reason,
3843        iterations: result.iterations,
3844        final_value: result.final_value,
3845        projected_grad_norm,
3846        stationarity_standard: stationarity_standard.into(),
3847        rho_checkpoint: result.rho.to_vec(),
3848    }
3849}
3850
3851/// Roundoff envelope for two independently assembled values of the same outer
3852/// criterion at the same point.
3853///
3854/// Value-only and derivative-bearing evaluation lanes may use different
3855/// kernels and reduction trees, so bitwise identity is not a valid contract.
3856/// Their relative disagreement must nevertheless stay below the square root of
3857/// machine epsilon: beyond that scale the derivative sample is not evidence
3858/// about the scalar objective the optimizer's value lane ranks.
3859pub fn outer_value_agreement_bound(value_only: f64, derivative_sample: f64) -> f64 {
3860    f64::EPSILON.sqrt() * value_only.abs().max(derivative_sample.abs()).max(1.0)
3861}
3862
3863/// `lane_inner_convergence` is `(value_lane, derivative_lane)`, each captured
3864/// IMMEDIATELY after its own evaluation — `inner_solve_converged` reads one
3865/// shared snapshot, so a flag sampled after both lanes describes only the
3866/// second (#2228). `None` means the caller did not sample that lane.
3867fn audit_outer_value_agreement(
3868    context: &str,
3869    value_only: f64,
3870    derivative_sample: f64,
3871    result: &mut OuterResult,
3872    projected_grad_norm: Option<f64>,
3873    stationarity_standard: impl Into<StationarityStandard>,
3874    lane_inner_convergence: (Option<bool>, Option<bool>),
3875) -> Result<(), EstimationError> {
3876    let bound = outer_value_agreement_bound(value_only, derivative_sample);
3877    let disagreement = (value_only - derivative_sample).abs();
3878    if disagreement <= bound {
3879        return Ok(());
3880    }
3881    // Which lane's inner solve converged decides whether this is roundoff
3882    // between two reduction trees (the bound's premise) or a warm-start basin
3883    // gap the bound was never derived for.
3884    let lane_note = |flag: Option<bool>| match flag {
3885        Some(true) => "converged",
3886        Some(false) => "NOT-CONVERGED",
3887        None => "unsampled",
3888    };
3889    let inner_evidence = format!(
3890        ", inner solve: value-lane={}, derivative-lane={}",
3891        lane_note(lane_inner_convergence.0),
3892        lane_note(lane_inner_convergence.1),
3893    );
3894
3895    // The value-only lane is the scalar criterion authority. Preserve it on
3896    // the resumable checkpoint rather than the derivative lane's inconsistent
3897    // scalar; no certificate may be attached to this mixed evidence.
3898    result.final_value = value_only;
3899    Err(outer_nonconvergence_error(
3900        context,
3901        &format!(
3902            "cost-only value disagrees with analytic-sample value at the same outer point: \
3903             value-only={value_only:.16e}, analytic-sample={derivative_sample:.16e}, \
3904             disagreement={disagreement:.3e}, roundoff bound={bound:.3e}{inner_evidence}"
3905        ),
3906        result,
3907        projected_grad_norm,
3908        stationarity_standard,
3909    ))
3910}
3911
3912fn certify_fixed_point_optimality(
3913    obj: &mut dyn OuterObjective,
3914    config: &OuterConfig,
3915    context: &str,
3916    result: &mut OuterResult,
3917    fidelity: CertificationFidelity,
3918) -> Result<OuterCriterionCertificate, EstimationError> {
3919    let layout = obj.capability().theta_layout();
3920    // A fixed-point residual is only a certificate for the scalar objective
3921    // when both lanes price that same objective at the same rho. Sample the
3922    // authoritative value lane first; the analytic fixed-point evaluator then
3923    // remains the installed terminal-state owner.
3924    //
3925    // MINT ONLY (#2359), for the same reason as on the analytic path: whether
3926    // the two lanes agree is terminal proof work, not a ranking signal, and on
3927    // this route it is the ONLY thing that distinguishes screening from mint —
3928    // there is no order-four ladder to reserve here.
3929    let value_only = match fidelity {
3930        CertificationFidelity::Screening => None,
3931        CertificationFidelity::Mint => {
3932            let sample = obj
3933                .eval_with_order(&result.rho, OuterEvalOrder::Value)
3934                .map_err(|err| {
3935                    outer_nonconvergence_error(
3936                        context,
3937                        &format!("terminal value-only certificate evaluation failed: {err}"),
3938                        result,
3939                        None,
3940                        StationarityStandard::NoComparison,
3941                    )
3942                })?
3943                .cost;
3944            if !sample.is_finite() {
3945                return Err(outer_nonconvergence_error(
3946                    context,
3947                    "terminal value-only certificate evaluation returned a non-finite objective value",
3948                    result,
3949                    None,
3950                    StationarityStandard::NoComparison,
3951                ));
3952            }
3953            Some(sample)
3954        }
3955    };
3956    // Sampled HERE, not after the certificate lane below: `inner_solve_converged`
3957    // returns one shared snapshot describing the most recent inner solve, so a
3958    // read taken after both lanes cannot speak for this one (#2228). Mirrors the
3959    // capture-immediately discipline already used for `terminal_inner_converged`.
3960    let value_lane_inner_converged =
3961        value_only.map(|_| inner_solve_converged(config.outer_inner_cap.as_ref()));
3962    // #2228: these two lanes run back-to-back on ONE `&mut obj` with no reset
3963    // between them. Criteria that fit `(t, β)` in place therefore warm-start
3964    // this certificate lane from wherever the value-only lane above stopped —
3965    // the lanes are not two evaluations of one function, the second is a
3966    // continuation of the first.
3967    //
3968    // That is the hazard `OuterObjective::owns_terminal_coefficient_mode`
3969    // already documents and measures ("disagree by a whole basin (measured:
3970    // 9.1931e2 vs 9.1671e2 on the cause-specific survival gate)"), but its
3971    // `obj.reset()` remedy fires once BEFORE `finalize_outer_result`, not
3972    // between the pair here.
3973    //
3974    // It matters because `outer_value_agreement_bound` — applied to exactly
3975    // these two scalars below — is a sqrt(EPSILON) ROUNDOFF envelope, derived
3976    // for "different kernels and reduction trees". A warm-start basin gap is
3977    // not roundoff.
3978    //
3979    // MEASURED CORRECTION (2026-07-31). I originally wrote here that the
3980    // audit's 1.5x..1.1e7x spread "has the shape of a divergence that usually
3981    // lands in the same basin and occasionally does not". A direct test of the
3982    // mild end refutes that for at least that end. On
3983    // `pure_duchon_aniso_fit_optimizes_without_introducing_hybrid_scale`:
3984    //
3985    //   value-only=-3.6708914555093685e1  analytic-sample=-3.6708920839323071e1
3986    //   disagreement=6.284e-6  bound=5.470e-7
3987    //   inner solve: value-lane=converged, derivative-lane=converged
3988    //
3989    // BOTH lanes converged. If the certificate lane warm-starts from the value
3990    // lane's converged point and itself converges, it should stay there and the
3991    // scalars should agree — so chaining does not explain this one, and the
3992    // spread is probably NOT a single mechanism. At |value| ~ 36.7 the bound is
3993    // sqrt(EPSILON)*36.7 = 5.47e-7 and the gap is ~11.5x it, i.e. the two cost
3994    // routes differ by ~1.7e-7 RELATIVE: an order above roundoff, many orders
3995    // below a basin gap.
3996    //
3997    // The live question for this end is therefore not state chaining but
3998    // whether sqrt(EPSILON) can cover two lanes that evaluate at different
3999    // ORDERS (`Value` vs `ValueAndGradient`/`VGH`) and hence through different
4000    // kernels — the same "machine constant standing in for a measured quantity"
4001    // shape as #2614's requested resolution. To test the OTHER end, capture the
4002    // same flags on a high-end (1.1e7x) firing; the instrumentation is on both
4003    // audit sites now, so it costs one run.
4004    //
4005    // Adding a reset here is NOT a safe drive-by: the same trait doc states
4006    // that a `false` (default) objective "retains the very state its evaluation
4007    // at result.rho depends on and must not be reset". Any fix has to reset
4008    // before BOTH lanes, under the `owns_terminal_coefficient_mode` guard, so
4009    // the pair shares one baseline instead of chaining.
4010    let evaluation = obj
4011        .eval_fixed_point_certificate(&result.rho)
4012        .map_err(|err| {
4013            outer_nonconvergence_error(
4014                context,
4015                &format!("analytic fixed-point certificate evaluation failed: {err}"),
4016                result,
4017                None,
4018                StationarityStandard::NoComparison,
4019            )
4020        })?;
4021    let certificate_lane_inner_converged = inner_solve_converged(config.outer_inner_cap.as_ref());
4022    if !certificate_lane_inner_converged {
4023        return Err(outer_nonconvergence_error(
4024            context,
4025            "terminal fixed-point evidence was evaluated at a non-converged inner state",
4026            result,
4027            None,
4028            StationarityStandard::NoComparison,
4029        ));
4030    }
4031    if evaluation.coordinates.len() != layout.n_params {
4032        return Err(outer_nonconvergence_error(
4033            context,
4034            &format!(
4035                "fixed-point certificate returned {} coordinates for an outer problem of dimension {}",
4036                evaluation.coordinates.len(),
4037                layout.n_params
4038            ),
4039            result,
4040            None,
4041            StationarityStandard::NoComparison,
4042        ));
4043    }
4044    if !evaluation.cost.is_finite() {
4045        return Err(outer_nonconvergence_error(
4046            context,
4047            "fixed-point certificate returned a non-finite objective value",
4048            result,
4049            None,
4050            StationarityStandard::NoComparison,
4051        ));
4052    }
4053    if let Some(value_only) = value_only {
4054        audit_outer_value_agreement(
4055            context,
4056            value_only,
4057            evaluation.cost,
4058            result,
4059            None,
4060            StationarityStandard::NoComparison,
4061            (
4062                value_lane_inner_converged,
4063                Some(certificate_lane_inner_converged),
4064            ),
4065        )?;
4066    }
4067
4068    let mut normalized_updates = Vec::with_capacity(layout.n_params);
4069    let mut uncovered = Vec::new();
4070    for (index, coordinate) in evaluation.coordinates.iter().enumerate() {
4071        match coordinate {
4072            FixedPointCoordinateCertificate::Covered { update, scale }
4073                if update.is_finite() && scale.is_finite() && *scale > 0.0 =>
4074            {
4075                normalized_updates.push(*update / *scale);
4076            }
4077            FixedPointCoordinateCertificate::Covered { update, scale } => {
4078                uncovered.push(format!(
4079                    "coordinate {index} has invalid covered residual update={update} scale={scale}"
4080                ));
4081                normalized_updates.push(f64::NAN);
4082            }
4083            FixedPointCoordinateCertificate::Uncovered { reason } => {
4084                uncovered.push(format!("coordinate {index}: {reason}"));
4085                normalized_updates.push(f64::NAN);
4086            }
4087        }
4088    }
4089    if !uncovered.is_empty() {
4090        return Err(outer_nonconvergence_error(
4091            context,
4092            &format!(
4093                "fixed-point certificate lacks root-equivalent analytic coverage: {}",
4094                uncovered.join("; ")
4095            ),
4096            result,
4097            None,
4098            StationarityStandard::NoComparison,
4099        ));
4100    }
4101
4102    let (lower, upper) = outer_model_domain_bounds_template(config, layout.n_params);
4103    let mut raw_inf = 0.0_f64;
4104    let mut projected_inf = 0.0_f64;
4105    for index in 0..layout.n_params {
4106        let update = normalized_updates[index];
4107        raw_inf = raw_inf.max(update.abs());
4108        // `update` is a signed descent/update direction, the negative of the
4109        // gradient convention used by `projected_gradient_norm`: at a lower
4110        // bound a negative update points out of the box, and at an upper bound
4111        // a positive update does. Only those infeasible multiplier components
4112        // are removed.
4113        let projected = if result.rho[index] <= lower[index] {
4114            update.max(0.0)
4115        } else {
4116            update
4117        };
4118        let projected = if result.rho[index] >= upper[index] {
4119            projected.min(0.0)
4120        } else {
4121            projected
4122        };
4123        projected_inf = projected_inf.max(projected.abs());
4124    }
4125
4126    result.final_value = evaluation.cost;
4127    result.final_grad_norm = None;
4128    result.final_gradient = None;
4129    result.final_hessian = None;
4130
4131    let certificate = OuterCriterionCertificate {
4132        stationarity: OuterStationarityCertificate::FixedPoint {
4133            residual_inf_norm: raw_inf,
4134            projected_residual_inf_norm: projected_inf,
4135            bound: config.tolerance,
4136            rung: StationarityBoundSource::FixedPointResidual.provenance().into(),
4137            covered_coordinates: layout.n_params,
4138        },
4139        // The EFS/fixed-point route exposes no analytic Hessian, so there was
4140        // a curvature question and nothing could answer it (#2561).
4141        curvature: CurvatureEvidence::NotAvailable,
4142        lambdas_railed: certificate_railed_lambdas(&result.rho, layout.rho_dim(), config),
4143        railed_facts: railed_coordinate_facts(
4144            &result.rho,
4145            // #2624: the EVIDENCE field, unlike `lambdas_railed` above, is not
4146            // lambda-scoped -- it states the interval and margin each
4147            // coordinate was judged against, and the judgement is taken on the
4148            // theta-wide face. A psi/log-kappa coordinate pinned on its own
4149            // window was structurally absent from it.
4150            &certificate_railed_coordinates(&result.rho, config),
4151            config,
4152        ),
4153        curvature_floor: None,
4154    };
4155    result.criterion_certificate = Some(certificate.clone());
4156    if !certificate.certifies() {
4157        return Err(outer_nonconvergence_error(
4158            context,
4159            &certificate.summary(),
4160            result,
4161            Some(projected_inf),
4162            StationarityBound::fixed_point_residual(config),
4163        ));
4164    }
4165
4166    let via = match result.termination.proposed_via() {
4167        Some(via @ OuterConvergedVia::RecurrentIncumbent { .. }) => via,
4168        _ => OuterConvergedVia::FixedPointStationary {
4169            projected_residual_inf_norm: projected_inf,
4170            certificate_bound: config.tolerance,
4171        },
4172    };
4173    result.termination.certify(via);
4174    log::info!("[CERTIFICATE] {context}: {}", certificate.summary());
4175    Ok(certificate)
4176}
4177
4178/// Build the mandatory analytic optimality certificate at the returned point.
4179///
4180/// The objective is evaluated at the selected point through both its
4181/// authoritative value-only lane and its analytic derivative lane. The two
4182/// values must agree within their roundoff envelope before the derivative
4183/// sample can certify the scalar objective. Missing, malformed, non-finite, or
4184/// split-objective evidence is non-convergence: an optimizer status bit cannot
4185/// substitute for a stationarity certificate. Exact analytic curvature is
4186/// checked when the objective declares it and can materialize it; BFGS/EFS
4187/// solver geometry is never mistaken for objective curvature.
4188/// Which derivative fidelity a certification pass is allowed to spend.
4189///
4190/// #2359: the generic REML/LAML Hessian consumes the row-family derivative
4191/// ladder through order FOUR while its analytic gradient stops at order three,
4192/// so order four is a mint-time cost, not a per-candidate one. A multi-start
4193/// screens every seed it starts — that screening is a first-order gate
4194/// (stationarity, KKT projection, rail facts), and `curvature_admissible()`
4195/// reads `hessian_psd != Some(false)`, so a `None` curvature verdict certifies
4196/// on stationarity alone. The one order-four evaluation belongs to the winner,
4197/// once, and its verdict is the one that mints.
4198/// Remove the soft rho-guard BARRIER's gradient from the coordinates the KKT
4199/// projection is about to treat as sitting AT a box bound (#2545).
4200///
4201/// # The defect this closes
4202///
4203/// The REML criterion carries an unconditional `log cosh` barrier whose only job
4204/// is to keep the outer SEARCH off the `ρ → ±RHO_BOUND` walls. A `log cosh`
4205/// gradient SATURATES — `w·a·tanh(a·ρ̃) → w·a = 1.3333e-7` at `RHO_BOUND = 30`
4206/// — instead of decaying. At an upper rail [`project_gradient_vector`] keeps
4207/// exactly the positive part (`gi.max(0.0)`), and the barrier's contribution
4208/// there IS positive, so `|Pg| ≥ 1.3333e-7` at every upper rail no matter how
4209/// clean the fit: a λ=∞ face could never register as a constrained stationary
4210/// point. Measured on the #2450 fixture at ρ=30, the barrier was `1.332439e-7`
4211/// of a total `1.332521e-7` — 99.999% of the residual — with the criterion's own
4212/// `87.51·e^{−ρ}` face tail underneath it.
4213///
4214/// # Why removing it is not "judging a different function"
4215///
4216/// Only at an ACTIVE bound, and that is the whole argument. The barrier is a
4217/// numerical device, structurally separate from `configured_rho_prior_atom` (the
4218/// prior the user DECLARED), added unconditionally at `w = 1e-6`. At a
4219/// coordinate pinned to its bound the bound is enforced EXACTLY by the box
4220/// projection — the barrier's job is already done by something else — so its
4221/// surviving gradient is a KKT multiplier against a constraint the projection
4222/// already accounts for, counted twice. INTERIOR coordinates keep the full
4223/// gradient: there the optimizer descends criterion-plus-barrier and halts where
4224/// their SUM vanishes, so subtracting the barrier from an interior stationarity
4225/// test would judge a function nothing optimized and manufacture
4226/// "solver converged, certificate refused".
4227///
4228/// `guard` is the barrier gradient the objective published
4229/// ([`OuterObjective::soft_rho_guard_gradient`]) — the same atom's emission the
4230/// criterion ADDED, so the subtraction cannot drift from the addition, and it
4231/// carries the weight anchor `ρ̃ = ρ − log g(w)` that a re-derived raw-ρ closed
4232/// form would drop on every weighted fit (#877). `None`, or any shape the
4233/// coordinates do not line up with, returns the gradient untouched.
4234///
4235/// `bounds` MUST be the same box the subsequent [`project_gradient_vector`] call
4236/// uses (the rail-relaxed one at certificate time), and the active test here is
4237/// the same `>= upper` / `<= lower` test, so "railed" means one thing to the
4238/// subtraction and to the projection.
4239pub(crate) fn gradient_with_rail_barrier_removed(
4240    rho: &Array1<f64>,
4241    gradient: &Array1<f64>,
4242    bounds: &(Array1<f64>, Array1<f64>),
4243    guard: Option<&Array1<f64>>,
4244) -> Array1<f64> {
4245    let (lower, upper) = bounds;
4246    let Some(guard) = guard else {
4247        return gradient.clone();
4248    };
4249    if guard.len() != gradient.len()
4250        || rho.len() != gradient.len()
4251        || lower.len() != gradient.len()
4252        || upper.len() != gradient.len()
4253    {
4254        return gradient.clone();
4255    }
4256    Array1::from_iter((0..gradient.len()).map(|i| {
4257        if (rho[i] >= upper[i] || rho[i] <= lower[i]) && guard[i].is_finite() {
4258            gradient[i] - guard[i]
4259        } else {
4260            gradient[i]
4261        }
4262    }))
4263}
4264
4265#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4266pub(crate) enum CertificationFidelity {
4267    /// Per-candidate multi-start gate. Never spends order four.
4268    Screening,
4269    /// The single terminal mint audit. Spends order four when the objective
4270    /// declares an analytic Hessian.
4271    Mint,
4272}
4273
4274pub(crate) fn certify_outer_optimality(
4275    obj: &mut dyn OuterObjective,
4276    config: &OuterConfig,
4277    context: &str,
4278    result: &mut OuterResult,
4279) -> Result<OuterCriterionCertificate, EstimationError> {
4280    certify_outer_optimality_with_fidelity(obj, config, context, result, CertificationFidelity::Mint)
4281}
4282
4283pub(crate) fn certify_outer_optimality_with_fidelity(
4284    obj: &mut dyn OuterObjective,
4285    config: &OuterConfig,
4286    context: &str,
4287    result: &mut OuterResult,
4288    fidelity: CertificationFidelity,
4289) -> Result<OuterCriterionCertificate, EstimationError> {
4290    result.termination.begin_certification();
4291    let terminal_cap_guard = config
4292        .outer_inner_cap
4293        .as_ref()
4294        .map(FullFidelityInnerCapGuard::lift);
4295    if terminal_cap_guard.is_some() || obj.owns_terminal_coefficient_mode() {
4296        // `reset` is deliberately conditional on the presence of the cap
4297        // contract.  Those are the REML/mixture objectives whose search cache
4298        // can contain a coarse inner state; uncapped objectives retain their
4299        // ordinary stateful certification semantics.
4300        //
4301        // The `owns_terminal_coefficient_mode()` disjunct (#2334) closes the
4302        // gap for cap-less objectives that install an owned coefficient mode:
4303        // the certifying re-eval below must start from the same clean baseline
4304        // that `finalize_outer_result` used, so the mode's objective bitwise
4305        // matches the certified `final_value` even when the inner solve is
4306        // bimodal at `rho_star`.
4307        obj.reset();
4308    }
4309    let outcome =
4310        certify_outer_optimality_at_terminal_fidelity(obj, config, context, result, true, fidelity);
4311    drop(terminal_cap_guard);
4312    if outcome.is_err() {
4313        result.termination.refuse_certificate();
4314    }
4315    outcome
4316}
4317
4318fn certify_outer_optimality_at_terminal_fidelity(
4319    obj: &mut dyn OuterObjective,
4320    config: &OuterConfig,
4321    context: &str,
4322    result: &mut OuterResult,
4323    allow_tail_snap: bool,
4324    fidelity: CertificationFidelity,
4325) -> Result<OuterCriterionCertificate, EstimationError> {
4326    let capability = obj.capability();
4327    let layout = capability.theta_layout();
4328    layout
4329        .validate_point_len(&result.rho, "outer certificate point")
4330        .map_err(|err| {
4331            EstimationError::RemlOptimizationFailed(format!(
4332                "{context}: invalid outer certificate point: {err}"
4333            ))
4334        })?;
4335    // Certification is an ownership boundary: reinstall the model-domain
4336    // derivative face before either screening or mint evaluates the selected
4337    // point. This makes the terminal audit independent of whichever
4338    // canonicalized plan or prior fit last touched this thread's IFT state, and
4339    // prevents an active-set search override from surviving as derivative
4340    // geometry. Screening and mint can differ in derivative order, never in the
4341    // model whose feasible directions they differentiate (#2514).
4342    let model_domain_bounds_for_derivatives =
4343        outer_model_domain_bounds_template(config, layout.n_params);
4344    crate::estimate::reml::outer_eval::record_current_outer_rho_model_upper_bounds_for_ift(
4345        &model_domain_bounds_for_derivatives.1,
4346    );
4347    if result.rho.iter().any(|value| !value.is_finite()) {
4348        return Err(outer_nonconvergence_error(
4349            context,
4350            "the selected checkpoint contains non-finite coordinates",
4351            result,
4352            None,
4353            StationarityStandard::NoComparison,
4354        ));
4355    }
4356    if layout.n_params == 0 {
4357        let value = obj.eval_cost(&result.rho).map_err(|err| {
4358            outer_nonconvergence_error(
4359                context,
4360                &format!("zero-dimensional final objective evaluation failed: {err}"),
4361                result,
4362                Some(0.0),
4363                StationarityStandard::NoComparison,
4364            )
4365        })?;
4366        if !value.is_finite() {
4367            return Err(outer_nonconvergence_error(
4368                context,
4369                "the zero-dimensional final objective is non-finite",
4370                result,
4371                Some(0.0),
4372                StationarityStandard::NoComparison,
4373            ));
4374        }
4375        let certificate = OuterCriterionCertificate {
4376            stationarity: OuterStationarityCertificate::AnalyticGradient {
4377                grad_norm: 0.0,
4378                projected_grad_norm: 0.0,
4379                bound: outer_gradient_tolerance(config).abs,
4380                // No smoothing estimand: the empty score is stationary by
4381                // construction, not by clearing this band (#2530).
4382                rung: StationarityRung::EMPTY_ESTIMAND.into(),
4383            },
4384            // No estimand, so no curvature exists to be admissible — the
4385            // second-order twin of the EMPTY_ESTIMAND rung above (#2561).
4386            curvature: CurvatureEvidence::NoEstimand,
4387            lambdas_railed: Vec::new(),
4388            railed_facts: Vec::new(),
4389            curvature_floor: None,
4390        };
4391        result.final_value = value;
4392        result.final_grad_norm = Some(0.0);
4393        result.final_gradient = Some(Array1::zeros(0));
4394        result.final_hessian = None;
4395        result
4396            .termination
4397            .certify(OuterConvergedVia::GradientStationary);
4398        result.criterion_certificate = Some(certificate.clone());
4399        return Ok(certificate);
4400    }
4401    if matches!(result.plan_used.solver, Solver::Efs | Solver::HybridEfs)
4402        && capability.gradient != Derivative::Analytic
4403    {
4404        return certify_fixed_point_optimality(obj, config, context, result, fidelity);
4405    }
4406    if capability.gradient != Derivative::Analytic {
4407        return Err(outer_nonconvergence_error(
4408            context,
4409            "the objective exposes no analytic gradient for final certification",
4410            result,
4411            None,
4412            StationarityStandard::NoComparison,
4413        ));
4414    }
4415
4416    // Sample the scalar authority FIRST and leave the derivative-bearing
4417    // evaluator as the terminal state owner. This is one same-rho audit, not a
4418    // finite-difference derivative: it catches a split value/gradient
4419    // implementation without violating the production no-FD contract.
4420    //
4421    // The order is load-bearing in two independent ways, and #2583 briefly
4422    // inverted it:
4423    //
4424    //   * Every real outer objective's derivative lane is a warm-started inner
4425    //     SOLVE, so evaluating it ADVANCES the inner state. A value sample
4426    //     taken afterwards reads the state that solve left behind, not the
4427    //     state it priced. The value lane, by contrast, reads the installed
4428    //     state without advancing it, so value-then-derivative is the only
4429    //     order in which both samples price ONE inner state.
4430    //   * The mint's derivative-bearing evaluation must be the LAST evaluation
4431    //     of the certification, because it is the terminal coefficient-mode
4432    //     owner every downstream consumer reads (#2359). A trailing value-only
4433    //     request re-installs a value-only pass as that owner.
4434    //
4435    // Inverting it also made the audit VACUOUS on the one route it was aimed
4436    // at: the REML objective's outer-eval cache serves any `Value` request from
4437    // whatever entry the derivative call just wrote, so the audit compared a
4438    // number with itself and could not fire. Agreement by cache lookup is not
4439    // agreement between two assemblies of the criterion.
4440    //
4441    // The genuine #2583 hazard — two INNER SOLVES at one ρ, disagreeing at
4442    // inner-tolerance scale rather than at the √ε scale this bound is
4443    // calibrated for — is addressed where it lives: the value lane's bundle is
4444    // stored under the shared ρ key, so the derivative lane that follows reuses
4445    // that inner solution instead of re-solving from it.
4446    //
4447    // BOTH fidelities pay this, deliberately. It is tempting to reserve it for
4448    // the mint the way order four is reserved (#2359), but the two are not
4449    // alike: order four is evidence the filter does not consume, while the
4450    // value-agreement audit is a REFUSAL GATE on the very number the
4451    // multistart ranks by. `retain_best_outer_checkpoint` orders candidates on
4452    // `final_value`; admitting a candidate whose value lane disagrees with its
4453    // derivative lane lets a desynced candidate win the ranking on a number
4454    // nothing has validated, and mint then refuses the whole fit instead of a
4455    // runner-up carrying it. Pinned by
4456    // `analytic_hessian_candidate_screening_requires_only_first_order_evidence_2414`,
4457    // which asserts screening's orders are exactly [Value, ValueAndGradient].
4458    //
4459    // The EFS/fixed-point route is different and IS gated on fidelity — see
4460    // `certify_fixed_point_optimality`. It returns above this block, and there
4461    // screening and mint are otherwise identical work (no order-four ladder to
4462    // reserve), so running the audit twice buys nothing at all.
4463    let value_only = obj
4464        .eval_with_order(&result.rho, OuterEvalOrder::Value)
4465        .map_err(|err| {
4466            outer_nonconvergence_error(
4467                context,
4468                &format!("terminal value-only certificate evaluation failed: {err}"),
4469                result,
4470                result.final_grad_norm,
4471                StationarityStandard::NoComparison,
4472            )
4473        })?
4474        .cost;
4475    // Sampled HERE, while the shared inner-progress snapshot still describes
4476    // THIS lane. The guard further down reads it after the analytic lane has
4477    // also run, so it can only speak for that one (#2228). Measured: this is the
4478    // audit site that actually fires in practice, and it was reporting
4479    // "value-lane=unsampled" because only the fixed-point route was wired.
4480    let value_lane_inner_converged = inner_solve_converged(config.outer_inner_cap.as_ref());
4481    if !value_only.is_finite() {
4482        return Err(outer_nonconvergence_error(
4483            context,
4484            "terminal value-only certificate evaluation returned a non-finite objective value",
4485            result,
4486            result.final_grad_norm,
4487            StationarityStandard::NoComparison,
4488        ));
4489    }
4490
4491    // Order four is reserved for the mint audit (#2359). A screening pass takes
4492    // the same first-order evidence the no-analytic-Hessian path already
4493    // certifies on, so the multi-start keeps its filter without building the
4494    // order-four family tower once per seed.
4495    // One boolean drives BOTH the request below and the requirement at the
4496    // analytic-Hessian block: a pass that does not ask for curvature must not
4497    // then refuse the candidate for not supplying it. Splitting them made every
4498    // screened candidate of an analytic-Hessian objective fail certification
4499    // with "declared analytic curvature but returned none at the final point" —
4500    // a statement about this pass's own eval order, not about the candidate.
4501    //
4502    // #2596 addendum, and the reason this line is no longer the whole story:
4503    // the reservation is sound for the CURVATURE VERDICT (a missing verdict is
4504    // permissive) but NOT for the stationarity BOUND, one of whose rungs the
4505    // same Hessian owns and where a missing rung is strictly tightening. The
4506    // screening pass therefore re-acquires the Hessian, for the bound alone,
4507    // when the un-widened band would refuse — see `screening_bound_curvature`
4508    // below. That escalation is the reason this boolean can stay
4509    // fidelity-gated: it still governs the ORDINARY path, and order four still
4510    // costs nothing on any candidate that clears its first-order band.
4511    let wants_analytic_hessian =
4512        capability.hessian.is_analytic() && matches!(fidelity, CertificationFidelity::Mint);
4513    let order = if wants_analytic_hessian {
4514        OuterEvalOrder::ValueGradientHessian
4515    } else {
4516        OuterEvalOrder::ValueAndGradient
4517    };
4518    let evaluation = obj.eval_with_order(&result.rho, order).map_err(|err| {
4519        outer_nonconvergence_error(
4520            context,
4521            &format!("analytic final-point evaluation failed: {err}"),
4522            result,
4523            result.final_grad_norm,
4524            StationarityStandard::NoComparison,
4525        )
4526    })?;
4527
4528    let analytic_lane_inner_converged = inner_solve_converged(config.outer_inner_cap.as_ref());
4529    if !analytic_lane_inner_converged {
4530        return Err(outer_nonconvergence_error(
4531            context,
4532            "terminal analytic evidence was evaluated at a non-converged inner state",
4533            result,
4534            None,
4535            StationarityStandard::NoComparison,
4536        ));
4537    }
4538    layout
4539        .validate_gradient_len(&evaluation.gradient, "outer certificate gradient")
4540        .map_err(|err| {
4541            outer_nonconvergence_error(
4542                context,
4543                &format!("malformed analytic final gradient: {err}"),
4544                result,
4545                None,
4546                StationarityStandard::NoComparison,
4547            )
4548        })?;
4549    if !evaluation.cost.is_finite() || evaluation.gradient.iter().any(|value| !value.is_finite()) {
4550        return Err(outer_nonconvergence_error(
4551            context,
4552            "the analytic final-point value or gradient is non-finite",
4553            result,
4554            None,
4555            StationarityStandard::NoComparison,
4556        ));
4557    }
4558
4559    let bounds = outer_model_domain_bounds_template(config, layout.n_params);
4560    // A penalty creeping toward the ±rho_bound infinite-smoothing ceiling never reaches
4561    // it EXACTLY — each outer step only shrinks the gap, so it lands strictly inside the
4562    // box (the #2299 checkpoint sits at ρ=29.9938, not 30). `certificate_railed_lambdas`
4563    // then flags it railed via `CERTIFICATE_RAIL_MARGIN`, but the exact `x >= upper` /
4564    // `x <= lower` box-KKT projection treats it as INTERIOR and its outward pull inflates
4565    // |Pg| above the (tiny) stationarity bound — the fit refuses a genuine railed optimum.
4566    // Project the stationarity residual with the box endpoints relaxed inward by that SAME
4567    // rail margin, so "railed" means ONE thing to the detector AND the projector: a
4568    // within-tolerance coordinate whose gradient points OUT of the box has its KKT-multiplier
4569    // component removed rather than counted as a stationarity residual (#2299). The
4570    // projection only zeros the OUTWARD half (`.max(0.0)`/`.min(0.0)`), so a coordinate near
4571    // the bound that still has feasible-descent gradient keeps it and is never falsely
4572    // certified.
4573    let rail_projection_bounds = rail_relaxed_bounds(&bounds);
4574    let grad_norm = evaluation.gradient.dot(&evaluation.gradient).sqrt();
4575    // The terminal inner coefficients β(ρ̂), published by the REML bridge on
4576    // every eval (`inner_beta_hint`). Used to scale the estimand tolerance for
4577    // the asymptote-rail certificate (#2348 Inc 1).
4578    let terminal_beta = evaluation.inner_beta_hint.clone();
4579    // KKT-projected gradient VECTOR (not just its norm): the norm feeds the
4580    // stationarity certificate below, and the vector feeds the curvature-scaled
4581    // flat-valley Newton decrement (#2253/#2249/#2015) once the analytic Hessian
4582    // is in hand.
4583    //
4584    // #2545: at a coordinate the projection is about to treat as AT its bound,
4585    // the criterion's unconditional `log cosh` barrier contributes a saturated
4586    // `+w·a = 1.3333e-7` that `gi.max(0.0)` retains — a standing residual no fit
4587    // can clear, so a λ=∞ face could never certify. Remove exactly that term,
4588    // exactly on those coordinates, from the certificate's VIEW of the gradient
4589    // (`gradient_with_rail_barrier_removed` documents why an interior coordinate
4590    // must keep it). `result.final_gradient` and `grad_norm` below stay the raw
4591    // criterion gradient — this is what the certificate JUDGES, not what the
4592    // criterion IS.
4593    let rail_barrier_gradient = obj.soft_rho_guard_gradient(&result.rho);
4594    let certificate_gradient = gradient_with_rail_barrier_removed(
4595        &result.rho,
4596        &evaluation.gradient,
4597        &rail_projection_bounds,
4598        rail_barrier_gradient.as_ref(),
4599    );
4600    if log::log_enabled!(log::Level::Info) && certificate_gradient != evaluation.gradient {
4601        let removed = (&evaluation.gradient - &certificate_gradient)
4602            .iter()
4603            .map(|v| v * v)
4604            .sum::<f64>()
4605            .sqrt();
4606        log::info!(
4607            "[CERTIFICATE-BARRIER] {context}: removed the soft rho-guard barrier from the \
4608             railed coordinates of the certificate's gradient view (#2545), \
4609             ||removed||={removed:.6e}"
4610        );
4611    }
4612    let projected_gradient = project_gradient_vector(
4613        &result.rho,
4614        &certificate_gradient,
4615        Some(&rail_projection_bounds),
4616    );
4617    let projected_grad_norm = projected_gradient.iter().map(|v| v * v).sum::<f64>().sqrt();
4618    // #2514: preserve the literal first-order geometry used by this pass without
4619    // dumping an O(p) vector for large outer problems. A temporary active-set
4620    // search box can differ from the model's feasible box; when that happens,
4621    // screening and mint may otherwise print identical rho/raw gradients while
4622    // silently projecting against different faces. Record every detector-active
4623    // or actually projected coordinate, capped only in the log payload.
4624    if log::log_enabled!(log::Level::Info) {
4625        const PROJECTION_RECORD_LIMIT: usize = 32;
4626        let mut active_or_projected = 0usize;
4627        let mut projection_records = Vec::new();
4628        for k in 0..result.rho.len() {
4629            let detector_railed = outer_coordinate_is_railed(&result.rho, k, config);
4630            let raw = evaluation.gradient[k];
4631            let projected = projected_gradient[k];
4632            if detector_railed || raw.to_bits() != projected.to_bits() {
4633                active_or_projected += 1;
4634                if projection_records.len() < PROJECTION_RECORD_LIMIT {
4635                    projection_records.push((
4636                        k,
4637                        result.rho[k],
4638                        bounds.0[k],
4639                        bounds.1[k],
4640                        rail_projection_bounds.0[k],
4641                        rail_projection_bounds.1[k],
4642                        raw,
4643                        projected,
4644                        detector_railed,
4645                    ));
4646                }
4647            }
4648        }
4649        if active_or_projected > 0 {
4650            log::info!(
4651                "[CERTIFICATE-PROJECTION] {context}: fidelity={fidelity:?},                  active_or_projected={active_or_projected},                  records(index,rho,model_lo,model_hi,projection_lo,projection_hi,raw_g,projected_g,detector_railed)={projection_records:?}{}",
4652                if active_or_projected > projection_records.len() {
4653                    " (truncated)"
4654                } else {
4655                    ""
4656                },
4657            );
4658        }
4659    }
4660    // Anchored at the criterion value of the point being JUDGED, which is what
4661    // the mgcv `magic` rule means and what the solver's own band deliberately
4662    // no longer does (#2613): see `outer_stationarity_band_at`.
4663    //
4664    // #2688: the band and its rung arrive TOGETHER. This used to be followed by
4665    // `let mut bound_source = SolverBand;`, so the engine's declared band, the
4666    // point-anchored widening and the caller's cap -- three quantities, one of
4667    // which is not a defect in the fit -- all reported one label.
4668    let band_at_point = outer_stationarity_band_and_rung_at(config, evaluation.cost);
4669    let solver_bound = band_at_point.bound;
4670    let mut bound_source = band_at_point.source;
4671    if bound_source == StationarityBoundSource::CallerRequirement {
4672        // #2568's audit line, which until #2688 could not print on this path:
4673        // the cap it announces had already been applied silently in the band
4674        // helper, so the guard below that emits it was false by construction.
4675        log::info!(
4676            "[2568-REQUIREMENT] {context}: caller requires |Pg| <= {:.6e}; \
4677             engine bound was {:.6e} (rung {}); measured |Pg| = \
4678             {projected_grad_norm:.6e}",
4679            solver_bound,
4680            band_at_point.engine_bound,
4681            band_at_point.engine_source.label(),
4682        );
4683    }
4684    // #2458: this used to open with a rung gated on
4685    // `operator_stop_reason == CostStallFlatValley` that installed
4686    // `flat_valley_converged_grad_bound(cost)` — `1e-3·(1 + |score|)` capped at
4687    // `1.0`. Two things were wrong with it, and they compound.
4688    //
4689    // First, the gate is an EXIT REASON and the bound is a pure function of the
4690    // criterion value. The same point, with the same criterion, the same
4691    // gradient and the same curvature, was judged against two different
4692    // standards depending on which stop reason the operator happened to record.
4693    // That is this issue's thesis in its purest form: a predicate answering
4694    // "how did the loop stop?" consumed where a property of the objective is
4695    // needed.
4696    //
4697    // Second, and worse, the constant was redundant with a MEASUREMENT taken in
4698    // the same regime, and won exactly where the measurement had declined. A
4699    // cost-stall exit carries the guard's probe-noise floor `σ̂/Δ`
4700    // (`flat_noise_grad_bound`, applied just below) — the criterion's own
4701    // demonstrated gradient resolution at the step scale the search actually
4702    // probed. When `σ̂/Δ` exceeds `FLAT_VALLEY_CONVERGED_ABS_GRAD_CAP` the guard
4703    // reports `ProbeNoiseVerdict::Unresolvable` and licenses NO bound, because
4704    // "the criterion resolves no gradient at this step scale" (`bridges.rs`).
4705    // The deleted rung then supplied `min(1e-3·(1+|score|), 1.0)` anyway. So at
4706    // precisely the points where the instrument measured itself too noisy to
4707    // certify anything, the certificate substituted a constant and certified.
4708    // A measurement that declines must make the certificate decline, not be
4709    // replaced by a number that was never measured.
4710    //
4711    // What remains in the ladder below is, in every rung: the configured band,
4712    // a measurement (`flat_noise_grad_bound`, gradient reproducibility), a
4713    // derivation (`|Pg|·√(τ/Δpred)`), or the caller's own requirement. No rung
4714    // is a magic relative constant, and none is selected by how the search
4715    // exited. `FLAT_VALLEY_CONVERGED_REL_GRAD` / `_ABS_GRAD_CAP` are retained
4716    // because the cost-stall guard itself still uses the cap as its resolution
4717    // ceiling — that use is a measured ratio compared against a declared
4718    // ceiling, which is a different thing from a bound built out of one.
4719    let mut stationarity_bound = solver_bound;
4720    // #2241 — a cost-stall exit carries the guard's measured probe-noise-floor
4721    // gradient bound σ̂/Δ. The certificate must judge the re-measured final
4722    // gradient against the same flat band the guard certified, or the guard's
4723    // noise-scale convergence would be granted in the loop and revoked here.
4724    if let Some(noise_bound) = result.flat_noise_grad_bound
4725        && noise_bound.is_finite()
4726    {
4727        if noise_bound > stationarity_bound {
4728            bound_source = StationarityBoundSource::ProbeNoiseFloor;
4729            stationarity_bound = noise_bound;
4730        }
4731    }
4732    // #2568 -- the caller's requirement caps the ladder's TOP. Every rung above
4733    // widens, so capping here is the only placement that cannot be defeated by a
4734    // rung that fires later; in particular the score-relative widening is what
4735    // produced the saturated `bound = 1.000e0` this issue was filed against.
4736    //
4737    // #2688 -- this is now the SECOND cap, not the only one, and it says so.
4738    // `outer_stationarity_band_and_rung_at` already capped the engine's own
4739    // band and labelled the result; what reaches here is a bound that a
4740    // widening ABOVE (today: the probe-noise floor) pushed back past the
4741    // requirement. Before #2688 `required < stationarity_bound` was false by
4742    // construction on every exit where nothing widened the already-capped
4743    // value, which is why this audit line had never been observed to print.
4744    //
4745    // No new refusal path is needed and none is added: the acceptance test below
4746    // already compares `projected_grad_norm` against `stationarity_bound`, so
4747    // tightening the bound refuses the fit through the machinery that was always
4748    // there, with the rung naming who decided.
4749    //
4750    // Both numbers are reported. The engine's own bound is what the fit would
4751    // have been held to and is the quantity a reader needs to judge whether the
4752    // requirement was reasonable; the requirement is what actually decided. A
4753    // refusal that named only one of them would be unauditable in exactly the
4754    // way #2465 is about.
4755    if let Some(required) = config.required_projected_gradient_norm
4756        && required < stationarity_bound
4757    {
4758        log::info!(
4759            "[2568-REQUIREMENT] {context}: caller requires |Pg| <= {required:.6e}; \
4760             engine bound was {stationarity_bound:.6e} (rung {}); measured |Pg| = \
4761             {projected_grad_norm:.6e}",
4762            bound_source.label(),
4763        );
4764        bound_source = StationarityBoundSource::CallerRequirement;
4765        stationarity_bound = required;
4766    }
4767    audit_outer_value_agreement(
4768        context,
4769        value_only,
4770        evaluation.cost,
4771        result,
4772        Some(projected_grad_norm),
4773        StationarityBound::from_ladder(stationarity_bound, bound_source),
4774        (
4775            Some(value_lane_inner_converged),
4776            Some(analytic_lane_inner_converged),
4777        ),
4778    )?;
4779
4780    // The optimizer's own recorded best-iterate evidence, captured before the
4781    // fresh certificate-time measurement overwrites it below. Together with
4782    // `evaluation` this is a SECOND independent measurement of the objective
4783    // at the same ρ — the raw material for the gradient-reproducibility floor
4784    // further down, at zero additional objective evaluations.
4785    let run_recorded_gradient = result.final_gradient.take();
4786    let run_recorded_value = result.final_value;
4787
4788    // Install measured first-order evidence before any fallible curvature
4789    // processing. If curvature is malformed, the retained resume checkpoint
4790    // still carries the exact value/gradient that caused certification to stop.
4791    result.final_value = evaluation.cost;
4792    result.final_grad_norm = Some(projected_grad_norm);
4793    result.final_gradient = Some(evaluation.gradient);
4794
4795    // #2596 — a pass that spends LESS evidence must not produce a STRONGER
4796    // refusal than the pass that mints.
4797    //
4798    // The stationarity bound is a LADDER, and one of its rungs — the
4799    // curvature-resolvability widening below — is owned by the analytic outer
4800    // Hessian. `Screening` deliberately does not spend the order-four ladder
4801    // (#2359), and that reservation was argued on the CURVATURE CONJUNCT:
4802    // `curvature_admissible()` reads `hessian_psd != Some(false)`, so a `None`
4803    // curvature verdict certifies on stationarity alone. True — but the Hessian
4804    // ALSO owns a rung of the stationarity BOUND, and there `None` is not
4805    // permissive: it silently reverts screening to the un-widened solver band.
4806    // Screening therefore applied a strictly tighter standard than the mint, and
4807    // a candidate it refused was discarded rather than deferred.
4808    //
4809    // Measured (#2596, lognormal location-scale AFT with a double-penalty
4810    // `s(z, bs="tp", k=10)`): the BFGS converged to the correct interior optimum
4811    // ρ = (0.378, −4.975) at cost 4.1926 with |Pg| = 7.29e-5 against a solver
4812    // band of 5.19e-5 — refused by a factor of 1.4. Both interior seeds were
4813    // refused, the multi-start fell through to the seed lattice's
4814    // over-smoothing boundary candidate, and THAT certified vacuously (at a
4815    // railed corner the box-KKT projection makes |Pg| identically zero) and was
4816    // minted at cost 110.94. The published smoothing parameter's own LAML was
4817    // 26× worse than the one the optimizer had already measured, and the fitted
4818    // smooth carried none of its signal. Every sibling arm of the same suite
4819    // reached the mint and got the rung that would have saved this one
4820    // (`curvature-scaled flat-valley bound 1.537e-3 … widened from
4821    // gradient-band 2.359e-4`); which side of the band a fit lands on is which
4822    // side the last BFGS step stopped on, not a statistical distinction.
4823    //
4824    // So spend the ladder at screening too — but ONLY when the un-widened bound
4825    // would refuse, and ONLY for the bound. The escalated curvature never
4826    // reaches the curvature verdict, the rail certificate, or the tail-snap, so
4827    // this can only ever turn a screening refusal into a screening
4828    // certification and never the reverse. A fit that clears its first-order
4829    // band is byte-identical and pays nothing, so #2359's "order four exactly
4830    // once, at the mint" continues to hold for every healthy fit.
4831    let screening_bound_curvature = if !wants_analytic_hessian
4832        && capability.hessian.is_analytic()
4833        && projected_grad_norm > stationarity_bound
4834    {
4835        log::info!(
4836            "[CERTIFICATE] {context}: screening's first-order band would refuse \
4837             (|Pg|={projected_grad_norm:.3e} > bound={stationarity_bound:.3e}, rung={}); \
4838             spending the order-four ladder so this refusal is judged by the SAME \
4839             stationarity bound the mint applies (#2596)",
4840            bound_source.label(),
4841        );
4842        // A failed or malformed escalation is NOT a refusal of the candidate: it
4843        // only means this rung is unavailable, which is exactly the state the
4844        // pass was already in. Fall through to the un-widened comparison.
4845        match obj.eval_with_order(&result.rho, OuterEvalOrder::ValueGradientHessian) {
4846            Ok(escalated) => match escalated.hessian.materialize_dense() {
4847                Ok(Some(hessian))
4848                    if layout
4849                        .validate_hessian_shape(&hessian, "outer certificate Hessian")
4850                        .is_ok()
4851                        && hessian.iter().all(|value| value.is_finite()) =>
4852                {
4853                    Some(hessian)
4854                }
4855                _ => {
4856                    log::info!(
4857                        "[CERTIFICATE] {context}: the escalated order-four evaluation \
4858                         supplied no usable curvature; keeping the first-order bound"
4859                    );
4860                    None
4861                }
4862            },
4863            Err(error) => {
4864                log::info!(
4865                    "[CERTIFICATE] {context}: the escalated order-four evaluation failed \
4866                     ({error}); keeping the first-order bound"
4867                );
4868                None
4869            }
4870        }
4871    } else {
4872        None
4873    };
4874
4875    let analytic_hessian = if wants_analytic_hessian {
4876        match evaluation.hessian.materialize_dense() {
4877            Ok(Some(hessian)) => {
4878                layout
4879                    .validate_hessian_shape(&hessian, "outer certificate Hessian")
4880                    .map_err(|err| {
4881                        outer_nonconvergence_error(
4882                            context,
4883                            &format!("malformed analytic final Hessian: {err}"),
4884                            result,
4885                            Some(projected_grad_norm),
4886                            StationarityBound::from_ladder(stationarity_bound, bound_source),
4887                        )
4888                    })?;
4889                if hessian.iter().any(|value| !value.is_finite()) {
4890                    return Err(outer_nonconvergence_error(
4891                        context,
4892                        "the analytic final Hessian contains non-finite entries",
4893                        result,
4894                        Some(projected_grad_norm),
4895                        StationarityBound::from_ladder(stationarity_bound, bound_source),
4896                    ));
4897                }
4898                Some(hessian)
4899            }
4900            Ok(None) => {
4901                return Err(outer_nonconvergence_error(
4902                    context,
4903                    "the objective declared analytic curvature but returned none at the final point",
4904                    result,
4905                    Some(projected_grad_norm),
4906                    StationarityBound::from_ladder(stationarity_bound, bound_source),
4907                ));
4908            }
4909            Err(err) => {
4910                return Err(outer_nonconvergence_error(
4911                    context,
4912                    &format!("analytic final Hessian could not be certified: {err}"),
4913                    result,
4914                    Some(projected_grad_norm),
4915                    StationarityBound::from_ladder(stationarity_bound, bound_source),
4916                ));
4917            }
4918        }
4919    } else {
4920        None
4921    };
4922
4923    // #2458 — the same escalation, for the routes that cannot take the one
4924    // above.
4925    //
4926    // The block above closes a gap between two FIDELITIES of one route. The
4927    // remaining gap is between two ROUTES: a route declaring
4928    // `DeclaredHessianForm::Unavailable` has no order-four ladder to spend at
4929    // either fidelity, so it can never reach the curvature-resolvability rung
4930    // and is held to the raw reproducibility band -- the strictest tier in the
4931    // subsystem, awarded to the route that knows the least. Measured across six
4932    // tests of one subsystem in one binary: bounds from 5.675e-6 to 4.771e-2, a
4933    // factor of 8,406, with one |Pg| = 4.637052e-7 graded against four of them.
4934    //
4935    // That gap is #2458 and it is real. It is NOT closed here, and the attempt
4936    // to close it here (`f2cae93ee`, reverted by `finite_difference_outer_hessian`'s
4937    // removal) is the reason this comment exists rather than a code block.
4938    // Forward-differencing the route's analytic gradient produces a number that
4939    // decides which fits are certified, and SPEC line 2 permits finite
4940    // differences only outside production. The workspace's one other production
4941    // finite difference -- the psi audit in `run_plan.rs`, behind
4942    // `outer_gradient_fd_capture_enabled` -- is read by nothing outside tests:
4943    // it RECORDS what happened. A rung that overwrites `stationarity_bound`
4944    // DECIDES what is true, and the precedent does not reach it.
4945    //
4946    // The correct fix is upstream and is being applied there: a route with no
4947    // analytic Hessian should acquire one, not have one estimated on its behalf
4948    // by the code judging it. The named producer -- the constant-curvature outer
4949    // problem, a ONE-parameter profiled Gaussian REML -- now derives its single
4950    // second derivative in closed form and declares `DeclaredHessianForm::Dense`
4951    // (`gam-models/src/fit_orchestration/drivers/spatial_optimization.rs`), so it
4952    // reaches `CurvatureResolvability` by the ordinary path with the family's own
4953    // exact curvature. A route that still declares `Unavailable` is held to the
4954    // raw band and the run record says so (`derived_standard=false`), which is a
4955    // typed inability to certify rather than a silently different standard.
4956
4957    // Curvature-scaled stationarity (#2253/#2249/#2015/#2091). The re-measured
4958    // projected gradient can sit modestly ABOVE the score-relative / probe-noise
4959    // bands even though NO step reduces the objective by more than the outer
4960    // tolerance — a weakly-identified small-n fit reaches this by a flat-valley
4961    // cost-stall, and an *already-stationary* fit reaches it at iteration 0 when
4962    // the plan search exhausts without stepping (a 2-parameter Gaussian-linear
4963    // REML lands λ→0 at a genuine interior optimum whose |Pg|≈1e-7 sits just above
4964    // an absolute score·1e-9 gradient floor tighter than the REML gradient's
4965    // matrix-factorization round-off). Whether that residual is genuine descent is
4966    // a second-order question the flat bands above cannot answer: they are
4967    // gradient-magnitude tests, blind to how the local curvature maps a gradient
4968    // to an objective change. The Newton decrement `½·gᵀH⁻¹g` (see
4969    // `newton_predicted_decrease`) IS that map — the exact predicted improvement of
4970    // a safeguarded second-order step. When it is below the outer objective
4971    // tolerance, the point is stationary at the resolution the criterion can be
4972    // optimized ("no further descent possible"), independent of HOW the solver
4973    // stopped.
4974    //
4975    // Applied whenever a PSD-along-gradient analytic Hessian is in hand (NOT gated
4976    // to a specific exit reason: the decrement test is the certificate, the exit
4977    // reason is not). It can NEVER wrongly certify a fit with real available
4978    // descent: it only widens when `curvature_grad_bound > stationarity_bound`, so
4979    // a well-identified fit that already clears `solver_bound` is untouched; a
4980    // gradient aligned with a near-flat Hessian direction inflates the decrement
4981    // and is rejected; a globally indefinite Hessian is rejected independently by
4982    // the `hessian_psd` gate inside `certifies()`. The derived widening is a
4983    // genuine, direction-aware curvature-scaled GRADIENT bound — the largest ‖Pg‖
4984    // that, in this gradient's direction under this curvature, still predicts a
4985    // decrease of exactly `objective_tol` — not a constant bump: because the
4986    // decrement scales quadratically with ‖g‖ at fixed direction, that bound is
4987    // `‖Pg‖·√(objective_tol/Δpred)`, which clears the actual ‖Pg‖ iff
4988    // `Δpred ≤ objective_tol`.
4989    // `screening_bound_curvature` is the #2596 escalation: at `Mint` it is
4990    // always `None` and this reads `analytic_hessian` exactly as before; at
4991    // `Screening` it is `Some` only on the would-refuse path, and it feeds THIS
4992    // rung and nothing else.
4993    if let Some(hessian) = analytic_hessian
4994        .as_ref()
4995        .or(screening_bound_curvature.as_ref())
4996        && let Some(predicted_decrease) = newton_predicted_decrease(hessian, &projected_gradient)
4997        && predicted_decrease.is_finite()
4998        && predicted_decrease > 0.0
4999    {
5000        // The SAME relative cost floor the cost-stall guard used to declare the
5001        // criterion stalled (run_plan.rs), so certification asserts nothing
5002        // tighter than the loop already proved about this surface.
5003        let objective_tol = outer_rel_cost_floor(config) * (1.0 + evaluation.cost.abs());
5004        let curvature_grad_bound =
5005            projected_grad_norm * (objective_tol / predicted_decrease).sqrt();
5006        if curvature_grad_bound.is_finite() && curvature_grad_bound > stationarity_bound {
5007            log::info!(
5008                "[CERTIFICATE] {context}: curvature-scaled flat-valley bound {curvature_grad_bound:.3e} \
5009                 (|Pg|={projected_grad_norm:.3e}, Newton ½gᵀH⁻¹g={predicted_decrease:.3e} ≤ tol {objective_tol:.3e}) \
5010                 widened from gradient-band {stationarity_bound:.3e}"
5011            );
5012            stationarity_bound = curvature_grad_bound;
5013            bound_source = StationarityBoundSource::CurvatureResolvability;
5014        }
5015    }
5016
5017    // Gradient-reproducibility floor (#2299 fully-saturated smooth). A
5018    // stationarity certificate cannot resolve below the reproducibility of its
5019    // own measuring instrument: at a rail-adjacent optimum (λ ~ 1e12, the term
5020    // collapsed onto its penalty null space, edf saturated) the analytic
5021    // gradient is a difference of enormous canceling log-det terms whose
5022    // evaluation drifts run to run, so |Pg| measures round-off, not slope —
5023    // observed as the SAME ρ returning |g| ∈ {2.5e-3 … 4.5e-2} across
5024    // consecutive evaluations while the objective stays flat to 1e-7.
5025    //
5026    // The certifier already holds TWO independent measurements at this ρ: the
5027    // optimizer's recorded best-iterate gradient (`run_recorded_gradient`) and
5028    // the fresh certificate-time `evaluation` — so the instrument's
5029    // demonstrated noise costs ZERO additional objective evaluations (scripted
5030    // test objectives keep their exact call counts). A REAL residual gradient
5031    // reproduces (spread ≈ 0, no widening — genuine descent can never be
5032    // masked, and a deterministic objective yields bit-identical pairs), while
5033    // cancellation noise decorrelates (spread ~ |Pg|). The widening is gated
5034    // on the two measurements' objective VALUES agreeing to the same relative
5035    // floor the cost-stall guard uses, and the PSD gate below is unchanged.
5036    if projected_grad_norm > stationarity_bound
5037        && let Some(prior_gradient) = run_recorded_gradient.as_ref()
5038        && layout
5039            .validate_gradient_len(prior_gradient, "outer run-recorded gradient")
5040            .is_ok()
5041        && prior_gradient.iter().all(|value| value.is_finite())
5042        && run_recorded_value.is_finite()
5043    {
5044        const GRADIENT_REPRODUCIBILITY_WIDENING: f64 = 2.0;
5045        let objective_tol = config
5046            .rel_cost_tolerance
5047            .unwrap_or(config.tolerance * 1.0e-2)
5048            .max(COST_STALL_REL_TOL_FLOOR)
5049            * (1.0 + evaluation.cost.abs());
5050        let cost_drift = (run_recorded_value - evaluation.cost).abs();
5051        // The spread must compare two measurements of the SAME quantity, so the
5052        // run-recorded gradient gets the identical #2545 barrier removal the
5053        // certificate-time one got. Comparing a barrier-removed view against a
5054        // barrier-bearing one would inject a deterministic `w·a` into a number
5055        // whose entire meaning is "how much of |Pg| is instrument noise".
5056        let prior_projected = project_gradient_vector(
5057            &result.rho,
5058            &gradient_with_rail_barrier_removed(
5059                &result.rho,
5060                prior_gradient,
5061                &rail_projection_bounds,
5062                rail_barrier_gradient.as_ref(),
5063            ),
5064            Some(&rail_projection_bounds),
5065        );
5066        let spread = (&prior_projected - &projected_gradient)
5067            .iter()
5068            .map(|v| v * v)
5069            .sum::<f64>()
5070            .sqrt();
5071        let repro_bound = GRADIENT_REPRODUCIBILITY_WIDENING * spread;
5072        if cost_drift <= objective_tol
5073            && repro_bound.is_finite()
5074            && repro_bound > stationarity_bound
5075            && projected_grad_norm <= repro_bound
5076        {
5077            log::info!(
5078                "[CERTIFICATE] {context}: gradient-reproducibility floor widened the \
5079                 stationarity bound to {repro_bound:.3e} (|Pg|={projected_grad_norm:.3e}, \
5080                 same-ρ spread between the run-recorded and certificate-time gradients \
5081                 {spread:.3e}, cost drift {cost_drift:.3e} ≤ tol {objective_tol:.3e})"
5082            );
5083            stationarity_bound = repro_bound;
5084            bound_source = StationarityBoundSource::GradientReproducibility;
5085        }
5086    }
5087
5088    // #2458/#2479 -- the bound's own provenance, emitted UNCONDITIONALLY rather
5089    // than only when a rung happens to widen. A certificate that does not carry
5090    // which of its five terms decided it can only be re-derived from source,
5091    // never audited; the same complaint this file's refusal messages make about
5092    // the fits they refuse. Fidelity and the exact rho identify whether a
5093    // screening verdict and the terminal mint measured the same candidate.
5094    // `derived_standard=false` is the actionable bit: it says this verdict rests
5095    // on a gradient-magnitude substitute because the resolvability form was
5096    // unavailable on this route, not because the problem called for it.
5097    log::info!(
5098        "[CERTIFICATE-BOUND] {context}: fidelity={fidelity:?}, rho={:?}, \
5099         bound {stationarity_bound:.6e} set by {} (derived_standard={}, \
5100         |Pg|={projected_grad_norm:.6e}, |g|={grad_norm:.6e}, \
5101         solver_band={solver_bound:.6e}, cost={:.6e})",
5102        result.rho.to_vec(),
5103        bound_source.label(),
5104        bound_source.is_derived_standard(),
5105        evaluation.cost,
5106    );
5107
5108    // Large-step flatness certificate (#2299 fully-saturated smooth). After the
5109    // reproducibility floor a coordinate that has collapsed EXACTLY onto its
5110    // penalty null space (λ ~ 1e12, edf saturated) can still carry a projected
5111    // gradient component that is DETERMINISTIC cancellation bias from the
5112    // 1e12-conditioned logdet derivative (≈ ε·κ·scale). Being deterministic it
5113    // reproduces run to run, so the spread-keyed reproducibility floor above
5114    // cannot see it; and the Newton decrement anti-rescues, because the near-null
5115    // Hessian direction inflates gᵀH⁻¹g by design. The decisive question is
5116    // second-order-independent: does the criterion actually MOVE along that
5117    // coordinate at MACROSCOPIC scale? This block answers it directly — it probes
5118    // the objective a full e-fold in λ to either side of a near-null-curvature
5119    // coordinate and, for coordinates whose value is provably flat there, removes
5120    // their measured gradient component (numerical bias, not slope) from the
5121    // projected gradient before the bound test. A coordinate whose large-step
5122    // value MOVES is left untouched, so a genuine pseudologdet ramp still refuses.
5123    //
5124    // Gated as narrowly as possible: it runs only when the certificate would
5125    // OTHERWISE refuse on |Pg|, only with an analytic Hessian that is PSD-within-
5126    // noise in hand, and only probes coordinates whose curvature row is below the
5127    // roundoff floor — so a well-conditioned objective (every scripted mock at its
5128    // certification point) probes nothing and pays zero extra evaluations.
5129    // The coordinates railed at ±rho_bound (the infinite-smoothing ceiling). Their
5130    // saturated curvature direction makes the FULL Hessian indefinite, so the
5131    // flatness certificate below — and the final curvature gate — judge PSD on the
5132    // interior (un-railed) sub-block instead, or a rail-caused indefiniteness would
5133    // disable the very certificate that exists to certify a railed optimum (#2299).
5134    // The FACE the certificate reasons on: every θ coordinate against its own
5135    // bound, ρ and non-ρ alike. See `certificate_railed_coordinates` for why
5136    // the λ-block report cannot be used here.
5137    let certificate_railed = certificate_railed_coordinates(&result.rho, config);
5138    // #2676: the directions along which THIS criterion is exactly constant by
5139    // construction of its penalty map. Read once, at the certified point, and
5140    // threaded to every curvature test below, so the certificate and the
5141    // smoothing correction judge the same subspace instead of being able to
5142    // reach opposite verdicts on one matrix at one point. `None` for every
5143    // objective that declares no invariance, which restores the pre-#2676
5144    // behaviour bit for bit.
5145    let criterion_invariance = obj.criterion_invariant_directions(&result.rho);
5146    if let Some(basis) = criterion_invariance.as_ref() {
5147        log::info!(
5148            "[CERTIFICATE] {context}: deflating {} criterion-invariant direction(s) of {} \
5149             before the curvature verdict -- their rho-curvature is the chain-rule term \
5150             `sum_k g_k t_k^2` identically, so its sign measures the gradient code against \
5151             the Hessian code, not the fit (#2676)",
5152            basis.ncols(),
5153            basis.nrows(),
5154        );
5155    }
5156    // The λ-block REPORT that ships on the certificate. Same predicate,
5157    // narrower scan, because `lambdas_railed` indexes smoothing parameters.
5158    let railed_lambda_block = certificate_railed_lambdas(&result.rho, layout.rho_dim(), config);
5159
5160    // Typed stationary-at-asymptote rail certificate (#2348 Inc 1, #2299 layer 3,
5161    // #2337 Thm 2.1). Before falling through to the generic gradient/criterion-flat
5162    // verdict, POSITIVELY certify a railed optimum: the interior (non-railed)
5163    // coordinates are gradient-stationary, and each coordinate railed at the
5164    // infinite-/zero-smoothing box bound sits on a confirmed exponential tail whose
5165    // fitted model has already reached the rail limit to within the estimand
5166    // tolerance. This supersedes the untyped `lambdas_railed` flag with a proof that
5167    // the criterion improvement and coefficient travel still available by running to
5168    // the rail are both below tolerance.
5169    //
5170    // Computed in its own statement so the borrow of `analytic_hessian` ends before
5171    // the mint branch moves it onto the result. Gated to a genuinely railed optimum
5172    // with outward pull (`grad_norm` above the stationarity bound) and an analytic
5173    // Hessian: a well-conditioned interior fit, or a coordinate merely resting near a
5174    // bound with a vanishing gradient, probes nothing and keeps its ordinary verdict.
5175    let asymptote_objective_tol = config
5176        .rel_cost_tolerance
5177        .unwrap_or(config.tolerance * 1.0e-2)
5178        .max(COST_STALL_REL_TOL_FLOOR)
5179        * (1.0 + evaluation.cost.abs());
5180    let rail_outcome = match analytic_hessian.as_ref() {
5181        Some(hessian) if !certificate_railed.is_empty() && grad_norm > stationarity_bound => {
5182            Some(try_certify_asymptote_rail(
5183                obj,
5184                &AsymptoteRailInputs {
5185                    rho: &result.rho,
5186                    projected_gradient: &projected_gradient,
5187                    railed: &certificate_railed,
5188                    layout,
5189                    hessian,
5190                    bounds: &bounds,
5191                    terminal_beta: terminal_beta.as_ref(),
5192                    stationarity_bound: StationarityBound::from_ladder(stationarity_bound, bound_source),
5193                    objective_tol: asymptote_objective_tol,
5194                    context,
5195                },
5196            )?)
5197        }
5198        _ => None,
5199    };
5200    // A refused railed mint carries its typed decline reason into the final
5201    // refusal summary (mirroring the tail-snap decline note), so a railed
5202    // non-mint names the gate that refused instead of failing silently.
5203    let mut asymptote_rail_note: Option<String> = None;
5204    let mut probes_ran = rail_outcome.is_some();
5205    if let Some(outcome) = rail_outcome {
5206        match outcome {
5207            Err(reason) => asymptote_rail_note = Some(reason),
5208            Ok(minted) => {
5209                let (interior_projected_grad_norm, effective_interior_bound, rails) = minted;
5210                // The tail probes were derivative-bearing evaluations at probe
5211                // ρ's, so the EVALUATOR-side terminal-mode carrier now owns the
5212                // last probe, not the checkpoint (#2155 regression: every
5213                // custom-family at-point mint then failed the bitwise terminal
5214                // theta identity at fit assembly). Re-evaluate at the minted
5215                // point and ship ITS numbers as the terminal facts: the same
5216                // evaluation sets the evaluator carrier, so the optimizer
5217                // certificate and the owned mode are bitwise-identical by
5218                // construction. The certified stationarity facts (interior
5219                // norms, rails) remain the judged ones.
5220                let restored = obj
5221                    .eval_with_order(&result.rho, OuterEvalOrder::ValueAndGradient)
5222                    .map_err(|err| {
5223                        EstimationError::RemlOptimizationFailed(format!(
5224                            "{context}: failed to re-own the certified point after \
5225                             asymptote-rail probing: {err}"
5226                        ))
5227                    })?;
5228                result.final_value = restored.cost;
5229                let restored_projected = project_gradient_vector(
5230                    &result.rho,
5231                    &gradient_with_rail_barrier_removed(
5232                        &result.rho,
5233                        &restored.gradient,
5234                        &rail_projection_bounds,
5235                        rail_barrier_gradient.as_ref(),
5236                    ),
5237                    Some(&rail_projection_bounds),
5238                );
5239                result.final_grad_norm = Some(
5240                    restored_projected
5241                        .iter()
5242                        .map(|v| v * v)
5243                        .sum::<f64>()
5244                        .sqrt(),
5245                );
5246                result.final_gradient = Some(restored.gradient);
5247                let certificate = OuterCriterionCertificate {
5248                    stationarity: OuterStationarityCertificate::AsymptoteRail {
5249                        interior_projected_grad_norm,
5250                        // The bound that admitted the interior: the raw stationarity
5251                        // bound, or the curvature-scaled flat-valley widening when the
5252                        // interior sub-block's Newton decrement is below the loop's
5253                        // cost resolution (shared judgment with the Inc 2c mint).
5254                        bound: effective_interior_bound.value(),
5255                        rung: effective_interior_bound.rung().into(),
5256                        rails,
5257                    },
5258                    curvature: CurvatureEvidence::Measured { psd: true },
5259                    lambdas_railed: railed_lambda_block.clone(),
5260                    railed_facts: railed_coordinate_facts(
5261                        &result.rho,
5262                        // #2624: theta-wide, see `certificate_railed_coordinates`.
5263                        &certificate_railed,
5264                        config,
5265                    ),
5266                    curvature_floor: None,
5267                };
5268                // Move the certified curvature onto the result; the mint path returns
5269                // immediately, so the fall-through below never observes the move.
5270                result.final_hessian = analytic_hessian;
5271                result.criterion_certificate = Some(certificate.clone());
5272                if !certificate.certifies() {
5273                    return Err(outer_nonconvergence_error(
5274                        context,
5275                        &certificate.summary(),
5276                        result,
5277                        Some(interior_projected_grad_norm),
5278                        StationarityBound::from_ladder(stationarity_bound, bound_source),
5279                    ));
5280                }
5281                result
5282                    .termination
5283                    .certify(OuterConvergedVia::AsymptoteStationary {
5284                        rails: certificate.stationarity.rails().len(),
5285                    });
5286                log::info!("[CERTIFICATE] {context}: {}", certificate.summary());
5287                return Ok(certificate);
5288            }
5289        }
5290    }
5291
5292    let mut certified_projected_grad_norm = projected_grad_norm;
5293    if projected_grad_norm > stationarity_bound
5294        && let Some(hessian) = analytic_hessian.as_ref()
5295        && certificate_hessian_is_psd_off_railed(
5296            hessian,
5297            &certificate_railed,
5298            criterion_invariance.as_ref(),
5299        ) == Some(true)
5300    {
5301        let n = layout.n_params;
5302        // Curvature scale of the analytic outer Hessian: its dominant diagonal,
5303        // the same ‖H‖ scale `certificate_hessian_is_psd` and
5304        // `newton_predicted_decrease` regularize against. A coordinate's curvature
5305        // ROW is indistinguishable from the assembly's roundoff — it has no
5306        // curvature the arithmetic can resolve and has collapsed onto the penalty
5307        // null space — when its largest entry falls below the SAME √ε·‖H‖ margin
5308        // those two probes use to separate a real curvature direction from
5309        // O(ε·‖H‖) accumulation noise. This is the derivation of the threshold:
5310        // NULL_CURVATURE_REL = √ε (machine epsilon's square root, the assembled
5311        // Hessian's relative resolution), scaled by the Hessian's own max-diagonal
5312        // magnitude, floored at 1 exactly as the PSD/Newton shift is.
5313        let max_diag = (0..n).fold(0.0_f64, |acc, j| acc.max(hessian[[j, j]].abs()));
5314        let null_curvature_threshold = f64::EPSILON.sqrt() * max_diag.max(1.0);
5315        // The SAME relative cost floor the cost-stall guard and both widenings
5316        // above use: certification asserts nothing tighter about this surface's
5317        // macroscopic flatness than the loop already proved.
5318        let objective_tol = config
5319            .rel_cost_tolerance
5320            .unwrap_or(config.tolerance * 1.0e-2)
5321            .max(COST_STALL_REL_TOL_FLOOR)
5322            * (1.0 + evaluation.cost.abs());
5323        // One e-fold in log-λ per coordinate (ρ IS log-λ): the +δ/−δ pair spans e²
5324        // in λ, a macroscopic move across which no genuine descent slope can hide.
5325        const LARGE_STEP_DELTA: f64 = 1.0;
5326        let mut saturated_flat: Vec<usize> = Vec::new();
5327        let mut probe_reports: Vec<String> = Vec::new();
5328        let mut probed_any = false;
5329        let mut probe_failed = false;
5330        for k in 0..n {
5331            let row_inf = (0..n).fold(0.0_f64, |acc, j| acc.max(hessian[[k, j]].abs()));
5332            // Only near-null-curvature coordinates the measured gradient actually
5333            // loads on can be responsible for |Pg| exceeding the band; skip every
5334            // other coordinate, so no probe fires on a well-conditioned surface.
5335            if row_inf > null_curvature_threshold || projected_gradient[k] == 0.0 {
5336                continue;
5337            }
5338            let mut plus = result.rho.clone();
5339            plus[k] += LARGE_STEP_DELTA;
5340            let mut minus = result.rho.clone();
5341            minus[k] -= LARGE_STEP_DELTA;
5342            probed_any = true;
5343            let (Ok(cost_plus), Ok(cost_minus)) = (obj.eval_cost(&plus), obj.eval_cost(&minus))
5344            else {
5345                // A failed probe is not evidence of flatness — refuse to classify
5346                // (conservative) and leave |Pg| intact for the bound test.
5347                probe_failed = true;
5348                break;
5349            };
5350            if !cost_plus.is_finite() || !cost_minus.is_finite() {
5351                probe_failed = true;
5352                break;
5353            }
5354            let up = (cost_plus - evaluation.cost).abs();
5355            let down = (cost_minus - evaluation.cost).abs();
5356            if up <= objective_tol && down <= objective_tol {
5357                saturated_flat.push(k);
5358                probe_reports.push(format!("k={k} |ΔV|+={up:.3e} |ΔV|-={down:.3e}"));
5359            }
5360        }
5361        if !probe_failed && !saturated_flat.is_empty() {
5362            // Recompute |Pg| with the provably macroscopically-flat coordinates
5363            // removed: their measured gradient is deterministic cancellation bias,
5364            // not slope. Coordinates that moved keep their component and still count
5365            // against the bound.
5366            let reduced_sq = (0..n)
5367                .filter(|k| !saturated_flat.contains(k))
5368                .map(|k| projected_gradient[k] * projected_gradient[k])
5369                .sum::<f64>();
5370            certified_projected_grad_norm = reduced_sq.sqrt();
5371            let flat_list = saturated_flat
5372                .iter()
5373                .map(usize::to_string)
5374                .collect::<Vec<_>>()
5375                .join(", ");
5376            let probe_summary = probe_reports.join("; ");
5377            log::info!(
5378                "[CERTIFICATE] {context}: large-step flatness certificate classified \
5379                 coordinate(s) [{flat_list}] saturated-flat (curvature row ≤ \
5380                 {null_curvature_threshold:.3e}, probed Δ=±{LARGE_STEP_DELTA} with \
5381                 {probe_summary}, cost-flat to tol {objective_tol:.3e}); projected \
5382                 gradient reduced from {projected_grad_norm:.3e} to \
5383                 {certified_projected_grad_norm:.3e}"
5384            );
5385        }
5386        // `eval_cost` warm-starts the inner solve, so the probes moved the objective
5387        // off the certified point. Restore it to ρ̂ once iff we actually probed, so
5388        // the downstream state (and the rho-uncertainty diagnostic) sees the fitted
5389        // point. A failure to re-evaluate the same ρ that certified moments ago is a
5390        // genuinely broken objective and refuses conservatively.
5391        if probed_any {
5392            obj.eval_cost(&result.rho).map_err(|err| {
5393                outer_nonconvergence_error(
5394                    context,
5395                    &format!(
5396                        "failed to restore the objective to the certified point after \
5397                         flatness probing: {err}"
5398                    ),
5399                    result,
5400                    Some(certified_projected_grad_norm),
5401                    StationarityBound::from_ladder(stationarity_bound, bound_source),
5402                )
5403            })?;
5404        }
5405    }
5406
5407    let mut certificate = OuterCriterionCertificate {
5408        stationarity: OuterStationarityCertificate::AnalyticGradient {
5409            grad_norm,
5410            projected_grad_norm: certified_projected_grad_norm,
5411            bound: stationarity_bound,
5412            rung: bound_source.provenance().into(),
5413        },
5414        // The RAW measurement — unchanged, and what every consumer that asks
5415        // for a genuine PSD certificate keeps receiving.
5416        curvature: match analytic_hessian.as_ref() {
5417            Some(hessian) => CurvatureEvidence::from_measurement(
5418                certificate_hessian_is_psd_off_railed(
5419                    hessian,
5420                    &certificate_railed,
5421                    criterion_invariance.as_ref(),
5422                ),
5423            ),
5424            // A screening pass deliberately declines the order-four ladder
5425            // (the documented design at `CertificationFidelity`); a Mint pass
5426            // reaching here simply has no analytic Hessian to test. Those were
5427            // the same `None` before #2561, which is why the design's own
5428            // promise — the winner's verdict is the one that mints — could not
5429            // be checked by anyone.
5430            None if matches!(fidelity, CertificationFidelity::Screening) => {
5431                CurvatureEvidence::NotSpent
5432            }
5433            None => CurvatureEvidence::NotAvailable,
5434        },
5435        lambdas_railed: railed_lambda_block.clone(),
5436        // #2624: `lambdas_railed` above is the lambda-scoped REPORT and stays
5437        // that way; `railed_facts` is the EVIDENCE for a decision taken on the
5438        // theta-wide face (`certificate_railed`, used by the off-railed PSD
5439        // test and the curvature floor immediately below), so it must carry the
5440        // same coordinates that decision was taken on. On an exact-joint
5441        // spatial route the psi coordinate is the only one carrying gradient,
5442        // and it was the one coordinate the refusal could not print.
5443        railed_facts: railed_coordinate_facts(&result.rho, &certificate_railed, config),
5444        // The floor's verdict on that same curvature, recorded beside it.
5445        curvature_floor: analytic_hessian.as_ref().and_then(|hessian| {
5446            interior_curvature_floor_clearance(
5447                hessian,
5448                &certificate_railed,
5449                &projected_gradient,
5450                criterion_invariance.as_ref(),
5451            )
5452        }),
5453    };
5454    // Certify-time tail snap (#2348 Inc 2). About to refuse a point whose
5455    // residual gradient is carried by un-railed coordinates crawling a
5456    // CONFIRMED exponential tail toward the ρ-box (the one-e-fold-per-step
5457    // grind: the loop budget can exhaust strictly inside the box, where the
5458    // Inc 1 railed mint can never fire), snap those coordinates to their box
5459    // bound and publish that point as a one-shot optimization reseed. The
5460    // resumed optimizer lets coupled interior coordinates move before the
5461    // FULL Inc 1 rail discipline judges the result — this path grants nothing
5462    // by itself.
5463    let mut tail_snap_note: Option<String> = None;
5464    if allow_tail_snap
5465        && !certificate.certifies()
5466        && grad_norm > stationarity_bound
5467        && let Some(hessian) = analytic_hessian.as_ref()
5468    {
5469        probes_ran = true;
5470        match try_tail_snap_to_rail(
5471            obj,
5472            &AsymptoteRailInputs {
5473                rho: &result.rho,
5474                projected_gradient: &projected_gradient,
5475                railed: &certificate_railed,
5476                layout,
5477                hessian,
5478                bounds: &bounds,
5479                terminal_beta: terminal_beta.as_ref(),
5480                stationarity_bound: StationarityBound::from_ladder(stationarity_bound, bound_source),
5481                objective_tol: asymptote_objective_tol,
5482                context,
5483            },
5484        )? {
5485            TailSnapOutcome::TailStationaryAtPoint {
5486                rails,
5487                interior_projected_grad_norm,
5488                effective_interior_bound,
5489            } => {
5490                // #2348 Inc 2c: the confirmed tails extrapolate below the bound
5491                // AT the checkpoint — mint the typed asymptote certificate for
5492                // the point as it stands. `hessian_psd` is the interior
5493                // sub-block verdict established before probing (the full
5494                // matrix is expected non-PD from the noise-corrupted tail
5495                // entry); the stored bound is the one that actually certified
5496                // the interior (raw, or the sub-block curvature-scaled
5497                // flat-valley bound).
5498                //
5499                // Re-own the minted point first: the tail probes were
5500                // derivative-bearing evaluations at probe ρ's, so the
5501                // evaluator-side terminal-mode carrier owns the last probe —
5502                // shipping the pre-probe terminal numbers then fails the
5503                // bitwise terminal theta identity at custom-family fit
5504                // assembly (the #2155 all-links regression). One fresh
5505                // evaluation at the checkpoint sets the carrier AND supplies
5506                // the terminal facts, so both sides are bitwise-identical by
5507                // construction.
5508                let restored = obj
5509                    .eval_with_order(&result.rho, OuterEvalOrder::ValueAndGradient)
5510                    .map_err(|err| {
5511                        EstimationError::RemlOptimizationFailed(format!(
5512                            "{context}: failed to re-own the certified point after \
5513                             tail-snap probing: {err}"
5514                        ))
5515                    })?;
5516                result.final_value = restored.cost;
5517                let restored_projected = project_gradient_vector(
5518                    &result.rho,
5519                    &gradient_with_rail_barrier_removed(
5520                        &result.rho,
5521                        &restored.gradient,
5522                        &rail_projection_bounds,
5523                        rail_barrier_gradient.as_ref(),
5524                    ),
5525                    Some(&rail_projection_bounds),
5526                );
5527                result.final_grad_norm = Some(
5528                    restored_projected
5529                        .iter()
5530                        .map(|v| v * v)
5531                        .sum::<f64>()
5532                        .sqrt(),
5533                );
5534                result.final_gradient = Some(restored.gradient);
5535                let certificate = OuterCriterionCertificate {
5536                    stationarity: OuterStationarityCertificate::AsymptoteRail {
5537                        interior_projected_grad_norm,
5538                        bound: effective_interior_bound.value(),
5539                        rung: effective_interior_bound.rung().into(),
5540                        rails,
5541                    },
5542                    curvature: CurvatureEvidence::Measured { psd: true },
5543                    lambdas_railed: railed_lambda_block.clone(),
5544                    railed_facts: railed_coordinate_facts(
5545                        &result.rho,
5546                        // #2624: theta-wide, see `certificate_railed_coordinates`.
5547                        &certificate_railed,
5548                        config,
5549                    ),
5550                    curvature_floor: None,
5551                };
5552                result.final_hessian = analytic_hessian;
5553                result.criterion_certificate = Some(certificate.clone());
5554                if !certificate.certifies() {
5555                    return Err(outer_nonconvergence_error(
5556                        context,
5557                        &certificate.summary(),
5558                        result,
5559                        Some(interior_projected_grad_norm),
5560                        effective_interior_bound,
5561                    ));
5562                }
5563                result
5564                    .termination
5565                    .certify(OuterConvergedVia::AsymptoteStationary {
5566                        rails: certificate.stationarity.rails().len(),
5567                    });
5568                log::info!(
5569                    "[CERTIFICATE] {context}: tail-stationary at the checkpoint \
5570                     (#2348 Inc 2c): {}",
5571                    certificate.summary()
5572                );
5573                return Ok(certificate);
5574            }
5575            TailSnapOutcome::ConfirmedNeedsReseed(snapped) => {
5576                log::info!(
5577                    "[CERTIFICATE] {context}: confirmed exponential tail on un-railed \
5578                     coordinate(s); publishing the snapped point {snapped} as a waypoint \
5579                     for one re-optimization retry (#2348 Inc 2b / #2358)"
5580                );
5581                tail_snap_note = Some(
5582                    "tail confirmed; retry seeded at the snapped rail waypoint".to_string(),
5583                );
5584                result.tail_snap_reseed = Some(snapped);
5585            }
5586            TailSnapOutcome::Declined(reason) => {
5587                tail_snap_note = Some(reason);
5588            }
5589        }
5590    }
5591    // Install the measured evidence before deciding its verdict.  A rejected
5592    // candidate is retained only as a resumable checkpoint, and that
5593    // checkpoint must carry the actual analytic residual/curvature evidence
5594    // that caused the rejection rather than the optimizer's stale terminal
5595    // status.
5596    result.final_hessian = analytic_hessian;
5597    result.criterion_certificate = Some(certificate.clone());
5598    // Screening deliberately spends no curvature. The caller's strict
5599    // second-order requirement belongs exclusively to the terminal mint; applying
5600    // it here would reject every first-order candidate as `NotSpent` and turn a
5601    // two-basin comparison into seed-budget exhaustion.
5602    let mut curvature_requirement_met =
5603        certificate_meets_curvature_requirement(&certificate, config.require_measured_psd, fidelity);
5604    // #2612 — ADJUDICATE a curvature refusal against the criterion, BEFORE
5605    // deciding it.
5606    //
5607    // This block used to live inside the refusal below, which meant its verdict
5608    // could only ever mint a reseed: a run that found no descending trial
5609    // returned `None`, indistinguishable from "the escape was never runnable",
5610    // and the refusal then proceeded on the matrix's word. But "no feasible step
5611    // along the reported negative eigenvector lowers the objective, anywhere in
5612    // the range where the claim predicts a decrease the criterion can represent"
5613    // is a MEASUREMENT of the criterion, and it contradicts the matrix. Spending
5614    // a refusal on evidence the criterion has just falsified is the failure mode
5615    // #2665 documented from the other side (an analytic `λ_min = −1721.5` whose
5616    // objective curvature along the same eigenvector is `+121.6`), and no
5617    // resolution bound can catch it — the matrix is not imprecise there, it is
5618    // wrong.
5619    //
5620    // Moving it here changes nothing about a real saddle: a descending trial
5621    // still mints the same one-shot reseed, and the refusal that follows is
5622    // still the refusal a genuinely indefinite point earns.
5623    // The ADJUDICATION is a measurement and is NOT gated by `allow_tail_snap`
5624    // (#2612). That flag is the one-shot budget for the RESEED — it exists so
5625    // the retry pass cannot mint a second escape and recurse. Adjudicating
5626    // cannot recurse: it evaluates a bounded, derived ladder of trial points and
5627    // its only two outcomes here are "a descent exists" (which still needs the
5628    // budget to be spent, and is refused below when there is none) and "the
5629    // criterion contradicts the matrix" (which publishes no reseed at all).
5630    // Gating the measurement on the reseed budget made the retry pass refuse on
5631    // the matrix's word for want of a measurement it could have made for free —
5632    // the same "nobody looked" / "it was measured and it is a saddle" collapse
5633    // `CurvatureEvidence` was introduced to prevent.
5634    let strict_curvature_refused =
5635        config.require_measured_psd && certificate.hessian_psd() == Some(false);
5636    result.saddle_escape_reseed = None;
5637    if certificate.is_stationary()
5638        && (!certificate.curvature_not_refused() || strict_curvature_refused)
5639        && let Some(hessian) = result.final_hessian.clone()
5640        && let Some(gradient) = result.final_gradient.clone()
5641    {
5642        probes_ran = true;
5643        let saddle_rho = result.rho.clone();
5644        let baseline_cost = result.final_value;
5645        // `curvature_not_refused()` is `false` exactly when the REDUCED
5646        // (off-railed) Hessian is indefinite, so a railed coordinate does not
5647        // waive the adjudication: rails are passed through and held fixed while
5648        // the step searches the free-direction saddle (#2155). They must be held
5649        // fixed on the SAME face the reduction was taken on — the certificate's
5650        // λ-block report would leave a railed ψ free to be stepped out of its
5651        // box.
5652        match adjudicate_negative_curvature(
5653            obj,
5654            &saddle_rho,
5655            &gradient,
5656            &hessian,
5657            &certificate_railed,
5658            criterion_invariance.as_ref(),
5659            baseline_cost,
5660            asymptote_objective_tol,
5661            &bounds,
5662            context,
5663        ) {
5664            SaddleAdjudication::Descended(point) => {
5665                // The reseed — and only the reseed — is one-shot. On the retry
5666                // pass the descent is still a real finding: the criterion agrees
5667                // with the matrix, so the refusal that follows is the refusal a
5668                // genuine saddle earns, and it is recorded as such rather than
5669                // as an escape that was never run.
5670                if allow_tail_snap {
5671                    result.saddle_escape_reseed = Some(point);
5672                } else {
5673                    log::info!(
5674                        "[CERTIFICATE] {context}: the criterion CONFIRMS the reported negative \
5675                         curvature (a feasible trial along its eigenvector lowers the objective \
5676                         by more than the criterion's resolution), and the one-shot escape \
5677                         reseed has already been spent on this fit. The refusal that follows is \
5678                         a measured saddle, not an unmeasured one (#2612)."
5679                    );
5680                }
5681            }
5682            SaddleAdjudication::Contradicted {
5683                probed,
5684                smallest_step,
5685                predicted_at_smallest,
5686                objective_resolution,
5687                best_seen_cost,
5688                criterion_curvature,
5689            } => {
5690                // #2748: the ladder's verdict on the analytic Hessian outlives
5691                // this function. `invert_identified_rho_hessian` judges the SAME
5692                // matrix at the SAME point later, and without this it does so
5693                // against an eigensolver's backward error -- a bound on the
5694                // decomposition, not on the assembly -- and refuses fits this
5695                // certificate accepted. That is #2428, and carrying the
5696                // measurement is what removes the asymmetry rather than
5697                // widening either side's bar.
5698                result.criterion_hessian_error = criterion_curvature;
5699                log::info!(
5700                    "[CERTIFICATE] {context}: WITHDRAWING the curvature verdict — {probed} \
5701                     evaluated trial(s) down to step {smallest_step:.3e}, where the claim's own \
5702                     predicted decrease {predicted_at_smallest:.3e} reaches the criterion's \
5703                     resolution {objective_resolution:.3e}; best cost seen {best_seen_cost:.9e} \
5704                     never fell below the checkpoint. The certificate records \
5705                     `criterion-contradicted`, NOT a PSD claim (#2612)."
5706                );
5707                // The verdict is withdrawn, not inverted: nothing here showed
5708                // the point IS a minimum. `curvature_floor` goes with it —
5709                // every field in it (`interior_min_eigenvalue`, the floor, the
5710                // floored eigenvalue) is a statement about the matrix whose
5711                // negative direction has just been falsified, and reporting
5712                // them beside a withdrawn verdict is exactly the #2550
5713                // misdirection.
5714                certificate.curvature = CurvatureEvidence::CriterionContradicted;
5715                certificate.curvature_floor = None;
5716                result.criterion_certificate = Some(certificate.clone());
5717                curvature_requirement_met = certificate_meets_curvature_requirement(
5718                    &certificate,
5719                    config.require_measured_psd,
5720                    fidelity,
5721                );
5722            }
5723            SaddleAdjudication::Declined(reason) => {
5724                log::info!("[CERTIFICATE] {context}: saddle escape declined -- {reason}");
5725            }
5726        }
5727    }
5728    if !certificate.certifies() || !curvature_requirement_met {
5729        // Mint the #2392 reseeds fresh for THIS refused point: clear any value a
5730        // prior (multistart / pre-polish) certification of a different ρ left on
5731        // the result so the resume loop never consumes a stale pull-back/freeze.
5732        result.wrong_rail_reseed = None;
5733        result.active_set_reseed = None;
5734        // Reaching here means the #2612 adjudication above did NOT withdraw the
5735        // curvature verdict: either it minted a descending reseed (a real
5736        // saddle, and this refusal carries the retry), or it declined, or the
5737        // refusal is not about curvature at all.
5738        //
5739        // #2665: when a MEASURED analytic Hessian is what refuses the fit, say
5740        // whether that Hessian agrees with the objective it claims to be the
5741        // curvature of. On the SAS/mixture cluster it does not: the rho/rho
5742        // block agreed to 2.8e-5 relative while the psi/psi block was
5743        // SIGN-FLIPPED in every entry, and that block's eigenvalue was the
5744        // whole of `interior lambda_min = -1721.5`. Without this, a wrong
5745        // Hessian and a genuine saddle print identically, and the refusal
5746        // reads as a verdict about the point when it is a verdict about the
5747        // matrix.
5748        //
5749        // Gated on `hessian_psd() == Some(false)` and NOT on
5750        // `strict_curvature_refused`: `require_measured_psd` is FALSE on the
5751        // flexible-link path that motivated this, so the stricter gate never
5752        // fires there. Cost is 2n gradient evaluations on a path that is about
5753        // to refuse the fit outright, capped at n <= 8 so a wide theta cannot
5754        // turn a refusal into a long run.
5755        if certificate.hessian_psd() == Some(false)
5756            && let Some(h_an) = result.final_hessian.clone()
5757            && result.rho.len() == h_an.nrows()
5758            && h_an.nrows() == h_an.ncols()
5759            && (1..=8).contains(&result.rho.len())
5760        {
5761            let n = result.rho.len();
5762            let mut h_fd = Array2::<f64>::zeros((n, n));
5763            let mut complete = true;
5764            for k in 0..n {
5765                // Central differences of the ANALYTIC gradient: this compares
5766                // the Hessian against the derivative the search itself
5767                // consumes, so a disagreement cannot be blamed on a different
5768                // objective. Step is relative where the coordinate is large
5769                // (rho reaches ~21 here) and absolute near zero.
5770                let step = (result.rho[k].abs() * 1e-6).max(1e-5);
5771                let mut plus = result.rho.clone();
5772                plus[k] += step;
5773                let mut minus = result.rho.clone();
5774                minus[k] -= step;
5775                match (
5776                    obj.eval_with_order(&plus, OuterEvalOrder::ValueAndGradient),
5777                    obj.eval_with_order(&minus, OuterEvalOrder::ValueAndGradient),
5778                ) {
5779                    (Ok(forward), Ok(backward)) => {
5780                        for i in 0..n {
5781                            h_fd[[i, k]] =
5782                                (forward.gradient[i] - backward.gradient[i]) / (2.0 * step);
5783                        }
5784                    }
5785                    // A refusal here is EVIDENCE, not an absence of it: an
5786                    // objective that cannot be evaluated a step away from the
5787                    // point it just refused explains a failed line search on its
5788                    // own, with no gradient defect anywhere. Swallowing the
5789                    // reason left the reader with "could not evaluate" and
5790                    // nothing to act on, so name the coordinate, the side, the
5791                    // step, and what the evaluator actually said.
5792                    (forward, backward) => {
5793                        complete = false;
5794                        for (side, outcome) in [("+", forward), ("-", backward)] {
5795                            if let Err(error) = outcome {
5796                                log::info!(
5797                                    "[CERTIFICATE] {context}: the FD-vs-analytic Hessian probe \
5798                                     could not evaluate the objective at coordinate {k} \
5799                                     (rho[{k}]={:.6e}, side {side}, step {step:.3e}): {error}",
5800                                    result.rho[k],
5801                                );
5802                            }
5803                        }
5804                    }
5805                }
5806            }
5807            // Re-own the checkpoint: the probes above were derivative-bearing
5808            // evaluations, so the evaluator-side terminal carrier holds the
5809            // last one until this runs.
5810            if let Err(err) = obj.eval_cost(&result.rho) {
5811                log::warn!(
5812                    "[CERTIFICATE] {context}: could not restore the checkpoint after the \
5813                     FD-vs-analytic Hessian probe: {err}"
5814                );
5815            }
5816            if complete {
5817                let rho_dim = layout.rho_dim().min(n);
5818                log::info!(
5819                    "[CERTIFICATE] {context}: FD-vs-analytic outer Hessian at the refused \
5820                     point (rho_dim={rho_dim} of {n}): analytic={h_an:?} finite_difference={h_fd:?}"
5821                );
5822                // Report BOTH norms per block. `|H_fd - H_an|` says they
5823                // disagree; `|H_fd + H_an|` says HOW: near zero means the block
5824                // is negated, and ~2*|H_an| is what a block that AGREES looks
5825                // like. The two call for different repairs.
5826                for (label, rows, cols) in [
5827                    ("rho_rho", 0..rho_dim, 0..rho_dim),
5828                    ("rho_psi", 0..rho_dim, rho_dim..n),
5829                    ("psi_rho", rho_dim..n, 0..rho_dim),
5830                    ("psi_psi", rho_dim..n, rho_dim..n),
5831                ] {
5832                    let (mut difference, mut sum, mut analytic) = (0.0f64, 0.0f64, 0.0f64);
5833                    for i in rows.clone() {
5834                        for j in cols.clone() {
5835                            difference += (h_fd[[i, j]] - h_an[[i, j]]).powi(2);
5836                            sum += (h_fd[[i, j]] + h_an[[i, j]]).powi(2);
5837                            analytic += h_an[[i, j]].powi(2);
5838                        }
5839                    }
5840                    if analytic > 0.0 {
5841                        log::info!(
5842                            "[CERTIFICATE] {context}: FD-vs-analytic block {label}: \
5843                             |H_fd-H_an|={:.6e} |H_fd+H_an|={:.6e} |H_an|={:.6e} \
5844                             relative={:.6e}",
5845                            difference.sqrt(),
5846                            sum.sqrt(),
5847                            analytic.sqrt(),
5848                            difference.sqrt() / analytic.sqrt(),
5849                        );
5850                    }
5851                }
5852            } else {
5853                log::info!(
5854                    "[CERTIFICATE] {context}: the FD-vs-analytic Hessian probe could not \
5855                     evaluate the objective at every displaced point; no comparison reported"
5856                );
5857            }
5858        }
5859        // #2665: the escape below is the remedy for a point that is first-order
5860        // stationary yet refused for curvature — exactly the SAS/mixture-link
5861        // failures, where `|Pg|` lands 1-10x BELOW its bound and only the
5862        // second-order conjunct refuses, on a MEASURED analytic Hessian with
5863        // `lambda_min ~ -1.6e3` against a `~1e-5` gradient floor.
5864        //
5865        // But the guard is a FIVE-way conjunction, and three of its conjuncts
5866        // can be false because something was never populated rather than
5867        // because a decision was taken. When that happens the refusal message
5868        // says only "indefinite curvature", and the fact that the remedy was
5869        // never even attempted leaves no trace at all. Name the blocking
5870        // conjunct at the moment of refusal so a saddle refusal can be
5871        // attributed instead of guessed.
5872        if certificate.is_stationary()
5873            && (!certificate.curvature_not_refused() || strict_curvature_refused)
5874        {
5875            // `allow_tail_snap` is no longer a blocker here: the adjudication
5876            // runs on every refusal (#2612) and only the RESEED is one-shot, so
5877            // the two remaining conjuncts are the only ways the measurement can
5878            // fail to happen at all.
5879            let escape_blocker = if result.final_hessian.is_none() {
5880                Some("final_hessian=None (no terminal Hessian retained on this route)")
5881            } else if result.final_gradient.is_none() {
5882                Some("final_gradient=None (no terminal gradient retained on this route)")
5883            } else {
5884                None
5885            };
5886            if let Some(reason) = escape_blocker {
5887                log::warn!(
5888                    "[CERTIFICATE] {context}: negative-curvature saddle escape NOT ATTEMPTED at a \
5889                     first-order-stationary point that is being refused for curvature \
5890                     (hessian_psd={:?}, require_measured_psd={}); blocked by {reason}. The \
5891                     refusal that follows is therefore not evidence that no escape exists — \
5892                     the remedy was never run (#2665).",
5893                    certificate.hessian_psd(),
5894                    config.require_measured_psd,
5895                );
5896            }
5897        }
5898        // #2392 — wrong-rail pull-back and active-set reduction. A coordinate at
5899        // the ρ box whose deep-λ terminal gradient is instrument noise leaves the
5900        // outer search unable to move it: the trust region's local model is flat
5901        // there. Two evidence-gated one-shot reseeds recover the fit (both gated
5902        // by `allow_tail_snap` so the retry pass cannot recurse):
5903        //   (1) WRONG-RAIL PULL-BACK: the coordinate's clean-band probes (a few
5904        //       e-folds inside, above the noise floor) prove the objective
5905        //       DECREASES inward — it was driven to the wrong bound. Reseed it at
5906        //       its clean-band interior scale so the optimizer descends to the
5907        //       true interior optimum. Fires ONLY on the opposite-sign clean-tail
5908        //       proof, so a genuine λ→∞/λ→0 rail is never pulled off its bound.
5909        //   (2) ACTIVE-SET REDUCTION: no wrong rail, but the INTERIOR is not
5910        //       stationary while a rail is present — the railed coordinate's
5911        //       ill-conditioned Hessian row poisons the joint step. Freeze the
5912        //       KKT-ACTIVE rail(s) at their bound and re-run so the interior
5913        //       converges in the reduced space; the plan runner re-certifies the
5914        //       polished point under the ORIGINAL box, so the reduction can
5915        //       never redefine the feasible set.
5916        //
5917        //       "Active" is decided by the PROJECTOR, at freeze time, not by
5918        //       distance to a bound (#2454): a coordinate on its bound whose
5919        //       projected gradient survives still has feasible descent and is
5920        //       left free. This is where the un-freeze has to happen. Doing it
5921        //       after the reduced solve is not available — the retry is one-shot
5922        //       (`allow_tail_snap` is cleared on it, so no second reseed can be
5923        //       published) and the only path back off a frozen bound is the
5924        //       wrong-rail pull-back, which demands a clean opposite-sign
5925        //       exponential tail and declines on any coordinate that has none.
5926        // (1) takes precedence: a wrong rail must be pulled back, never frozen.
5927        if allow_tail_snap && !certificate_railed.is_empty() {
5928            let beta_norm = terminal_beta
5929                .as_ref()
5930                .map(|b| b.dot(b).sqrt())
5931                .filter(|v| v.is_finite())
5932                .unwrap_or(0.0);
5933            let mut rail_tol =
5934                AsymptoteTolerances::exp4_rail_bands(ASYMPTOTE_ESTIMAND_REL_TOL * (1.0 + beta_norm));
5935            rail_tol.tail_drift_rel = TAIL_SNAP_DRIFT_REL;
5936            let (lower, upper) = &bounds;
5937            let mut wrong_rail_point: Option<Array1<f64>> = None;
5938            for &k in certificate_railed.iter() {
5939                if k >= result.rho.len() || k >= lower.len() || k >= upper.len() {
5940                    continue;
5941                }
5942                let side = if (upper[k] - result.rho[k]).abs() <= (result.rho[k] - lower[k]).abs() {
5943                    AsymptoteSide::Upper
5944                } else {
5945                    AsymptoteSide::Lower
5946                };
5947                if let Some(target) = detect_wrong_rail_pullback(
5948                    obj,
5949                    &result.rho,
5950                    k,
5951                    side,
5952                    &rail_tol,
5953                    (lower[k], upper[k]),
5954                )? {
5955                    let mut reseed = result.rho.clone();
5956                    reseed[k] = target;
5957                    wrong_rail_point = Some(reseed);
5958                    break;
5959                }
5960            }
5961            if let Some(reseed) = wrong_rail_point {
5962                result.wrong_rail_reseed = Some(reseed);
5963            } else if let Some(hessian) = result.final_hessian.as_ref() {
5964                // Active-set reduction needs curvature to prove that the free
5965                // subspace is genuinely unpolished. Wrong-rail pull-back above
5966                // is a first-order tail-sign proof and deliberately does NOT:
5967                // gradient-only BFGS objectives can rail incorrectly too, and
5968                // withholding a valid pull-back merely because they do not
5969                // materialize H would make recovery depend on solver class.
5970                let interior_indices =
5971                    interior_face_indices(&projected_gradient, &certificate_railed);
5972                let interior_not_stationary = !interior_indices.is_empty()
5973                    && certify_interior_stationarity(
5974                        &projected_gradient,
5975                        &hessian,
5976                        &interior_indices,
5977                        StationarityBound::from_ladder(stationarity_bound, bound_source),
5978                        asymptote_objective_tol,
5979                    )
5980                    .is_err();
5981                if interior_not_stationary {
5982                    let mut froz_lower = lower.clone();
5983                    let mut froz_upper = upper.clone();
5984                    let mut reseed = result.rho.clone();
5985                    let mut froze_any = false;
5986                    for &k in certificate_railed.iter() {
5987                        if k >= reseed.len() {
5988                            continue;
5989                        }
5990                        // Freeze the KKT-ACTIVE set, not the proximity set
5991                        // (#2454). `certificate_railed` is a DISTANCE test —
5992                        // "within `CERTIFICATE_RAIL_MARGIN` of a bound" — and
5993                        // being near a bound says nothing about whether the
5994                        // constraint is active. The active set is the one the
5995                        // projector already decided: at a bound it keeps only
5996                        // the feasible-descent half, so a coordinate whose
5997                        // projected component survives is one the search can
5998                        // still move INWARD, and `interior_face_indices` (three
5999                        // statements up) has already classified it as interior
6000                        // for exactly that reason.
6001                        //
6002                        // Freezing it anyway pins the coordinate carrying the
6003                        // descent that made `interior_not_stationary` true in
6004                        // the first place, and the reduced solve then converges
6005                        // everything ELSE around it. Measured on #2454's Matérn
6006                        // monotone fixture, whose ψ seed sits on its box edge:
6007                        // ψ is frozen at −2.4849 with `∂V/∂ψ = −4.3164`
6008                        // (FD-confirmed), the three ρ converge to
6009                        // `‖g_ρ‖ = 1.03e-4`, the solver reports
6010                        // `claimed_converged=true` off that reduced norm, and
6011                        // the re-certification under the original box then
6012                        // refuses at `|Pg| = 4.316e0` against a `7.06e-4`
6013                        // bound — with ψ bit-identical to its seed after 26
6014                        // outer iterations, because it was never free to move.
6015                        //
6016                        // The un-freeze the reduction's contract promises ("a
6017                        // frozen coordinate whose gradient turns inward
6018                        // re-certifies under the ORIGINAL box") cannot rescue
6019                        // this: the gradient had ALREADY turned inward when the
6020                        // freeze was taken, so the reduced solve is asked to
6021                        // polish a face that was never active.
6022                        if projected_gradient
6023                            .get(k)
6024                            .is_some_and(|component| *component != 0.0)
6025                        {
6026                            log::info!(
6027                                "[ACTIVE-SET] {context}: coordinate {k} is within the rail \
6028                                 margin of its bound but its projected gradient is \
6029                                 {:.6e} (feasible descent remains), so it is INTERIOR and \
6030                                 stays free rather than being frozen (#2454)",
6031                                projected_gradient[k],
6032                            );
6033                            continue;
6034                        }
6035                        let rail = if (upper[k] - reseed[k]).abs() <= (reseed[k] - lower[k]).abs() {
6036                            upper[k]
6037                        } else {
6038                            lower[k]
6039                        };
6040                        reseed[k] = rail;
6041                        froz_lower[k] = rail;
6042                        froz_upper[k] = rail;
6043                        froze_any = true;
6044                    }
6045                    if froze_any {
6046                        result.active_set_reseed = Some(ActiveSetReseed {
6047                            rho: reseed,
6048                            bounds: (froz_lower, froz_upper),
6049                        });
6050                    }
6051                }
6052            }
6053        }
6054        // Carry the railed-mint and tail-snap decline evidence into the
6055        // refusal so a railed or budget-exhausted crawl explains which
6056        // certificate gate refused instead of failing silently.
6057        let mut summary = certificate.summary();
6058        if !curvature_requirement_met {
6059            // This gate refuses on two OPPOSITE grounds and used to report both
6060            // with one sentence, which reads as an optimizer failure in either
6061            // case (#2641):
6062            //
6063            //   * `hessian_psd=no`  — curvature WAS measured and is indefinite.
6064            //     A verdict about the point. Refusing is correct; #2665 is such
6065            //     a case (λ_min = -1.6e3 against a 1e-5 floor).
6066            //   * `hessian_psd=n/a` — curvature was never measured at all, so
6067            //     there is no eigenvalue and no verdict. Nothing about the point
6068            //     has been established either way.
6069            //
6070            // The second splits again, and one branch is a CONFIGURATION
6071            // CONTRADICTION rather than anything the optimizer did: a caller can
6072            // declare `DeclaredHessianForm::Unavailable` (e.g. a lane that
6073            // deliberately avoids realizing an O(n) second-order slab) while the
6074            // same outer problem sets `require_measured_psd`. That combination
6075            // can never be satisfied by any amount of optimizer work, so say so
6076            // at the refusal instead of sending the reader to the solver.
6077            let detail = match certificate.hessian_psd() {
6078                Some(false) => "this objective requires a positive-semidefinite analytic Hessian \
6079                     at the selected local minimum, and the Hessian measured HERE is indefinite \
6080                     (`hessian_psd=no`) — a curvature verdict about this point, not a missing \
6081                     measurement"
6082                    .to_string(),
6083                _ if !capability.hessian.is_analytic() => format!(
6084                    "this objective requires a MEASURED positive-semidefinite analytic Hessian at \
6085                     the selected local minimum, but the problem declared \
6086                     `{:?}`, so no Hessian was ever evaluated and `hessian_psd` is \
6087                     unavailable. CONFIGURATION CONTRADICTION: the same outer problem both \
6088                     suppressed the analytic Hessian and required a measured one. No optimizer \
6089                     result can satisfy this — fix the construction (declare the Hessian, at \
6090                     least for the terminal certification evaluation) rather than the search",
6091                    capability.hessian
6092                ),
6093                _ => "this objective requires a MEASURED positive-semidefinite analytic Hessian \
6094                     at the selected local minimum. The Hessian is declared available, yet none \
6095                     was materialized at certification, so `hessian_psd` is unavailable and NO \
6096                     curvature verdict has been reached about this point"
6097                    .to_string(),
6098            };
6099            summary = format!("{summary}; {detail}");
6100        }
6101        // `summary()` prints `lambdas_railed`, which is the λ-block report. When
6102        // the face the certificate actually reasoned on is wider — a joint
6103        // [ρ, ψ] search with a κ coordinate on its data-derived window — say so,
6104        // or the refusal reads as `railed=[]` while the projector has already
6105        // discarded that coordinate's outward pull and nothing explains where
6106        // `|g|` went (#979).
6107        if certificate_railed.len() != railed_lambda_block.len() {
6108            summary = format!(
6109                "{summary}; outer coordinates railed (theta-wide, incl. non-rho blocks): \
6110                 {certificate_railed:?}"
6111            );
6112        }
6113        // #2465: `railed=[…]` without its box is unfalsifiable from the run
6114        // record. Every value here is live at the emission site.
6115        if !certificate_railed.is_empty() {
6116            summary = format!(
6117                "{summary}; rail tests: [{}]",
6118                rail_test_summary(&result.rho, &certificate_railed, config)
6119            );
6120        }
6121        // #2465 again, one level up: the `solver provenance` this refusal is
6122        // about to append reports the terminating `|g|` of the run that produced
6123        // `result`. When that run executed under an active-set reduction, its
6124        // box PINNED some coordinates (`lower == upper`) and its `|g|` therefore
6125        // ranges over the FREE ones only, while `|Pg|` above ranges over all of
6126        // θ under the model domain. The two are then not the same quantity, and
6127        // nothing in the string said so: on #2454's Matérn fixture the refusal
6128        // read `claimed_converged=true, gradient_tolerance(|g|=1.029235e-4 <
6129        // 5.487011e-4)` beside `|Pg|=4.316e0`, a four-order gap that is entirely
6130        // the pinned ψ coordinate and looks like a contradiction until the two
6131        // coordinate sets are named. Name them.
6132        if let Some((search_lower, search_upper)) = config.search_bounds_override.as_ref() {
6133            let pinned: Vec<usize> = (0..search_lower.len().min(search_upper.len()))
6134                .filter(|&k| search_lower[k] == search_upper[k])
6135                .collect();
6136            if !pinned.is_empty() {
6137                summary = format!(
6138                    "{summary}; NOTE the run that produced this point searched a REDUCED box \
6139                     with coordinate(s) {pinned:?} pinned (active-set reduction), so the \
6140                     solver-provenance |g| below ranges over the FREE coordinates only and is \
6141                     NOT comparable with the |Pg| above, which ranges over all of theta under \
6142                     the model domain"
6143                );
6144            }
6145        }
6146        if let Some(note) = asymptote_rail_note {
6147            summary = format!("{summary}; asymptote-rail declined: {note}");
6148        }
6149        let summary = match tail_snap_note {
6150            Some(note) => format!("{summary}; tail-snap declined: {note}"),
6151            None => summary,
6152        };
6153        return Err(outer_nonconvergence_error(
6154            context,
6155            &summary,
6156            result,
6157            Some(certified_projected_grad_norm),
6158            StationarityBound::from_ladder(stationarity_bound, bound_source),
6159        ));
6160    }
6161
6162    // #2155 regression, the LAST carrier-stealing path: the rail-mint and
6163    // tail-snap attempts probe with derivative-bearing evaluations, and the
6164    // ORDINARY certificate can still certify after a declined attempt (e.g. a
6165    // KKT-railed projection whose raw gradient norm sits above the bound), so
6166    // this success would ship pre-probe terminal numbers while the evaluator's
6167    // terminal-mode carrier owns the last probe — refusing the bitwise theta
6168    // identity at custom-family fit assembly. Re-own the certified point with
6169    // one fresh evaluation and ship ITS numbers; the mint branches re-own for
6170    // themselves before their early returns, and the judged stationarity facts
6171    // above remain the measured pre-probe ones.
6172    if probes_ran {
6173        let restored = obj
6174            .eval_with_order(&result.rho, OuterEvalOrder::ValueAndGradient)
6175            .map_err(|err| {
6176                EstimationError::RemlOptimizationFailed(format!(
6177                    "{context}: failed to re-own the certified point after                      rail/tail probing: {err}"
6178                ))
6179            })?;
6180        result.final_value = restored.cost;
6181        let restored_projected = project_gradient_vector(
6182            &result.rho,
6183            &gradient_with_rail_barrier_removed(
6184                &result.rho,
6185                &restored.gradient,
6186                &rail_projection_bounds,
6187                rail_barrier_gradient.as_ref(),
6188            ),
6189            Some(&rail_projection_bounds),
6190        );
6191        result.final_grad_norm = Some(
6192            restored_projected
6193                .iter()
6194                .map(|v| v * v)
6195                .sum::<f64>()
6196                .sqrt(),
6197        );
6198        result.final_gradient = Some(restored.gradient);
6199    }
6200    // #2235/#2241 — record WHICH certificate concluded this run. A
6201    // Fellner–Schall model-state fixed point was pre-stamped by the runner and
6202    // is preserved (this analytic certificate is its corroborating evidence);
6203    // otherwise the verdict is decided by which stationarity band the measured
6204    // projected gradient actually cleared: the solver's own tolerance
6205    // (gradient-stationary) or only the widened flat certificate band
6206    // (criterion-flat, #2241).
6207    let via = match result.termination.proposed_via() {
6208        Some(via @ OuterConvergedVia::RecurrentIncumbent { .. }) => via,
6209        _ if certified_projected_grad_norm <= solver_bound => {
6210            OuterConvergedVia::GradientStationary
6211        }
6212        _ => OuterConvergedVia::CriterionFlat {
6213            residual_grad_norm: certified_projected_grad_norm,
6214            certificate_bound: stationarity_bound,
6215        },
6216    };
6217    result.termination.certify(via);
6218    log::info!("[CERTIFICATE] {context}: {}", certificate.summary());
6219    Ok(certificate)
6220}
6221
6222/// Estimand tolerance relative to the fitted coefficient scale for the
6223/// asymptote-rail certificate (#2348 Inc 1): the remaining coefficient travel
6224/// to the rail limit must fall below `ASYMPTOTE_ESTIMAND_REL_TOL·(1 + ‖β‖)` for
6225/// the fitted model to be certified equal to the rail-limit fit.
6226const ASYMPTOTE_ESTIMAND_REL_TOL: f64 = 1.0e-4;
6227
6228/// Number of one-e-fold-in-`ρ` probes stepped back from a railed coordinate
6229/// toward the interior when reconstructing its exponential tail (#2348 Inc 1).
6230/// Enough to span both the finite-difference floor next to the rail (rejected)
6231/// and a confirmable-tail run further in.
6232// 18 e-folds: the window must REACH the finite-difference-clean constant-ĉ
6233// band from a coordinate railed AT the box ceiling. The fused-Hessian
6234// trajectory (#2348) rails fits at ρ=30 that previously stalled mid-box, and
6235// the #2299 fixture's clean band sits 13–16 e-folds inside — the old 12-probe
6236// window (sized for mid-box crawls) stopped one row short of it, so a fully
6237// confirmed tail declined with "no finite-difference-clean tail window". Six
6238// extra value+gradient evals, paid only at certification of railed fits.
6239const ASYMPTOTE_PROBE_COUNT: usize = 18;
6240
6241/// Local confirmation resolution used only when the one-e-fold ladder cannot
6242/// find a clean run. A finite smoothing box can intersect a perfectly regular
6243/// asymptote before three whole e-folds of the leading-order tail are visible;
6244/// half-e-fold probes resolve that band without relaxing any certificate gate.
6245///
6246/// The probes remain equally spaced, which is load-bearing: the estimand
6247/// certificate interprets consecutive coefficient moves as one geometric
6248/// sequence. Six samples cover three e-folds, so the fallback still observes
6249/// curvature over a material interval instead of manufacturing constancy from
6250/// an arbitrarily small neighborhood.
6251const ASYMPTOTE_LOCAL_PROBE_DELTA: f64 = 0.5;
6252const ASYMPTOTE_LOCAL_PROBE_COUNT: usize = 6;
6253
6254/// Read-only inputs to [`try_certify_asymptote_rail`], bundled so the certify
6255/// path passes one borrow rather than a long positional argument list.
6256struct AsymptoteRailInputs<'a> {
6257    rho: &'a Array1<f64>,
6258    projected_gradient: &'a Array1<f64>,
6259    railed: &'a [usize],
6260    /// Which slots of `rho` carry `log λ`. The active-set half of the
6261    /// certificate reads `railed` as-is; the tail law reads it through
6262    /// [`tail_law_coordinates`], which is the whole difference.
6263    layout: OuterThetaLayout,
6264    hessian: &'a Array2<f64>,
6265    bounds: &'a (Array1<f64>, Array1<f64>),
6266    terminal_beta: Option<&'a Array1<f64>>,
6267    /// The ladder bound the full problem was judged against, carrying the rung
6268    /// that set it. A gradient-magnitude rung is already in the exact projected
6269    /// residual currency used on the face. A curvature-derived rung is not:
6270    /// the interior judgment must REPLACE it with its own sub-block curvature
6271    /// bound, and the replacement's rung is what the resulting certificate —
6272    /// or refusal — must report (#2458/#2559).
6273    stationarity_bound: StationarityBound,
6274    /// The run's relative objective tolerance resolved at the certified cost —
6275    /// the same flat-valley floor the cost-stall guard and the curvature-scaled
6276    /// widening use. The tail-snap interior judgment applies the identical
6277    /// Newton-decrement criterion on the interior SUB-BLOCK (the full-Hessian
6278    /// widening is disabled exactly when a noise-corrupted tail entry makes the
6279    /// full matrix non-PD).
6280    objective_tol: f64,
6281    context: &'a str,
6282}
6283
6284impl AsymptoteRailInputs<'_> {
6285    /// Split the railed set into the coordinates the exponential tail law
6286    /// speaks for and the ones it does not (#2453).
6287    ///
6288    /// Both halves stay in the active set: they are equally deleted from the
6289    /// interior gradient and the interior Hessian sub-block, because that
6290    /// reasoning is about a bound and not about a quantity. Only the first
6291    /// half may be *certified* by — or *required* to produce — a tail, since
6292    /// [`OuterThetaLayout::coordinate_is_log_smoothing`] is what makes
6293    /// `ĉ = ∓e^{±ρ}·∂V/∂ρ` a theorem rather than an arithmetic accident.
6294    ///
6295    /// The second half needs no tail: its bound is attainable, so the
6296    /// outward-gradient complementarity the projector already enforces is a
6297    /// complete first-order certificate there.
6298    fn tail_law_coordinates(&self) -> (Vec<usize>, Vec<usize>) {
6299        self.railed
6300            .iter()
6301            .copied()
6302            .partition(|&k| self.layout.coordinate_is_log_smoothing(k))
6303    }
6304}
6305
6306/// Attempt the typed stationary-at-asymptote rail certificate (#2348 Inc 1).
6307///
6308/// Returns `Some((interior_projected_grad_norm, rails))` when the interior
6309/// (non-railed) coordinates are gradient-stationary, the interior Hessian
6310/// sub-block is PSD, and EVERY railed coordinate is certified on a confirmed
6311/// exponential tail whose fitted model has reached the rail limit to within the
6312/// estimand tolerance. Returns `None` (fall through to the generic verdict) on
6313/// any failure — a non-stationary interior, indefinite interior curvature, or
6314/// any railed coordinate whose tail is not confirmable. Never errors on a
6315/// refusal; the only `Err` is a genuinely broken objective that cannot restore
6316/// its inner state to the certified point after probing.
6317fn try_certify_asymptote_rail(
6318    obj: &mut dyn OuterObjective,
6319    inputs: &AsymptoteRailInputs<'_>,
6320) -> Result<Result<(f64, StationarityBound, Vec<RailCoordinate>), String>, EstimationError> {
6321    let rho = inputs.rho;
6322    let projected_gradient = inputs.projected_gradient;
6323    let railed = inputs.railed;
6324    // The interior (non-railed) coordinates must be stationary in their own
6325    // right: the asymptote certificate speaks only to the railed directions,
6326    // never rescues a still-descending interior. Judged by the SAME two-stage
6327    // criterion as the Inc 2c at-point mint: a gradient-magnitude bound may
6328    // judge the exact KKT-projected residual directly; a curvature-derived
6329    // bound is reminted from the interior sub-block before it may admit
6330    // anything. A fit whose remaining interior Newton step would improve the
6331    // cost by less than the loop's own cost resolution is at its interior
6332    // optimum, and the residual gradient is the deep-λ instrument noise floor
6333    // (evaluations beside a saturated rail share the rail's logdet noise).
6334    let interior_indices = interior_face_indices(projected_gradient, railed);
6335    let (interior_projected_grad_norm, effective_interior_bound) =
6336        match certify_interior_stationarity(
6337            projected_gradient,
6338            inputs.hessian,
6339            &interior_indices,
6340            inputs.stationarity_bound,
6341            inputs.objective_tol,
6342        ) {
6343            Ok(certified) => certified,
6344            Err(reason) => return Ok(Err(reason)),
6345        };
6346    // The interior sub-block (railed coordinates removed) must be admissible
6347    // curvature for a minimum. A rail-caused indefiniteness in the saturated
6348    // direction is expected and excluded; genuine interior negative curvature is
6349    // not, and refuses the certificate.
6350    // #2676: read from the objective at THIS point, exactly as the generic
6351    // verdict does. A rail certificate that judged the invariance would decline
6352    // on the same rounding residual the generic path used to refuse on, and the
6353    // two paths would disagree about one matrix.
6354    let criterion_invariance = obj.criterion_invariant_directions(rho);
6355    if certificate_hessian_is_psd_off_railed_above_gradient_floor(
6356        inputs.hessian,
6357        railed,
6358        projected_gradient,
6359        criterion_invariance.as_ref(),
6360    ) != Some(true)
6361    {
6362        return Ok(Err("interior Hessian sub-block is not PSD".to_string()));
6363    }
6364    let beta_norm = inputs
6365        .terminal_beta
6366        .map(|b| b.dot(b).sqrt())
6367        .filter(|v| v.is_finite())
6368        .unwrap_or(0.0);
6369    let estimand_tol = ASYMPTOTE_ESTIMAND_REL_TOL * (1.0 + beta_norm);
6370    let mut tol = AsymptoteTolerances::exp4_rail_bands(estimand_tol);
6371    // Real REML tails hold ĉ to ~5e-3 relative, not the exp4 synthetic
6372    // characterization's 1e-3 (measured on the #2299 fixture during Inc 2c);
6373    // the tail-snap path already certifies against the widened band, and the
6374    // railed mint must judge the SAME physical tail by the same standard.
6375    tol.tail_drift_rel = TAIL_SNAP_DRIFT_REL;
6376    let (lower, upper) = inputs.bounds;
6377
6378    // #2453: only the log-λ coordinates go to the tail law. A ψ rail stays in
6379    // the active set above (it is excluded from the interior gradient and the
6380    // interior sub-block just like any other bound-active coordinate) but is
6381    // certified by complementarity, not by an asymptote it does not have.
6382    let (tail_railed, box_railed) = inputs.tail_law_coordinates();
6383    if tail_railed.is_empty() {
6384        return Ok(Err(format!(
6385            "no railed coordinate parameterizes log λ; {} bound-active ψ coordinate(s) {:?} carry \
6386             no exponential tail to certify",
6387            box_railed.len(),
6388            box_railed,
6389        )));
6390    }
6391    if !box_railed.is_empty() {
6392        log::info!(
6393            "[CERTIFICATE] {}: {} bound-active ψ coordinate(s) {:?} held out of the tail law \
6394             (their box endpoints are attainable, so complementarity certifies them); tail law \
6395             runs on log-λ coordinate(s) {:?}",
6396            inputs.context,
6397            box_railed.len(),
6398            box_railed,
6399            tail_railed,
6400        );
6401    }
6402
6403    // #2348 Inc 5 — the ANALYTIC face proof, first resort.
6404    //
6405    // Measuring a tail beside the ρ box asks the criterion for derivative
6406    // information exactly where its logdet pair cancels; and even when it
6407    // succeeds it speaks for ONE coordinate along ONE ray. The analytic route
6408    // forms the λ=∞ limit itself — the null-space-restricted fit, and the
6409    // exact first-order form of the logdet and trace terms there — and its
6410    // positive definiteness proves the whole face against every way of coming
6411    // off it. When the objective cannot form that limit, or the proof does not
6412    // hold, the measured-tail path below is unchanged.
6413    match try_certify_face_analytically(obj, inputs, &tail_railed, estimand_tol)? {
6414        Ok((rails, proof)) => {
6415            log::info!(
6416                "[CERTIFICATE] {}: analytic λ=∞ face proof on {} coordinate(s): λ_min(C)={:.6e} \
6417                 > margin {:.3e}, joint pencil ĉ={:.6e}, remaining value gap {:.3e}, estimand \
6418                 travel {:.3e}",
6419                inputs.context,
6420                rails.len(),
6421                proof.min_curvature,
6422                proof.curvature_margin,
6423                proof.joint_tail_constant,
6424                proof.value_gap,
6425                proof.estimand_travel,
6426            );
6427            return Ok(Ok((
6428                interior_projected_grad_norm,
6429                effective_interior_bound,
6430                rails,
6431            )));
6432        }
6433        Err(reason) => log::info!(
6434            "[CERTIFICATE] {}: analytic λ=∞ face proof declined ({reason}); measuring the tail \
6435             instead",
6436            inputs.context,
6437        ),
6438    }
6439
6440    let mut rails: Vec<RailCoordinate> = Vec::new();
6441    let mut decline: Option<String> = None;
6442    let mut probed_any = false;
6443    for &k in tail_railed.iter() {
6444        if k >= rho.len() || k >= lower.len() || k >= upper.len() {
6445            decline = Some(format!("railed coordinate {k} outside the box layout"));
6446            break;
6447        }
6448        // Which rail: the box endpoint the coordinate sits nearest. `Upper`
6449        // (λ → ∞) probes step ρ downward into the tail; `Lower` (λ → 0) step up.
6450        let side = if (upper[k] - rho[k]).abs() <= (rho[k] - lower[k]).abs() {
6451            AsymptoteSide::Upper
6452        } else {
6453            AsymptoteSide::Lower
6454        };
6455        probed_any = true;
6456        match build_and_assess_rail_coordinate(obj, rho, k, side, &tol, (lower[k], upper[k]))? {
6457            Ok(rail) => rails.push(rail),
6458            Err(reason) => {
6459                decline = Some(reason);
6460                break;
6461            }
6462        }
6463    }
6464
6465    // The probes warm-started the inner solve away from ρ̂; restore it so the
6466    // shipped fitted state (and the ρ-uncertainty diagnostic) sees the certified
6467    // point. A failure here is a genuinely broken objective, not a refusal.
6468    if probed_any {
6469        obj.eval_cost(rho).map_err(|err| {
6470            EstimationError::RemlOptimizationFailed(format!(
6471                "{}: failed to restore the objective to the certified point after \
6472                 asymptote-rail probing: {err}",
6473                inputs.context
6474            ))
6475        })?;
6476    }
6477
6478    if let Some(reason) = decline {
6479        return Ok(Err(reason));
6480    }
6481    if rails.is_empty() {
6482        return Ok(Err(
6483            "no railed coordinate produced a certifiable tail".to_string()
6484        ));
6485    }
6486    Ok(Ok((
6487        interior_projected_grad_norm,
6488        effective_interior_bound,
6489        rails,
6490    )))
6491}
6492
6493/// The analytic face law is falsified against production criterion VALUES, so
6494/// the admissible discrepancy is exactly the error sources the comparison is
6495/// made of: the run's own cost resolution (absolute, once per evaluation), the
6496/// law's `O(e^{−ρ})` second-order remainder (relative), and the digits the
6497/// Gram/Schur assembly loses. `FACE_LAW_ERROR_SLACK` widens that budget by the
6498/// factor a two-point difference accumulates — each evaluation carries its own
6499/// resolution error and the remainder enters both the predicted and the
6500/// measured side — so a CORRECT law is never refused by its own error bars,
6501/// while a wrong one (which misses by orders of magnitude, not by slack) still
6502/// is. The falsification only runs where the budget leaves a discriminating
6503/// band; where it does not, the analytic route declines rather than minting
6504/// unfalsifiable evidence.
6505const FACE_LAW_ERROR_SLACK: f64 = 4.0;
6506
6507/// Floor on the falsification band.
6508///
6509/// The derived numerical budget can land many orders below the honest fidelity
6510/// of a closed form that deliberately does not model the criterion's
6511/// stabilization ridge or its reparameterization; holding the law to that
6512/// budget would reject a CORRECT law over a difference that changes no
6513/// decision. What the falsification exists to catch is a structurally wrong law
6514/// — a missing Schur term, the wrong dispersion convention, a sign error — and
6515/// those miss by orders of magnitude, not by slack (measured on a fixture whose
6516/// released directions genuinely earned their cost: 100%). Half the predicted
6517/// drop is the coarsest band that still separates those two worlds.
6518const FACE_LAW_ORDER_BAND: f64 = 0.5;
6519
6520/// Prove the rail face analytically (#2348 Inc 5): ask the objective for the
6521/// exact λ→∞ limit, test the first-order form, and falsify the resulting law
6522/// against the production criterion before minting anything from it.
6523///
6524/// Returns the minted rail coordinates plus the proof, or a human-readable
6525/// decline that the caller logs before falling back to the measured tail.
6526fn try_certify_face_analytically(
6527    obj: &mut dyn OuterObjective,
6528    inputs: &AsymptoteRailInputs<'_>,
6529    tail_railed: &[usize],
6530    estimand_tol: f64,
6531) -> Result<Result<(Vec<RailCoordinate>, RailFaceProof), String>, EstimationError> {
6532    let rho = inputs.rho;
6533    let (lower, upper) = inputs.bounds;
6534    // The analytic limit is the INFINITE-smoothing face. A coordinate railed at
6535    // the zero-smoothing bound is the opposite limit (the penalty leaves the
6536    // model rather than pinning it) and belongs to the measured-tail path.
6537    for &k in tail_railed.iter() {
6538        if k >= rho.len() || k >= lower.len() || k >= upper.len() {
6539            return Ok(Err("railed coordinate outside the box layout".to_string()));
6540        }
6541        if (upper[k] - rho[k]).abs() > (rho[k] - lower[k]).abs() {
6542            return Ok(Err(format!(
6543                "coordinate {k} rails at the zero-smoothing bound; the analytic face limit \
6544                 covers λ→∞ only"
6545            )));
6546        }
6547    }
6548    let limit = match obj.rail_face_limit(rho, tail_railed)? {
6549        RailFaceLimitOutcome::Available(limit) => *limit,
6550        // The decline is typed, and the distinction is worth carrying into the
6551        // refusal: "outside this closed form" invites a different one, while
6552        // "the face is unavailable" is a statement about the face.
6553        RailFaceLimitOutcome::OutsideClosedForm { reason } => {
6554            return Ok(Err(format!("outside the analytic closed form: {reason}")));
6555        }
6556        RailFaceLimitOutcome::FaceUnavailable { reason } => {
6557            return Ok(Err(format!("the λ=∞ face is unavailable: {reason}")));
6558        }
6559    };
6560    let proof = match certify_rail_face(&limit) {
6561        RailFaceVerdict::Certified(proof) => proof,
6562        RailFaceVerdict::Refused { reason } => return Ok(Err(reason)),
6563    };
6564    // The face being optimal does not by itself mean the SHIPPED fit is the
6565    // limit fit; the estimand gate is the same one the measured path applies,
6566    // now answered by the exact first-order coefficient offset rather than a
6567    // geometric extrapolation of observed steps.
6568    if !(proof.estimand_travel <= estimand_tol) {
6569        return Ok(Err(format!(
6570            "λ=∞ face proven, but the shipped fit has not reached it: coefficient travel \
6571             {:.3e} > estimand tolerance {estimand_tol:.3e}",
6572            proof.estimand_travel
6573        )));
6574    }
6575    if let Err(reason) = falsify_face_law(obj, inputs, &limit, &proof)? {
6576        return Ok(Err(reason));
6577    }
6578    let rails: Vec<RailCoordinate> = limit
6579        .face
6580        .iter()
6581        .zip(limit.face_rho.iter())
6582        .zip(proof.tail_constants.iter())
6583        .map(|((&index, &rho_k), &tail_constant)| RailCoordinate {
6584            index,
6585            side: AsymptoteSide::Upper,
6586            tail_constant,
6587            // On the tail law the remaining value gap of one coordinate is
6588            // exactly `c_k·e^{−ρ_k}`; a coordinate the rest of the face has
6589            // already pinned reports `c_k = 0` because releasing it does not
6590            // move the criterion at all.
6591            value_gap: tail_constant * (-rho_k).exp(),
6592            estimand_travel_bound: proof.estimand_travel,
6593            // The face was PROVEN, so the standard it cleared is the form's own
6594            // eigen-backward-error margin — not a finite-difference floor, and
6595            // not a quantity comparable with one.
6596            evidence: RailTailEvidence::AnalyticFaceProof {
6597                min_curvature: proof.min_curvature,
6598                curvature_margin: proof.curvature_margin,
6599            },
6600        })
6601        .collect();
6602    Ok(Ok((rails, proof)))
6603}
6604
6605/// Falsify the analytic face law against the production criterion.
6606///
6607/// The law predicts `V(ρ) − V_∞ = ½tr((Σλ_kQᵀS_kQ)⁻¹C)`, so pulling every face
6608/// coordinate back by `Δ` must raise the criterion by exactly
6609/// `gap·(e^{Δ} − 1)`. That is a VALUE comparison — no derivative, no
6610/// cancellation — and it costs two evaluations instead of a probe ladder.
6611///
6612/// `Δ` is not a knob: the measurement error is `resolution/(gap·(e^Δ−1))` and
6613/// the law's own remainder is `O(e^{Δ−ρ})`, so their sum is minimized at
6614/// `Δ* = ½(ln(resolution/gap) + ρ)`, where both equal `√(resolution·e^{−ρ}/gap)`.
6615fn falsify_face_law(
6616    obj: &mut dyn OuterObjective,
6617    inputs: &AsymptoteRailInputs<'_>,
6618    limit: &RailFaceLimit,
6619    proof: &RailFaceProof,
6620) -> Result<Result<(), String>, EstimationError> {
6621    const FACE_LAW_DOMAIN_MARGIN: f64 = 1.0e-6;
6622    let rho = inputs.rho;
6623    let (lower, _) = inputs.bounds;
6624    let gap = proof.value_gap;
6625    let resolution = inputs.objective_tol;
6626    if !(gap > 0.0) || !(resolution > 0.0) {
6627        return Ok(Err(format!(
6628            "the face law carries no resolvable value gap: gap={gap:.3e}, cost resolution \
6629             {resolution:.3e}"
6630        )));
6631    }
6632    let deepest = limit
6633        .face_rho
6634        .iter()
6635        .fold(f64::INFINITY, |acc, v| acc.min(*v));
6636    let ideal = 0.5 * ((resolution / gap).ln() + deepest);
6637    let room = limit
6638        .face
6639        .iter()
6640        .zip(limit.face_rho.iter())
6641        .map(|(&k, &rho_k)| rho_k - lower[k] - FACE_LAW_DOMAIN_MARGIN)
6642        .fold(f64::INFINITY, f64::min);
6643    let delta = ideal.min(room);
6644    // One e-fold is the natural unit of the law being tested; below that the
6645    // predicted change is not a statement about a tail.
6646    if !(delta >= 1.0) || !delta.is_finite() {
6647        return Ok(Err(format!(
6648            "no room inside the box to falsify the face law: Δ={delta:.3e} e-folds"
6649        )));
6650    }
6651    let predicted = gap * (delta.exp() - 1.0);
6652    let measurement_error = resolution / predicted;
6653    let remainder_error = (delta - deepest).exp();
6654    let assembly_error = f64::EPSILON.sqrt() * limit.form_conditioning.sqrt();
6655    let budget = measurement_error + remainder_error + assembly_error;
6656    let admissible = (FACE_LAW_ERROR_SLACK * budget).max(FACE_LAW_ORDER_BAND);
6657    if !(admissible < 1.0) {
6658        return Ok(Err(format!(
6659            "the face law cannot be falsified here: error budget {budget:.3e} (measurement \
6660             {measurement_error:.3e}, remainder {remainder_error:.3e}, assembly \
6661             {assembly_error:.3e}) leaves no discriminating band"
6662        )));
6663    }
6664    let baseline = obj.eval_cost(rho)?;
6665    let mut pulled_back = rho.clone();
6666    for &k in limit.face.iter() {
6667        pulled_back[k] -= delta;
6668    }
6669    let pulled_value = obj.eval_cost(&pulled_back)?;
6670    // Every exit below ships the certified point, so restore it before judging.
6671    obj.eval_cost(rho)?;
6672    if !baseline.is_finite() || !pulled_value.is_finite() {
6673        return Ok(Err(
6674            "the criterion is not finite at the falsification points".to_string()
6675        ));
6676    }
6677    let measured = pulled_value - baseline;
6678    let discrepancy = (predicted - measured).abs() / predicted;
6679    if discrepancy > admissible {
6680        return Ok(Err(format!(
6681            "the analytic face law does not reproduce the criterion: pulling the face back \
6682             {delta:.2} e-folds should raise V by {predicted:.6e}, measured {measured:.6e} \
6683             (relative {discrepancy:.3e} > admissible {admissible:.3e})"
6684        )));
6685    }
6686    Ok(Ok(()))
6687}
6688
6689/// The coordinates a stationarity residual must still account for.
6690///
6691/// A coordinate leaves the interior only when the box has genuinely pinned it:
6692/// railed AND the projection zeroed its gradient, i.e. its entire pull was the
6693/// infeasible KKT multiplier. [`project_gradient_vector`] keeps a near-bound
6694/// coordinate's INWARD component on purpose — that component is feasible
6695/// descent — so dropping the row for *every* railed coordinate discards exactly
6696/// what the projector was written to preserve.
6697///
6698/// Deleting agrees with this only when every railed gradient points strictly
6699/// outward. `matern_nu_sweep_uniform_quality_on_sin1` is the counterexample
6700/// (#2471): coordinate 3 was reported railed while `1.2018e1` of its projected
6701/// gradient survived — 66.7x the stationarity bound, and 99.99% of the reported
6702/// `|Pg|`. With it deleted the interior norm read `1.986e-1`, i.e. 1.10x the
6703/// bound, which reads as "essentially converged" at a point still carrying that
6704/// much feasible descent. The certificate refused anyway, so the number misled
6705/// the reader rather than the verdict — but the ledger built on it classified a
6706/// genuine non-convergence as a railed-coordinate accounting artifact.
6707///
6708/// Since the projection either keeps a component unchanged or zeroes it, the
6709/// norm over these indices is exactly `‖Pg‖`. Where this differs from deleting
6710/// it includes MORE residual coordinates. Curvature evidence is deliberately
6711/// recomputed on that exact set: unlike the residual norm, a Newton decrement
6712/// is not transferable across Hessian subspaces or their regularization scales.
6713pub(crate) fn interior_face_indices(
6714    projected_gradient: &Array1<f64>,
6715    railed: &[usize],
6716) -> Vec<usize> {
6717    (0..projected_gradient.len())
6718        .filter(|k| !railed.contains(k) || projected_gradient[*k] != 0.0)
6719        .collect()
6720}
6721
6722/// Interior stationarity judgment shared by the Inc 1 railed mint and the
6723/// Inc 2c at-point mint (#2348/#2559).
6724///
6725/// The supplied indices make `interior_grad_norm` the exact KKT-projected
6726/// residual of the face being judged: normally [`interior_face_indices`]
6727/// itself, and with already-proven tail coordinates removed on the Inc 2c
6728/// route. A gradient-magnitude ladder rung can therefore judge it directly.
6729/// [`StationarityBoundSource::CurvatureResolvability`] is different: its value
6730/// contains the caller's Hessian and Newton decrement, so reusing it here would
6731/// make the ordinary rail path's early comparison reduce to the caller's own
6732/// `Δpred <= objective_tol` test. It must instead be derived from `sub_h` and
6733/// `sub_g`. Exact zero is stationary without a curvature scale.
6734///
6735/// The face-local curvature path certifies only when the active-face Newton
6736/// step would improve the objective by at most `objective_tol` — the loop's
6737/// own cost resolution. Returns the bound that actually admitted the norm;
6738/// `Err` carries both caller and face-local evidence when descent remains.
6739pub(crate) fn certify_interior_stationarity(
6740    gradient: &Array1<f64>,
6741    hessian: &Array2<f64>,
6742    interior_indices: &[usize],
6743    stationarity_bound: StationarityBound,
6744    objective_tol: f64,
6745) -> Result<(f64, StationarityBound), String> {
6746    let interior_grad_norm = interior_indices
6747        .iter()
6748        .map(|&k| gradient[k] * gradient[k])
6749        .sum::<f64>()
6750        .sqrt();
6751    if interior_grad_norm <= stationarity_bound.value()
6752        && (interior_grad_norm == 0.0
6753            || !stationarity_bound.requires_face_local_derivation())
6754    {
6755        return Ok((interior_grad_norm, stationarity_bound));
6756    }
6757    let m = interior_indices.len();
6758    let mut sub_h = Array2::<f64>::zeros((m, m));
6759    let mut sub_g = Array1::<f64>::zeros(m);
6760    for (i, &ri) in interior_indices.iter().enumerate() {
6761        sub_g[i] = gradient[ri];
6762        for (j, &rj) in interior_indices.iter().enumerate() {
6763            sub_h[[i, j]] = hessian[[ri, rj]];
6764        }
6765    }
6766    match newton_predicted_decrease(&sub_h, &sub_g) {
6767        Some(predicted_decrease) if predicted_decrease.is_finite() && predicted_decrease > 0.0 => {
6768            if predicted_decrease <= objective_tol {
6769                let curvature_grad_bound =
6770                    interior_grad_norm * (objective_tol / predicted_decrease).sqrt();
6771                if curvature_grad_bound.is_finite() && curvature_grad_bound >= interior_grad_norm {
6772                    // The returned bound is no longer the caller's: it is the
6773                    // interior sub-block's own `|Pg_int|·√(τ/Δpred)`. Carrying
6774                    // the caller's rung with it would report a widened bound
6775                    // under the standard that did NOT set it (#2458).
6776                    return Ok((
6777                        interior_grad_norm,
6778                        StationarityBound::from_ladder(
6779                            curvature_grad_bound,
6780                            StationarityBoundSource::CurvatureResolvability,
6781                        ),
6782                    ));
6783                }
6784            }
6785            Err(format!(
6786                "interior not stationary: active-face |Pg|={interior_grad_norm:.3e}, \
6787                 caller bound {:.3e} from {}; active-face Newton decrement \
6788                 {predicted_decrease:.3e} > cost resolution {objective_tol:.3e}",
6789                stationarity_bound.value(),
6790                stationarity_bound.rung().label,
6791            ))
6792        }
6793        _ => Err(format!(
6794            "interior not stationary: active-face |Pg|={interior_grad_norm:.3e}, \
6795             caller bound {:.3e} from {}; the active-face Hessian and residual \
6796             yield no positive finite PD Newton decrement",
6797            stationarity_bound.value(),
6798            stationarity_bound.rung().label,
6799        )),
6800    }
6801}
6802
6803/// Curvature-tie acceptance band for a certify-time tail-snap candidate
6804/// (#2348 Inc 2). On the #2337 Thm 2.1 exponential tail `V = V_∞ + c·e^{∓ρ}`
6805/// the coordinate's own curvature equals its gradient magnitude EXACTLY
6806/// (`H_kk = c·e^{∓ρ} = |g_k|`, unit decay rate in ρ = log λ), so `H_kk/|g_k| ≈ 1`
6807/// is a zero-cost analytic signature separating a live tail crawl from a
6808/// genuinely unconverged curved coordinate before any probe is spent. The band
6809/// tolerates the `O(e^{∓2ρ})` next-order term and assembly round-off; the
6810/// probing confirmation is the rigorous gate.
6811const TAIL_SNAP_CURVATURE_BAND: (f64, f64) = (0.25, 4.0);
6812
6813/// Certify-time tail snap (#2348 Inc 2): when certification is about to refuse
6814/// a point whose gradient residual is carried entirely by coordinates crawling
6815/// an exponential tail TOWARD the ρ-box (the one-e-fold-per-Newton-step grind
6816/// the asymptote certificate exists to kill — the loop can exhaust its budget
6817/// strictly inside the box, where the Inc 1 railed mint can never fire),
6818/// positively confirm each such coordinate's tail from the current point and
6819/// return the point with those coordinates snapped to their box bound as an
6820/// optimization waypoint. The caller re-runs the outer search from that point;
6821/// only the resulting point is eligible for the Inc 1 rail certificate.
6822///
6823/// Refusal semantics mirror [`try_certify_asymptote_rail`]: any gate failure
6824/// returns `Ok(None)` (fall through to the ordinary refusal); the only `Err` is
6825/// a genuinely broken objective that cannot restore its inner state after
6826/// probing. Gates, in order of cost:
6827/// 1. candidate coordinates = un-railed, `|g_k|` above the stationarity bound,
6828///    positive own-curvature within [`TAIL_SNAP_CURVATURE_BAND`] of `|g_k|`
6829///    (the tail-law tie), with the rail side read from the gradient sign;
6830/// 2. the interior Hessian sub-block (railed + candidates excluded) is PSD;
6831/// 3. every candidate's tail is confirmed by the same probing engine the rail
6832///    mint uses (`CertifiedAtAsymptote` or `OnTailNotYetEquivalent`).
6833/// Outcome of a certify-time tail-snap attempt: a point already stationary on
6834/// its confirmed tail, a confirmed-tail waypoint requiring one optimization
6835/// retry (#2348 Inc 2b / #2358), or a human-readable decline reason carried
6836/// into the refusal summary.
6837#[derive(Debug)]
6838enum TailSnapOutcome {
6839    /// Every candidate's confirmed tail EXTRAPOLATES to a gradient already
6840    /// below the stationarity bound at the CURRENT point (#2348 Inc 2c): the
6841    /// coordinate is tail-stationary where it stands, and the measured local
6842    /// gradient is instrument noise (observed on the #2299 fixture: measured
6843    /// |g|=1.04e-2 at ρ=26.56 vs the clean-band extrapolation ĉ·e^{−ρ} ≈
6844    /// 1.9e-8, 100× below the bound). Mint the AsymptoteRail at this point —
6845    /// no snap, no reseed. `interior_projected_grad_norm` and the
6846    /// `effective_interior_bound` that certified it (the raw stationarity
6847    /// bound, or the interior sub-block's curvature-scaled flat-valley bound —
6848    /// the full-Hessian widening is disabled precisely because the noisy tail
6849    /// entry makes the full matrix non-PD) ride along for the certificate.
6850    TailStationaryAtPoint {
6851        rails: Vec<RailCoordinate>,
6852        interior_projected_grad_norm: f64,
6853        effective_interior_bound: StationarityBound,
6854    },
6855    /// Tails confirmed, but the current point is not already
6856    /// tail-stationary. The snapped rail point is only a waypoint: the plan
6857    /// runner must retry ONCE from it so coupled coordinates can reoptimize
6858    /// before certification.
6859    ConfirmedNeedsReseed(Array1<f64>),
6860    Declined(String),
6861}
6862
6863/// Relative drift band for the tail-snap confirmation window, wider than the
6864/// exp4 characterization band (1e-3). The snap's evidentiary strength comes
6865/// from the EXTRAPOLATED-GAP margin, not the band tightness: a 1–2% spread in
6866/// `ĉ` across the clean run moves the extrapolated remaining gradient
6867/// `ĉ·e^{∓ρ}` by the same 1–2%, immaterial against the orders-of-magnitude
6868/// margin the at-point/stationarity decisions demand — while the true tail on
6869/// a REAL fixture still carries visible sub-percent curvature contamination at
6870/// probe depth (measured on #2299: ĉ ∈ {6544, 6565, 6574} over three e-folds,
6871/// drift 4.6e-3, against a wildly swinging noise region above).
6872const TAIL_SNAP_DRIFT_REL: f64 = 1.0e-2;
6873
6874fn try_tail_snap_to_rail(
6875    obj: &mut dyn OuterObjective,
6876    inputs: &AsymptoteRailInputs<'_>,
6877) -> Result<TailSnapOutcome, EstimationError> {
6878    let rho = inputs.rho;
6879    let gradient = inputs.projected_gradient;
6880    let hessian = inputs.hessian;
6881    let (lower, upper) = inputs.bounds;
6882    let n = gradient.len();
6883    if rho.len() != n
6884        || hessian.nrows() != n
6885        || hessian.ncols() != n
6886        || lower.len() < n
6887        || upper.len() < n
6888    {
6889        return Ok(TailSnapOutcome::Declined("shape mismatch".to_string()));
6890    }
6891
6892    let mut candidates: Vec<(usize, AsymptoteSide)> = Vec::new();
6893    let mut rejected: Vec<String> = Vec::new();
6894    for k in 0..n {
6895        if inputs.railed.contains(&k) {
6896            continue;
6897        }
6898        // #2453: snapping a coordinate to its bound and declaring the fit
6899        // finished is an act the tail law authorizes and nothing else does.
6900        // For a ψ coordinate — a curvature, a log length-scale — there is no
6901        // `λ = e^ρ` behind the box, so a gradient pointing at the endpoint is
6902        // just an unfinished search along that quantity, and pinning it there
6903        // would manufacture an optimum out of an arithmetic coincidence.
6904        if !inputs.layout.coordinate_is_log_smoothing(k) {
6905            rejected.push(format!(
6906                "k={k}: psi coordinate (rho_dim={}), no exponential tail law",
6907                inputs.layout.rho_dim()
6908            ));
6909            continue;
6910        }
6911        let g_k = gradient[k];
6912        let side = match AsymptoteSide::from_gradient(g_k, inputs.stationarity_bound.value()) {
6913            Some(side) => side,
6914            None => continue,
6915        };
6916        // A tail candidate must be DEEP toward the bound its gradient points
6917        // at — within the probe span of the box. The tail law is an asymptotic
6918        // statement; a coordinate sitting many probe-spans inside the interior
6919        // (every scripted mock optimum, every ordinary unconverged fit) has no
6920        // asymptote to confirm there, and probing it would spend a dozen
6921        // objective evaluations per would-refuse certification for nothing
6922        // (breaking eval-count-asserting harnesses along the way).
6923        let probe_span = ASYMPTOTE_PROBE_COUNT as f64;
6924        let deep_enough = match side {
6925            AsymptoteSide::Upper => upper[k] - rho[k] <= probe_span,
6926            AsymptoteSide::Lower => rho[k] - lower[k] <= probe_span,
6927        };
6928        if !deep_enough {
6929            rejected.push(format!(
6930                "k={k}: ρ={:.2} more than {probe_span:.0} e-folds inside the box",
6931                rho[k]
6932            ));
6933            continue;
6934        }
6935        let h_kk = hessian[[k, k]];
6936        // The tie is judged on |H_kk|/|g_k| — MAGNITUDE only. On the exact
6937        // tail `H_kk = |g_k|` (positive), but the assembled ρ-Hessian's tail
6938        // entry is `λV_λ + λ²V_λλ`, and when the `λ²V_λλ` trace pair cancels
6939        // to roundoff in the deep-smoothing regime (the #2298 rail-cancellation
6940        // class), what survives is `λV_λ = g_k` — magnitude right, SIGN
6941        // flipped (measured on the #2299 fixture: g=-1.040e-2, H_kk=-1.018e-2,
6942        // ratio -0.979). The sign at the tail is exactly the corrupted datum,
6943        // so it cannot gate; the probing confirmation is the rigorous test.
6944        let ratio = h_kk.abs() / g_k.abs();
6945        if !(TAIL_SNAP_CURVATURE_BAND.0..=TAIL_SNAP_CURVATURE_BAND.1).contains(&ratio) {
6946            rejected.push(format!(
6947                "k={k}: g={g_k:.3e} H_kk={h_kk:.3e} |ratio|={ratio:.3e} outside tie band"
6948            ));
6949            continue;
6950        }
6951        candidates.push((k, side));
6952    }
6953    if candidates.is_empty() {
6954        return Ok(TailSnapOutcome::Declined(if rejected.is_empty() {
6955            "no super-bound coordinate".to_string()
6956        } else {
6957            format!(
6958                "no candidate passed the curvature tie ({})",
6959                rejected.join("; ")
6960            )
6961        }));
6962    }
6963
6964    // The curvature left after excluding the railed + candidate directions
6965    // must be admissible for a minimum; a genuinely indefinite interior
6966    // refuses before any probe is spent.
6967    let excluded: Vec<usize> = inputs
6968        .railed
6969        .iter()
6970        .copied()
6971        .chain(candidates.iter().map(|(k, _)| *k))
6972        .collect();
6973    let criterion_invariance = obj.criterion_invariant_directions(rho);
6974    if certificate_hessian_is_psd_off_railed_above_gradient_floor(
6975        hessian,
6976        &excluded,
6977        gradient,
6978        criterion_invariance.as_ref(),
6979    ) != Some(true)
6980    {
6981        return Ok(TailSnapOutcome::Declined(
6982            "interior Hessian sub-block not PSD".to_string(),
6983        ));
6984    }
6985
6986    let beta_norm = inputs
6987        .terminal_beta
6988        .map(|b| b.dot(b).sqrt())
6989        .filter(|v| v.is_finite())
6990        .unwrap_or(0.0);
6991    let mut tol =
6992        AsymptoteTolerances::exp4_rail_bands(ASYMPTOTE_ESTIMAND_REL_TOL * (1.0 + beta_norm));
6993    tol.tail_drift_rel = TAIL_SNAP_DRIFT_REL;
6994    let mut decline: Option<String> = None;
6995    // Rails for candidates whose confirmed tail extrapolates to an
6996    // already-below-bound gradient at the CURRENT point; when every candidate
6997    // qualifies, the point is minted where it stands (#2348 Inc 2c).
6998    let mut at_point_rails: Vec<RailCoordinate> = Vec::new();
6999    for (k, side) in &candidates {
7000        let verdict = match probe_tail_window(obj, rho, *k, *side, &tol, (lower[*k], upper[*k]))? {
7001            (Some(window), rows) => match assess_coordinate(&window, &tol) {
7002                AsymptoteVerdict::CertifiedAtAsymptote {
7003                    side: assessed_side,
7004                    tail_constant,
7005                    estimand_travel_bound,
7006                    ..
7007                } => {
7008                    // Extrapolate the confirmed tail law to the current point:
7009                    // the TRUE remaining gradient there, immune to the local
7010                    // instrument noise the certificate measured.
7011                    let extrapolated_gap = match assessed_side {
7012                        AsymptoteSide::Upper => tail_constant * (-rho[*k]).exp(),
7013                        AsymptoteSide::Lower => tail_constant * rho[*k].exp(),
7014                    };
7015                    if extrapolated_gap.is_finite()
7016                        && extrapolated_gap <= inputs.stationarity_bound.value()
7017                    {
7018                        at_point_rails.push(RailCoordinate {
7019                            index: *k,
7020                            side: assessed_side,
7021                            tail_constant,
7022                            value_gap: extrapolated_gap,
7023                            estimand_travel_bound,
7024                            evidence: RailTailEvidence::ProbedTail {
7025                                noise_floor: tol.tail_noise_floor,
7026                                drift_band: tol.tail_drift_rel,
7027                            },
7028                        });
7029                    }
7030                    None
7031                }
7032                AsymptoteVerdict::OnTailNotYetEquivalent { .. } => None,
7033                AsymptoteVerdict::NoAsymptote { reason } => {
7034                    Some(format!("{reason}; probes: {rows}"))
7035                }
7036            },
7037            (None, rows) => Some(format!(
7038                "no finite-difference-clean tail run; probes: {rows}"
7039            )),
7040        };
7041        if let Some(reason) = verdict {
7042            decline = Some(format!("candidate k={k} tail unconfirmed: {reason}"));
7043            break;
7044        }
7045    }
7046    // #2349 round 7: a multi-coordinate rail face. When a candidate's OWN
7047    // one-dimensional tail law fails and several candidates ride out together,
7048    // the marginal law is the wrong object — overlapping penalties share range
7049    // space, so a lone coordinate's gradient saturates once the others
7050    // dominate the shared term (the measured #2349 ladder swept ĉ₀ across 8
7051    // orders of magnitude). The scalar section along the joint face direction
7052    // has the ordinary exponential tail; certify THAT with the same
7053    // discipline, and mint every face coordinate from the joint law.
7054    // A face confirmed through the JOINT fallback snaps as a WAYPOINT, never a
7055    // candidate optimum: the joint law certifies the direction of the optimum,
7056    // but individual face coordinates can hold interior optima once the others
7057    // sit railed (measured on the #2349 fixture: after snapping the 5-face,
7058    // coordinate 0's own gradient crossed zero near ρ₀ ≈ 7.5 — 4.5 e-folds
7059    // inside its snapped rail — while V dropped 3.86 from the checkpoint). The
7060    // reseed retry re-descends from the snapped point with the rails free to
7061    // hold or relax; a direct re-certification there would refuse exactly that
7062    // relaxation.
7063    if decline.is_some() && candidates.len() >= 2 {
7064        let (window, joint_rows) =
7065            probe_joint_tail_window(obj, rho, &candidates, &tol, (lower, upper))?;
7066        match window.as_ref().map(|w| assess_coordinate(w, &tol)) {
7067            Some(AsymptoteVerdict::CertifiedAtAsymptote {
7068                tail_constant,
7069                estimand_travel_bound,
7070                ..
7071            }) => {
7072                // Extrapolate the joint law back to the checkpoint: the true
7073                // remaining directional gradient there, immune to the local
7074                // instrument noise. All face gradients share one sign
7075                // structure along the face, so the joint gap bounds each
7076                // coordinate's own remaining gradient.
7077                let r0 = candidates
7078                    .iter()
7079                    .map(|(k, side)| match side {
7080                        AsymptoteSide::Upper => rho[*k],
7081                        AsymptoteSide::Lower => -rho[*k],
7082                    })
7083                    .sum::<f64>()
7084                    / candidates.len() as f64;
7085                let joint_gap = tail_constant * (-r0).exp();
7086                if joint_gap.is_finite() && joint_gap <= inputs.stationarity_bound.value() {
7087                    at_point_rails = candidates
7088                        .iter()
7089                        .map(|(k, side)| RailCoordinate {
7090                            index: *k,
7091                            side: *side,
7092                            tail_constant,
7093                            value_gap: joint_gap,
7094                            estimand_travel_bound,
7095                            evidence: RailTailEvidence::ProbedTail {
7096                                noise_floor: tol.tail_noise_floor,
7097                                drift_band: tol.tail_drift_rel,
7098                            },
7099                        })
7100                        .collect();
7101                }
7102                decline = None;
7103            }
7104            Some(AsymptoteVerdict::OnTailNotYetEquivalent { .. }) => {
7105                // Confirmed on the joint tail; travel not yet settled — the
7106                // face snaps/reseeds below exactly as a confirmed single
7107                // candidate would.
7108                decline = None;
7109            }
7110            Some(AsymptoteVerdict::NoAsymptote { reason }) => {
7111                // A returned window IS the law: it exists only when a
7112                // drift-band-clean, above-noise-floor, uniformly-positive
7113                // pencil-constant run of MIN_TAIL_SAMPLES was found, so the
7114                // only `NoAsymptote` reachable from it is the estimand
7115                // contraction gate — the β-steps in the retained (deep
7116                // interior) rows still move, i.e. the checkpoint is genuinely
7117                // NOT at the face limit yet (measured on the #2349 checkpoint:
7118                // ĉ settled to 34.2 over the last four probes while the crawl
7119                // was still travelling). That is the same state as
7120                // `OnTailNotYetEquivalent`: the law says WHERE the optimum is;
7121                // the snap below reoptimizes from the face, granting nothing
7122                // by itself.
7123                log::info!(
7124                    "[CERTIFICATE] joint {}-coordinate face: pencil-constant run \
7125                     confirmed but estimand not settled at the checkpoint \
7126                     ({reason}); snapping the face for re-optimization",
7127                    candidates.len(),
7128                );
7129                decline = None;
7130            }
7131            None => {
7132                decline = Some(format!(
7133                    "{}; joint {}-coordinate face: no finite-difference-clean run; joint probes: {joint_rows}",
7134                    decline.take().unwrap_or_default(),
7135                    candidates.len(),
7136                ));
7137            }
7138        }
7139    }
7140    // The probes warm-started the inner solve away from the checkpoint; every
7141    // exit below leaves the CURRENT point as the shipped state, so restore it
7142    // before returning. A failure here is a genuinely broken objective.
7143    obj.eval_cost(rho).map_err(|err| {
7144        EstimationError::RemlOptimizationFailed(format!(
7145            "{}: failed to restore the objective to the certified point after \
7146             tail-snap probing: {err}",
7147            inputs.context
7148        ))
7149    })?;
7150    if let Some(reason) = decline {
7151        return Ok(TailSnapOutcome::Declined(reason));
7152    }
7153
7154    let interior_indices: Vec<usize> = interior_face_indices(gradient, inputs.railed)
7155        .into_iter()
7156        .filter(|k| !candidates.iter().any(|(c, _)| c == k))
7157        .collect();
7158    // #2348 Inc 2c: every candidate's confirmed tail already extrapolates
7159    // BELOW the stationarity bound at the current point — the fit is
7160    // tail-stationary where it stands and the measured local gradient is
7161    // instrument noise. Judge the interior with the shared two-stage
7162    // criterion (`certify_interior_stationarity`): the raw bound, then the
7163    // curvature-scaled flat-valley bound on the interior SUB-BLOCK (the
7164    // full-Hessian widening is unavailable here exactly because the
7165    // noise-corrupted tail entry makes the full matrix non-PD).
7166    if at_point_rails.len() == candidates.len() {
7167        if let Ok((interior_projected_grad_norm, effective_interior_bound)) =
7168            certify_interior_stationarity(
7169                gradient,
7170                hessian,
7171                &interior_indices,
7172                inputs.stationarity_bound,
7173                inputs.objective_tol,
7174            )
7175        {
7176            return Ok(TailSnapOutcome::TailStationaryAtPoint {
7177                rails: at_point_rails,
7178                interior_projected_grad_norm,
7179                effective_interior_bound,
7180            });
7181        }
7182        // Real interior descent remains: fall through to the reseed path so
7183        // one more optimizer pass polishes it.
7184    }
7185
7186    let mut snapped = rho.clone();
7187    for (k, side) in &candidates {
7188        snapped[*k] = match side {
7189            AsymptoteSide::Upper => upper[*k],
7190            AsymptoteSide::Lower => lower[*k],
7191        };
7192    }
7193
7194    // Tails confirmed, but a finite snap can change every coupled coordinate's
7195    // optimum even when its PRE-snap gradient was stationary (#2358 measured
7196    // the location-scale interior gradient jumping to 0.5). The snap proves a
7197    // direction and supplies a waypoint; only a resumed optimization and its
7198    // subsequent certificate can prove the endpoint.
7199    Ok(TailSnapOutcome::ConfirmedNeedsReseed(snapped))
7200}
7201
7202/// Reconstruct one railed coordinate's exponential tail by probing the analytic
7203/// gradient back from the rail at coarse and, when needed, local resolution;
7204/// locate the longest finite-difference-clean run (rejecting the noise floor
7205/// adjacent to the rail); and assess it against the tail law (#2348 Inc 1 /
7206/// #2337 Thm 2.1). Returns the certified [`RailCoordinate`] or `None` if no
7207/// confirmable tail is found.
7208fn build_and_assess_rail_coordinate(
7209    obj: &mut dyn OuterObjective,
7210    rho: &Array1<f64>,
7211    coord: usize,
7212    side: AsymptoteSide,
7213    tol: &AsymptoteTolerances,
7214    domain: (f64, f64),
7215) -> Result<Result<RailCoordinate, String>, EstimationError> {
7216    let window = match probe_tail_window(obj, rho, coord, side, tol, domain)? {
7217        (Some(window), _) => window,
7218        (None, rows) => {
7219            // #2450: name the floor instead of leaving the reader to find it.
7220            // The criterion ALWAYS carries the soft rho-guard barrier
7221            // (`soft_rho_guard_prior_atom`), and a `log cosh` barrier's gradient
7222            // SATURATES to `w*a` rather than decaying, so once the REML tail
7223            // falls below it, `c_hat = -e^rho * dV/drho` diverges and no tail is
7224            // observable THROUGH the guard at any box width or probe count. That
7225            // is a different failure from a noisy or truncated window, and
7226            // reporting both as "no finite-difference-clean tail window" cost
7227            // real time: it reads as a fixture/conditioning problem when it is a
7228            // property of the objective. Measured: the floor is exactly
7229            // `w*a*tanh(a*rho)` = 1.3324e-7 at rho=30 against a predicted
7230            // 1.3324e-7 (five significant figures, every coordinate).
7231            let guard_floor = crate::estimate::RHO_SOFT_PRIOR_WEIGHT
7232                * (crate::estimate::RHO_SOFT_PRIOR_SHARPNESS / crate::estimate::RHO_BOUND);
7233            // #2545: say WHICH of the two failures this is. The probe ladder now
7234            // subtracts the barrier when the objective publishes it, so a
7235            // refusal from a publishing objective is genuinely about the window;
7236            // a refusal from a non-publishing one may still be the barrier.
7237            let barrier_note = match obj
7238                .soft_rho_guard_gradient(rho)
7239                .and_then(|guard| guard.get(coord).copied())
7240            {
7241                Some(value) => format!(
7242                    "this objective PUBLISHES its soft rho-guard barrier gradient \
7243                     ({value:.4e} here, saturating at w*a={guard_floor:.4e} instead of \
7244                     decaying) and every probe below already has it SUBTRACTED (#2545), \
7245                     so the window is what failed, not the barrier"
7246                ),
7247                None => format!(
7248                    "this objective publishes NO soft rho-guard barrier gradient, so if \
7249                     its criterion carries the barrier the probes below still include it; \
7250                     that gradient saturates at w*a={guard_floor:.4e} rather than decaying, \
7251                     and any tail whose |dV/drho| is at or below that is unobservable \
7252                     THROUGH the barrier rather than merely unclean (#2450/#2545)"
7253                ),
7254            };
7255            return Ok(Err(format!(
7256                "k={coord}: no finite-difference-clean tail window; {barrier_note}; \
7257                 probes {rows}"
7258            )));
7259        }
7260    };
7261    match assess_coordinate(&window, tol) {
7262        AsymptoteVerdict::CertifiedAtAsymptote {
7263            side,
7264            tail_constant,
7265            value_gap,
7266            estimand_travel_bound,
7267        } => Ok(Ok(RailCoordinate {
7268            index: coord,
7269            side,
7270            tail_constant,
7271            value_gap,
7272            estimand_travel_bound,
7273            evidence: RailTailEvidence::ProbedTail {
7274                noise_floor: tol.tail_noise_floor,
7275                drift_band: tol.tail_drift_rel,
7276            },
7277        })),
7278        other => Ok(Err(format!("k={coord}: tail verdict {other:?}"))),
7279    }
7280}
7281
7282/// Detect a WRONG-RAIL coordinate (#2392): one sitting AT its ρ box bound whose
7283/// clean-band probes prove the objective strictly DECREASES as the coordinate
7284/// moves INWARD — the outer search drove it to the wrong bound. Returns the
7285/// interior ρ to reseed the coordinate at (the deepest drift-clean probe, where
7286/// `|g|` is largest and the descent is most informative) when the proof holds,
7287/// else `None`.
7288///
7289/// # Proof condition (evidence-gated; cannot launder a genuine λ→∞ / λ→0 optimum)
7290///
7291/// Probe up to [`ASYMPTOTE_PROBE_COUNT`] e-folds inward and let the FIRST
7292/// contiguous clean, drift-stable run of at least [`MIN_TAIL_SAMPLES`] decide
7293/// the local rail:
7294/// 1. above the gradient interior floor, `|g| > interior_grad_tol` (so a probe
7295///    whose gradient has decayed into finite-difference cancellation next to the
7296///    rail is excluded rather than read as a settled tail);
7297/// 2. above the pencil-constant noise floor, `|ĉ| > tail_noise_floor`, where
7298///    `ĉ = side.tail_constant(ρ, g)` uses the coordinate's ACTUAL rail side;
7299/// 3. drift-band-clean in `ĉ` within `tail_drift_rel` (the same constant-pencil
7300///    band the genuine tail uses — `run_drift_within_band` keys on `|mean|`, so a
7301///    uniformly-negative run is judged on its magnitude).
7302///
7303/// A first clean run with `ĉ < 0` proves descent AWAY from the bound and returns
7304/// its deepest point. A first clean run with `ĉ > 0` proves descent TOWARD the
7305/// bound and refuses the pull-back immediately. Deciding on the first clean run
7306/// is load-bearing: the question is the LOCAL orientation of the objective at
7307/// this rail. Continuing another fifteen expensive objective evaluations after
7308/// that proof could discover a remote sign reversal in the interior, but must
7309/// not use it to relabel a locally genuine bound as a wrong rail.
7310fn detect_wrong_rail_pullback(
7311    obj: &mut dyn OuterObjective,
7312    rho: &Array1<f64>,
7313    coord: usize,
7314    side: AsymptoteSide,
7315    tol: &AsymptoteTolerances,
7316    domain: (f64, f64),
7317) -> Result<Option<f64>, EstimationError> {
7318    const PROBE_DELTA: f64 = 1.0;
7319    const PROBE_DOMAIN_MARGIN: f64 = 1.0e-6;
7320    // Upper rail (ρ → +∞): step ρ DOWN into the interior. Lower rail: step UP.
7321    let sign = match side {
7322        AsymptoteSide::Upper => -1.0,
7323        AsymptoteSide::Lower => 1.0,
7324    };
7325    // The closest finite-difference-clean constant-pencil run is the local rail
7326    // evidence. Noise rows reset the run; a sign change starts a new candidate.
7327    let mut run_sign = 0_i8;
7328    let mut run_constants: Vec<f64> = Vec::with_capacity(MIN_TAIL_SAMPLES);
7329    for j in 1..=ASYMPTOTE_PROBE_COUNT {
7330        let stepped = rho[coord] + sign * (j as f64) * PROBE_DELTA;
7331        if stepped <= domain.0 + PROBE_DOMAIN_MARGIN || stepped >= domain.1 - PROBE_DOMAIN_MARGIN {
7332            break;
7333        }
7334        let mut probe = rho.clone();
7335        probe[coord] = stepped;
7336        let eval = match obj.eval_with_order(&probe, OuterEvalOrder::ValueAndGradient) {
7337            Ok(eval) => eval,
7338            Err(_) => break,
7339        };
7340        if !eval.cost.is_finite()
7341            || coord >= eval.gradient.len()
7342            || !eval.gradient[coord].is_finite()
7343        {
7344            break;
7345        }
7346        let gradient = eval.gradient[coord];
7347        let constant = side.tail_constant(stepped, gradient);
7348        let clean = constant.is_finite()
7349            && constant.abs() > tol.tail_noise_floor
7350            && gradient.abs() > tol.interior_grad_tol;
7351        if !clean {
7352            run_sign = 0;
7353            run_constants.clear();
7354            continue;
7355        }
7356        let constant_sign = if constant < 0.0 { -1 } else { 1 };
7357        if constant_sign != run_sign {
7358            run_sign = constant_sign;
7359            run_constants.clear();
7360        }
7361        run_constants.push(constant);
7362        if run_constants.len() > MIN_TAIL_SAMPLES {
7363            run_constants.remove(0);
7364        }
7365        if run_constants.len() >= MIN_TAIL_SAMPLES
7366            && run_drift_within_band(&run_constants, tol.tail_drift_rel)
7367        {
7368            return if run_sign < 0 {
7369                Ok(Some(stepped))
7370            } else {
7371                Ok(None)
7372            };
7373        }
7374    }
7375    Ok(None)
7376}
7377
7378/// Probe one coordinate's tail toward the interior (the shared probing engine
7379/// of [`build_and_assess_rail_coordinate`] and the certify-time tail snap).
7380/// The one-e-fold ladder runs first; if it finds no clean run, a short
7381/// half-e-fold ladder resolves a narrower local band without mixing step sizes
7382/// in one estimand window. Returns the longest finite-difference-clean
7383/// constant-`ĉ` run (newest sample nearest `rho[coord]`), or `None` when neither
7384/// resolution contains at least [`MIN_TAIL_SAMPLES`] clean rows. The second
7385/// element dumps `(ρ, ∂V/∂ρ, ĉ)` evidence for every attempted resolution.
7386fn probe_tail_window(
7387    obj: &mut dyn OuterObjective,
7388    rho: &Array1<f64>,
7389    coord: usize,
7390    side: AsymptoteSide,
7391    tol: &AsymptoteTolerances,
7392    domain: (f64, f64),
7393) -> Result<(Option<AsymptoteWindow>, String), EstimationError> {
7394    let (coarse_window, coarse_rows) = probe_tail_window_at_resolution(
7395        obj,
7396        rho,
7397        coord,
7398        side,
7399        tol,
7400        domain,
7401        (1.0, ASYMPTOTE_PROBE_COUNT),
7402    )?;
7403    if coarse_window.is_some() {
7404        return Ok((coarse_window, coarse_rows));
7405    }
7406
7407    // The coarse ladder is deliberately retained as the first pass: railed
7408    // dense REML fits can have several e-folds of cancellation noise beside
7409    // the box followed by a clean band far inside it. The local pass addresses
7410    // the complementary geometry exposed by #2358, where a modest finite box
7411    // contains a narrow but regular tail and unit steps skip over it.
7412    let (local_window, local_rows) = probe_tail_window_at_resolution(
7413        obj,
7414        rho,
7415        coord,
7416        side,
7417        tol,
7418        domain,
7419        (
7420            ASYMPTOTE_LOCAL_PROBE_DELTA,
7421            ASYMPTOTE_LOCAL_PROBE_COUNT,
7422        ),
7423    )?;
7424    Ok((
7425        local_window,
7426        format!("coarse[{coarse_rows}] local[{local_rows}]"),
7427    ))
7428}
7429
7430/// Probe a single equally-spaced resolution of one coordinate's tail.
7431///
7432/// Keeping each returned window at one resolution is essential for
7433/// [`assess_coordinate`]: its coefficient-travel bound estimates a geometric
7434/// ratio from consecutive steps, which is only meaningful when their `Δρ`
7435/// values are identical.
7436fn probe_tail_window_at_resolution(
7437    obj: &mut dyn OuterObjective,
7438    rho: &Array1<f64>,
7439    coord: usize,
7440    side: AsymptoteSide,
7441    tol: &AsymptoteTolerances,
7442    domain: (f64, f64),
7443    resolution: (f64, usize),
7444) -> Result<(Option<AsymptoteWindow>, String), EstimationError> {
7445    let (probe_delta, probe_count) = resolution;
7446    // Strictly-inside guard for probes against the probed coordinate's own box
7447    // interval (#2388). The ρ-gradient assembly freezes any coordinate at (or
7448    // within 1e-8 of) its recorded upper bound to the #197 KKT projection — a
7449    // literal 0.0 — so a probe at or past a box bound samples the frozen-axis
7450    // convention, not the criterion's tail: a fabricated hard-zero tail that
7451    // the drift band can never confirm. Out-of-box points are outside the
7452    // λ-selection domain altogether; they are not evidence for or against a
7453    // tail, so the ladder stops at the last strictly-in-domain probe.
7454    const PROBE_DOMAIN_MARGIN: f64 = 1.0e-6;
7455    // Upper rail (ρ → +∞): step ρ DOWN into the tail. Lower rail: step UP.
7456    let sign = match side {
7457        AsymptoteSide::Upper => -1.0,
7458        AsymptoteSide::Lower => 1.0,
7459    };
7460    // rows[r] corresponds to probe j=r+1: r=0 is the point CLOSEST to the rail,
7461    // increasing r steps further into the interior (larger |grad|).
7462    let mut rows: Vec<(f64, f64, Option<Array1<f64>>)> = Vec::new();
7463    for j in 1..=probe_count {
7464        let stepped = rho[coord] + sign * (j as f64) * probe_delta;
7465        if stepped <= domain.0 + PROBE_DOMAIN_MARGIN || stepped >= domain.1 - PROBE_DOMAIN_MARGIN {
7466            break;
7467        }
7468        let mut probe = rho.clone();
7469        probe[coord] = stepped;
7470        let eval = match obj.eval_with_order(&probe, OuterEvalOrder::ValueAndGradient) {
7471            Ok(eval) => eval,
7472            // A failed probe is not evidence against a tail; stop probing and
7473            // assess whatever clean run the earlier probes established.
7474            Err(_) => break,
7475        };
7476        if !eval.cost.is_finite()
7477            || coord >= eval.gradient.len()
7478            || !eval.gradient[coord].is_finite()
7479        {
7480            break;
7481        }
7482        // #2545: the tail law `ĉ = −e^ρ·∂V/∂ρ` is a statement about the
7483        // CRITERION's λ→∞ face. The objective may also carry an unconditional
7484        // `log cosh` numerical barrier whose gradient SATURATES at `w·a` rather
7485        // than decaying (`RHO_SOFT_PRIOR_*`, 1.3333e-7 at `RHO_BOUND = 30`), so
7486        // once the face's own `c·e^{−ρ}` falls below it the measured ĉ diverges
7487        // and NO coordinate can be certified at an asymptote — measured on the
7488        // #2450 fixture, the barrier is 99.999% of the ρ-gradient at ρ=30
7489        // (1.332439e-7 of 1.332521e-7) and the tail underneath it is a clean
7490        // `87.51·e^{−ρ}`. Subtract the barrier's own gradient, published by the
7491        // objective from the SAME atom that ADDED it (so it cannot drift, and so
7492        // it carries the weight anchor `ρ̃ = ρ − log g(w)` that a re-derived
7493        // `w·a·tanh(a·ρ)` would silently drop on every weighted fit). Objectives
7494        // that publish no barrier are unaffected: the subtrahend is 0.
7495        let barrier = obj
7496            .soft_rho_guard_gradient(&probe)
7497            .and_then(|guard| guard.get(coord).copied())
7498            .filter(|value| value.is_finite())
7499            .unwrap_or(0.0);
7500        rows.push((
7501            probe[coord],
7502            eval.gradient[coord] - barrier,
7503            eval.inner_beta_hint,
7504        ));
7505    }
7506    let rows_summary = rows
7507        .iter()
7508        .map(|(r, g, _)| {
7509            format!(
7510                "(ρ={r:.2}, g={g:.3e}, ĉ={:.3e})",
7511                side.tail_constant(*r, *g)
7512            )
7513        })
7514        .collect::<Vec<_>>()
7515        .join(" ");
7516    if rows.len() < MIN_TAIL_SAMPLES {
7517        return Ok((None, rows_summary));
7518    }
7519
7520    // Per-row pencil constant ĉ and the element-clean predicate: ĉ above the
7521    // noise floor AND the gradient above the interior floor (so a row adjacent to
7522    // the rail, whose gradient has decayed into finite-difference cancellation, is
7523    // excluded rather than mistaken for a settled tail).
7524    let constants: Vec<f64> = rows
7525        .iter()
7526        .map(|(r, g, _)| side.tail_constant(*r, *g))
7527        .collect();
7528    let element_clean: Vec<bool> = rows
7529        .iter()
7530        .zip(&constants)
7531        .map(|((_, g, _), c)| {
7532            c.is_finite() && *c > tol.tail_noise_floor && g.abs() > tol.interior_grad_tol
7533        })
7534        .collect();
7535
7536    // Longest contiguous run that is element-clean AND holds ĉ within the drift
7537    // band; ties broken toward the rail (smallest start) for the most settled
7538    // estimand.
7539    let mut best: Option<(usize, usize)> = None;
7540    for a in 0..rows.len() {
7541        if !element_clean[a] {
7542            continue;
7543        }
7544        for b in a..rows.len() {
7545            if !element_clean[b] {
7546                break;
7547            }
7548            if b - a + 1 < MIN_TAIL_SAMPLES {
7549                continue;
7550            }
7551            if !run_drift_within_band(&constants[a..=b], tol.tail_drift_rel) {
7552                continue;
7553            }
7554            let len = b - a + 1;
7555            match best {
7556                Some((ba, bb)) if bb - ba + 1 >= len => {}
7557                _ => best = Some((a, b)),
7558            }
7559        }
7560    }
7561    let (a, b) = match best {
7562        Some(run) => run,
7563        None => return Ok((None, rows_summary)),
7564    };
7565
7566    // Build the window oldest → newest: newest (window `latest`) is the row
7567    // CLOSEST to the rail (r=a). A sample's coefficient move is ‖β(r) − β(r+1)‖,
7568    // the step from the next-farther retained row toward the rail.
7569    let mut window = AsymptoteWindow::with_capacity(b - a + 1);
7570    for r in (a..=b).rev() {
7571        let (rho_r, grad_r, beta_r) = &rows[r];
7572        let coef_step_norm = match (beta_r, rows.get(r + 1).map(|row| &row.2)) {
7573            (Some(cur), Some(Some(farther))) if cur.len() == farther.len() => {
7574                (cur - farther).iter().map(|v| v * v).sum::<f64>().sqrt()
7575            }
7576            _ => 0.0,
7577        };
7578        window.push(AsymptoteSample {
7579            rho: *rho_r,
7580            grad: *grad_r,
7581            coef_step_norm,
7582        });
7583    }
7584
7585    Ok((Some(window), rows_summary))
7586}
7587
7588/// Probe a JOINT multi-coordinate rail face (#2349 round 7 / #2348): step every
7589/// face coordinate one e-fold toward the interior TOGETHER and assess the
7590/// directional gradient along the outward face direction against the same
7591/// exponential tail law, noise floor, and drift band as the single-coordinate
7592/// window.
7593///
7594/// Why a joint law exists where the per-coordinate laws fail: for OVERLAPPING
7595/// penalties (e.g. the multinomial per-class family's coalesced pseudo-logdet
7596/// `½log|Σ_s λ_s M_s|₊`) several λs ride to ∞ on one face and share range
7597/// space. Moving ONE coordinate down leaves the shared term dominated by the
7598/// others, so that coordinate's own gradient saturates and its per-probe pencil
7599/// constant `ĉ_k = |g_k|e^{ρ_k}` sweeps orders of magnitude — measured on the
7600/// #2349 checkpoint: ĉ₀ spanning 4.5e2 → 1.0e-6 over the ladder, an honest
7601/// refusal of a law that genuinely does not hold marginally. Along the face
7602/// direction `u` (`u_k = +1` toward an upper rail, `−1` toward a lower rail)
7603/// the shared term moves coherently and the scalar objective section
7604/// `t ↦ V(ρ + t·u)` has the ordinary one-dimensional exponential tail; its
7605/// pencil constant is assessed with the pseudo-coordinate `r = mean_k(u_k ρ_k)`
7606/// and the directional derivative `g_u = Σ_{k∈face} u_k g_k = dV/dt`.
7607///
7608/// The window it returns speaks the [`assess_coordinate`] conventions
7609/// verbatim: on a genuine face `g_u < 0` at every interior probe (descent runs
7610/// outward), so the verdict side is `Upper` in the pseudo-coordinate
7611/// regardless of the mix of physical sides, and `ĉ = −e^{r}·g_u` recovers the
7612/// joint tail constant. Per-coordinate rails minted from it keep their own
7613/// physical [`AsymptoteSide`].
7614fn probe_joint_tail_window(
7615    obj: &mut dyn OuterObjective,
7616    rho: &Array1<f64>,
7617    face: &[(usize, AsymptoteSide)],
7618    tol: &AsymptoteTolerances,
7619    bounds: (&Array1<f64>, &Array1<f64>),
7620) -> Result<(Option<AsymptoteWindow>, String), EstimationError> {
7621    const PROBE_DELTA: f64 = 1.0;
7622    const PROBE_DOMAIN_MARGIN: f64 = 1.0e-6;
7623    let (lower, upper) = bounds;
7624    // Outward unit direction of the face; probes step INWARD (−u).
7625    let direction: Vec<(usize, f64)> = face
7626        .iter()
7627        .map(|(k, side)| {
7628            (
7629                *k,
7630                match side {
7631                    AsymptoteSide::Upper => 1.0,
7632                    AsymptoteSide::Lower => -1.0,
7633                },
7634            )
7635        })
7636        .collect();
7637    let r0 = direction
7638        .iter()
7639        .map(|(k, u)| u * rho[*k])
7640        .sum::<f64>()
7641        / direction.len() as f64;
7642    let mut rows: Vec<(f64, f64, Option<Array1<f64>>)> = Vec::new();
7643    for j in 1..=ASYMPTOTE_PROBE_COUNT {
7644        let step = (j as f64) * PROBE_DELTA;
7645        let mut probe = rho.clone();
7646        let mut in_domain = true;
7647        for (k, u) in &direction {
7648            let stepped = rho[*k] - u * step;
7649            if stepped <= lower[*k] + PROBE_DOMAIN_MARGIN
7650                || stepped >= upper[*k] - PROBE_DOMAIN_MARGIN
7651            {
7652                in_domain = false;
7653                break;
7654            }
7655            probe[*k] = stepped;
7656        }
7657        if !in_domain {
7658            break;
7659        }
7660        let eval = match obj.eval_with_order(&probe, OuterEvalOrder::ValueAndGradient) {
7661            Ok(eval) => eval,
7662            Err(_) => break,
7663        };
7664        if !eval.cost.is_finite() {
7665            break;
7666        }
7667        let mut g_u = 0.0;
7668        let mut finite = true;
7669        for (k, u) in &direction {
7670            match eval.gradient.get(*k) {
7671                Some(g) if g.is_finite() => g_u += u * g,
7672                _ => {
7673                    finite = false;
7674                    break;
7675                }
7676            }
7677        }
7678        if !finite {
7679            break;
7680        }
7681        rows.push((r0 - step, g_u, eval.inner_beta_hint));
7682    }
7683    let rows_summary = rows
7684        .iter()
7685        .map(|(r, g, _)| {
7686            format!(
7687                "(r={r:.2}, dV/dt={g:.3e}, ĉ={:.3e})",
7688                AsymptoteSide::Upper.tail_constant(*r, *g)
7689            )
7690        })
7691        .collect::<Vec<_>>()
7692        .join(" ");
7693    if rows.len() < MIN_TAIL_SAMPLES {
7694        return Ok((None, rows_summary));
7695    }
7696    let constants: Vec<f64> = rows
7697        .iter()
7698        .map(|(r, g, _)| AsymptoteSide::Upper.tail_constant(*r, *g))
7699        .collect();
7700    let element_clean: Vec<bool> = rows
7701        .iter()
7702        .zip(&constants)
7703        .map(|((_, g, _), c)| {
7704            c.is_finite() && *c > tol.tail_noise_floor && g.abs() > tol.interior_grad_tol
7705        })
7706        .collect();
7707    let mut best: Option<(usize, usize)> = None;
7708    for a in 0..rows.len() {
7709        if !element_clean[a] {
7710            continue;
7711        }
7712        for b in a..rows.len() {
7713            if !element_clean[b] {
7714                break;
7715            }
7716            if b - a + 1 < MIN_TAIL_SAMPLES {
7717                continue;
7718            }
7719            if !run_drift_within_band(&constants[a..=b], tol.tail_drift_rel) {
7720                continue;
7721            }
7722            let len = b - a + 1;
7723            match best {
7724                Some((ba, bb)) if bb - ba + 1 >= len => {}
7725                _ => best = Some((a, b)),
7726            }
7727        }
7728    }
7729    let (a, b) = match best {
7730        Some(run) => run,
7731        None => return Ok((None, rows_summary)),
7732    };
7733    let mut window = AsymptoteWindow::with_capacity(b - a + 1);
7734    for r in (a..=b).rev() {
7735        let (rho_r, grad_r, beta_r) = &rows[r];
7736        let coef_step_norm = match (beta_r, rows.get(r + 1).map(|row| &row.2)) {
7737            (Some(cur), Some(Some(farther))) if cur.len() == farther.len() => {
7738                (cur - farther).iter().map(|v| v * v).sum::<f64>().sqrt()
7739            }
7740            _ => 0.0,
7741        };
7742        window.push(AsymptoteSample {
7743            rho: *rho_r,
7744            grad: *grad_r,
7745            coef_step_norm,
7746        });
7747    }
7748    Ok((Some(window), rows_summary))
7749}
7750
7751/// Whether a run of pencil constants holds constant within the relative drift
7752/// band `(max − min)/|mean| ≤ band` (deterministic, ordered).
7753fn run_drift_within_band(constants: &[f64], band: f64) -> bool {
7754    if constants.len() < MIN_TAIL_SAMPLES {
7755        return false;
7756    }
7757    let mut sum = 0.0_f64;
7758    let mut lo = f64::INFINITY;
7759    let mut hi = f64::NEG_INFINITY;
7760    for &c in constants {
7761        if !c.is_finite() {
7762            return false;
7763        }
7764        sum += c;
7765        lo = lo.min(c);
7766        hi = hi.max(c);
7767    }
7768    let mean = sum / constants.len() as f64;
7769    if !(mean.abs() > 0.0) {
7770        return false;
7771    }
7772    (hi - lo) / mean.abs() <= band
7773}
7774
7775pub(crate) fn compute_rho_uncertainty_diagnostic(
7776    obj: &mut dyn OuterObjective,
7777    config: &OuterConfig,
7778    context: &str,
7779    result: &mut OuterResult,
7780) -> crate::rho_uncertainty::RhoUncertaintyDiagnostic {
7781    let terminal_cap_guard = config
7782        .outer_inner_cap
7783        .as_ref()
7784        .map(FullFidelityInnerCapGuard::lift);
7785    // Do not reset here. The diagnostic intentionally runs before terminal
7786    // installation and certification; the certificate must remain the final
7787    // owner of objective state. Holding cap=0 ensures proposal evaluations use
7788    // terminal fidelity, and the selected point is reinstalled immediately
7789    // afterward before the mint audit.
7790    let diagnostic =
7791        compute_rho_uncertainty_diagnostic_at_terminal_fidelity(obj, config, context, result);
7792    drop(terminal_cap_guard);
7793    diagnostic
7794}
7795
7796fn compute_rho_uncertainty_diagnostic_at_terminal_fidelity(
7797    obj: &mut dyn OuterObjective,
7798    config: &OuterConfig,
7799    context: &str,
7800    result: &mut OuterResult,
7801) -> crate::rho_uncertainty::RhoUncertaintyDiagnostic {
7802    let cap = obj.capability();
7803    let layout = cap.theta_layout();
7804    let rho_dim = layout.rho_dim();
7805    let gate = crate::rho_uncertainty::RhoUncertaintyCostGate {
7806        sample_count: 32,
7807        problem_size: config.rho_uncertainty_problem_size,
7808    };
7809    if let Err(reason) = crate::rho_uncertainty::cost_gate_allows(rho_dim, gate) {
7810        return crate::rho_uncertainty::RhoUncertaintyDiagnostic::skipped(reason, 0);
7811    }
7812    if result.rho.len() != layout.n_params {
7813        return crate::rho_uncertainty::RhoUncertaintyDiagnostic::skipped(
7814            format!(
7815                "final outer point length {} does not match objective dimension {}",
7816                result.rho.len(),
7817                layout.n_params
7818            ),
7819            0,
7820        );
7821    }
7822    // The ρ-uncertainty diagnostic needs the EXACT outer Hessian, but it runs
7823    // BEFORE terminal certification so that the certificate remains the final
7824    // owner of objective state. Under the optimize-3/certify-4 protocol (#2359)
7825    // this diagnostic may therefore consume only curvature the SEARCH already
7826    // retained. It must never trigger its own `ValueGradientHessian` evaluation:
7827    // a gradient-only search has deliberately reserved that one order-four pass
7828    // for the mint gate. Such a search skips this optional diagnostic; the
7829    // terminal certificate still computes and persists exact curvature.
7830    if !cap.hessian.is_analytic() {
7831        return crate::rho_uncertainty::RhoUncertaintyDiagnostic::skipped(
7832            "outer Hessian is not analytic; rho-uncertainty diagnostic needs exact curvature",
7833            0,
7834        );
7835    }
7836    if result.plan_used.hessian_source != HessianSource::Analytic {
7837        return crate::rho_uncertainty::RhoUncertaintyDiagnostic::skipped(
7838            "search did not use exact outer curvature; order-four work is reserved for the terminal certificate",
7839            0,
7840        );
7841    }
7842
7843    let hessian = match result.final_hessian.as_ref() {
7844        Some(hessian) => hessian.clone(),
7845        None => {
7846            return crate::rho_uncertainty::RhoUncertaintyDiagnostic::skipped(
7847                "search retained no exact outer Hessian; order-four work is reserved for the terminal certificate",
7848                0,
7849            );
7850        }
7851    };
7852    if hessian.nrows() != layout.n_params || hessian.ncols() != layout.n_params {
7853        return crate::rho_uncertainty::RhoUncertaintyDiagnostic::skipped(
7854            format!(
7855                "exact outer Hessian shape {}x{} does not match objective dimension {}",
7856                hessian.nrows(),
7857                hessian.ncols(),
7858                layout.n_params
7859            ),
7860            1,
7861        );
7862    }
7863    let mut hessian_rho = Array2::<f64>::zeros((rho_dim, rho_dim));
7864    for row in 0..rho_dim {
7865        for col in 0..rho_dim {
7866            hessian_rho[[row, col]] = hessian[[row, col]];
7867        }
7868    }
7869    let rho_hat = result.rho.slice(ndarray::s![..rho_dim]).to_owned();
7870    let theta_hat = result.rho.clone();
7871    let cost_hat = result.final_value;
7872    let diagnostic = {
7873        let mut served_hat_cost = false;
7874        let mut criterion = |rho: &Array1<f64>| -> Option<f64> {
7875            let is_hat = rho.len() == rho_hat.len()
7876                && rho
7877                    .iter()
7878                    .zip(rho_hat.iter())
7879                    .all(|(&left, &right)| left.to_bits() == right.to_bits());
7880            if is_hat && !served_hat_cost {
7881                served_hat_cost = true;
7882                return Some(cost_hat);
7883            }
7884            let mut theta = theta_hat.clone();
7885            for idx in 0..rho_dim {
7886                theta[idx] = rho[idx];
7887            }
7888            obj.eval_cost(&theta).ok()
7889        };
7890        crate::rho_uncertainty::rho_uncertainty_diagnostic(
7891            &rho_hat,
7892            &hessian_rho,
7893            gate,
7894            &mut criterion,
7895        )
7896    };
7897    match &diagnostic.status {
7898        crate::rho_uncertainty::RhoUncertaintyStatus::NoEvidenceOfHeavyTails => {
7899            log::info!(
7900                "[RHO uncertainty] {context}: no heavy-tail evidence at sampled rho proposals k_hat={:.3} evals={}",
7901                diagnostic.k_hat.unwrap_or(f64::NAN),
7902                diagnostic.n_evaluations,
7903            );
7904        }
7905        crate::rho_uncertainty::RhoUncertaintyStatus::HeavyTailsDetected { k_hat } => {
7906            log::warn!(
7907                "[RHO uncertainty] {context}: heavy rho-importance tail detected k_hat={:.3} evals={}",
7908                k_hat,
7909                diagnostic.n_evaluations,
7910            );
7911        }
7912        crate::rho_uncertainty::RhoUncertaintyStatus::Skipped { reason } => {
7913            log::info!("[RHO uncertainty] {context}: skipped ({reason})");
7914        }
7915    }
7916    diagnostic
7917}
7918
7919/// Why the operator trust-region outer loop stopped.
7920///
7921/// The inhabitants of this enum are exactly the image of
7922/// `bridges::stop_reason_from`, which is its ONE producer: a value only ever
7923/// reaches a `RhoOptimizerResult` by mapping an `opt::TerminationReason`.
7924/// Adding a variant that map cannot emit adds a label, not a state — that is
7925/// how `RoutingMismatch` ("family returned a non-operator Hessian mid-flight")
7926/// came to sit here unreachable, and it was deleted for it (#2670).
7927#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7928pub enum OperatorTrustRegionStopReason {
7929    Converged,
7930    RejectFloor,
7931    IterationBudget,
7932    /// The objective stopped changing on a criterion-flat surface. The
7933    /// in-loop guard may already have certified the score-relative residual or
7934    /// may have returned a non-stationary floor; either way the final analytic
7935    /// certificate needs this provenance to reproduce the guard's derived
7936    /// stationarity band exactly.
7937    CostStallFlatValley,
7938    /// The solver stopped because something failed, not because a test it
7939    /// stands behind was satisfied: the line search gave up, an objective
7940    /// evaluation failed, or the arithmetic went non-finite.
7941    ///
7942    /// These used to map to [`Self::Converged`], on the stated premise that
7943    /// they are "a hard failure the caller sees through the `Err` arm". That
7944    /// premise is false for the line-search case on the path it actually
7945    /// takes: `run_plan` turns a `BfgsError::LineSearchFailed` whose last
7946    /// iterate is finite into `Ok(non-converged)`, because that iterate is a
7947    /// usable checkpoint. The caller therefore sees no `Err`, and the coarse
7948    /// reason it does see said `Converged` — which is how a binomial/logit
7949    /// REML fit that never accepted a single step came to report
7950    /// `stop_reason=Converged after 1 outer iteration(s)` (#2614).
7951    ///
7952    /// Reporting-only today: no consumer branches on `Converged`, so this
7953    /// splits a label without moving a decision. Kept separate from
7954    /// [`Self::IterationBudget`] because "ran out of budget" and "could not
7955    /// take a step" call for different repairs.
7956    SolverFailure,
7957}
7958
7959/// Run the outer smoothing-parameter optimization.
7960///
7961/// This is the single entry point that replaces the scattered optimizer wiring
7962/// across estimate.rs, joint.rs, and custom_family.rs. It:
7963///
7964/// 1. Queries and canonicalizes the objective's capability declaration.
7965/// 2. Calls `plan()` to select solver + hessian source.
7966/// 3. Logs the plan and the analytic derivative capabilities it will consume.
7967/// 4. Generates seed candidates.
7968/// 5. Runs the chosen solver on candidates in heuristic order up to budget.
7969/// 6. If the configured fallback policy allows it, re-plans with degraded
7970///    capabilities chosen centrally inside outer_strategy and retries.
7971/// 7. Returns the best result (including which plan was actually used).
7972///
7973/// Do not wrap `run_outer` calls in try/catch with ad-hoc solver recovery.
7974/// Callers should declare only the primary capability and, at most, whether
7975/// automatic fallback is enabled at all.
7976///
7977/// Bound on the certify-last checkpoint-resume loop (#2273/#2374). When a
7978/// solver CLAIMS convergence but the mandatory analytic certificate refuses,
7979/// the loop re-runs the outer search seeded AT the refused checkpoint (with a
7980/// fresh metric for gradient-only outers, since `final_hessian` is `None`)
7981/// while each resume strictly reduces the objective — real descent the claim
7982/// left unexploited. #2273 introduced this as a SINGLE retry for the
7983/// stale-tolerance desync (one reseed re-anchors the in-loop tolerance to the
7984/// terminal cost scale and certifies). #2374 generalized it to a
7985/// progress-bounded loop: a gradient-only `opt::Bfgs` outer in log-λ space can
7986/// exit its flat-valley `StallPolicy` at `‖g‖∞ ≤ tol·(1 + ‖ρ‖∞)` — a gate
7987/// inflated ~10× by a railed coordinate — reporting `Ok(converged)` at a
7988/// checkpoint whose projected gradient the un-inflated certificate correctly
7989/// rejects, and a single fresh-metric reseed rarely lands the optimum in one
7990/// hop (the transformation-survival LAML of #2373 needed two). This bound caps
7991/// how many such reseeds are attempted before the honest non-convergence is
7992/// surfaced; a fit that certifies on the first pass never enters the loop, and
7993/// a reseed that fails to reduce the objective (a genuine non-stationary floor
7994/// a fresh metric cannot escape) stops the loop immediately regardless of the
7995/// remaining budget.
7996const OUTER_CERTIFY_RESUME_BUDGET: usize = 16;
7997
7998/// Max **interior** strict-saddle escape resumes (#2357/#2155/#2612).
7999///
8000/// The pathology this guards is named in #2155/#2363: a bimodal inner solve
8001/// whose warm re-descent keeps reporting a phantom improvement the cold
8002/// certificate cannot reproduce. `certify_resume_made_progress` is the loop's
8003/// own descent gate and it can be fooled by exactly that hysteresis — the warm
8004/// value looks improved — so a small cap is the backstop, and it stays.
8005///
8006/// It applies only to an escape whose reseed lands in the **interior** of the
8007/// box. Such an escape retires nothing and can in principle repeat forever, so a
8008/// count is the only bound available for it.
8009///
8010/// It does NOT apply to an escape whose reseed lands ON the box face, and that
8011/// distinction is the whole of #2612. The escape direction is exactly zero on
8012/// every railed coordinate (`judged_subspace_basis`), so the ray's box
8013/// intersection is set by a FREE coordinate: a reseed on the face has retired a
8014/// previously-free coordinate onto a rail. There are only `n` coordinates to
8015/// retire, so that escape cannot be the repeating pathology, and it is bounded
8016/// by [`OUTER_CERTIFY_RESUME_BUDGET`] like every other reseed kind.
8017///
8018/// The old value carried the premise *"a genuine saddle is cleared in one
8019/// escape"*, and #2612 measured that false: on the multinomial banded fixture
8020/// the criterion descends monotonically for six e-folds to the wall, and on
8021/// penguins four successive escapes each ran to a face
8022/// (`α_box = 9.39, 4.77, 9.14, 6.11`) while the criterion fell
8023/// `2.158034 → 2.156725`. Capping THAT by a count refuses a point the criterion
8024/// is still descending toward, one coordinate short of the corner.
8025pub(crate) const OUTER_SADDLE_ESCAPE_BUDGET: usize = 3;
8026
8027/// Roundoff-relative scale below which a certify-last reseed's objective
8028/// reduction is numerical noise rather than exploited descent (#2374). A
8029/// fresh-metric BFGS restart seeded AT the refused checkpoint can only reduce
8030/// the objective from that checkpoint, so `retried == prior` (to roundoff)
8031/// means the restart found no descent — a genuine stationary floor — while a
8032/// false flat-valley stall yields a reduction orders of magnitude above this
8033/// scale. The progress gate MUST anchor on roundoff, not the much larger
8034/// cost-stall relative floor: a flat valley crawls out in per-reseed steps far
8035/// smaller than `rel_cost·(1 + |cost|)` (the transformation-survival LAML moves
8036/// ~4e-5 relative per reseed), and gating on that coarser floor stops the crawl
8037/// after a single hop and refuses a well-posed fit.
8038const CERTIFY_RESUME_PROGRESS_REL: f64 = 32.0 * f64::EPSILON;
8039
8040pub(crate) fn run_outer(
8041    obj: &mut dyn OuterObjective,
8042    config: &OuterConfig,
8043    context: &str,
8044) -> Result<OuterResult, EstimationError> {
8045    // Permutation-invariant outer search (#1538/#1539). When the caller has
8046    // supplied per-coordinate structural keys that induce a non-identity
8047    // canonical order, run the ENTIRE outer pipeline (seeding, multistart,
8048    // optimization, and the #934 certificate / uncertainty audits) in that
8049    // canonical layout against a permuting wrapper, then map the result back to
8050    // the native layout. Seeding/tie-breaking then see byte-identical
8051    // coordinates for every term order, so both orders select the same λ̂.
8052    if let Some(keys) = config.rho_canonical_keys.as_ref()
8053        && let Some(perm) = canonical_permutation(keys)
8054    {
8055        let canonical_config = canonicalize_outer_config(config, &perm);
8056        let mut canonical_obj = CanonicalizedObjective::new(obj, perm.clone());
8057        let result = run_outer(&mut canonical_obj, &canonical_config, context)?;
8058        return Ok(outer_result_to_native(result, &perm));
8059    }
8060    let mut result = run_outer_uncertified(obj, config, context)?;
8061    if obj.begin_exact_polish() {
8062        // A sampled outer-derivative pilot is an optimization stage, never a
8063        // certifiable objective. Continue from its best checkpoint on the
8064        // family's exact full-data measure before the mandatory analytic
8065        // certificate. This transition is unconditional whenever the family
8066        // reports that a sample actually ran, so convergence before a nominal
8067        // phase budget cannot strand the optimizer on the stochastic surface
8068        // (#979: matrix-free TR stopped after 6 evaluations while the family
8069        // waited for a 12-evaluation counter).
8070        let pilot_iterations = result.iterations;
8071        let mut exact_config = config.clone();
8072        exact_config.initial_rho = Some(result.rho.clone());
8073        exact_config.heuristic_lambdas = None;
8074        exact_config.seed_config.max_seeds = 1;
8075        exact_config.seed_config.seed_budget = 1;
8076        exact_config.screen_initial_rho = false;
8077        exact_config.operator_initial_trust_radius = result.operator_trust_radius;
8078        exact_config.warm_start_outer_hessian = result.final_hessian.clone();
8079        log::info!(
8080            "[OUTER] {context}: sampled derivative pilot completed after {} iteration(s); \
8081             continuing from its checkpoint on the exact full-data measure",
8082            pilot_iterations,
8083        );
8084        let mut polished = run_outer_uncertified(obj, &exact_config, context)?;
8085        polished.iterations = polished.iterations.saturating_add(pilot_iterations);
8086        result = polished;
8087    }
8088    // Mandatory analytic optimality certificate (#934): once at the selected
8089    // point, outside every hot loop, for every solver path and every iteration
8090    // budget. Missing or failed evidence is typed non-convergence; there is no
8091    // max-iteration or logging-level bypass.
8092    //
8093    // #2273 STALE-TOLERANCE DESYNC RETRY.
8094    //
8095    // HISTORY, because the mechanism this was written for no longer exists.
8096    // The solver's in-loop threshold used to be resolved ONCE from the SEED's
8097    // cost scale (`rel_cost·(1+|seed_cost|)`) while this certificate re-derived
8098    // the same formula at the terminal point's own, often far smaller, cost. On
8099    // a perfectly-separated binomial the score plunges between the oversmoothed
8100    // heuristic seed and the first accepted step, so the solver declared
8101    // victory against a bound orders of magnitude looser than the one that then
8102    // refused it here (measured: |g|=8.1e-1 accepted in-loop vs bound 8.3e-3 at
8103    // certification, "NOT STATIONARY after 1 outer iteration"; the pass/fail
8104    // pattern was non-monotone in n because it tracked the seed-to-terminus
8105    // cost ratio, not identifiability). The retry removed that by construction:
8106    // re-seeded AT the refused checkpoint, the retry's seed cost IS the
8107    // certificate's cost.
8108    //
8109    // #2613 removed the anchor desync itself. The solver's band
8110    // (`outer_gradient_tolerance`) is now a function of the declared problem
8111    // and of nothing else, and this certificate's band
8112    // (`outer_stationarity_band_at`) is floored at it, so
8113    // `certificate_bound >= solver_bound` holds identically — a point the
8114    // solver legitimately converged at CANNOT be refused here for being above
8115    // its band. `certificate_band_never_undercuts_the_solver_band_2613` gates
8116    // that invariant directly.
8117    //
8118    // What the retry still covers is a different desync with the same shape: a
8119    // FIDELITY one. Search-time evaluations may run under the inner-PIRLS cap,
8120    // so the gradient the solver stopped on and the gradient re-measured here
8121    // at full inner fidelity are not the same number. Re-seeding at the refused
8122    // checkpoint still collapses that difference, for the same reason. Bounded
8123    // to a single retry; only fires when the solver CLAIMED convergence (a
8124    // budget-exhausted result is not a desync — its refusal is genuine).
8125    // CERTIFICATION-LAST FIT OWNERSHIP. The uncertainty diagnostic evaluates
8126    // proposal points after theta-hat and the terminal reinstallation
8127    // re-evaluates at `result.rho`, so any certificate measured BEFORE them
8128    // describes a state the caller never receives: on a nonconvex profile the
8129    // certificate-time inner mode and the finally-installed inner mode can sit
8130    // in different coefficient basins (measured on the cause-specific survival
8131    // gate as a stable bitwise mismatch, terminal 9.1931e2 vs certified
8132    // 9.1671e2, because the two paths prime the inner solve under different
8133    // eval orders). Running the diagnostic and the terminal installation
8134    // FIRST and certifying LAST makes the certificate's own evaluation the
8135    // final objective-state installer, so the sealed terminal identity fit
8136    // assembly binds against IS the certified evidence — bitwise, by
8137    // construction, independent of basin multiplicity.
8138    let certify_diagnose_and_install = |obj: &mut dyn OuterObjective,
8139                                        result: &mut OuterResult|
8140     -> Result<OuterCriterionCertificate, EstimationError> {
8141        result.rho_uncertainty_diagnostic = Some(compute_rho_uncertainty_diagnostic(
8142            obj, config, context, result,
8143        ));
8144        // Reinstall the selected point under cap=0 so the certificate below
8145        // measures the full-fidelity state belonging to `result.rho`, not
8146        // the diagnostic's final proposal (seeding beta alone does not
8147        // restore weights, factors, or link state). Reset forces a real
8148        // installation instead of an LRU value hit.
8149        let terminal_cap_guard = config
8150            .outer_inner_cap
8151            .as_ref()
8152            .map(FullFidelityInnerCapGuard::lift);
8153        // Reset is conditional on the cap contract, mirroring
8154        // `certify_outer_optimality`'s own doctrine: REML/mixture
8155        // objectives with a cap can hold a coarse search cache that must
8156        // not be installed as terminal state, while uncapped stateful
8157        // objectives (reactive-domain entries among them) retain the very
8158        // state their evaluation at `result.rho` depends on — an
8159        // unconditional reset here wiped it and made the certification
8160        // evaluation non-finite on the reactive fixture.
8161        //
8162        // OR-in the terminal-coefficient-mode ownership signal (#2334):
8163        // objectives that install an owned coefficient mode here but hold
8164        // their inner cap in a different field (custom families) leave
8165        // `outer_inner_cap` `None`, so the cap gate alone never fires and
8166        // `finalize` here could land in a different inner basin than the
8167        // certifying re-eval below — a spurious bitwise bind failure on a
8168        // bimodal inner solve. Forcing the reset for mode-owning objectives
8169        // makes both installations start from the same clean baseline.
8170        if terminal_cap_guard.is_some() || obj.owns_terminal_coefficient_mode() {
8171            obj.reset();
8172        }
8173        let terminal_installation = obj.finalize_outer_result(&result.rho, &result.plan_used);
8174        let terminal_inner_converged = inner_solve_converged(config.outer_inner_cap.as_ref());
8175        drop(terminal_cap_guard);
8176        terminal_installation?;
8177        if !terminal_inner_converged {
8178            return Err(outer_nonconvergence_error(
8179                context,
8180                "final outer state installation did not converge at full inner fidelity",
8181                result,
8182                result.final_grad_norm,
8183                StationarityStandard::NoComparison,
8184            ));
8185        }
8186        certify_outer_optimality(obj, config, context, result)
8187    };
8188    // Certify-last checkpoint-resume loop (#2273 stale-tolerance desync,
8189    // generalized by #2374). A solver that CLAIMS convergence but fails the
8190    // mandatory analytic certificate is re-run once per iteration seeded AT the
8191    // refused checkpoint — re-anchoring the in-loop tolerance to the terminal
8192    // cost scale and, for gradient-only outers (`final_hessian == None`),
8193    // restarting `opt::Bfgs` with a fresh inverse-Hessian metric that breaks the
8194    // flat-valley `StallPolicy` false stop the accumulated metric crawled into.
8195    // Looping (rather than the original single retry) matters because that stall
8196    // gate is inflated by `(1 + ‖ρ‖∞)` in log-λ space, so one fresh-metric
8197    // reseed rarely lands the optimum in a single hop. The loop stops the moment
8198    // certification passes; it also stops — regardless of remaining budget —
8199    // when a reseed fails to strictly reduce the objective, because a point that
8200    // a fresh-metric restart cannot improve is a genuine non-stationary floor
8201    // (or a true flat valley), not an exploitable false stall, and further
8202    // reseeds would only re-derive the same refusal. A result that never claimed
8203    // convergence (e.g. a budget-exhausted `MaxIterationsReached`) is refused
8204    // immediately with no reseed: its non-convergence is genuine.
8205    let mut resumes_remaining = OUTER_CERTIFY_RESUME_BUDGET;
8206    // INTERIOR strict-saddle escapes are bounded separately and tightly: one that
8207    // lands inside the box retires nothing, so a count is the only bound there is
8208    // for it, and a non-convergent bimodal-inner grind (#2155/#2363) is cut off
8209    // well before it exhausts the general resume budget (#2357). An escape that
8210    // lands on the box FACE has retired a free coordinate onto a rail and is
8211    // bounded by the general budget instead — see [`OUTER_SADDLE_ESCAPE_BUDGET`]
8212    // (#2612).
8213    let mut interior_saddle_escapes_remaining: usize = OUTER_SADDLE_ESCAPE_BUDGET;
8214    // #2569 — seed points this loop has already started and already had refused.
8215    // A resume changes `initial_rho` and nothing else the cascade reads, and it
8216    // runs from a reset objective, so a NON-initial seed re-entered on a later
8217    // round terminates exactly where it terminated before. Measured on the
8218    // grouped-binomial design of #2569: 17 rounds re-ran one cold lattice seed
8219    // to the identical `|g|` after the identical 42 outer iterations, 1628 s of
8220    // a 9016 s fit. Accumulated across rounds and handed to the retry so the
8221    // cascade replays the recorded verdict instead of re-deriving it.
8222    let mut refused_seed_points: Vec<Array1<f64>> = Vec::new();
8223    let certificate = loop {
8224        let claimed_converged = result.solver_claimed_convergence();
8225        match certify_diagnose_and_install(obj, &mut result) {
8226            Ok(certificate) => break certificate,
8227            Err(refusal) => {
8228                // #2357/#2155 — interior strict-saddle escape. When the refusal
8229                // is a first-order-stationary point whose reduced (off-railed)
8230                // Hessian is indefinite, the certificate publishes a
8231                // negative-curvature reseed stepped strictly BELOW the saddle
8232                // (`adjudicate_negative_curvature`). Reseeding the resume at the
8233                // refused checkpoint itself would re-descend straight back to that
8234                // zero-gradient saddle — the #2273/#2374 stale-tolerance resume
8235                // anchors the tolerance and breaks flat-valley stalls, but it
8236                // cannot break a genuine saddle. Seed at the escape point instead,
8237                // off the ridge, and start from a FRESH outer metric so the
8238                // saddle's indefinite curvature is not transferred into the
8239                // restart. This is the run_outer-level consumer of the reseed that
8240                // the multistart-loop consumer (`run_outer_with_plan`) mints only
8241                // when a per-seed claim is already stationary; the terminal
8242                // certificate is where stationarity is reached for the binomial
8243                // link-wiggle families, so without this the reseed was minted and
8244                // then dropped.
8245                let saddle_escape_reseed = result.saddle_escape_reseed.take();
8246                let resume_from_saddle_escape = saddle_escape_reseed.is_some();
8247                // Did this escape RETIRE a free coordinate onto a rail (#2612)?
8248                //
8249                // Read off the reseed rather than plumbed down from the
8250                // adjudication, because it is a property of the two points and
8251                // the box and nothing else: a coordinate the refused checkpoint
8252                // held strictly inside the box now sits on a bound. The escape
8253                // direction is exactly zero on every already-railed coordinate,
8254                // so the ray's box intersection can only be set by a free one —
8255                // which is why "landed on the face" and "retired a free
8256                // coordinate" are the same event.
8257                let saddle_escape_retires_a_coordinate =
8258                    saddle_escape_reseed.as_ref().is_some_and(|reseed| {
8259                        let (lower, upper) =
8260                            outer_model_domain_bounds_template(config, reseed.len());
8261                        (0..reseed.len()).any(|i| {
8262                            let on_bound = reseed[i] <= lower[i] || reseed[i] >= upper[i];
8263                            let was_interior = result
8264                                .rho
8265                                .get(i)
8266                                .is_some_and(|held| *held > lower[i] && *held < upper[i]);
8267                            on_bound && was_interior
8268                        })
8269                    });
8270                // #2348 Inc 2b, completed (#2349 round 8): a confirmed-tail
8271                // snap that needs a re-descent publishes the snapped face as
8272                // `tail_snap_reseed` — previously minted and then DROPPED
8273                // (declared, set, never consumed), so every ConfirmedNeedsReseed
8274                // outcome fell through to the plain refusal. The joint tail law
8275                // is first-order evidence of WHERE the optimum is, so the retry
8276                // is warranted regardless of the solver's convergence claim,
8277                // exactly like the saddle-escape reseed (measured on the #2349
8278                // fixture: the face snap descends 3.86 with |Pg| dropping
8279                // 2.05 → 0.35; the retry lets over-snapped coordinates relax
8280                // back to their interior optima while the rest hold the rail).
8281                let tail_snap_reseed = if resume_from_saddle_escape {
8282                    result.tail_snap_reseed.take();
8283                    None
8284                } else {
8285                    result.tail_snap_reseed.take()
8286                };
8287                let resume_from_tail_snap = tail_snap_reseed.is_some();
8288                // #2392 — wrong-rail pull-back and active-set reduction reseeds,
8289                // consumed with LOWER precedence than the saddle/tail-snap
8290                // reseeds. A higher-precedence reseed DROPS them (take-and-discard)
8291                // so no stale reseed leaks into a later iteration, exactly as the
8292                // saddle escape drops a co-minted tail snap above. Both are
8293                // first-order evidence (a proven inward descent / a poisoned-rail
8294                // interior), so — like the tail snap — they fire regardless of the
8295                // solver's convergence claim.
8296                let higher_precedence_reseed = resume_from_saddle_escape || resume_from_tail_snap;
8297                let wrong_rail_reseed = if higher_precedence_reseed {
8298                    result.wrong_rail_reseed.take();
8299                    None
8300                } else {
8301                    result.wrong_rail_reseed.take()
8302                };
8303                let resume_from_wrong_rail = wrong_rail_reseed.is_some();
8304                let active_set_reseed = if higher_precedence_reseed || resume_from_wrong_rail {
8305                    result.active_set_reseed.take();
8306                    None
8307                } else {
8308                    result.active_set_reseed.take()
8309                };
8310                let resume_from_active_set = active_set_reseed.is_some();
8311                let active_set_rho = active_set_reseed.as_ref().map(|a| a.rho.clone());
8312                let active_set_bounds = active_set_reseed.map(|a| a.bounds);
8313                // A published reseed means the refused point IS first-order
8314                // stationary (the escape mint gate requires `is_stationary`), so
8315                // it is a genuine saddle escapable regardless of whether the
8316                // solver "claimed" convergence: for exact-Hessian link-wiggle
8317                // families the terminal certificate — not the in-loop gate — is
8318                // where stationarity is first reached, so they arrive here with
8319                // `converged == false` yet stationary. The #2273/#2374
8320                // stale-tolerance resume, which reseeds AT the refused checkpoint,
8321                // still requires a genuine convergence claim (a budget-exhausted
8322                // non-stationary iterate has no desync to remove).
8323                if (!claimed_converged
8324                    && !resume_from_saddle_escape
8325                    && !resume_from_tail_snap
8326                    && !resume_from_wrong_rail
8327                    && !resume_from_active_set)
8328                    || resumes_remaining == 0
8329                    || (resume_from_saddle_escape
8330                        && !saddle_escape_retires_a_coordinate
8331                        && interior_saddle_escapes_remaining == 0)
8332                {
8333                    return Err(refusal);
8334                }
8335                resumes_remaining -= 1;
8336                if resume_from_saddle_escape && !saddle_escape_retires_a_coordinate {
8337                    // An INTERIOR escape retires nothing, so it can in principle
8338                    // repeat forever — a pathological objective (a bimodal inner
8339                    // solve whose warm re-descent keeps reporting a phantom
8340                    // improvement the cold certificate cannot reproduce, #2155 /
8341                    // #2363) would otherwise burn the whole resume budget
8342                    // re-escaping a family of shallow saddles that never
8343                    // certifies. A count is the only bound available for that, so
8344                    // the small cap stands and past it the honest refusal is
8345                    // taken.
8346                    //
8347                    // An escape that landed on the box FACE is a different event
8348                    // (#2612): it retired a previously-free coordinate onto a
8349                    // rail, there are only `n` coordinates to retire, and the
8350                    // criterion strictly decreased on the way. It is bounded by
8351                    // `resumes_remaining` above, like every other reseed kind, and
8352                    // by the descent gate below, which stops the loop the moment a
8353                    // resume fails to strictly improve.
8354                    interior_saddle_escapes_remaining -= 1;
8355                }
8356                let prior_iterations = result.iterations;
8357                let prior_value = result.final_value;
8358                log::info!(
8359                    "[OUTER] {context}: analytic certification refused after \
8360                     {prior_iterations} iteration(s) (final_value={prior_value:.6e}); re-running \
8361                     seeded {} so the in-loop tolerance anchors to the terminal cost scale \
8362                     ({resumes_remaining} resume(s) left after this one; #2273/#2374/#2155)",
8363                    if resume_from_saddle_escape {
8364                        "off the negative-curvature saddle ridge"
8365                    } else if resume_from_tail_snap {
8366                        "at the confirmed-tail snapped face"
8367                    } else if resume_from_wrong_rail {
8368                        "at the wrong-rail coordinate's clean-band interior scale"
8369                    } else if resume_from_active_set {
8370                        "with the poisoned rail frozen so the interior polishes in the reduced box"
8371                    } else {
8372                        "at the refused checkpoint"
8373                    }
8374                );
8375                let mut retry_cfg = config.clone();
8376                retry_cfg.initial_rho = Some(
8377                    saddle_escape_reseed
8378                        .or(tail_snap_reseed)
8379                        .or(wrong_rail_reseed)
8380                        .or(active_set_rho)
8381                        .unwrap_or_else(|| result.rho.clone()),
8382                );
8383                // Active-set reduction (#2392): the polish runs in the REDUCED
8384                // (frozen) box so the interior converges without the railed
8385                // coordinate's ill-conditioned Hessian row poisoning the step. The
8386                // loop re-certifies the polished point under the ORIGINAL box at
8387                // the top of the next iteration (`certify_outer_optimality` reads
8388                // `model_domain_bounds`, which `search_bounds_override` cannot
8389                // redefine), so the reduction can narrow the SEARCH but never the
8390                // feasible set a certificate is judged against. `config.clone()`
8391                // reset each iteration, so the frozen box never persists past
8392                // this run.
8393                //
8394                // A coordinate is only frozen here if the projector had already
8395                // zeroed it (#2454), i.e. it was on an active constraint when the
8396                // freeze was taken. That test is at freeze time deliberately: the
8397                // retry is one-shot, so there is no second reduction to undo a
8398                // wrong freeze, and the only path back off a frozen bound is the
8399                // wrong-rail pull-back, which needs a clean opposite-sign
8400                // exponential tail and declines on any coordinate without one.
8401                if let Some(frozen_bounds) = active_set_bounds {
8402                    retry_cfg.search_bounds_override = Some(frozen_bounds);
8403                }
8404                retry_cfg.heuristic_lambdas = None;
8405                retry_cfg.seed_config.max_seeds = 1;
8406                retry_cfg.seed_config.seed_budget = 1;
8407                retry_cfg.screen_initial_rho = false;
8408                // `seed_budget = 1` above is NOT binding: `should_start_next_seed`
8409                // lets the cascade continue past it while nothing has certified,
8410                // and on a resume the reseeded slot-0 candidate is exactly what
8411                // failed certification, so `best` is `None` and the fall-through
8412                // fires every round on the same regenerated lattice seed (#2569).
8413                // Suppress only seeds this loop has ALREADY started and had
8414                // refused; a seed that has never run is still reachable, so the
8415                // fall-through keeps its rescue role.
8416                for point in result.refused_seed_points.iter() {
8417                    if !refused_seed_points.contains(point) {
8418                        refused_seed_points.push(point.clone());
8419                    }
8420                }
8421                retry_cfg.previously_refused_seed_points = refused_seed_points.clone();
8422                // Every reseed kind lands at a genuinely different point, so the
8423                // refused checkpoint's metric (trust radius, outer Hessian)
8424                // must not be transferred into the restart.
8425                let fresh_metric = resume_from_saddle_escape
8426                    || resume_from_tail_snap
8427                    || resume_from_wrong_rail
8428                    || resume_from_active_set;
8429                retry_cfg.operator_initial_trust_radius = if fresh_metric {
8430                    None
8431                } else {
8432                    result.operator_trust_radius
8433                };
8434                retry_cfg.warm_start_outer_hessian = if fresh_metric {
8435                    None
8436                } else {
8437                    result.final_hessian.clone()
8438                };
8439                obj.reset();
8440                match run_outer_uncertified(obj, &retry_cfg, context) {
8441                    Ok(mut retried) => {
8442                        retried.iterations = retried.iterations.saturating_add(prior_iterations);
8443                        // Progress gate. A fresh-metric reseed seeded AT the
8444                        // checkpoint can only descend from it, so a reduction at
8445                        // roundoff scale means it found no descent — a genuine
8446                        // stationary floor — while a false flat-valley stall
8447                        // yields a reduction orders of magnitude larger. Gate on
8448                        // roundoff (NOT the coarser cost-stall floor) so a valley
8449                        // that crawls out in tiny per-reseed steps is not cut off
8450                        // after one hop; stop only when a reseed truly stalls, so
8451                        // the next iteration certifies the best point once more
8452                        // and takes the honest refusal.
8453                        let improved = certify_resume_made_progress(
8454                            prior_value,
8455                            retried.final_value,
8456                            CERTIFY_RESUME_PROGRESS_REL,
8457                        );
8458                        result = retried;
8459                        if !improved {
8460                            resumes_remaining = 0;
8461                        }
8462                    }
8463                    // The reseed could not even run (e.g. the checkpoint is a
8464                    // hard refusal wall for the objective): surface the
8465                    // certification refusal from the point we started this
8466                    // iteration at, which carries the checkpoint evidence.
8467                    Err(_) => return Err(refusal),
8468                }
8469            }
8470        }
8471    };
8472    result.criterion_certificate = Some(certificate);
8473    Ok(result)
8474}
8475
8476/// Build a CANONICAL-order copy of an [`OuterConfig`] for the
8477/// permutation-invariant outer search (#1538/#1539).
8478///
8479/// `perm[c]` is the native coordinate at canonical slot `c`. Every
8480/// per-coordinate config field (initial ρ seed, heuristic-λ seed, per-axis
8481/// bounds, transferred warm Hessian) is reordered native→canonical so the
8482/// optimizer's seeding and multistart operate entirely in canonical space;
8483/// scalar fields are copied verbatim. `rho_canonical_keys` is cleared so the
8484/// recursive [`run_outer`] frame runs the normal (identity-order) pipeline on
8485/// the already-canonical objective.
8486fn canonicalize_outer_config(config: &OuterConfig, perm: &[usize]) -> OuterConfig {
8487    // Permute a per-coordinate slice native→canonical; pass through any length
8488    // that does not match the permutation (defensive — should not occur).
8489    let permute_vec = |v: &[f64]| -> Vec<f64> {
8490        if v.len() == perm.len() {
8491            perm.iter().map(|&i| v[i]).collect()
8492        } else {
8493            v.to_vec()
8494        }
8495    };
8496    let permute_arr = |a: &Array1<f64>| -> Array1<f64> {
8497        if a.len() == perm.len() {
8498            Array1::from_iter(perm.iter().map(|&i| a[i]))
8499        } else {
8500            a.clone()
8501        }
8502    };
8503    let mut canonical = config.clone();
8504    canonical.rho_canonical_keys = None;
8505    if let Some(initial) = config.initial_rho.as_ref() {
8506        canonical.initial_rho = Some(permute_arr(initial));
8507    }
8508    canonical.previously_refused_seed_points = config
8509        .previously_refused_seed_points
8510        .iter()
8511        .map(permute_arr)
8512        .collect();
8513    canonical.initial_rho_candidates = config
8514        .initial_rho_candidates
8515        .iter()
8516        .map(permute_arr)
8517        .collect();
8518    if let Some(bound) = config.initial_inner_seed.as_ref() {
8519        canonical.initial_inner_seed = Some(BoundInnerSeed {
8520            theta: permute_arr(&bound.theta),
8521            beta: bound.beta.clone(),
8522        });
8523    }
8524    if let Some(h) = config.heuristic_lambdas.as_ref() {
8525        canonical.heuristic_lambdas = Some(permute_vec(h));
8526    }
8527    if let Some((lower, upper)) = config.model_domain_bounds.as_ref() {
8528        canonical.model_domain_bounds = Some((permute_arr(lower), permute_arr(upper)));
8529    }
8530    if let Some((lower, upper)) = config.search_bounds_override.as_ref() {
8531        canonical.search_bounds_override = Some((permute_arr(lower), permute_arr(upper)));
8532    }
8533    // A transferred dense outer Hessian is in native coordinate order; permute
8534    // it into canonical order so the BFGS warm metric stays aligned. (None on
8535    // the cold-start canonicalized path, so this is usually a no-op.)
8536    if let Some(h) = config.warm_start_outer_hessian.as_ref()
8537        && h.nrows() == perm.len()
8538        && h.ncols() == perm.len()
8539    {
8540        let mut hc = Array2::<f64>::zeros((perm.len(), perm.len()));
8541        for (a, &ia) in perm.iter().enumerate() {
8542            for (b, &ib) in perm.iter().enumerate() {
8543                hc[[a, b]] = h[[ia, ib]];
8544            }
8545        }
8546        canonical.warm_start_outer_hessian = Some(hc);
8547    }
8548    canonical
8549}
8550
8551/// The solver ladder behind [`run_outer`], without the #934 self-audit.
8552pub(crate) fn run_outer_uncertified(
8553    obj: &mut dyn OuterObjective,
8554    config: &OuterConfig,
8555    context: &str,
8556) -> Result<OuterResult, EstimationError> {
8557    let cap = primary_capability_for_config(obj.capability(), config, context);
8558    cap.validate_layout(context)?;
8559    // #2370: reject a degenerate / inverted ρ-box up front, as a typed error.
8560    // Every downstream stage — the per-atom EFS path below and
8561    // `run_outer_with_plan` — projects seeds against these bounds with
8562    // `f64::clamp`, whose `min > max` (or NaN) precondition panics *inside the
8563    // Rust boundary* and surfaces as an opaque `GamError: ... panicked` across
8564    // the FFI, violating the fail-loudly contract. The configured box can invert
8565    // whenever an independently-derived upper bound drifts below the lower wall
8566    // (e.g. the custom-family effective-df ceiling vs. `rho_lower_bound`).
8567    // Validating the *effective* template here — the same one every consumer
8568    // reads — turns any such inversion into `EstimationError::InvalidInput`
8569    // regardless of how the bounds were constructed.
8570    {
8571        let (model_lo, model_hi) =
8572            outer_model_domain_bounds_template(config, cap.n_params);
8573        let (bound_lo, bound_hi) = outer_search_bounds_template(config, cap.n_params);
8574        if model_lo.len() != cap.n_params
8575            || model_hi.len() != cap.n_params
8576            || bound_lo.len() != cap.n_params
8577            || bound_hi.len() != cap.n_params
8578        {
8579            return Err(EstimationError::InvalidInput(format!(
8580                "{context}: outer bound dimension mismatch: parameters={},                  model_lower={}, model_upper={}, search_lower={}, search_upper={}",
8581                cap.n_params,
8582                model_lo.len(),
8583                model_hi.len(),
8584                bound_lo.len(),
8585                bound_hi.len(),
8586            )));
8587        }
8588        for i in 0..cap.n_params {
8589            if !(model_lo[i].is_finite() && model_hi[i].is_finite())
8590                || model_lo[i] > model_hi[i]
8591            {
8592                return Err(EstimationError::InvalidInput(format!(
8593                    "{context}: outer model-domain bounds are invalid at coordinate {i}:                      lower={}, upper={}",
8594                    model_lo[i], model_hi[i]
8595                )));
8596            }
8597            if bound_lo[i] < model_lo[i] || bound_hi[i] > model_hi[i] {
8598                return Err(EstimationError::InvalidInput(format!(
8599                    "{context}: outer search bounds escape the model domain at coordinate {i}:                      model=[{}, {}], search=[{}, {}]",
8600                    model_lo[i], model_hi[i], bound_lo[i], bound_hi[i]
8601                )));
8602            }
8603            if !(bound_lo[i].is_finite() && bound_hi[i].is_finite()) {
8604                return Err(EstimationError::InvalidInput(format!(
8605                    "{context}: outer rho bounds are non-finite at coordinate {i}: \
8606                     lower={}, upper={}",
8607                    bound_lo[i], bound_hi[i]
8608                )));
8609            }
8610
8611            // Report a collapsed interval with BOTH walls. `outer_bounds` below
8612            // is the backstop and rejects the same condition, but its message
8613            // names only the coordinate. The panic this guard replaced printed
8614            // `min = -10.0, max = -11.855421656441532`, and those two numbers
8615            // are what made #2370 diagnosable from a bug report alone: they
8616            // identify WHICH pair of independently-derived bounds drifted, and
8617            // by how much. A typed error must not be a weaker diagnostic than
8618            // the panic it replaced.
8619            //
8620            // Two tests constrain this string: `inverted_rho_box_is_a_typed_
8621            // error_not_a_clamp_panic_2370` greps for the word "bound", and
8622            // `the_inverted_box_refusal_carries_both_bound_values_2370` pins
8623            // both numeric walls. Keep both when rewording.
8624            if bound_lo[i] > bound_hi[i] {
8625                return Err(EstimationError::InvalidInput(format!(
8626                    "{context}: outer rho bounds are inverted at coordinate {i}: \
8627                     lower bound {} exceeds upper bound {}",
8628                    bound_lo[i], bound_hi[i]
8629                )));
8630            }
8631        }
8632        outer_bounds(&bound_lo, &bound_hi)
8633            .map_err(|err| EstimationError::InvalidInput(format!("{context}: {err}")))?;
8634    }
8635    if let Some(initial_rho) = config.initial_rho.as_ref() {
8636        cap.theta_layout()
8637            .validate_point_len(initial_rho, "initial outer seed")
8638            .map_err(|err| {
8639                EstimationError::fatal_objective_evaluation(
8640                    format!("{context}: initial outer seed validation"),
8641                    err,
8642                )
8643            })?;
8644    }
8645    // Frontier ρ-scaling auto-switch (#986): at per-atom-EFS-eligible frontier
8646    // rho dimension the decoupled per-atom fixed point is the primary outer
8647    // iteration; everything else falls through to the dense / standard path
8648    // below. Routed here so every entry point inherits it (magic by default).
8649    if let Some(result) = run_per_atom_efs_if_frontier(obj, config, context)? {
8650        if result.solver_claimed_convergence() {
8651            return Ok(result);
8652        }
8653        return Err(outer_nonconvergence_error(
8654            context,
8655            "per-atom EFS exhausted its iteration budget before the fixed-point step converged",
8656            &result,
8657            None,
8658            StationarityStandard::NoComparison,
8659        ));
8660    }
8661
8662    if cap.n_params == 0 {
8663        let cost = obj.eval_cost(&Array1::zeros(0))?;
8664        let the_plan = plan(&cap);
8665        let mut result =
8666            outer_result_with_gradient_norm(Array1::zeros(0), cost, 0, Some(0.0), true, the_plan);
8667        result.origin = OuterResultOrigin::EmptyParameterSpace;
8668        return Ok(result);
8669    }
8670
8671    // Build the ordered list of capabilities to attempt: primary first, then
8672    // any centrally-derived degraded capabilities. Aux direct-search has no
8673    // degraded ladder — a single attempt either succeeds or the failure is
8674    // surfaced to the caller.
8675    let fallback_attempts = match config.fallback_policy {
8676        FallbackPolicy::Automatic => automatic_fallback_attempts(&cap),
8677        FallbackPolicy::Disabled => Vec::new(),
8678    };
8679    let mut attempts: Vec<OuterCapability> = Vec::with_capacity(1 + fallback_attempts.len());
8680    attempts.push(cap.clone());
8681    for degraded in fallback_attempts {
8682        attempts.push(degraded);
8683    }
8684
8685    let mut last_error: Option<EstimationError> = None;
8686    let mut best_checkpoint: Option<OuterResult> = None;
8687    // A recoverable refusal at the point proposed by EFS says nothing against
8688    // the finite incumbent that proposed it.  Carry that incumbent across the
8689    // plan boundary exactly once; the analytic-gradient fallback must resume
8690    // it before any unrelated seed is considered.
8691    let mut fixed_point_continuation: Option<FixedPointContinuationCheckpoint> = None;
8692    // A fixed-point walk can stop normally while its mandatory analytic
8693    // screening certificate disproves the proposed root.  `run_outer_with_plan`
8694    // returns that state as `Exhausted`, not `Converged`; retain its best finite
8695    // checkpoint so the already-declared analytic-gradient fallback starts
8696    // there instead of replaying the same refuted fixed-point walk or throwing
8697    // away useful work.
8698    let mut refuted_fixed_point_continuation: Option<OuterResult> = None;
8699
8700    'plan_attempts: for (attempt_idx, attempt_cap) in attempts.iter().enumerate() {
8701        let the_plan = plan(attempt_cap);
8702        if attempt_idx > 0 {
8703            log::debug!("[OUTER] {context}: primary plan failed; falling back to {the_plan}");
8704        }
8705        log_plan(context, attempt_cap, &the_plan);
8706
8707        obj.reset();
8708
8709        let mut attempt_config = config.clone();
8710        if let Some(checkpoint) = fixed_point_continuation.take() {
8711            if !matches!(the_plan.solver, Solver::Bfgs) {
8712                return Err(EstimationError::RemlOptimizationFailed(format!(
8713                    "{context}: fixed-point continuation requires analytic-gradient BFGS, \
8714                     but the next declared plan is {the_plan}"
8715                )));
8716            }
8717            attempt_config.initial_rho = Some(checkpoint.point.clone());
8718            attempt_config.initial_inner_seed = checkpoint.inner_seed.clone();
8719            attempt_config.screen_initial_rho = false;
8720            // This is a mid-run finite incumbent, not a terminal certificate
8721            // imported from a prior fit.  Leaving the original config's cache
8722            // provenance set could let the zero-iteration resume path accept
8723            // the checkpoint without ever running the promised BFGS polish.
8724            attempt_config.initial_rho_is_prior_terminal_certificate = false;
8725            // A transferred Hessian is bound to the prior fit's terminal rho,
8726            // not to this mid-run EFS incumbent.  BFGS must rebuild curvature
8727            // from gradients at the continued point instead of combining two
8728            // different checkpoints.
8729            attempt_config.warm_start_outer_hessian = None;
8730            // The finite incumbent owns the nominal slot: try it first, without
8731            // screening or neutral-seed promotion. Preserve the caller's
8732            // absolute lattice, though. If this continuation certifies, the
8733            // ordinary seed loop stops immediately and no other start runs; if
8734            // it is refused or remains nonstationary, `should_start_next_seed`
8735            // may advance through the remaining bounded lattice until a fit
8736            // certifies. Setting `max_seeds = 1` here used to erase that recovery
8737            // authority exactly when the incumbent lay outside the criterion's
8738            // finite observed-information domain (#2653).
8739            attempt_config.seed_config.seed_budget = 1;
8740            log::info!(
8741                "[OUTER] {context}: resuming {the_plan} first from the last finite {:?} \
8742                 incumbent after {} iteration(s): cost={:.6e}, |step|={:.3e}, inner_beta={}",
8743                checkpoint.plan_used.solver,
8744                checkpoint.iterations,
8745                checkpoint.sample.value,
8746                checkpoint.sample.step.dot(&checkpoint.sample.step).sqrt(),
8747                checkpoint
8748                    .inner_seed
8749                    .as_ref()
8750                    .map_or(0, |seed| seed.beta.len()),
8751            );
8752        }
8753        if let Some(checkpoint) = refuted_fixed_point_continuation.take() {
8754            if !matches!(the_plan.solver, Solver::Bfgs) {
8755                return Err(EstimationError::RemlOptimizationFailed(format!(
8756                    "{context}: an analytically refuted fixed point requires \
8757                     analytic-gradient BFGS, but the next declared plan is {the_plan}"
8758                )));
8759            }
8760            attempt_config.initial_rho = Some(checkpoint.rho.clone());
8761            attempt_config.initial_inner_seed = None;
8762            attempt_config.screen_initial_rho = false;
8763            attempt_config.initial_rho_is_prior_terminal_certificate = false;
8764            attempt_config.warm_start_outer_hessian = None;
8765            // The checkpoint owns the first nominal slot.  Preserve the
8766            // caller's absolute recovery lattice: if this continuation still
8767            // refuses, `should_start_next_seed` may advance until a candidate
8768            // certifies, exactly as for a rho-local EFS trial refusal.
8769            attempt_config.seed_config.seed_budget = 1;
8770            log::info!(
8771                "[OUTER] {context}: analytic screening refuted the {:?} fixed point; \
8772                 resuming {the_plan} first from its best finite checkpoint after {} \
8773                 iteration(s): cost={:.6e}",
8774                checkpoint.plan_used.solver,
8775                checkpoint.iterations,
8776                checkpoint.final_value,
8777            );
8778        }
8779
8780        // ARC budget-exhaustion retry: when an Arc attempt runs out of
8781        // outer iterations, reseed a fresh Arc run from the previous
8782        // attempt's last ρ and trust radius. Inner caches (PIRLS LRU,
8783        // eval bundle, warm-start predictor, adaptive signals) are wiped
8784        // by `obj.reset()`; the operator-TR's Cauchy/Newton/CG state has
8785        // no resume API and is not preserved. The lever that changes for
8786        // the resumed run is the inner-PIRLS cap (uncapped via the
8787        // feedback handle), not `max_iter` — empirically the prior stall
8788        // was an inner-tolerance / model-fidelity issue, not an outer
8789        // budget shortfall, and doubling `max_iter` only replays the
8790        // same trajectory byte-for-byte. The retry is gated on observed
8791        // `‖g‖` progress so trajectories that made no headway fall
8792        // through to the degraded plan instead of replaying.
8793        let mut arc_retries_left: u32 = if matches!(the_plan.solver, Solver::Arc) {
8794            2
8795        } else {
8796            0
8797        };
8798        let mut retry_config: Option<OuterConfig> = None;
8799        // Tracks the previous ARC attempt's terminal `‖g‖`. The retry
8800        // gate compares attempt-over-attempt: if a retry didn't move
8801        // the gradient norm, the trajectory replayed (same seed, same
8802        // trust radius, cold caches, deterministic optimizer) and
8803        // further retries cannot help. First retry is unconditional
8804        // (no prior attempt to compare against).
8805        let mut prev_attempt_grad_norm: Option<f64> = None;
8806
8807        let outcome = loop {
8808            // Bind the active config by cloning into a local owned value so
8809            // subsequent retry-config assignment does not collide with the
8810            // borrow used inside this iteration body.
8811            let active_config_owned: OuterConfig = retry_config
8812                .clone()
8813                .unwrap_or_else(|| attempt_config.clone());
8814            let active_config: &OuterConfig = &active_config_owned;
8815            match run_outer_with_plan(obj, active_config, context, attempt_cap, &the_plan, true) {
8816                Ok(PlanRunOutcome::Converged(result)) => break Ok(result),
8817                Ok(PlanRunOutcome::FirstOrderFallbackRequested(request)) => {
8818                    log::debug!(
8819                        "[OUTER] {context}: attempt {} (plan={the_plan}) requested a joint \
8820                         first-order fallback: {}",
8821                        attempt_idx + 1,
8822                        request.reason(),
8823                    );
8824                    last_error = Some(EstimationError::RemlOptimizationFailed(
8825                        request.reason().to_string(),
8826                    ));
8827                    continue 'plan_attempts;
8828                }
8829                Ok(PlanRunOutcome::FixedPointContinuationRequested(request)) => {
8830                    let has_bfgs_fallback = attempts
8831                        .get(attempt_idx + 1)
8832                        .is_some_and(|next| matches!(plan(next).solver, Solver::Bfgs));
8833                    if !has_bfgs_fallback {
8834                        return Err(EstimationError::RemlOptimizationFailed(format!(
8835                            "{context}: {:?} refused a trial after {} finite iteration(s) \
8836                             at rho={} (cost={:.6e}), but no analytic-gradient BFGS \
8837                             continuation is declared: {}",
8838                            request.checkpoint.plan_used.solver,
8839                            request.checkpoint.iterations,
8840                            request.checkpoint.point,
8841                            request.checkpoint.sample.value,
8842                            request.refusal,
8843                        )));
8844                    }
8845                    last_error = Some(EstimationError::RemlOptimizationFailed(format!(
8846                        "{:?} continuation requested after rho-local trial refusal: {}",
8847                        request.checkpoint.plan_used.solver, request.refusal,
8848                    )));
8849                    fixed_point_continuation = Some(request.checkpoint);
8850                    continue 'plan_attempts;
8851                }
8852                Ok(PlanRunOutcome::Exhausted(result)) => {
8853                    // `Exhausted` is a proof-bearing outcome: every solver
8854                    // claim in this plan failed the mandatory analytic
8855                    // screening certificate.  A fixed-point solver may still
8856                    // leave `solver_claimed_convergence == true` on the retained
8857                    // checkpoint because its heuristic update was zero.  Do
8858                    // not collapse that checkpoint back into success below;
8859                    // continue it with the analytic-gradient fallback that the
8860                    // capability ladder already declared.
8861                    let has_bfgs_fallback = attempts
8862                        .get(attempt_idx + 1)
8863                        .is_some_and(|next| matches!(plan(next).solver, Solver::Bfgs));
8864                    if result.solver_claimed_convergence()
8865                        && matches!(the_plan.solver, Solver::Efs | Solver::HybridEfs)
8866                        && has_bfgs_fallback
8867                    {
8868                        log::info!(
8869                            "[OUTER] {context}: {:?} stopped at a fixed point, but no \
8870                             candidate passed analytic screening; continuing the best finite \
8871                             checkpoint with analytic-gradient BFGS",
8872                            the_plan.solver,
8873                        );
8874                        last_error = Some(EstimationError::RemlOptimizationFailed(format!(
8875                            "{:?} fixed point was refuted by analytic screening",
8876                            the_plan.solver,
8877                        )));
8878                        refuted_fixed_point_continuation = Some(result);
8879                        continue 'plan_attempts;
8880                    }
8881                    if arc_retries_left == 0
8882                        || matches!(
8883                            result.operator_stop_reason,
8884                            Some(
8885                                OperatorTrustRegionStopReason::RejectFloor
8886                                    // #1690: a flat-valley cost-stall is a CONVERGED
8887                                    // cost plateau over the whole stall window, not a
8888                                    // budget shortfall. The ARC retry only reseeds
8889                                    // from the same last ρ with a reset trust radius
8890                                    // and the same deterministic operator state, so it
8891                                    // replays the identical trajectory and re-halts at
8892                                    // the same valley floor with the same |g| (verified
8893                                    // on the #1690 Gamma repro: two retries, each
8894                                    // returning |g|=0.3646 byte-for-byte). Treat it
8895                                    // like `RejectFloor` and stop — the genuine
8896                                    // stationarity verdict is reconciled downstream
8897                                    // against the authoritative shipped-β gradient
8898                                    // (`optimizer.rs`), and a non-stationary floor is
8899                                    // still reported non-converged. This skips the
8900                                    // wasted full-trajectory replay that dominated the
8901                                    // count-family slowdown.
8902                                    | OperatorTrustRegionStopReason::CostStallFlatValley
8903                            )
8904                        )
8905                    {
8906                        break Ok(result);
8907                    }
8908                    // Gate the retry on attempt-over-attempt `‖g‖`
8909                    // progress. The first retry is unconditional (no
8910                    // prior attempt). Subsequent retries fall through
8911                    // to the degraded plan when the gradient norm did
8912                    // not materially shrink — the deterministic
8913                    // optimizer with the same seed and trust radius
8914                    // would replay the same trajectory.
8915                    let Some(cur_grad_norm) = result.final_grad_norm else {
8916                        log::info!(
8917                            "[OUTER] {context}: ARC attempt exhausted budget at \
8918                             iter={} cost={:.6e} without a final gradient norm; \
8919                             falling through to degraded plan",
8920                            result.iterations,
8921                            result.final_value,
8922                        );
8923                        break Ok(result);
8924                    };
8925                    if let Some(prev_g) = prev_attempt_grad_norm {
8926                        // The gate's job, in its own words above, is to catch a
8927                        // trajectory that "didn't move the gradient norm" — a
8928                        // REPLAY: same seed, same trust radius, cold caches,
8929                        // deterministic optimizer, so the retry recomputes what
8930                        // the previous attempt already computed. That is
8931                        // `cur >= prev`.
8932                        //
8933                        // It was implemented as `cur < 0.5 * prev`, which is a
8934                        // HALVING requirement, and a retry that improves the
8935                        // gradient by less than a factor of two was declared a
8936                        // replay and threw away the rest of a two-retry budget.
8937                        // Measured on gam#2735's stress fixture: `|g|` went
8938                        // 1.966390e0 → 1.605910e0, an 18 % reduction — plainly
8939                        // not a replay — and the ladder fell through to the
8940                        // degraded plan with a retry still unspent. The retry
8941                        // count (`arc_retries_left = 2`) is what bounds slow
8942                        // grinding; this gate only has to tell motion from
8943                        // stillness.
8944                        let progressed = cur_grad_norm.is_finite()
8945                            && prev_g.is_finite()
8946                            && cur_grad_norm < prev_g;
8947                        if !progressed {
8948                            log::info!(
8949                                "[OUTER] {context}: ARC retry stalled at \
8950                                 iter={} cost={:.6e} |g|={:.6e} (prev |g|={:.6e}, \
8951                                 ratio {:.4}); the retry did not reduce the gradient \
8952                                 at all, so deterministic replay is suspected and \
8953                                 further retries cannot help; falling through to \
8954                                 degraded plan",
8955                                result.iterations,
8956                                result.final_value,
8957                                cur_grad_norm,
8958                                prev_g,
8959                                cur_grad_norm / prev_g,
8960                            );
8961                            break Ok(result);
8962                        }
8963                        log::info!(
8964                            "[OUTER] {context}: ARC retry reduced the gradient \
8965                             {:.6e} -> {:.6e} (ratio {:.4}); spending another of \
8966                             the {} remaining retries rather than reading slow \
8967                             progress as a replay",
8968                            prev_g,
8969                            cur_grad_norm,
8970                            cur_grad_norm / prev_g,
8971                            arc_retries_left,
8972                        );
8973                    }
8974                    let next_trust_radius =
8975                        sanitized_operator_trust_restart_radius(result.operator_trust_radius);
8976                    log::info!(
8977                        "[OUTER] {context}: ARC attempt exhausted budget at \
8978                         iter={} cost={:.6e} |g|={:.6e}; resuming from last \
8979                         rho + trust_radius={:?}, inner-PIRLS uncapped \
8980                         (objective caches wiped; operator-TR Cauchy/Newton \
8981                         state is not resumable)",
8982                        result.iterations,
8983                        result.final_value,
8984                        cur_grad_norm,
8985                        next_trust_radius,
8986                    );
8987                    // Snapshot the cap-feedback handle before we
8988                    // reassign `retry_config` (which currently backs
8989                    // `active_config`'s borrow). `InnerProgressFeedback`
8990                    // is an Arc-wrapper bundle, so the clone is cheap.
8991                    let cap_feedback = active_config.outer_inner_cap.clone();
8992                    let mut next = active_config.clone();
8993                    prev_attempt_grad_norm = Some(cur_grad_norm);
8994                    next.initial_rho = Some(result.rho.clone());
8995                    next.operator_initial_trust_radius = next_trust_radius;
8996                    retry_config = Some(next);
8997                    arc_retries_left -= 1;
8998                    obj.reset();
8999                    // Lift any inner-PIRLS cap for the resumed run. The
9000                    // schedule's cold-start ladder (3/5/10) would
9001                    // re-coarsen exactly the inner solves whose tolerance
9002                    // is suspected to have starved the prior trajectory.
9003                    // The next outer iter consumes ρ near a near-stationary
9004                    // point where exact β / gradient / Hessian is the
9005                    // load-bearing input to the operator-TR geometry.
9006                    if let Some(feedback) = cap_feedback.as_ref() {
9007                        feedback.cap.store(0, Ordering::Relaxed);
9008                    }
9009                }
9010                Err(e) => break Err(e),
9011            }
9012        };
9013
9014        match outcome {
9015            Ok(result) => {
9016                if result.solver_claimed_convergence() {
9017                    return Ok(result);
9018                }
9019
9020                let improves_checkpoint = result.final_value.is_finite()
9021                    && best_checkpoint.as_ref().is_none_or(|checkpoint| {
9022                        !checkpoint.final_value.is_finite()
9023                            || result.final_value < checkpoint.final_value
9024                    });
9025                if improves_checkpoint {
9026                    best_checkpoint = Some(result);
9027                }
9028
9029                let message = format!(
9030                    "{context}: attempt {} (plan={the_plan}) exhausted without convergence",
9031                    attempt_idx + 1
9032                );
9033                log::debug!("[OUTER] {message}; trying degraded fallback plan");
9034                last_error = Some(EstimationError::RemlOptimizationFailed(message));
9035            }
9036            Err(e) => {
9037                if e.is_fatal_outer_evaluation() {
9038                    return Err(e);
9039                }
9040                log::debug!(
9041                    "[OUTER] {context}: attempt {} (plan={the_plan}) failed: {e}",
9042                    attempt_idx + 1
9043                );
9044                last_error = Some(e);
9045            }
9046        }
9047    }
9048
9049    if let Some(checkpoint) = best_checkpoint {
9050        // The solver ladder produced no result that its OWN internal
9051        // (raw-gradient) convergence test accepted — but that test cannot see a
9052        // railed or already-stationary optimum. At a smoothing parameter railed to
9053        // the ρ box floor (λ→0, e.g. an exact linear fit or a separated smooth),
9054        // the RAW gradient stays large along the railed axis — it "wants" to push
9055        // past the boundary — so the solver reports non-convergence and can take
9056        // zero steps, even though the KKT-PROJECTED gradient (which zeroes
9057        // outward-railed axes) is stationary and no feasible step reduces the
9058        // objective. Only the mandatory analytic certificate in `run_outer`
9059        // computes that projected gradient AND the curvature-scaled flat-valley
9060        // bound (½·gᵀH⁻¹g ≤ objective_tol), so IT, not this raw-gradient ladder, is
9061        // the sole authority on stationarity. Hand it the best finite checkpoint:
9062        // `certify_outer_optimality` mints iff the point is genuinely stationary
9063        // (interior, railed, or flat-valley) and returns typed non-convergence
9064        // otherwise, so a truly divergent fit is still rejected there.
9065        return Ok(checkpoint);
9066    }
9067
9068    Err(last_error.unwrap_or_else(|| {
9069        EstimationError::RemlOptimizationFailed(format!("all plan attempts exhausted ({context})"))
9070    }))
9071}
9072
9073// ─── Frontier ρ-scaling auto-switch (issue #986) ─────────────────────────
9074//
9075// ARD-per-atom assigns one smoothing coordinate per dictionary atom, so the
9076// ρ-vector reaches 10^4–10^5 coordinates. A dense outer quasi-Newton over that
9077// materializes an O(K²) Hessian and is impossible at scale. When the ρ-dimension
9078// is frontier-scale AND every coordinate is penalty-like with a working
9079// fixed-point hook, route the PRIMARY outer iteration to the per-atom decoupled
9080// EFS path (`crate::estimate::reml::per_atom_efs`) instead of the dense
9081// ARC/BFGS lane. The decision is auto-derived from the coordinate count alone —
9082// there is no flag — and it is additive: the dense path is unchanged for small K
9083// and for any objective that is not per-atom-EFS-eligible.
9084
9085/// Whether this capability is in the frontier ρ-scaling regime where the
9086/// per-atom decoupled EFS primary should take over from the dense outer.
9087///
9088/// Delegates the eligibility decision to
9089/// [`crate::estimate::reml::per_atom_efs::per_atom_efs_eligible`], which
9090/// requires all-penalty-like coordinates, a working `eval_efs` hook,
9091/// fixed-point not disabled, and a frontier-scale ρ-dimension. This is the
9092/// single auto-switch predicate; `plan` keeps selecting the
9093/// dense or standard-EFS solver for everything below the frontier threshold.
9094pub fn is_per_atom_efs_frontier(cap: &OuterCapability) -> bool {
9095    crate::estimate::reml::per_atom_efs::per_atom_efs_eligible(cap)
9096}
9097
9098/// Auto-switch entry point: when `cap` is frontier-scale per-atom-EFS-eligible,
9099/// run the per-atom decoupled EFS primary and return its [`OuterResult`];
9100/// otherwise return `Ok(None)` so the caller falls through to the existing dense
9101/// / standard-EFS path via [`OuterProblem::run`] / [`run_outer`].
9102///
9103/// Builds the same bounded seed and tolerance/budget the standard plan path
9104/// uses, picks the seed (initial-ρ if supplied, else the first generated
9105/// candidate — the per-atom fixed point is a contraction near the optimum and
9106/// does not need the multi-seed cascade the dense path runs for its non-convex
9107/// quasi-Newton surface), then drives the per-atom EFS loop. The shared-border
9108/// topology defaults to disjoint (every atom owns a private penalty block — the
9109/// common ARD-per-atom case); callers with a known arrow-border overlap can run
9110/// the module's `run_per_atom_efs` directly with a populated
9111/// `SharedBorderTopology`.
9112///
9113/// Additive: this function neither mutates nor bypasses the dense path; it is
9114/// the pre-dispatch shortcut [`run_outer`] calls before the dense ladder.
9115pub(crate) fn run_per_atom_efs_if_frontier(
9116    obj: &mut dyn OuterObjective,
9117    config: &OuterConfig,
9118    context: &str,
9119) -> Result<Option<OuterResult>, EstimationError> {
9120    let cap = primary_capability_for_config(obj.capability(), config, context);
9121    cap.validate_layout(context)?;
9122    if !is_per_atom_efs_frontier(&cap) {
9123        return Ok(None);
9124    }
9125
9126    let the_plan = plan(&cap);
9127    let rho_dim = cap.theta_layout().rho_dim();
9128
9129    let model_domain_bounds = outer_model_domain_bounds_template(config, cap.n_params);
9130    crate::estimate::reml::outer_eval::record_current_outer_rho_model_upper_bounds_for_ift(
9131        &model_domain_bounds.1,
9132    );
9133    let (lower, upper) = outer_search_bounds_template(config, cap.n_params);
9134
9135    // Seed: cache/explicit initial ρ if present, otherwise the first generated
9136    // candidate. The per-atom multiplicative fixed point is locally
9137    // contractive, so a single seed suffices; the heavy multi-seed cascade
9138    // exists for the dense quasi-Newton's non-convex surface, not for EFS.
9139    let seed = match config.initial_rho.as_ref() {
9140        Some(initial) if initial.len() == cap.n_params => initial.clone(),
9141        _ => {
9142            let generated = crate::seeding::generate_rho_candidates(
9143                cap.n_params,
9144                config.heuristic_lambdas.as_deref(),
9145                &config.seed_config,
9146            )?;
9147            match generated.into_iter().next() {
9148                Some(first) => first,
9149                None => Array1::<f64>::zeros(cap.n_params),
9150            }
9151        }
9152    };
9153
9154    log::info!(
9155        "[OUTER] {context}: frontier ρ-scaling (rho_dim={rho_dim}) → per-atom decoupled EFS primary"
9156    );
9157
9158    let pa_cfg = crate::estimate::reml::per_atom_efs::PerAtomEfsConfig::new(
9159        config.tolerance,
9160        config.max_iter,
9161        lower,
9162        upper,
9163    );
9164    let topology = crate::estimate::reml::per_atom_efs::SharedBorderTopology::disjoint(rho_dim);
9165
9166    obj.reset();
9167    install_matching_initial_inner_seed(obj, config, &seed, context)?;
9168    let result =
9169        crate::estimate::reml::per_atom_efs::run_per_atom_efs(obj, &seed, &pa_cfg, &topology)?;
9170    Ok(Some(result.into_outer_result(the_plan)))
9171}
9172
9173#[cfg(test)]
9174#[path = "inverted_rho_box_tests.rs"]
9175mod inverted_rho_box_tests;
9176
9177pub(crate) fn outer_bounds(lo: &Array1<f64>, hi: &Array1<f64>) -> Result<Bounds, EstimationError> {
9178    Bounds::new(lo.clone(), hi.clone(), 1e-6).map_err(|err| {
9179        EstimationError::InvalidInput(format!("outer rho bounds are invalid: {err}"))
9180    })
9181}
9182
9183pub(crate) fn outer_model_domain_bounds_template(
9184    config: &OuterConfig,
9185    n: usize,
9186) -> (Array1<f64>, Array1<f64>) {
9187    config.model_domain_bounds.clone().unwrap_or_else(|| {
9188        (
9189            Array1::<f64>::from_elem(n, -config.rho_bound),
9190            Array1::<f64>::from_elem(n, config.rho_bound),
9191        )
9192    })
9193}
9194
9195pub(crate) fn outer_search_bounds_template(
9196    config: &OuterConfig,
9197    n: usize,
9198) -> (Array1<f64>, Array1<f64>) {
9199    config
9200        .search_bounds_override
9201        .clone()
9202        .unwrap_or_else(|| outer_model_domain_bounds_template(config, n))
9203}
9204
9205/// Intersect typed objective-domain faces with the caller's declared model
9206/// domain. The resulting box is the immutable feasible set used by every
9207/// stationarity certificate. Algorithmic active-set reduction is represented
9208/// separately by `search_bounds_override` and cannot redefine this domain.
9209pub(super) fn install_objective_domain(
9210    config: &mut OuterConfig,
9211    n_params: usize,
9212    objective_lower: Option<Array1<f64>>,
9213    objective_upper: Option<Array1<f64>>,
9214) -> Result<(), EstimationError> {
9215    let (mut lower, mut upper) = outer_model_domain_bounds_template(config, n_params);
9216    if lower.len() != n_params || upper.len() != n_params {
9217        return Err(EstimationError::InvalidInput(format!(
9218            "outer configured bounds dimension mismatch: parameters={n_params}, lower={}, upper={}",
9219            lower.len(),
9220            upper.len(),
9221        )));
9222    }
9223    if let Some(domain) = objective_lower.as_ref()
9224        && domain.len() != n_params
9225    {
9226        return Err(EstimationError::InvalidInput(format!(
9227            "outer objective-domain lower-bound dimension mismatch: parameters={n_params}, lower={}",
9228            domain.len()
9229        )));
9230    }
9231    if let Some(domain) = objective_upper.as_ref()
9232        && domain.len() != n_params
9233    {
9234        return Err(EstimationError::InvalidInput(format!(
9235            "outer objective-domain upper-bound dimension mismatch: parameters={n_params}, upper={}",
9236            domain.len()
9237        )));
9238    }
9239    for index in 0..n_params {
9240        if let Some(domain) = objective_lower.as_ref() {
9241            let value = domain[index];
9242            if !value.is_finite() {
9243                return Err(EstimationError::InvalidInput(format!(
9244                    "outer objective-domain lower bound[{index}] must be finite; got {value}"
9245                )));
9246            }
9247            lower[index] = lower[index].max(value);
9248        }
9249        if let Some(domain) = objective_upper.as_ref() {
9250            let value = domain[index];
9251            if !value.is_finite() {
9252                return Err(EstimationError::InvalidInput(format!(
9253                    "outer objective-domain upper bound[{index}] must be finite; got {value}"
9254                )));
9255            }
9256            upper[index] = upper[index].min(value);
9257        }
9258        if !(lower[index].is_finite() && upper[index].is_finite() && lower[index] < upper[index]) {
9259            return Err(EstimationError::InvalidInput(format!(
9260                "outer objective-domain intersection is empty or non-finite at coordinate {index}: lower={}, upper={}",
9261                lower[index], upper[index]
9262            )));
9263        }
9264    }
9265    config.model_domain_bounds = Some((lower, upper));
9266    config.search_bounds_override = None;
9267    Ok(())
9268}
9269
9270pub(crate) fn outer_tolerance(value: f64) -> Result<Tolerance, EstimationError> {
9271    Tolerance::new(value)
9272        .map_err(|err| EstimationError::InvalidInput(format!("outer tolerance is invalid: {err}")))
9273}
9274
9275/// The relative cost floor shared by the cost-stall guard, the curvature-scaled
9276/// flat-valley certificate, and the certify-last resume progress gate: nothing
9277/// tighter than what the in-loop stall detector already proved about the
9278/// surface. `rel_cost_tolerance` when set, else a small fraction of the absolute
9279/// tolerance, never below `COST_STALL_REL_TOL_FLOOR`.
9280pub(crate) fn outer_rel_cost_floor(config: &OuterConfig) -> f64 {
9281    config
9282        .rel_cost_tolerance
9283        .unwrap_or(config.tolerance * 1.0e-2)
9284        .max(COST_STALL_REL_TOL_FLOOR)
9285}
9286
9287/// Whether a certify-last checkpoint reseed (#2273/#2374) exploited real descent.
9288///
9289/// A reseed that does not strictly reduce the outer objective past the shared
9290/// relative cost floor `rel_cost_floor·(1 + min(|prior|, |retried|))` is at a
9291/// genuine non-stationary floor (or a true flat valley) a fresh metric cannot
9292/// escape, so the resume loop must stop rather than spend its remaining budget
9293/// re-deriving the same refusal. Anchoring the floor on the SMALLER of the two
9294/// costs keeps a tiny uphill wobble from a metric restart from reading as
9295/// progress, and a non-finite retried value is never progress.
9296pub(crate) fn certify_resume_made_progress(
9297    prior_value: f64,
9298    retried_value: f64,
9299    rel_cost_floor: f64,
9300) -> bool {
9301    let floor = rel_cost_floor * (1.0 + prior_value.abs().min(retried_value.abs()));
9302    retried_value.is_finite() && retried_value < prior_value - floor
9303}
9304
9305/// The user-requested outer precision, expressed relative to the criterion's
9306/// magnitude — the mgcv `magic` rule `‖g‖ ≤ τ·(1 + |V|)`.
9307///
9308/// This is a CONVERGENCE tolerance, not a resolution floor: at the default
9309/// `τ = 1e-5` it sits ~670× above the arithmetic floor `√ε`. What it needs from
9310/// the caller is `|V|`, and the whole content of #2613 is *which* `|V|`.
9311#[inline]
9312pub(crate) fn outer_cost_relative_tolerance(config: &OuterConfig) -> f64 {
9313    config.rel_cost_tolerance.unwrap_or(config.tolerance)
9314}
9315
9316/// The arithmetic resolution of the declared objective scale.
9317///
9318/// A matrix-factorization REML/LAML score cannot resolve relative perturbations
9319/// below the forward-error scale √ε. Requiring a smaller absolute residual made
9320/// gradient-only / operator-curvature objectives impossible to certify unless
9321/// an unrelated Hessian or probe-noise rescue happened to be available (#2269).
9322#[inline]
9323fn outer_arithmetic_gradient_floor(config: &OuterConfig) -> f64 {
9324    config
9325        .objective_scale
9326        .map(|scale| config.tolerance.max(scale * f64::EPSILON.sqrt()))
9327        .unwrap_or(config.tolerance)
9328}
9329
9330/// The stationarity band handed to the SOLVER, and to the cost-stall guard's
9331/// stationarity gate.
9332///
9333/// It is a function of the DECLARED problem and of nothing else. `opt` resolves
9334/// a `GradientTolerance` exactly once, at run start, against the seed cost —
9335/// so handing it a `rel_cost` component makes the band a function of where a
9336/// seed happened to land. On #2392's exponentially stiff recovery that produced
9337/// an eighteen-order spread across the seeds of ONE fit: a generated lattice
9338/// seed at `ρ = 1.0`, where the criterion is `1.79e13`, gave
9339///
9340/// ```text
9341/// termination=gradient_tolerance(|g|=1.522998e-4 < 1.792397e8)
9342/// ```
9343///
9344/// i.e. the solver claimed convergence on the wrong rail against a threshold no
9345/// gradient can fail. A stationarity test that depends on the starting point is
9346/// not a stationarity test: two seeds converging to the same optimum must reach
9347/// the same verdict.
9348///
9349/// So the cost-relative term is anchored on `objective_scale` — a property of
9350/// the data (`n_obs` on the routes that set it), fixed for the whole fit. On
9351/// those routes this is magnitude-preserving, because a REML/LAML score is a
9352/// sum over `n` rows and `1 + |V| = O(n)` is what `1 + scale` already says.
9353///
9354/// When no scale is declared, gam does not know the criterion's magnitude, and
9355/// the honest band is the absolute tolerance the caller asked for. It does NOT
9356/// silently substitute a trajectory point for the thing it does not know. The
9357/// consequence — an outer loop that keeps stepping past the point a
9358/// score-relative band would have stopped at — is bounded by the cost-stall
9359/// guard, whose own score-relative rung
9360/// (`flat_valley_converged_grad_bound(best_value)`) is anchored at the BEST
9361/// iterate and is therefore the correctly-anchored version of the same idea.
9362///
9363/// The certificate keeps the point-anchored form, which is what mgcv means:
9364/// see [`outer_stationarity_band_and_rung_at`].
9365pub(crate) fn outer_gradient_tolerance(config: &OuterConfig) -> GradientTolerance {
9366    let mut abs = outer_engine_gradient_band(config);
9367    // #2568 -- a caller's requirement is the one input to this band that may
9368    // TIGHTEN it. Everything above widens: the arithmetic floor and the
9369    // scale-relative rung both exist to stop the optimizer chasing digits the
9370    // criterion cannot resolve. A caller asking for `|Pg| <= 1e-3` on a fit
9371    // whose sealed band is `1.0` is asking the search to keep working, so the
9372    // requirement enters HERE and not only at the certificate -- surfacing the
9373    // number without letting it drive the search moves the disappointment later
9374    // without changing the answer.
9375    //
9376    // `min`, never `max`: a requirement looser than the engine's own band is not
9377    // a request for anything, and honouring it would let a caller *weaken* a
9378    // standard the engine derived from the criterion's resolution.
9379    if let Some(required) = config.required_projected_gradient_norm {
9380        abs = abs.min(required);
9381    }
9382    GradientTolerance {
9383        abs,
9384        rel_initial_grad: None,
9385        // Never delegated: `opt`'s only anchor is the seed. See above.
9386        rel_cost: None,
9387        projected: true,
9388    }
9389}
9390
9391/// The engine's own declared band, BEFORE any caller requirement caps it.
9392///
9393/// Split out of [`outer_gradient_tolerance`] for #2688: a rung cannot tell
9394/// "the engine decided this" from "the caller decided this" out of a number in
9395/// which the two have already been `min`-ed together.
9396fn outer_engine_gradient_band(config: &OuterConfig) -> f64 {
9397    let mut abs = outer_arithmetic_gradient_floor(config);
9398    if let Some(scale) = config.objective_scale {
9399        abs = abs.max(outer_cost_relative_tolerance(config) * (1.0 + scale));
9400    }
9401    abs
9402}
9403
9404/// A certificate band together with the rung that produced it (#2688).
9405///
9406/// The band is decided three ways and every one of them used to reach the call
9407/// site as a bare `f64` that was then labelled `SolverBand` unconditionally.
9408/// Returning the pair is the invariant [`StationarityBound::from_ladder`]
9409/// already enforces one level up, pushed down to where the number is decided,
9410/// so no `SolverBand` literal is left at the call site for a fourth branch to
9411/// drift past.
9412#[derive(Debug, Clone, Copy)]
9413pub(crate) struct CertificateBandAt {
9414    /// The band the certificate applies.
9415    pub(crate) bound: f64,
9416    /// Which of the three inputs produced [`Self::bound`].
9417    pub(crate) source: StationarityBoundSource,
9418    /// What the ENGINE would have applied with no caller requirement, reported
9419    /// beside `bound` so a reader can judge whether the requirement was
9420    /// reasonable. Equal to [`Self::bound`] unless the cap bound.
9421    pub(crate) engine_bound: f64,
9422    /// The rung that produced [`Self::engine_bound`]. Never `CallerRequirement`.
9423    pub(crate) engine_source: StationarityBoundSource,
9424}
9425
9426/// The stationarity band a CERTIFICATE applies at the point it is judging.
9427///
9428/// Same formula, correct anchor: `cost_at_point` is the criterion value of the
9429/// candidate optimum, which is what the mgcv `magic` rule means by `f₀` and
9430/// what every consumer of a certificate reads the bound as. Unlike the solver's
9431/// band this one is resolved per point, so it costs nothing to anchor it right.
9432///
9433/// Floored at the SOLVER's band by construction. A certificate tighter than the
9434/// threshold the optimizer was told to reach manufactures the "solver claimed
9435/// convergence, certificate refused" family out of nothing but a disagreement
9436/// between two spellings of one tolerance: the solver stops exactly where it
9437/// was asked to and the certificate then declares the stop illegitimate. The
9438/// certificate may be LOOSER — that is what the score-relative widening is for
9439/// — but never stricter.
9440/// The value is bit-for-bit what this returned before #2688:
9441/// `min(max(engine, score_relative), required)` is the same number as the old
9442/// `min(max(min(engine, required), score_relative), required)`, the inner `min`
9443/// being dominated by the outer one in every ordering of the three. What
9444/// changed is that the caller now also learns WHICH of the three it got.
9445pub(crate) fn outer_stationarity_band_and_rung_at(
9446    config: &OuterConfig,
9447    cost_at_point: f64,
9448) -> CertificateBandAt {
9449    let engine_band = outer_engine_gradient_band(config);
9450    // A non-finite criterion value anchors nothing, so the declared band stands.
9451    let score_relative = if cost_at_point.is_finite() {
9452        outer_cost_relative_tolerance(config) * (1.0 + cost_at_point.abs())
9453    } else {
9454        f64::NEG_INFINITY
9455    };
9456    // Strict `>`: on a tie the widening added nothing and must not claim the rung.
9457    let (engine_bound, engine_source) = if score_relative > engine_band {
9458        (
9459            score_relative,
9460            StationarityBoundSource::CertificateScoreRelative,
9461        )
9462    } else {
9463        (engine_band, StationarityBoundSource::SolverBand)
9464    };
9465    // #2568 -- the score-relative widening above is what produced the saturated
9466    // `bound = 1.000e0`, so a caller requirement that did not survive it would
9467    // be defeated by exactly the case it was introduced for. Cap after widening.
9468    // This does NOT manufacture the "solver claimed convergence, certificate
9469    // refused" family warned about above: `outer_gradient_tolerance` is floored
9470    // at the same requirement, so the solver was told to reach the number the
9471    // certificate now applies. #2688 -- strict `<`, and the rung says so.
9472    match config.required_projected_gradient_norm {
9473        Some(required) if required < engine_bound => CertificateBandAt {
9474            bound: required,
9475            source: StationarityBoundSource::CallerRequirement,
9476            engine_bound,
9477            engine_source,
9478        },
9479        _ => CertificateBandAt {
9480            bound: engine_bound,
9481            source: engine_source,
9482            engine_bound,
9483            engine_source,
9484        },
9485    }
9486}
9487
9488pub(crate) fn outer_max_iterations(value: usize) -> Result<MaxIterations, EstimationError> {
9489    MaxIterations::new(value)
9490        .map_err(|err| EstimationError::InvalidInput(format!("outer max_iter is invalid: {err}")))
9491}
9492
9493pub(crate) fn sanitized_operator_trust_restart_radius(radius: Option<f64>) -> Option<f64> {
9494    radius
9495        .filter(|value| value.is_finite() && *value > 0.0)
9496        .map(|value| value.max(OPERATOR_TRUST_RESTART_RADIUS_FLOOR))
9497}
9498
9499pub(crate) fn bfgs_axis_step_caps(
9500    config: &OuterConfig,
9501    layout: OuterThetaLayout,
9502) -> Option<Array1<f64>> {
9503    if config.bfgs_step_cap.is_none() && config.bfgs_step_cap_psi.is_none() {
9504        return None;
9505    }
9506    let mut caps = Array1::from_elem(layout.n_params, f64::INFINITY);
9507    if let Some(cap) = config.bfgs_step_cap {
9508        for i in 0..layout.rho_dim() {
9509            caps[i] = cap;
9510        }
9511    }
9512    if let Some(cap) = config.bfgs_step_cap_psi {
9513        for i in layout.rho_dim()..layout.n_params {
9514            caps[i] = cap;
9515        }
9516    }
9517    Some(caps)
9518}
9519
9520pub(crate) enum FixedPointOuterRunError {
9521    SeedRejected(ObjectiveEvalError),
9522    IterationRejected(FixedPointContinuationRequest),
9523    ImmediateFallback(FirstOrderFallbackRequest),
9524    Failed(EstimationError),
9525}
9526
9527/// Last complete fixed-point incumbent preceding a refused trial point.
9528///
9529/// `opt::FixedPoint` currently returns only the refused evaluation's message;
9530/// it does not return its still-finite incumbent on `ObjectiveFailed`.  This
9531/// carrier preserves the exact optimizer state needed to continue with a
9532/// different algorithm: outer point, criterion, proposed fixed-point step,
9533/// fixed-point status, completed iteration count, plan, and the matching inner
9534/// coefficient state when the EFS producer supplied one.
9535#[derive(Clone, Debug)]
9536pub(crate) struct FixedPointContinuationCheckpoint {
9537    pub(crate) point: Array1<f64>,
9538    pub(crate) sample: FixedPointSample,
9539    pub(crate) iterations: usize,
9540    pub(crate) plan_used: OuterPlan,
9541    pub(crate) inner_seed: Option<BoundInnerSeed>,
9542}
9543
9544/// Typed request to continue a fixed-point incumbent after one rho-local
9545/// refusal.
9546///
9547/// The refusal remains an [`ObjectiveEvalError`] all the way through the plan
9548/// boundary, so the fallback decision is based on the producer's recoverable
9549/// verdict rather than on message text.
9550#[derive(Clone, Debug)]
9551pub(crate) struct FixedPointContinuationRequest {
9552    pub(crate) checkpoint: FixedPointContinuationCheckpoint,
9553    pub(crate) refusal: ObjectiveEvalError,
9554}
9555
9556/// Carries a fixed-point objective's complete typed refusal across the lossy
9557/// `opt` fixed-point return boundary.
9558///
9559/// `OuterFixedPointBridge::eval_step` returns a typed `ObjectiveEvalError`
9560/// whose kind is the producer's verdict: `Recoverable` for a refusal that is
9561/// a property of THIS rho (a non-finite cost, a non-finite EFS step, a
9562/// bubbled `RemlOptimizationFailed`, or any `EstimationError` for which
9563/// `is_trial_point_infeasible()` answers true), `Fatal` only for a structural
9564/// failure. `opt::FixedPoint::run` then does `err.into_message()` and hands
9565/// back `FixedPointError::ObjectiveFailed { message }`. This adapter retains
9566/// the original error in a publication slot, so its producer verdict, typed
9567/// source, and any first-order routing request all survive.
9568///
9569/// `run_fixed_point_outer_solver` used to answer that String with an
9570/// unconditional `fatal_outer_evaluation`, and `is_fatal_outer_evaluation()`
9571/// is a hard `return Err(e)` in BOTH the seed loop (`run_plan.rs`) and the
9572/// strategy ladder (`run.rs`). So one recoverable per-rho refusal at outer
9573/// iteration k killed the remaining seeds and the entire fallback ladder --
9574/// including the `disable_fixed_point` BFGS plan `automatic_fallback_attempts`
9575/// builds for precisely this situation. The SEED evaluation in the same
9576/// function never had this bug: it still holds the typed error there and asks
9577/// `is_recoverable()`. Only the iterations lost the verdict, in transit.
9578///
9579/// This adapter is the publication slot that gets it back -- the same device
9580/// `recurrent_incumbent_exit` uses to hand a value out of a moved bridge. The
9581/// slot is written on EVERY evaluation (cleared to `None` on success), so an
9582/// error can never be read stale.
9583pub(crate) struct RetainingObjective<ObjFn> {
9584    inner: ObjFn,
9585    last_error: Arc<Mutex<Option<ObjectiveEvalError>>>,
9586}
9587
9588impl<ObjFn> RetainingObjective<ObjFn> {
9589    pub(crate) fn new(
9590        inner: ObjFn,
9591        last_error: Arc<Mutex<Option<ObjectiveEvalError>>>,
9592    ) -> Self {
9593        Self { inner, last_error }
9594    }
9595
9596    fn publish<T>(&self, outcome: &Result<T, ObjectiveEvalError>) {
9597        *self
9598            .last_error
9599            .lock()
9600            .expect("objective error publication lock poisoned") =
9601            outcome.as_ref().err().cloned();
9602    }
9603}
9604
9605impl<ObjFn> ZerothOrderObjective for RetainingObjective<ObjFn>
9606where
9607    ObjFn: ZerothOrderObjective,
9608{
9609    fn eval_cost(&mut self, x: &Array1<f64>) -> Result<f64, ObjectiveEvalError> {
9610        let outcome = self.inner.eval_cost(x);
9611        self.publish(&outcome);
9612        outcome
9613    }
9614}
9615
9616impl<ObjFn> FirstOrderObjective for RetainingObjective<ObjFn>
9617where
9618    ObjFn: FirstOrderObjective,
9619{
9620    fn eval_grad(&mut self, x: &Array1<f64>) -> Result<FirstOrderSample, ObjectiveEvalError> {
9621        let outcome = self.inner.eval_grad(x);
9622        self.publish(&outcome);
9623        outcome
9624    }
9625
9626    fn set_finite_difference_bounds(&mut self, bounds: Option<&Bounds>) {
9627        self.inner.set_finite_difference_bounds(bounds);
9628    }
9629}
9630
9631impl<ObjFn> SecondOrderObjective for RetainingObjective<ObjFn>
9632where
9633    ObjFn: SecondOrderObjective,
9634{
9635    fn eval_hessian(&mut self, x: &Array1<f64>) -> Result<SecondOrderSample, ObjectiveEvalError> {
9636        let outcome = self.inner.eval_hessian(x);
9637        self.publish(&outcome);
9638        outcome
9639    }
9640}
9641
9642impl<ObjFn> FixedPointObjective for RetainingObjective<ObjFn>
9643where
9644    ObjFn: FixedPointObjective,
9645{
9646    fn eval_step(&mut self, x: &Array1<f64>) -> Result<FixedPointSample, ObjectiveEvalError> {
9647        let outcome = self.inner.eval_step(x);
9648        self.publish(&outcome);
9649        outcome
9650    }
9651}
9652
9653/// Retains every successful fixed-point sample as well as the typed error
9654/// retained by [`RetainingObjective`].
9655///
9656/// A failed evaluation belongs to the proposed *next* point.  Therefore it
9657/// must not overwrite `incumbent`: that slot remains the exact last finite
9658/// point from which another solver can continue.
9659pub(crate) struct RetainingFixedPointObjective<ObjFn> {
9660    inner: RetainingObjective<ObjFn>,
9661    incumbent: Arc<Mutex<FixedPointContinuationCheckpoint>>,
9662    evaluated_inner_seed: Arc<Mutex<Option<BoundInnerSeed>>>,
9663    successful_iterations: usize,
9664    plan_used: OuterPlan,
9665}
9666
9667impl<ObjFn> RetainingFixedPointObjective<ObjFn> {
9668    pub(crate) fn new(
9669        inner: ObjFn,
9670        last_error: Arc<Mutex<Option<ObjectiveEvalError>>>,
9671        incumbent: Arc<Mutex<FixedPointContinuationCheckpoint>>,
9672        evaluated_inner_seed: Arc<Mutex<Option<BoundInnerSeed>>>,
9673        plan_used: OuterPlan,
9674    ) -> Self {
9675        Self {
9676            inner: RetainingObjective::new(inner, last_error),
9677            incumbent,
9678            evaluated_inner_seed,
9679            successful_iterations: 0,
9680            plan_used,
9681        }
9682    }
9683}
9684
9685impl<ObjFn> FixedPointObjective for RetainingFixedPointObjective<ObjFn>
9686where
9687    ObjFn: FixedPointObjective,
9688{
9689    fn eval_step(&mut self, x: &Array1<f64>) -> Result<FixedPointSample, ObjectiveEvalError> {
9690        let outcome = self.inner.eval_step(x);
9691        if let Ok(sample) = &outcome {
9692            self.successful_iterations = self.successful_iterations.saturating_add(1);
9693            let inner_seed = self
9694                .evaluated_inner_seed
9695                .lock()
9696                .expect("fixed-point inner-state publication lock poisoned")
9697                .clone()
9698                .filter(|seed| outer_theta_bitwise_eq(&seed.theta, x));
9699            *self
9700                .incumbent
9701                .lock()
9702                .expect("fixed-point incumbent publication lock poisoned") =
9703                FixedPointContinuationCheckpoint {
9704                    point: x.clone(),
9705                    sample: sample.clone(),
9706                    iterations: self.successful_iterations,
9707                    plan_used: self.plan_used,
9708                    inner_seed,
9709                };
9710        }
9711        outcome
9712    }
9713}
9714
9715pub(crate) fn run_fixed_point_outer_solver(
9716    obj: &mut dyn OuterObjective,
9717    layout: OuterThetaLayout,
9718    barrier_config: Option<BarrierConfig>,
9719    config: &OuterConfig,
9720    context: &str,
9721    seed: &Array1<f64>,
9722    the_plan: OuterPlan,
9723    label: &str,
9724    failure_prefix: &str,
9725) -> Result<OuterResult, FixedPointOuterRunError> {
9726    // Shared publication slot for the recurrent-restored-incumbent stop
9727    // (#2235 verdict 2): the bridge is moved into the driver, so the streak
9728    // count comes back through this cell and is stamped onto the returned
9729    // `OuterResult` below.
9730    let recurrent_incumbent_exit = Arc::new(Mutex::new(None));
9731    let evaluated_inner_seed = Arc::new(Mutex::new(None));
9732    let mut objective = OuterFixedPointBridge {
9733        obj,
9734        layout,
9735        barrier_config,
9736        fixed_point_tolerance: config.tolerance,
9737        evaluated_inner_seed: Arc::clone(&evaluated_inner_seed),
9738        consecutive_psi_zero_iters: 0,
9739        last_restored_incumbent_streak: None,
9740        recurrent_incumbent_exit: Arc::clone(&recurrent_incumbent_exit),
9741    };
9742    let seed_sample = match objective.eval_step(seed) {
9743        Ok(sample) => sample,
9744        Err(err) if first_order_fallback_request(&err).is_some() => {
9745            let request = first_order_fallback_request(&err)
9746                .expect("guard established a typed first-order fallback request")
9747                .clone();
9748            return Err(FixedPointOuterRunError::ImmediateFallback(request));
9749        }
9750        Err(err) if err.is_recoverable() => {
9751            return Err(FixedPointOuterRunError::SeedRejected(err));
9752        }
9753        Err(err) => {
9754            return Err(FixedPointOuterRunError::Failed(
9755                EstimationError::fatal_objective_evaluation(
9756                    "outer fixed-point seed evaluation",
9757                    err,
9758                ),
9759            ));
9760        }
9761    };
9762    let (lo, hi) = outer_search_bounds_template(config, layout.n_params);
9763    let bounds = outer_bounds(&lo, &hi).map_err(FixedPointOuterRunError::Failed)?;
9764    let tol = outer_tolerance(config.tolerance).map_err(FixedPointOuterRunError::Failed)?;
9765    let max_iter =
9766        outer_max_iterations(config.max_iter).map_err(FixedPointOuterRunError::Failed)?;
9767    // Publication slot for the complete producer error from the last failed
9768    // `eval_step`. `opt::FixedPoint` returns only its message, so this is the
9769    // ownership channel for the typed source and routing request.
9770    let last_step_error: Arc<Mutex<Option<ObjectiveEvalError>>> = Arc::new(Mutex::new(None));
9771    let seed_inner_state = evaluated_inner_seed
9772        .lock()
9773        .expect("fixed-point inner-state publication lock poisoned")
9774        .clone()
9775        .filter(|inner_seed| outer_theta_bitwise_eq(&inner_seed.theta, seed));
9776    let incumbent = Arc::new(Mutex::new(FixedPointContinuationCheckpoint {
9777        point: seed.clone(),
9778        sample: seed_sample.clone(),
9779        iterations: 0,
9780        plan_used: the_plan,
9781        inner_seed: seed_inner_state,
9782    }));
9783    let objective = RetainingFixedPointObjective::new(
9784        objective,
9785        Arc::clone(&last_step_error),
9786        Arc::clone(&incumbent),
9787        Arc::clone(&evaluated_inner_seed),
9788        the_plan,
9789    );
9790    let mut optimizer = FixedPoint::new(seed.clone(), objective)
9791        // Seed validation already paid the complete EFS inner solve. Reuse that
9792        // exact sample so iteration zero neither repeats the expensive solve nor
9793        // mistakes two evaluations at the identical rho for recurrent incumbent
9794        // evidence (#2241).
9795        .with_initial_sample(seed.clone(), seed_sample)
9796        .with_bounds(bounds)
9797        .with_tolerance(tol)
9798        .with_max_iterations(max_iter);
9799    match optimizer.run() {
9800        Ok(sol) => {
9801            let mut result = solution_into_outer_result(sol, true, the_plan);
9802            // Stamp the model-state fixed-point stop when the bridge published
9803            // one; `None` means the walk stopped through the ordinary
9804            // step-norm test instead.
9805            if let Some(consecutive_restores) =
9806                recurrent_incumbent_exit.lock().ok().and_then(|slot| *slot)
9807            {
9808                result.termination = OuterTermination::SolverClaimed {
9809                    proposed_via: Some(OuterConvergedVia::RecurrentIncumbent {
9810                        consecutive_restores,
9811                    }),
9812                };
9813            }
9814            Ok(result)
9815        }
9816        Err(FixedPointError::MaxIterationsReached { last_solution }) => {
9817            let step_norm = last_solution.final_step_norm.expect(
9818                "a fixed-point max-iteration solution must carry its final accepted step norm",
9819            );
9820            log::warn!(
9821                "[OUTER warning] {context}: {label} hit max_iter={} at final_value={:.6e} step_norm={:.3e}",
9822                config.max_iter,
9823                last_solution.final_value,
9824                step_norm,
9825            );
9826            Ok(solution_into_outer_result(*last_solution, false, the_plan))
9827        }
9828        Err(FixedPointError::ObjectiveFailed { .. }) => {
9829            let error = last_step_error
9830                .lock()
9831                .expect("fixed-point objective error publication lock poisoned")
9832                .take()
9833                .expect("FixedPoint::ObjectiveFailed must follow a failed classified eval_step");
9834            if error.is_recoverable() {
9835                let checkpoint = incumbent
9836                    .lock()
9837                    .expect("fixed-point incumbent publication lock poisoned")
9838                    .clone();
9839                return Err(FixedPointOuterRunError::IterationRejected(
9840                    FixedPointContinuationRequest {
9841                        checkpoint,
9842                        refusal: error,
9843                    },
9844                ));
9845            }
9846            Err(FixedPointOuterRunError::Failed(
9847                EstimationError::fatal_objective_evaluation(
9848                    "outer fixed-point evaluation",
9849                    error,
9850                ),
9851            ))
9852        }
9853        Err(e) => Err(FixedPointOuterRunError::Failed(
9854            EstimationError::RemlOptimizationFailed(format!("{failure_prefix}: {e:?}")),
9855        )),
9856    }
9857}
9858
9859#[cfg(test)]
9860#[path = "asymptote_rail_certify_tests.rs"]
9861mod asymptote_rail_certify_tests;
9862
9863#[cfg(test)]
9864#[path = "rail_barrier_removal_tests.rs"]
9865mod rail_barrier_removal_tests;
9866
9867#[cfg(test)]
9868#[path = "certify_resume_progress_tests.rs"]
9869mod certify_resume_progress_tests;
9870
9871#[cfg(test)]
9872#[path = "outer_stationarity_band_tests.rs"]
9873mod outer_stationarity_band_tests;
9874
9875#[cfg(test)]
9876#[path = "criterion_curvature_ladder_2748_tests.rs"]
9877mod criterion_curvature_ladder_2748_tests;