pub trait OuterObjective {
Show 24 methods
// Required methods
fn capability(&self) -> OuterCapability;
fn eval_cost(&mut self, rho: &Array1<f64>) -> Result<f64, EstimationError>;
fn eval(&mut self, rho: &Array1<f64>) -> Result<OuterEval, EstimationError>;
fn reset(&mut self);
fn seed_inner_state(
&mut self,
beta: &Array1<f64>,
) -> Result<SeedOutcome, EstimationError>;
// Provided methods
fn eval_screening_proxy(
&mut self,
rho: &Array1<f64>,
) -> Result<f64, EstimationError> { ... }
fn eval_with_order(
&mut self,
rho: &Array1<f64>,
order: OuterEvalOrder,
) -> Result<OuterEval, EstimationError> { ... }
fn eval_efs(
&mut self,
rho: &Array1<f64>,
) -> Result<EfsEval, EstimationError> { ... }
fn eval_fixed_point_certificate(
&mut self,
rho: &Array1<f64>,
) -> Result<FixedPointCertificateEval, EstimationError> { ... }
fn rail_face_limit(
&mut self,
rho: &Array1<f64>,
face: &[usize],
) -> Result<RailFaceLimitOutcome, EstimationError> { ... }
fn owns_terminal_coefficient_mode(&self) -> bool { ... }
fn begin_exact_polish(&mut self) -> bool { ... }
fn outer_domain_upper_bound(
&self,
) -> Result<Option<Array1<f64>>, EstimationError> { ... }
fn outer_domain_lower_bound(
&self,
) -> Result<Option<Array1<f64>>, EstimationError> { ... }
fn outer_device_admission(&self) -> Option<RemlOuterAdmission> { ... }
fn reactive_domain_scalar_contract(
&self,
) -> Result<Option<ContinuationScalarContract>, EstimationError> { ... }
fn install_reactive_domain_scalar_state(
&mut self,
state: &ContinuationScalarState,
) -> Result<(), EstimationError> { ... }
fn begin_reactive_domain_waypoint(&mut self) -> Result<(), EstimationError> { ... }
fn commit_reactive_domain_waypoint(
&mut self,
rho: &Array1<f64>,
) -> Result<(), EstimationError> { ... }
fn rollback_reactive_domain_waypoint(
&mut self,
) -> Result<(), EstimationError> { ... }
fn curvature_homotopy_entry(
&mut self,
rho: &Array1<f64>,
) -> Option<Result<bool, EstimationError>> { ... }
fn accept_seed_without_outer_iterations(
&mut self,
rho: &Array1<f64>,
) -> Result<Option<f64>, EstimationError> { ... }
fn terminal_eval_order(&self) -> Option<OuterEvalOrder> { ... }
fn finalize_outer_result(
&mut self,
rho: &Array1<f64>,
plan: &OuterPlan,
) -> Result<(), EstimationError> { ... }
}Expand description
Common interface for outer smoothing-parameter objectives.
Every model path that optimizes smoothing parameters implements this trait.
The runner function consumes it and handles solver selection,
multi-start, and logging while delegating derivative fallback policy to
opt.
§Contract
capability()must be stable (same result across calls).eval()may returnHessianValue::Unavailableat individual trial points even whencapability().hessian == Analytic;optdegrades that step to first-order behavior instead of requiring the objective to fake a stale or non-finite Hessian.- Use
eval_cost()/OuterEval::infeasible()for infeasible trial points. ReturnErr(...)only when the evaluation artifact itself cannot be constructed. Such errors are fatal across screening, multistart, and solver plans; they are never reinterpreted as another numerical trial. eval_cost()is used only for cost-based optimization paths.eval()is the main evaluation path (cost + gradient + optional Hessian).eval_efs()is used only by the EFS solver. It runs the inner solve, builds theInnerSolution, and computes the EFS step vector. The default implementation returns an error; only objectives that support EFS need to override it.reset()restores state to a clean baseline (for multi-start).
Required Methods§
Sourcefn capability(&self) -> OuterCapability
fn capability(&self) -> OuterCapability
Declare what this objective can compute analytically.
Sourcefn eval_cost(&mut self, rho: &Array1<f64>) -> Result<f64, EstimationError>
fn eval_cost(&mut self, rho: &Array1<f64>) -> Result<f64, EstimationError>
Evaluate cost only for cost-based optimization paths.
Sourcefn eval(&mut self, rho: &Array1<f64>) -> Result<OuterEval, EstimationError>
fn eval(&mut self, rho: &Array1<f64>) -> Result<OuterEval, EstimationError>
Evaluate cost + gradient + (if capable) Hessian.
Sourcefn seed_inner_state(
&mut self,
beta: &Array1<f64>,
) -> Result<SeedOutcome, EstimationError>
fn seed_inner_state( &mut self, beta: &Array1<f64>, ) -> Result<SeedOutcome, EstimationError>
Seed the inner-solver iterate before the first eval, e.g. when the
outer-iterate cache restored a (ρ, β) pair from a prior run, or
when a typed reactive continuation path forwards
OuterEval::inner_beta_hint
from the previous step.
Objectives make an explicit choice via the SeedOutcome return:
implementations with an inner β slot return SeedOutcome::Installed
after storing β; implementations without one return
SeedOutcome::NoSlot. Genuine seeding failures (wrong dimension
when a slot exists, etc.) are reported via Err(EstimationError).
Callers that need to distinguish “no slot” from “installed” (the
outer cache warm-start path, which logs cache provenance) branch on
the variant. Callers that don’t care (the reactive continuation path,
which only proceeds cold when the hint is unusable) ignore it and only
propagate Err.
Provided Methods§
Sourcefn eval_screening_proxy(
&mut self,
rho: &Array1<f64>,
) -> Result<f64, EstimationError>
fn eval_screening_proxy( &mut self, rho: &Array1<f64>, ) -> Result<f64, EstimationError>
Evaluate the seed-screening ranking proxy at this rho.
Used exclusively by the rank_seeds_with_screening cascade. The
default delegates to OuterObjective::eval_cost, which preserves
behavior for non-REML objectives.
Concrete REML-state objectives override this to return the per-seed
minimum penalized deviance observed during the inner P-IRLS solve
(a monotonically descending quantity that remains a meaningful
quality signal even at a 3-iteration screening cap), instead of the
V_LAML criterion (which is dominated by a poorly-conditioned
0.5·log|H| term at partial-fit β̂ and ranks seeds little better
than random). The proxy fires only in screening mode; outside
screening it must return the regular V_LAML cost so the optimization
objective is unchanged.
§Why the eval_cost default is correct for everyone else (#969)
The partial-fit pathology is CAUSED by the screening cap: it is the
0.5·log|H| term evaluated at a β̂ whose inner solve was truncated
by screening_max_inner_iterations. An objective only suffers it if
it (a) consumes that cap atomic AND (b) ranks on a curvature-bearing
criterion at the truncated iterate — which is exactly the REML/LAML
state-objective family, all of which override this method (or are
built via build_objective_with_screening_proxy). Objectives that
never wire the cap pay the full inner solve during screening, so
their screened cost IS the true criterion — slower, but a correct
ranking by definition, and a proxy could only degrade it. Any future
objective that starts honoring the screening cap on a
curvature-bearing criterion must override this with its own
monotonically-descending inner quantity (the penalized-deviance
pattern above generalizes: rank on the best inner merit seen, never
on a curvature term at a truncated iterate).
Sourcefn eval_with_order(
&mut self,
rho: &Array1<f64>,
order: OuterEvalOrder,
) -> Result<OuterEval, EstimationError>
fn eval_with_order( &mut self, rho: &Array1<f64>, order: OuterEvalOrder, ) -> Result<OuterEval, EstimationError>
Evaluate the outer objective at the order requested by the active plan.
The default preserves legacy behavior by delegating value-only requests
to OuterObjective::eval_cost and derivative requests to
OuterObjective::eval.
Sourcefn eval_efs(&mut self, rho: &Array1<f64>) -> Result<EfsEval, EstimationError>
fn eval_efs(&mut self, rho: &Array1<f64>) -> Result<EfsEval, EstimationError>
Evaluate cost + EFS step vector. Only needed when the plan selects
Solver::Efs. The default returns an error indicating EFS is not
supported by this objective.
Sourcefn eval_fixed_point_certificate(
&mut self,
rho: &Array1<f64>,
) -> Result<FixedPointCertificateEval, EstimationError>
fn eval_fixed_point_certificate( &mut self, rho: &Array1<f64>, ) -> Result<FixedPointCertificateEval, EstimationError>
Re-evaluate the terminal fixed point and provide an explicit analytic residual for every optimized coordinate.
This is a proof surface, not an alias for Self::eval_efs: iteration
steps may contain guarded or structurally unsupported zeros. The default
refuses certification so an EFS-capable objective must deliberately
describe complete, root-equivalent coordinate coverage before a fixed-
point result can mint a fit.
Sourcefn rail_face_limit(
&mut self,
rho: &Array1<f64>,
face: &[usize],
) -> Result<RailFaceLimitOutcome, EstimationError>
fn rail_face_limit( &mut self, rho: &Array1<f64>, face: &[usize], ) -> Result<RailFaceLimitOutcome, EstimationError>
Analytic λ→∞ limit data for a rail face (#2348 Inc 5).
face lists the ρ-coordinates sitting at their infinite-smoothing
bound. An objective that can form the limit EXACTLY — the fit
restricted to the railed penalties’ common null space, together with
the analytic first-order form of the criterion’s logdet and trace terms
there — returns it here, and the outer certificate proves the face from
it instead of probing a tail at finite λ.
A decline is not a failure, and it is typed: OutsideClosedForm leaves
room for a different closed form to apply, while FaceUnavailable is a
statement about the face itself. Either way the caller keeps whatever
evidence it already had. The default declines for every objective that
has no analytic limit at all.
Sourcefn owns_terminal_coefficient_mode(&self) -> bool
fn owns_terminal_coefficient_mode(&self) -> bool
Whether this objective owns a terminal coefficient mode whose bitwise identity fit assembly will later bind against the certified outer value.
The certification sequence (run.rs) installs the terminal state twice
at result.rho: once via Self::finalize_outer_result (which the
mode-owning evaluator uses to install its coefficient mode) and once via
the analytic re-evaluation inside certify_outer_optimality (which sets
result.final_value). On a nonconvex profiled objective those two
evaluations can settle in different coefficient basins unless each is
forced to re-install from the same clean baseline through Self::reset
— otherwise they prime the inner solve off whatever warm state the
preceding diagnostic/finalize left behind, and the mode’s objective and
the certified value disagree by a whole basin (measured: 9.1931e2 vs
9.1671e2 on the cause-specific survival gate).
That terminal reset is otherwise gated on config.outer_inner_cap,
which the REML/mixture objectives wire but the custom-family (and any
other terminal-mode-owning closure) objective does not — it holds its
inner cap in a different field and leaves outer_inner_cap None, so
the reset never fires and the bitwise bind can spuriously fail on a
bimodal inner solve. Returning true here forces the terminal reset
independently of the cap, so finalize and certify provably come
from one fresh evaluation at rho_star. It deliberately does NOT touch
the inner_solve_converged(config.outer_inner_cap) gate: an objective
that owns a terminal mode but does not populate the cap’s convergence
atomic keeps its own stateful convergence semantics.
The default is false: an objective that owns no terminal coefficient
mode (the reactive-domain fixture among them) retains the very state its
evaluation at result.rho depends on and must not be reset.
Sourcefn begin_exact_polish(&mut self) -> bool
fn begin_exact_polish(&mut self) -> bool
Transition an objective that actually used an approximate derivative pilot to its exact full-data measure.
The runner calls this once after the pilot solver returns a checkpoint.
true means the objective changed measure and must be optimized again
from that checkpoint before analytic certification. Exact objectives and
pilots that never installed a sample return false.
Sourcefn outer_domain_upper_bound(
&self,
) -> Result<Option<Array1<f64>>, EstimationError>
fn outer_domain_upper_bound( &self, ) -> Result<Option<Array1<f64>>, EstimationError>
Optional objective-owned hard upper domain for the outer coordinates.
The generic optimizer intersects this vector with its configured box
before projecting seeds, constructing a solver, or opening reactive
continuation. Consequently the exact same upper endpoint is both the
solver’s legal box face and the continuation path’s literal rho entry.
None means the objective has no domain narrower than the configured
generic box. An advertised vector must have capability().n_params
finite entries; malformed contracts are typed runner errors.
Sourcefn outer_domain_lower_bound(
&self,
) -> Result<Option<Array1<f64>>, EstimationError>
fn outer_domain_lower_bound( &self, ) -> Result<Option<Array1<f64>>, EstimationError>
Optional objective-owned hard lower domain for the outer coordinates.
This is intersected with the caller’s configured box at the same single
runner seam as Self::outer_domain_upper_bound, before any seed,
continuation waypoint, solver evaluation, or stationarity certificate can
observe an out-of-domain coordinate.
Sourcefn outer_device_admission(&self) -> Option<RemlOuterAdmission>
fn outer_device_admission(&self) -> Option<RemlOuterAdmission>
Optional opt-in to the device-resident outer REML BFGS-over-ρ driver
(crate::gpu::reml_outer::run_reml_outer_on_device). Returns
Some(adm) when the objective is a REML evaluator whose
(spec, n, p, num_rho) admission predicate accepts the device path,
and None otherwise.
The default returns None so non-REML objectives (line-search-only
inner bridges, screening proxies, the EFS / hybrid-EFS sub-objectives)
keep the host BFGS branch unconditionally — only the concrete
REML-state objectives override this to consult
[crate::estimate::reml::outer_eval::outer_reml_device_admission].
Sourcefn reactive_domain_scalar_contract(
&self,
) -> Result<Option<ContinuationScalarContract>, EstimationError>
fn reactive_domain_scalar_contract( &self, ) -> Result<Option<ContinuationScalarContract>, EstimationError>
Typed scalar continuation contract for repairing a non-finite literal
outer seed through crate::continuation_path::ContinuationPath.
This is a typed domain-entry capability, not a fallback objective. The
objective supplies both the smoother entry state and its literal target
state. None means this objective has no such domain homotopy. The
runner always probes the real seed first, so merely supplying a contract
performs no waypoint installation or heavy work on a finite seed.
Sourcefn install_reactive_domain_scalar_state(
&mut self,
state: &ContinuationScalarState,
) -> Result<(), EstimationError>
fn install_reactive_domain_scalar_state( &mut self, state: &ContinuationScalarState, ) -> Result<(), EstimationError>
Install one scalar waypoint before the continuation rho spine evaluates
the objective. Objectives that return Some from
Self::reactive_domain_scalar_contract must override this method; the
default is a typed contract refusal, never a silent no-op.
Sourcefn begin_reactive_domain_waypoint(&mut self) -> Result<(), EstimationError>
fn begin_reactive_domain_waypoint(&mut self) -> Result<(), EstimationError>
Snapshot the objective’s complete accepted inner state before a reactive
coupled waypoint is installed. Contract-advertising objectives must make
this transactional: a failed trial is restored by
Self::rollback_reactive_domain_waypoint.
Sourcefn commit_reactive_domain_waypoint(
&mut self,
rho: &Array1<f64>,
) -> Result<(), EstimationError>
fn commit_reactive_domain_waypoint( &mut self, rho: &Array1<f64>, ) -> Result<(), EstimationError>
Commit the converged full inner state produced by the value evaluation
at rho. A coefficient-only handoff is insufficient: latent coordinates,
routing logits, decoder frames, loss, and scalar state must advance as one
accepted waypoint.
Sourcefn rollback_reactive_domain_waypoint(&mut self) -> Result<(), EstimationError>
fn rollback_reactive_domain_waypoint(&mut self) -> Result<(), EstimationError>
Restore the full accepted state saved by
Self::begin_reactive_domain_waypoint after an errored or non-finite
trial.
Sourcefn curvature_homotopy_entry(
&mut self,
rho: &Array1<f64>,
) -> Option<Result<bool, EstimationError>>
fn curvature_homotopy_entry( &mut self, rho: &Array1<f64>, ) -> Option<Result<bool, EstimationError>>
Run the objective’s certified curvature-homotopy entry leg, if it has
one, leaving the inner state warm at the real (η = 1) objective.
An objective with a certified anchor — a point known by construction to
be the global optimum of a relaxed problem — can replace the blind
multi-seed multistart with a single predictor-corrector walk from that
anchor to the true objective (#1007). The SAE-manifold objective
overrides this: its η = 0 base-topology relaxation is convex, and a
genuine low-rank (Eckart-Young / SVD) residual ceiling is certified by
linear_span_anchor — the η = 0 endpoint is NOT a linear/affine model
(for curved bases its base columns still embed curvature); “Eckart-Young”
names the rank ceiling, not the chart. The walk in η tracks the unique
optimal branch to η = 1. The walk monitors the
arrow-factor min-pivot and halves the η step when it shrinks; a pivot
collapse below tolerance is a DETECTED bifurcation (recorded on the fit
payload, never silent), at which point the objective falls back to the
documented multi-seed cascade.
Returns:
None— no certified anchor; use the standard seed cascade (the default for every other objective).Some(Ok(true))— the walk arrived; the inner state is warm at the certifiedη = 1solution and the seed cascade is bypassed.Some(Ok(false))— the anchor degenerated or the walk detected a bifurcation; fall back to the multi-seed cascade (the report is recorded on the objective for the fit payload).Some(Err(_))— a hard failure constructing the anchor.
Sourcefn accept_seed_without_outer_iterations(
&mut self,
rho: &Array1<f64>,
) -> Result<Option<f64>, EstimationError>
fn accept_seed_without_outer_iterations( &mut self, rho: &Array1<f64>, ) -> Result<Option<f64>, EstimationError>
Let an objective declare that a seed is already a terminal outer result. Used for objectives with a certified high-quality construction seed where the generic rho optimizer can only degrade the fitted state.
Sourcefn terminal_eval_order(&self) -> Option<OuterEvalOrder>
fn terminal_eval_order(&self) -> Option<OuterEvalOrder>
Optional analytic evaluation order that must own the final installed
objective state, independently of the solver plan that found rho.
The default follows the solver (EFS finalizes through eval_efs,
BFGS through first order, ARC through second order). Stateful profiled
objectives may override this when only one evaluator produces the
ownership payload consumed by fit assembly.
Sourcefn finalize_outer_result(
&mut self,
rho: &Array1<f64>,
plan: &OuterPlan,
) -> Result<(), EstimationError>
fn finalize_outer_result( &mut self, rho: &Array1<f64>, plan: &OuterPlan, ) -> Result<(), EstimationError>
Re-install the selected outer result into the mutable objective before
callers consume objective-owned fitted state. Optimizers may evaluate
rejected trial points after the best point was found; without this final
synchronization, stateful objectives can report the last trial fit rather
than the returned OuterResult::rho.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".