39
39
RangedExpression ,
40
40
)
41
41
from pyomo .core .expr .expr_common import _type_check_exception_arg
42
+ from pyomo .core .expr .relational_expr import TrivialRelationalExpression
42
43
from pyomo .core .expr .template_expr import templatize_constraint
43
44
from pyomo .core .base .component import ActiveComponentData , ModelComponentFactory
44
45
from pyomo .core .base .global_set import UnindexedComponent_index
63
64
64
65
_inf = float ('inf' )
65
66
_nonfinite_values = {_inf , - _inf }
66
- _known_relational_expressions = {
67
+ _known_relational_expression_types = {
67
68
EqualityExpression ,
68
69
InequalityExpression ,
69
70
RangedExpression ,
71
+ TrivialRelationalExpression ,
70
72
}
71
73
_strict_relational_exprs = {True , (False , True ), (True , False ), (True , True )}
72
74
_rule_returned_none_error = """Constraint '%s': rule returned None.
@@ -382,9 +384,7 @@ def get_value(self):
382
384
383
385
def set_value (self , expr ):
384
386
"""Set the expression on this constraint."""
385
- # Clear any previously-cached normalized constraint
386
- self ._expr = None
387
- if expr .__class__ in _known_relational_expressions :
387
+ if expr .__class__ in _known_relational_expression_types :
388
388
if getattr (expr , 'strict' , False ) in _strict_relational_exprs :
389
389
raise ValueError (
390
390
"Constraint '%s' encountered a strict "
@@ -393,6 +393,7 @@ def set_value(self, expr):
393
393
"using '<=', '>=', or '=='." % (self .name ,)
394
394
)
395
395
self ._expr = expr
396
+ return
396
397
397
398
elif expr .__class__ is tuple : # or expr_type is list:
398
399
for arg in expr :
@@ -407,7 +408,7 @@ def set_value(self, expr):
407
408
"Constraint expressions expressed as tuples must "
408
409
"contain native numeric types or Pyomo NumericValue "
409
410
"objects. Tuple %s contained invalid type, %s"
410
- % (self .name , expr , arg . __class__ .__name__ )
411
+ % (self .name , expr , type ( arg ) .__name__ )
411
412
)
412
413
if len (expr ) == 2 :
413
414
#
@@ -420,6 +421,7 @@ def set_value(self, expr):
420
421
"cannot contain None [received %s]" % (self .name , expr )
421
422
)
422
423
self ._expr = EqualityExpression (expr )
424
+ return
423
425
elif len (expr ) == 3 :
424
426
#
425
427
# Form (ranged) inequality expression
@@ -430,6 +432,7 @@ def set_value(self, expr):
430
432
self ._expr = InequalityExpression (expr [:2 ], False )
431
433
else :
432
434
self ._expr = RangedExpression (expr , False )
435
+ return
433
436
else :
434
437
raise ValueError (
435
438
"Constraint '%s' does not have a proper value. "
@@ -442,25 +445,9 @@ def set_value(self, expr):
442
445
#
443
446
# Ignore an 'empty' constraint
444
447
#
445
- elif expr . __class__ is type :
448
+ if expr is Constraint . Skip :
446
449
del self .parent_component ()[self .index ()]
447
- if expr is Constraint .Skip :
448
- return
449
- elif expr is Constraint .Infeasible :
450
- # TODO: create a trivial infeasible constraint. This
451
- # could be useful in the case of GDP where certain
452
- # disjuncts are trivially infeasible, but we would still
453
- # like to express the disjunction.
454
- # del self.parent_component()[self.index()]
455
- raise ValueError ("Constraint '%s' is always infeasible" % (self .name ,))
456
- else :
457
- raise ValueError (
458
- "Constraint '%s' does not have a proper "
459
- "value. Found '%s'\n Expecting a tuple or "
460
- "relational expression. Examples:"
461
- "\n sum(model.costs) == model.income"
462
- "\n (0, model.price[item], 50)" % (self .name , str (expr ))
463
- )
450
+ return
464
451
465
452
elif expr is None :
466
453
raise ValueError (_rule_returned_none_error % (self .name ,))
@@ -479,17 +466,18 @@ def set_value(self, expr):
479
466
try :
480
467
if expr .is_expression_type (ExpressionType .RELATIONAL ):
481
468
self ._expr = expr
469
+ return
482
470
except AttributeError :
483
471
pass
484
- if self . _expr is None :
485
- msg = (
486
- "Constraint '%s' does not have a proper "
487
- "value. Found '%s'\n Expecting a tuple or "
488
- "relational expression. Examples:"
489
- "\n sum(model.costs) == model.income"
490
- "\n (0, model.price[item], 50)" % ( self . name , str ( expr ))
491
- )
492
- raise ValueError ( msg )
472
+
473
+ raise ValueError (
474
+ "Constraint '%s' does not have a proper "
475
+ "value. Found %s '%s'\n Expecting a tuple or "
476
+ "relational expression. Examples:"
477
+ "\n sum(model.costs) == model.income"
478
+ "\n (0, model.price[item], 50)"
479
+ % ( self . name , type ( expr ). __name__ , str ( expr ) )
480
+ )
493
481
494
482
def lslack (self ):
495
483
"""
@@ -619,10 +607,9 @@ class Constraint(ActiveIndexedComponent):
619
607
620
608
_ComponentDataClass = ConstraintData
621
609
622
- class Infeasible ( object ):
623
- pass
610
+ Infeasible = TrivialRelationalExpression ( ' Infeasible' , ( 1 , 0 ))
611
+ Feasible = TrivialRelationalExpression ( 'Feasible' , ( 0 , 0 ))
624
612
625
- Feasible = ActiveIndexedComponent .Skip
626
613
NoConstraint = ActiveIndexedComponent .Skip
627
614
Violated = Infeasible
628
615
Satisfied = Feasible
@@ -640,11 +627,11 @@ def __new__(cls: Type[IndexedConstraint], *args, **kwds) -> IndexedConstraint: .
640
627
641
628
def __new__ (cls , * args , ** kwds ):
642
629
if cls != Constraint :
643
- return super (Constraint , cls ).__new__ (cls )
630
+ return super ().__new__ (cls )
644
631
if not args or (args [0 ] is UnindexedComponent_set and len (args ) == 1 ):
645
- return super (Constraint , cls ).__new__ (AbstractScalarConstraint )
632
+ return super ().__new__ (AbstractScalarConstraint )
646
633
else :
647
- return super (Constraint , cls ).__new__ (IndexedConstraint )
634
+ return super ().__new__ (IndexedConstraint )
648
635
649
636
@overload
650
637
def __init__ (self , * indexes , expr = None , rule = None , name = None , doc = None ): ...
@@ -901,7 +888,7 @@ def set_value(self, expr):
901
888
"""Set the expression on this constraint."""
902
889
if not self ._data :
903
890
self ._data [None ] = self
904
- return super (ScalarConstraint , self ).set_value (expr )
891
+ return super ().set_value (expr )
905
892
906
893
#
907
894
# Leaving this method for backward compatibility reasons.
@@ -928,6 +915,7 @@ class SimpleConstraint(metaclass=RenamedClass):
928
915
'add' ,
929
916
'set_value' ,
930
917
'to_bounded_expression' ,
918
+ 'expr' ,
931
919
'body' ,
932
920
'lower' ,
933
921
'upper' ,
@@ -981,7 +969,7 @@ def __init__(self, **kwargs):
981
969
_rule = kwargs .pop ('rule' , None )
982
970
self ._starting_index = kwargs .pop ('starting_index' , 1 )
983
971
984
- super (ConstraintList , self ).__init__ (Set (dimen = 1 ), ** kwargs )
972
+ super ().__init__ (Set (dimen = 1 ), ** kwargs )
985
973
986
974
self .rule = Initializer (
987
975
_rule , treat_sequences_as_mappings = False , allow_generators = True
0 commit comments