Skip to content

Constraint's equality attribute sometimes fails to properly identify equality constraints #2093

Open
@bknueven

Description

@bknueven

Summary

Occasionally constraint's new equality attribute will fail in ways that are difficult to reproduce.

Steps to reproduce the issue

This happened while I was running the sequential decomposition tool, but I can't seem to create a small example which reproduces the issue on my system. Instead I've included some hopefully useful output from the debugger:

ipdb> type(con)
<class 'pyomo.core.base.constraint._GeneralConstraintData'>
ipdb> type(con._expr)
<class 'pyomo.core.expr.logical_expr.RangedExpression'>
ipdb> con.pprint()
{Member of flow_mass_phase_comp_equality} : Size=2, Index=fs.feed._flow_mass_phase_comp_outlet_ref_index, Active=True
    Key                 : Lower : Body                                                                                                                                       : Upper : Active
    (0.0, 'Liq', 'H2O') :   0.0 : fs.feed.properties[0.0].flow_mass_phase_comp[Liq,H2O] - fs.PrimaryPumps[1].control_volume.properties_in[0.0].flow_mass_phase_comp[Liq,H2O] :   0.0 :   True
ipdb> id(con._expr.arg(2))
140324945312400
ipdb> id(con._expr.arg(0))
140324945312432
ipdb> con._expr.arg(0) == con._expr.arg(2)
True
ipdb> type(con._expr.arg(0))
<class 'float'>
ipdb> type(con._expr.arg(2))
<class 'float'>
ipdb> con._expr.arg(0) is con._expr.arg(2)
False
ipdb> con.equality
False
ipdb> 

Information on your system

Pyomo version: 6.1.1
Python version: 3.7.9
Operating system: Ubuntu 20.04.2
How Pyomo was installed (PyPI, conda, source): source
Solver (if applicable): N/A

Additional information

I believe the issue is with the is check on bounds in the equality attribute on line 416:

@property
def equality(self):
"""A boolean indicating whether this is an equality constraint."""
if self._expr.__class__ is logical_expr.EqualityExpression:
return True
elif self._expr.__class__ is logical_expr.RangedExpression:
# TODO: this is a very restrictive form of structural equality.
lb = self._expr.arg(0)
if lb is not None and lb is self._expr.arg(2):
return True
return False

For RangedExpressions with floating point bounds, CPython does not guarantee that the same valued float objects will occupy the same location in memory (as shown by the debugging output above). However, it is usually the case that identical floating point values do occupy the same location in memory, making this difficult to reproduce. There was some discussion around resolving this issue in Python itself: https://discuss.python.org/t/demoting-the-is-operator-to-avoid-an-identity-crisis/86, but the CPython team went a different route: https://bugs.python.org/issue34850.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions