Skip to main content

SensBacksolver

Trait SensBacksolver 

Source
pub trait SensBacksolver {
    // Required methods
    fn dim(&self) -> usize;
    fn solve(&self, rhs: &[f64], lhs: &mut [f64]) -> bool;

    // Provided methods
    fn natural_units_factor(&self) -> Option<&[f64]> { ... }
    fn bound_rows(&self) -> Option<&[BoundRow]> { ... }
    fn solve_released(
        &self,
        _released: &[usize],
        _rhs: &[f64],
        _lhs: &mut [f64],
    ) -> bool { ... }
    fn solve_released_step(
        &self,
        _released: &[usize],
        _rhs: &[f64],
        _lhs: &mut [f64],
    ) -> bool { ... }
    fn solve_released_pinned(
        &self,
        _released: &[usize],
        _pinned: &[usize],
        _rhs: &[f64],
        _lhs: &mut [f64],
    ) -> bool { ... }
    fn supports_release(&self) -> bool { ... }
}
Expand description

Solve K · lhs = rhs against the converged KKT factor. Returns false on failure (e.g. backend reports Singular).

Mirrors Ipopt::SensBacksolver::Solve (SensBacksolver.hpp:28-31). Pounce takes flat &[Number] / &mut [Number] rather than upstream’s block-structured IteratesVector because the sensitivity-side data is naturally flat; if the algorithm-side wrapper in Phase B.2 needs the block layout it converts before calling.

Required Methods§

Source

fn dim(&self) -> usize

Size of the linear system in entries (length of lhs and rhs). The backsolver’s notion of “full state” — pounce’s IPM uses the compound (x, s, λ_c, λ_d, z_l, z_u, v_l, v_u) concatenation here.

Source

fn solve(&self, rhs: &[f64], lhs: &mut [f64]) -> bool

Solve K · lhs = rhs. The implementation may use rhs as scratch; callers should treat it as moved-from on return. lhs must have length self.dim().

Provided Methods§

Source

fn natural_units_factor(&self) -> Option<&[f64]>

Per-row factor carrying a quantity read off the converged iterate into the units Self::solve answers in, indexed by compound KKT row. None when the solve ran unscaled, which is the identity.

This is the same F the natural-units back-solve applies to its result, so a bound multiplier read raw off curr.z_l and multiplied by f[row] agrees with the z rows of a step this backsolver returns. The two disagree by d/df otherwise, and mixing them puts a scaled right-hand side into a Schur complement whose other rows are natural, which moves every coordinate of the answer rather than one.

Source

fn bound_rows(&self) -> Option<&[BoundRow]>

The variable behind each bound-multiplier row. None when the backsolver cannot report it, which leaves crate::boundcheck::refine_step_onto_bounds with no way to release and so pinning only.

A release re-factors with that variable’s sigma removed, so it needs to know which variable each row belongs to and which side it bounds.

Source

fn solve_released( &self, _released: &[usize], _rhs: &[f64], _lhs: &mut [f64], ) -> bool

Solve against the system with the bounds named by released – compound multiplier rows – taken out of the active set.

This re-factors, and has to. An active bound puts sigma = z / s on the x diagonal, and on a tightly converged bound that term is large enough to destroy the released system’s information in the converged factor: recovering it from there needs the difference of two quantities agreeing to about eps * sigma, so the released answer comes out worse the better the solve converged. Re-factoring with the term removed is what buys those digits back, and one factorization still sits an order of magnitude under a re-solve.

This solves in the released system and does nothing to the right-hand side, so it is the right call for every solve the refinement makes once a bound is out – including the unit vectors a Schur complement is built from, which carry no multiplier to move.

false by default, which leaves the refinement pinning only.

Source

fn solve_released_step( &self, _released: &[usize], _rhs: &[f64], _lhs: &mut [f64], ) -> bool

Self::solve_released against a parametric right-hand side, which additionally needs the released multipliers moved onto their variables’ x rows.

Dropping sigma gives the released matrix; this gives the released right-hand side to go with it. Only the step itself gets this treatment – applying it to a Schur complement’s unit vectors would shift a right-hand side that has no multiplier in it to begin with.

Source

fn solve_released_pinned( &self, _released: &[usize], _pinned: &[usize], _rhs: &[f64], _lhs: &mut [f64], ) -> bool

Self::solve_released with each primal KKT row in pinned carrying an extra diagonal stiff enough to hold that coordinate in place.

This is not the pin the walk applies – that one is a Schur row on top of the factored system, and it is exact. This is the operator that pin is applied to, for the case where the released system on its own has no inverse for a Schur complement to be built from: releasing a bound takes its Sigma off the diagonal, and on a model with no curvature two released variables sharing a constraint are left with linearly dependent stationarity rows (gh#930).

Adding the diagonal back regularizes exactly those rows, and costs nothing in accuracy, because the Schur pin then holds the same coordinates at zero: Eᵀw = 0 annihilates the term that was added, so the pinned system solved is the released one after all. The diagonal has to be reachable – large enough that the regularized operator is invertible, small enough that the row’s own couplings survive it, which is the gh#737 ceiling.

false by default, which leaves the caller reporting the Schur pin’s failure.

Source

fn supports_release(&self) -> bool

Whether Self::solve_released is implemented.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§