Skip to main content

PdFullSpaceSolver

Struct PdFullSpaceSolver 

Source
pub struct PdFullSpaceSolver {
    pub min_refinement_steps: Index,
    pub max_refinement_steps: Index,
    pub residual_ratio_max: Number,
    pub residual_ratio_singular: Number,
    pub residual_improvement_factor: Number,
    pub neg_curv_test_tol: Number,
    pub neg_curv_test_reg: bool,
    /* private fields */
}

Fields§

§min_refinement_steps: Index§max_refinement_steps: Index§residual_ratio_max: Number§residual_ratio_singular: Number§residual_improvement_factor: Number§neg_curv_test_tol: Number

Negative-curvature test tolerance (neg_curv_test_tol_, α_n in Zavala & Chiang 2014). Zero — upstream’s RegisterOptions default — keeps the inertia check and disables the heuristic. Positive turns the inertia check off and instead accepts a factorization whose inertia is wrong only when the computed direction passes the curvature test in Self::solve_once (IpPDFullSpaceSolver.cpp:592-634).

§neg_curv_test_reg: bool

neg_curv_test_reg_ — include the primal regularization δ_x‖dx‖² + δ_s‖ds‖² in the curvature test. Upstream’s RegisterOptions default is yes; no reproduces the original Ipopt form that ignores it. Only read when neg_curv_test_tol > 0.

Implementations§

Source§

impl PdFullSpaceSolver

Source

pub fn new( aug_solver: Box<dyn AugSystemSolver>, perturb: Rc<RefCell<PdPerturbationHandler>>, ) -> Self

Source

pub fn aug_solver(&self) -> &dyn AugSystemSolver

Source

pub fn aug_solver_mut(&mut self) -> &mut dyn AugSystemSolver

Source

pub fn quality_escalations(&self) -> u64

Successful quality escalations recorded so far — see Self::quality_escalations for what is and is not counted.

Source

pub fn set_quality_escalation_counter(&mut self, counter: Rc<Cell<u64>>)

Share this solver’s escalation tally with counter, so a restoration sub-solve’s escalations land in the same total as the main loop’s. Any count already recorded here is folded in, which makes the call order-independent; in practice it is made at build time, before the first solve.

Source

pub fn wrap_aug_solver<F>(&mut self, wrap: F)
where F: FnOnce(Box<dyn AugSystemSolver>) -> Box<dyn AugSystemSolver>,

Replace the underlying AugSystemSolver by passing the existing one through the supplied wrapper closure. Used by the restoration phase to decorate the inner StdAugSystemSolver with AugRestoSystemSolver (which performs the 8-block → 4-block Schur reduction before delegating).

Source

pub fn negative_curvature_direction( &mut self, data: &IpoptDataHandle, cq: &IpoptCqHandle, nlp: &Rc<RefCell<dyn IpoptNlp>>, w_at_curr: Option<Rc<dyn SymMatrix>>, ) -> Option<NegativeCurvature>

Look for a direction of negative curvature in the null space of the constraint Jacobian at the current iterate (gh #797).

The filter line-search IPM certifies first-order stationarity. On a nonconvex model that is not the same as a local minimum: a point where the reduced Hessian on null(A) is negative definite is a constrained maximum, and every first-order residual at it is zero, so the convergence check has nothing to object to and the Newton step is exactly zero — inertia correction included, since δ_x I is symmetric and cannot break a symmetry the iterates already have. nonconvex_qp.nl (min x₀x₁ s.t. x₀+x₁ = 2, 0 ≤ x ≤ 4) is the reported case: from the symmetric bound-pushed start the first Newton step lands on (1,1), f = 1, the maximum of the concave x₀(2-x₀) along the feasible segment, and the solve reports Solve_Succeeded there.

This is the second-order information the step computation throws away. It runs only where a stationary point is about to be certified, and it answers two questions with the machinery already in place:

  1. Is the point second-order suspect? Factor the augmented system unperturbed with the inertia check on. Correct inertia is exactly the statement that W + Σ is positive definite on null(A), so a Success at δ_x = 0 ends the probe with None and costs one factorization. Only a WrongInertia continues.
  2. Which way is down? Escalate δ_x on the ladder below until the inertia is correct. The δ_x that first works is within a factor of [NEG_CURV_DELTA_FACTOR] of -λ_min of the reduced Hessian, so (W + Σ + δ_x I)⁻¹ restricted to null(A) has its largest amplification precisely along the eigenvector of λ_min. A few inverse-iteration back-solves against that factor therefore converge to the most-negative-curvature direction, and each one is a back-solve against the cached factor rather than a refactorization.

The returned direction is never trusted on the strength of that argument: dᵀ(W + Σ)d is measured for each candidate and the probe returns None unless the best one is strictly negative. It also satisfies J_c d_x = 0 and J_d d_x - d_s = 0 to the accuracy of the factorization (both dual perturbations are held at zero), so stepping along it does not move the linearised constraints.

Returns None — never an error — for every shape it cannot answer for: a backend that reports no inertia, a non-dense iterate (the restoration inner IPM’s CompoundVector), a singular or breaking-down factorization, or a ladder that reaches [NEG_CURV_DELTA_MAX] without fixing the inertia. Declining is always safe here: the caller’s fallback is the pre-#797 behaviour of reporting the stationary point.

The augmented-system cache is invalidated on every exit, because the factor left behind describes a perturbed matrix that no ordinary solve asked for.

Source

pub fn solve( &mut self, data: &IpoptDataHandle, cq: &IpoptCqHandle, nlp: &Rc<RefCell<dyn IpoptNlp>>, alpha: Number, beta: Number, rhs: &IteratesVector, res: &mut IteratesVectorMut, allow_inexact: bool, improve_solution: bool, ) -> bool

Solve the full PD system. res = α · M⁻¹ · rhs + β · res_in, matching IpPDFullSpaceSolver::Solve. Returns true on success. The iterate fields used to assemble the system are pulled from data (W, curr) and cq (jacobians, slacks, sigmas).

Source

pub fn solve_with_sigma( &mut self, data: &IpoptDataHandle, cq: &IpoptCqHandle, nlp: &Rc<RefCell<dyn IpoptNlp>>, alpha: Number, beta: Number, rhs: &IteratesVector, res: &mut IteratesVectorMut, allow_inexact: bool, improve_solution: bool, sigma_override: SigmaOverride, ) -> bool

Self::solve against the same system with the barrier diagonals sigma_x / sigma_s replaced.

sigma is the barrier term the active bounds contribute to the x (variable bounds) and s (inequality-row bounds) diagonals, z / s per bound. Two callers want it substituted, for different reasons:

  • Release. Zeroing an entry takes that bound back out of the active set, which is what a released bound means, so factoring the result gives the released system directly. The released system cannot be recovered from the converged factor: reaching it by a rank-1 downdate through a Schur complement asks for the difference of two quantities that agree to about eps * sigma, and on a tightly converged bound sigma is large enough that the difference is noise – measured, the released answer degrades in proportion to how well the solve converged. Factoring is what buys those digits back, and it is still one factorization against the twenty to a hundred a re-solve would run.
  • Crossover (gh#654). A crossed-over iterate sits on the declared bounds, so its live slacks read bound_relax_factor rather than the barrier’s mu/z, and sigma comes out of the cache describing a looser pin than the point actually has. The sensitivity path substitutes the declared-frame diagonal here.

Only pounce-sensitivity calls this; the algorithm’s own step computation goes through Self::solve and is unaffected, so no solver trajectory moves. Both sigmas are among the thirteen dependency tags, so passing a different vector misses the factorization cache and re-factors, and the next ordinary solve misses it back – correctness needs no extra bookkeeping here.

Source

pub fn solve_many_cached<F1, F2>( &mut self, data: &IpoptDataHandle, cq: &IpoptCqHandle, nlp: &Rc<RefCell<dyn IpoptNlp>>, n_rhs: usize, write_rhs: F1, write_lhs: F2, ) -> Option<bool>

Batched back-substitution against the cached KKT factor for n_rhs right-hand sides, sharing one pounce_linsol::TSymLinearSolver::multi_solve call with nrhs > 1. Each column k pulls its RHS through write_rhs(k, &mut iv) and emits its solution through write_lhs(k, &iv) — closures over the caller’s flat / strided buffer keep the rhs/sol IteratesVectorMut scratch out of the API surface.

Returns:

  • Some(true) — fast path executed against the cached factor.
  • Some(false) — fast path was attempted but the linsol reported a back-solve failure.
  • None — fast path not taken. Either the matrix tags differ from the last successful Self::solve (cache miss), the matrix has not been considered yet, or the underlying AugSystemSolver does not implement AugSystemSolver::try_resolve_many_flat. The caller should fall back to looping Self::solve.

Used by pounce_sensitivity::PdSensBacksolver::solve_many for the JaxProblem jacrev backward path, where every cotangent re-solves against the same converged factor (pounce#77 follow-up).

Source

pub fn solve_many_cached_flat( &mut self, data: &IpoptDataHandle, cq: &IpoptCqHandle, nlp: &Rc<RefCell<dyn IpoptNlp>>, n_rhs: usize, rhs_flat: &[Number], lhs_flat: &mut [Number], block_dims: [usize; 8], ) -> Option<bool>

Flat-slice cached-factor multi-RHS path. Same cache-check semantics as Self::solve_many_cached but operates on row-major (n_rhs, total) flat buffers without going through IteratesVectorMut or any dyn Vector / dyn Matrix dispatch in the per-RHS inner loops — the eight source blocks (slack_{x,s}_{l,u}, z_{l,u}, v_{l,u}) get downcast to DenseVector once at the top, the four bound-expansion matrices (px_l, px_u, pd_l, pd_u) get downcast to ExpansionMatrix once, and Phase 1 / Phase 3 then run as raw &[Number] / &mut [Number] arithmetic on the flat buffers.

total is the sum of the eight block_dims entries (in the same (x, s, y_c, y_d, z_l, z_u, v_l, v_u) order that IteratesVector uses); rhs_flat.len() == lhs_flat.len() == n_rhs * total.

Returns None (caller should fall back to Self::solve_many_cached) when:

  • the cache check fails (matrix tags differ),
  • any block source vector is not a DenseVector or is homogeneous (uniform-scalar) on a non-empty block,
  • any bound-expansion matrix is not an ExpansionMatrix,
  • the underlying AugSystemSolver doesn’t implement AugSystemSolver::try_resolve_many_flat.

Returns Some(true) on success, Some(false) on linsol back- solve failure.

Used by pounce_sensitivity::PdSensBacksolver::solve_many as the fastest tier of the JaxProblem jacrev backward path (pounce#77 follow-up).

Trait Implementations§

Source§

impl PdSystemSolver for PdFullSpaceSolver

Source§

fn solve_status(&self) -> ESymSolverStatus

Run the configured iterative-refinement loop on the 8-block system. Phase 6 placeholder for the full-signature method; super::pd_full_space_solver::PdFullSpaceSolver::solve will implement it once IteratesVector is wired through.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> ByRef<T> for T

Source§

fn by_ref(&self) -> &T

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Imply<T> for U
where T: ?Sized, U: ?Sized,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more