ortools.math_opt.python.objectives

An Objective for a MathOpt optimization model.

  1# Copyright 2010-2025 Google LLC
  2# Licensed under the Apache License, Version 2.0 (the "License");
  3# you may not use this file except in compliance with the License.
  4# You may obtain a copy of the License at
  5#
  6#     http://www.apache.org/licenses/LICENSE-2.0
  7#
  8# Unless required by applicable law or agreed to in writing, software
  9# distributed under the License is distributed on an "AS IS" BASIS,
 10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 11# See the License for the specific language governing permissions and
 12# limitations under the License.
 13
 14"""An Objective for a MathOpt optimization model."""
 15
 16import abc
 17from typing import Any, Iterator
 18
 19from ortools.math_opt.elemental.python import enums
 20from ortools.math_opt.python import from_model
 21from ortools.math_opt.python import variables
 22from ortools.math_opt.python.elemental import elemental
 23
 24
 25class Objective(from_model.FromModel, metaclass=abc.ABCMeta):
 26    """The objective for an optimization model.
 27
 28    An objective is either of the form:
 29      min o + sum_{i in I} c_i * x_i + sum_{i, j in I, i <= j} q_i,j * x_i * x_j
 30    or
 31      max o + sum_{i in I} c_i * x_i + sum_{(i, j) in Q} q_i,j * x_i * x_j
 32    where x_i are the decision variables of the problem and where all pairs (i, j)
 33    in Q satisfy i <= j. The values of o, c_i and q_i,j should be finite and not
 34    NaN.
 35
 36    The objective can be configured as follows:
 37      * offset: a float property, o above. Should be finite and not NaN.
 38      * is_maximize: a bool property, if the objective is to maximize or minimize.
 39      * set_linear_coefficient and get_linear_coefficient control the c_i * x_i
 40        terms. The variables must be from the same model as this objective, and
 41        the c_i must be finite and not NaN. The coefficient for any variable not
 42        set is 0.0, and setting a coefficient to 0.0 removes it from I above.
 43      * set_quadratic_coefficient and get_quadratic_coefficient control the
 44        q_i,j * x_i * x_j terms. The variables must be from the same model as this
 45        objective, and the q_i,j must be finite and not NaN. The coefficient for
 46        any pair of variables not set is 0.0, and setting a coefficient to 0.0
 47        removes the associated (i,j) from Q above.
 48
 49    Do not create an Objective directly, use Model.objective to access the
 50    objective instead (or Model.add_auxiliary_objective()). Two Objective objects
 51    can represent the same objective (for the same model). They will have the same
 52    underlying Objective.elemental for storing the data. The Objective class is
 53    simply a reference to an Elemental.
 54
 55    The objective is linear if only linear coefficients are set. This can be
 56    useful to avoid solve-time errors with solvers that do not accept quadratic
 57    objectives. To facilitate this linear objective guarantee we provide three
 58    functions to add to the objective:
 59      * add(), which accepts linear or quadratic expressions,
 60      * add_quadratic(), which also accepts linear or quadratic expressions and
 61        can be used to signal a quadratic objective is possible, and
 62      * add_linear(), which only accepts linear expressions and can be used to
 63        guarantee the objective remains linear.
 64
 65    For quadratic terms, the order that variables are provided does not matter,
 66    we always canonicalize to first_var <= second_var. So if you set (x1, x2) to 7
 67    then:
 68      * getting (x2, x1) returns 7
 69      * setting (x2, x1) to 10 overwrites the value of 7.
 70    Likewise, when we return nonzero quadratic coefficients, we always use the
 71    form first_var <= second_var.
 72
 73    Most problems have only a single objective, but hierarchical objectives are
 74    supported (see Model.add_auxiliary_objective()). Note that quadratic Auxiliary
 75    objectives are not supported.
 76    """
 77
 78    __slots__ = ("_elemental",)
 79
 80    def __init__(self, elem: elemental.Elemental) -> None:
 81        """Do not invoke directly, prefer Model.objective."""
 82        self._elemental: elemental.Elemental = elem
 83
 84    @property
 85    def elemental(self) -> elemental.Elemental:
 86        """The underlying data structure for the model, for internal use only."""
 87        return self._elemental
 88
 89    @property
 90    @abc.abstractmethod
 91    def name(self) -> str:
 92        """The immutable name of this objective, for display only."""
 93
 94    @property
 95    @abc.abstractmethod
 96    def is_maximize(self) -> bool:
 97        """If true, the direction is maximization, otherwise minimization."""
 98
 99    @is_maximize.setter
100    @abc.abstractmethod
101    def is_maximize(self, is_maximize: bool) -> None: ...
102
103    @property
104    @abc.abstractmethod
105    def offset(self) -> float:
106        """A constant added to the objective."""
107
108    @offset.setter
109    @abc.abstractmethod
110    def offset(self, value: float) -> None: ...
111
112    @property
113    @abc.abstractmethod
114    def priority(self) -> int:
115        """For hierarchical problems, determines the order to apply objectives.
116
117        The objectives are applied from lowest priority to highest.
118
119        The default priority for the primary objective is zero, and auxiliary
120        objectives must specific a priority at creation time.
121
122        Priority has no effect for problems with only one objective.
123        """
124
125    @priority.setter
126    @abc.abstractmethod
127    def priority(self, value: int) -> None: ...
128
129    @abc.abstractmethod
130    def set_linear_coefficient(self, var: variables.Variable, coef: float) -> None:
131        """Sets the coefficient of `var` to `coef` in the objective."""
132
133    @abc.abstractmethod
134    def get_linear_coefficient(self, var: variables.Variable) -> float:
135        """Returns the coefficinet of `var` (or zero if unset)."""
136
137    @abc.abstractmethod
138    def linear_terms(self) -> Iterator[variables.LinearTerm]:
139        """Yields variable coefficient pairs for variables with nonzero objective coefficient in undefined order."""
140
141    @abc.abstractmethod
142    def set_quadratic_coefficient(
143        self,
144        first_variable: variables.Variable,
145        second_variable: variables.Variable,
146        coef: float,
147    ) -> None:
148        """Sets the coefficient for product of variables (see class description)."""
149
150    @abc.abstractmethod
151    def get_quadratic_coefficient(
152        self,
153        first_variable: variables.Variable,
154        second_variable: variables.Variable,
155    ) -> float:
156        """Gets the coefficient for product of variables (see class description)."""
157
158    @abc.abstractmethod
159    def quadratic_terms(self) -> Iterator[variables.QuadraticTerm]:
160        """Yields quadratic terms with nonzero objective coefficient in undefined order."""
161
162    @abc.abstractmethod
163    def clear(self) -> None:
164        """Clears objective coefficients and offset. Does not change direction."""
165
166    def as_linear_expression(self) -> variables.LinearExpression:
167        """Returns an equivalent LinearExpression, or errors if quadratic."""
168        if any(self.quadratic_terms()):
169            raise TypeError("Cannot get a quadratic objective as a linear expression")
170        return variables.as_flat_linear_expression(
171            self.offset + variables.LinearSum(self.linear_terms())
172        )
173
174    def as_quadratic_expression(self) -> variables.QuadraticExpression:
175        """Returns an equivalent QuadraticExpression to this objetive."""
176        return variables.as_flat_quadratic_expression(
177            self.offset
178            + variables.LinearSum(self.linear_terms())
179            + variables.QuadraticSum(self.quadratic_terms())
180        )
181
182    def add(self, objective: variables.QuadraticTypes) -> None:
183        """Adds the provided expression `objective` to the objective function.
184
185        For a compile time guarantee that the objective remains linear, use
186        add_linear() instead.
187
188        Args:
189          objective: the expression to add to the objective function.
190        """
191        if isinstance(objective, (variables.LinearBase, int, float)):
192            self.add_linear(objective)
193        elif isinstance(objective, variables.QuadraticBase):
194            self.add_quadratic(objective)
195        else:
196            raise TypeError(
197                "unsupported type in objective argument for "
198                f"Objective.add(): {type(objective).__name__!r}"
199            )
200
201    def add_linear(self, objective: variables.LinearTypes) -> None:
202        """Adds the provided linear expression `objective` to the objective function."""
203        if not isinstance(objective, (variables.LinearBase, int, float)):
204            raise TypeError(
205                "unsupported type in objective argument for "
206                f"Objective.add_linear(): {type(objective).__name__!r}"
207            )
208        objective_expr = variables.as_flat_linear_expression(objective)
209        self.offset += objective_expr.offset
210        for var, coefficient in objective_expr.terms.items():
211            self.set_linear_coefficient(
212                var, self.get_linear_coefficient(var) + coefficient
213            )
214
215    def add_quadratic(self, objective: variables.QuadraticTypes) -> None:
216        """Adds the provided quadratic expression `objective` to the objective function."""
217        if not isinstance(
218            objective, (variables.QuadraticBase, variables.LinearBase, int, float)
219        ):
220            raise TypeError(
221                "unsupported type in objective argument for "
222                f"Objective.add(): {type(objective).__name__!r}"
223            )
224        objective_expr = variables.as_flat_quadratic_expression(objective)
225        self.offset += objective_expr.offset
226        for var, coefficient in objective_expr.linear_terms.items():
227            self.set_linear_coefficient(
228                var, self.get_linear_coefficient(var) + coefficient
229            )
230        for key, coefficient in objective_expr.quadratic_terms.items():
231            self.set_quadratic_coefficient(
232                key.first_var,
233                key.second_var,
234                self.get_quadratic_coefficient(key.first_var, key.second_var)
235                + coefficient,
236            )
237
238    def set_to_linear_expression(self, linear_expr: variables.LinearTypes) -> None:
239        """Sets the objective to optimize to `linear_expr`."""
240        if not isinstance(linear_expr, (variables.LinearBase, int, float)):
241            raise TypeError(
242                "unsupported type in objective argument for "
243                f"set_to_linear_expression: {type(linear_expr).__name__!r}"
244            )
245        self.clear()
246        objective_expr = variables.as_flat_linear_expression(linear_expr)
247        self.offset = objective_expr.offset
248        for var, coefficient in objective_expr.terms.items():
249            self.set_linear_coefficient(var, coefficient)
250
251    def set_to_quadratic_expression(
252        self, quadratic_expr: variables.QuadraticTypes
253    ) -> None:
254        """Sets the objective to optimize the `quadratic_expr`."""
255        if not isinstance(
256            quadratic_expr,
257            (variables.QuadraticBase, variables.LinearBase, int, float),
258        ):
259            raise TypeError(
260                "unsupported type in objective argument for "
261                f"set_to_quadratic_expression: {type(quadratic_expr).__name__!r}"
262            )
263        self.clear()
264        objective_expr = variables.as_flat_quadratic_expression(quadratic_expr)
265        self.offset = objective_expr.offset
266        for var, coefficient in objective_expr.linear_terms.items():
267            self.set_linear_coefficient(var, coefficient)
268        for quad_key, coefficient in objective_expr.quadratic_terms.items():
269            self.set_quadratic_coefficient(
270                quad_key.first_var, quad_key.second_var, coefficient
271            )
272
273    def set_to_expression(self, expr: variables.QuadraticTypes) -> None:
274        """Sets the objective to optimize the `expr`."""
275        if isinstance(expr, (variables.LinearBase, int, float)):
276            self.set_to_linear_expression(expr)
277        elif isinstance(expr, variables.QuadraticBase):
278            self.set_to_quadratic_expression(expr)
279        else:
280            raise TypeError(
281                "unsupported type in objective argument for "
282                f"set_to_expression: {type(expr).__name__!r}"
283            )
284
285
286class PrimaryObjective(Objective):
287    """The main objective, but users should program against Objective directly."""
288
289    __slots__ = ()
290
291    @property
292    def name(self) -> str:
293        return self._elemental.primary_objective_name
294
295    @property
296    def is_maximize(self) -> bool:
297        return self._elemental.get_attr(enums.BoolAttr0.MAXIMIZE, ())
298
299    @is_maximize.setter
300    def is_maximize(self, is_maximize: bool) -> None:
301        self._elemental.set_attr(enums.BoolAttr0.MAXIMIZE, (), is_maximize)
302
303    @property
304    def offset(self) -> float:
305        return self._elemental.get_attr(enums.DoubleAttr0.OBJECTIVE_OFFSET, ())
306
307    @offset.setter
308    def offset(self, value: float) -> None:
309        self._elemental.set_attr(enums.DoubleAttr0.OBJECTIVE_OFFSET, (), value)
310
311    @property
312    def priority(self) -> int:
313        return self._elemental.get_attr(enums.IntAttr0.OBJECTIVE_PRIORITY, ())
314
315    @priority.setter
316    def priority(self, value: int) -> None:
317        self._elemental.set_attr(enums.IntAttr0.OBJECTIVE_PRIORITY, (), value)
318
319    def set_linear_coefficient(self, var: variables.Variable, coef: float) -> None:
320        from_model.model_is_same(self, var)
321        self._elemental.set_attr(
322            enums.DoubleAttr1.OBJECTIVE_LINEAR_COEFFICIENT, (var.id,), coef
323        )
324
325    def get_linear_coefficient(self, var: variables.Variable) -> float:
326        from_model.model_is_same(self, var)
327        return self._elemental.get_attr(
328            enums.DoubleAttr1.OBJECTIVE_LINEAR_COEFFICIENT, (var.id,)
329        )
330
331    def linear_terms(self) -> Iterator[variables.LinearTerm]:
332        keys = self._elemental.get_attr_non_defaults(
333            enums.DoubleAttr1.OBJECTIVE_LINEAR_COEFFICIENT
334        )
335        var_index = 0
336        coefs = self._elemental.get_attrs(
337            enums.DoubleAttr1.OBJECTIVE_LINEAR_COEFFICIENT, keys
338        )
339        for i in range(len(keys)):
340            yield variables.LinearTerm(
341                variable=variables.Variable(self._elemental, int(keys[i, var_index])),
342                coefficient=float(coefs[i]),
343            )
344
345    def set_quadratic_coefficient(
346        self,
347        first_variable: variables.Variable,
348        second_variable: variables.Variable,
349        coef: float,
350    ) -> None:
351        from_model.model_is_same(self, first_variable)
352        from_model.model_is_same(self, second_variable)
353        self._elemental.set_attr(
354            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT,
355            (first_variable.id, second_variable.id),
356            coef,
357        )
358
359    def get_quadratic_coefficient(
360        self,
361        first_variable: variables.Variable,
362        second_variable: variables.Variable,
363    ) -> float:
364        from_model.model_is_same(self, first_variable)
365        from_model.model_is_same(self, second_variable)
366        return self._elemental.get_attr(
367            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT,
368            (first_variable.id, second_variable.id),
369        )
370
371    def quadratic_terms(self) -> Iterator[variables.QuadraticTerm]:
372        keys = self._elemental.get_attr_non_defaults(
373            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT
374        )
375        coefs = self._elemental.get_attrs(
376            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT, keys
377        )
378        for i in range(len(keys)):
379            yield variables.QuadraticTerm(
380                variables.QuadraticTermKey(
381                    variables.Variable(self._elemental, int(keys[i, 0])),
382                    variables.Variable(self._elemental, int(keys[i, 1])),
383                ),
384                coefficient=float(coefs[i]),
385            )
386
387    def clear(self) -> None:
388        self._elemental.clear_attr(enums.DoubleAttr0.OBJECTIVE_OFFSET)
389        self._elemental.clear_attr(enums.DoubleAttr1.OBJECTIVE_LINEAR_COEFFICIENT)
390        self._elemental.clear_attr(
391            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT
392        )
393
394    def __eq__(self, other: Any) -> bool:
395        if isinstance(other, PrimaryObjective):
396            return self._elemental is other._elemental
397        return False
398
399    def __hash__(self) -> int:
400        return hash(self._elemental)
401
402
403class AuxiliaryObjective(Objective):
404    """An additional objective that can be optimized after objectives."""
405
406    __slots__ = ("_id",)
407
408    def __init__(self, elem: elemental.Elemental, obj_id: int) -> None:
409        """Internal only, prefer Model functions (add_auxiliary_objective() and get_auxiliary_objective())."""
410        super().__init__(elem)
411        if not isinstance(obj_id, int):
412            raise TypeError(
413                f"obj_id type should be int, was: {type(obj_id).__name__!r}"
414            )
415        self._id: int = obj_id
416
417    @property
418    def name(self) -> str:
419        return self._elemental.get_element_name(
420            enums.ElementType.AUXILIARY_OBJECTIVE, self._id
421        )
422
423    @property
424    def id(self) -> int:
425        """Returns the id of this objective."""
426        return self._id
427
428    @property
429    def is_maximize(self) -> bool:
430        return self._elemental.get_attr(
431            enums.BoolAttr1.AUXILIARY_OBJECTIVE_MAXIMIZE, (self._id,)
432        )
433
434    @is_maximize.setter
435    def is_maximize(self, is_maximize: bool) -> None:
436        self._elemental.set_attr(
437            enums.BoolAttr1.AUXILIARY_OBJECTIVE_MAXIMIZE,
438            (self._id,),
439            is_maximize,
440        )
441
442    @property
443    def offset(self) -> float:
444        return self._elemental.get_attr(
445            enums.DoubleAttr1.AUXILIARY_OBJECTIVE_OFFSET, (self._id,)
446        )
447
448    @offset.setter
449    def offset(self, value: float) -> None:
450        self._elemental.set_attr(
451            enums.DoubleAttr1.AUXILIARY_OBJECTIVE_OFFSET,
452            (self._id,),
453            value,
454        )
455
456    @property
457    def priority(self) -> int:
458        return self._elemental.get_attr(
459            enums.IntAttr1.AUXILIARY_OBJECTIVE_PRIORITY, (self._id,)
460        )
461
462    @priority.setter
463    def priority(self, value: int) -> None:
464        self._elemental.set_attr(
465            enums.IntAttr1.AUXILIARY_OBJECTIVE_PRIORITY,
466            (self._id,),
467            value,
468        )
469
470    def set_linear_coefficient(self, var: variables.Variable, coef: float) -> None:
471        from_model.model_is_same(self, var)
472        self._elemental.set_attr(
473            enums.DoubleAttr2.AUXILIARY_OBJECTIVE_LINEAR_COEFFICIENT,
474            (self._id, var.id),
475            coef,
476        )
477
478    def get_linear_coefficient(self, var: variables.Variable) -> float:
479        from_model.model_is_same(self, var)
480        return self._elemental.get_attr(
481            enums.DoubleAttr2.AUXILIARY_OBJECTIVE_LINEAR_COEFFICIENT,
482            (
483                self._id,
484                var.id,
485            ),
486        )
487
488    def linear_terms(self) -> Iterator[variables.LinearTerm]:
489        keys = self._elemental.slice_attr(
490            enums.DoubleAttr2.AUXILIARY_OBJECTIVE_LINEAR_COEFFICIENT,
491            0,
492            self._id,
493        )
494        var_index = 1
495        coefs = self._elemental.get_attrs(
496            enums.DoubleAttr2.AUXILIARY_OBJECTIVE_LINEAR_COEFFICIENT, keys
497        )
498        for i in range(len(keys)):
499            yield variables.LinearTerm(
500                variable=variables.Variable(self._elemental, int(keys[i, var_index])),
501                coefficient=float(coefs[i]),
502            )
503
504    def set_quadratic_coefficient(
505        self,
506        first_variable: variables.Variable,
507        second_variable: variables.Variable,
508        coef: float,
509    ) -> None:
510        raise ValueError("Quadratic auxiliary objectives are not supported.")
511
512    def get_quadratic_coefficient(
513        self,
514        first_variable: variables.Variable,
515        second_variable: variables.Variable,
516    ) -> float:
517        from_model.model_is_same(self, first_variable)
518        from_model.model_is_same(self, second_variable)
519        if not self._elemental.element_exists(
520            enums.ElementType.VARIABLE, first_variable.id
521        ):
522            raise ValueError(f"Variable {first_variable} does not exist")
523        if not self._elemental.element_exists(
524            enums.ElementType.VARIABLE, second_variable.id
525        ):
526            raise ValueError(f"Variable {second_variable} does not exist")
527        return 0.0
528
529    def quadratic_terms(self) -> Iterator[variables.QuadraticTerm]:
530        return iter(())
531
532    def clear(self) -> None:
533        """Clears objective coefficients and offset. Does not change direction."""
534        self._elemental.clear_attr(enums.DoubleAttr1.AUXILIARY_OBJECTIVE_OFFSET)
535        self._elemental.clear_attr(
536            enums.DoubleAttr2.AUXILIARY_OBJECTIVE_LINEAR_COEFFICIENT
537        )
538
539    def __eq__(self, other: Any) -> bool:
540        if isinstance(other, AuxiliaryObjective):
541            return self._elemental is other._elemental and self._id == other._id
542        return False
543
544    def __hash__(self) -> int:
545        return hash((self._elemental, self._id))
class Objective(ortools.math_opt.python.from_model.FromModel):
 26class Objective(from_model.FromModel, metaclass=abc.ABCMeta):
 27    """The objective for an optimization model.
 28
 29    An objective is either of the form:
 30      min o + sum_{i in I} c_i * x_i + sum_{i, j in I, i <= j} q_i,j * x_i * x_j
 31    or
 32      max o + sum_{i in I} c_i * x_i + sum_{(i, j) in Q} q_i,j * x_i * x_j
 33    where x_i are the decision variables of the problem and where all pairs (i, j)
 34    in Q satisfy i <= j. The values of o, c_i and q_i,j should be finite and not
 35    NaN.
 36
 37    The objective can be configured as follows:
 38      * offset: a float property, o above. Should be finite and not NaN.
 39      * is_maximize: a bool property, if the objective is to maximize or minimize.
 40      * set_linear_coefficient and get_linear_coefficient control the c_i * x_i
 41        terms. The variables must be from the same model as this objective, and
 42        the c_i must be finite and not NaN. The coefficient for any variable not
 43        set is 0.0, and setting a coefficient to 0.0 removes it from I above.
 44      * set_quadratic_coefficient and get_quadratic_coefficient control the
 45        q_i,j * x_i * x_j terms. The variables must be from the same model as this
 46        objective, and the q_i,j must be finite and not NaN. The coefficient for
 47        any pair of variables not set is 0.0, and setting a coefficient to 0.0
 48        removes the associated (i,j) from Q above.
 49
 50    Do not create an Objective directly, use Model.objective to access the
 51    objective instead (or Model.add_auxiliary_objective()). Two Objective objects
 52    can represent the same objective (for the same model). They will have the same
 53    underlying Objective.elemental for storing the data. The Objective class is
 54    simply a reference to an Elemental.
 55
 56    The objective is linear if only linear coefficients are set. This can be
 57    useful to avoid solve-time errors with solvers that do not accept quadratic
 58    objectives. To facilitate this linear objective guarantee we provide three
 59    functions to add to the objective:
 60      * add(), which accepts linear or quadratic expressions,
 61      * add_quadratic(), which also accepts linear or quadratic expressions and
 62        can be used to signal a quadratic objective is possible, and
 63      * add_linear(), which only accepts linear expressions and can be used to
 64        guarantee the objective remains linear.
 65
 66    For quadratic terms, the order that variables are provided does not matter,
 67    we always canonicalize to first_var <= second_var. So if you set (x1, x2) to 7
 68    then:
 69      * getting (x2, x1) returns 7
 70      * setting (x2, x1) to 10 overwrites the value of 7.
 71    Likewise, when we return nonzero quadratic coefficients, we always use the
 72    form first_var <= second_var.
 73
 74    Most problems have only a single objective, but hierarchical objectives are
 75    supported (see Model.add_auxiliary_objective()). Note that quadratic Auxiliary
 76    objectives are not supported.
 77    """
 78
 79    __slots__ = ("_elemental",)
 80
 81    def __init__(self, elem: elemental.Elemental) -> None:
 82        """Do not invoke directly, prefer Model.objective."""
 83        self._elemental: elemental.Elemental = elem
 84
 85    @property
 86    def elemental(self) -> elemental.Elemental:
 87        """The underlying data structure for the model, for internal use only."""
 88        return self._elemental
 89
 90    @property
 91    @abc.abstractmethod
 92    def name(self) -> str:
 93        """The immutable name of this objective, for display only."""
 94
 95    @property
 96    @abc.abstractmethod
 97    def is_maximize(self) -> bool:
 98        """If true, the direction is maximization, otherwise minimization."""
 99
100    @is_maximize.setter
101    @abc.abstractmethod
102    def is_maximize(self, is_maximize: bool) -> None: ...
103
104    @property
105    @abc.abstractmethod
106    def offset(self) -> float:
107        """A constant added to the objective."""
108
109    @offset.setter
110    @abc.abstractmethod
111    def offset(self, value: float) -> None: ...
112
113    @property
114    @abc.abstractmethod
115    def priority(self) -> int:
116        """For hierarchical problems, determines the order to apply objectives.
117
118        The objectives are applied from lowest priority to highest.
119
120        The default priority for the primary objective is zero, and auxiliary
121        objectives must specific a priority at creation time.
122
123        Priority has no effect for problems with only one objective.
124        """
125
126    @priority.setter
127    @abc.abstractmethod
128    def priority(self, value: int) -> None: ...
129
130    @abc.abstractmethod
131    def set_linear_coefficient(self, var: variables.Variable, coef: float) -> None:
132        """Sets the coefficient of `var` to `coef` in the objective."""
133
134    @abc.abstractmethod
135    def get_linear_coefficient(self, var: variables.Variable) -> float:
136        """Returns the coefficinet of `var` (or zero if unset)."""
137
138    @abc.abstractmethod
139    def linear_terms(self) -> Iterator[variables.LinearTerm]:
140        """Yields variable coefficient pairs for variables with nonzero objective coefficient in undefined order."""
141
142    @abc.abstractmethod
143    def set_quadratic_coefficient(
144        self,
145        first_variable: variables.Variable,
146        second_variable: variables.Variable,
147        coef: float,
148    ) -> None:
149        """Sets the coefficient for product of variables (see class description)."""
150
151    @abc.abstractmethod
152    def get_quadratic_coefficient(
153        self,
154        first_variable: variables.Variable,
155        second_variable: variables.Variable,
156    ) -> float:
157        """Gets the coefficient for product of variables (see class description)."""
158
159    @abc.abstractmethod
160    def quadratic_terms(self) -> Iterator[variables.QuadraticTerm]:
161        """Yields quadratic terms with nonzero objective coefficient in undefined order."""
162
163    @abc.abstractmethod
164    def clear(self) -> None:
165        """Clears objective coefficients and offset. Does not change direction."""
166
167    def as_linear_expression(self) -> variables.LinearExpression:
168        """Returns an equivalent LinearExpression, or errors if quadratic."""
169        if any(self.quadratic_terms()):
170            raise TypeError("Cannot get a quadratic objective as a linear expression")
171        return variables.as_flat_linear_expression(
172            self.offset + variables.LinearSum(self.linear_terms())
173        )
174
175    def as_quadratic_expression(self) -> variables.QuadraticExpression:
176        """Returns an equivalent QuadraticExpression to this objetive."""
177        return variables.as_flat_quadratic_expression(
178            self.offset
179            + variables.LinearSum(self.linear_terms())
180            + variables.QuadraticSum(self.quadratic_terms())
181        )
182
183    def add(self, objective: variables.QuadraticTypes) -> None:
184        """Adds the provided expression `objective` to the objective function.
185
186        For a compile time guarantee that the objective remains linear, use
187        add_linear() instead.
188
189        Args:
190          objective: the expression to add to the objective function.
191        """
192        if isinstance(objective, (variables.LinearBase, int, float)):
193            self.add_linear(objective)
194        elif isinstance(objective, variables.QuadraticBase):
195            self.add_quadratic(objective)
196        else:
197            raise TypeError(
198                "unsupported type in objective argument for "
199                f"Objective.add(): {type(objective).__name__!r}"
200            )
201
202    def add_linear(self, objective: variables.LinearTypes) -> None:
203        """Adds the provided linear expression `objective` to the objective function."""
204        if not isinstance(objective, (variables.LinearBase, int, float)):
205            raise TypeError(
206                "unsupported type in objective argument for "
207                f"Objective.add_linear(): {type(objective).__name__!r}"
208            )
209        objective_expr = variables.as_flat_linear_expression(objective)
210        self.offset += objective_expr.offset
211        for var, coefficient in objective_expr.terms.items():
212            self.set_linear_coefficient(
213                var, self.get_linear_coefficient(var) + coefficient
214            )
215
216    def add_quadratic(self, objective: variables.QuadraticTypes) -> None:
217        """Adds the provided quadratic expression `objective` to the objective function."""
218        if not isinstance(
219            objective, (variables.QuadraticBase, variables.LinearBase, int, float)
220        ):
221            raise TypeError(
222                "unsupported type in objective argument for "
223                f"Objective.add(): {type(objective).__name__!r}"
224            )
225        objective_expr = variables.as_flat_quadratic_expression(objective)
226        self.offset += objective_expr.offset
227        for var, coefficient in objective_expr.linear_terms.items():
228            self.set_linear_coefficient(
229                var, self.get_linear_coefficient(var) + coefficient
230            )
231        for key, coefficient in objective_expr.quadratic_terms.items():
232            self.set_quadratic_coefficient(
233                key.first_var,
234                key.second_var,
235                self.get_quadratic_coefficient(key.first_var, key.second_var)
236                + coefficient,
237            )
238
239    def set_to_linear_expression(self, linear_expr: variables.LinearTypes) -> None:
240        """Sets the objective to optimize to `linear_expr`."""
241        if not isinstance(linear_expr, (variables.LinearBase, int, float)):
242            raise TypeError(
243                "unsupported type in objective argument for "
244                f"set_to_linear_expression: {type(linear_expr).__name__!r}"
245            )
246        self.clear()
247        objective_expr = variables.as_flat_linear_expression(linear_expr)
248        self.offset = objective_expr.offset
249        for var, coefficient in objective_expr.terms.items():
250            self.set_linear_coefficient(var, coefficient)
251
252    def set_to_quadratic_expression(
253        self, quadratic_expr: variables.QuadraticTypes
254    ) -> None:
255        """Sets the objective to optimize the `quadratic_expr`."""
256        if not isinstance(
257            quadratic_expr,
258            (variables.QuadraticBase, variables.LinearBase, int, float),
259        ):
260            raise TypeError(
261                "unsupported type in objective argument for "
262                f"set_to_quadratic_expression: {type(quadratic_expr).__name__!r}"
263            )
264        self.clear()
265        objective_expr = variables.as_flat_quadratic_expression(quadratic_expr)
266        self.offset = objective_expr.offset
267        for var, coefficient in objective_expr.linear_terms.items():
268            self.set_linear_coefficient(var, coefficient)
269        for quad_key, coefficient in objective_expr.quadratic_terms.items():
270            self.set_quadratic_coefficient(
271                quad_key.first_var, quad_key.second_var, coefficient
272            )
273
274    def set_to_expression(self, expr: variables.QuadraticTypes) -> None:
275        """Sets the objective to optimize the `expr`."""
276        if isinstance(expr, (variables.LinearBase, int, float)):
277            self.set_to_linear_expression(expr)
278        elif isinstance(expr, variables.QuadraticBase):
279            self.set_to_quadratic_expression(expr)
280        else:
281            raise TypeError(
282                "unsupported type in objective argument for "
283                f"set_to_expression: {type(expr).__name__!r}"
284            )

The objective for an optimization model.

An objective is either of the form:

min o + sum_{i in I} c_i * x_i + sum_{i, j in I, i <= j} q_i,j * x_i * x_j

or max o + sum_{i in I} c_i * x_i + sum_{(i, j) in Q} q_i,j * x_i * x_j where x_i are the decision variables of the problem and where all pairs (i, j) in Q satisfy i <= j. The values of o, c_i and q_i,j should be finite and not NaN.

The objective can be configured as follows:
  • offset: a float property, o above. Should be finite and not NaN.
  • is_maximize: a bool property, if the objective is to maximize or minimize.
  • set_linear_coefficient and get_linear_coefficient control the c_i * x_i terms. The variables must be from the same model as this objective, and the c_i must be finite and not NaN. The coefficient for any variable not set is 0.0, and setting a coefficient to 0.0 removes it from I above.
  • set_quadratic_coefficient and get_quadratic_coefficient control the q_i,j * x_i * x_j terms. The variables must be from the same model as this objective, and the q_i,j must be finite and not NaN. The coefficient for any pair of variables not set is 0.0, and setting a coefficient to 0.0 removes the associated (i,j) from Q above.

Do not create an Objective directly, use Model.objective to access the objective instead (or Model.add_auxiliary_objective()). Two Objective objects can represent the same objective (for the same model). They will have the same underlying Objective.elemental for storing the data. The Objective class is simply a reference to an Elemental.

The objective is linear if only linear coefficients are set. This can be useful to avoid solve-time errors with solvers that do not accept quadratic objectives. To facilitate this linear objective guarantee we provide three functions to add to the objective:

  • add(), which accepts linear or quadratic expressions,
  • add_quadratic(), which also accepts linear or quadratic expressions and can be used to signal a quadratic objective is possible, and
  • add_linear(), which only accepts linear expressions and can be used to guarantee the objective remains linear.

For quadratic terms, the order that variables are provided does not matter, we always canonicalize to first_var <= second_var. So if you set (x1, x2) to 7 then:

  • getting (x2, x1) returns 7
  • setting (x2, x1) to 10 overwrites the value of 7. Likewise, when we return nonzero quadratic coefficients, we always use the form first_var <= second_var.

Most problems have only a single objective, but hierarchical objectives are supported (see Model.add_auxiliary_objective()). Note that quadratic Auxiliary objectives are not supported.

81    def __init__(self, elem: elemental.Elemental) -> None:
82        """Do not invoke directly, prefer Model.objective."""
83        self._elemental: elemental.Elemental = elem

Do not invoke directly, prefer Model.objective.

85    @property
86    def elemental(self) -> elemental.Elemental:
87        """The underlying data structure for the model, for internal use only."""
88        return self._elemental

The underlying data structure for the model, for internal use only.

name: str
90    @property
91    @abc.abstractmethod
92    def name(self) -> str:
93        """The immutable name of this objective, for display only."""

The immutable name of this objective, for display only.

is_maximize: bool
95    @property
96    @abc.abstractmethod
97    def is_maximize(self) -> bool:
98        """If true, the direction is maximization, otherwise minimization."""

If true, the direction is maximization, otherwise minimization.

offset: float
104    @property
105    @abc.abstractmethod
106    def offset(self) -> float:
107        """A constant added to the objective."""

A constant added to the objective.

priority: int
113    @property
114    @abc.abstractmethod
115    def priority(self) -> int:
116        """For hierarchical problems, determines the order to apply objectives.
117
118        The objectives are applied from lowest priority to highest.
119
120        The default priority for the primary objective is zero, and auxiliary
121        objectives must specific a priority at creation time.
122
123        Priority has no effect for problems with only one objective.
124        """

For hierarchical problems, determines the order to apply objectives.

The objectives are applied from lowest priority to highest.

The default priority for the primary objective is zero, and auxiliary objectives must specific a priority at creation time.

Priority has no effect for problems with only one objective.

@abc.abstractmethod
def set_linear_coefficient( self, var: ortools.math_opt.python.variables.Variable, coef: float) -> None:
130    @abc.abstractmethod
131    def set_linear_coefficient(self, var: variables.Variable, coef: float) -> None:
132        """Sets the coefficient of `var` to `coef` in the objective."""

Sets the coefficient of var to coef in the objective.

@abc.abstractmethod
def get_linear_coefficient(self, var: ortools.math_opt.python.variables.Variable) -> float:
134    @abc.abstractmethod
135    def get_linear_coefficient(self, var: variables.Variable) -> float:
136        """Returns the coefficinet of `var` (or zero if unset)."""

Returns the coefficinet of var (or zero if unset).

@abc.abstractmethod
def linear_terms(self) -> Iterator[ortools.math_opt.python.variables.LinearTerm]:
138    @abc.abstractmethod
139    def linear_terms(self) -> Iterator[variables.LinearTerm]:
140        """Yields variable coefficient pairs for variables with nonzero objective coefficient in undefined order."""

Yields variable coefficient pairs for variables with nonzero objective coefficient in undefined order.

@abc.abstractmethod
def set_quadratic_coefficient( self, first_variable: ortools.math_opt.python.variables.Variable, second_variable: ortools.math_opt.python.variables.Variable, coef: float) -> None:
142    @abc.abstractmethod
143    def set_quadratic_coefficient(
144        self,
145        first_variable: variables.Variable,
146        second_variable: variables.Variable,
147        coef: float,
148    ) -> None:
149        """Sets the coefficient for product of variables (see class description)."""

Sets the coefficient for product of variables (see class description).

@abc.abstractmethod
def get_quadratic_coefficient( self, first_variable: ortools.math_opt.python.variables.Variable, second_variable: ortools.math_opt.python.variables.Variable) -> float:
151    @abc.abstractmethod
152    def get_quadratic_coefficient(
153        self,
154        first_variable: variables.Variable,
155        second_variable: variables.Variable,
156    ) -> float:
157        """Gets the coefficient for product of variables (see class description)."""

Gets the coefficient for product of variables (see class description).

@abc.abstractmethod
def quadratic_terms(self) -> Iterator[ortools.math_opt.python.variables.QuadraticTerm]:
159    @abc.abstractmethod
160    def quadratic_terms(self) -> Iterator[variables.QuadraticTerm]:
161        """Yields quadratic terms with nonzero objective coefficient in undefined order."""

Yields quadratic terms with nonzero objective coefficient in undefined order.

@abc.abstractmethod
def clear(self) -> None:
163    @abc.abstractmethod
164    def clear(self) -> None:
165        """Clears objective coefficients and offset. Does not change direction."""

Clears objective coefficients and offset. Does not change direction.

def as_linear_expression(self) -> ortools.math_opt.python.variables.LinearExpression:
167    def as_linear_expression(self) -> variables.LinearExpression:
168        """Returns an equivalent LinearExpression, or errors if quadratic."""
169        if any(self.quadratic_terms()):
170            raise TypeError("Cannot get a quadratic objective as a linear expression")
171        return variables.as_flat_linear_expression(
172            self.offset + variables.LinearSum(self.linear_terms())
173        )

Returns an equivalent LinearExpression, or errors if quadratic.

def as_quadratic_expression(self) -> ortools.math_opt.python.variables.QuadraticExpression:
175    def as_quadratic_expression(self) -> variables.QuadraticExpression:
176        """Returns an equivalent QuadraticExpression to this objetive."""
177        return variables.as_flat_quadratic_expression(
178            self.offset
179            + variables.LinearSum(self.linear_terms())
180            + variables.QuadraticSum(self.quadratic_terms())
181        )

Returns an equivalent QuadraticExpression to this objetive.

def add( self, objective: Union[int, float, ForwardRef('LinearBase'), ForwardRef('QuadraticBase')]) -> None:
183    def add(self, objective: variables.QuadraticTypes) -> None:
184        """Adds the provided expression `objective` to the objective function.
185
186        For a compile time guarantee that the objective remains linear, use
187        add_linear() instead.
188
189        Args:
190          objective: the expression to add to the objective function.
191        """
192        if isinstance(objective, (variables.LinearBase, int, float)):
193            self.add_linear(objective)
194        elif isinstance(objective, variables.QuadraticBase):
195            self.add_quadratic(objective)
196        else:
197            raise TypeError(
198                "unsupported type in objective argument for "
199                f"Objective.add(): {type(objective).__name__!r}"
200            )

Adds the provided expression objective to the objective function.

For a compile time guarantee that the objective remains linear, use add_linear() instead.

Arguments:
  • objective: the expression to add to the objective function.
def add_linear(self, objective: Union[int, float, ForwardRef('LinearBase')]) -> None:
202    def add_linear(self, objective: variables.LinearTypes) -> None:
203        """Adds the provided linear expression `objective` to the objective function."""
204        if not isinstance(objective, (variables.LinearBase, int, float)):
205            raise TypeError(
206                "unsupported type in objective argument for "
207                f"Objective.add_linear(): {type(objective).__name__!r}"
208            )
209        objective_expr = variables.as_flat_linear_expression(objective)
210        self.offset += objective_expr.offset
211        for var, coefficient in objective_expr.terms.items():
212            self.set_linear_coefficient(
213                var, self.get_linear_coefficient(var) + coefficient
214            )

Adds the provided linear expression objective to the objective function.

def add_quadratic( self, objective: Union[int, float, ForwardRef('LinearBase'), ForwardRef('QuadraticBase')]) -> None:
216    def add_quadratic(self, objective: variables.QuadraticTypes) -> None:
217        """Adds the provided quadratic expression `objective` to the objective function."""
218        if not isinstance(
219            objective, (variables.QuadraticBase, variables.LinearBase, int, float)
220        ):
221            raise TypeError(
222                "unsupported type in objective argument for "
223                f"Objective.add(): {type(objective).__name__!r}"
224            )
225        objective_expr = variables.as_flat_quadratic_expression(objective)
226        self.offset += objective_expr.offset
227        for var, coefficient in objective_expr.linear_terms.items():
228            self.set_linear_coefficient(
229                var, self.get_linear_coefficient(var) + coefficient
230            )
231        for key, coefficient in objective_expr.quadratic_terms.items():
232            self.set_quadratic_coefficient(
233                key.first_var,
234                key.second_var,
235                self.get_quadratic_coefficient(key.first_var, key.second_var)
236                + coefficient,
237            )

Adds the provided quadratic expression objective to the objective function.

def set_to_linear_expression(self, linear_expr: Union[int, float, ForwardRef('LinearBase')]) -> None:
239    def set_to_linear_expression(self, linear_expr: variables.LinearTypes) -> None:
240        """Sets the objective to optimize to `linear_expr`."""
241        if not isinstance(linear_expr, (variables.LinearBase, int, float)):
242            raise TypeError(
243                "unsupported type in objective argument for "
244                f"set_to_linear_expression: {type(linear_expr).__name__!r}"
245            )
246        self.clear()
247        objective_expr = variables.as_flat_linear_expression(linear_expr)
248        self.offset = objective_expr.offset
249        for var, coefficient in objective_expr.terms.items():
250            self.set_linear_coefficient(var, coefficient)

Sets the objective to optimize to linear_expr.

def set_to_quadratic_expression( self, quadratic_expr: Union[int, float, ForwardRef('LinearBase'), ForwardRef('QuadraticBase')]) -> None:
252    def set_to_quadratic_expression(
253        self, quadratic_expr: variables.QuadraticTypes
254    ) -> None:
255        """Sets the objective to optimize the `quadratic_expr`."""
256        if not isinstance(
257            quadratic_expr,
258            (variables.QuadraticBase, variables.LinearBase, int, float),
259        ):
260            raise TypeError(
261                "unsupported type in objective argument for "
262                f"set_to_quadratic_expression: {type(quadratic_expr).__name__!r}"
263            )
264        self.clear()
265        objective_expr = variables.as_flat_quadratic_expression(quadratic_expr)
266        self.offset = objective_expr.offset
267        for var, coefficient in objective_expr.linear_terms.items():
268            self.set_linear_coefficient(var, coefficient)
269        for quad_key, coefficient in objective_expr.quadratic_terms.items():
270            self.set_quadratic_coefficient(
271                quad_key.first_var, quad_key.second_var, coefficient
272            )

Sets the objective to optimize the quadratic_expr.

def set_to_expression( self, expr: Union[int, float, ForwardRef('LinearBase'), ForwardRef('QuadraticBase')]) -> None:
274    def set_to_expression(self, expr: variables.QuadraticTypes) -> None:
275        """Sets the objective to optimize the `expr`."""
276        if isinstance(expr, (variables.LinearBase, int, float)):
277            self.set_to_linear_expression(expr)
278        elif isinstance(expr, variables.QuadraticBase):
279            self.set_to_quadratic_expression(expr)
280        else:
281            raise TypeError(
282                "unsupported type in objective argument for "
283                f"set_to_expression: {type(expr).__name__!r}"
284            )

Sets the objective to optimize the expr.

class PrimaryObjective(Objective):
287class PrimaryObjective(Objective):
288    """The main objective, but users should program against Objective directly."""
289
290    __slots__ = ()
291
292    @property
293    def name(self) -> str:
294        return self._elemental.primary_objective_name
295
296    @property
297    def is_maximize(self) -> bool:
298        return self._elemental.get_attr(enums.BoolAttr0.MAXIMIZE, ())
299
300    @is_maximize.setter
301    def is_maximize(self, is_maximize: bool) -> None:
302        self._elemental.set_attr(enums.BoolAttr0.MAXIMIZE, (), is_maximize)
303
304    @property
305    def offset(self) -> float:
306        return self._elemental.get_attr(enums.DoubleAttr0.OBJECTIVE_OFFSET, ())
307
308    @offset.setter
309    def offset(self, value: float) -> None:
310        self._elemental.set_attr(enums.DoubleAttr0.OBJECTIVE_OFFSET, (), value)
311
312    @property
313    def priority(self) -> int:
314        return self._elemental.get_attr(enums.IntAttr0.OBJECTIVE_PRIORITY, ())
315
316    @priority.setter
317    def priority(self, value: int) -> None:
318        self._elemental.set_attr(enums.IntAttr0.OBJECTIVE_PRIORITY, (), value)
319
320    def set_linear_coefficient(self, var: variables.Variable, coef: float) -> None:
321        from_model.model_is_same(self, var)
322        self._elemental.set_attr(
323            enums.DoubleAttr1.OBJECTIVE_LINEAR_COEFFICIENT, (var.id,), coef
324        )
325
326    def get_linear_coefficient(self, var: variables.Variable) -> float:
327        from_model.model_is_same(self, var)
328        return self._elemental.get_attr(
329            enums.DoubleAttr1.OBJECTIVE_LINEAR_COEFFICIENT, (var.id,)
330        )
331
332    def linear_terms(self) -> Iterator[variables.LinearTerm]:
333        keys = self._elemental.get_attr_non_defaults(
334            enums.DoubleAttr1.OBJECTIVE_LINEAR_COEFFICIENT
335        )
336        var_index = 0
337        coefs = self._elemental.get_attrs(
338            enums.DoubleAttr1.OBJECTIVE_LINEAR_COEFFICIENT, keys
339        )
340        for i in range(len(keys)):
341            yield variables.LinearTerm(
342                variable=variables.Variable(self._elemental, int(keys[i, var_index])),
343                coefficient=float(coefs[i]),
344            )
345
346    def set_quadratic_coefficient(
347        self,
348        first_variable: variables.Variable,
349        second_variable: variables.Variable,
350        coef: float,
351    ) -> None:
352        from_model.model_is_same(self, first_variable)
353        from_model.model_is_same(self, second_variable)
354        self._elemental.set_attr(
355            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT,
356            (first_variable.id, second_variable.id),
357            coef,
358        )
359
360    def get_quadratic_coefficient(
361        self,
362        first_variable: variables.Variable,
363        second_variable: variables.Variable,
364    ) -> float:
365        from_model.model_is_same(self, first_variable)
366        from_model.model_is_same(self, second_variable)
367        return self._elemental.get_attr(
368            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT,
369            (first_variable.id, second_variable.id),
370        )
371
372    def quadratic_terms(self) -> Iterator[variables.QuadraticTerm]:
373        keys = self._elemental.get_attr_non_defaults(
374            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT
375        )
376        coefs = self._elemental.get_attrs(
377            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT, keys
378        )
379        for i in range(len(keys)):
380            yield variables.QuadraticTerm(
381                variables.QuadraticTermKey(
382                    variables.Variable(self._elemental, int(keys[i, 0])),
383                    variables.Variable(self._elemental, int(keys[i, 1])),
384                ),
385                coefficient=float(coefs[i]),
386            )
387
388    def clear(self) -> None:
389        self._elemental.clear_attr(enums.DoubleAttr0.OBJECTIVE_OFFSET)
390        self._elemental.clear_attr(enums.DoubleAttr1.OBJECTIVE_LINEAR_COEFFICIENT)
391        self._elemental.clear_attr(
392            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT
393        )
394
395    def __eq__(self, other: Any) -> bool:
396        if isinstance(other, PrimaryObjective):
397            return self._elemental is other._elemental
398        return False
399
400    def __hash__(self) -> int:
401        return hash(self._elemental)

The main objective, but users should program against Objective directly.

name: str
292    @property
293    def name(self) -> str:
294        return self._elemental.primary_objective_name

The immutable name of this objective, for display only.

is_maximize: bool
296    @property
297    def is_maximize(self) -> bool:
298        return self._elemental.get_attr(enums.BoolAttr0.MAXIMIZE, ())

If true, the direction is maximization, otherwise minimization.

offset: float
304    @property
305    def offset(self) -> float:
306        return self._elemental.get_attr(enums.DoubleAttr0.OBJECTIVE_OFFSET, ())

A constant added to the objective.

priority: int
312    @property
313    def priority(self) -> int:
314        return self._elemental.get_attr(enums.IntAttr0.OBJECTIVE_PRIORITY, ())

For hierarchical problems, determines the order to apply objectives.

The objectives are applied from lowest priority to highest.

The default priority for the primary objective is zero, and auxiliary objectives must specific a priority at creation time.

Priority has no effect for problems with only one objective.

def set_linear_coefficient( self, var: ortools.math_opt.python.variables.Variable, coef: float) -> None:
320    def set_linear_coefficient(self, var: variables.Variable, coef: float) -> None:
321        from_model.model_is_same(self, var)
322        self._elemental.set_attr(
323            enums.DoubleAttr1.OBJECTIVE_LINEAR_COEFFICIENT, (var.id,), coef
324        )

Sets the coefficient of var to coef in the objective.

def get_linear_coefficient(self, var: ortools.math_opt.python.variables.Variable) -> float:
326    def get_linear_coefficient(self, var: variables.Variable) -> float:
327        from_model.model_is_same(self, var)
328        return self._elemental.get_attr(
329            enums.DoubleAttr1.OBJECTIVE_LINEAR_COEFFICIENT, (var.id,)
330        )

Returns the coefficinet of var (or zero if unset).

def linear_terms(self) -> Iterator[ortools.math_opt.python.variables.LinearTerm]:
332    def linear_terms(self) -> Iterator[variables.LinearTerm]:
333        keys = self._elemental.get_attr_non_defaults(
334            enums.DoubleAttr1.OBJECTIVE_LINEAR_COEFFICIENT
335        )
336        var_index = 0
337        coefs = self._elemental.get_attrs(
338            enums.DoubleAttr1.OBJECTIVE_LINEAR_COEFFICIENT, keys
339        )
340        for i in range(len(keys)):
341            yield variables.LinearTerm(
342                variable=variables.Variable(self._elemental, int(keys[i, var_index])),
343                coefficient=float(coefs[i]),
344            )

Yields variable coefficient pairs for variables with nonzero objective coefficient in undefined order.

def set_quadratic_coefficient( self, first_variable: ortools.math_opt.python.variables.Variable, second_variable: ortools.math_opt.python.variables.Variable, coef: float) -> None:
346    def set_quadratic_coefficient(
347        self,
348        first_variable: variables.Variable,
349        second_variable: variables.Variable,
350        coef: float,
351    ) -> None:
352        from_model.model_is_same(self, first_variable)
353        from_model.model_is_same(self, second_variable)
354        self._elemental.set_attr(
355            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT,
356            (first_variable.id, second_variable.id),
357            coef,
358        )

Sets the coefficient for product of variables (see class description).

def get_quadratic_coefficient( self, first_variable: ortools.math_opt.python.variables.Variable, second_variable: ortools.math_opt.python.variables.Variable) -> float:
360    def get_quadratic_coefficient(
361        self,
362        first_variable: variables.Variable,
363        second_variable: variables.Variable,
364    ) -> float:
365        from_model.model_is_same(self, first_variable)
366        from_model.model_is_same(self, second_variable)
367        return self._elemental.get_attr(
368            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT,
369            (first_variable.id, second_variable.id),
370        )

Gets the coefficient for product of variables (see class description).

def quadratic_terms(self) -> Iterator[ortools.math_opt.python.variables.QuadraticTerm]:
372    def quadratic_terms(self) -> Iterator[variables.QuadraticTerm]:
373        keys = self._elemental.get_attr_non_defaults(
374            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT
375        )
376        coefs = self._elemental.get_attrs(
377            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT, keys
378        )
379        for i in range(len(keys)):
380            yield variables.QuadraticTerm(
381                variables.QuadraticTermKey(
382                    variables.Variable(self._elemental, int(keys[i, 0])),
383                    variables.Variable(self._elemental, int(keys[i, 1])),
384                ),
385                coefficient=float(coefs[i]),
386            )

Yields quadratic terms with nonzero objective coefficient in undefined order.

def clear(self) -> None:
388    def clear(self) -> None:
389        self._elemental.clear_attr(enums.DoubleAttr0.OBJECTIVE_OFFSET)
390        self._elemental.clear_attr(enums.DoubleAttr1.OBJECTIVE_LINEAR_COEFFICIENT)
391        self._elemental.clear_attr(
392            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT
393        )

Clears objective coefficients and offset. Does not change direction.

class AuxiliaryObjective(Objective):
404class AuxiliaryObjective(Objective):
405    """An additional objective that can be optimized after objectives."""
406
407    __slots__ = ("_id",)
408
409    def __init__(self, elem: elemental.Elemental, obj_id: int) -> None:
410        """Internal only, prefer Model functions (add_auxiliary_objective() and get_auxiliary_objective())."""
411        super().__init__(elem)
412        if not isinstance(obj_id, int):
413            raise TypeError(
414                f"obj_id type should be int, was: {type(obj_id).__name__!r}"
415            )
416        self._id: int = obj_id
417
418    @property
419    def name(self) -> str:
420        return self._elemental.get_element_name(
421            enums.ElementType.AUXILIARY_OBJECTIVE, self._id
422        )
423
424    @property
425    def id(self) -> int:
426        """Returns the id of this objective."""
427        return self._id
428
429    @property
430    def is_maximize(self) -> bool:
431        return self._elemental.get_attr(
432            enums.BoolAttr1.AUXILIARY_OBJECTIVE_MAXIMIZE, (self._id,)
433        )
434
435    @is_maximize.setter
436    def is_maximize(self, is_maximize: bool) -> None:
437        self._elemental.set_attr(
438            enums.BoolAttr1.AUXILIARY_OBJECTIVE_MAXIMIZE,
439            (self._id,),
440            is_maximize,
441        )
442
443    @property
444    def offset(self) -> float:
445        return self._elemental.get_attr(
446            enums.DoubleAttr1.AUXILIARY_OBJECTIVE_OFFSET, (self._id,)
447        )
448
449    @offset.setter
450    def offset(self, value: float) -> None:
451        self._elemental.set_attr(
452            enums.DoubleAttr1.AUXILIARY_OBJECTIVE_OFFSET,
453            (self._id,),
454            value,
455        )
456
457    @property
458    def priority(self) -> int:
459        return self._elemental.get_attr(
460            enums.IntAttr1.AUXILIARY_OBJECTIVE_PRIORITY, (self._id,)
461        )
462
463    @priority.setter
464    def priority(self, value: int) -> None:
465        self._elemental.set_attr(
466            enums.IntAttr1.AUXILIARY_OBJECTIVE_PRIORITY,
467            (self._id,),
468            value,
469        )
470
471    def set_linear_coefficient(self, var: variables.Variable, coef: float) -> None:
472        from_model.model_is_same(self, var)
473        self._elemental.set_attr(
474            enums.DoubleAttr2.AUXILIARY_OBJECTIVE_LINEAR_COEFFICIENT,
475            (self._id, var.id),
476            coef,
477        )
478
479    def get_linear_coefficient(self, var: variables.Variable) -> float:
480        from_model.model_is_same(self, var)
481        return self._elemental.get_attr(
482            enums.DoubleAttr2.AUXILIARY_OBJECTIVE_LINEAR_COEFFICIENT,
483            (
484                self._id,
485                var.id,
486            ),
487        )
488
489    def linear_terms(self) -> Iterator[variables.LinearTerm]:
490        keys = self._elemental.slice_attr(
491            enums.DoubleAttr2.AUXILIARY_OBJECTIVE_LINEAR_COEFFICIENT,
492            0,
493            self._id,
494        )
495        var_index = 1
496        coefs = self._elemental.get_attrs(
497            enums.DoubleAttr2.AUXILIARY_OBJECTIVE_LINEAR_COEFFICIENT, keys
498        )
499        for i in range(len(keys)):
500            yield variables.LinearTerm(
501                variable=variables.Variable(self._elemental, int(keys[i, var_index])),
502                coefficient=float(coefs[i]),
503            )
504
505    def set_quadratic_coefficient(
506        self,
507        first_variable: variables.Variable,
508        second_variable: variables.Variable,
509        coef: float,
510    ) -> None:
511        raise ValueError("Quadratic auxiliary objectives are not supported.")
512
513    def get_quadratic_coefficient(
514        self,
515        first_variable: variables.Variable,
516        second_variable: variables.Variable,
517    ) -> float:
518        from_model.model_is_same(self, first_variable)
519        from_model.model_is_same(self, second_variable)
520        if not self._elemental.element_exists(
521            enums.ElementType.VARIABLE, first_variable.id
522        ):
523            raise ValueError(f"Variable {first_variable} does not exist")
524        if not self._elemental.element_exists(
525            enums.ElementType.VARIABLE, second_variable.id
526        ):
527            raise ValueError(f"Variable {second_variable} does not exist")
528        return 0.0
529
530    def quadratic_terms(self) -> Iterator[variables.QuadraticTerm]:
531        return iter(())
532
533    def clear(self) -> None:
534        """Clears objective coefficients and offset. Does not change direction."""
535        self._elemental.clear_attr(enums.DoubleAttr1.AUXILIARY_OBJECTIVE_OFFSET)
536        self._elemental.clear_attr(
537            enums.DoubleAttr2.AUXILIARY_OBJECTIVE_LINEAR_COEFFICIENT
538        )
539
540    def __eq__(self, other: Any) -> bool:
541        if isinstance(other, AuxiliaryObjective):
542            return self._elemental is other._elemental and self._id == other._id
543        return False
544
545    def __hash__(self) -> int:
546        return hash((self._elemental, self._id))

An additional objective that can be optimized after objectives.

AuxiliaryObjective( elem: ortools.math_opt.python.elemental.elemental.Elemental, obj_id: int)
409    def __init__(self, elem: elemental.Elemental, obj_id: int) -> None:
410        """Internal only, prefer Model functions (add_auxiliary_objective() and get_auxiliary_objective())."""
411        super().__init__(elem)
412        if not isinstance(obj_id, int):
413            raise TypeError(
414                f"obj_id type should be int, was: {type(obj_id).__name__!r}"
415            )
416        self._id: int = obj_id

Internal only, prefer Model functions (add_auxiliary_objective() and get_auxiliary_objective()).

name: str
418    @property
419    def name(self) -> str:
420        return self._elemental.get_element_name(
421            enums.ElementType.AUXILIARY_OBJECTIVE, self._id
422        )

The immutable name of this objective, for display only.

id: int
424    @property
425    def id(self) -> int:
426        """Returns the id of this objective."""
427        return self._id

Returns the id of this objective.

is_maximize: bool
429    @property
430    def is_maximize(self) -> bool:
431        return self._elemental.get_attr(
432            enums.BoolAttr1.AUXILIARY_OBJECTIVE_MAXIMIZE, (self._id,)
433        )

If true, the direction is maximization, otherwise minimization.

offset: float
443    @property
444    def offset(self) -> float:
445        return self._elemental.get_attr(
446            enums.DoubleAttr1.AUXILIARY_OBJECTIVE_OFFSET, (self._id,)
447        )

A constant added to the objective.

priority: int
457    @property
458    def priority(self) -> int:
459        return self._elemental.get_attr(
460            enums.IntAttr1.AUXILIARY_OBJECTIVE_PRIORITY, (self._id,)
461        )

For hierarchical problems, determines the order to apply objectives.

The objectives are applied from lowest priority to highest.

The default priority for the primary objective is zero, and auxiliary objectives must specific a priority at creation time.

Priority has no effect for problems with only one objective.

def set_linear_coefficient( self, var: ortools.math_opt.python.variables.Variable, coef: float) -> None:
471    def set_linear_coefficient(self, var: variables.Variable, coef: float) -> None:
472        from_model.model_is_same(self, var)
473        self._elemental.set_attr(
474            enums.DoubleAttr2.AUXILIARY_OBJECTIVE_LINEAR_COEFFICIENT,
475            (self._id, var.id),
476            coef,
477        )

Sets the coefficient of var to coef in the objective.

def get_linear_coefficient(self, var: ortools.math_opt.python.variables.Variable) -> float:
479    def get_linear_coefficient(self, var: variables.Variable) -> float:
480        from_model.model_is_same(self, var)
481        return self._elemental.get_attr(
482            enums.DoubleAttr2.AUXILIARY_OBJECTIVE_LINEAR_COEFFICIENT,
483            (
484                self._id,
485                var.id,
486            ),
487        )

Returns the coefficinet of var (or zero if unset).

def linear_terms(self) -> Iterator[ortools.math_opt.python.variables.LinearTerm]:
489    def linear_terms(self) -> Iterator[variables.LinearTerm]:
490        keys = self._elemental.slice_attr(
491            enums.DoubleAttr2.AUXILIARY_OBJECTIVE_LINEAR_COEFFICIENT,
492            0,
493            self._id,
494        )
495        var_index = 1
496        coefs = self._elemental.get_attrs(
497            enums.DoubleAttr2.AUXILIARY_OBJECTIVE_LINEAR_COEFFICIENT, keys
498        )
499        for i in range(len(keys)):
500            yield variables.LinearTerm(
501                variable=variables.Variable(self._elemental, int(keys[i, var_index])),
502                coefficient=float(coefs[i]),
503            )

Yields variable coefficient pairs for variables with nonzero objective coefficient in undefined order.

def set_quadratic_coefficient( self, first_variable: ortools.math_opt.python.variables.Variable, second_variable: ortools.math_opt.python.variables.Variable, coef: float) -> None:
505    def set_quadratic_coefficient(
506        self,
507        first_variable: variables.Variable,
508        second_variable: variables.Variable,
509        coef: float,
510    ) -> None:
511        raise ValueError("Quadratic auxiliary objectives are not supported.")

Sets the coefficient for product of variables (see class description).

def get_quadratic_coefficient( self, first_variable: ortools.math_opt.python.variables.Variable, second_variable: ortools.math_opt.python.variables.Variable) -> float:
513    def get_quadratic_coefficient(
514        self,
515        first_variable: variables.Variable,
516        second_variable: variables.Variable,
517    ) -> float:
518        from_model.model_is_same(self, first_variable)
519        from_model.model_is_same(self, second_variable)
520        if not self._elemental.element_exists(
521            enums.ElementType.VARIABLE, first_variable.id
522        ):
523            raise ValueError(f"Variable {first_variable} does not exist")
524        if not self._elemental.element_exists(
525            enums.ElementType.VARIABLE, second_variable.id
526        ):
527            raise ValueError(f"Variable {second_variable} does not exist")
528        return 0.0

Gets the coefficient for product of variables (see class description).

def quadratic_terms(self) -> Iterator[ortools.math_opt.python.variables.QuadraticTerm]:
530    def quadratic_terms(self) -> Iterator[variables.QuadraticTerm]:
531        return iter(())

Yields quadratic terms with nonzero objective coefficient in undefined order.

def clear(self) -> None:
533    def clear(self) -> None:
534        """Clears objective coefficients and offset. Does not change direction."""
535        self._elemental.clear_attr(enums.DoubleAttr1.AUXILIARY_OBJECTIVE_OFFSET)
536        self._elemental.clear_attr(
537            enums.DoubleAttr2.AUXILIARY_OBJECTIVE_LINEAR_COEFFICIENT
538        )

Clears objective coefficients and offset. Does not change direction.