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))
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.
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()).
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.
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.
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.
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.
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.
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.
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.
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.