3232 Variable ,
3333 alias ,
3434 constraint ,
35+ infer_quantifiables ,
3536 interval ,
3637)
3738from .identifiers import Name
@@ -157,9 +158,9 @@ def __init__(
157158 lower_bound : bool = True ,
158159 upper_bound : bool = True ,
159160 ) -> None :
161+ if not quantifiables :
162+ quantifiables = infer_quantifiables (tensor )
160163 if isinstance (tensor , Tensor ):
161- if not quantifiables :
162- quantifiables = tensor .quantifiables ()
163164 if not image :
164165 image = tensor .image
165166 if lower_bound and tensor .image .lower_bound == 0 :
@@ -246,9 +247,9 @@ class ActivationVariable(ModelFragment):
246247 used, if `False` no activation constraint will be added.
247248 lower_bound: Value of the lower bound used in the deactivation
248249 constraint. If `True` the variable's image's lower bound will be
249- used, if `False` no deactivation constraint will be added.
250+ used, if `False` no deactivation constraint will be added. Can only
251+ be used if the tensor is non-negative.
250252 name: Name of the generated activation variable
251- negate: Negate the returned indicator variable.
252253 projection: Mask used to project the variable's quantification. When
253254 this is set, the indicator variable will be set to 1 iff at least
254255 one of the projected tensor values is positive.
@@ -263,11 +264,10 @@ def __new__(
263264 upper_bound : ExpressionLike | TensorLike | bool = True ,
264265 lower_bound : ExpressionLike | TensorLike | bool = False ,
265266 name : Name | None = None ,
266- negate : bool = False ,
267267 projection : Projection = - 1 ,
268268 ) -> ActivationVariable :
269- if not quantifiables and isinstance ( tensor , Tensor ) :
270- quantifiables = tensor . quantifiables ( )
269+ if not quantifiables :
270+ quantifiables = infer_quantifiables ( tensor )
271271 domains = tuple (domain (q ) for q in quantifiables )
272272
273273 def quantification (
@@ -299,8 +299,7 @@ def activates(self) -> Quantified:
299299 bound = bound (* cp .lifted )
300300 elif bound is True :
301301 bound = tensor_image ().upper_bound
302- value = 1 - self .value (* cp ) if negate else self .value (* cp )
303- yield bound * value >= tensor (* cp .lifted )
302+ yield bound * self .value (* cp ) >= tensor (* cp .lifted )
304303
305304 @constraint (disabled = lower_bound is False )
306305 def deactivates (self ) -> Quantified :
@@ -317,8 +316,7 @@ def deactivates(self) -> Quantified:
317316 bound = bound (* cp )
318317 elif bound is True :
319318 bound = tensor_image ().lower_bound
320- value = 1 - self .value (* cp ) if negate else self .value (* cp )
321- yield bound * value <= term
319+ yield bound * self .value (* cp ) <= term
322320
323321 return _Fragment ()
324322
@@ -362,7 +360,6 @@ def activation_variable(
362360 upper_bound : ExpressionLike | TensorLike | bool = True ,
363361 lower_bound : ExpressionLike | TensorLike | bool = False ,
364362 name : Name | None = None ,
365- negate : bool = False ,
366363 projection : Projection = - 1 ,
367364) -> Callable [[TensorLike ], ActivationVariable ]:
368365 """Transforms a method into an :class:`ActivationVariable` fragment
@@ -379,7 +376,6 @@ def wrapper(fn: TensorLike) -> ActivationVariable:
379376 lower_bound = lower_bound ,
380377 upper_bound = upper_bound ,
381378 name = name ,
382- negate = negate ,
383379 projection = projection ,
384380 )
385381
@@ -416,8 +412,8 @@ def __init__(
416412 raise NotImplementedError () # TODO: Implement.
417413
418414 self ._tensor = tensor
419- if not quantifiables and isinstance ( tensor , Tensor ) :
420- quantifiables = tensor . quantifiables ( )
415+ if not quantifiables :
416+ quantifiables = infer_quantifiables ( tensor )
421417 self ._domains = tuple (domain (q ) for q in quantifiables )
422418
423419 self ._piece_count = Parameter .discrete (
@@ -528,7 +524,6 @@ class ActivatedVariable(ModelFragment):
528524 subscripts
529525 upper_bound: Tensor upper bound, can be omitted if `tensor` is a
530526 :class:`~opvious.modeling.Tensor` instance
531- negate: Negate the input indicator
532527 force_activation: Add constraint to ensure that the derived variable is
533528 at least equal to `tensor` when `indicator` is non-zero. You may
534529 choose to omit this if the variable is already pushed up via other
@@ -549,13 +544,11 @@ def __init__(
549544 upper_bound : ExpressionLike | None = None ,
550545 force_activation : bool = True ,
551546 force_deactivation : bool = True ,
552- negate : bool = False ,
553547 name : Name | None = None ,
554548 ) -> None :
555549 self ._tensor = tensor
556550 self ._indicator = indicator
557551 self ._indicator_projection = indicator_projection
558- self ._negate = negate
559552 self ._force_activation = force_activation
560553 self ._force_deactivation = force_deactivation
561554
@@ -593,12 +586,9 @@ def deactivates(self) -> Quantified:
593586 This constraint will be omitted if `force_deactivation` is false.
594587 """
595588 for cp in self ._quantification ():
596- toggle = (
597- 1 - self ._indicator (* cp )
598- if self ._negate
599- else self ._indicator (* cp )
600- )
601- yield self .value (* cp .lifted ) <= self ._upper_bound * toggle
589+ yield self .value (
590+ * cp .lifted
591+ ) <= self ._upper_bound * self ._indicator (* cp )
602592
603593 @constraint (lambda init , self : init (disabled = not self ._force_activation ))
604594 def activates (self ) -> Quantified :
@@ -607,14 +597,10 @@ def activates(self) -> Quantified:
607597 This constraint will be omitted if `force_activation` is false.
608598 """
609599 for cp in self ._quantification ():
610- toggle = (
611- self ._indicator (* cp )
612- if self ._negate
613- else 1 - self ._indicator (* cp )
614- )
615600 yield (
616601 self .value (* cp .lifted )
617- >= self ._tensor (* cp .lifted ) - self ._upper_bound * toggle
602+ >= self ._tensor (* cp .lifted )
603+ - self ._upper_bound * (1 - self ._indicator (* cp ))
618604 )
619605
620606
@@ -624,7 +610,6 @@ def activated_variable(
624610 indicator : Tensor ,
625611 indicator_projection : Projection = - 1 ,
626612 upper_bound : ExpressionLike | None = None ,
627- negate : bool = False ,
628613 force_activation : bool = True ,
629614 force_deactivation : bool = True ,
630615 name : Name | None = None ,
@@ -641,7 +626,6 @@ def wrapper(fn: TensorLike) -> ActivatedVariable:
641626 indicator = indicator ,
642627 indicator_projection = indicator_projection ,
643628 upper_bound = upper_bound ,
644- negate = negate ,
645629 force_activation = force_activation ,
646630 force_deactivation = force_deactivation ,
647631 name = name ,
0 commit comments