14"""Bounded (above and below), upper bounded, and lower bounded expressions."""
17from typing
import Any, Generic, NoReturn, Optional, Type, TypeVar
19_CHAINED_COMPARISON_MESSAGE = (
20 "If you were trying to create a two-sided or "
21 "ranged linear inequality of the form `lb <= "
22 "expr <= ub`, try `(lb <= expr) <= ub` instead"
30 extra_message: Optional[str] =
None,
32 """Raises TypeError on unsupported operators."""
34 f
"unsupported operand type(s) for {operator}: {lhs.__name__!r} and"
37 if extra_message
is not None:
38 message +=
"\n" + extra_message
39 raise TypeError(message)
46 """An inequality of the form lower_bound <= expression <= upper_bound.
49 * expression is a T, typically LinearBase or QuadraticBase.
50 * lower_bound is a float.
51 * upper_bound is a float.
53 Note: Because of limitations related to Python's handling of chained
54 comparisons, bounded expressions cannot be directly created usign
55 overloaded comparisons as in `lower_bound <= expression <= upper_bound`.
56 One solution is to wrap one of the inequalities in parenthesis as in
57 `(lower_bound <= expression) <= upper_bound`.
60 __slots__ =
"_expression",
"_lower_bound",
"_upper_bound"
62 def __init__(self, lower_bound: float, expression: T, upper_bound: float) ->
None:
81 "__bool__ is unsupported for BoundedExpression"
83 + _CHAINED_COMPARISON_MESSAGE
87 return f
"{self._lower_bound} <= {self._expression!s} <= {self._upper_bound}"
90 return f
"{self._lower_bound} <= {self._expression!r} <= {self._upper_bound}"
94 """An inequality of the form expression <= upper_bound.
97 * expression is a T, and
98 * upper_bound is a float
101 __slots__ =
"_expression",
"_upper_bound"
103 def __init__(self, expression: T, upper_bound: float) ->
None:
104 """Operator overloading can be used instead: e.g. `x + y <= 2.0`."""
120 def __ge__(self, lhs: float) -> BoundedExpression[T]:
121 if isinstance(lhs, (int, float)):
127 "__bool__ is unsupported for UpperBoundedExpression"
129 + _CHAINED_COMPARISON_MESSAGE
133 return f
"{self._expression!s} <= {self._upper_bound}"
136 return f
"{self._expression!r} <= {self._upper_bound}"
140 """An inequality of the form expression >= lower_bound.
143 * expression is a linear expression, and
144 * lower_bound is a float
147 __slots__ =
"_expression",
"_lower_bound"
149 def __init__(self, expression: T, lower_bound: float) ->
None:
150 """Operator overloading can be used instead: e.g. `x + y >= 2.0`."""
166 def __le__(self, rhs: float) -> BoundedExpression[T]:
167 if isinstance(rhs, (int, float)):
173 "__bool__ is unsupported for LowerBoundedExpression"
175 + _CHAINED_COMPARISON_MESSAGE
179 return f
"{self._expression!s} >= {self._lower_bound}"
182 return f
"{self._expression!r} >= {self._lower_bound}"