Skip to content

Strategy #5: rapid infeasibility detection + penalty-IPM #41

@jkitchin

Description

@jkitchin

Status

  • Part A (rapid infeasibility detection): shipped. Landed in 8a711f3 ("feat(conv): rapid infeasibility detection in the main loop") and widened by 84a8ef1 ("Widen locally-infeasible detection to inner-failure exits"). On by default (infeas_stationarity_tol = 1e-8, infeas_max_streak = 5); regression test at crates/pounce-restoration/tests/infeas_detection_benefit.rs. Design note status header: dev-notes/research/penalty-ipm-infeasibility-detection.md §3.
  • Part B (penalty-IPM): design / proposed, not started. Gated on Strategy #4: composite-step (Byrd–Omojokun) globalization #40 Phase 1 (composite step) being evaluated, since they share the feasibility direction.

Summary

Follow-up to land SOTA feasibility-restoration strategy #5. Full design note: dev-notes/research/penalty-ipm-infeasibility-detection.md.

Strategy #5 has two separable halves. The note recommends shipping the detection half first — it is small, low-risk, and high value on its own.

Part A — rapid infeasibility detection (SHIPPED)

A cheap per-iteration test that recognizes convergence to a stationary point of the infeasibility measure with ‖c(x)‖ bounded away from zero, and exits immediately with LocalInfeasibility instead of grinding to max_iter or thrashing restoration.

The test fires when all three hold:

  • θ = ‖c(x)‖ > κ · constr_viol_tol (e.g. κ = 100) — violation bounded away from zero;
  • scaled gradient ‖J(x)ᵀc(x)‖ / max(1, ‖c(x)‖) below a tight tolerance — infeasibility stationary;
  • held for N consecutive iterations (e.g. N = 5) — a streak counter.

Jᵀc is already computable from curr_jac_* / curr_c / curr_d_minus_s in ipopt_cq.rs — no new linear solve, a few inner products per iteration.

As shipped: IpoptCalculatedQuantities::curr_infeasibility_stationarity (ipopt_cq.rs:767), ConvergenceStatus::LocallyInfeasible (conv_check/trait.rs:34), streak fields + gate in OptErrorConvCheck (conv_check/opt_error.rs), main-loop mapping in ipopt_alg.rs:475SolverReturn::LocalInfeasibility, options in upstream_options.rs / application.rs / alg_builder.rs.

Why first: removes three hand-tuned, fragile cycle detectors in invoke_restoration (ipopt_alg.rs:984-1052) with a principled test; faithful to Ipopt's IpRestoConvCheck.cpp LOCALLY_INFEASIBLE, of which pounce previously had only the restoration-side half.

Part B — penalty-IPM (proposed second, multi-week)

A true penalty-IPM modifies the KKT system: barrier problem becomes min f(x) + ν‖c(x)‖₁ + barrier, constraints enter as penalized elastic variables, steering rule drives ν from predicted infeasibility reduction (Curtis–Nocedal–Wächter 2010). Reuses the restoration NLP's 5-block elastic reformulation applied to the main problem; shares the feasibility direction with strategy #4's normal step. Should not start until #4 Phase 1 is evaluated.

Note: pounce already has a PenaltyLsAcceptor (penalty-based globalization, not a full penalty-IPM) and a pounce-l1penalty crate.

Open questions

  • Default Part A on? Resolved: yes, default on (infeas_stationarity_tol = 1e-8, infeas_max_streak = 5).
  • Retire the three cycle detectors immediately or keep one release as belt-and-suspenders? (Recommendation in design note: leave for one release, measure overlap, then delete.)
  • Tune infeas_stationarity_tol, κ, N on the restoration-heavy subset (S365, S365MOD, PFIT1/4, ACOPR family).
  • Is Part B in scope at all, or is PenaltyLsAcceptor + Part A sufficient coverage of strategy Add Thierry-Biegler l1-exact penalty-barrier phase for degenerate NLPs #5?

In-tree reference: ref/Ipopt/src/Algorithm/IpRestoConvCheck.cpp.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions