Skip to content

Commit

Permalink
Add tensor total convenience method (#101)
Browse files Browse the repository at this point in the history
  • Loading branch information
mtth authored Jul 18, 2023
1 parent add81eb commit c66ad00
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 9 deletions.
12 changes: 12 additions & 0 deletions opvious/modeling/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
literal,
render_identifier,
to_expression,
total,
within_domain,
)
from .identifiers import (
Expand Down Expand Up @@ -325,6 +326,17 @@ def __call__(self, *subscripts: ExpressionLike) -> Expression:
self._identifier, tuple(to_expression(s) for s in subscripts)
)

def total(self, absolute=False) -> Expression:
"""The tensor's total summed value
Args:
absolute: Sum the absolute value of the tensor's terms instead of
its raw values
"""
return total(
abs(self(*q)) if absolute else self(*q) for q in self.space()
)

def render_statement(self, label: Label) -> Optional[str]:
_logger.debug("Rendering tensor %s...", label)
c = self.category[0].lower()
Expand Down
16 changes: 10 additions & 6 deletions opvious/modeling/fragments.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ def __new__(
cls,
tensor: TensorLike,
*quantifiables: Quantifiable,
upper_bound: Union[ExpressionLike, bool] = True,
lower_bound: Union[ExpressionLike, bool] = False,
upper_bound: Union[ExpressionLike, TensorLike, bool] = True,
lower_bound: Union[ExpressionLike, TensorLike, bool] = False,
name: Optional[Name] = None,
projection: Projection = -1,
) -> ActivationVariable:
Expand Down Expand Up @@ -190,16 +190,16 @@ def __call__(self, *subs: ExpressionLike) -> Expression:
@constraint(disabled=upper_bound is False)
def activates(self):
bound = upper_bound
if bound is True:
bound = tensor_image().upper_bound
for cp in quantification(lift=True):
if callable(bound):
bound = bound(*cp.lifted)
elif bound is True:
bound = tensor_image().upper_bound
yield bound * self.value(*cp) >= tensor(*cp.lifted)

@constraint(disabled=lower_bound is False)
def deactivates(self):
bound = lower_bound
if bound is True:
bound = tensor_image().lower_bound
for cp in quantification():
if projection >= 0:
term = total(
Expand All @@ -208,6 +208,10 @@ def deactivates(self):
)
else:
term = tensor(*cp)
if callable(bound):
bound = bound(*cp)
elif bound is True:
bound = tensor_image().lower_bound
yield bound * self.value(*cp) <= term

return _Fragment()
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "opvious"
version = "0.16.6rc1"
version = "0.16.7rc1"
description = "Opvious Python SDK"
authors = ["Opvious Engineering <[email protected]>"]
readme = "README.md"
Expand Down
5 changes: 3 additions & 2 deletions tests/test_modeling.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ def __init__(self) -> None:
self.setup_cost = om.Parameter.non_negative(self.steps)
self.demand = om.Parameter.non_negative(self.steps)

self.production = om.Variable.non_negative(self.steps)
self.production = om.Variable.non_negative(
self.steps, upper_bound=self.demand.total(absolute=True)
)
self.production_indicator = om.fragments.ActivationIndicator(
tensor=self.production,
upper_bound=om.total(self.demand(t) for t in self.steps),
)
self.inventory = om.Variable.non_negative(self.steps)

Expand Down

0 comments on commit c66ad00

Please sign in to comment.