14"""A minimal pure python implementation of model_storage.ModelStorage."""
16from typing
import Dict, Iterable, Iterator, Optional, Set, Tuple
28 """Tracks model updates for HashModelStorage."""
32 self.
model:
"HashModelStorage" = mod
64 == self.
model.next_linear_constraint_id()
80 result = model_update_pb2.ModelUpdateProto()
86 result.variable_updates.lower_bounds,
90 result.variable_updates.upper_bounds,
94 (vid, self.
model.get_variable_is_integer(vid))
97 result.variable_updates.integers,
102 (cid, self.
model.get_linear_constraint_lb(cid))
105 result.linear_constraint_updates.lower_bounds,
109 (cid, self.
model.get_linear_constraint_ub(cid))
112 result.linear_constraint_updates.upper_bounds,
117 var = self.
model.variables.get(vid)
119 new_vars.append((vid, var))
122 for lin_con_id
in range(
124 self.
model.next_linear_constraint_id(),
126 lin_con = self.
model.linear_constraints.get(lin_con_id)
127 if lin_con
is not None:
128 new_lin_cons.append((lin_con_id, lin_con))
132 result.objective_updates.direction_update = self.
model.get_is_maximize()
134 result.objective_updates.offset_update = self.
model.get_objective_offset()
137 (var, self.
model.get_linear_objective_coefficient(var))
140 result.objective_updates.linear_coefficients,
147 obj_coef = self.
model.linear_objective_coefficient.get(new_var, 0.0)
149 result.objective_updates.linear_coefficients.ids.append(new_var)
150 result.objective_updates.linear_coefficients.values.append(obj_coef)
152 quadratic_objective_updates = [
156 self.
model.get_quadratic_objective_coefficient(key.id1, key.id2),
161 if self.
model.variable_exists(new_var):
162 for other_var
in self.
model.get_quadratic_objective_adjacent_variables(
166 if new_var >= other_var:
168 quadratic_objective_updates.append(
172 self.
model.get_quadratic_objective_coefficient(
177 quadratic_objective_updates.sort()
178 if quadratic_objective_updates:
179 first_var_ids, second_var_ids, coefficients = zip(
180 *quadratic_objective_updates
182 result.objective_updates.quadratic_coefficients.row_ids[:] = first_var_ids
183 result.objective_updates.quadratic_coefficients.column_ids[:] = (
186 result.objective_updates.quadratic_coefficients.coefficients[:] = (
191 (l, v, self.
model.get_linear_constraint_coefficient(l, v))
195 if self.
model.variable_exists(new_var):
196 for lin_con
in self.
model.get_linear_constraints_with_variable(new_var):
197 matrix_updates.append(
201 self.
model.get_linear_constraint_coefficient(
206 for new_lin_con
in range(
208 self.
model.next_linear_constraint_id(),
210 if self.
model.linear_constraint_exists(new_lin_con):
211 for var
in self.
model.get_variables_for_linear_constraint(new_lin_con):
215 matrix_updates.append(
219 self.
model.get_linear_constraint_coefficient(
224 matrix_updates.sort()
226 lin_cons, variables, coefs = zip(*matrix_updates)
227 result.linear_constraint_matrix_updates.row_ids[:] = lin_cons
228 result.linear_constraint_matrix_updates.column_ids[:] = variables
229 result.linear_constraint_matrix_updates.coefficients[:] = coefs
252 """Data specific to each decision variable in the optimization problem."""
254 def __init__(self, lb: float, ub: float, is_integer: bool, name: str) ->
None:
263 """Data specific to each linear constraint in the optimization problem."""
265 def __init__(self, lb: float, ub: float, name: str) ->
None:
273 """Data describing quadratic terms with non-zero coefficients."""
284 """Returns true if and only if there are any quadratic terms with non-zero coefficients."""
288 """Yields the variables multiplying a variable in the stored quadratic terms.
290 If variable_id is not in the model the function yields the empty set.
293 variable_id: Function yields the variables multiplying variable_id in the
294 stored quadratic terms.
297 The variables multiplying variable_id in the stored quadratic terms.
301 def keys(self) -> Iterator[_QuadraticKey]:
302 """Yields the variable-pair keys associated to the stored quadratic terms."""
306 """Yields the stored quadratic terms as QuadraticEntry."""
311 """Updates the data structure to consider variable_id as deleted."""
315 if variable_id != adjacent_variable_id:
321 """Clears the data structure."""
326 self, first_variable_id: int, second_variable_id: int, value: float
328 """Sets the coefficient for the quadratic term associated to the product between two variables.
330 The ordering of the input variables does not matter.
333 first_variable_id: The first variable in the product.
334 second_variable_id: The second variable in the product.
335 value: The value of the coefficient.
338 True if the coefficient is updated, False otherwise.
348 if first_variable_id != second_variable_id:
361 """Gets the objective coefficient for the quadratic term associated to the product between two variables.
363 The ordering of the input variables does not matter.
366 first_variable_id: The first variable in the product.
367 second_variable_id: The second variable in the product.
370 The value of the coefficient.
378 """A simple, pure python implementation of ModelStorage.
381 _linear_constraint_matrix: A dictionary with (linear_constraint_id,
382 variable_id) keys and numeric values, representing the matrix A for the
383 constraints lb_c <= A*x <= ub_c. Invariant: the values have no zeros.
384 linear_objective_coefficient: A dictionary with variable_id keys and
385 numeric values, representing the linear terms in the objective.
386 Invariant: the values have no zeros.
387 _quadratic_objective_coefficients: A data structure containing quadratic
388 terms in the objective.
411 def add_variable(self, lb: float, ub: float, is_integer: bool, name: str) -> int:
422 if variable_id < watcher.variables_checkpoint:
423 watcher.variable_deletes.add(variable_id)
424 watcher.variable_lbs.discard(variable_id)
425 watcher.variable_ubs.discard(variable_id)
426 watcher.variable_integers.discard(variable_id)
427 watcher.linear_objective_coefficients.discard(variable_id)
434 watcher.quadratic_objective_coefficients.discard(key)
435 for lin_con_id
in variable.linear_constraint_nonzeros:
436 if lin_con_id < watcher.linear_constraints_checkpoint:
437 watcher.linear_constraint_matrix.discard(
438 (lin_con_id, variable_id)
441 for lin_con_id
in variable.linear_constraint_nonzeros:
456 if lb == self.
variables[variable_id].lower_bound:
458 self.
variables[variable_id].lower_bound = lb
460 if variable_id < watcher.variables_checkpoint:
461 watcher.variable_lbs.add(variable_id)
465 if ub == self.
variables[variable_id].upper_bound:
467 self.
variables[variable_id].upper_bound = ub
469 if variable_id < watcher.variables_checkpoint:
470 watcher.variable_ubs.add(variable_id)
474 if is_integer == self.
variables[variable_id].is_integer:
476 self.
variables[variable_id].is_integer = is_integer
478 if variable_id < watcher.variables_checkpoint:
479 watcher.variable_integers.add(variable_id)
483 return self.
variables[variable_id].lower_bound
487 return self.
variables[variable_id].upper_bound
491 return self.
variables[variable_id].is_integer
511 if linear_constraint_id < watcher.linear_constraints_checkpoint:
512 watcher.linear_constraint_deletes.add(linear_constraint_id)
513 watcher.linear_constraint_lbs.discard(linear_constraint_id)
514 watcher.linear_constraint_ubs.discard(linear_constraint_id)
515 for var_id
in con.variable_nonzeros:
516 if var_id < watcher.variables_checkpoint:
517 watcher.linear_constraint_matrix.discard(
518 (linear_constraint_id, var_id)
521 for var_id
in con.variable_nonzeros:
522 self.
variables[var_id].linear_constraint_nonzeros.remove(
540 if linear_constraint_id < watcher.linear_constraints_checkpoint:
541 watcher.linear_constraint_lbs.add(linear_constraint_id)
549 if linear_constraint_id < watcher.linear_constraints_checkpoint:
550 watcher.linear_constraint_ubs.add(linear_constraint_id)
568 self, linear_constraint_id: int, variable_id: int, value: float
573 (linear_constraint_id, variable_id), 0.0
578 (linear_constraint_id, variable_id),
None
580 self.
variables[variable_id].linear_constraint_nonzeros.discard(
588 self.
variables[variable_id].linear_constraint_nonzeros.add(
596 variable_id < watcher.variables_checkpoint
597 and linear_constraint_id < watcher.linear_constraints_checkpoint
599 watcher.linear_constraint_matrix.add(
600 (linear_constraint_id, variable_id)
604 self, linear_constraint_id: int, variable_id: int
609 (linear_constraint_id, variable_id), 0.0
614 yield from self.
variables[variable_id].linear_constraint_nonzeros
617 self, linear_constraint_id: int
624 ) -> Iterator[model_storage.LinearConstraintMatrixIdEntry]:
627 linear_constraint_id=constraint,
628 variable_id=variable,
635 if variable_id < watcher.variables_checkpoint:
636 watcher.linear_objective_coefficients.add(variable_id)
640 if key.id2 < watcher.variables_checkpoint:
641 watcher.quadratic_objective_coefficients.add(key)
654 if variable_id < watcher.variables_checkpoint:
655 watcher.linear_objective_coefficients.add(variable_id)
663 ) -> Iterator[model_storage.LinearObjectiveEntry]:
666 variable_id=var_id, coefficient=coef
670 self, first_variable_id: int, second_variable_id: int, value: float
675 first_variable_id, second_variable_id, value
680 max(first_variable_id, second_variable_id)
681 < watcher.variables_checkpoint
683 watcher.quadratic_objective_coefficients.add(
688 self, first_variable_id: int, second_variable_id: int
693 first_variable_id, second_variable_id
698 ) -> Iterator[model_storage.QuadraticEntry]:
702 self, variable_id: int
714 watcher.objective_direction =
True
724 watcher.objective_offset =
True
730 m: model_pb2.ModelProto = model_pb2.ModelProto()
740 m.objective.linear_coefficients.ids.extend(obj_ids)
741 m.objective.linear_coefficients.values.extend(obj_coefs)
743 first_var_ids, second_var_ids, coefficients = zip(
746 (entry.id_key.id1, entry.id_key.id2, entry.coefficient)
751 m.objective.quadratic_coefficients.row_ids.extend(first_var_ids)
752 m.objective.quadratic_coefficients.column_ids.extend(second_var_ids)
753 m.objective.quadratic_coefficients.coefficients.extend(coefficients)
755 flat_matrix_items = [
756 (con_id, var_id, coef)
759 lin_con_ids, var_ids, lin_con_coefs = zip(*sorted(flat_matrix_items))
760 m.linear_constraint_matrix.row_ids.extend(lin_con_ids)
761 m.linear_constraint_matrix.column_ids.extend(var_ids)
762 m.linear_constraint_matrix.coefficients.extend(lin_con_coefs)
771 self, tracker: model_storage.StorageUpdateTracker
774 tracker.retired =
True
786 id_value_pairs: Iterable[Tuple[int, float]],
787 proto: sparse_containers_pb2.SparseDoubleVectorProto,
789 """id_value_pairs must be sorted, proto is filled."""
790 if not id_value_pairs:
792 ids, values = zip(*id_value_pairs)
794 proto.values[:] = values
798 id_value_pairs: Iterable[Tuple[int, bool]],
799 proto: sparse_containers_pb2.SparseBoolVectorProto,
801 """id_value_pairs must be sorted, proto is filled."""
802 if not id_value_pairs:
804 ids, values = zip(*id_value_pairs)
806 proto.values[:] = values
810 variables: Iterable[Tuple[int, _VariableStorage]],
811 proto: model_pb2.VariablesProto,
813 """Exports variables to proto."""
814 has_named_var =
False
815 for _, var_storage
in variables:
819 for var_id, var_storage
in variables:
820 proto.ids.append(var_id)
821 proto.lower_bounds.append(var_storage.lower_bound)
822 proto.upper_bounds.append(var_storage.upper_bound)
823 proto.integers.append(var_storage.is_integer)
825 proto.names.append(var_storage.name)
829 linear_constraints: Iterable[Tuple[int, _LinearConstraintStorage]],
830 proto: model_pb2.LinearConstraintsProto,
832 """Exports variables to proto."""
833 has_named_lin_con =
False
834 for _, lin_con_storage
in linear_constraints:
835 if lin_con_storage.name:
836 has_named_lin_con =
True
838 for lin_con_id, lin_con_storage
in linear_constraints:
839 proto.ids.append(lin_con_id)
840 proto.lower_bounds.append(lin_con_storage.lower_bound)
841 proto.upper_bounds.append(lin_con_storage.upper_bound)
842 if has_named_lin_con:
843 proto.names.append(lin_con_storage.name)