63
63
TEMPLATIZE_CONSTRAINTS = False
64
64
65
65
_inf = float ('inf' )
66
- _nonfinite_values = {_inf , - _inf }
66
+ _ninf = - _inf
67
+ _nonfinite_values = {_inf , _ninf }
67
68
_known_relational_expression_types = {
68
69
EqualityExpression ,
69
70
InequalityExpression ,
@@ -246,21 +247,31 @@ def to_bounded_expression(self, evaluate_bounds=False):
246
247
247
248
if evaluate_bounds :
248
249
lb , body , ub = ans
249
- return self ._evaluate_bound (lb , True ), body , self ._evaluate_bound (ub , False )
250
+ return self ._evaluate_bound (lb , _ninf ), body , self ._evaluate_bound (ub , _inf )
250
251
return ans
251
252
252
- def _evaluate_bound (self , bound , is_lb ):
253
+ def _evaluate_bound (self , bound , unbounded ):
253
254
if bound is None :
254
255
return None
255
256
if bound .__class__ not in native_numeric_types :
256
- bound = float (value (bound ))
257
+ bound = value (bound )
258
+ if bound .__class__ not in native_numeric_types :
259
+ # Starting in numpy 1.25, casting 1-element ndarray to
260
+ # float is deprecated. We still want to support
261
+ # that... but without enforcing a hard numpy dependence
262
+ for cls in bound .__class__ .__mro__ :
263
+ if cls .__name__ == 'ndarray' and cls .__module__ == 'numpy' :
264
+ if len (bound ) == 1 :
265
+ bound = bound [0 ]
266
+ break
267
+ bound = float (bound )
257
268
# Note that "bound != bound" catches float('nan')
258
269
if bound in _nonfinite_values or bound != bound :
259
- if bound == ( - _inf if is_lb else _inf ) :
270
+ if bound == unbounded :
260
271
return None
261
272
raise ValueError (
262
273
f"Constraint '{ self .name } ' created with an invalid non-finite "
263
- f"{ 'lower ' if is_lb else 'upper ' } bound ({ bound } )."
274
+ f"{ 'upper ' if unbounded == _inf else 'lower ' } bound ({ bound } )."
264
275
)
265
276
return bound
266
277
@@ -333,12 +344,12 @@ def upper(self):
333
344
@property
334
345
def lb (self ):
335
346
"""float : the value of the lower bound of a constraint expression."""
336
- return self ._evaluate_bound (self .to_bounded_expression ()[0 ], True )
347
+ return self ._evaluate_bound (self .to_bounded_expression ()[0 ], _ninf )
337
348
338
349
@property
339
350
def ub (self ):
340
351
"""float : the value of the upper bound of a constraint expression."""
341
- return self ._evaluate_bound (self .to_bounded_expression ()[2 ], False )
352
+ return self ._evaluate_bound (self .to_bounded_expression ()[2 ], _inf )
342
353
343
354
@property
344
355
def equality (self ):
@@ -527,20 +538,9 @@ class _GeneralConstraintData(metaclass=RenamedClass):
527
538
__renamed__version__ = '6.7.2'
528
539
529
540
530
- class TemplateConstraintData ( ConstraintData ):
541
+ class TemplateDataMixin ( object ):
531
542
__slots__ = ()
532
543
533
- def __init__ (self , template_info , component , index ):
534
- # These lines represent in-lining of the
535
- # following constructors:
536
- # - ConstraintData,
537
- # - ActiveComponentData
538
- # - ComponentData
539
- self ._component = component
540
- self ._active = True
541
- self ._index = index
542
- self ._expr = template_info
543
-
544
544
@property
545
545
def expr (self ):
546
546
# Note that it is faster to just generate the expression from
@@ -552,17 +552,41 @@ def template_expr(self):
552
552
return self ._expr
553
553
554
554
def set_value (self , expr ):
555
- self .__class__ = ConstraintData
555
+ # Setting a value will convert this instance from a templatized
556
+ # type to the original Data type (and call the original set_value()).
557
+ #
558
+ # Note: We assume that the templatized type is created by
559
+ # inheriting (TemplateDataMixin, <original data class>), and
560
+ # that this instance doesn't have additional multiple
561
+ # inheritance that could re-order the MRO.
562
+ self .__class__ = self .__class__ .__mro__ [
563
+ self .__class__ .__mro__ .index (TemplateDataMixin ) + 1
564
+ ]
556
565
return self .set_value (expr )
557
566
558
- def to_bounded_expression (self ):
567
+ def to_bounded_expression (self , evaluate_bounds = False ):
559
568
tmp , self ._expr = self ._expr , self ._expr [0 ]
560
569
try :
561
- return super ().to_bounded_expression ()
570
+ return super ().to_bounded_expression (evaluate_bounds )
562
571
finally :
563
572
self ._expr = tmp
564
573
565
574
575
+ class TemplateConstraintData (TemplateDataMixin , ConstraintData ):
576
+ __slots__ = ()
577
+
578
+ def __init__ (self , template_info , component , index ):
579
+ # These lines represent in-lining of the
580
+ # following constructors:
581
+ # - ConstraintData,
582
+ # - ActiveComponentData
583
+ # - ComponentData
584
+ self ._component = component
585
+ self ._active = True
586
+ self ._index = index
587
+ self ._expr = template_info
588
+
589
+
566
590
@ModelComponentFactory .register ("General constraint expressions." )
567
591
class Constraint (ActiveIndexedComponent ):
568
592
"""
@@ -695,11 +719,17 @@ def construct(self, data=None):
695
719
if TEMPLATIZE_CONSTRAINTS :
696
720
try :
697
721
template_info = templatize_constraint (self )
698
- comp = weakref_ref (self )
699
- self ._data = {
700
- idx : TemplateConstraintData (template_info , comp , idx )
701
- for idx in self .index_set ()
702
- }
722
+ if self .is_indexed ():
723
+ comp = weakref_ref (self )
724
+ self ._data = {
725
+ idx : TemplateConstraintData (template_info , comp , idx )
726
+ for idx in self .index_set ()
727
+ }
728
+ else :
729
+ assert self .__class__ is ScalarConstraint
730
+ self .__class__ = TemplateScalarConstraint
731
+ self ._expr = template_info
732
+ self ._data = {None : self }
703
733
return
704
734
except TemplateExpressionError :
705
735
pass
@@ -926,6 +956,7 @@ class SimpleConstraint(metaclass=RenamedClass):
926
956
927
957
@disable_methods (
928
958
{
959
+ '__call__' ,
929
960
'add' ,
930
961
'set_value' ,
931
962
'to_bounded_expression' ,
@@ -947,6 +978,10 @@ class AbstractSimpleConstraint(metaclass=RenamedClass):
947
978
__renamed__version__ = '6.0'
948
979
949
980
981
+ class TemplateScalarConstraint (TemplateDataMixin , ScalarConstraint ):
982
+ pass
983
+
984
+
950
985
class IndexedConstraint (Constraint ):
951
986
#
952
987
# Leaving this method for backward compatibility reasons
0 commit comments