14"""Define Variables and Linear Expressions."""
45LinearTypes = Union[int, float,
"LinearBase"]
46QuadraticTypes = Union[int, float,
"LinearBase",
"QuadraticBase"]
47LinearTypesExceptVariable = Union[
48 float, int,
"LinearTerm",
"LinearExpression",
"LinearSum",
"LinearProduct"
52_EXPRESSION_COMP_EXPRESSION_MESSAGE = (
53 "This error can occur when adding "
54 "inequalities of the form `(a <= b) <= "
55 "c` where (a, b, c) includes two or more"
56 " non-constant linear expressions"
64 extra_message: Optional[str] =
None,
66 """Raises TypeError on unsupported operators."""
68 f
"unsupported operand type(s) for {operator}: {lhs.__name__!r} and"
71 if extra_message
is not None:
72 message +=
"\n" + extra_message
73 raise TypeError(message)
77 raise TypeError(
"!= constraints are not supported")
86 """The result of the equality comparison between two Variable.
88 We use an object here to delay the evaluation of equality so that we can use
89 the operator== in two use-cases:
91 1. when the user want to test that two Variable values references the same
92 variable. This is supported by having this object support implicit
95 2. when the user want to use the equality to create a constraint of equality
96 between two variables.
99 __slots__ =
"_first_variable",
"_second_variable"
103 first_variable:
"Variable",
104 second_variable:
"Variable",
124 return f
"{self.first_variable!s} == {self._second_variable!s}"
127 return f
"{self.first_variable!r} == {self._second_variable!r}"
130BoundedLinearTypesList = (
131 LowerBoundedLinearExpression,
132 UpperBoundedLinearExpression,
133 BoundedLinearExpression,
136BoundedLinearTypes = Union[BoundedLinearTypesList]
146BoundedQuadraticTypesList = (
147 LowerBoundedQuadraticExpression,
148 UpperBoundedQuadraticExpression,
149 BoundedQuadraticExpression,
151BoundedQuadraticTypes = Union[BoundedQuadraticTypesList]
156 """An id-ordered pair of variables used as a key for quadratic terms."""
158 __slots__ =
"_first_var",
"_second_var"
161 """Variables a and b will be ordered internally by their ids."""
175 def __eq__(self, other:
"QuadraticTermKey") -> bool:
185 return f
"{self._first_var!s} * {self._second_var!s}"
188 return f
"QuadraticTermKey({self._first_var!r}, {self._second_var!r})"
191@dataclasses.dataclass
193 """Auxiliary data class for LinearBase._flatten_once_and_add_to()."""
195 terms: DefaultDict[
"Variable", float] = dataclasses.field(
196 default_factory=
lambda: collections.defaultdict(float)
201@dataclasses.dataclass
203 """Auxiliary data class for QuadraticBase._quadratic_flatten_once_and_add_to()."""
205 quadratic_terms: DefaultDict[
"QuadraticTermKey", float] = dataclasses.field(
206 default_factory=
lambda: collections.defaultdict(float)
211 """Auxiliary to-process stack interface for LinearBase._flatten_once_and_add_to() and QuadraticBase._quadratic_flatten_once_and_add_to()."""
215 def append(self, term:
"LinearBase", scale: float) ->
None:
216 """Add a linear object and scale to the to-process stack."""
219_T = TypeVar(
"_T",
"LinearBase", Union[
"LinearBase",
"QuadraticBase"])
223 """Auxiliary data class for LinearBase._flatten_once_and_add_to()."""
225 __slots__ = (
"_queue",)
227 def __init__(self, term: _T, scale: float) ->
None:
228 self.
_queue: Deque[Tuple[_T, float]] = collections.deque([(term, scale)])
230 def append(self, term: _T, scale: float) ->
None:
233 def pop(self) -> Tuple[_T, float]:
234 return self.
_queue.popleft()
240_LinearToProcessElements = _ToProcessElementsImplementation[
"LinearBase"]
241_QuadraticToProcessElements = _ToProcessElementsImplementation[
242 Union[
"LinearBase",
"QuadraticBase"]
247 """Interface for types that can build linear expressions with +, -, * and /.
249 Classes derived from LinearBase (plus float and int scalars) are used to
250 build expression trees describing a linear expression. Operations nodes of the
251 expression tree include:
253 * LinearSum: describes a deferred sum of LinearTypes objects.
254 * LinearProduct: describes a deferred product of a scalar and a
257 Leaf nodes of the expression tree include:
259 * float and int scalars.
260 * Variable: a single variable.
261 * LinearTerm: the product of a scalar and a Variable object.
262 * LinearExpression: the sum of a scalar and LinearTerm objects.
264 LinearBase objects/expression-trees can be used directly to create
265 constraints or objective functions. However, to facilitate their inspection,
266 any LinearTypes object can be flattened to a LinearExpression
269 as_flat_linear_expression(value: LinearTypes) -> LinearExpression:
271 In addition, all LinearBase objects are immutable.
275 Using an expression tree representation instead of an eager construction of
276 LinearExpression objects reduces known inefficiencies associated with the
277 use of operator overloading to construct linear expressions. In particular, we
278 expect the runtime of as_flat_linear_expression() to be linear in the size of
279 the expression tree. Additional performance can gained by using LinearSum(c)
280 instead of sum(c) for a container c, as the latter creates len(c) LinearSum
295 processed_elements: _ProcessedElements,
296 target_stack: _ToProcessElements,
298 """Flatten one level of tree if needed and add to targets.
300 Classes derived from LinearBase only need to implement this function
301 to enable transformation to LinearExpression through
302 as_flat_linear_expression().
305 scale: multiply elements by this number when processing or adding to
307 processed_elements: where to add LinearTerms and scalars that can be
308 processed immediately.
309 target_stack: where to add LinearBase elements that are not scalars or
310 LinearTerms (i.e. elements that need further flattening).
311 Implementations should append() to this stack to avoid being recursive.
315 self, rhs: LinearTypes
317 BoundedLinearExpression
321 if isinstance(rhs, QuadraticBase):
322 return NotImplemented
323 if isinstance(rhs, (int, float)):
325 if not isinstance(rhs, LinearBase):
330 self, rhs: LinearTypes
337 def __le__(self, rhs: float) ->
"UpperBoundedLinearExpression": ...
340 def __le__(self, rhs:
"LinearBase") ->
"BoundedLinearExpression": ...
343 def __le__(self, rhs:
"BoundedLinearExpression") -> NoReturn: ...
348 if isinstance(rhs, QuadraticBase):
349 return NotImplemented
350 if isinstance(rhs, (int, float)):
352 if isinstance(rhs, LinearBase):
356 "<=", type(self), type(rhs), _EXPRESSION_COMP_EXPRESSION_MESSAGE
361 def __ge__(self, lhs: float) ->
"LowerBoundedLinearExpression": ...
364 def __ge__(self, lhs:
"LinearBase") ->
"BoundedLinearExpression": ...
367 def __ge__(self, lhs:
"BoundedLinearExpression") -> NoReturn: ...
372 if isinstance(lhs, QuadraticBase):
373 return NotImplemented
374 if isinstance(lhs, (int, float)):
376 if isinstance(lhs, LinearBase):
380 ">=", type(self), type(lhs), _EXPRESSION_COMP_EXPRESSION_MESSAGE
384 def __add__(self, expr: LinearTypes) ->
"LinearSum":
385 if not isinstance(expr, (int, float, LinearBase)):
386 return NotImplemented
389 def __radd__(self, expr: LinearTypes) ->
"LinearSum":
390 if not isinstance(expr, (int, float, LinearBase)):
391 return NotImplemented
394 def __sub__(self, expr: LinearTypes) ->
"LinearSum":
395 if not isinstance(expr, (int, float, LinearBase)):
396 return NotImplemented
399 def __rsub__(self, expr: LinearTypes) ->
"LinearSum":
400 if not isinstance(expr, (int, float, LinearBase, QuadraticBase)):
401 return NotImplemented
405 def __mul__(self, other: float) ->
"LinearProduct": ...
408 def __mul__(self, other:
"LinearBase") ->
"LinearLinearProduct": ...
411 if not isinstance(other, (int, float, LinearBase)):
412 return NotImplemented
413 if isinstance(other, LinearBase):
417 def __rmul__(self, constant: float) ->
"LinearProduct":
418 if not isinstance(constant, (int, float)):
419 return NotImplemented
424 if not isinstance(constant, (int, float)):
425 return NotImplemented
433 """Interface for types that can build quadratic expressions with +, -, * and /.
435 Classes derived from QuadraticBase and LinearBase (plus float and int scalars)
436 are used to build expression trees describing a quadratic expression.
437 Operations nodes of the expression tree include:
439 * QuadraticSum: describes a deferred sum of QuadraticTypes objects.
440 * QuadraticProduct: describes a deferred product of a scalar and a
441 QuadraticTypes object.
442 * LinearLinearProduct: describes a deferred product of two LinearTypes
445 Leaf nodes of the expression tree include:
447 * float and int scalars.
448 * Variable: a single variable.
449 * LinearTerm: the product of a scalar and a Variable object.
450 * LinearExpression: the sum of a scalar and LinearTerm objects.
451 * QuadraticTerm: the product of a scalar and two Variable objects.
452 * QuadraticExpression: the sum of a scalar, LinearTerm objects and
453 QuadraticTerm objects.
455 QuadraticBase objects/expression-trees can be used directly to create
456 objective functions. However, to facilitate their inspection, any
457 QuadraticTypes object can be flattened to a QuadraticExpression
460 as_flat_quadratic_expression(value: QuadraticTypes) -> QuadraticExpression:
462 In addition, all QuadraticBase objects are immutable.
466 Using an expression tree representation instead of an eager construction of
467 QuadraticExpression objects reduces known inefficiencies associated with the
468 use of operator overloading to construct quadratic expressions. In particular,
469 we expect the runtime of as_flat_quadratic_expression() to be linear in the
470 size of the expression tree. Additional performance can gained by using
471 QuadraticSum(c) instead of sum(c) for a container c, as the latter creates
472 len(c) QuadraticSum objects.
486 processed_elements: _QuadraticProcessedElements,
487 target_stack: _QuadraticToProcessElements,
489 """Flatten one level of tree if needed and add to targets.
491 Classes derived from QuadraticBase only need to implement this function
492 to enable transformation to QuadraticExpression through
493 as_flat_quadratic_expression().
496 scale: multiply elements by this number when processing or adding to
498 processed_elements: where to add linear terms, quadratic terms and scalars
499 that can be processed immediately.
500 target_stack: where to add LinearBase and QuadraticBase elements that are
501 not scalars or linear terms or quadratic terms (i.e. elements that need
502 further flattening). Implementations should append() to this stack to
503 avoid being recursive.
507 self, rhs: QuadraticTypes
509 BoundedQuadraticExpression
511 if isinstance(rhs, (int, float)):
513 if not isinstance(rhs, (LinearBase, QuadraticBase)):
518 self, rhs: QuadraticTypes
525 def __le__(self, rhs: float) -> UpperBoundedQuadraticExpression: ...
529 self, rhs: Union[LinearBase,
"QuadraticBase"]
530 ) -> BoundedQuadraticExpression: ...
533 def __le__(self, rhs: BoundedQuadraticExpression) -> NoReturn: ...
536 if isinstance(rhs, (int, float)):
538 if isinstance(rhs, (LinearBase, QuadraticBase)):
542 "<=", type(self), type(rhs), _EXPRESSION_COMP_EXPRESSION_MESSAGE
547 def __ge__(self, lhs: float) -> LowerBoundedQuadraticExpression: ...
551 self, lhs: Union[LinearBase,
"QuadraticBase"]
552 ) -> BoundedQuadraticExpression: ...
555 def __ge__(self, lhs: BoundedQuadraticExpression) -> NoReturn: ...
558 if isinstance(lhs, (int, float)):
560 if isinstance(lhs, (LinearBase, QuadraticBase)):
564 ">=", type(self), type(lhs), _EXPRESSION_COMP_EXPRESSION_MESSAGE
568 def __add__(self, expr: QuadraticTypes) ->
"QuadraticSum":
569 if not isinstance(expr, (int, float, LinearBase, QuadraticBase)):
570 return NotImplemented
573 def __radd__(self, expr: QuadraticTypes) ->
"QuadraticSum":
574 if not isinstance(expr, (int, float, LinearBase, QuadraticBase)):
575 return NotImplemented
578 def __sub__(self, expr: QuadraticTypes) ->
"QuadraticSum":
579 if not isinstance(expr, (int, float, LinearBase, QuadraticBase)):
580 return NotImplemented
583 def __rsub__(self, expr: QuadraticTypes) ->
"QuadraticSum":
584 if not isinstance(expr, (int, float, LinearBase, QuadraticBase)):
585 return NotImplemented
588 def __mul__(self, other: float) ->
"QuadraticProduct":
589 if not isinstance(other, (int, float)):
590 return NotImplemented
593 def __rmul__(self, other: float) ->
"QuadraticProduct":
594 if not isinstance(other, (int, float)):
595 return NotImplemented
600 if not isinstance(constant, (int, float)):
601 return NotImplemented
609 """A decision variable for an optimization model.
611 A decision variable takes a value from a domain, either the real numbers or
612 the integers, and restricted to be in some interval [lb, ub] (where lb and ub
613 can be infinite). The case of lb == ub is allowed, this means the variable
614 must take a single value. The case of lb > ub is also allowed, this implies
615 that the problem is infeasible.
617 A Variable is configured as follows:
618 * lower_bound: a float property, lb above. Should not be NaN nor +inf.
619 * upper_bound: a float property, ub above. Should not be NaN nor -inf.
620 * integer: a bool property, if the domain is integer or continuous.
622 The name is optional, read only, and used only for debugging. Non-empty names
625 Every Variable is associated with a Model (defined below). Note that data
626 describing the variable (e.g. lower_bound) is owned by Model.storage, this
627 class is simply a reference to that data. Do not create a Variable directly,
628 use Model.add_variable() instead.
631 __slots__ =
"_elemental",
"_id"
633 def __init__(self, elem: elemental.Elemental, vid: int) ->
None:
634 """Internal only, prefer Model functions (add_variable() and get_variable())."""
635 if not isinstance(vid, int):
636 raise TypeError(f
"vid type should be int, was:{type(vid)}")
643 enums.DoubleAttr1.VARIABLE_LOWER_BOUND, (self.
_id,)
649 enums.DoubleAttr1.VARIABLE_LOWER_BOUND,
657 enums.DoubleAttr1.VARIABLE_UPPER_BOUND, (self.
_id,)
663 enums.DoubleAttr1.VARIABLE_UPPER_BOUND,
670 return self.
_elemental.get_attr(enums.BoolAttr1.VARIABLE_INTEGER, (self.
_id,))
674 self.
_elemental.set_attr(enums.BoolAttr1.VARIABLE_INTEGER, (self.
_id,), value)
678 return self.
_elemental.get_element_name(enums.ElementType.VARIABLE, self.
_id)
685 def elemental(self) -> elemental.Elemental:
686 """Internal use only."""
690 """Returns the name, or a string containing the id if the name is empty."""
691 return self.
name if self.
name else f
"variable_{self.id}"
694 return f
"<Variable id: {self.id}, name: {self.name!r}>"
697 def __eq__(self, rhs:
"Variable") ->
"VarEqVar": ...
700 def __eq__(self, rhs: LinearTypesExceptVariable) ->
"BoundedLinearExpression": ...
703 if isinstance(rhs, Variable):
705 return super().
__eq__(rhs)
708 def __ne__(self, rhs:
"Variable") -> bool: ...
711 def __ne__(self, rhs: LinearTypesExceptVariable) -> NoReturn: ...
714 if isinstance(rhs, Variable):
715 return not self == rhs
719 return hash(self.
_id)
722 def __mul__(self, other: float) ->
"LinearTerm": ...
725 def __mul__(self, other: Union[
"Variable",
"LinearTerm"]) ->
"QuadraticTerm": ...
728 def __mul__(self, other:
"LinearBase") ->
"LinearLinearProduct": ...
731 if not isinstance(other, (int, float, LinearBase)):
732 return NotImplemented
733 if isinstance(other, Variable):
735 if isinstance(other, LinearTerm):
739 if isinstance(other, LinearBase):
743 def __rmul__(self, constant: float) ->
"LinearTerm":
744 if not isinstance(constant, (int, float)):
745 return NotImplemented
750 if not isinstance(constant, (int, float)):
751 return NotImplemented
760 processed_elements: _ProcessedElements,
761 target_stack: _ToProcessElements,
763 processed_elements.terms[self] += scale
767 """The product of a scalar and a variable.
769 This class is immutable.
772 __slots__ =
"_variable",
"_coefficient"
774 def __init__(self, variable: Variable, coefficient: float) ->
None:
789 processed_elements: _ProcessedElements,
790 target_stack: _ToProcessElements,
795 def __mul__(self, other: float) ->
"LinearTerm": ...
798 def __mul__(self, other: Union[
"Variable",
"LinearTerm"]) ->
"QuadraticTerm": ...
801 def __mul__(self, other:
"LinearBase") ->
"LinearLinearProduct": ...
804 if not isinstance(other, (int, float, LinearBase)):
805 return NotImplemented
806 if isinstance(other, Variable):
810 if isinstance(other, LinearTerm):
815 if isinstance(other, LinearBase):
819 def __rmul__(self, constant: float) ->
"LinearTerm":
820 if not isinstance(constant, (int, float)):
821 return NotImplemented
825 if not isinstance(constant, (int, float)):
826 return NotImplemented
833 return f
"{self._coefficient} * {self._variable}"
836 return f
"LinearTerm({self._variable!r}, {self._coefficient!r})"
840 """The product of a scalar and two variables.
842 This class is immutable.
845 __slots__ =
"_key",
"_coefficient"
847 def __init__(self, key: QuadraticTermKey, coefficient: float) ->
None:
848 self.
_key: QuadraticTermKey = key
852 def key(self) -> QuadraticTermKey:
862 processed_elements: _QuadraticProcessedElements,
863 target_stack: _ToProcessElements,
867 def __mul__(self, constant: float) ->
"QuadraticTerm":
868 if not isinstance(constant, (int, float)):
869 return NotImplemented
872 def __rmul__(self, constant: float) ->
"QuadraticTerm":
873 if not isinstance(constant, (int, float)):
874 return NotImplemented
878 if not isinstance(constant, (int, float)):
879 return NotImplemented
886 return f
"{self._coefficient} * {self._key!s}"
889 return f
"QuadraticTerm({self._key!r}, {self._coefficient})"
893 """For variables x, an expression: b + sum_{i in I} a_i * x_i.
895 This class is immutable.
898 __slots__ =
"__weakref__",
"_terms",
"_offset"
901 def __init__(self, /, other: LinearTypes = 0) ->
None:
903 if isinstance(other, (int, float)):
905 self.
_terms: Mapping[Variable, float] = immutabledict.immutabledict()
911 linear, coef = to_process.pop()
912 linear._flatten_once_and_add_to(coef, processed_elements, to_process)
914 self.
_terms: Mapping[Variable, float] = immutabledict.immutabledict(
915 processed_elements.terms
917 self.
_offset = processed_elements.offset
920 def terms(self) -> Mapping[Variable, float]:
927 def evaluate(self, variable_values: Mapping[Variable, float]) -> float:
928 """Returns the value of this expression for given variable values.
930 E.g. if this is 3 * x + 4 and variable_values = {x: 2.0}, then
931 evaluate(variable_values) equals 10.0.
933 See also mathopt.evaluate_expression(), which works on any type in
937 variable_values: Must contain a value for every variable in expression.
940 The value of this expression when replacing variables by their value.
943 for var, coef
in sorted(
944 self.
_terms.items(), key=
lambda var_coef_pair: var_coef_pair[0].id
946 result += coef * variable_values[var]
952 processed_elements: _ProcessedElements,
953 target_stack: _ToProcessElements,
955 for var, val
in self.
_terms.items():
956 processed_elements.terms[var] += val * scale
957 processed_elements.offset += scale * self.
offset
962 """Returns the name, or a string containing the id if the name is empty."""
964 sorted_keys = sorted(self.
_terms.keys(), key=str)
965 for var
in sorted_keys:
969 coefficient = self.
_terms[var]
970 if coefficient == 0.0:
976 result += str(abs(coefficient)) +
" * " + str(var)
980 result = f
"LinearExpression({self.offset}, " +
"{"
982 f
"{var!r}: {coefficient}" for var, coefficient
in self.
_terms.items()
989 """For variables x, an expression: b + sum_{i in I} a_i * x_i + sum_{i,j in I, i<=j} a_i,j * x_i * x_j.
991 This class is immutable.
994 __slots__ =
"__weakref__",
"_linear_terms",
"_quadratic_terms",
"_offset"
999 if isinstance(other, (int, float)):
1003 immutabledict.immutabledict()
1012 linear_or_quadratic, coef = to_process.pop()
1013 if isinstance(linear_or_quadratic, LinearBase):
1014 linear_or_quadratic._flatten_once_and_add_to(
1015 coef, processed_elements, to_process
1018 linear_or_quadratic._quadratic_flatten_once_and_add_to(
1019 coef, processed_elements, to_process
1022 self.
_linear_terms: Mapping[Variable, float] = immutabledict.immutabledict(
1023 processed_elements.terms
1026 immutabledict.immutabledict(processed_elements.quadratic_terms)
1028 self.
_offset = processed_elements.offset
1042 def evaluate(self, variable_values: Mapping[Variable, float]) -> float:
1043 """Returns the value of this expression for given variable values.
1045 E.g. if this is 3 * x * x + 4 and variable_values = {x: 2.0}, then
1046 evaluate(variable_values) equals 16.0.
1048 See also mathopt.evaluate_expression(), which works on any type in
1052 variable_values: Must contain a value for every variable in expression.
1055 The value of this expression when replacing variables by their value.
1058 for var, coef
in sorted(
1060 key=
lambda var_coef_pair: var_coef_pair[0].id,
1062 result += coef * variable_values[var]
1063 for key, coef
in sorted(
1065 key=
lambda quad_coef_pair: (
1066 quad_coef_pair[0].first_var.id,
1067 quad_coef_pair[0].second_var.id,
1071 coef * variable_values[key.first_var] * variable_values[key.second_var]
1078 processed_elements: _QuadraticProcessedElements,
1079 target_stack: _QuadraticToProcessElements,
1082 processed_elements.terms[var] += val * scale
1084 processed_elements.quadratic_terms[key] += val * scale
1085 processed_elements.offset += scale * self.
offset
1090 result = str(self.
offset)
1091 sorted_linear_keys = sorted(self.
_linear_terms.keys(), key=str)
1092 for var
in sorted_linear_keys:
1097 if coefficient == 0.0:
1103 result += str(abs(coefficient)) +
" * " + str(var)
1105 for key
in sorted_quadratic_keys:
1110 if coefficient == 0.0:
1116 result += str(abs(coefficient)) +
" * " + str(key)
1120 result = f
"QuadraticExpression({self.offset}, " +
"{"
1121 result +=
", ".join(
1122 f
"{var!r}: {coefficient}" for var, coefficient
in self.
_linear_terms.items()
1125 result +=
", ".join(
1126 f
"{key!r}: {coefficient}"
1136 """A deferred sum of LinearBase objects.
1138 LinearSum objects are automatically created when two linear objects are added
1139 and, as noted in the documentation for Linear, can reduce the inefficiencies.
1140 In particular, they are created when calling sum(iterable) when iterable is
1141 an Iterable[LinearTypes]. However, using LinearSum(iterable) instead
1142 can result in additional performance improvements:
1144 * sum(iterable): creates a nested set of LinearSum objects (e.g.
1145 `sum([a, b, c])` is `LinearSum(0, LinearSum(a, LinearSum(b, c)))`).
1146 * LinearSum(iterable): creates a single LinearSum that saves a tuple with
1147 all the LinearTypes objects in iterable (e.g.
1148 `LinearSum([a, b, c])` does not create additional objects).
1150 This class is immutable.
1153 __slots__ =
"__weakref__",
"_elements"
1157 def __init__(self, iterable: Iterable[LinearTypes]) ->
None:
1158 """Creates a LinearSum object. A copy of iterable is saved as a tuple."""
1162 if not isinstance(item, (LinearBase, int, float)):
1164 "unsupported type in iterable argument for "
1165 f
"LinearSum: {type(item).__name__!r}"
1175 processed_elements: _ProcessedElements,
1176 target_stack: _ToProcessElements,
1179 if isinstance(term, (int, float)):
1180 processed_elements.offset += scale * float(term)
1182 target_stack.append(term, scale)
1188 result =
"LinearSum(("
1189 result +=
", ".join(repr(linear)
for linear
in self.
_elements)
1197 """A deferred sum of QuadraticTypes objects.
1199 QuadraticSum objects are automatically created when a quadratic object is
1200 added to quadratic or linear objects and, as has performance optimizations
1201 similar to LinearSum.
1203 This class is immutable.
1206 __slots__ =
"__weakref__",
"_elements"
1210 def __init__(self, iterable: Iterable[QuadraticTypes]) ->
None:
1211 """Creates a QuadraticSum object. A copy of iterable is saved as a tuple."""
1215 if not isinstance(item, (LinearBase, QuadraticBase, int, float)):
1217 "unsupported type in iterable argument for "
1218 f
"QuadraticSum: {type(item).__name__!r}"
1228 processed_elements: _QuadraticProcessedElements,
1229 target_stack: _QuadraticToProcessElements,
1232 if isinstance(term, (int, float)):
1233 processed_elements.offset += scale * float(term)
1235 target_stack.append(term, scale)
1241 result =
"QuadraticSum(("
1242 result +=
", ".join(repr(element)
for element
in self.
_elements)
1248 """A deferred multiplication computation for linear expressions.
1250 This class is immutable.
1253 __slots__ =
"_scalar",
"_linear"
1255 def __init__(self, scalar: float, linear: LinearBase) ->
None:
1256 if not isinstance(scalar, (float, int)):
1258 "unsupported type for scalar argument in "
1259 f
"LinearProduct: {type(scalar).__name__!r}"
1261 if not isinstance(linear, LinearBase):
1263 "unsupported type for linear argument in "
1264 f
"LinearProduct: {type(linear).__name__!r}"
1280 processed_elements: _ProcessedElements,
1281 target_stack: _ToProcessElements,
1289 result = f
"LinearProduct({self._scalar!r}, "
1290 result += f
"{self._linear!r})"
1295 """A deferred multiplication computation for quadratic expressions.
1297 This class is immutable.
1300 __slots__ =
"_scalar",
"_quadratic"
1302 def __init__(self, scalar: float, quadratic: QuadraticBase) ->
None:
1303 if not isinstance(scalar, (float, int)):
1305 "unsupported type for scalar argument in "
1306 f
"QuadraticProduct: {type(scalar).__name__!r}"
1308 if not isinstance(quadratic, QuadraticBase):
1310 "unsupported type for linear argument in "
1311 f
"QuadraticProduct: {type(quadratic).__name__!r}"
1327 processed_elements: _QuadraticProcessedElements,
1328 target_stack: _QuadraticToProcessElements,
1336 return f
"QuadraticProduct({self._scalar}, {self._quadratic!r})"
1340 """A deferred multiplication of two linear expressions.
1342 This class is immutable.
1345 __slots__ =
"_first_linear",
"_second_linear"
1347 def __init__(self, first_linear: LinearBase, second_linear: LinearBase) ->
None:
1348 if not isinstance(first_linear, LinearBase):
1350 "unsupported type for first_linear argument in "
1351 f
"LinearLinearProduct: {type(first_linear).__name__!r}"
1353 if not isinstance(second_linear, LinearBase):
1355 "unsupported type for second_linear argument in "
1356 f
"LinearLinearProduct: {type(second_linear).__name__!r}"
1372 processed_elements: _QuadraticProcessedElements,
1373 target_stack: _QuadraticToProcessElements,
1379 processed_elements.offset += (
1380 first_expression.offset * second_expression.offset * scale
1382 for first_var, first_val
in first_expression.terms.items():
1383 processed_elements.terms[first_var] += (
1384 second_expression.offset * first_val * scale
1386 for second_var, second_val
in second_expression.terms.items():
1387 processed_elements.terms[second_var] += (
1388 first_expression.offset * second_val * scale
1391 for first_var, first_val
in first_expression.terms.items():
1392 for second_var, second_val
in second_expression.terms.items():
1393 processed_elements.quadratic_terms[
1395 ] += (first_val * second_val * scale)
1401 result =
"LinearLinearProduct("
1402 result += f
"{self._first_linear!r}, "
1403 result += f
"{self._second_linear!r})"
1408 """Converts floats, ints and Linear objects to a LinearExpression."""
1409 if isinstance(value, LinearExpression):
1415 """Converts floats, ints, LinearBase and QuadraticBase objects to a QuadraticExpression."""
1416 if isinstance(value, QuadraticExpression):