Skip to content

Helpful error when a solver can't handle the PH quadratic proximal term (issue #762, part 1)#768

Open
DLWoodruff wants to merge 1 commit into
Pyomo:mainfrom
DLWoodruff:prox-quadratic-solver-check
Open

Helpful error when a solver can't handle the PH quadratic proximal term (issue #762, part 1)#768
DLWoodruff wants to merge 1 commit into
Pyomo:mainfrom
DLWoodruff:prox-quadratic-solver-check

Conversation

@DLWoodruff

Copy link
Copy Markdown
Collaborator

Part 1 of issue #762

When Progressive Hedging uses a quadratic proximal term and the solver cannot handle a quadratic objective, the run fails confusingly. For HiGHS on a problem with integers/binaries (an MIQP), the failure is the cryptic status=unknown, TerminationCondition=unknown. This adds an actionable message.

Approach

PH's proximal term makes the subproblem objective quadratic unless --linearize-proximal-terms is set, so this is a quadratic-objective/solver mismatch. Pyomo's capability information is uneven (the legacy has_capability API reports it for solvers like cbc/glpk, but the APPSI and pyomo.contrib.solver wrappers used for HiGHS don't expose it), so a single up-front check isn't enough. Two complementary checks in phbase.iterk_loop cover both cases:

  • Proactive — before the first proximal solve, if the solver reports (via has_capability) that it cannot handle a quadratic objective, raise immediately with guidance. Catches e.g. cbc, glpk before wasting a solve.
  • Reactive — after the first proximal solve, if no subproblem anywhere produced a solution, raise with guidance. Covers solvers that expose no capability info (HiGHS et al.). The "no solution anywhere" test uses allreduce_or, so the raise decision is identical on every rank (no MPI desync).

Both checks no-op unless a non-linearized proximal term is actually active, and the reactive check is limited to iteration 1 — if the first quadratic solve succeeds, the solver supports quadratic objectives, so a later failure is a genuine optimization issue, not a capability problem. The error message points the user at --linearize-proximal-terms (and --linearize-binary-proximal-terms for binaries).

Changes

  • mpisppy/utils/sputils.py: solver_quadratic_objective_capability() — tri-state probe (True/False/None, where None means "unknown", never "unsupported").
  • mpisppy/phbase.py: _prox_is_quadratic, _check_prox_solver_capability (proactive), _check_prox_solve_succeeded (reactive); wired into iterk_loop.
  • mpisppy/tests/test_prox_solver_compat.py: solver-independent unit tests (probe tri-state, both guards, MPI-consistency via allreduce_or), wired into run_coverage.bash and .github/workflows/test_pr_and_main.yml.

Validation

  • New unit tests: 16 pass.
  • Existing PH tests (test_ef_ph.py): pass, with no false positives on a quadratic-capable solver (one unrelated pre-existing failure, test_scenario_lpwriter_extension, fails identically on main).
  • End-to-end: PH with glpk (no QP) at PHIterLimit=1 now raises Solver 'glpk' reports that it cannot handle a quadratic objective ... Re-run with --linearize-proximal-terms ...; the same run with --linearize-proximal-terms completes.

🤖 Generated with Claude Code

…issue Pyomo#762)

Progressive Hedging's proximal term makes the subproblem objective
quadratic (unless --linearize-proximal-terms is used). A solver that
cannot handle a quadratic objective then fails -- and for HiGHS on an
MIQP the failure is cryptic (TerminationCondition=unknown). Detect this
and tell the user what to do, via two complementary checks in iterk_loop:

- Proactive: before the first proximal solve, if the solver reports via
  the legacy has_capability API that it cannot handle a quadratic
  objective (e.g. cbc, glpk), raise immediately with guidance.
- Reactive: after the first proximal solve, if no subproblem anywhere
  produced a solution, raise with guidance. This covers solvers that do
  not expose has_capability (e.g. HiGHS via the APPSI / contrib.solver
  interfaces). The "no solution anywhere" test uses allreduce_or so the
  raise decision is identical on every rank.

Both checks no-op unless a non-linearized proximal term is actually
active, and the reactive check is limited to iteration 1 (if the first
quadratic solve works, the solver supports quadratic objectives).

Adds sputils.solver_quadratic_objective_capability (tri-state probe) and
a solver-independent unit test, wired into run_coverage.bash and the CI
workflow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@DLWoodruff

DLWoodruff commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator Author

@tvalenciaz this addresses part 1 of #762 — please review it when you get a chance.

@codecov

codecov Bot commented Jun 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 74.16%. Comparing base (efd19a4) to head (4647505).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #768      +/-   ##
==========================================
+ Coverage   74.13%   74.16%   +0.02%     
==========================================
  Files         166      166              
  Lines       21181    21207      +26     
==========================================
+ Hits        15703    15728      +25     
- Misses       5478     5479       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant