Skip to content

Exact hull reformulation for quadratic constraints#3874

Merged
blnicho merged 56 commits intoPyomo:mainfrom
sergey-gusev94:exact_hull_combined
Apr 30, 2026
Merged

Exact hull reformulation for quadratic constraints#3874
blnicho merged 56 commits intoPyomo:mainfrom
sergey-gusev94:exact_hull_combined

Conversation

@sergey-gusev94
Copy link
Copy Markdown
Contributor

Fixes # .

Summary/Motivation:

This PR adds support for the exact hull reformulation for quadratic constraints in Generalized Disjunctive Programming (GDP).

The current gdp.hull transformation applies perspective-function based relaxations for nonlinear constraints. While effective for many nonlinear expressions, this approach can produce weaker relaxations for quadratically constrained disjunctions.

This PR implements the exact hull reformulation for quadratic constraints described in:

Gusev, S., & Bernal Neira, D. E. (2025).
Exact Hull Reformulation for Quadratically Constrained Generalized Disjunctive Programs.
https://arxiv.org/abs/2508.16093

The implementation extends the existing hull transformation with an optional configuration flag (which is False by default) that detects quadratic constraints and applies the exact hull formulation when appropriate.

For convex quadratic constraints, the reformulation is expressed as a rotated second-order cone (SOC) representable constraint. For nonconvex quadratics and equality constraints, the general exact hull formulation is used.

Convexity is determined automatically through eigenvalue analysis of the Hessian matrix.

This provides a tighter relaxation for quadratic GDP models and maintains the quadratic structure of constraints in hull reformulation.

Changes proposed in this PR:

  • Extend the gdp.hull transformation with a new configuration option:

    • exact_hull_quadratic (default: False)
  • When enabled, quadratic constraints inside disjuncts are reformulated using the exact hull formulation instead of the standard perspective function.

  • Implement automatic convexity detection via eigenvalue decomposition of the quadratic Hessian matrix.

  • Apply different reformulations depending on structure:

    • Convex quadratic inequalities
      Reformulated using a rotated second-order cone representation.

    • Nonconvex quadratics or equalities
      Reformulated using the general exact hull formulation.

  • Add a configuration parameter:

    • eigenvalue_tolerance
      Controls numerical tolerance when checking positive or negative semidefiniteness of the Hessian.
  • Extend the implementation in pyomo.gdp.plugins.hull to support these reformulations.

  • Add unit tests verifying:

    • correctness of the generated constraints
    • compatibility with existing hull behavior
    • correctness of variable disaggregation and mappings.

The default behavior of gdp.hull is unchanged unless the new option is enabled.

Example usage:

TransformationFactory("gdp.hull").apply_to(
    model,
    exact_hull_quadratic=True
)

Legal Acknowledgement

By contributing to this software project, I have read the contribution guide and agree to the following terms and conditions for my contribution:

  1. I agree my contributions are submitted under the BSD license.
  2. I represent I am authorized to make the contributions and grant the license. If my employer has rights to intellectual property that includes these contributions, I represent that I have received permission to make contributions and grant the required license on behalf of that employer.

sergey-gusev94 and others added 30 commits March 6, 2026 15:19
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: sergey-gusev94 <101810399+sergey-gusev94@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Make eigenvalue PSD/NSD tolerance configurable in hull exact quadratic reformulation
Co-authored-by: sergey-gusev94 <101810399+sergey-gusev94@users.noreply.github.com>
…ic feature

Co-authored-by: sergey-gusev94 <101810399+sergey-gusev94@users.noreply.github.com>
doc: add `exact_hull_quadratic` to Hull class docstring
…unctions

Co-authored-by: sergey-gusev94 <101810399+sergey-gusev94@users.noreply.github.com>
…envalue_tolerance in permissive test

Co-authored-by: sergey-gusev94 <101810399+sergey-gusev94@users.noreply.github.com>
Add TestExactHullQuadratic tests and extract models into models.py
Co-authored-by: sergey-gusev94 <101810399+sergey-gusev94@users.noreply.github.com>
Co-authored-by: sergey-gusev94 <101810399+sergey-gusev94@users.noreply.github.com>
Co-authored-by: sergey-gusev94 <101810399+sergey-gusev94@users.noreply.github.com>
Co-authored-by: sergey-gusev94 <101810399+sergey-gusev94@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: sergey-gusev94 <101810399+sergey-gusev94@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Clean up stale "treated as linear" language and inline imports in test_hull.py
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 17, 2026

Codecov Report

❌ Patch coverage is 96.61017% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.78%. Comparing base (ae0ae63) to head (86bfc81).
⚠️ Report is 119 commits behind head on main.

Files with missing lines Patch % Lines
pyomo/gdp/plugins/hull.py 96.61% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3874      +/-   ##
==========================================
+ Coverage   86.80%   89.78%   +2.97%     
==========================================
  Files         903      903              
  Lines      106604   106780     +176     
==========================================
+ Hits        92537    95871    +3334     
+ Misses      14067    10909    -3158     
Flag Coverage Δ
builders 29.17% <5.08%> (-0.04%) ⬇️
default 86.30% <96.61%> (?)
expensive 35.61% <5.08%> (?)
linux 87.44% <96.61%> (+1.05%) ⬆️
linux_other 87.44% <96.61%> (?)
oldsolvers 27.62% <5.08%> (-0.53%) ⬇️
osx 82.80% <96.61%> (?)
win 85.88% <96.61%> (?)
win_other 85.88% <96.61%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 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.

@blnicho blnicho requested review from emma58 and jsiirola March 17, 2026 18:56
Copy link
Copy Markdown
Contributor

@emma58 emma58 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks pretty good, but I'm submitting a partial review because there's one major revision: See my notes below, but can you please rewrite this to use the QuadraticRepnVisitor rather than generate_standard_repn? You'll be able to get all the same data, though it will come in a slightly different structure. But it is much more robust to edge cases--I think generate_standard_repn is not long for this world, actually.

Comment thread pyomo/gdp/plugins/hull.py Outdated
Comment thread pyomo/gdp/plugins/hull.py
Comment on lines +749 to +755
"""Transform a single Constraint on a Disjunct.

Applies the appropriate hull reformulation to each
``ConstraintData`` in ``obj``. When ``exact_hull_quadratic`` is
enabled and the constraint body has polynomial degree 2, an exact
hull formulation is used instead of the perspective function.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the docstring!!

Comment thread pyomo/gdp/plugins/hull.py Outdated
Comment thread pyomo/gdp/plugins/hull.py Outdated
Comment thread pyomo/gdp/plugins/hull.py Outdated
@sergey-gusev94 sergey-gusev94 requested a review from emma58 April 14, 2026 01:54
@mrmundt
Copy link
Copy Markdown
Contributor

mrmundt commented Apr 14, 2026

@jsiirola , @blnicho - This one is ready for a re-review!

@mrmundt mrmundt self-requested a review April 14, 2026 18:36
Copy link
Copy Markdown
Contributor

@emma58 emma58 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you so much for making the change to the QuadraticRepnVisitor! This is looking quite good. Most of the changes suggested below are fairly minor, though if you can, it would be nice to abandon generate_standard_repn in the tests as well and to add public APIs where you are testing private components added by the transformation.

Comment thread pyomo/gdp/plugins/hull.py
Comment thread pyomo/gdp/plugins/hull.py Outdated
Comment thread pyomo/gdp/plugins/hull.py Outdated
Comment thread pyomo/gdp/plugins/hull.py Outdated
Comment thread pyomo/gdp/plugins/hull.py Outdated
Comment thread pyomo/gdp/plugins/hull.py
Comment thread pyomo/gdp/tests/test_hull.py Outdated
Comment on lines +2940 to +2941
t_var = relaxBlock.component('_conic_aux_t_c')
self.assertIsNotNone(t_var, "Expected auxiliary variable '_conic_aux_t_c'")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend against testing that depends on private names since any naming changes will break tests. It might be worth including an API (using the private_data Block) to retrieve the auxiliary variables, and then you can test them that way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 73e309e

Comment thread pyomo/gdp/tests/test_hull.py Outdated
Copy link
Copy Markdown
Contributor

@emma58 emma58 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the lightning-fast response! A couple more minor comments and then I really will be done complaining!

Comment thread pyomo/gdp/plugins/hull.py Outdated
Comment thread pyomo/gdp/plugins/hull.py Outdated
Comment thread pyomo/gdp/plugins/hull.py Outdated
@blnicho blnicho merged commit c1ce36d into Pyomo:main Apr 30, 2026
35 checks passed
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.

8 participants