15"""Define Variables and Linear Expressions."""
46LinearTypes = Union[int, float,
"LinearBase"]
47QuadraticTypes = Union[int, float,
"LinearBase",
"QuadraticBase"]
48LinearTypesExceptVariable = Union[
49 float, int,
"LinearTerm",
"LinearExpression",
"LinearSum",
"LinearProduct"
53_EXPRESSION_COMP_EXPRESSION_MESSAGE = (
54 "This error can occur when adding "
55 "inequalities of the form `(a <= b) <= "
56 "c` where (a, b, c) includes two or more"
57 " non-constant linear expressions"
65 extra_message: Optional[str] =
None,
67 """Raises TypeError on unsupported operators."""
69 f
"unsupported operand type(s) for {operator}: {lhs.__name__!r} and"
72 if extra_message
is not None:
73 message +=
"\n" + extra_message
74 raise TypeError(message)
78 raise TypeError(
"!= constraints are not supported")
87 """The result of the equality comparison between two Variable.
89 We use an object here to delay the evaluation of equality so that we can use
90 the operator== in two use-cases:
92 1. when the user want to test that two Variable values references the same
93 variable. This is supported by having this object support implicit
96 2. when the user want to use the equality to create a constraint of equality
97 between two variables.
100 __slots__ =
"_first_variable",
"_second_variable"
104 first_variable:
"Variable",
105 second_variable:
"Variable",
125 return f
"{self.first_variable!s} == {self._second_variable!s}"
128 return f
"{self.first_variable!r} == {self._second_variable!r}"
131BoundedLinearTypesList = (
132 LowerBoundedLinearExpression,
133 UpperBoundedLinearExpression,
134 BoundedLinearExpression,
137BoundedLinearTypes = Union[BoundedLinearTypesList]
147BoundedQuadraticTypesList = (
148 LowerBoundedQuadraticExpression,
149 UpperBoundedQuadraticExpression,
150 BoundedQuadraticExpression,
152BoundedQuadraticTypes = Union[BoundedQuadraticTypesList]
157 """An id-ordered pair of variables used as a key for quadratic terms."""
159 __slots__ =
"_first_var",
"_second_var"
162 """Variables a and b will be ordered internally by their ids."""
176 def __eq__(self, other:
"QuadraticTermKey") -> bool:
186 return f
"{self._first_var!s} * {self._second_var!s}"
189 return f
"QuadraticTermKey({self._first_var!r}, {self._second_var!r})"
192@dataclasses.dataclass
194 """Auxiliary data class for LinearBase._flatten_once_and_add_to()."""
196 terms: DefaultDict[
"Variable", float] = dataclasses.field(
197 default_factory=
lambda: collections.defaultdict(float)
202@dataclasses.dataclass
204 """Auxiliary data class for QuadraticBase._quadratic_flatten_once_and_add_to()."""
206 quadratic_terms: DefaultDict[
"QuadraticTermKey", float] = dataclasses.field(
207 default_factory=
lambda: collections.defaultdict(float)
212 """Auxiliary to-process stack interface for LinearBase._flatten_once_and_add_to() and QuadraticBase._quadratic_flatten_once_and_add_to()."""
216 def append(self, term:
"LinearBase", scale: float) ->
None:
217 """Add a linear object and scale to the to-process stack."""
220_T = TypeVar(
"_T",
"LinearBase", Union[
"LinearBase",
"QuadraticBase"])
224 """Auxiliary data class for LinearBase._flatten_once_and_add_to()."""
226 __slots__ = (
"_queue",)
228 def __init__(self, term: _T, scale: float) ->
None:
229 self.
_queue: Deque[Tuple[_T, float]] = collections.deque([(term, scale)])
231 def append(self, term: _T, scale: float) ->
None:
234 def pop(self) -> Tuple[_T, float]:
235 return self.
_queue.popleft()
241_LinearToProcessElements = _ToProcessElementsImplementation[
"LinearBase"]
242_QuadraticToProcessElements = _ToProcessElementsImplementation[
243 Union[
"LinearBase",
"QuadraticBase"]
248 """Interface for types that can build linear expressions with +, -, * and /.
250 Classes derived from LinearBase (plus float and int scalars) are used to
251 build expression trees describing a linear expression. Operations nodes of the
252 expression tree include:
254 * LinearSum: describes a deferred sum of LinearTypes objects.
255 * LinearProduct: describes a deferred product of a scalar and a
258 Leaf nodes of the expression tree include:
260 * float and int scalars.
261 * Variable: a single variable.
262 * LinearTerm: the product of a scalar and a Variable object.
263 * LinearExpression: the sum of a scalar and LinearTerm objects.
265 LinearBase objects/expression-trees can be used directly to create
266 constraints or objective functions. However, to facilitate their inspection,
267 any LinearTypes object can be flattened to a LinearExpression
270 as_flat_linear_expression(value: LinearTypes) -> LinearExpression:
272 In addition, all LinearBase objects are immutable.
276 Using an expression tree representation instead of an eager construction of
277 LinearExpression objects reduces known inefficiencies associated with the
278 use of operator overloading to construct linear expressions. In particular, we
279 expect the runtime of as_flat_linear_expression() to be linear in the size of
280 the expression tree. Additional performance can gained by using LinearSum(c)
281 instead of sum(c) for a container c, as the latter creates len(c) LinearSum
296 processed_elements: _ProcessedElements,
297 target_stack: _ToProcessElements,
299 """Flatten one level of tree if needed and add to targets.
301 Classes derived from LinearBase only need to implement this function
302 to enable transformation to LinearExpression through
303 as_flat_linear_expression().
306 scale: multiply elements by this number when processing or adding to
308 processed_elements: where to add LinearTerms and scalars that can be
309 processed immediately.
310 target_stack: where to add LinearBase elements that are not scalars or
311 LinearTerms (i.e. elements that need further flattening).
312 Implementations should append() to this stack to avoid being recursive.
316 self, rhs: LinearTypes
317 ) -> BoundedLinearExpression:
320 if isinstance(rhs, QuadraticBase):
321 return NotImplemented
322 if isinstance(rhs, (int, float)):
324 if not isinstance(rhs, LinearBase):
328 def __ne__(self, rhs: LinearTypes) -> NoReturn:
332 def __le__(self, rhs: float) ->
"UpperBoundedLinearExpression": ...
335 def __le__(self, rhs:
"LinearBase") ->
"BoundedLinearExpression": ...
338 def __le__(self, rhs:
"BoundedLinearExpression") -> NoReturn: ...
343 if isinstance(rhs, QuadraticBase):
344 return NotImplemented
345 if isinstance(rhs, (int, float)):
347 if isinstance(rhs, LinearBase):
351 "<=", type(self), type(rhs), _EXPRESSION_COMP_EXPRESSION_MESSAGE
356 def __ge__(self, lhs: float) ->
"LowerBoundedLinearExpression": ...
359 def __ge__(self, lhs:
"LinearBase") ->
"BoundedLinearExpression": ...
362 def __ge__(self, lhs:
"BoundedLinearExpression") -> NoReturn: ...
367 if isinstance(lhs, QuadraticBase):
368 return NotImplemented
369 if isinstance(lhs, (int, float)):
371 if isinstance(lhs, LinearBase):
375 ">=", type(self), type(lhs), _EXPRESSION_COMP_EXPRESSION_MESSAGE
379 def __add__(self, expr: LinearTypes) ->
"LinearSum":
380 if not isinstance(expr, (int, float, LinearBase)):
381 return NotImplemented
384 def __radd__(self, expr: LinearTypes) ->
"LinearSum":
385 if not isinstance(expr, (int, float, LinearBase)):
386 return NotImplemented
389 def __sub__(self, expr: LinearTypes) ->
"LinearSum":
390 if not isinstance(expr, (int, float, LinearBase)):
391 return NotImplemented
394 def __rsub__(self, expr: LinearTypes) ->
"LinearSum":
395 if not isinstance(expr, (int, float, LinearBase, QuadraticBase)):
396 return NotImplemented
400 def __mul__(self, other: float) ->
"LinearProduct": ...
403 def __mul__(self, other:
"LinearBase") ->
"LinearLinearProduct": ...
406 if not isinstance(other, (int, float, LinearBase)):
407 return NotImplemented
408 if isinstance(other, LinearBase):
412 def __rmul__(self, constant: float) ->
"LinearProduct":
413 if not isinstance(constant, (int, float)):
414 return NotImplemented
419 if not isinstance(constant, (int, float)):
420 return NotImplemented
428 """Interface for types that can build quadratic expressions with +, -, * and /.
430 Classes derived from QuadraticBase and LinearBase (plus float and int scalars)
431 are used to build expression trees describing a quadratic expression.
432 Operations nodes of the expression tree include:
434 * QuadraticSum: describes a deferred sum of QuadraticTypes objects.
435 * QuadraticProduct: describes a deferred product of a scalar and a
436 QuadraticTypes object.
437 * LinearLinearProduct: describes a deferred product of two LinearTypes
440 Leaf nodes of the expression tree include:
442 * float and int scalars.
443 * Variable: a single variable.
444 * LinearTerm: the product of a scalar and a Variable object.
445 * LinearExpression: the sum of a scalar and LinearTerm objects.
446 * QuadraticTerm: the product of a scalar and two Variable objects.
447 * QuadraticExpression: the sum of a scalar, LinearTerm objects and
448 QuadraticTerm objects.
450 QuadraticBase objects/expression-trees can be used directly to create
451 objective functions. However, to facilitate their inspection, any
452 QuadraticTypes object can be flattened to a QuadraticExpression
455 as_flat_quadratic_expression(value: QuadraticTypes) -> QuadraticExpression:
457 In addition, all QuadraticBase objects are immutable.
461 Using an expression tree representation instead of an eager construction of
462 QuadraticExpression objects reduces known inefficiencies associated with the
463 use of operator overloading to construct quadratic expressions. In particular,
464 we expect the runtime of as_flat_quadratic_expression() to be linear in the
465 size of the expression tree. Additional performance can gained by using
466 QuadraticSum(c) instead of sum(c) for a container c, as the latter creates
467 len(c) QuadraticSum objects.
481 processed_elements: _QuadraticProcessedElements,
482 target_stack: _QuadraticToProcessElements,
484 """Flatten one level of tree if needed and add to targets.
486 Classes derived from QuadraticBase only need to implement this function
487 to enable transformation to QuadraticExpression through
488 as_flat_quadratic_expression().
491 scale: multiply elements by this number when processing or adding to
493 processed_elements: where to add linear terms, quadratic terms and scalars
494 that can be processed immediately.
495 target_stack: where to add LinearBase and QuadraticBase elements that are
496 not scalars or linear terms or quadratic terms (i.e. elements that need
497 further flattening). Implementations should append() to this stack to
498 avoid being recursive.
502 self, rhs: QuadraticTypes
503 ) -> BoundedQuadraticExpression:
504 if isinstance(rhs, (int, float)):
506 if not isinstance(rhs, (LinearBase, QuadraticBase)):
510 def __ne__(self, rhs: QuadraticTypes) -> NoReturn:
514 def __le__(self, rhs: float) -> UpperBoundedQuadraticExpression: ...
518 self, rhs: Union[LinearBase,
"QuadraticBase"]
519 ) -> BoundedQuadraticExpression: ...
522 def __le__(self, rhs: BoundedQuadraticExpression) -> NoReturn: ...
525 if isinstance(rhs, (int, float)):
527 if isinstance(rhs, (LinearBase, QuadraticBase)):
531 "<=", type(self), type(rhs), _EXPRESSION_COMP_EXPRESSION_MESSAGE
536 def __ge__(self, lhs: float) -> LowerBoundedQuadraticExpression: ...
540 self, lhs: Union[LinearBase,
"QuadraticBase"]
541 ) -> BoundedQuadraticExpression: ...
544 def __ge__(self, lhs: BoundedQuadraticExpression) -> NoReturn: ...
547 if isinstance(lhs, (int, float)):
549 if isinstance(lhs, (LinearBase, QuadraticBase)):
553 ">=", type(self), type(lhs), _EXPRESSION_COMP_EXPRESSION_MESSAGE
557 def __add__(self, expr: QuadraticTypes) ->
"QuadraticSum":
558 if not isinstance(expr, (int, float, LinearBase, QuadraticBase)):
559 return NotImplemented
562 def __radd__(self, expr: QuadraticTypes) ->
"QuadraticSum":
563 if not isinstance(expr, (int, float, LinearBase, QuadraticBase)):
564 return NotImplemented
567 def __sub__(self, expr: QuadraticTypes) ->
"QuadraticSum":
568 if not isinstance(expr, (int, float, LinearBase, QuadraticBase)):
569 return NotImplemented
572 def __rsub__(self, expr: QuadraticTypes) ->
"QuadraticSum":
573 if not isinstance(expr, (int, float, LinearBase, QuadraticBase)):
574 return NotImplemented
577 def __mul__(self, other: float) ->
"QuadraticProduct":
578 if not isinstance(other, (int, float)):
579 return NotImplemented
582 def __rmul__(self, other: float) ->
"QuadraticProduct":
583 if not isinstance(other, (int, float)):
584 return NotImplemented
589 if not isinstance(constant, (int, float)):
590 return NotImplemented
598 """A decision variable for an optimization model.
600 A decision variable takes a value from a domain, either the real numbers or
601 the integers, and restricted to be in some interval [lb, ub] (where lb and ub
602 can be infinite). The case of lb == ub is allowed, this means the variable
603 must take a single value. The case of lb > ub is also allowed, this implies
604 that the problem is infeasible.
606 A Variable is configured as follows:
607 * lower_bound: a float property, lb above. Should not be NaN nor +inf.
608 * upper_bound: a float property, ub above. Should not be NaN nor -inf.
609 * integer: a bool property, if the domain is integer or continuous.
611 The name is optional, read only, and used only for debugging. Non-empty names
614 Every Variable is associated with a Model (defined below). Note that data
615 describing the variable (e.g. lower_bound) is owned by Model.storage, this
616 class is simply a reference to that data. Do not create a Variable directly,
617 use Model.add_variable() instead.
620 __slots__ =
"_elemental",
"_id"
622 def __init__(self, elem: elemental.Elemental, vid: int) ->
None:
623 """Internal only, prefer Model functions (add_variable() and get_variable())."""
624 if not isinstance(vid, int):
625 raise TypeError(f
"vid type should be int, was:{type(vid)}")
632 enums.DoubleAttr1.VARIABLE_LOWER_BOUND, (self.
_id,)
638 enums.DoubleAttr1.VARIABLE_LOWER_BOUND,
646 enums.DoubleAttr1.VARIABLE_UPPER_BOUND, (self.
_id,)
652 enums.DoubleAttr1.VARIABLE_UPPER_BOUND,
659 return self.
_elemental.get_attr(enums.BoolAttr1.VARIABLE_INTEGER, (self.
_id,))
663 self.
_elemental.set_attr(enums.BoolAttr1.VARIABLE_INTEGER, (self.
_id,), value)
667 return self.
_elemental.get_element_name(enums.ElementType.VARIABLE, self.
_id)
674 def elemental(self) -> elemental.Elemental:
675 """Internal use only."""
679 """Returns the name, or a string containing the id if the name is empty."""
680 return self.
name if self.
name else f
"variable_{self.id}"
683 return f
"<Variable id: {self.id}, name: {self.name!r}>"
686 def __eq__(self, rhs:
"Variable") ->
"VarEqVar": ...
689 def __eq__(self, rhs: LinearTypesExceptVariable) ->
"BoundedLinearExpression": ...
692 if isinstance(rhs, Variable):
694 return super().
__eq__(rhs)
697 def __ne__(self, rhs:
"Variable") -> bool: ...
700 def __ne__(self, rhs: LinearTypesExceptVariable) -> NoReturn: ...
703 if isinstance(rhs, Variable):
704 return not self == rhs
708 return hash(self.
_id)
711 def __mul__(self, other: float) ->
"LinearTerm": ...
714 def __mul__(self, other: Union[
"Variable",
"LinearTerm"]) ->
"QuadraticTerm": ...
717 def __mul__(self, other:
"LinearBase") ->
"LinearLinearProduct": ...
720 if not isinstance(other, (int, float, LinearBase)):
721 return NotImplemented
722 if isinstance(other, Variable):
724 if isinstance(other, LinearTerm):
728 if isinstance(other, LinearBase):
732 def __rmul__(self, constant: float) ->
"LinearTerm":
733 if not isinstance(constant, (int, float)):
734 return NotImplemented
739 if not isinstance(constant, (int, float)):
740 return NotImplemented
749 processed_elements: _ProcessedElements,
750 target_stack: _ToProcessElements,
752 processed_elements.terms[self] += scale
756 """The product of a scalar and a variable.
758 This class is immutable.
761 __slots__ =
"_variable",
"_coefficient"
763 def __init__(self, variable: Variable, coefficient: float) ->
None:
778 processed_elements: _ProcessedElements,
779 target_stack: _ToProcessElements,
784 def __mul__(self, other: float) ->
"LinearTerm": ...
787 def __mul__(self, other: Union[
"Variable",
"LinearTerm"]) ->
"QuadraticTerm": ...
790 def __mul__(self, other:
"LinearBase") ->
"LinearLinearProduct": ...
793 if not isinstance(other, (int, float, LinearBase)):
794 return NotImplemented
795 if isinstance(other, Variable):
799 if isinstance(other, LinearTerm):
804 if isinstance(other, LinearBase):
808 def __rmul__(self, constant: float) ->
"LinearTerm":
809 if not isinstance(constant, (int, float)):
810 return NotImplemented
814 if not isinstance(constant, (int, float)):
815 return NotImplemented
822 return f
"{self._coefficient} * {self._variable}"
825 return f
"LinearTerm({self._variable!r}, {self._coefficient!r})"
829 """The product of a scalar and two variables.
831 This class is immutable.
834 __slots__ =
"_key",
"_coefficient"
836 def __init__(self, key: QuadraticTermKey, coefficient: float) ->
None:
837 self.
_key: QuadraticTermKey = key
841 def key(self) -> QuadraticTermKey:
851 processed_elements: _QuadraticProcessedElements,
852 target_stack: _ToProcessElements,
856 def __mul__(self, constant: float) ->
"QuadraticTerm":
857 if not isinstance(constant, (int, float)):
858 return NotImplemented
861 def __rmul__(self, constant: float) ->
"QuadraticTerm":
862 if not isinstance(constant, (int, float)):
863 return NotImplemented
867 if not isinstance(constant, (int, float)):
868 return NotImplemented
875 return f
"{self._coefficient} * {self._key!s}"
878 return f
"QuadraticTerm({self._key!r}, {self._coefficient})"
882 """For variables x, an expression: b + sum_{i in I} a_i * x_i.
884 This class is immutable.
887 __slots__ =
"__weakref__",
"_terms",
"_offset"
890 def __init__(self, /, other: LinearTypes = 0) ->
None:
892 if isinstance(other, (int, float)):
894 self.
_terms: Mapping[Variable, float] = immutabledict.immutabledict()
900 linear, coef = to_process.pop()
901 linear._flatten_once_and_add_to(coef, processed_elements, to_process)
903 self.
_terms: Mapping[Variable, float] = immutabledict.immutabledict(
904 processed_elements.terms
906 self.
_offset = processed_elements.offset
909 def terms(self) -> Mapping[Variable, float]:
916 def evaluate(self, variable_values: Mapping[Variable, float]) -> float:
917 """Returns the value of this expression for given variable values.
919 E.g. if this is 3 * x + 4 and variable_values = {x: 2.0}, then
920 evaluate(variable_values) equals 10.0.
922 See also mathopt.evaluate_expression(), which works on any type in
926 variable_values: Must contain a value for every variable in expression.
929 The value of this expression when replacing variables by their value.
932 for var, coef
in sorted(
933 self.
_terms.items(), key=
lambda var_coef_pair: var_coef_pair[0].id
935 result += coef * variable_values[var]
941 processed_elements: _ProcessedElements,
942 target_stack: _ToProcessElements,
944 for var, val
in self.
_terms.items():
945 processed_elements.terms[var] += val * scale
946 processed_elements.offset += scale * self.
offset
951 """Returns the name, or a string containing the id if the name is empty."""
953 sorted_keys = sorted(self.
_terms.keys(), key=str)
954 for var
in sorted_keys:
958 coefficient = self.
_terms[var]
959 if coefficient == 0.0:
965 result += str(abs(coefficient)) +
" * " + str(var)
969 result = f
"LinearExpression({self.offset}, " +
"{"
971 f
"{var!r}: {coefficient}" for var, coefficient
in self.
_terms.items()
978 """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.
980 This class is immutable.
983 __slots__ =
"__weakref__",
"_linear_terms",
"_quadratic_terms",
"_offset"
988 if isinstance(other, (int, float)):
990 self.
_linear_terms: Mapping[Variable, float] = immutabledict.immutabledict()
992 immutabledict.immutabledict()
1001 linear_or_quadratic, coef = to_process.pop()
1002 if isinstance(linear_or_quadratic, LinearBase):
1003 linear_or_quadratic._flatten_once_and_add_to(
1004 coef, processed_elements, to_process
1007 linear_or_quadratic._quadratic_flatten_once_and_add_to(
1008 coef, processed_elements, to_process
1011 self.
_linear_terms: Mapping[Variable, float] = immutabledict.immutabledict(
1012 processed_elements.terms
1015 immutabledict.immutabledict(processed_elements.quadratic_terms)
1017 self.
_offset = processed_elements.offset
1031 def evaluate(self, variable_values: Mapping[Variable, float]) -> float:
1032 """Returns the value of this expression for given variable values.
1034 E.g. if this is 3 * x * x + 4 and variable_values = {x: 2.0}, then
1035 evaluate(variable_values) equals 16.0.
1037 See also mathopt.evaluate_expression(), which works on any type in
1041 variable_values: Must contain a value for every variable in expression.
1044 The value of this expression when replacing variables by their value.
1047 for var, coef
in sorted(
1049 key=
lambda var_coef_pair: var_coef_pair[0].id,
1051 result += coef * variable_values[var]
1052 for key, coef
in sorted(
1054 key=
lambda quad_coef_pair: (
1055 quad_coef_pair[0].first_var.id,
1056 quad_coef_pair[0].second_var.id,
1060 coef * variable_values[key.first_var] * variable_values[key.second_var]
1067 processed_elements: _QuadraticProcessedElements,
1068 target_stack: _QuadraticToProcessElements,
1071 processed_elements.terms[var] += val * scale
1073 processed_elements.quadratic_terms[key] += val * scale
1074 processed_elements.offset += scale * self.
offset
1079 result = str(self.
offset)
1080 sorted_linear_keys = sorted(self.
_linear_terms.keys(), key=str)
1081 for var
in sorted_linear_keys:
1086 if coefficient == 0.0:
1092 result += str(abs(coefficient)) +
" * " + str(var)
1094 for key
in sorted_quadratic_keys:
1099 if coefficient == 0.0:
1105 result += str(abs(coefficient)) +
" * " + str(key)
1109 result = f
"QuadraticExpression({self.offset}, " +
"{"
1110 result +=
", ".join(
1111 f
"{var!r}: {coefficient}" for var, coefficient
in self.
_linear_terms.items()
1114 result +=
", ".join(
1115 f
"{key!r}: {coefficient}"
1125 """A deferred sum of LinearBase objects.
1127 LinearSum objects are automatically created when two linear objects are added
1128 and, as noted in the documentation for Linear, can reduce the inefficiencies.
1129 In particular, they are created when calling sum(iterable) when iterable is
1130 an Iterable[LinearTypes]. However, using LinearSum(iterable) instead
1131 can result in additional performance improvements:
1133 * sum(iterable): creates a nested set of LinearSum objects (e.g.
1134 `sum([a, b, c])` is `LinearSum(0, LinearSum(a, LinearSum(b, c)))`).
1135 * LinearSum(iterable): creates a single LinearSum that saves a tuple with
1136 all the LinearTypes objects in iterable (e.g.
1137 `LinearSum([a, b, c])` does not create additional objects).
1139 This class is immutable.
1142 __slots__ =
"__weakref__",
"_elements"
1146 def __init__(self, iterable: Iterable[LinearTypes]) ->
None:
1147 """Creates a LinearSum object. A copy of iterable is saved as a tuple."""
1151 if not isinstance(item, (LinearBase, int, float)):
1153 "unsupported type in iterable argument for "
1154 f
"LinearSum: {type(item).__name__!r}"
1164 processed_elements: _ProcessedElements,
1165 target_stack: _ToProcessElements,
1168 if isinstance(term, (int, float)):
1169 processed_elements.offset += scale * float(term)
1171 target_stack.append(term, scale)
1177 result =
"LinearSum(("
1178 result +=
", ".join(repr(linear)
for linear
in self.
_elements)
1186 """A deferred sum of QuadraticTypes objects.
1188 QuadraticSum objects are automatically created when a quadratic object is
1189 added to quadratic or linear objects and, as has performance optimizations
1190 similar to LinearSum.
1192 This class is immutable.
1195 __slots__ =
"__weakref__",
"_elements"
1199 def __init__(self, iterable: Iterable[QuadraticTypes]) ->
None:
1200 """Creates a QuadraticSum object. A copy of iterable is saved as a tuple."""
1204 if not isinstance(item, (LinearBase, QuadraticBase, int, float)):
1206 "unsupported type in iterable argument for "
1207 f
"QuadraticSum: {type(item).__name__!r}"
1217 processed_elements: _QuadraticProcessedElements,
1218 target_stack: _QuadraticToProcessElements,
1221 if isinstance(term, (int, float)):
1222 processed_elements.offset += scale * float(term)
1224 target_stack.append(term, scale)
1230 result =
"QuadraticSum(("
1231 result +=
", ".join(repr(element)
for element
in self.
_elements)
1237 """A deferred multiplication computation for linear expressions.
1239 This class is immutable.
1242 __slots__ =
"_scalar",
"_linear"
1244 def __init__(self, scalar: float, linear: LinearBase) ->
None:
1245 if not isinstance(scalar, (float, int)):
1247 "unsupported type for scalar argument in "
1248 f
"LinearProduct: {type(scalar).__name__!r}"
1250 if not isinstance(linear, LinearBase):
1252 "unsupported type for linear argument in "
1253 f
"LinearProduct: {type(linear).__name__!r}"
1269 processed_elements: _ProcessedElements,
1270 target_stack: _ToProcessElements,
1278 result = f
"LinearProduct({self._scalar!r}, "
1279 result += f
"{self._linear!r})"
1284 """A deferred multiplication computation for quadratic expressions.
1286 This class is immutable.
1289 __slots__ =
"_scalar",
"_quadratic"
1291 def __init__(self, scalar: float, quadratic: QuadraticBase) ->
None:
1292 if not isinstance(scalar, (float, int)):
1294 "unsupported type for scalar argument in "
1295 f
"QuadraticProduct: {type(scalar).__name__!r}"
1297 if not isinstance(quadratic, QuadraticBase):
1299 "unsupported type for linear argument in "
1300 f
"QuadraticProduct: {type(quadratic).__name__!r}"
1316 processed_elements: _QuadraticProcessedElements,
1317 target_stack: _QuadraticToProcessElements,
1325 return f
"QuadraticProduct({self._scalar}, {self._quadratic!r})"
1329 """A deferred multiplication of two linear expressions.
1331 This class is immutable.
1334 __slots__ =
"_first_linear",
"_second_linear"
1336 def __init__(self, first_linear: LinearBase, second_linear: LinearBase) ->
None:
1337 if not isinstance(first_linear, LinearBase):
1339 "unsupported type for first_linear argument in "
1340 f
"LinearLinearProduct: {type(first_linear).__name__!r}"
1342 if not isinstance(second_linear, LinearBase):
1344 "unsupported type for second_linear argument in "
1345 f
"LinearLinearProduct: {type(second_linear).__name__!r}"
1361 processed_elements: _QuadraticProcessedElements,
1362 target_stack: _QuadraticToProcessElements,
1368 processed_elements.offset += (
1369 first_expression.offset * second_expression.offset * scale
1371 for first_var, first_val
in first_expression.terms.items():
1372 processed_elements.terms[first_var] += (
1373 second_expression.offset * first_val * scale
1375 for second_var, second_val
in second_expression.terms.items():
1376 processed_elements.terms[second_var] += (
1377 first_expression.offset * second_val * scale
1380 for first_var, first_val
in first_expression.terms.items():
1381 for second_var, second_val
in second_expression.terms.items():
1382 processed_elements.quadratic_terms[
1384 ] += (first_val * second_val * scale)
1390 result =
"LinearLinearProduct("
1391 result += f
"{self._first_linear!r}, "
1392 result += f
"{self._second_linear!r})"
1397 """Converts floats, ints and Linear objects to a LinearExpression."""
1398 if isinstance(value, LinearExpression):
1404 """Converts floats, ints, LinearBase and QuadraticBase objects to a QuadraticExpression."""
1405 if isinstance(value, QuadraticExpression):