v0.7.0
[0.7.0] - 2026-04-23
First release where ripopt solves strictly more HS and more CUTEst
problems than native Ipopt (+2 HS, +1 CUTEst). The headline theme of
this cycle is closing the remaining behavioral gaps between ripopt and
Ipopt 3.14.x — most importantly the convergence semantics around the
bound multipliers z_L/z_U, the post-restoration multiplier reset,
the Mehrotra predictor-corrector, the KKT backward-error probe, and
the barrier-subproblem stop-test gate in Free-mode μ updates.
Breaking changes
- **
refactor(ipm)!: dropz_opt, align convergence with Ipopt's iterative-zsemantics** (e35407c). Previously ripopt kept a separatez_optreconstruction used only by the convergence test; now the iterativez_L/z_Uthemselves are the convergence vector, matching Ipopt'sPDFullSpaceSolversemantics. TheSolveDiagnostics::final_dual_inf_scaledfield has been removed — callers that consumed it should switch tofinal_dual_inf`. Bumped
to a minor release for this reason. SolveStatus::Acceptablewas removed. Non-Optimalstatuses now
always surface honestly (MaxIterations,NumericalError,
RestorationFailed,Infeasible). Consumers that treated
Acceptableas success should audit their integration.
Added
ripopt-py: direct Python interface with JAX autodiff (b89f169,
a9ba046). Exposes a persistentProblemclass with dual warm start
(Problem.solve(lam0=, z_l0=, z_u0=), b95e5b8), accepts Ipopt-style
option aliases (mu_strategy,sb,bound_push, cyipopt-style
names, 4779b37), and is now published on PyPI (e32b57b) alongside
pyomo-ripopt(8cce31a).- NaN/Inf evaluation hardening and C API parity (66d77a1,
42f4015). ripopt now matches Ipopt's behavior when user callbacks
return NaN/Inf or signal failure: α-halving retry on post-step
evaluation failure (2b5cb99), softEvalError → NumericalError
transition instead of hard abort (07cf37f), and structural parity
with Ipopt'sTNLP::eval_*error-handling contract. NlpProblem::new_xflag for evaluation caching (d138a25). User
code can now short-circuit repeated evaluations at the same
iterate, matching Ipopt'snew_xcontract. Existing trait
implementations compile unchanged (the parameter defaults totrue
on old code paths).num-dualautomatic-differentiation example (4aad98a) with a
dedicated README section showing forward-mode AD through
NlpProblem.- TSV direction-diff harness for step-by-step comparison against
a reference solver trace (e11832f), with an extended trace schema
(α_max, τ, Σ condition number, SOC-accepted, c6178d6). - GAMS nlpbench benchmark harness (eac674b). New
gams/Makefile
targetsbench-smoke/bench-small/bench-medium/bench-large
/bench-alldrive the (vendored, gitignored)gams/nlpbench/
test-set runner against both ripopt and ipopt and emit
BENCHMARK_REPORT_<size>_<version>.md. Status returned by ripopt is
mapped to nlpbench's signed-status convention so reports classify
"locally optimal", "infeasible", and "iteration limit" correctly. - Adversary agent sweep (new
adversary/runs/). First full run of
the automated NLP correctness-testing harness: Rosen-Suzuki, HS13,
Discrete Boundary Value (n ∈ {20, 200, 500, 1000, 2000, 5000}),
parametric projection, Powell badly-scaled. Four PASS; HS13 filed as
issue #19 (solver limitation on a known LICQ/MFCQ-degenerate problem). - Reference-gap roadmap (
docs/REFERENCE_GAP_ROADMAP.md). ~700-line
ripopt-vs-Ipopt and rmumps-vs-MUMPS gap analysis drafted with the
ipopt-expert and mumps-expert agents, cataloging known deficiencies
(D1-D10 for ripopt, (a)-(i) for rmumps), genuine advantages, and a
ranked roadmap of 20 cross-cutting items (correctness-first).
Changed
- Post-restoration multiplier reset matches Ipopt exactly (07dcdcc,
20b51ce, af0bf09). Thez_L/z_Ureset after restoration now
absorbs the correct contribution into the least-squaresy
re-solve, computed via Ipopt's exact augmented system. This was the
source of several silent failures at the boundary between
restoration and the main IPM. - Mehrotra predictor-corrector: removed the skip gate that
previously disabled corrections on highly infeasible iterates
(72bae01); fixed the cross-term in the primal RHS and thedz
recovery step (9deaff4); split Mehrotra vs filter-LS RHS
assembly so the corrector and the line-search share no hidden
state (eeae3d5). - Ipopt barrier-subproblem stop-test gate in Free-mode μ updates
(7f333de). μ no longer decreases until the current barrier
subproblem passes Ipopt's stop test, preventing premature
centering collapse on marginally feasible iterates. - Always verify KKT backward error; no IC-undoing iterative
refinement (66bce53). Then+m ≥ 100shortcut in
factor_with_inertia_correctionis gone — every accepted
factorization now runs the backward-error probe. Iterative
refinement no longer tries to undo the inertia-correction
perturbation; Ipopt treats IC as part of the linear system, and so
does ripopt now. - Reject rank-deficient solutions with huge magnitude (3211838).
When the solve produces a correction whose norm is inconsistent
with the residual, the factorization is rejected and perturbation
escalates, matching Ipopt'sPDPerturbationHandlerbehavior. - Iterative refinement in custom-RHS and condensed solves
(96329a2). Custom-RHS solves (used by sensitivity, SOC, and the
condensed-KKT path) now run the same iterative-refinement loop as
the primary solve. - Dropped element-wise NaN checks on
grad_fandg(0ed77e1).
Replaced with vector-norm finiteness checks, matching Ipopt. - Loqo μ-oracle monotone floor (02471aa). Re-applies the floor
that prevents μ from increasing inside a barrier subproblem.
Fixed
- Documentation: rustdoc intra-doc link warnings from
[i]index
brackets — escape as\[i\]to prevent rustdoc from parsing them
as link references. - Benchmark runners: drop stale
final_dual_inf_scaledreferences in
the CUTEst runner (bc1fa21) and the HS native-ipopt runner
(2780600). - CI: gate
cat_a_probeexample behind thecutestfeature (2b293e2). - Two cyipopt-style spelling aliases were accepted on both the
Python and Rust option paths (4779b37). - Preprocessing: skip redundancy detection on callback eval failure
(ff9144e).detect_redundant_constraintspreviously panicked when
the user'sconstraints()callback returnedfalseat the synthetic
probe point; it now bails out cleanly, leaves the original
constraint set intact, and lets the IPM handle the eval failure
through the normal α-halving path. - Julia binding: status-code constants align with
RipoptReturnStatus
(437bba0, 7733576).Ripopt.jland the embedded C wrapper status
enums had drifted from the Rust-sideRipoptReturnStatus
definitions; both are now regenerated from the canonical list so
MOI.TerminationStatusreports the correct Ipopt-compatible code. - GAMS bridge: pass
index_styletoripopt_create(61aa808).
The GAMS bridge was callingripopt_createwithout the new
index_styleargument, causing 1-based / 0-based indexing confusion
on GAMS-formulated NLPs. - Dead code removed (6e91f55). LS-y helpers and unused imports
pruned fromsrc/ipm.rs,src/kkt.rs, andsrc/c_api.rs. No
behavioral change.
Performance
Fresh benchmark results (2026-04-21 on Apple Mac Mini, aarch64-apple-darwin):
- HS suite: ripopt 118/120 (98.3%), Ipopt 116/120 (96.7%).
15.0× geometric-mean speedup on 116 commonly-solved, median 14.2×,
ripopt faster on 113/116 (97%). ripopt-only solves: HS214, HS223.
Ipopt-only solves: 0. - CUTEst suite: ripopt 562/727 (77.3%), Ipopt 561/727 (77.2%).
9.9× geometric-mean speedup on 525 commonly-solved, median 18.9×,
ripopt faster on 440/525 (84%). ripopt-only solves: 37;
Ipopt-only solves: 36. - Electrolyte thermodynamics: ripopt 13/13, Ipopt 12/13. 17.5×
geometric-mean speedup on 12 commonly-solved. Seawater speciation
now takes 1,415 iterations (was 22 at v0.6.2) under the stricter
v0.7.0 convergence semantics, but still solves where Ipopt declares
Infeasible. - Grid (AC OPF): ripopt 3/4, Ipopt 4/4. Geometric-mean 2.8× on
the 3 commonly-solved. See Notes below for the case30_ieee
regression.
Notes
- case30_ieee regression. At v0.7.0 ripopt reaches
MaxIterations
on PGLib-OPFcase30_ieee, regressing from v0.6.2 which converged
to a different local minimum (obj=8,609.66, 4.6% above the known
optimum of 8,081.52, compared to Ipopt's 8,208.52 at 1.6% above).
The regression is a side-effect of dropping then+m ≥ 100
shortcut infactor_with_inertia_correction(66bce53): the
stricter backward-error probe now perturbs more aggressively on
this rank-deficient AC-OPF Jacobian. The v0.6.2 "solve" was in
fact converging to a different local optimum than Ipopt, so this
is less of a correctness regression than a robustness regression;
still, ripopt now solves one fewer grid problem than it did at
v0.6.2. Tracked for a future patch. - Poisson 2.5K large-scale benchmark. The Poisson 2.5K problem
exhaustsmax_iterunder the v0.7.0 convergence semantics and
causesmake benchmarkto hang if run to completion; the large-
scale sweep is therefore reported for the 4 problems that finish
quickly (Rosenbrock 500, Bratu 1K, SparseQP 1K, OptControl 2.5K).
Historical v0.6.2 timings for the other large-scale problems
remain inbenchmarks/large_scale/large_scale_results.txt. Full
sweep is gated on a separate investigation. - ripopt-py on PyPI as
ripopt-py(direct JAX-backed interface)
andpyomo-ripopt(Pyomo plugin). Workspace crates stay on
semver:ripopt→ 0.7.0,rmumpsunchanged at 0.1.1.