Skip to content

Commit 892ba17

Browse files
Add min and max to formula engine
The operations are private until we've added more tests and know that they are working. Signed-off-by: Matthias Wende <[email protected]>
1 parent e8c93d1 commit 892ba17

File tree

1 file changed

+109
-12
lines changed

1 file changed

+109
-12
lines changed

src/frequenz/sdk/timeseries/_formula_engine/_formula_engine.py

Lines changed: 109 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
ConstantValue,
3636
Divider,
3737
FormulaStep,
38+
Maximizer,
3839
MetricFetcher,
40+
Minimizer,
3941
Multiplier,
4042
OpenParen,
4143
Subtractor,
@@ -45,12 +47,14 @@
4547
_logger = logging.Logger(__name__)
4648

4749
_operator_precedence = {
48-
"(": 0,
49-
"/": 1,
50-
"*": 2,
51-
"-": 3,
52-
"+": 4,
53-
")": 5,
50+
"max": 0,
51+
"min": 1,
52+
"(": 2,
53+
"/": 3,
54+
"*": 4,
55+
"-": 5,
56+
"+": 6,
57+
")": 7,
5458
}
5559

5660

@@ -168,6 +172,36 @@ def __truediv__(
168172
"""
169173
return self._higher_order_builder(self, self._create_method) / other # type: ignore
170174

175+
def _max(
176+
self, other: _GenericEngine | _GenericHigherOrderBuilder | QuantityT
177+
) -> _GenericHigherOrderBuilder:
178+
"""Return a formula engine that outputs the maximum of `self` and `other`.
179+
180+
Args:
181+
other: A formula receiver, a formula builder or a QuantityT instance
182+
corresponding to a sub-expression.
183+
184+
Returns:
185+
A formula builder that can take further expressions, or can be built
186+
into a formula engine.
187+
"""
188+
return self._higher_order_builder(self, self._create_method).max(other) # type: ignore
189+
190+
def _min(
191+
self, other: _GenericEngine | _GenericHigherOrderBuilder | QuantityT
192+
) -> _GenericHigherOrderBuilder:
193+
"""Return a formula engine that outputs the minimum of `self` and `other`.
194+
195+
Args:
196+
other: A formula receiver, a formula builder or a QuantityT instance
197+
corresponding to a sub-expression.
198+
199+
Returns:
200+
A formula builder that can take further expressions, or can be built
201+
into a formula engine.
202+
"""
203+
return self._higher_order_builder(self, self._create_method).min(other) # type: ignore
204+
171205

172206
class FormulaEngine(
173207
Generic[QuantityT],
@@ -466,6 +500,10 @@ def push_oper(self, oper: str) -> None:
466500
self._build_stack.append(Divider())
467501
elif oper == "(":
468502
self._build_stack.append(OpenParen())
503+
elif oper == "max":
504+
self._build_stack.append(Maximizer())
505+
elif oper == "min":
506+
self._build_stack.append(Minimizer())
469507

470508
def push_metric(
471509
self,
@@ -650,15 +688,15 @@ def _push(
650688
self._steps.append((TokenType.OPER, ")"))
651689
self._steps.append((TokenType.OPER, oper))
652690

653-
# pylint: disable=protected-access
654691
if isinstance(other, (FormulaEngine, FormulaEngine3Phase)):
655692
self._steps.append((TokenType.COMPONENT_METRIC, other))
656-
elif isinstance(other, (Quantity, float)):
693+
elif isinstance(other, (Quantity, float, int)):
657694
match oper:
658-
case "+" | "-":
695+
case "+" | "-" | "max" | "min":
659696
if not isinstance(other, Quantity):
660697
raise RuntimeError(
661-
f"A Quantity must be provided for addition or subtraction to {other}"
698+
"A Quantity must be provided for addition,"
699+
f" subtraction, min or max to {other}"
662700
)
663701
case "*" | "/":
664702
if not isinstance(other, (float, int)):
@@ -668,9 +706,8 @@ def _push(
668706
self._steps.append((TokenType.CONSTANT, other))
669707
elif isinstance(other, _BaseHOFormulaBuilder):
670708
self._steps.append((TokenType.OPER, "("))
671-
self._steps.extend(other._steps)
709+
self._steps.extend(other._steps) # pylint: disable=protected-access
672710
self._steps.append((TokenType.OPER, ")"))
673-
# pylint: enable=protected-access
674711
else:
675712
raise RuntimeError(f"Can't build a formula from: {other}")
676713
assert isinstance(
@@ -801,6 +838,66 @@ def __truediv__(
801838
"""
802839
return self._push("/", other)
803840

841+
@overload
842+
def max(
843+
self, other: _CompositionType1Phase
844+
) -> HigherOrderFormulaBuilder[QuantityT]:
845+
...
846+
847+
@overload
848+
def max(
849+
self, other: _CompositionType3Phase | QuantityT
850+
) -> HigherOrderFormulaBuilder3Phase[QuantityT]:
851+
...
852+
853+
def max(
854+
self, other: _CompositionType | QuantityT
855+
) -> (
856+
HigherOrderFormulaBuilder[QuantityT]
857+
| HigherOrderFormulaBuilder3Phase[QuantityT]
858+
):
859+
"""Return a formula builder that calculates the maximum of `self` and `other`.
860+
861+
Args:
862+
other: A formula receiver, or a formula builder instance corresponding to a
863+
sub-expression.
864+
865+
Returns:
866+
A formula builder that can take further expressions, or can be built
867+
into a formula engine.
868+
"""
869+
return self._push("max", other)
870+
871+
@overload
872+
def min(
873+
self, other: _CompositionType1Phase
874+
) -> HigherOrderFormulaBuilder[QuantityT]:
875+
...
876+
877+
@overload
878+
def min(
879+
self, other: _CompositionType3Phase | QuantityT
880+
) -> HigherOrderFormulaBuilder3Phase[QuantityT]:
881+
...
882+
883+
def min(
884+
self, other: _CompositionType | QuantityT
885+
) -> (
886+
HigherOrderFormulaBuilder[QuantityT]
887+
| HigherOrderFormulaBuilder3Phase[QuantityT]
888+
):
889+
"""Return a formula builder that calculates the minimum of `self` and `other`.
890+
891+
Args:
892+
other: A formula receiver, or a formula builder instance corresponding to a
893+
sub-expression.
894+
895+
Returns:
896+
A formula builder that can take further expressions, or can be built
897+
into a formula engine.
898+
"""
899+
return self._push("min", other)
900+
804901

805902
class HigherOrderFormulaBuilder(Generic[QuantityT], _BaseHOFormulaBuilder[QuantityT]):
806903
"""A specialization of the _BaseHOFormulaBuilder for `FormulaReceiver`."""

0 commit comments

Comments
 (0)