ortools.math_opt.python.objectives

An Objective for a MathOpt optimization model.

  1#!/usr/bin/env python3
  2# Copyright 2010-2025 Google LLC
  3# Licensed under the Apache License, Version 2.0 (the "License");
  4# you may not use this file except in compliance with the License.
  5# You may obtain a copy of the License at
  6#
  7#     http://www.apache.org/licenses/LICENSE-2.0
  8#
  9# Unless required by applicable law or agreed to in writing, software
 10# distributed under the License is distributed on an "AS IS" BASIS,
 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12# See the License for the specific language governing permissions and
 13# limitations under the License.
 14
 15"""An Objective for a MathOpt optimization model."""
 16
 17import abc
 18from typing import Any, Iterator
 19
 20from ortools.math_opt.elemental.python import enums
 21from ortools.math_opt.python import from_model
 22from ortools.math_opt.python import variables
 23from ortools.math_opt.python.elemental import elemental
 24
 25
 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            )
285
286
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)
402
403
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))
class Objective(ortools.math_opt.python.from_model.FromModel):
 27class Objective(from_model.FromModel, metaclass=abc.ABCMeta):
 28    """The objective for an optimization model.
 29
 30    An objective is either of the form:
 31      min o + sum_{i in I} c_i * x_i + sum_{i, j in I, i <= j} q_i,j * x_i * x_j
 32    or
 33      max o + sum_{i in I} c_i * x_i + sum_{(i, j) in Q} q_i,j * x_i * x_j
 34    where x_i are the decision variables of the problem and where all pairs (i, j)
 35    in Q satisfy i <= j. The values of o, c_i and q_i,j should be finite and not
 36    NaN.
 37
 38    The objective can be configured as follows:
 39      * offset: a float property, o above. Should be finite and not NaN.
 40      * is_maximize: a bool property, if the objective is to maximize or minimize.
 41      * set_linear_coefficient and get_linear_coefficient control the c_i * x_i
 42        terms. The variables must be from the same model as this objective, and
 43        the c_i must be finite and not NaN. The coefficient for any variable not
 44        set is 0.0, and setting a coefficient to 0.0 removes it from I above.
 45      * set_quadratic_coefficient and get_quadratic_coefficient control the
 46        q_i,j * x_i * x_j terms. The variables must be from the same model as this
 47        objective, and the q_i,j must be finite and not NaN. The coefficient for
 48        any pair of variables not set is 0.0, and setting a coefficient to 0.0
 49        removes the associated (i,j) from Q above.
 50
 51    Do not create an Objective directly, use Model.objective to access the
 52    objective instead (or Model.add_auxiliary_objective()). Two Objective objects
 53    can represent the same objective (for the same model). They will have the same
 54    underlying Objective.elemental for storing the data. The Objective class is
 55    simply a reference to an Elemental.
 56
 57    The objective is linear if only linear coefficients are set. This can be
 58    useful to avoid solve-time errors with solvers that do not accept quadratic
 59    objectives. To facilitate this linear objective guarantee we provide three
 60    functions to add to the objective:
 61      * add(), which accepts linear or quadratic expressions,
 62      * add_quadratic(), which also accepts linear or quadratic expressions and
 63        can be used to signal a quadratic objective is possible, and
 64      * add_linear(), which only accepts linear expressions and can be used to
 65        guarantee the objective remains linear.
 66
 67    For quadratic terms, the order that variables are provided does not matter,
 68    we always canonicalize to first_var <= second_var. So if you set (x1, x2) to 7
 69    then:
 70      * getting (x2, x1) returns 7
 71      * setting (x2, x1) to 10 overwrites the value of 7.
 72    Likewise, when we return nonzero quadratic coefficients, we always use the
 73    form first_var <= second_var.
 74
 75    Most problems have only a single objective, but hierarchical objectives are
 76    supported (see Model.add_auxiliary_objective()). Note that quadratic Auxiliary
 77    objectives are not supported.
 78    """
 79
 80    __slots__ = ("_elemental",)
 81
 82    def __init__(self, elem: elemental.Elemental) -> None:
 83        """Do not invoke directly, prefer Model.objective."""
 84        self._elemental: elemental.Elemental = elem
 85
 86    @property
 87    def elemental(self) -> elemental.Elemental:
 88        """The underlying data structure for the model, for internal use only."""
 89        return self._elemental
 90
 91    @property
 92    @abc.abstractmethod
 93    def name(self) -> str:
 94        """The immutable name of this objective, for display only."""
 95
 96    @property
 97    @abc.abstractmethod
 98    def is_maximize(self) -> bool:
 99        """If true, the direction is maximization, otherwise minimization."""
100
101    @is_maximize.setter
102    @abc.abstractmethod
103    def is_maximize(self, is_maximize: bool) -> None: ...
104
105    @property
106    @abc.abstractmethod
107    def offset(self) -> float:
108        """A constant added to the objective."""
109
110    @offset.setter
111    @abc.abstractmethod
112    def offset(self, value: float) -> None: ...
113
114    @property
115    @abc.abstractmethod
116    def priority(self) -> int:
117        """For hierarchical problems, determines the order to apply objectives.
118
119        The objectives are applied from lowest priority to highest.
120
121        The default priority for the primary objective is zero, and auxiliary
122        objectives must specific a priority at creation time.
123
124        Priority has no effect for problems with only one objective.
125        """
126
127    @priority.setter
128    @abc.abstractmethod
129    def priority(self, value: int) -> None: ...
130
131    @abc.abstractmethod
132    def set_linear_coefficient(self, var: variables.Variable, coef: float) -> None:
133        """Sets the coefficient of `var` to `coef` in the objective."""
134
135    @abc.abstractmethod
136    def get_linear_coefficient(self, var: variables.Variable) -> float:
137        """Returns the coefficinet of `var` (or zero if unset)."""
138
139    @abc.abstractmethod
140    def linear_terms(self) -> Iterator[variables.LinearTerm]:
141        """Yields variable coefficient pairs for variables with nonzero objective coefficient in undefined order."""
142
143    @abc.abstractmethod
144    def set_quadratic_coefficient(
145        self,
146        first_variable: variables.Variable,
147        second_variable: variables.Variable,
148        coef: float,
149    ) -> None:
150        """Sets the coefficient for product of variables (see class description)."""
151
152    @abc.abstractmethod
153    def get_quadratic_coefficient(
154        self,
155        first_variable: variables.Variable,
156        second_variable: variables.Variable,
157    ) -> float:
158        """Gets the coefficient for product of variables (see class description)."""
159
160    @abc.abstractmethod
161    def quadratic_terms(self) -> Iterator[variables.QuadraticTerm]:
162        """Yields quadratic terms with nonzero objective coefficient in undefined order."""
163
164    @abc.abstractmethod
165    def clear(self) -> None:
166        """Clears objective coefficients and offset. Does not change direction."""
167
168    def as_linear_expression(self) -> variables.LinearExpression:
169        """Returns an equivalent LinearExpression, or errors if quadratic."""
170        if any(self.quadratic_terms()):
171            raise TypeError("Cannot get a quadratic objective as a linear expression")
172        return variables.as_flat_linear_expression(
173            self.offset + variables.LinearSum(self.linear_terms())
174        )
175
176    def as_quadratic_expression(self) -> variables.QuadraticExpression:
177        """Returns an equivalent QuadraticExpression to this objetive."""
178        return variables.as_flat_quadratic_expression(
179            self.offset
180            + variables.LinearSum(self.linear_terms())
181            + variables.QuadraticSum(self.quadratic_terms())
182        )
183
184    def add(self, objective: variables.QuadraticTypes) -> None:
185        """Adds the provided expression `objective` to the objective function.
186
187        For a compile time guarantee that the objective remains linear, use
188        add_linear() instead.
189
190        Args:
191          objective: the expression to add to the objective function.
192        """
193        if isinstance(objective, (variables.LinearBase, int, float)):
194            self.add_linear(objective)
195        elif isinstance(objective, variables.QuadraticBase):
196            self.add_quadratic(objective)
197        else:
198            raise TypeError(
199                "unsupported type in objective argument for "
200                f"Objective.add(): {type(objective).__name__!r}"
201            )
202
203    def add_linear(self, objective: variables.LinearTypes) -> None:
204        """Adds the provided linear expression `objective` to the objective function."""
205        if not isinstance(objective, (variables.LinearBase, int, float)):
206            raise TypeError(
207                "unsupported type in objective argument for "
208                f"Objective.add_linear(): {type(objective).__name__!r}"
209            )
210        objective_expr = variables.as_flat_linear_expression(objective)
211        self.offset += objective_expr.offset
212        for var, coefficient in objective_expr.terms.items():
213            self.set_linear_coefficient(
214                var, self.get_linear_coefficient(var) + coefficient
215            )
216
217    def add_quadratic(self, objective: variables.QuadraticTypes) -> None:
218        """Adds the provided quadratic expression `objective` to the objective function."""
219        if not isinstance(
220            objective, (variables.QuadraticBase, variables.LinearBase, int, float)
221        ):
222            raise TypeError(
223                "unsupported type in objective argument for "
224                f"Objective.add(): {type(objective).__name__!r}"
225            )
226        objective_expr = variables.as_flat_quadratic_expression(objective)
227        self.offset += objective_expr.offset
228        for var, coefficient in objective_expr.linear_terms.items():
229            self.set_linear_coefficient(
230                var, self.get_linear_coefficient(var) + coefficient
231            )
232        for key, coefficient in objective_expr.quadratic_terms.items():
233            self.set_quadratic_coefficient(
234                key.first_var,
235                key.second_var,
236                self.get_quadratic_coefficient(key.first_var, key.second_var)
237                + coefficient,
238            )
239
240    def set_to_linear_expression(self, linear_expr: variables.LinearTypes) -> None:
241        """Sets the objective to optimize to `linear_expr`."""
242        if not isinstance(linear_expr, (variables.LinearBase, int, float)):
243            raise TypeError(
244                "unsupported type in objective argument for "
245                f"set_to_linear_expression: {type(linear_expr).__name__!r}"
246            )
247        self.clear()
248        objective_expr = variables.as_flat_linear_expression(linear_expr)
249        self.offset = objective_expr.offset
250        for var, coefficient in objective_expr.terms.items():
251            self.set_linear_coefficient(var, coefficient)
252
253    def set_to_quadratic_expression(
254        self, quadratic_expr: variables.QuadraticTypes
255    ) -> None:
256        """Sets the objective to optimize the `quadratic_expr`."""
257        if not isinstance(
258            quadratic_expr,
259            (variables.QuadraticBase, variables.LinearBase, int, float),
260        ):
261            raise TypeError(
262                "unsupported type in objective argument for "
263                f"set_to_quadratic_expression: {type(quadratic_expr).__name__!r}"
264            )
265        self.clear()
266        objective_expr = variables.as_flat_quadratic_expression(quadratic_expr)
267        self.offset = objective_expr.offset
268        for var, coefficient in objective_expr.linear_terms.items():
269            self.set_linear_coefficient(var, coefficient)
270        for quad_key, coefficient in objective_expr.quadratic_terms.items():
271            self.set_quadratic_coefficient(
272                quad_key.first_var, quad_key.second_var, coefficient
273            )
274
275    def set_to_expression(self, expr: variables.QuadraticTypes) -> None:
276        """Sets the objective to optimize the `expr`."""
277        if isinstance(expr, (variables.LinearBase, int, float)):
278            self.set_to_linear_expression(expr)
279        elif isinstance(expr, variables.QuadraticBase):
280            self.set_to_quadratic_expression(expr)
281        else:
282            raise TypeError(
283                "unsupported type in objective argument for "
284                f"set_to_expression: {type(expr).__name__!r}"
285            )

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.

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

Do not invoke directly, prefer Model.objective.

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

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

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

The immutable name of this objective, for display only.

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

If true, the direction is maximization, otherwise minimization.

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

A constant added to the objective.

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

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:
131    @abc.abstractmethod
132    def set_linear_coefficient(self, var: variables.Variable, coef: float) -> None:
133        """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:
135    @abc.abstractmethod
136    def get_linear_coefficient(self, var: variables.Variable) -> float:
137        """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]:
139    @abc.abstractmethod
140    def linear_terms(self) -> Iterator[variables.LinearTerm]:
141        """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:
143    @abc.abstractmethod
144    def set_quadratic_coefficient(
145        self,
146        first_variable: variables.Variable,
147        second_variable: variables.Variable,
148        coef: float,
149    ) -> None:
150        """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:
152    @abc.abstractmethod
153    def get_quadratic_coefficient(
154        self,
155        first_variable: variables.Variable,
156        second_variable: variables.Variable,
157    ) -> float:
158        """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]:
160    @abc.abstractmethod
161    def quadratic_terms(self) -> Iterator[variables.QuadraticTerm]:
162        """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:
164    @abc.abstractmethod
165    def clear(self) -> None:
166        """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:
168    def as_linear_expression(self) -> variables.LinearExpression:
169        """Returns an equivalent LinearExpression, or errors if quadratic."""
170        if any(self.quadratic_terms()):
171            raise TypeError("Cannot get a quadratic objective as a linear expression")
172        return variables.as_flat_linear_expression(
173            self.offset + variables.LinearSum(self.linear_terms())
174        )

Returns an equivalent LinearExpression, or errors if quadratic.

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

Returns an equivalent QuadraticExpression to this objetive.

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

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

Adds the provided linear expression objective to the objective function.

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

Adds the provided quadratic expression objective to the objective function.

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

Sets the objective to optimize to linear_expr.

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

Sets the objective to optimize the quadratic_expr.

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

Sets the objective to optimize the expr.

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

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

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

The immutable name of this objective, for display only.

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

If true, the direction is maximization, otherwise minimization.

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

A constant added to the objective.

priority: int
313    @property
314    def priority(self) -> int:
315        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:
321    def set_linear_coefficient(self, var: variables.Variable, coef: float) -> None:
322        from_model.model_is_same(self, var)
323        self._elemental.set_attr(
324            enums.DoubleAttr1.OBJECTIVE_LINEAR_COEFFICIENT, (var.id,), coef
325        )

Sets the coefficient of var to coef in the objective.

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

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

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

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:
347    def set_quadratic_coefficient(
348        self,
349        first_variable: variables.Variable,
350        second_variable: variables.Variable,
351        coef: float,
352    ) -> None:
353        from_model.model_is_same(self, first_variable)
354        from_model.model_is_same(self, second_variable)
355        self._elemental.set_attr(
356            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT,
357            (first_variable.id, second_variable.id),
358            coef,
359        )

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:
361    def get_quadratic_coefficient(
362        self,
363        first_variable: variables.Variable,
364        second_variable: variables.Variable,
365    ) -> float:
366        from_model.model_is_same(self, first_variable)
367        from_model.model_is_same(self, second_variable)
368        return self._elemental.get_attr(
369            enums.SymmetricDoubleAttr2.OBJECTIVE_QUADRATIC_COEFFICIENT,
370            (first_variable.id, second_variable.id),
371        )

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

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

Yields quadratic terms with nonzero objective coefficient in undefined order.

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

Clears objective coefficients and offset. Does not change direction.

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

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

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

The immutable name of this objective, for display only.

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

Returns the id of this objective.

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

If true, the direction is maximization, otherwise minimization.

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

A constant added to the objective.

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

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:
472    def set_linear_coefficient(self, var: variables.Variable, coef: float) -> None:
473        from_model.model_is_same(self, var)
474        self._elemental.set_attr(
475            enums.DoubleAttr2.AUXILIARY_OBJECTIVE_LINEAR_COEFFICIENT,
476            (self._id, var.id),
477            coef,
478        )

Sets the coefficient of var to coef in the objective.

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

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

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

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:
506    def set_quadratic_coefficient(
507        self,
508        first_variable: variables.Variable,
509        second_variable: variables.Variable,
510        coef: float,
511    ) -> None:
512        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:
514    def get_quadratic_coefficient(
515        self,
516        first_variable: variables.Variable,
517        second_variable: variables.Variable,
518    ) -> float:
519        from_model.model_is_same(self, first_variable)
520        from_model.model_is_same(self, second_variable)
521        if not self._elemental.element_exists(
522            enums.ElementType.VARIABLE, first_variable.id
523        ):
524            raise ValueError(f"Variable {first_variable} does not exist")
525        if not self._elemental.element_exists(
526            enums.ElementType.VARIABLE, second_variable.id
527        ):
528            raise ValueError(f"Variable {second_variable} does not exist")
529        return 0.0

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

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

Yields quadratic terms with nonzero objective coefficient in undefined order.

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

Clears objective coefficients and offset. Does not change direction.