Skip to content

Releases: jkitchin/ripopt

ripopt v0.7.1

25 Apr 22:10

Choose a tag to compare

Patch release focused on the Reference-Gap Roadmap: porting concrete behavioral details from Ipopt 3.14 that the v0.7.0 ripopt-vs-Ipopt audit flagged as missing.

Algorithmic ports from Ipopt 3.14

  • Full IpScaledNLP-equivalent x-scaling wrapper (roadmap #6)
  • PDPerturbationHandler δ-escalation schedule (roadmap #19)
  • Soft restoration phase: TrySoftRestoStep with the E_μ test (roadmap #12)
  • Restoration convergence three-gate: RestoFilterConvCheck (roadmap #13)
  • μ-dependent δ_c regularization (roadmap #6)
  • Separate s_d / s_c convergence scaling, drop of the legacy 1e4 cap (roadmap #1)
  • Drop of the ±1 inertia acceptance heuristic (roadmap #5)
  • Centrality term in the Quality-Function μ oracle (roadmap #14)

New features

  • AMPL external functions via the funcadd_ASL ABI: models depending on user-supplied function libraries (IDAES cbrt, Sundials, custom property packages) now load and solve in ripopt without modification (#15, #20)
  • warm_start_target_mu option for warm-start μ override (roadmap #18)
  • user_obj_scaling, mu_oracle_quality_function, quality_function_centrality exposed through C API, AMPL CLI, and the Python binding
  • IPM iteration log gains a compl column

Refactoring

~198 commits extracting named helpers from src/ipm.rs (e.g. compute_barrier_phi, apply_gondzio_mcc, run_line_search_loop, run_post_ls_restoration_cascade, try_soft_restoration, try_nlp_restoration_phase). No behavioral change — the IPM main loop now reads as a sequence of named phases instead of a 3000-line control-flow blob.

Benchmarks

Suite v0.7.0 v0.7.1 Native Ipopt
HS strict-Optimal 118/120 118/120 116/120
CUTEst strict-Optimal 560/727 556/727 556/727
HS geo-mean speedup 15.0× 20.8× 1× (baseline)
CUTEst geo-mean speedup 9.9× 12.0× 1× (baseline)

CUTEst regressed by 4 problems (20 lost, 16 gained) on least-squares / rank-deficient Jacobian families. The most plausible suspects are the stricter s_d/s_c convergence metric and the new μ-dependent δ_c regularization. Tagged comparison artifacts: benchmarks/cutest/results_v0.7.0.json vs benchmarks/cutest/results_v0.7.1.json.

Distribution

  • cargo add ripopt (crates.io) — the core Rust library
  • pip install ripopt (PyPI) — Python binding via the AMPL NL interface
  • pip install pyomo-ripopt (PyPI) — Pyomo SolverFactory('ripopt') integration
  • Ripopt.jl — Julia/JuMP via MathOptInterface
  • GAMS solver link via the GMO API
  • AMPL via --solver=ripopt

See CHANGELOG.md for the full list of changes.

v0.7.0

23 Apr 14:30

Choose a tag to compare

[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)!: drop z_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::Acceptable was removed. Non-Optimal statuses now
    always surface honestly (MaxIterations, NumericalError,
    RestorationFailed, Infeasible). Consumers that treated
    Acceptable as success should audit their integration.

Added

  • ripopt-py: direct Python interface with JAX autodiff (b89f169,
    a9ba046). Exposes a persistent Problem class 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), soft EvalError → NumericalError
    transition instead of hard abort (07cf37f), and structural parity
    with Ipopt's TNLP::eval_* error-handling contract.
  • NlpProblem::new_x flag for evaluation caching (d138a25). User
    code can now short-circuit repeated evaluations at the same
    iterate, matching Ipopt's new_x contract. Existing trait
    implementations compile unchanged (the parameter defaults to true
    on old code paths).
  • num-dual automatic-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
    targets bench-smoke / bench-small / bench-medium / bench-large
    / bench-all drive 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). The z_L/z_U reset after restoration now
    absorbs the correct contribution into the least-squares y
    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 the dz
    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). The n+m ≥ 100 shortcut in
    factor_with_inertia_correction is 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's PDPerturbationHandler behavior.
  • 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_f and g (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_scaled references in
    the CUTEst runner (bc1fa21) and the HS native-ipopt runner
    (2780600).
  • CI: gate cat_a_probe example behind the cutest feature (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_constraints previously panicked when
    the user's constraints() callback returned false at 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.jl and the embedded C wrapper status
    enums had drifted from the Rust-side RipoptReturnStatus
    definitions; both are now regenerated from the canonical list so
    MOI.TerminationStatus reports the correct Ipopt-compatible code.
  • GAMS bridge: pass index_style to ripopt_create (61aa808).
    The GAMS bridge was calling ripopt_create without the new
    index_style argument, causing 1-based / 0-based indexing confusion
    on GAMS-formulated NLPs.
  • Dead code removed (6e91f55). LS-y helpers and unused imports
    pruned from src/ipm.rs, src/kkt.rs, and src/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-OPF case30_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 the n+m ≥ 100
    shortcut in factor_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
    exhausts max_iter under the v0.7.0 convergence semantics and
    causes make benchmark to 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 in benchmarks/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)
    and pyomo-ripopt (Pyomo plugin). Workspace crates stay on
    semver: ripopt → 0.7.0, rmumps unchanged at 0.1.1.

ripopt v0.6.2

12 Apr 16:51

Choose a tag to compare

[0.6.2] - 2026-04-12

Added

  • Reorganized benchmarks under benchmarks/ with one self-contained
    subdirectory per suite: hs/, cutest/, electrolyte/, grid/ (renamed
    from opf/), cho/, large_scale/, gas/, and the new water/. Each
    suite has its own README and per-suite report. The composite report at
    benchmarks/BENCHMARK_REPORT.md aggregates HS + CUTEst + electrolyte +
    grid + CHO + large-scale; the gas and water suites are standalone (AMPL
    .nl interface, per-problem .sol output).
  • benchmarks/water/: 6 water distribution network design NLPs from
    MINLPLib (Hazen-Williams head-loss formulation). Solved via the AMPL
    interface; ripopt matches the best-known primal bound for water.nl
    (963.13) where Ipopt converges to a different local minimum (1001.16).
  • benchmarks/gas/: 4 gas pipeline NLPs from PDE-discretized Euler
    equations on pipe networks (gaslib11/40, steady/dynamic). Solved via the
    AMPL interface.
  • Loqo mu oracle (mu_oracle = "loqo") enabled by default, matching Ipopt's
    mu_oracle=quality-function strategy. Uses centrality measure
    xi = min(z*s)/avg_compl to set centering parameter sigma, preventing
    premature mu decrease from highly infeasible starting points. On gaslib11_steady:
    164→134 iters, NLP restorations 1→0, mode switches 24→8.
  • Sparse Gauss-Newton restoration: sparse J*J^T factorization for GN restoration
    when m > 500, removing the dense Bunch-Kaufman bottleneck (6s/step → 0.02s for
    gas pipeline NLPs). Sparse LS multiplier estimates for post-restoration y
    initialization.
  • Ruiz equilibration KKT scaling matching MUMPS SimScale schedule
    (KEEP(52)=7 for SYM=2): 1 inf-norm iteration + 3 one-norm iterations. Activated
    on demand when backward error is poor. Added row_abs_sum() to
    DenseSymmetric, SparseSymmetric, and KktMatrix.
  • Pretend-singular fallback using Ipopt's normwise residual ratio (threshold
    1e-5). When iterative refinement cannot reach target accuracy, tries delta_c
    first (PerturbForSingularity), then delta_w.
  • Adaptive mu dual infeasibility safeguard: prevents mu from collapsing to
    1e-11 while dual infeasibility remains large. Adds du_floor to barrier error
    and dual-infeasibility stagnation detection.
  • Structural degeneracy detection: after 3 consecutive iterations requiring
    delta_w > 0, skips the unperturbed factorization trial on subsequent iterations.
  • Dense BK increase_quality(): configurable pivot threshold with escalation
    0.64 → 0.8 → 0.95 → 1.0.
  • KKT factorization diagnostics (dim, nnz, wall time) at print_level ≥ 5.
    Line-search rejection details at print_level ≥ 7.

Changed

  • PretendSingular chain now matches Ipopt's PDFullSpaceSolver:
    solve → refine(fail) → IncreaseQuality → re-solve → perturbation.
    Previously perturbation was applied before IncreaseQuality.
  • Iterative refinement: max steps raised from 3–5 to 5–10 (matching Ipopt
    default). Stagnation detection extended to the non-IC path with a relaxed
    factor (1−1e−6 vs Ipopt's 1−1e−9).
  • Default sparse threshold unchanged (n+m ≥ 110) but sparse GN restoration
    now activates at m > 500 even when the outer IPM is dense.
  • Workspace version bumped — ripopt → 0.6.2, rmumps → 0.1.1.
  • ref/ directory and pyomo-ripopt/build/ now ignored in git.

Fixed

  • Fallback-result regression (is_strictly_better): when the main IPM
    reports NumericalError at a feasible iterate with a meaningful objective,
    a fallback solver that converges to a worse local minimum no longer
    silently replaces the main-IPM result. The comparator now requires either
    strict objective improvement (with primal feasibility ≤ 1e-4) or that the
    current result has no usable objective. This was the root cause of the
    c_api_hs071_basic, c_api_hs071_multiplier_extraction,
    c_api_null_output_params, test_hs071_sensitivity_vs_finite_differences,
    and test_sensitivity_linear_prediction_accuracy regressions introduced by
    the recent Loqo oracle / KKT quality-chain work.
  • Phosphoric-acid electrolyte test (electrolyte_05_phosphoric_acid)
    pinned to mu_oracle_quality_function=false. The Loqo oracle steers the
    solver into a chemically-wrong local minimum (pH ≈ 11.84) of the Gibbs
    free-energy surface; the pre-Loqo default converges to the correct basin
    (pH ≈ 2.25). Documented in the test comment.
  • All compiler warnings cleaned up in src/ipm.rs, rmumps/src/frontal.rs,
    and the adversary example suite.
  • 13 adversary example files updated to the current NlpProblem trait
    signature (removed _new_x: bool parameters and -> bool return types).
  • tests/large_scale_benchmark.rs unused import cleaned up.

Performance

  • Fresh benchmark results (2026-04-11 on Apple Mac Mini, aarch64-apple-darwin):
    • HS suite: ripopt 115/120 (95.8%), Ipopt 116/120 (96.7%) — nearly tied.
      14.0× geometric mean speedup on 113 commonly solved, median 15.1×,
      ripopt faster on 111/113 (98%).
    • CUTEst suite: ripopt 553/727 (76.1%), Ipopt 561/727 (77.2%). 8.0× geometric
      mean speedup on 513 commonly solved, median 18.8×, ripopt faster on 415/513 (81%).
      ripopt-only solves: 40; Ipopt-only solves: 48.
    • Electrolyte thermodynamics: ripopt 13/13 (100%), Ipopt 12/13, 20.8× geo mean.
    • Grid (AC OPF): 4/4 for both, Ipopt faster (0.2× geo mean).
  • gas pipeline NLPs: sparse GN restoration reduces per-step cost 300× on m > 500.

Notes

  • The fresh benchmark shows a small shift in both solvers' solve counts vs. the
    0.6.1 numbers reported in the prior CHANGELOG. This reflects run-to-run
    floating-point sensitivity on borderline problems combined with the quality-chain
    changes; the dominant failure modes (NumericalError, LocalInfeasibility) are
    unchanged.