ortools.math_opt.python.quadratic_constraints

Quadratic constraint in a model.

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

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( elem: ortools.math_opt.python.elemental.elemental.Elemental, cid: int)
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

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

lower_bound: float
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        )

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

upper_bound: float
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        )

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

name: str
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        )

The name of this constraint.

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

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

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

Internal use only.

def get_linear_coefficient(self, var: ortools.math_opt.python.variables.Variable) -> float:
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        )

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

def linear_terms(self) -> Iterator[ortools.math_opt.python.variables.LinearTerm]:
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            )

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:
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        )

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]:
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            )

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

Only the pairs with nonzero coefficient are returned.

Yields:

The variable, coefficient pairs.