14"""A solver independent library for modeling optimization problems.
16Example use to model the optimization problem:
22 model = mathopt.Model(name='my_model')
23 x = model.add_binary_variable(name='x')
24 y = model.add_variable(lb=0.0, ub=2.5, name='y')
25 # We can directly use linear combinations of variables ...
26 model.add_linear_constraint(x + y <= 1.5, name='c')
27 # ... or build them incrementally.
28 objective_expression = 0
29 objective_expression += 2 * x
30 objective_expression += y
31 model.maximize(objective_expression)
33 # May raise a RuntimeError on invalid input or internal solver errors.
34 result = mathopt.solve(model, mathopt.SolverType.GSCIP)
36 if result.termination.reason not in (mathopt.TerminationReason.OPTIMAL,
37 mathopt.TerminationReason.FEASIBLE):
38 raise RuntimeError(f'model failed to solve: {result.termination}')
40 print(f'Objective value: {result.objective_value()}')
41 print(f'Value for variable x: {result.variable_values()[x]}')
45from typing
import Iterator, Optional, Tuple, Union
62 """Tracks updates to an optimization model from a ModelStorage.
64 Do not instantiate directly, instead create through
65 ModelStorage.add_update_tracker().
67 Querying an UpdateTracker after calling Model.remove_update_tracker will
68 result in a model_storage.UsedUpdateTrackerAfterRemovalError.
72 x = mod.add_variable(0.0, 1.0, True, 'x')
73 y = mod.add_variable(0.0, 1.0, True, 'y')
74 tracker = mod.add_update_tracker()
75 mod.set_variable_ub(x, 3.0)
76 tracker.export_update()
77 => "variable_updates: {upper_bounds: {ids: [0], values[3.0] }"
78 mod.set_variable_ub(y, 2.0)
79 tracker.export_update()
80 => "variable_updates: {upper_bounds: {ids: [0, 1], values[3.0, 2.0] }"
81 tracker.advance_checkpoint()
82 tracker.export_update()
84 mod.set_variable_ub(y, 4.0)
85 tracker.export_update()
86 => "variable_updates: {upper_bounds: {ids: [1], values[4.0] }"
87 tracker.advance_checkpoint()
88 mod.remove_update_tracker(tracker)
96 """Do not invoke directly, use Model.add_update_tracker() instead."""
101 self, *, remove_names: bool =
False
102 ) -> Optional[model_update_pb2.ModelUpdateProto]:
103 """Returns changes to the model since last call to checkpoint/creation."""
105 self.
_diff_id, remove_names=remove_names
109 """Track changes to the model only after this function call."""
118 """An optimization model.
120 The objective function of the model can be linear or quadratic, and some
121 solvers can only handle linear objective functions. For this reason Model has
122 three versions of all objective setting functions:
123 * A generic one (e.g. maximize()), which accepts linear or quadratic
125 * a quadratic version (e.g. maximize_quadratic_objective()), which also
126 accepts linear or quadratic expressions and can be used to signal a
127 quadratic objective is possible, and
128 * a linear version (e.g. maximize_linear_objective()), which only accepts
129 linear expressions and can be used to avoid solve time errors for solvers
130 that do not accept quadratic objectives.
133 name: A description of the problem, can be empty.
134 objective: A function to maximize or minimize.
135 storage: Implementation detail, do not access directly.
136 _variable_ids: Maps variable ids to Variable objects.
137 _linear_constraint_ids: Maps linear constraint ids to LinearConstraint
141 __slots__ = (
"_elemental",)
147 primary_objective_name: str =
"",
150 model_name=name, primary_objective_name=primary_objective_name
164 lb: float = -math.inf,
165 ub: float = math.inf,
166 is_integer: bool =
False,
168 ) -> variables_mod.Variable:
169 """Adds a decision variable to the optimization model.
172 lb: The new variable must take at least this value (a lower bound).
173 ub: The new variable must be at most this value (an upper bound).
174 is_integer: Indicates if the variable can only take integer values
175 (otherwise, the variable can take any continuous value).
176 name: For debugging purposes only, but nonempty names must be distinct.
179 A reference to the new decision variable.
182 variable_id = self.
_elemental.add_element(enums.ElementType.VARIABLE, name)
183 result = variables_mod.Variable(self.
_elemental, variable_id)
184 result.lower_bound = lb
185 result.upper_bound = ub
186 result.integer = is_integer
190 self, *, lb: float = -math.inf, ub: float = math.inf, name: str =
""
191 ) -> variables_mod.Variable:
192 return self.
add_variable(lb=lb, ub=ub, is_integer=
True, name=name)
195 return self.
add_variable(lb=0.0, ub=1.0, is_integer=
True, name=name)
198 self, var_id: int, *, validate: bool =
True
199 ) -> variables_mod.Variable:
200 """Returns the Variable for the id var_id, or raises KeyError."""
201 if validate
and not self.
_elemental.element_exists(
202 enums.ElementType.VARIABLE, var_id
204 raise KeyError(f
"Variable does not exist with id {var_id}.")
205 return variables_mod.Variable(self.
_elemental, var_id)
208 """Returns true if a Variable with this id is in the model."""
209 return self.
_elemental.element_exists(enums.ElementType.VARIABLE, var_id)
212 """Returns the number of variables in the model."""
213 return self.
_elemental.get_num_elements(enums.ElementType.VARIABLE)
216 """Returns the id of the next variable created in the model."""
217 return self.
_elemental.get_next_element_id(enums.ElementType.VARIABLE)
220 """If the next variable id would be less than `var_id`, sets it to `var_id`."""
221 self.
_elemental.ensure_next_element_id_at_least(
222 enums.ElementType.VARIABLE, var_id
226 """Removes this variable from the model."""
228 if not self.
_elemental.delete_element(enums.ElementType.VARIABLE, var.id):
229 raise ValueError(f
"Variable with id {var.id} was not in the model.")
231 def variables(self) -> Iterator[variables_mod.Variable]:
232 """Yields the variables in the order of creation."""
233 var_ids = self.
_elemental.get_elements(enums.ElementType.VARIABLE)
235 for var_id
in var_ids:
236 yield variables_mod.Variable(self.
_elemental, int(var_id))
246 def maximize(self, obj: variables_mod.QuadraticTypes) ->
None:
247 """Sets the objective to maximize the provided expression `obj`."""
251 """Sets the objective to maximize the provided linear expression `obj`."""
255 """Sets the objective to maximize the provided quadratic expression `obj`."""
258 def minimize(self, obj: variables_mod.QuadraticTypes) ->
None:
259 """Sets the objective to minimize the provided expression `obj`."""
263 """Sets the objective to minimize the provided linear expression `obj`."""
267 """Sets the objective to minimize the provided quadratic expression `obj`."""
271 self, obj: variables_mod.QuadraticTypes, *, is_maximize: bool
273 """Sets the objective to optimize the provided expression `obj`."""
278 self, obj: variables_mod.LinearTypes, *, is_maximize: bool
280 """Sets the objective to optimize the provided linear expression `obj`."""
281 self.
objective.set_to_linear_expression(obj)
285 self, obj: variables_mod.QuadraticTypes, *, is_maximize: bool
287 """Sets the objective to optimize the provided quadratic expression `obj`."""
288 self.
objective.set_to_quadratic_expression(obj)
292 """Yields variable coefficient pairs for variables with nonzero objective coefficient in undefined order."""
296 """Yields the quadratic terms with nonzero objective coefficient in undefined order."""
297 yield from self.
objective.quadratic_terms()
308 expr: Optional[variables_mod.LinearTypes] =
None,
309 is_maximize: bool =
False,
311 """Adds an additional objective to the model."""
313 enums.ElementType.AUXILIARY_OBJECTIVE, name
316 enums.IntAttr1.AUXILIARY_OBJECTIVE_PRIORITY, (obj_id,), priority
320 result.set_to_linear_expression(expr)
321 result.is_maximize = is_maximize
325 self, expr: variables_mod.LinearTypes, *, priority: int, name: str =
""
327 """Adds an additional objective to the model that is maximizaition."""
329 priority=priority, name=name, expr=expr, is_maximize=
True
334 self, expr: variables_mod.LinearTypes, *, priority: int, name: str =
""
336 """Adds an additional objective to the model that is minimizaition."""
338 priority=priority, name=name, expr=expr, is_maximize=
False
343 """Removes an auxiliary objective from the model."""
346 enums.ElementType.AUXILIARY_OBJECTIVE, obj.id
349 f
"Auxiliary objective with id {obj.id} is not in the model."
353 """Returns true if the model has an auxiliary objective with id `obj_id`."""
355 enums.ElementType.AUXILIARY_OBJECTIVE, obj_id
359 """Returns the id of the next auxiliary objective added to the model."""
361 enums.ElementType.AUXILIARY_OBJECTIVE
365 """Returns the number of auxiliary objectives in this model."""
366 return self.
_elemental.get_num_elements(enums.ElementType.AUXILIARY_OBJECTIVE)
369 """If the next auxiliary objective id would be less than `obj_id`, sets it to `obj_id`."""
370 self.
_elemental.ensure_next_element_id_at_least(
371 enums.ElementType.AUXILIARY_OBJECTIVE, obj_id
375 self, obj_id: int, *, validate: bool =
True
377 """Returns the auxiliary objective with this id.
379 If there is no objective with this id, an exception is thrown if validate is
380 true, and an invalid AuxiliaryObjective is returned if validate is false
381 (later interactions with this object will cause unpredictable errors). Only
382 set validate=False if there is a known performance problem.
385 obj_id: The id of the auxiliary objective to look for.
386 validate: Set to false for more speed, but fails to raise an exception if
387 the objective is missing.
390 KeyError: If `validate` is True and there is no objective with this id.
393 raise KeyError(f
"Model has no auxiliary objective with id {obj_id}")
397 """Returns the auxiliary objectives in the model in the order of creation."""
398 ids = self.
_elemental.get_elements(enums.ElementType.AUXILIARY_OBJECTIVE)
400 for aux_obj_id
in ids:
412 bounded_expr: Optional[Union[bool, variables_mod.BoundedLinearTypes]] =
None,
414 lb: Optional[float] =
None,
415 ub: Optional[float] =
None,
416 expr: Optional[variables_mod.LinearTypes] =
None,
418 ) -> linear_constraints_mod.LinearConstraint:
419 """Adds a linear constraint to the optimization model.
421 The simplest way to specify the constraint is by passing a one-sided or
422 two-sided linear inequality as in:
423 * add_linear_constraint(x + y + 1.0 <= 2.0),
424 * add_linear_constraint(x + y >= 2.0), or
425 * add_linear_constraint((1.0 <= x + y) <= 2.0).
427 Note the extra parenthesis for two-sided linear inequalities, which is
428 required due to some language limitations (see
429 https://peps.python.org/pep-0335/ and https://peps.python.org/pep-0535/).
430 If the parenthesis are omitted, a TypeError will be raised explaining the
431 issue (if this error was not raised the first inequality would have been
432 silently ignored because of the noted language limitations).
434 The second way to specify the constraint is by setting lb, ub, and/or expr
436 * add_linear_constraint(expr=x + y + 1.0, ub=2.0),
437 * add_linear_constraint(expr=x + y, lb=2.0),
438 * add_linear_constraint(expr=x + y, lb=1.0, ub=2.0), or
439 * add_linear_constraint(lb=1.0).
440 Omitting lb is equivalent to setting it to -math.inf and omiting ub is
441 equivalent to setting it to math.inf.
443 These two alternatives are exclusive and a combined call like:
444 * add_linear_constraint(x + y <= 2.0, lb=1.0), or
445 * add_linear_constraint(x + y <= 2.0, ub=math.inf)
446 will raise a ValueError. A ValueError is also raised if expr's offset is
450 bounded_expr: a linear inequality describing the constraint. Cannot be
451 specified together with lb, ub, or expr.
452 lb: The constraint's lower bound if bounded_expr is omitted (if both
453 bounder_expr and lb are omitted, the lower bound is -math.inf).
454 ub: The constraint's upper bound if bounded_expr is omitted (if both
455 bounder_expr and ub are omitted, the upper bound is math.inf).
456 expr: The constraint's linear expression if bounded_expr is omitted.
457 name: For debugging purposes only, but nonempty names must be distinct.
460 A reference to the new linear constraint.
462 norm_ineq = normalized_inequality.as_normalized_linear_inequality(
463 bounded_expr, lb=lb, ub=ub, expr=expr
466 enums.ElementType.LINEAR_CONSTRAINT, name
469 result = linear_constraints_mod.LinearConstraint(self.
_elemental, lin_con_id)
470 result.lower_bound = norm_ineq.lb
471 result.upper_bound = norm_ineq.ub
472 for var, coefficient
in norm_ineq.coefficients.items():
473 result.set_coefficient(var, coefficient)
477 """Returns true if a linear constraint with this id is in the model."""
479 enums.ElementType.LINEAR_CONSTRAINT, con_id
483 """Returns the number of linear constraints in the model."""
484 return self.
_elemental.get_num_elements(enums.ElementType.LINEAR_CONSTRAINT)
487 """Returns the id of the next linear constraint created in the model."""
488 return self.
_elemental.get_next_element_id(enums.ElementType.LINEAR_CONSTRAINT)
491 """If the next linear constraint id would be less than `con_id`, sets it to `con_id`."""
492 self.
_elemental.ensure_next_element_id_at_least(
493 enums.ElementType.LINEAR_CONSTRAINT, con_id
497 self, con_id: int, *, validate: bool =
True
498 ) -> linear_constraints_mod.LinearConstraint:
499 """Returns the LinearConstraint for the id con_id."""
500 if validate
and not self.
_elemental.element_exists(
501 enums.ElementType.LINEAR_CONSTRAINT, con_id
503 raise KeyError(f
"Linear constraint does not exist with id {con_id}.")
504 return linear_constraints_mod.LinearConstraint(self.
_elemental, con_id)
507 self, lin_con: linear_constraints_mod.LinearConstraint
511 enums.ElementType.LINEAR_CONSTRAINT, lin_con.id
514 f
"Linear constraint with id {lin_con.id} was not in the model."
517 def linear_constraints(
519 ) -> Iterator[linear_constraints_mod.LinearConstraint]:
520 """Yields the linear constraints in the order of creation."""
521 lin_con_ids = self.
_elemental.get_elements(enums.ElementType.LINEAR_CONSTRAINT)
523 for lin_con_id
in lin_con_ids:
524 yield linear_constraints_mod.LinearConstraint(
529 self, lin_con: linear_constraints_mod.LinearConstraint
530 ) -> Iterator[variables_mod.Variable]:
531 """Yields the variables with nonzero coefficient for this linear constraint."""
533 enums.DoubleAttr2.LINEAR_CONSTRAINT_COEFFICIENT, 0, lin_con.id
535 for var_id
in keys[:, 1]:
536 yield variables_mod.Variable(self.
_elemental, int(var_id))
539 self, var: variables_mod.Variable
540 ) -> Iterator[linear_constraints_mod.LinearConstraint]:
541 """Yields the linear constraints with nonzero coefficient for this variable."""
543 enums.DoubleAttr2.LINEAR_CONSTRAINT_COEFFICIENT, 1, var.id
545 for lin_con_id
in keys[:, 0]:
546 yield linear_constraints_mod.LinearConstraint(
552 ) -> Iterator[linear_constraints_mod.LinearConstraintMatrixEntry]:
553 """Yields the nonzero elements of the linear constraint matrix in undefined order."""
555 enums.DoubleAttr2.LINEAR_CONSTRAINT_COEFFICIENT
558 enums.DoubleAttr2.LINEAR_CONSTRAINT_COEFFICIENT, keys
560 for i
in range(len(keys)):
561 yield linear_constraints_mod.LinearConstraintMatrixEntry(
562 linear_constraint=linear_constraints_mod.LinearConstraint(
565 variable=variables_mod.Variable(self.
_elemental, int(keys[i, 1])),
566 coefficient=float(coefs[i]),
575 bounded_expr: Optional[
578 variables_mod.BoundedLinearTypes,
579 variables_mod.BoundedQuadraticTypes,
583 lb: Optional[float] =
None,
584 ub: Optional[float] =
None,
585 expr: Optional[variables_mod.QuadraticTypes] =
None,
588 """Adds a quadratic constraint to the optimization model.
590 The simplest way to specify the constraint is by passing a one-sided or
591 two-sided quadratic inequality as in:
592 * add_quadratic_constraint(x * x + y + 1.0 <= 2.0),
593 * add_quadratic_constraint(x * x + y >= 2.0), or
594 * add_quadratic_constraint((1.0 <= x * x + y) <= 2.0).
596 Note the extra parenthesis for two-sided linear inequalities, which is
597 required due to some language limitations (see add_linear_constraint for
600 The second way to specify the constraint is by setting lb, ub, and/or expr
602 * add_quadratic_constraint(expr=x * x + y + 1.0, ub=2.0),
603 * add_quadratic_constraint(expr=x * x + y, lb=2.0),
604 * add_quadratic_constraint(expr=x * x + y, lb=1.0, ub=2.0), or
605 * add_quadratic_constraint(lb=1.0).
606 Omitting lb is equivalent to setting it to -math.inf and omiting ub is
607 equivalent to setting it to math.inf.
609 These two alternatives are exclusive and a combined call like:
610 * add_quadratic_constraint(x * x + y <= 2.0, lb=1.0), or
611 * add_quadratic_constraint(x * x+ y <= 2.0, ub=math.inf)
612 will raise a ValueError. A ValueError is also raised if expr's offset is
616 bounded_expr: a quadratic inequality describing the constraint. Cannot be
617 specified together with lb, ub, or expr.
618 lb: The constraint's lower bound if bounded_expr is omitted (if both
619 bounder_expr and lb are omitted, the lower bound is -math.inf).
620 ub: The constraint's upper bound if bounded_expr is omitted (if both
621 bounder_expr and ub are omitted, the upper bound is math.inf).
622 expr: The constraint's quadratic expression if bounded_expr is omitted.
623 name: For debugging purposes only, but nonempty names must be distinct.
626 A reference to the new quadratic constraint.
628 norm_quad = normalized_inequality.as_normalized_quadratic_inequality(
629 bounded_expr, lb=lb, ub=ub, expr=expr
632 enums.ElementType.QUADRATIC_CONSTRAINT, name
634 for var, coef
in norm_quad.linear_coefficients.items():
636 enums.DoubleAttr2.QUADRATIC_CONSTRAINT_LINEAR_COEFFICIENT,
637 (quad_con_id, var.id),
640 for key, coef
in norm_quad.quadratic_coefficients.items():
642 enums.SymmetricDoubleAttr3.QUADRATIC_CONSTRAINT_QUADRATIC_COEFFICIENT,
643 (quad_con_id, key.first_var.id, key.second_var.id),
646 if norm_quad.lb > -math.inf:
648 enums.DoubleAttr1.QUADRATIC_CONSTRAINT_LOWER_BOUND,
652 if norm_quad.ub < math.inf:
654 enums.DoubleAttr1.QUADRATIC_CONSTRAINT_UPPER_BOUND,
661 """Returns true if a quadratic constraint with this id is in the model."""
663 enums.ElementType.QUADRATIC_CONSTRAINT, con_id
667 """Returns the number of quadratic constraints in the model."""
668 return self.
_elemental.get_num_elements(enums.ElementType.QUADRATIC_CONSTRAINT)
671 """Returns the id of the next quadratic constraint created in the model."""
673 enums.ElementType.QUADRATIC_CONSTRAINT
677 """If the next quadratic constraint id would be less than `con_id`, sets it to `con_id`."""
678 self.
_elemental.ensure_next_element_id_at_least(
679 enums.ElementType.QUADRATIC_CONSTRAINT, con_id
683 self, con_id: int, *, validate: bool =
True
685 """Returns the constraint for the id, or raises KeyError if not in model."""
686 if validate
and not self.
_elemental.element_exists(
687 enums.ElementType.QUADRATIC_CONSTRAINT, con_id
689 raise KeyError(f
"Quadratic constraint does not exist with id {con_id}.")
693 self, quad_con: quadratic_constraints.QuadraticConstraint
695 """Deletes the constraint with id, or raises ValueError if not in model."""
698 enums.ElementType.QUADRATIC_CONSTRAINT, quad_con.id
701 f
"Quadratic constraint with id {quad_con.id} was not in the model."
706 ) -> Iterator[quadratic_constraints.QuadraticConstraint]:
707 """Yields the quadratic constraints in the order of creation."""
709 enums.ElementType.QUADRATIC_CONSTRAINT
712 for quad_con_id
in quad_con_ids:
721 quadratic_constraints.QuadraticConstraint,
722 variables_mod.Variable,
726 """Yields the linear coefficients for all quadratic constraints in the model."""
728 enums.DoubleAttr2.QUADRATIC_CONSTRAINT_LINEAR_COEFFICIENT
731 enums.DoubleAttr2.QUADRATIC_CONSTRAINT_LINEAR_COEFFICIENT, keys
733 for i
in range(len(keys)):
738 variables_mod.Variable(self.
_elemental, int(keys[i, 1])),
746 quadratic_constraints.QuadraticConstraint,
747 variables_mod.Variable,
748 variables_mod.Variable,
752 """Yields the quadratic coefficients for all quadratic constraints in the model."""
754 enums.SymmetricDoubleAttr3.QUADRATIC_CONSTRAINT_QUADRATIC_COEFFICIENT
757 enums.SymmetricDoubleAttr3.QUADRATIC_CONSTRAINT_QUADRATIC_COEFFICIENT,
760 for i
in range(len(keys)):
765 variables_mod.Variable(self.
_elemental, int(keys[i, 1])),
766 variables_mod.Variable(self.
_elemental, int(keys[i, 2])),
777 indicator: Optional[variables_mod.Variable] =
None,
778 activate_on_zero: bool =
False,
779 implied_constraint: Optional[
780 Union[bool, variables_mod.BoundedLinearTypes]
782 implied_lb: Optional[float] =
None,
783 implied_ub: Optional[float] =
None,
784 implied_expr: Optional[variables_mod.LinearTypes] =
None,
787 """Adds an indicator constraint to the model.
789 If indicator is None or the variable equal to indicator is deleted from
790 the model, the model will be considered invalid at solve time (unless this
791 constraint is also deleted before solving). Likewise, the variable indicator
792 must be binary at solve time for the model to be valid.
794 If implied_constraint is set, you may not set implied_lb, implied_ub, or
798 indicator: The variable whose value determines if implied_constraint must
800 activate_on_zero: If true, implied_constraint must hold when indicator is
801 zero, otherwise, the implied_constraint must hold when indicator is one.
802 implied_constraint: A linear constraint to conditionally enforce, if set.
803 If None, that information is instead passed via implied_lb, implied_ub,
805 implied_lb: The lower bound of the condtionally enforced linear constraint
806 (or -inf if None), used only when implied_constraint is None.
807 implied_ub: The upper bound of the condtionally enforced linear constraint
808 (or +inf if None), used only when implied_constraint is None.
809 implied_expr: The linear part of the condtionally enforced linear
810 constraint (or 0 if None), used only when implied_constraint is None. If
811 expr has a nonzero offset, it is subtracted from lb and ub.
812 name: For debugging purposes only, but nonempty names must be distinct.
815 A reference to the new indicator constraint.
818 enums.ElementType.INDICATOR_CONSTRAINT, name
820 if indicator
is not None:
822 enums.VariableAttr1.INDICATOR_CONSTRAINT_INDICATOR,
827 enums.BoolAttr1.INDICATOR_CONSTRAINT_ACTIVATE_ON_ZERO,
831 implied_inequality = normalized_inequality.as_normalized_linear_inequality(
832 implied_constraint, lb=implied_lb, ub=implied_ub, expr=implied_expr
835 enums.DoubleAttr1.INDICATOR_CONSTRAINT_LOWER_BOUND,
837 implied_inequality.lb,
840 enums.DoubleAttr1.INDICATOR_CONSTRAINT_UPPER_BOUND,
842 implied_inequality.ub,
844 for var, coef
in implied_inequality.coefficients.items():
846 enums.DoubleAttr2.INDICATOR_CONSTRAINT_LINEAR_COEFFICIENT,
847 (ind_con_id, var.id),
854 """Returns true if an indicator constraint with this id is in the model."""
856 enums.ElementType.INDICATOR_CONSTRAINT, con_id
860 """Returns the number of indicator constraints in the model."""
861 return self.
_elemental.get_num_elements(enums.ElementType.INDICATOR_CONSTRAINT)
864 """Returns the id of the next indicator constraint created in the model."""
866 enums.ElementType.INDICATOR_CONSTRAINT
870 """If the next indicator constraint id would be less than `con_id`, sets it to `con_id`."""
871 self.
_elemental.ensure_next_element_id_at_least(
872 enums.ElementType.INDICATOR_CONSTRAINT, con_id
876 self, con_id: int, *, validate: bool =
True
878 """Returns the IndicatorConstraint for the id con_id."""
879 if validate
and not self.
_elemental.element_exists(
880 enums.ElementType.INDICATOR_CONSTRAINT, con_id
882 raise KeyError(f
"Indicator constraint does not exist with id {con_id}.")
886 self, ind_con: indicator_constraints.IndicatorConstraint
890 enums.ElementType.INDICATOR_CONSTRAINT, ind_con.id
893 f
"Indicator constraint with id {ind_con.id} was not in the model."
898 ) -> Iterator[indicator_constraints.IndicatorConstraint]:
899 """Yields the indicator constraints in the order of creation."""
901 enums.ElementType.INDICATOR_CONSTRAINT
904 for ind_con_id
in ind_con_ids:
914 """Returns a protocol buffer equivalent to this model."""
918 """Creates an UpdateTracker registered on this model to view changes."""
922 """Stops tracker from getting updates on changes to this model.
924 An error will be raised if tracker was not created by this Model or if
925 tracker has been previously removed.
927 Using (via checkpoint or update) an UpdateTracker after it has been removed
928 will result in an error.
931 tracker: The UpdateTracker to unregister.
934 KeyError: The tracker was created by another model or was already removed.
939 """Raises a ValueError if the model of var_or_constraint is not self."""
942 f
"Expected element from model named: '{self._elemental.model_name}',"
943 f
" but observed element {e} from model named:"
944 f
" '{e.elemental.model_name}'."