pub struct SqpOptions {Show 18 fields
pub globalization: SqpGlobalization,
pub hessian: SqpHessianSource,
pub tol: Number,
pub constr_viol_tol: Number,
pub dual_inf_tol: Number,
pub max_iter: u32,
pub l1_penalty: Number,
pub l1_penalty_safety: Number,
pub l1_penalty_max: Number,
pub bt_reduction: Number,
pub bt_min_alpha: Number,
pub print_level: u8,
pub lbfgs_max_history: u32,
pub dense_hessian_max_n: usize,
pub qp_schur_min_dim: Option<usize>,
pub qp_schur_indefinite_max_dim: Option<usize>,
pub qp_ipm_cold_start_min_dim: Option<usize>,
pub deadline: Option<Deadline>,
}Fields§
§globalization: SqpGlobalization§hessian: SqpHessianSource§tol: NumberKKT tolerance (max-norm) on the stationarity residual.
constr_viol_tol: NumberTolerance on constraint violation (max-norm).
dual_inf_tol: NumberTolerance on stationarity residual (max-norm).
max_iter: u32Outer-iteration cap.
l1_penalty: Numberl1-merit penalty parameter ν. Used only when
globalization = L1Elastic. Filter globalization ignores
this. Default is a moderate starting value; the Han-Powell
ν-adaptation in l1_merit_line_search grows ν to dominate
‖λ_qp‖_∞ + l1_penalty_safety on every iteration.
l1_penalty_safety: NumberAdditive safety margin in the Han-Powell ν update:
ν_new = max(ν, ‖λ_qp‖_∞ + l1_penalty_safety). Default 0.1
per Nocedal-Wright §18.4. Only consulted when
globalization = L1Elastic.
l1_penalty_max: NumberUpper clamp on ν. Prevents catastrophic Armijo failure on
pathological problems where ‖λ_qp‖ momentarily spikes.
Default 1e10 — large enough not to interfere with normal
runs, small enough to keep the merit numerically stable.
Only consulted when globalization = L1Elastic.
bt_reduction: NumberBacktracking line-search reduction factor.
bt_min_alpha: NumberMinimum step before declaring line-search failure.
print_level: u8print_level: 0 = silent, 1 = per-iteration summary,
2+ = trace (planned).
lbfgs_max_history: u32Number of (s, y) history pairs retained when
hessian = Lbfgs. Mirrors the upstream
limited_memory_max_history default of 6 (Nocedal-Wright
recommends 3–20). Ignored for Exact and DampedBfgs.
dense_hessian_max_n: usizeLargest n at which a quasi-Newton Hessian is handed to the QP as a
dense matrix (sqp_dense_hessian_max_n). Above it, Lbfgs poses the
QP in its lifted low-rank form
(crate::sqp::SqpQpData::build_low_rank) and QuasiNewtonAuto
resolves to Lbfgs rather than DampedBfgs. At or below it nothing
changes from before gh#1054. Default 1000.
qp_schur_min_dim: Option<usize>Size from which the step QP uses Schur-complement updates
(gh#1054): when Some(d), each solve sets
QpOptions::use_schur_updates to n + m ≥ d; None leaves the QP
options as given. The driver reads sqp_qp_use_schur_updates only
when it is set explicitly, and then passes None here.
Why size: the refactor path pays a full sparse factorization per
working-set change, which is what made the cold first QP of a
15 000-variable model run for 30 minutes, while on small models the
two paths cost the same and the Schur path is the less robust one
(measured on the CLI fixture corpus under algorithm=active-set-sqp).
See docs/src/active-set-sqp.md for the Maros-Mészáros comparison
behind the default.
qp_schur_indefinite_max_dim: Option<usize>Size from which a warm-started step QP whose Hessian is not known
PSD goes back to the refactor path, overriding
Self::qp_schur_min_dim (gh#1083, gh#1091). None never does; cold
solves and PSD step QPs are not affected.
Such a QP carries the inertia hint Indefinite, and on it every
drop refactors anyway (a rank-2 update cannot re-run inertia control
on the enlarged null space), so only adds take the update. A warm step
QP is drop-dominated: the reporter’s took 187 Schur updates against
4181 working-set changes over a 3600 s run. It then pays for the larger
K_max — dearer factorizations, back-solves about 5× the small KKT’s —
and gets almost nothing back; on its handover QP the Schur path took
126 s, the refactor path 72 s.
Two step QPs carry the hint: an exact Hessian’s, and the lifted
L-BFGS one above sqp_dense_hessian_max_n
(crate::sqp::SqpQpData::build_low_rank), whose Hessian
diag(σI, ±1) is indefinite although the B it lifts is not. The rule
reads the hint, which is what the QP solver branches on, not the
Hessian source; until gh#1091 it read the source and missed the lift.
Cold solves keep the size rule because they are the add-dominated
ones the Schur path wins on, and at this size they are not all seeded
from the interior point: when the hint is not Psd the seed certifies
the Hessian PSD numerically (ipm_seeded_step_qp) and declines if it
is not. The lifted QP’s -1 entries never pass, and a nonconvex
model’s exact Hessian need not, so such a cold QP still builds its
working set one add at a time. (A convex model’s exact Hessian does
pass, and its cold step QPs are seeded.)
qp_ipm_cold_start_min_dim: Option<usize>NLP size n + m from which a step QP solved without a working set,
or with one that pins to an infeasible point, is first solved by the
convex interior point, when its Hessian is PSD, and the active-set
solver warm-started from the working set that solution identifies
(gh#1061). None never does. The warm case: the reporter’s second
step QP went to the ℓ1-elastic phase 1 from its infeasible warm start
and reached a 300 s cap after 564 working-set changes; seeded, 12.5 s.
A cold active-set start builds its working set one pivot at a time,
and on a large model that is the whole cost: gh#1061’s first step QP
(n = 14 952, 10 818 equalities, H = I) went through the elastic
phase 1 for more than 30 minutes on either factorization path without
finishing. The interior point solved it in 16 iterations, 4.3 s, and
the active-set solve from its working set took zero working-set
changes and one factorization, 0.4 s, to the same objective. The
active-set solve still has the last word, so the step and its
multipliers are what the active-set method certifies, and anything
short of an interior-point Optimal falls back to the cold start.
At the same size a warm start that pins feasibly is given a budget of four active-set iterations per iteration the last seed took, and is seeded once it runs over. gh#1061’s warm step QPs took 68–350 working-set changes and 51–263 s each at about one factorization per change; on the handover QP the budget and the seed together take 34 s against 72 s warm (126 s with Schur updates).
deadline: Option<Deadline>Solve-wide time budget (max_wall_time / max_cpu_time), the
same Deadline the interior-point arm checks. None means
unlimited and is the default for a directly constructed driver.
A shared handle rather than two durations, because the SQP is not
always the first thing a solve runs: crossover hands it the
interior-point phase’s own deadline, and a duration measured from
optimize would grant it a fresh budget (gh#1054).
Implementations§
Source§impl SqpOptions
impl SqpOptions
Sourcepub fn step_qp_schur_updates(
&self,
dim: usize,
warm: Option<HessianInertia>,
) -> Option<bool>
pub fn step_qp_schur_updates( &self, dim: usize, warm: Option<HessianInertia>, ) -> Option<bool>
Whether a step QP of size n + m = dim uses Schur updates, by
Self::qp_schur_min_dim and Self::qp_schur_indefinite_max_dim.
warm is the inertia hint of a QP warm-started from a working set,
None for a cold one, whose hint the rule does not read. None back
when the caller chose (qp_schur_min_dim unset), which leaves the QP
options as given.
Trait Implementations§
Source§impl Clone for SqpOptions
impl Clone for SqpOptions
Source§impl Debug for SqpOptions
impl Debug for SqpOptions
Auto Trait Implementations§
impl !Send for SqpOptions
impl !Sync for SqpOptions
impl Freeze for SqpOptions
impl RefUnwindSafe for SqpOptions
impl Unpin for SqpOptions
impl UnsafeUnpin for SqpOptions
impl UnwindSafe for SqpOptions
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T, U> Imply<T> for U
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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