Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion pyomo/gdp/plugins/hull.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,18 @@ class Hull_Reformulation(GDP_to_MIP_Transformation):
'LeeGrossmann', or 'GrossmannLee'
EPS : float
The value to use for epsilon [default: 1e-4]
eigenvalue_tolerance : float
Numerical tolerance for eigenvalue-based positive/negative
semi-definite checks when using the exact hull reformulation for
quadratic constraints (``exact_hull_quadratic=True``). An
eigenvalue :math:`\lambda` is treated as non-negative if
:math:`\lambda >= -\text{eigenvalue_tolerance}` and as
non-positive if :math:`\lambda <= \text{eigenvalue_tolerance}`
(i.e., eigenvalues in
``[-eigenvalue_tolerance, eigenvalue_tolerance]`` are treated as
zero). Increasing this value makes the convexity check more
permissive; decreasing it makes it more conservative.
[default: 1e-10]
targets : block, disjunction, or list of those types
The targets to transform. This can be a block, disjunction, or a
list of blocks and Disjunctions [default: the instance]
Expand Down Expand Up @@ -184,6 +196,30 @@ class Hull_Reformulation(GDP_to_MIP_Transformation):
description="Epsilon value to use in perspective function",
),
)
CONFIG.declare(
'eigenvalue_tolerance',
cfg.ConfigValue(
default=1e-10,
domain=cfg.NonNegativeFloat,
description="Numerical tolerance for eigenvalue-based PSD/NSD checks "
"in exact hull quadratic reformulations",
doc="""
Numerical tolerance used when determining positive semi-definiteness
(PSD) or negative semi-definiteness (NSD) of the Hessian matrix Q in
the exact hull reformulation for quadratic constraints
(``exact_hull_quadratic=True``).

An eigenvalue ``lam`` is treated as non-negative if
``lam >= -eigenvalue_tolerance``, and non-positive if
``lam <= eigenvalue_tolerance``. Increasing this value makes the
convexity classification more permissive (i.e., a wider band around
zero is treated as numerically zero, so more eigenvalues are accepted
as PSD/NSD); decreasing it makes the check more conservative (i.e.,
eigenvalues must be further from zero). For ill-conditioned Q matrices
a larger tolerance may be appropriate.
""",
),
)
CONFIG.declare(
'assume_fixed_vars_permanent',
cfg.ConfigValue(
Expand Down Expand Up @@ -976,7 +1012,7 @@ def _build_exact_quadratic_hull(
Q[idx_i, idx_j] += 0.5 * coef
Q[idx_j, idx_i] += 0.5 * coef

numerical_tolerance = 1e-10
numerical_tolerance = self._config.eigenvalue_tolerance
eigenvalues, _ = np.linalg.eigh(Q)
Q_is_psd = not np.any(eigenvalues < -numerical_tolerance)
Q_is_nsd = not np.any(eigenvalues > numerical_tolerance)
Comment on lines +1015 to 1018
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

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

This change introduces a new public config option (eigenvalue_tolerance) that affects whether the conic vs general exact-hull formulation is selected, but there do not appear to be any existing tests covering exact_hull_quadratic (and thus none validating that this tolerance is honored). Please add a focused unit test that sets exact_hull_quadratic=True and varies eigenvalue_tolerance to flip the PSD/NSD classification for a nearly-singular Q, asserting the chosen reformulation differs.

Copilot uses AI. Check for mistakes.
Expand Down