pub trait BoxAffineScaling: Sized {
// Required methods
fn compute_cl_scaling(
&self,
gradient: &Self,
lower: &Self,
upper: &Self,
d_sq: &mut Self,
c_diag: &mut Self,
);
fn max_feasible_step(&self, step: &Self, lower: &Self, upper: &Self) -> f64;
fn cl_kkt_inf_norm(&self, d_sq: &Self) -> f64;
fn weighted_norm_squared(&self, weights: &Self) -> f64;
fn project_strictly_inside(
&mut self,
lower: &Self,
upper: &Self,
rstep: f64,
);
}Expand description
Box-constrained NLLS vector primitives. Implemented for every
vector backend basin ships (Vec<f64>, nalgebra::DVector<f64>,
ndarray::Array1<f64>, faer::Col<f64>).
Used exclusively by the Trf solver today.
The trait groups three otherwise-unrelated element-wise operations
because they share their caller and have no other users — splitting
would just inflate the solver’s where clause.
Required Methods§
Sourcefn compute_cl_scaling(
&self,
gradient: &Self,
lower: &Self,
upper: &Self,
d_sq: &mut Self,
c_diag: &mut Self,
)
fn compute_cl_scaling( &self, gradient: &Self, lower: &Self, upper: &Self, d_sq: &mut Self, c_diag: &mut Self, )
Coleman-Li affine scaling diagonals per Branch-Coleman-Li 1999
eqs (i)–(iv). self is the current iterate x. After return:
d_sq[i] = 1/|v_i|, wherev_ifollows the four-case definition based onsign(g_i)and finiteness of bounds:v_i = x_i − u_i(case i:g_i < 0and finite upper),v_i = x_i − l_i(case ii:g_i ≥ 0and finite lower),v_i = ±1(cases iii/iv: relevant bound is infinite).c_diag[i] = |g_i|/|v_i|for cases (i)/(ii),0for cases (iii)/(iv). This is the diagonal of BCL’sC = D·diag(g)·J^v·Dterm, always non-negative.
d_sq and c_diag are overwritten. All five arguments must
be the same shape; backends panic on mismatch.
§Contract
- Caller must: pass
selfstrictly inside[lower, upper](no equalities). Cases (i)/(ii) divide by|x_i − bound_i|, which the caller must keep strictly positive. The TRF solver enforces this with aninit-time strict-interior projection. - Implementor must: match the BCL case dispatch exactly.
Tied cases (
g_i = 0exactly) fall into case (ii) per theg_i ≥ 0clause.
Sourcefn max_feasible_step(&self, step: &Self, lower: &Self, upper: &Self) -> f64
fn max_feasible_step(&self, step: &Self, lower: &Self, upper: &Self) -> f64
Largest τ ≥ 0 such that self + τ·step stays component-wise
inside [lower, upper]. Returns f64::INFINITY when no
component of step points at a finite bound from the current
iterate (e.g. all bounds infinite, or step is zero where
bounds are finite).
§Contract
- Caller must: pass
self,step,lower,upperof the same shape; backends panic on mismatch. - Implementor must: for each component
iwithstep[i] ≠ 0, compute the per-component limit(upper_i − self_i) / step_iifstep_i > 0(andupper_iis finite) or(lower_i − self_i) / step_iifstep_i < 0(andlower_iis finite). Take the minimum over all such limits. Components withstep_i = 0or with the relevant bound infinite contribute no limit. - The TRF caller multiplies
τby a strict-interior factorθ ∈ [θ_l, 1)to keep iterates in the open box.
Sourcefn cl_kkt_inf_norm(&self, d_sq: &Self) -> f64
fn cl_kkt_inf_norm(&self, d_sq: &Self) -> f64
max_i |self[i]| / d_sq[i] — the BCL first-order optimality
measure ‖v ⊙ g‖_∞ when self = g and d_sq[i] = 1/|v_i| is
the [compute_cl_scaling] output. Equals max_i |g_i · v_i|.
This metric goes to zero at any KKT point of the box-constrained
problem — interior or face-active. The unscaled ‖g‖_∞
doesn’t vanish on a finite face, and the scaled ‖D·g‖_∞ = max |g_i| / √|v_i| actually blows up on a face (denominator
→ 0), so neither is a usable termination measure for TRF.
SciPy’s least_squares(method='trf') uses the same metric (its
g_norm = max |g · v|); see
references/branch-coleman-li-1999/NOTES.md for the derivation.
§Contract
- Caller must: pass
d_sqof the same shape asself, with strictly positive entries. Backends panic on shape mismatch; entries of0produceinf, which propagates.
Sourcefn weighted_norm_squared(&self, weights: &Self) -> f64
fn weighted_norm_squared(&self, weights: &Self) -> f64
Σ self[i]² · weights[i] — the squared D-norm ‖D · self‖²
when weights = d_sq is the [compute_cl_scaling] output.
Used in the BCL scaled trust-region predicted-reduction
½(μ · ‖D·h‖² − h^T g) — the analogue of Nielsen’s LM
½(μ‖h‖² − h^T g) with the affine-scaling D folded in.
§Contract
- Caller must: pass
weightsof the same shape asself. Backends panic on shape mismatch. - Implementor must: return
Σᵢ self[i]² · weights[i].
Sourcefn project_strictly_inside(&mut self, lower: &Self, upper: &Self, rstep: f64)
fn project_strictly_inside(&mut self, lower: &Self, upper: &Self, rstep: f64)
Project self into the open box (lower, upper) element-wise.
Components outside [lower, upper] are clamped to a strict-
interior point; components already strictly inside are
unchanged. The strict-interior offset is
rstep · max(1, |bound|) from the relevant finite bound.
Used at TRF init to bring an arbitrary starting point into
the open feasible region — the affine scaling matrix D is
undefined where v_i = 0 (i.e. on a finite face).
§Contract
- Caller must: pass
lower/upperof the same shape asselfwithlower[i] < upper[i]for any component that has both bounds finite. Backends panic on shape mismatch; equal finite bounds produce an undefined post-projection iterate. - Implementor must: for each component
i:- If both bounds infinite: leave
self[i]unchanged. - Else compute
lo_inner = lower[i] + rstep · max(1, |lower[i]|)(treating infinitelower[i]as-∞, which makeslo_inner = -∞). - Compute
hi_inner = upper[i] - rstep · max(1, |upper[i]|)analogously. - Set
self[i] ← clamp(self[i], lo_inner, hi_inner).
- If both bounds infinite: leave
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.