Google OR-Tools v9.14
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
quadratic_constraints.py
Go to the documentation of this file.
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
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
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}")
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)):
123 variable=variables.Variable(self._elemental, int(keys[i, 1])),
124 coefficient=float(coefs[i]),
125 )
126
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)):
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))
float get_quadratic_coefficient(self, variables.Variable var1, variables.Variable var2)