Map Constraint.Feasible/Infeasible to concrete constraints#3546
Map Constraint.Feasible/Infeasible to concrete constraints#3546blnicho merged 42 commits intoPyomo:mainfrom
Conversation
This permits assigning logical expressions based on IntervalVar objects, restoring previous behavior. We should revisit template expressions in the future to permit better expression type validation.
|
I feel like I am missing some context for this PR. Other than the example in #2918, I can't think of a use case for In short, I'm not really a user of |
pyomo/core/base/constraint.py
Outdated
| pass | ||
|
|
||
| Feasible = ActiveIndexedComponent.Skip | ||
| class Feasible(object): |
There was a problem hiding this comment.
| class Feasible(object): | |
| class Feasible: |
| pass | ||
|
|
||
| Feasible = ActiveIndexedComponent.Skip | ||
| class Feasible(object): |
There was a problem hiding this comment.
| class Feasible(object): | |
| class Feasible: |
| self.add(expr) | ||
| if self.rule is not None: | ||
| _rule = self.rule(self.parent_block(), ()) | ||
| for cc in iter(_rule): |
There was a problem hiding this comment.
Not entirely sure - it is just a temporary, and was copied from the implementation in ConstraintList (and preserved for consistency)
emma58
left a comment
There was a problem hiding this comment.
This is really nice! I'm very happy about the implications for GDP. :) I have a few questions and a couple comments, but mostly just so I understand things.
pyomo/core/base/constraint.py
Outdated
| # Note that an inequality is sufficient to induce | ||
| # infeasibility and is simpler to relax (in the Big-M | ||
| # sense) than an equality. | ||
| self._expr = InequalityExpression((1, 0), False) | ||
| return | ||
| elif expr is Constraint.Feasible: | ||
| # Note that we do not want to provide a trivial equality | ||
| # constraint as that can confuse solvers like Ipopt into | ||
| # believing that the model has fewer degrees of freedom | ||
| # than it actually has. | ||
| self._expr = InequalityExpression((0, 0), False) | ||
| return |
There was a problem hiding this comment.
Just so that I understand: Why do we have to put trivial expressions at all? Could we not consider Constraint.Feasible and Constraint.Infeasible to be (weird) relational expression nodes in the expression tree, themselves?
There was a problem hiding this comment.
We certainly could. That is, we could make Constraint.Feasible and Constraint.Infeasible be singleton expression objects (and not types). I think I would still make them special cases of InequalityExpression, mostly so that all the walkers, writers, and whatnot don't have to be updated to handle them as special cases. This just seemed simpler?
There was a problem hiding this comment.
Maybe it is simpler, but it's a bit confusing to me why, because it feels like we lose information in this step rather than carrying it through. Because if they were singleton expression objects, then wouldn't we would know after set_value that the Constraint is either non-trivial or it's Constraint.Infeasible or Constraint.Feasible? With this implementation, it could be trivial, and that has to be checked later (by transformations, writers, whatever). Otherwise downstream things would know they either have these special nodes or a non-trivial constraint. Which might be nice...?
There was a problem hiding this comment.
OK - I have re-implemented Feasible and Infeasible to be singleton inequality expressions. These are preserved and stored in the ConstraintData.expr. This resulted in some baseline changes (the least of which is that the string representation of them is Feasible and Infeasible now). I made a similar change to LogicalConstraint (except that they are singleton BooleanConstant objects).
| def body(self): | ||
| def expr(self): |
There was a problem hiding this comment.
Does this PR deprecate body? I agree with the change, but missed the magic somewhere, I think?
There was a problem hiding this comment.
Nope. .body is still implemented in the LogicalConstraintData base class and is an alias to .expr
Convert Feasible/Infeasible to singletons
Fixes #2918 .
Summary/Motivation:
We have historically mapped
Constraint.FeasibletoConstraint.Skipand hadConstraint.Infeasibleimmediately raise aValueError. This causes some undesirable behavior:Constraintthat was set byConstraint.Feasiblebecause theConstraintDatawas not created (see this question on SO)This updates
ConstraintandLogicalConstraintto mapConstraint.Feasibleto a trivial inequality, andConstraint.Infeasibleto a trivial infeasible Inequality.Changes proposed in this PR:
Constraint.FeasibleandConstraint.Infeasibleto actual trivial constraintslogical_to_linearandlogical_to_disjunctiveto support constant expressionsLegal Acknowledgement
By contributing to this software project, I have read the contribution guide and agree to the following terms and conditions for my contribution: