Skip to main content

BoxAffineScaling

Trait BoxAffineScaling 

Source
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§

Source

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|, where v_i follows the four-case definition based on sign(g_i) and finiteness of bounds: v_i = x_i − u_i (case i: g_i < 0 and finite upper), v_i = x_i − l_i (case ii: g_i ≥ 0 and finite lower), v_i = ±1 (cases iii/iv: relevant bound is infinite).
  • c_diag[i] = |g_i|/|v_i| for cases (i)/(ii), 0 for cases (iii)/(iv). This is the diagonal of BCL’s C = D·diag(g)·J^v·D term, 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 self strictly 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 an init-time strict-interior projection.
  • Implementor must: match the BCL case dispatch exactly. Tied cases (g_i = 0 exactly) fall into case (ii) per the g_i ≥ 0 clause.
Source

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, upper of the same shape; backends panic on mismatch.
  • Implementor must: for each component i with step[i] ≠ 0, compute the per-component limit (upper_i − self_i) / step_i if step_i > 0 (and upper_i is finite) or (lower_i − self_i) / step_i if step_i < 0 (and lower_i is finite). Take the minimum over all such limits. Components with step_i = 0 or 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.
Source

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_sq of the same shape as self, with strictly positive entries. Backends panic on shape mismatch; entries of 0 produce inf, which propagates.
Source

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 weights of the same shape as self. Backends panic on shape mismatch.
  • Implementor must: return Σᵢ self[i]² · weights[i].
Source

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/upper of the same shape as self with lower[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 infinite lower[i] as -∞, which makes lo_inner = -∞).
    • Compute hi_inner = upper[i] - rstep · max(1, |upper[i]|) analogously.
    • Set self[i] ← clamp(self[i], lo_inner, hi_inner).

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.

Implementations on Foreign Types§

Source§

impl BoxAffineScaling for Vec<f64>

Source§

fn compute_cl_scaling( &self, gradient: &Self, lower: &Self, upper: &Self, d_sq: &mut Self, c_diag: &mut Self, )

Source§

fn max_feasible_step(&self, step: &Self, lower: &Self, upper: &Self) -> f64

Source§

fn cl_kkt_inf_norm(&self, d_sq: &Self) -> f64

Source§

fn weighted_norm_squared(&self, weights: &Self) -> f64

Source§

fn project_strictly_inside(&mut self, lower: &Self, upper: &Self, rstep: f64)

Implementors§