ortools.math_opt.python.model_storage
An interface for in memory storage of optimization problems.
1# Copyright 2010-2024 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"""An interface for in memory storage of optimization problems.""" 15 16import abc 17import dataclasses 18from typing import Iterator, Optional, Type, TypeVar 19 20from ortools.math_opt import model_pb2 21from ortools.math_opt import model_update_pb2 22 23 24# TODO(b/231426528): remove __slots__ and set slots=True when Python 3.10 is 25# available. 26@dataclasses.dataclass(frozen=True) 27class LinearConstraintMatrixIdEntry: 28 __slots__ = "linear_constraint_id", "variable_id", "coefficient" 29 linear_constraint_id: int 30 variable_id: int 31 coefficient: float 32 33 34# TODO(b/231426528): remove __slots__ and set slots=True when Python 3.10 is 35# available. 36@dataclasses.dataclass(frozen=True) 37class LinearObjectiveEntry: 38 __slots__ = "variable_id", "coefficient" 39 variable_id: int 40 coefficient: float 41 42 43# TODO(b/231426528): remove __slots__ and set slots=True when Python 3.10 is 44# available. 45@dataclasses.dataclass(frozen=True) 46class QuadraticTermIdKey: 47 """An ordered pair of ints used as a key for quadratic terms. 48 49 QuadraticTermIdKey.id1 <= QuadraticTermIdKey.id2. 50 """ 51 52 __slots__ = "id1", "id2" 53 id1: int 54 id2: int 55 56 def __init__(self, a: int, b: int): 57 """Ints a and b will be ordered internally.""" 58 id1 = a 59 id2 = b 60 if id1 > id2: 61 id1, id2 = id2, id1 62 object.__setattr__(self, "id1", id1) 63 object.__setattr__(self, "id2", id2) 64 65 66# TODO(b/231426528): remove __slots__ and set slots=True when Python 3.10 is 67# available. 68@dataclasses.dataclass(frozen=True) 69class QuadraticEntry: 70 """Represents an id-indexed quadratic term.""" 71 72 __slots__ = "id_key", "coefficient" 73 id_key: QuadraticTermIdKey 74 coefficient: float 75 76 77class StorageUpdateTracker(abc.ABC): 78 """Tracks updates to an optimization model from a ModelStorage. 79 80 Do not instantiate directly, instead create through 81 ModelStorage.add_update_tracker(). 82 83 Interacting with an update tracker after it has been removed from the model 84 will result in an UsedUpdateTrackerAfterRemovalError error. 85 86 Example: 87 mod = model_storage.ModelStorage() 88 x = mod.add_variable(0.0, 1.0, True, 'x') 89 y = mod.add_variable(0.0, 1.0, True, 'y') 90 tracker = mod.add_update_tracker() 91 mod.set_variable_ub(x, 3.0) 92 tracker.export_update() 93 => "variable_updates: {upper_bounds: {ids: [0], values[3.0] }" 94 mod.set_variable_ub(y, 2.0) 95 tracker.export_update() 96 => "variable_updates: {upper_bounds: {ids: [0, 1], values[3.0, 2.0] }" 97 tracker.advance_checkpoint() 98 tracker.export_update() 99 => "" 100 mod.set_variable_ub(y, 4.0) 101 tracker.export_update() 102 => "variable_updates: {upper_bounds: {ids: [1], values[4.0] }" 103 tracker.advance_checkpoint() 104 mod.remove_update_tracker(tracker) 105 => "" 106 """ 107 108 @abc.abstractmethod 109 def export_update(self) -> Optional[model_update_pb2.ModelUpdateProto]: 110 """Returns changes to the model since last call to checkpoint/creation, or None if no changes occurred.""" 111 pass 112 113 @abc.abstractmethod 114 def advance_checkpoint(self) -> None: 115 """Track changes to the model only after this function call.""" 116 pass 117 118 119class UsedUpdateTrackerAfterRemovalError(RuntimeError): 120 121 def __init__(self): 122 super().__init__( 123 "Attempted to use update tracker after removing it from model storage." 124 ) 125 126 127class BadVariableIdError(LookupError): 128 """Raised by ModelStorage when a bad variable id is given.""" 129 130 def __init__(self, variable_id): 131 super().__init__(f"Unexpected variable id: {variable_id}") 132 self.id = variable_id 133 134 135class BadLinearConstraintIdError(LookupError): 136 """Raised by ModelStorage when a bad linear constraint id is given.""" 137 138 def __init__(self, linear_constraint_id): 139 super().__init__(f"Unexpected linear constraint id: {linear_constraint_id}") 140 self.id = linear_constraint_id 141 142 143class ModelStorage(abc.ABC): 144 """An interface for in memory storage of an optimization model. 145 146 Most users should not use this class directly and use Model defined in 147 model.py. 148 149 Stores an mixed integer programming problem of the form: 150 151 {max/min} c*x + d 152 s.t. lb_c <= A * x <= ub_c 153 lb_v <= x <= ub_v 154 x_i integer for i in I 155 156 where x is a vector of n decision variables, d is a number, lb_v, ub_v, and c 157 are vectors of n numbers, lb_c and ub_c are vectors of m numbers, A is a 158 m by n matrix, and I is a subset of {1,..., n}. 159 160 Each of the n variables and m constraints have an integer id that you use to 161 get/set the problem data (c, A, lb_c etc.). Ids begin at zero and increase 162 sequentially. They are not reused after deletion. Note that if a variable is 163 deleted, your model has nonconsecutive variable ids. 164 165 For all methods taking an id (e.g. set_variable_lb), providing a bad id 166 (including the id of a deleted variable) will raise a BadVariableIdError or 167 BadLinearConstraintIdError. Further, the ModelStorage instance is assumed to 168 be in a bad state after any such error and there are no guarantees on further 169 interactions. 170 171 All implementations must have a constructor taking a str argument for the 172 model name with a default value of the empty string. 173 174 Any ModelStorage can be exported to model_pb2.ModelProto, the format consumed 175 by MathOpt solvers. Changes to a model can be exported to a 176 model_update_pb2.ModelUpdateProto with an UpdateTracker, see the UpdateTracker 177 documentation for details. 178 179 When solving this optimization problem we will additionally require that: 180 * No numbers are NaN, 181 * c, d, and A are all finite, 182 * lb_c and lb_v are not +inf, 183 * ub_c and ub_v are not -inf, 184 but those assumptions are not checked or enforced here (NaNs and infinite 185 values can be used anywhere). 186 """ 187 188 @property 189 @abc.abstractmethod 190 def name(self) -> str: 191 pass 192 193 @abc.abstractmethod 194 def add_variable(self, lb: float, ub: float, is_integer: bool, name: str) -> int: 195 pass 196 197 @abc.abstractmethod 198 def delete_variable(self, variable_id: int) -> None: 199 pass 200 201 @abc.abstractmethod 202 def variable_exists(self, variable_id: int) -> bool: 203 pass 204 205 @abc.abstractmethod 206 def next_variable_id(self) -> int: 207 pass 208 209 @abc.abstractmethod 210 def set_variable_lb(self, variable_id: int, lb: float) -> None: 211 pass 212 213 @abc.abstractmethod 214 def set_variable_ub(self, variable_id: int, ub: float) -> None: 215 pass 216 217 @abc.abstractmethod 218 def set_variable_is_integer(self, variable_id: int, is_integer: bool) -> None: 219 pass 220 221 @abc.abstractmethod 222 def get_variable_lb(self, variable_id: int) -> float: 223 pass 224 225 @abc.abstractmethod 226 def get_variable_ub(self, variable_id: int) -> float: 227 pass 228 229 @abc.abstractmethod 230 def get_variable_is_integer(self, variable_id: int) -> bool: 231 pass 232 233 @abc.abstractmethod 234 def get_variable_name(self, variable_id: int) -> str: 235 pass 236 237 @abc.abstractmethod 238 def get_variables(self) -> Iterator[int]: 239 """Yields the variable ids in order of creation.""" 240 pass 241 242 @abc.abstractmethod 243 def add_linear_constraint(self, lb: float, ub: float, name: str) -> int: 244 pass 245 246 @abc.abstractmethod 247 def delete_linear_constraint(self, linear_constraint_id: int) -> None: 248 pass 249 250 @abc.abstractmethod 251 def linear_constraint_exists(self, linear_constraint_id: int) -> bool: 252 pass 253 254 @abc.abstractmethod 255 def next_linear_constraint_id(self) -> int: 256 pass 257 258 @abc.abstractmethod 259 def set_linear_constraint_lb(self, linear_constraint_id: int, lb: float) -> None: 260 pass 261 262 @abc.abstractmethod 263 def set_linear_constraint_ub(self, linear_constraint_id: int, ub: float) -> None: 264 pass 265 266 @abc.abstractmethod 267 def get_linear_constraint_lb(self, linear_constraint_id: int) -> float: 268 pass 269 270 @abc.abstractmethod 271 def get_linear_constraint_ub(self, linear_constraint_id: int) -> float: 272 pass 273 274 @abc.abstractmethod 275 def get_linear_constraint_name(self, linear_constraint_id: int) -> str: 276 pass 277 278 @abc.abstractmethod 279 def get_linear_constraints(self) -> Iterator[int]: 280 """Yields the linear constraint ids in order of creation.""" 281 pass 282 283 @abc.abstractmethod 284 def set_linear_constraint_coefficient( 285 self, linear_constraint_id: int, variable_id: int, lb: float 286 ) -> None: 287 pass 288 289 @abc.abstractmethod 290 def get_linear_constraint_coefficient( 291 self, linear_constraint_id: int, variable_id: int 292 ) -> float: 293 pass 294 295 @abc.abstractmethod 296 def get_linear_constraints_with_variable(self, variable_id: int) -> Iterator[int]: 297 """Yields the linear constraints with nonzero coefficient for a variable in undefined order.""" 298 pass 299 300 @abc.abstractmethod 301 def get_variables_for_linear_constraint( 302 self, linear_constraint_id: int 303 ) -> Iterator[int]: 304 """Yields the variables with nonzero coefficient in a linear constraint in undefined order.""" 305 pass 306 307 @abc.abstractmethod 308 def get_linear_constraint_matrix_entries( 309 self, 310 ) -> Iterator[LinearConstraintMatrixIdEntry]: 311 """Yields the nonzero elements of the linear constraint matrix in undefined order.""" 312 pass 313 314 @abc.abstractmethod 315 def clear_objective(self) -> None: 316 """Clears objective coefficients and offset. Does not change direction.""" 317 318 @abc.abstractmethod 319 def set_linear_objective_coefficient(self, variable_id: int, value: float) -> None: 320 pass 321 322 @abc.abstractmethod 323 def get_linear_objective_coefficient(self, variable_id: int) -> float: 324 pass 325 326 @abc.abstractmethod 327 def get_linear_objective_coefficients(self) -> Iterator[LinearObjectiveEntry]: 328 """Yields the nonzero linear objective terms in undefined order.""" 329 pass 330 331 @abc.abstractmethod 332 def set_quadratic_objective_coefficient( 333 self, first_variable_id: int, second_variable_id: int, value: float 334 ) -> None: 335 """Sets the objective coefficient for the product of two variables. 336 337 The ordering of the input variables does not matter. 338 339 Args: 340 first_variable_id: The first variable in the product. 341 second_variable_id: The second variable in the product. 342 value: The value of the coefficient. 343 344 Raises: 345 BadVariableIdError if first_variable_id or second_variable_id are not in 346 the model. 347 """ 348 349 @abc.abstractmethod 350 def get_quadratic_objective_coefficient( 351 self, first_variable_id: int, second_variable_id: int 352 ) -> float: 353 """Gets the objective coefficient for the product of two variables. 354 355 The ordering of the input variables does not matter. 356 357 Args: 358 first_variable_id: The first variable in the product. 359 second_variable_id: The second variable in the product. 360 361 Raises: 362 BadVariableIdError if first_variable_id or second_variable_id are not in 363 the model. 364 365 Returns: 366 The value of the coefficient. 367 """ 368 369 @abc.abstractmethod 370 def get_quadratic_objective_coefficients(self) -> Iterator[QuadraticEntry]: 371 """Yields the nonzero quadratic objective terms in undefined order.""" 372 373 @abc.abstractmethod 374 def get_quadratic_objective_adjacent_variables( 375 self, variable_id: int 376 ) -> Iterator[int]: 377 """Yields the variables multiplying a variable in the objective function. 378 379 Variables are returned in an unspecified order. 380 381 For example, if variables x and y have ids 0 and 1 respectively, and the 382 quadratic portion of the objective is x^2 + 2 x*y, then 383 get_quadratic_objective_adjacent_variables(0) = (0, 1). 384 385 Args: 386 variable_id: Function yields the variables multiplying variable_id in the 387 objective function. 388 389 Yields: 390 The variables multiplying variable_id in the objective function. 391 392 Raises: 393 BadVariableIdError if variable_id is not in the model. 394 """ 395 396 @abc.abstractmethod 397 def set_is_maximize(self, is_maximize: bool) -> None: 398 pass 399 400 @abc.abstractmethod 401 def get_is_maximize(self) -> bool: 402 pass 403 404 @abc.abstractmethod 405 def set_objective_offset(self, offset: float) -> None: 406 pass 407 408 @abc.abstractmethod 409 def get_objective_offset(self) -> float: 410 pass 411 412 @abc.abstractmethod 413 def export_model(self) -> model_pb2.ModelProto: 414 pass 415 416 @abc.abstractmethod 417 def add_update_tracker(self) -> StorageUpdateTracker: 418 """Creates a StorageUpdateTracker registered with self to view model changes.""" 419 pass 420 421 @abc.abstractmethod 422 def remove_update_tracker(self, tracker: StorageUpdateTracker): 423 """Stops tracker from getting updates on model changes in self. 424 425 An error will be raised if tracker is not a StorageUpdateTracker created by 426 this Model that has not previously been removed. 427 428 Using an UpdateTracker (via checkpoint or export_update) after it has been 429 removed will result in an error. 430 431 Args: 432 tracker: The StorageUpdateTracker to unregister. 433 434 Raises: 435 KeyError: The tracker was created by another model or was already removed. 436 """ 437 pass 438 439 440ModelStorageImpl = TypeVar("ModelStorageImpl", bound=ModelStorage) 441ModelStorageImplClass = Type[ModelStorageImpl]
46@dataclasses.dataclass(frozen=True) 47class QuadraticTermIdKey: 48 """An ordered pair of ints used as a key for quadratic terms. 49 50 QuadraticTermIdKey.id1 <= QuadraticTermIdKey.id2. 51 """ 52 53 __slots__ = "id1", "id2" 54 id1: int 55 id2: int 56 57 def __init__(self, a: int, b: int): 58 """Ints a and b will be ordered internally.""" 59 id1 = a 60 id2 = b 61 if id1 > id2: 62 id1, id2 = id2, id1 63 object.__setattr__(self, "id1", id1) 64 object.__setattr__(self, "id2", id2)
An ordered pair of ints used as a key for quadratic terms.
57 def __init__(self, a: int, b: int): 58 """Ints a and b will be ordered internally.""" 59 id1 = a 60 id2 = b 61 if id1 > id2: 62 id1, id2 = id2, id1 63 object.__setattr__(self, "id1", id1) 64 object.__setattr__(self, "id2", id2)
Ints a and b will be ordered internally.
69@dataclasses.dataclass(frozen=True) 70class QuadraticEntry: 71 """Represents an id-indexed quadratic term.""" 72 73 __slots__ = "id_key", "coefficient" 74 id_key: QuadraticTermIdKey 75 coefficient: float
Represents an id-indexed quadratic term.
78class StorageUpdateTracker(abc.ABC): 79 """Tracks updates to an optimization model from a ModelStorage. 80 81 Do not instantiate directly, instead create through 82 ModelStorage.add_update_tracker(). 83 84 Interacting with an update tracker after it has been removed from the model 85 will result in an UsedUpdateTrackerAfterRemovalError error. 86 87 Example: 88 mod = model_storage.ModelStorage() 89 x = mod.add_variable(0.0, 1.0, True, 'x') 90 y = mod.add_variable(0.0, 1.0, True, 'y') 91 tracker = mod.add_update_tracker() 92 mod.set_variable_ub(x, 3.0) 93 tracker.export_update() 94 => "variable_updates: {upper_bounds: {ids: [0], values[3.0] }" 95 mod.set_variable_ub(y, 2.0) 96 tracker.export_update() 97 => "variable_updates: {upper_bounds: {ids: [0, 1], values[3.0, 2.0] }" 98 tracker.advance_checkpoint() 99 tracker.export_update() 100 => "" 101 mod.set_variable_ub(y, 4.0) 102 tracker.export_update() 103 => "variable_updates: {upper_bounds: {ids: [1], values[4.0] }" 104 tracker.advance_checkpoint() 105 mod.remove_update_tracker(tracker) 106 => "" 107 """ 108 109 @abc.abstractmethod 110 def export_update(self) -> Optional[model_update_pb2.ModelUpdateProto]: 111 """Returns changes to the model since last call to checkpoint/creation, or None if no changes occurred.""" 112 pass 113 114 @abc.abstractmethod 115 def advance_checkpoint(self) -> None: 116 """Track changes to the model only after this function call.""" 117 pass
Tracks updates to an optimization model from a ModelStorage.
Do not instantiate directly, instead create through ModelStorage.add_update_tracker().
Interacting with an update tracker after it has been removed from the model will result in an UsedUpdateTrackerAfterRemovalError error.
Example:
mod = model_storage.ModelStorage() x = mod.add_variable(0.0, 1.0, True, 'x') y = mod.add_variable(0.0, 1.0, True, 'y') tracker = mod.add_update_tracker() mod.set_variable_ub(x, 3.0) tracker.export_update() => "variable_updates: {upper_bounds: {ids: [0], values[3.0] }" mod.set_variable_ub(y, 2.0) tracker.export_update() => "variable_updates: {upper_bounds: {ids: [0, 1], values[3.0, 2.0] }" tracker.advance_checkpoint() tracker.export_update() => "" mod.set_variable_ub(y, 4.0) tracker.export_update() => "variable_updates: {upper_bounds: {ids: [1], values[4.0] }" tracker.advance_checkpoint() mod.remove_update_tracker(tracker) => ""
109 @abc.abstractmethod 110 def export_update(self) -> Optional[model_update_pb2.ModelUpdateProto]: 111 """Returns changes to the model since last call to checkpoint/creation, or None if no changes occurred.""" 112 pass
Returns changes to the model since last call to checkpoint/creation, or None if no changes occurred.
120class UsedUpdateTrackerAfterRemovalError(RuntimeError): 121 122 def __init__(self): 123 super().__init__( 124 "Attempted to use update tracker after removing it from model storage." 125 )
Unspecified run-time error.
Inherited Members
- builtins.BaseException
- with_traceback
- add_note
- args
128class BadVariableIdError(LookupError): 129 """Raised by ModelStorage when a bad variable id is given.""" 130 131 def __init__(self, variable_id): 132 super().__init__(f"Unexpected variable id: {variable_id}") 133 self.id = variable_id
Raised by ModelStorage when a bad variable id is given.
Inherited Members
- builtins.BaseException
- with_traceback
- add_note
- args
136class BadLinearConstraintIdError(LookupError): 137 """Raised by ModelStorage when a bad linear constraint id is given.""" 138 139 def __init__(self, linear_constraint_id): 140 super().__init__(f"Unexpected linear constraint id: {linear_constraint_id}") 141 self.id = linear_constraint_id
Raised by ModelStorage when a bad linear constraint id is given.
Inherited Members
- builtins.BaseException
- with_traceback
- add_note
- args
144class ModelStorage(abc.ABC): 145 """An interface for in memory storage of an optimization model. 146 147 Most users should not use this class directly and use Model defined in 148 model.py. 149 150 Stores an mixed integer programming problem of the form: 151 152 {max/min} c*x + d 153 s.t. lb_c <= A * x <= ub_c 154 lb_v <= x <= ub_v 155 x_i integer for i in I 156 157 where x is a vector of n decision variables, d is a number, lb_v, ub_v, and c 158 are vectors of n numbers, lb_c and ub_c are vectors of m numbers, A is a 159 m by n matrix, and I is a subset of {1,..., n}. 160 161 Each of the n variables and m constraints have an integer id that you use to 162 get/set the problem data (c, A, lb_c etc.). Ids begin at zero and increase 163 sequentially. They are not reused after deletion. Note that if a variable is 164 deleted, your model has nonconsecutive variable ids. 165 166 For all methods taking an id (e.g. set_variable_lb), providing a bad id 167 (including the id of a deleted variable) will raise a BadVariableIdError or 168 BadLinearConstraintIdError. Further, the ModelStorage instance is assumed to 169 be in a bad state after any such error and there are no guarantees on further 170 interactions. 171 172 All implementations must have a constructor taking a str argument for the 173 model name with a default value of the empty string. 174 175 Any ModelStorage can be exported to model_pb2.ModelProto, the format consumed 176 by MathOpt solvers. Changes to a model can be exported to a 177 model_update_pb2.ModelUpdateProto with an UpdateTracker, see the UpdateTracker 178 documentation for details. 179 180 When solving this optimization problem we will additionally require that: 181 * No numbers are NaN, 182 * c, d, and A are all finite, 183 * lb_c and lb_v are not +inf, 184 * ub_c and ub_v are not -inf, 185 but those assumptions are not checked or enforced here (NaNs and infinite 186 values can be used anywhere). 187 """ 188 189 @property 190 @abc.abstractmethod 191 def name(self) -> str: 192 pass 193 194 @abc.abstractmethod 195 def add_variable(self, lb: float, ub: float, is_integer: bool, name: str) -> int: 196 pass 197 198 @abc.abstractmethod 199 def delete_variable(self, variable_id: int) -> None: 200 pass 201 202 @abc.abstractmethod 203 def variable_exists(self, variable_id: int) -> bool: 204 pass 205 206 @abc.abstractmethod 207 def next_variable_id(self) -> int: 208 pass 209 210 @abc.abstractmethod 211 def set_variable_lb(self, variable_id: int, lb: float) -> None: 212 pass 213 214 @abc.abstractmethod 215 def set_variable_ub(self, variable_id: int, ub: float) -> None: 216 pass 217 218 @abc.abstractmethod 219 def set_variable_is_integer(self, variable_id: int, is_integer: bool) -> None: 220 pass 221 222 @abc.abstractmethod 223 def get_variable_lb(self, variable_id: int) -> float: 224 pass 225 226 @abc.abstractmethod 227 def get_variable_ub(self, variable_id: int) -> float: 228 pass 229 230 @abc.abstractmethod 231 def get_variable_is_integer(self, variable_id: int) -> bool: 232 pass 233 234 @abc.abstractmethod 235 def get_variable_name(self, variable_id: int) -> str: 236 pass 237 238 @abc.abstractmethod 239 def get_variables(self) -> Iterator[int]: 240 """Yields the variable ids in order of creation.""" 241 pass 242 243 @abc.abstractmethod 244 def add_linear_constraint(self, lb: float, ub: float, name: str) -> int: 245 pass 246 247 @abc.abstractmethod 248 def delete_linear_constraint(self, linear_constraint_id: int) -> None: 249 pass 250 251 @abc.abstractmethod 252 def linear_constraint_exists(self, linear_constraint_id: int) -> bool: 253 pass 254 255 @abc.abstractmethod 256 def next_linear_constraint_id(self) -> int: 257 pass 258 259 @abc.abstractmethod 260 def set_linear_constraint_lb(self, linear_constraint_id: int, lb: float) -> None: 261 pass 262 263 @abc.abstractmethod 264 def set_linear_constraint_ub(self, linear_constraint_id: int, ub: float) -> None: 265 pass 266 267 @abc.abstractmethod 268 def get_linear_constraint_lb(self, linear_constraint_id: int) -> float: 269 pass 270 271 @abc.abstractmethod 272 def get_linear_constraint_ub(self, linear_constraint_id: int) -> float: 273 pass 274 275 @abc.abstractmethod 276 def get_linear_constraint_name(self, linear_constraint_id: int) -> str: 277 pass 278 279 @abc.abstractmethod 280 def get_linear_constraints(self) -> Iterator[int]: 281 """Yields the linear constraint ids in order of creation.""" 282 pass 283 284 @abc.abstractmethod 285 def set_linear_constraint_coefficient( 286 self, linear_constraint_id: int, variable_id: int, lb: float 287 ) -> None: 288 pass 289 290 @abc.abstractmethod 291 def get_linear_constraint_coefficient( 292 self, linear_constraint_id: int, variable_id: int 293 ) -> float: 294 pass 295 296 @abc.abstractmethod 297 def get_linear_constraints_with_variable(self, variable_id: int) -> Iterator[int]: 298 """Yields the linear constraints with nonzero coefficient for a variable in undefined order.""" 299 pass 300 301 @abc.abstractmethod 302 def get_variables_for_linear_constraint( 303 self, linear_constraint_id: int 304 ) -> Iterator[int]: 305 """Yields the variables with nonzero coefficient in a linear constraint in undefined order.""" 306 pass 307 308 @abc.abstractmethod 309 def get_linear_constraint_matrix_entries( 310 self, 311 ) -> Iterator[LinearConstraintMatrixIdEntry]: 312 """Yields the nonzero elements of the linear constraint matrix in undefined order.""" 313 pass 314 315 @abc.abstractmethod 316 def clear_objective(self) -> None: 317 """Clears objective coefficients and offset. Does not change direction.""" 318 319 @abc.abstractmethod 320 def set_linear_objective_coefficient(self, variable_id: int, value: float) -> None: 321 pass 322 323 @abc.abstractmethod 324 def get_linear_objective_coefficient(self, variable_id: int) -> float: 325 pass 326 327 @abc.abstractmethod 328 def get_linear_objective_coefficients(self) -> Iterator[LinearObjectiveEntry]: 329 """Yields the nonzero linear objective terms in undefined order.""" 330 pass 331 332 @abc.abstractmethod 333 def set_quadratic_objective_coefficient( 334 self, first_variable_id: int, second_variable_id: int, value: float 335 ) -> None: 336 """Sets the objective coefficient for the product of two variables. 337 338 The ordering of the input variables does not matter. 339 340 Args: 341 first_variable_id: The first variable in the product. 342 second_variable_id: The second variable in the product. 343 value: The value of the coefficient. 344 345 Raises: 346 BadVariableIdError if first_variable_id or second_variable_id are not in 347 the model. 348 """ 349 350 @abc.abstractmethod 351 def get_quadratic_objective_coefficient( 352 self, first_variable_id: int, second_variable_id: int 353 ) -> float: 354 """Gets the objective coefficient for the product of two variables. 355 356 The ordering of the input variables does not matter. 357 358 Args: 359 first_variable_id: The first variable in the product. 360 second_variable_id: The second variable in the product. 361 362 Raises: 363 BadVariableIdError if first_variable_id or second_variable_id are not in 364 the model. 365 366 Returns: 367 The value of the coefficient. 368 """ 369 370 @abc.abstractmethod 371 def get_quadratic_objective_coefficients(self) -> Iterator[QuadraticEntry]: 372 """Yields the nonzero quadratic objective terms in undefined order.""" 373 374 @abc.abstractmethod 375 def get_quadratic_objective_adjacent_variables( 376 self, variable_id: int 377 ) -> Iterator[int]: 378 """Yields the variables multiplying a variable in the objective function. 379 380 Variables are returned in an unspecified order. 381 382 For example, if variables x and y have ids 0 and 1 respectively, and the 383 quadratic portion of the objective is x^2 + 2 x*y, then 384 get_quadratic_objective_adjacent_variables(0) = (0, 1). 385 386 Args: 387 variable_id: Function yields the variables multiplying variable_id in the 388 objective function. 389 390 Yields: 391 The variables multiplying variable_id in the objective function. 392 393 Raises: 394 BadVariableIdError if variable_id is not in the model. 395 """ 396 397 @abc.abstractmethod 398 def set_is_maximize(self, is_maximize: bool) -> None: 399 pass 400 401 @abc.abstractmethod 402 def get_is_maximize(self) -> bool: 403 pass 404 405 @abc.abstractmethod 406 def set_objective_offset(self, offset: float) -> None: 407 pass 408 409 @abc.abstractmethod 410 def get_objective_offset(self) -> float: 411 pass 412 413 @abc.abstractmethod 414 def export_model(self) -> model_pb2.ModelProto: 415 pass 416 417 @abc.abstractmethod 418 def add_update_tracker(self) -> StorageUpdateTracker: 419 """Creates a StorageUpdateTracker registered with self to view model changes.""" 420 pass 421 422 @abc.abstractmethod 423 def remove_update_tracker(self, tracker: StorageUpdateTracker): 424 """Stops tracker from getting updates on model changes in self. 425 426 An error will be raised if tracker is not a StorageUpdateTracker created by 427 this Model that has not previously been removed. 428 429 Using an UpdateTracker (via checkpoint or export_update) after it has been 430 removed will result in an error. 431 432 Args: 433 tracker: The StorageUpdateTracker to unregister. 434 435 Raises: 436 KeyError: The tracker was created by another model or was already removed. 437 """ 438 pass
An interface for in memory storage of an optimization model.
Most users should not use this class directly and use Model defined in model.py.
Stores an mixed integer programming problem of the form:
{max/min} c*x + d s.t. lb_c <= A * x <= ub_c lb_v <= x <= ub_v x_i integer for i in I
where x is a vector of n decision variables, d is a number, lb_v, ub_v, and c are vectors of n numbers, lb_c and ub_c are vectors of m numbers, A is a m by n matrix, and I is a subset of {1,..., n}.
Each of the n variables and m constraints have an integer id that you use to get/set the problem data (c, A, lb_c etc.). Ids begin at zero and increase sequentially. They are not reused after deletion. Note that if a variable is deleted, your model has nonconsecutive variable ids.
For all methods taking an id (e.g. set_variable_lb), providing a bad id (including the id of a deleted variable) will raise a BadVariableIdError or BadLinearConstraintIdError. Further, the ModelStorage instance is assumed to be in a bad state after any such error and there are no guarantees on further interactions.
All implementations must have a constructor taking a str argument for the model name with a default value of the empty string.
Any ModelStorage can be exported to model_pb2.ModelProto, the format consumed by MathOpt solvers. Changes to a model can be exported to a model_update_pb2.ModelUpdateProto with an UpdateTracker, see the UpdateTracker documentation for details.
When solving this optimization problem we will additionally require that:
- No numbers are NaN,
- c, d, and A are all finite,
- lb_c and lb_v are not +inf,
- ub_c and ub_v are not -inf,
but those assumptions are not checked or enforced here (NaNs and infinite values can be used anywhere).
238 @abc.abstractmethod 239 def get_variables(self) -> Iterator[int]: 240 """Yields the variable ids in order of creation.""" 241 pass
Yields the variable ids in order of creation.
279 @abc.abstractmethod 280 def get_linear_constraints(self) -> Iterator[int]: 281 """Yields the linear constraint ids in order of creation.""" 282 pass
Yields the linear constraint ids in order of creation.
296 @abc.abstractmethod 297 def get_linear_constraints_with_variable(self, variable_id: int) -> Iterator[int]: 298 """Yields the linear constraints with nonzero coefficient for a variable in undefined order.""" 299 pass
Yields the linear constraints with nonzero coefficient for a variable in undefined order.
301 @abc.abstractmethod 302 def get_variables_for_linear_constraint( 303 self, linear_constraint_id: int 304 ) -> Iterator[int]: 305 """Yields the variables with nonzero coefficient in a linear constraint in undefined order.""" 306 pass
Yields the variables with nonzero coefficient in a linear constraint in undefined order.
308 @abc.abstractmethod 309 def get_linear_constraint_matrix_entries( 310 self, 311 ) -> Iterator[LinearConstraintMatrixIdEntry]: 312 """Yields the nonzero elements of the linear constraint matrix in undefined order.""" 313 pass
Yields the nonzero elements of the linear constraint matrix in undefined order.
315 @abc.abstractmethod 316 def clear_objective(self) -> None: 317 """Clears objective coefficients and offset. Does not change direction."""
Clears objective coefficients and offset. Does not change direction.
327 @abc.abstractmethod 328 def get_linear_objective_coefficients(self) -> Iterator[LinearObjectiveEntry]: 329 """Yields the nonzero linear objective terms in undefined order.""" 330 pass
Yields the nonzero linear objective terms in undefined order.
332 @abc.abstractmethod 333 def set_quadratic_objective_coefficient( 334 self, first_variable_id: int, second_variable_id: int, value: float 335 ) -> None: 336 """Sets the objective coefficient for the product of two variables. 337 338 The ordering of the input variables does not matter. 339 340 Args: 341 first_variable_id: The first variable in the product. 342 second_variable_id: The second variable in the product. 343 value: The value of the coefficient. 344 345 Raises: 346 BadVariableIdError if first_variable_id or second_variable_id are not in 347 the model. 348 """
Sets the objective coefficient for the product of two variables.
The ordering of the input variables does not matter.
Arguments:
- first_variable_id: The first variable in the product.
- second_variable_id: The second variable in the product.
- value: The value of the coefficient.
Raises:
- BadVariableIdError if first_variable_id or second_variable_id are not in
- the model.
350 @abc.abstractmethod 351 def get_quadratic_objective_coefficient( 352 self, first_variable_id: int, second_variable_id: int 353 ) -> float: 354 """Gets the objective coefficient for the product of two variables. 355 356 The ordering of the input variables does not matter. 357 358 Args: 359 first_variable_id: The first variable in the product. 360 second_variable_id: The second variable in the product. 361 362 Raises: 363 BadVariableIdError if first_variable_id or second_variable_id are not in 364 the model. 365 366 Returns: 367 The value of the coefficient. 368 """
Gets the objective coefficient for the product of two variables.
The ordering of the input variables does not matter.
Arguments:
- first_variable_id: The first variable in the product.
- second_variable_id: The second variable in the product.
Raises:
- BadVariableIdError if first_variable_id or second_variable_id are not in
- the model.
Returns:
The value of the coefficient.
370 @abc.abstractmethod 371 def get_quadratic_objective_coefficients(self) -> Iterator[QuadraticEntry]: 372 """Yields the nonzero quadratic objective terms in undefined order."""
Yields the nonzero quadratic objective terms in undefined order.
374 @abc.abstractmethod 375 def get_quadratic_objective_adjacent_variables( 376 self, variable_id: int 377 ) -> Iterator[int]: 378 """Yields the variables multiplying a variable in the objective function. 379 380 Variables are returned in an unspecified order. 381 382 For example, if variables x and y have ids 0 and 1 respectively, and the 383 quadratic portion of the objective is x^2 + 2 x*y, then 384 get_quadratic_objective_adjacent_variables(0) = (0, 1). 385 386 Args: 387 variable_id: Function yields the variables multiplying variable_id in the 388 objective function. 389 390 Yields: 391 The variables multiplying variable_id in the objective function. 392 393 Raises: 394 BadVariableIdError if variable_id is not in the model. 395 """
Yields the variables multiplying a variable in the objective function.
Variables are returned in an unspecified order.
For example, if variables x and y have ids 0 and 1 respectively, and the quadratic portion of the objective is x^2 + 2 x*y, then get_quadratic_objective_adjacent_variables(0) = (0, 1).
Arguments:
- variable_id: Function yields the variables multiplying variable_id in the objective function.
Yields:
The variables multiplying variable_id in the objective function.
Raises:
- BadVariableIdError if variable_id is not in the model.
417 @abc.abstractmethod 418 def add_update_tracker(self) -> StorageUpdateTracker: 419 """Creates a StorageUpdateTracker registered with self to view model changes.""" 420 pass
Creates a StorageUpdateTracker registered with self to view model changes.
422 @abc.abstractmethod 423 def remove_update_tracker(self, tracker: StorageUpdateTracker): 424 """Stops tracker from getting updates on model changes in self. 425 426 An error will be raised if tracker is not a StorageUpdateTracker created by 427 this Model that has not previously been removed. 428 429 Using an UpdateTracker (via checkpoint or export_update) after it has been 430 removed will result in an error. 431 432 Args: 433 tracker: The StorageUpdateTracker to unregister. 434 435 Raises: 436 KeyError: The tracker was created by another model or was already removed. 437 """ 438 pass
Stops tracker from getting updates on model changes in self.
An error will be raised if tracker is not a StorageUpdateTracker created by this Model that has not previously been removed.
Using an UpdateTracker (via checkpoint or export_update) after it has been removed will result in an error.
Arguments:
- tracker: The StorageUpdateTracker to unregister.
Raises:
- KeyError: The tracker was created by another model or was already removed.