ortools.math_opt.python.quadratic_constraints

Quadratic constraint in a 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"""Quadratic constraint in a model."""
 16
 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 QuadraticConstraint(from_model.FromModel):
 26    """A quadratic constraint for an optimization model.
 27
 28    A QuadraticConstraint adds the following restriction on feasible solutions to
 29    an optimization model:
 30      lb <= sum_i a_i x_i + sum_i sum_{j : i <= j} b_ij x_i x_j <= ub
 31    where x_i are the decision variables of the problem. lb == ub is allowed, and
 32    this models an equality constraint. lb > ub is also allowed, but the
 33    optimization problem will be infeasible.
 34
 35    Quadratic constraints have limited mutability. You can delete a variable
 36    that the constraint uses, or you can delete the entire constraint. You
 37    currently cannot update bounds or coefficients. This may change in future
 38    versions.
 39
 40    A QuadraticConstraint can be queried as follows:
 41      * lower_bound: a float property, lb above. Should not be NaN nor +inf.
 42      * upper_bound: a float property, ub above. Should not be NaN nor -inf.
 43      * get_linear_coefficient(): get the a_i * x_i terms. The variable must be
 44        from the same model as this constraint, and the a_i must be finite and not
 45        NaN. The coefficient for any variable not set is 0.0.
 46      * get_quadratic_coefficient(): like get_linear_coefficient() but for the
 47        b_ij terms. Note that get_quadratic_coefficient(x, y, 8) and
 48        get_quadratic_coefficient(y, x, 8) have the same result.
 49
 50    The name is optional, read only, and used only for debugging. Non-empty names
 51    should be distinct.
 52
 53    Do not create a QuadraticConstraint directly, use
 54    Model.add_quadratic_constraint() instead. Two QuadraticConstraint objects
 55    can represent the same constraint (for the same model). They will have the
 56    same underlying QuadraticConstraint.elemental for storing the data. The
 57    QuadraticConstraint class is simply a reference to an Elemental.
 58    """
 59
 60    __slots__ = "_elemental", "_id"
 61
 62    def __init__(self, elem: elemental.Elemental, cid: int) -> None:
 63        """Internal only, prefer Model functions (add_quadratic_constraint() and get_quadratic_constraint())."""
 64        if not isinstance(cid, int):
 65            raise TypeError(f"cid type should be int, was:{type(cid).__name__!r}")
 66        self._elemental: elemental.Elemental = elem
 67        self._id: int = cid
 68
 69    @property
 70    def lower_bound(self) -> float:
 71        """The quadratic expression of the constraint must be at least this."""
 72        return self._elemental.get_attr(
 73            enums.DoubleAttr1.QUADRATIC_CONSTRAINT_LOWER_BOUND, (self._id,)
 74        )
 75
 76    @property
 77    def upper_bound(self) -> float:
 78        """The quadratic expression of the constraint must be at most this."""
 79        return self._elemental.get_attr(
 80            enums.DoubleAttr1.QUADRATIC_CONSTRAINT_UPPER_BOUND, (self._id,)
 81        )
 82
 83    @property
 84    def name(self) -> str:
 85        """The name of this constraint."""
 86        return self._elemental.get_element_name(
 87            enums.ElementType.QUADRATIC_CONSTRAINT, self._id
 88        )
 89
 90    @property
 91    def id(self) -> int:
 92        """A unique (for the model) identifier for this constraint."""
 93        return self._id
 94
 95    @property
 96    def elemental(self) -> elemental.Elemental:
 97        """Internal use only."""
 98        return self._elemental
 99
100    def get_linear_coefficient(self, var: variables.Variable) -> float:
101        """Returns the linear coefficient for var in the constraint's quadratic expression."""
102        from_model.model_is_same(var, self)
103        return self._elemental.get_attr(
104            enums.DoubleAttr2.QUADRATIC_CONSTRAINT_LINEAR_COEFFICIENT,
105            (self._id, var.id),
106        )
107
108    def linear_terms(self) -> Iterator[variables.LinearTerm]:
109        """Yields variable/coefficient pairs from the linear part of the constraint.
110
111        Only the pairs with nonzero coefficient are returned.
112
113        Yields:
114          The variable, coefficient pairs.
115        """
116        keys = self._elemental.slice_attr(
117            enums.DoubleAttr2.QUADRATIC_CONSTRAINT_LINEAR_COEFFICIENT, 0, self._id
118        )
119        coefs = self._elemental.get_attrs(
120            enums.DoubleAttr2.QUADRATIC_CONSTRAINT_LINEAR_COEFFICIENT, keys
121        )
122        for i in range(len(keys)):
123            yield variables.LinearTerm(
124                variable=variables.Variable(self._elemental, int(keys[i, 1])),
125                coefficient=float(coefs[i]),
126            )
127
128    def get_quadratic_coefficient(
129        self, var1: variables.Variable, var2: variables.Variable
130    ) -> float:
131        """Returns the quadratic coefficient for the pair (var1, var2) in the constraint's quadratic expression."""
132        from_model.model_is_same(var1, self)
133        from_model.model_is_same(var2, self)
134        return self._elemental.get_attr(
135            enums.SymmetricDoubleAttr3.QUADRATIC_CONSTRAINT_QUADRATIC_COEFFICIENT,
136            (self._id, var1.id, var2.id),
137        )
138
139    def quadratic_terms(self) -> Iterator[variables.QuadraticTerm]:
140        """Yields variable/coefficient pairs from the quadratic part of the constraint.
141
142        Only the pairs with nonzero coefficient are returned.
143
144        Yields:
145          The variable, coefficient pairs.
146        """
147        keys = self._elemental.slice_attr(
148            enums.SymmetricDoubleAttr3.QUADRATIC_CONSTRAINT_QUADRATIC_COEFFICIENT,
149            0,
150            self._id,
151        )
152        coefs = self._elemental.get_attrs(
153            enums.SymmetricDoubleAttr3.QUADRATIC_CONSTRAINT_QUADRATIC_COEFFICIENT,
154            keys,
155        )
156        for i in range(len(keys)):
157            yield variables.QuadraticTerm(
158                variables.QuadraticTermKey(
159                    variables.Variable(self._elemental, int(keys[i, 1])),
160                    variables.Variable(self._elemental, int(keys[i, 2])),
161                ),
162                coefficient=float(coefs[i]),
163            )
164
165    def __str__(self):
166        """Returns the name, or a string containing the id if the name is empty."""
167        return self.name if self.name else f"quadratic_constraint_{self.id}"
168
169    def __repr__(self):
170        return f"<QuadraticConstraint id: {self.id}, name: {self.name!r}>"
171
172    def __eq__(self, other: Any) -> bool:
173        if isinstance(other, QuadraticConstraint):
174            return self._id == other._id and self._elemental is other._elemental
175        return False
176
177    def __hash__(self) -> int:
178        return hash((self._id, self._elemental))
class QuadraticConstraint(ortools.math_opt.python.from_model.FromModel):
 26class QuadraticConstraint(from_model.FromModel):
 27    """A quadratic constraint for an optimization model.
 28
 29    A QuadraticConstraint adds the following restriction on feasible solutions to
 30    an optimization model:
 31      lb <= sum_i a_i x_i + sum_i sum_{j : i <= j} b_ij x_i x_j <= ub
 32    where x_i are the decision variables of the problem. lb == ub is allowed, and
 33    this models an equality constraint. lb > ub is also allowed, but the
 34    optimization problem will be infeasible.
 35
 36    Quadratic constraints have limited mutability. You can delete a variable
 37    that the constraint uses, or you can delete the entire constraint. You
 38    currently cannot update bounds or coefficients. This may change in future
 39    versions.
 40
 41    A QuadraticConstraint can be queried as follows:
 42      * lower_bound: a float property, lb above. Should not be NaN nor +inf.
 43      * upper_bound: a float property, ub above. Should not be NaN nor -inf.
 44      * get_linear_coefficient(): get the a_i * x_i terms. The variable must be
 45        from the same model as this constraint, and the a_i must be finite and not
 46        NaN. The coefficient for any variable not set is 0.0.
 47      * get_quadratic_coefficient(): like get_linear_coefficient() but for the
 48        b_ij terms. Note that get_quadratic_coefficient(x, y, 8) and
 49        get_quadratic_coefficient(y, x, 8) have the same result.
 50
 51    The name is optional, read only, and used only for debugging. Non-empty names
 52    should be distinct.
 53
 54    Do not create a QuadraticConstraint directly, use
 55    Model.add_quadratic_constraint() instead. Two QuadraticConstraint objects
 56    can represent the same constraint (for the same model). They will have the
 57    same underlying QuadraticConstraint.elemental for storing the data. The
 58    QuadraticConstraint class is simply a reference to an Elemental.
 59    """
 60
 61    __slots__ = "_elemental", "_id"
 62
 63    def __init__(self, elem: elemental.Elemental, cid: int) -> None:
 64        """Internal only, prefer Model functions (add_quadratic_constraint() and get_quadratic_constraint())."""
 65        if not isinstance(cid, int):
 66            raise TypeError(f"cid type should be int, was:{type(cid).__name__!r}")
 67        self._elemental: elemental.Elemental = elem
 68        self._id: int = cid
 69
 70    @property
 71    def lower_bound(self) -> float:
 72        """The quadratic expression of the constraint must be at least this."""
 73        return self._elemental.get_attr(
 74            enums.DoubleAttr1.QUADRATIC_CONSTRAINT_LOWER_BOUND, (self._id,)
 75        )
 76
 77    @property
 78    def upper_bound(self) -> float:
 79        """The quadratic expression of the constraint must be at most this."""
 80        return self._elemental.get_attr(
 81            enums.DoubleAttr1.QUADRATIC_CONSTRAINT_UPPER_BOUND, (self._id,)
 82        )
 83
 84    @property
 85    def name(self) -> str:
 86        """The name of this constraint."""
 87        return self._elemental.get_element_name(
 88            enums.ElementType.QUADRATIC_CONSTRAINT, self._id
 89        )
 90
 91    @property
 92    def id(self) -> int:
 93        """A unique (for the model) identifier for this constraint."""
 94        return self._id
 95
 96    @property
 97    def elemental(self) -> elemental.Elemental:
 98        """Internal use only."""
 99        return self._elemental
100
101    def get_linear_coefficient(self, var: variables.Variable) -> float:
102        """Returns the linear coefficient for var in the constraint's quadratic expression."""
103        from_model.model_is_same(var, self)
104        return self._elemental.get_attr(
105            enums.DoubleAttr2.QUADRATIC_CONSTRAINT_LINEAR_COEFFICIENT,
106            (self._id, var.id),
107        )
108
109    def linear_terms(self) -> Iterator[variables.LinearTerm]:
110        """Yields variable/coefficient pairs from the linear part of the constraint.
111
112        Only the pairs with nonzero coefficient are returned.
113
114        Yields:
115          The variable, coefficient pairs.
116        """
117        keys = self._elemental.slice_attr(
118            enums.DoubleAttr2.QUADRATIC_CONSTRAINT_LINEAR_COEFFICIENT, 0, self._id
119        )
120        coefs = self._elemental.get_attrs(
121            enums.DoubleAttr2.QUADRATIC_CONSTRAINT_LINEAR_COEFFICIENT, keys
122        )
123        for i in range(len(keys)):
124            yield variables.LinearTerm(
125                variable=variables.Variable(self._elemental, int(keys[i, 1])),
126                coefficient=float(coefs[i]),
127            )
128
129    def get_quadratic_coefficient(
130        self, var1: variables.Variable, var2: variables.Variable
131    ) -> float:
132        """Returns the quadratic coefficient for the pair (var1, var2) in the constraint's quadratic expression."""
133        from_model.model_is_same(var1, self)
134        from_model.model_is_same(var2, self)
135        return self._elemental.get_attr(
136            enums.SymmetricDoubleAttr3.QUADRATIC_CONSTRAINT_QUADRATIC_COEFFICIENT,
137            (self._id, var1.id, var2.id),
138        )
139
140    def quadratic_terms(self) -> Iterator[variables.QuadraticTerm]:
141        """Yields variable/coefficient pairs from the quadratic part of the constraint.
142
143        Only the pairs with nonzero coefficient are returned.
144
145        Yields:
146          The variable, coefficient pairs.
147        """
148        keys = self._elemental.slice_attr(
149            enums.SymmetricDoubleAttr3.QUADRATIC_CONSTRAINT_QUADRATIC_COEFFICIENT,
150            0,
151            self._id,
152        )
153        coefs = self._elemental.get_attrs(
154            enums.SymmetricDoubleAttr3.QUADRATIC_CONSTRAINT_QUADRATIC_COEFFICIENT,
155            keys,
156        )
157        for i in range(len(keys)):
158            yield variables.QuadraticTerm(
159                variables.QuadraticTermKey(
160                    variables.Variable(self._elemental, int(keys[i, 1])),
161                    variables.Variable(self._elemental, int(keys[i, 2])),
162                ),
163                coefficient=float(coefs[i]),
164            )
165
166    def __str__(self):
167        """Returns the name, or a string containing the id if the name is empty."""
168        return self.name if self.name else f"quadratic_constraint_{self.id}"
169
170    def __repr__(self):
171        return f"<QuadraticConstraint id: {self.id}, name: {self.name!r}>"
172
173    def __eq__(self, other: Any) -> bool:
174        if isinstance(other, QuadraticConstraint):
175            return self._id == other._id and self._elemental is other._elemental
176        return False
177
178    def __hash__(self) -> int:
179        return hash((self._id, self._elemental))

A quadratic constraint for an optimization model.

A QuadraticConstraint adds the following restriction on feasible solutions to an optimization model: lb <= sum_i a_i x_i + sum_i sum_{j : i <= j} b_ij x_i x_j <= ub where x_i are the decision variables of the problem. lb == ub is allowed, and this models an equality constraint. lb > ub is also allowed, but the optimization problem will be infeasible.

Quadratic constraints have limited mutability. You can delete a variable that the constraint uses, or you can delete the entire constraint. You currently cannot update bounds or coefficients. This may change in future versions.

A QuadraticConstraint can be queried as follows:
  • lower_bound: a float property, lb above. Should not be NaN nor +inf.
  • upper_bound: a float property, ub above. Should not be NaN nor -inf.
  • get_linear_coefficient(): get the a_i * x_i terms. The variable must be from the same model as this constraint, and the a_i must be finite and not NaN. The coefficient for any variable not set is 0.0.
  • get_quadratic_coefficient(): like get_linear_coefficient() but for the b_ij terms. Note that get_quadratic_coefficient(x, y, 8) and get_quadratic_coefficient(y, x, 8) have the same result.

The name is optional, read only, and used only for debugging. Non-empty names should be distinct.

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

QuadraticConstraint()
63    def __init__(self, elem: elemental.Elemental, cid: int) -> None:
64        """Internal only, prefer Model functions (add_quadratic_constraint() and get_quadratic_constraint())."""
65        if not isinstance(cid, int):
66            raise TypeError(f"cid type should be int, was:{type(cid).__name__!r}")
67        self._elemental: elemental.Elemental = elem
68        self._id: int = cid

Internal only, prefer Model functions (add_quadratic_constraint() and get_quadratic_constraint()).

lower_bound: float
70    @property
71    def lower_bound(self) -> float:
72        """The quadratic expression of the constraint must be at least this."""
73        return self._elemental.get_attr(
74            enums.DoubleAttr1.QUADRATIC_CONSTRAINT_LOWER_BOUND, (self._id,)
75        )

The quadratic expression of the constraint must be at least this.

upper_bound: float
77    @property
78    def upper_bound(self) -> float:
79        """The quadratic expression of the constraint must be at most this."""
80        return self._elemental.get_attr(
81            enums.DoubleAttr1.QUADRATIC_CONSTRAINT_UPPER_BOUND, (self._id,)
82        )

The quadratic expression of the constraint must be at most this.

name: str
84    @property
85    def name(self) -> str:
86        """The name of this constraint."""
87        return self._elemental.get_element_name(
88            enums.ElementType.QUADRATIC_CONSTRAINT, self._id
89        )

The name of this constraint.

id: int
91    @property
92    def id(self) -> int:
93        """A unique (for the model) identifier for this constraint."""
94        return self._id

A unique (for the model) identifier for this constraint.

elemental
96    @property
97    def elemental(self) -> elemental.Elemental:
98        """Internal use only."""
99        return self._elemental

Internal use only.

def get_linear_coefficient(self, var: ortools.math_opt.python.variables.Variable) -> float:
101    def get_linear_coefficient(self, var: variables.Variable) -> float:
102        """Returns the linear coefficient for var in the constraint's quadratic expression."""
103        from_model.model_is_same(var, self)
104        return self._elemental.get_attr(
105            enums.DoubleAttr2.QUADRATIC_CONSTRAINT_LINEAR_COEFFICIENT,
106            (self._id, var.id),
107        )

Returns the linear coefficient for var in the constraint's quadratic expression.

def linear_terms(self) -> Iterator[ortools.math_opt.python.variables.LinearTerm]:
109    def linear_terms(self) -> Iterator[variables.LinearTerm]:
110        """Yields variable/coefficient pairs from the linear part of the constraint.
111
112        Only the pairs with nonzero coefficient are returned.
113
114        Yields:
115          The variable, coefficient pairs.
116        """
117        keys = self._elemental.slice_attr(
118            enums.DoubleAttr2.QUADRATIC_CONSTRAINT_LINEAR_COEFFICIENT, 0, self._id
119        )
120        coefs = self._elemental.get_attrs(
121            enums.DoubleAttr2.QUADRATIC_CONSTRAINT_LINEAR_COEFFICIENT, keys
122        )
123        for i in range(len(keys)):
124            yield variables.LinearTerm(
125                variable=variables.Variable(self._elemental, int(keys[i, 1])),
126                coefficient=float(coefs[i]),
127            )

Yields variable/coefficient pairs from the linear part of the constraint.

Only the pairs with nonzero coefficient are returned.

Yields:

The variable, coefficient pairs.

def get_quadratic_coefficient( self, var1: ortools.math_opt.python.variables.Variable, var2: ortools.math_opt.python.variables.Variable) -> float:
129    def get_quadratic_coefficient(
130        self, var1: variables.Variable, var2: variables.Variable
131    ) -> float:
132        """Returns the quadratic coefficient for the pair (var1, var2) in the constraint's quadratic expression."""
133        from_model.model_is_same(var1, self)
134        from_model.model_is_same(var2, self)
135        return self._elemental.get_attr(
136            enums.SymmetricDoubleAttr3.QUADRATIC_CONSTRAINT_QUADRATIC_COEFFICIENT,
137            (self._id, var1.id, var2.id),
138        )

Returns the quadratic coefficient for the pair (var1, var2) in the constraint's quadratic expression.

def quadratic_terms(self) -> Iterator[ortools.math_opt.python.variables.QuadraticTerm]:
140    def quadratic_terms(self) -> Iterator[variables.QuadraticTerm]:
141        """Yields variable/coefficient pairs from the quadratic part of the constraint.
142
143        Only the pairs with nonzero coefficient are returned.
144
145        Yields:
146          The variable, coefficient pairs.
147        """
148        keys = self._elemental.slice_attr(
149            enums.SymmetricDoubleAttr3.QUADRATIC_CONSTRAINT_QUADRATIC_COEFFICIENT,
150            0,
151            self._id,
152        )
153        coefs = self._elemental.get_attrs(
154            enums.SymmetricDoubleAttr3.QUADRATIC_CONSTRAINT_QUADRATIC_COEFFICIENT,
155            keys,
156        )
157        for i in range(len(keys)):
158            yield variables.QuadraticTerm(
159                variables.QuadraticTermKey(
160                    variables.Variable(self._elemental, int(keys[i, 1])),
161                    variables.Variable(self._elemental, int(keys[i, 2])),
162                ),
163                coefficient=float(coefs[i]),
164            )

Yields variable/coefficient pairs from the quadratic part of the constraint.

Only the pairs with nonzero coefficient are returned.

Yields:

The variable, coefficient pairs.