15"""Bounded (above and below), upper bounded, and lower bounded expressions."""
18from typing
import Any, Generic, NoReturn, Optional, Type, TypeVar
20_CHAINED_COMPARISON_MESSAGE = (
21 "If you were trying to create a two-sided or "
22 "ranged linear inequality of the form `lb <= "
23 "expr <= ub`, try `(lb <= expr) <= ub` instead"
31 extra_message: Optional[str] =
None,
33 """Raises TypeError on unsupported operators."""
35 f
"unsupported operand type(s) for {operator}: {lhs.__name__!r} and"
38 if extra_message
is not None:
39 message +=
"\n" + extra_message
40 raise TypeError(message)
47 """An inequality of the form lower_bound <= expression <= upper_bound.
50 * expression is a T, typically LinearBase or QuadraticBase.
51 * lower_bound is a float.
52 * upper_bound is a float.
54 Note: Because of limitations related to Python's handling of chained
55 comparisons, bounded expressions cannot be directly created usign
56 overloaded comparisons as in `lower_bound <= expression <= upper_bound`.
57 One solution is to wrap one of the inequalities in parenthesis as in
58 `(lower_bound <= expression) <= upper_bound`.
61 __slots__ =
"_expression",
"_lower_bound",
"_upper_bound"
63 def __init__(self, lower_bound: float, expression: T, upper_bound: float) ->
None:
82 "__bool__ is unsupported for BoundedExpression"
84 + _CHAINED_COMPARISON_MESSAGE
88 return f
"{self._lower_bound} <= {self._expression!s} <= {self._upper_bound}"
91 return f
"{self._lower_bound} <= {self._expression!r} <= {self._upper_bound}"
95 """An inequality of the form expression <= upper_bound.
98 * expression is a T, and
99 * upper_bound is a float
102 __slots__ =
"_expression",
"_upper_bound"
104 def __init__(self, expression: T, upper_bound: float) ->
None:
105 """Operator overloading can be used instead: e.g. `x + y <= 2.0`."""
121 def __ge__(self, lhs: float) -> BoundedExpression[T]:
122 if isinstance(lhs, (int, float)):
128 "__bool__ is unsupported for UpperBoundedExpression"
130 + _CHAINED_COMPARISON_MESSAGE
134 return f
"{self._expression!s} <= {self._upper_bound}"
137 return f
"{self._expression!r} <= {self._upper_bound}"
141 """An inequality of the form expression >= lower_bound.
144 * expression is a linear expression, and
145 * lower_bound is a float
148 __slots__ =
"_expression",
"_lower_bound"
150 def __init__(self, expression: T, lower_bound: float) ->
None:
151 """Operator overloading can be used instead: e.g. `x + y >= 2.0`."""
167 def __le__(self, rhs: float) -> BoundedExpression[T]:
168 if isinstance(rhs, (int, float)):
174 "__bool__ is unsupported for LowerBoundedExpression"
176 + _CHAINED_COMPARISON_MESSAGE
180 return f
"{self._expression!s} >= {self._lower_bound}"
183 return f
"{self._expression!r} >= {self._lower_bound}"