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))
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.
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()).
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.
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.
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.
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.
96 @property 97 def elemental(self) -> elemental.Elemental: 98 """Internal use only.""" 99 return self._elemental
Internal use only.
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.
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.
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.
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.