24#include "absl/log/check.h"
25#include "absl/memory/memory.h"
26#include "absl/status/status.h"
27#include "absl/status/statusor.h"
28#include "absl/strings/string_view.h"
29#include "absl/types/span.h"
36#include "ortools/math_opt/model.pb.h"
37#include "ortools/math_opt/model_update.pb.h"
38#include "ortools/math_opt/sparse_containers.pb.h"
48absl::StatusOr<std::unique_ptr<ModelStorage>> ModelStorage::FromModelProto(
49 const ModelProto& model_proto) {
56 auto storage = std::make_unique<ModelStorage>(model_proto.name(),
57 model_proto.objective().name());
60 storage->AddVariables(model_proto.variables());
64 model_proto.objective().maximize());
66 model_proto.objective().offset());
67 storage->UpdateLinearObjectiveCoefficients(
69 storage->UpdateQuadraticObjectiveCoefficients(
73 storage->AddAuxiliaryObjectives(model_proto.auxiliary_objectives());
76 storage->AddLinearConstraints(model_proto.linear_constraints());
79 storage->UpdateLinearConstraintCoefficients(
80 model_proto.linear_constraint_matrix());
83 storage->copyable_data_.quadratic_constraints.AddConstraints(
84 model_proto.quadratic_constraints());
87 storage->copyable_data_.soc_constraints.AddConstraints(
88 model_proto.second_order_cone_constraints());
91 storage->copyable_data_.sos1_constraints.AddConstraints(
92 model_proto.sos1_constraints());
93 storage->copyable_data_.sos2_constraints.AddConstraints(
94 model_proto.sos2_constraints());
97 storage->copyable_data_.indicator_constraints.AddConstraints(
98 model_proto.indicator_constraints());
103void ModelStorage::UpdateObjective(
const ObjectiveId id,
104 const ObjectiveUpdatesProto& update) {
105 if (update.has_direction_update()) {
106 set_is_maximize(
id, update.direction_update());
108 if (update.has_priority_update()) {
109 set_objective_priority(
id, update.priority_update());
111 if (update.has_offset_update()) {
112 set_objective_offset(
id, update.offset_update());
114 UpdateLinearObjectiveCoefficients(
id, update.linear_coefficients());
115 UpdateQuadraticObjectiveCoefficients(
id, update.quadratic_coefficients());
118void ModelStorage::UpdateLinearObjectiveCoefficients(
119 const ObjectiveId id,
const SparseDoubleVectorProto& coefficients) {
120 for (
const auto [var_id, value] :
MakeView(coefficients)) {
121 set_linear_objective_coefficient(
id, VariableId(var_id), value);
125void ModelStorage::UpdateQuadraticObjectiveCoefficients(
126 const ObjectiveId id,
const SparseDoubleMatrixProto& coefficients) {
127 for (
int i = 0;
i < coefficients.row_ids_size(); ++
i) {
130 set_quadratic_objective_coefficient(
id, VariableId(coefficients.row_ids(i)),
131 VariableId(coefficients.column_ids(i)),
132 coefficients.coefficients(i));
136void ModelStorage::UpdateLinearConstraintCoefficients(
137 const SparseDoubleMatrixProto& coefficients) {
138 for (
int i = 0;
i < coefficients.row_ids_size(); ++
i) {
140 set_linear_constraint_coefficient(
141 LinearConstraintId(coefficients.row_ids(i)),
142 VariableId(coefficients.column_ids(i)), coefficients.coefficients(i));
146std::unique_ptr<ModelStorage> ModelStorage::Clone(
147 const std::optional<absl::string_view> new_name)
const {
150 std::unique_ptr<ModelStorage> clone =
151 absl::WrapUnique(
new ModelStorage(*
this));
152 if (new_name.has_value()) {
153 clone->copyable_data_.name = *new_name;
158VariableId ModelStorage::AddVariable(
const double lower_bound,
159 const double upper_bound,
160 const bool is_integer,
161 const absl::string_view name) {
162 return copyable_data_.variables.Add(lower_bound, upper_bound, is_integer,
166void ModelStorage::AddVariables(
const VariablesProto& variables) {
167 const bool has_names = !variables.names().empty();
168 for (
int v = 0; v < variables.ids_size(); ++v) {
172 ensure_next_variable_id_at_least(VariableId(variables.ids(v)));
173 AddVariable(variables.lower_bounds(v),
174 variables.upper_bounds(v),
175 variables.integers(v),
176 has_names ? variables.names(v) : absl::string_view());
180void ModelStorage::DeleteVariable(
const VariableId
id) {
181 CHECK(copyable_data_.variables.contains(
id));
182 const auto& trackers = update_trackers_.GetUpdatedTrackers();
185 copyable_data_.objectives.DeleteVariable(
188 copyable_data_.linear_constraints.DeleteVariable(
192 copyable_data_.quadratic_constraints.DeleteVariable(
id);
193 copyable_data_.soc_constraints.DeleteVariable(
id);
194 copyable_data_.sos1_constraints.DeleteVariable(
id);
195 copyable_data_.sos2_constraints.DeleteVariable(
id);
196 copyable_data_.indicator_constraints.DeleteVariable(
id);
197 copyable_data_.variables.Delete(
202std::vector<VariableId> ModelStorage::variables()
const {
203 return copyable_data_.variables.Variables();
206std::vector<VariableId> ModelStorage::SortedVariables()
const {
207 return copyable_data_.variables.SortedVariables();
210LinearConstraintId ModelStorage::AddLinearConstraint(
211 const double lower_bound,
const double upper_bound,
212 const absl::string_view name) {
213 return copyable_data_.linear_constraints.Add(lower_bound, upper_bound, name);
216void ModelStorage::AddLinearConstraints(
217 const LinearConstraintsProto& linear_constraints) {
218 const bool has_names = !linear_constraints.names().empty();
219 for (
int c = 0;
c < linear_constraints.ids_size(); ++
c) {
223 ensure_next_linear_constraint_id_at_least(
224 LinearConstraintId(linear_constraints.ids(
c)));
227 linear_constraints.lower_bounds(
c),
228 linear_constraints.upper_bounds(
c),
229 has_names ? linear_constraints.names(
c) : absl::string_view());
233void ModelStorage::DeleteLinearConstraint(
const LinearConstraintId
id) {
234 CHECK(copyable_data_.linear_constraints.contains(
id));
235 copyable_data_.linear_constraints.Delete(
id,
236 UpdateAndGetLinearConstraintDiffs());
239std::vector<LinearConstraintId> ModelStorage::LinearConstraints()
const {
240 return copyable_data_.linear_constraints.LinearConstraints();
243std::vector<LinearConstraintId> ModelStorage::SortedLinearConstraints()
const {
244 return copyable_data_.linear_constraints.SortedLinearConstraints();
247void ModelStorage::AddAuxiliaryObjectives(
248 const google::protobuf::Map<int64_t, ObjectiveProto>& objectives) {
250 const AuxiliaryObjectiveId
id = AuxiliaryObjectiveId(raw_id);
251 ensure_next_auxiliary_objective_id_at_least(
id);
252 const ObjectiveProto& proto = objectives.at(raw_id);
253 AddAuxiliaryObjective(proto.priority(), proto.name());
254 set_is_maximize(
id, proto.maximize());
255 set_objective_offset(
id, proto.offset());
256 for (
const auto [raw_var_id, coeff] :
257 MakeView(proto.linear_coefficients())) {
258 set_linear_objective_coefficient(
id, VariableId(raw_var_id), coeff);
265ModelProto ModelStorage::ExportModel(
const bool remove_names)
const {
267 result.set_name(copyable_data_.name);
268 *result.mutable_variables() = copyable_data_.variables.Proto();
270 auto [primary, auxiliary] = copyable_data_.objectives.Proto();
271 *result.mutable_objective() = std::move(primary);
272 *result.mutable_auxiliary_objectives() = std::move(auxiliary);
275 auto [constraints, matrix] = copyable_data_.linear_constraints.Proto();
276 *result.mutable_linear_constraints() = std::move(constraints);
277 *result.mutable_linear_constraint_matrix() = std::move(matrix);
279 *result.mutable_quadratic_constraints() =
280 copyable_data_.quadratic_constraints.Proto();
281 *result.mutable_second_order_cone_constraints() =
282 copyable_data_.soc_constraints.Proto();
283 *result.mutable_sos1_constraints() = copyable_data_.sos1_constraints.Proto();
284 *result.mutable_sos2_constraints() = copyable_data_.sos2_constraints.Proto();
285 *result.mutable_indicator_constraints() =
286 copyable_data_.indicator_constraints.Proto();
296std::optional<ModelUpdateProto>
297ModelStorage::UpdateTrackerData::ExportModelUpdate(
298 const ModelStorage& storage,
const bool remove_names)
const {
302 if (storage.copyable_data_.variables.diff_is_empty(dirty_variables) &&
303 storage.copyable_data_.objectives.diff_is_empty(dirty_objective) &&
304 storage.copyable_data_.linear_constraints.diff_is_empty(
305 dirty_linear_constraints) &&
306 storage.copyable_data_.quadratic_constraints.diff_is_empty(
307 dirty_quadratic_constraints) &&
308 storage.copyable_data_.soc_constraints.diff_is_empty(
309 dirty_soc_constraints) &&
310 storage.copyable_data_.sos1_constraints.diff_is_empty(
311 dirty_sos1_constraints) &&
312 storage.copyable_data_.sos2_constraints.diff_is_empty(
313 dirty_sos2_constraints) &&
314 storage.copyable_data_.indicator_constraints.diff_is_empty(
315 dirty_indicator_constraints)) {
319 ModelUpdateProto result;
324 storage.copyable_data_.variables.Update(dirty_variables);
325 *result.mutable_deleted_variable_ids() = std::move(variable_update.deleted);
326 *result.mutable_variable_updates() = std::move(variable_update.updates);
327 *result.mutable_new_variables() = std::move(variable_update.creates);
329 const std::vector<VariableId> new_variables =
330 storage.copyable_data_.variables.VariablesFrom(
331 dirty_variables.checkpoint);
336 storage.copyable_data_.linear_constraints.Update(
337 dirty_linear_constraints, dirty_variables.deleted, new_variables);
338 *result.mutable_deleted_linear_constraint_ids() =
339 std::move(lin_con_update.deleted);
340 *result.mutable_linear_constraint_updates() =
341 std::move(lin_con_update.updates);
342 *result.mutable_new_linear_constraints() =
343 std::move(lin_con_update.creates);
344 *result.mutable_linear_constraint_matrix_updates() =
345 std::move(lin_con_update.matrix_updates);
349 *result.mutable_quadratic_constraint_updates() =
350 storage.copyable_data_.quadratic_constraints.Update(
351 dirty_quadratic_constraints);
354 *result.mutable_second_order_cone_constraint_updates() =
355 storage.copyable_data_.soc_constraints.Update(dirty_soc_constraints);
358 *result.mutable_sos1_constraint_updates() =
359 storage.copyable_data_.sos1_constraints.Update(dirty_sos1_constraints);
360 *result.mutable_sos2_constraint_updates() =
361 storage.copyable_data_.sos2_constraints.Update(dirty_sos2_constraints);
364 *result.mutable_indicator_constraint_updates() =
365 storage.copyable_data_.indicator_constraints.Update(
366 dirty_indicator_constraints);
370 auto [primary, auxiliary] = storage.copyable_data_.objectives.Update(
371 dirty_objective, dirty_variables.deleted, new_variables);
372 *result.mutable_objective_updates() = std::move(primary);
373 *result.mutable_auxiliary_objectives_updates() = std::move(auxiliary);
379 return {std::move(result)};
382void ModelStorage::UpdateTrackerData::AdvanceCheckpoint(
383 const ModelStorage& storage) {
384 storage.copyable_data_.variables.AdvanceCheckpointInDiff(dirty_variables);
385 storage.copyable_data_.objectives.AdvanceCheckpointInDiff(
386 dirty_variables.checkpoint, dirty_objective);
387 storage.copyable_data_.linear_constraints.AdvanceCheckpointInDiff(
388 dirty_variables.checkpoint, dirty_linear_constraints);
389 storage.copyable_data_.quadratic_constraints.AdvanceCheckpointInDiff(
390 dirty_quadratic_constraints);
391 storage.copyable_data_.soc_constraints.AdvanceCheckpointInDiff(
392 dirty_soc_constraints);
393 storage.copyable_data_.sos1_constraints.AdvanceCheckpointInDiff(
394 dirty_sos1_constraints);
395 storage.copyable_data_.sos2_constraints.AdvanceCheckpointInDiff(
396 dirty_sos2_constraints);
397 storage.copyable_data_.indicator_constraints.AdvanceCheckpointInDiff(
398 dirty_indicator_constraints);
401UpdateTrackerId ModelStorage::NewUpdateTracker() {
402 return update_trackers_.NewUpdateTracker(
403 copyable_data_.variables, copyable_data_.objectives,
404 copyable_data_.linear_constraints, copyable_data_.quadratic_constraints,
405 copyable_data_.soc_constraints, copyable_data_.sos1_constraints,
406 copyable_data_.sos2_constraints, copyable_data_.indicator_constraints);
409void ModelStorage::DeleteUpdateTracker(
const UpdateTrackerId update_tracker) {
410 update_trackers_.DeleteUpdateTracker(update_tracker);
413std::optional<ModelUpdateProto> ModelStorage::ExportModelUpdate(
414 const UpdateTrackerId update_tracker,
const bool remove_names)
const {
415 return update_trackers_.GetData(update_tracker)
416 .ExportModelUpdate(*
this, remove_names);
419void ModelStorage::AdvanceCheckpoint(UpdateTrackerId update_tracker) {
420 update_trackers_.GetData(update_tracker).AdvanceCheckpoint(*
this);
423absl::Status ModelStorage::ApplyUpdateProto(
424 const ModelUpdateProto& update_proto) {
430 for (
const VariableId
id : SortedVariables()) {
431 RETURN_IF_ERROR(summary.variables.Insert(
id.value(), variable_name(
id)))
432 <<
"invalid variable id in model";
435 copyable_data_.variables.next_id().value()));
436 for (
const AuxiliaryObjectiveId
id : SortedAuxiliaryObjectives()) {
438 summary.auxiliary_objectives.Insert(
id.value(), objective_name(
id)))
439 <<
"invalid auxiliary objective id in model";
442 copyable_data_.objectives.next_id().value()));
443 for (
const LinearConstraintId
id : SortedLinearConstraints()) {
445 id.value(), linear_constraint_name(
id)))
446 <<
"invalid linear constraint id in model";
449 copyable_data_.linear_constraints.next_id().value()));
450 for (
const auto id : SortedConstraints<QuadraticConstraintId>()) {
452 id.value(), copyable_data_.quadratic_constraints.data(
id).name))
453 <<
"invalid quadratic constraint id in model";
456 copyable_data_.quadratic_constraints.next_id().value()));
457 for (
const auto id : SortedConstraints<SecondOrderConeConstraintId>()) {
459 id.value(), copyable_data_.soc_constraints.data(
id).name))
460 <<
"invalid second-order cone constraint id in model";
463 copyable_data_.soc_constraints.next_id().value()));
464 for (
const Sos1ConstraintId
id : SortedConstraints<Sos1ConstraintId>()) {
466 id.value(), constraint_data(
id).name()))
467 <<
"invalid SOS1 constraint id in model";
470 copyable_data_.sos1_constraints.next_id().value()));
471 for (
const Sos2ConstraintId
id : SortedConstraints<Sos2ConstraintId>()) {
473 id.value(), constraint_data(
id).name()))
474 <<
"invalid SOS2 constraint id in model";
477 copyable_data_.sos2_constraints.next_id().value()));
479 for (
const IndicatorConstraintId
id :
480 SortedConstraints<IndicatorConstraintId>()) {
482 id.value(), constraint_data(
id).name));
485 copyable_data_.indicator_constraints.next_id().value()));
488 <<
"update not valid";
492 for (
const int64_t v_id : update_proto.deleted_variable_ids()) {
493 DeleteVariable(VariableId(v_id));
495 for (
const int64_t o_id :
496 update_proto.auxiliary_objectives_updates().deleted_objective_ids()) {
497 DeleteAuxiliaryObjective(AuxiliaryObjectiveId(o_id));
499 for (
const int64_t c_id : update_proto.deleted_linear_constraint_ids()) {
500 DeleteLinearConstraint(LinearConstraintId(c_id));
502 for (
const int64_t c_id :
503 update_proto.quadratic_constraint_updates().deleted_constraint_ids()) {
504 DeleteAtomicConstraint(QuadraticConstraintId(c_id));
506 for (
const int64_t c_id : update_proto.second_order_cone_constraint_updates()
507 .deleted_constraint_ids()) {
508 DeleteAtomicConstraint(SecondOrderConeConstraintId(c_id));
510 for (
const int64_t c_id :
511 update_proto.sos1_constraint_updates().deleted_constraint_ids()) {
512 DeleteAtomicConstraint(Sos1ConstraintId(c_id));
514 for (
const int64_t c_id :
515 update_proto.sos2_constraint_updates().deleted_constraint_ids()) {
516 DeleteAtomicConstraint(Sos2ConstraintId(c_id));
518 for (
const int64_t c_id :
519 update_proto.indicator_constraint_updates().deleted_constraint_ids()) {
520 DeleteAtomicConstraint(IndicatorConstraintId(c_id));
524 for (
const auto [v_id, lb] :
525 MakeView(update_proto.variable_updates().lower_bounds())) {
526 set_variable_lower_bound(VariableId(v_id), lb);
528 for (
const auto [v_id, ub] :
529 MakeView(update_proto.variable_updates().upper_bounds())) {
530 set_variable_upper_bound(VariableId(v_id), ub);
532 for (
const auto [v_id, is_integer] :
533 MakeView(update_proto.variable_updates().integers())) {
534 set_variable_is_integer(VariableId(v_id), is_integer);
538 for (
const auto [c_id, lb] :
539 MakeView(update_proto.linear_constraint_updates().lower_bounds())) {
540 set_linear_constraint_lower_bound(LinearConstraintId(c_id), lb);
542 for (
const auto [c_id, ub] :
543 MakeView(update_proto.linear_constraint_updates().upper_bounds())) {
544 set_linear_constraint_upper_bound(LinearConstraintId(c_id), ub);
548 AddVariables(update_proto.new_variables());
549 AddAuxiliaryObjectives(
550 update_proto.auxiliary_objectives_updates().new_objectives());
551 AddLinearConstraints(update_proto.new_linear_constraints());
552 copyable_data_.quadratic_constraints.AddConstraints(
553 update_proto.quadratic_constraint_updates().new_constraints());
554 copyable_data_.soc_constraints.AddConstraints(
555 update_proto.second_order_cone_constraint_updates().new_constraints());
556 copyable_data_.sos1_constraints.AddConstraints(
557 update_proto.sos1_constraint_updates().new_constraints());
558 copyable_data_.sos2_constraints.AddConstraints(
559 update_proto.sos2_constraint_updates().new_constraints());
560 copyable_data_.indicator_constraints.AddConstraints(
561 update_proto.indicator_constraint_updates().new_constraints());
567 for (
const auto& [raw_id, objective_update] :
568 update_proto.auxiliary_objectives_updates().objective_updates()) {
569 UpdateObjective(AuxiliaryObjectiveId(raw_id), objective_update);
573 UpdateLinearConstraintCoefficients(
574 update_proto.linear_constraint_matrix_updates());
576 return absl::OkStatus();
#define RETURN_IF_ERROR(expr)
An object oriented wrapper for quadratic constraints in ModelStorage.
absl::Status ValidateModelUpdate(const ModelUpdateProto &model_update, ModelSummary &model_summary)
constexpr ObjectiveId kPrimaryObjectiveId
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
auto MakeUpdateDataFieldRange(const UpdateTrackers &trackers)
void RemoveNames(ModelProto &model)
Removes the model, variables and constraints names of the provided model.
std::optional< AuxiliaryObjectiveId > ObjectiveId
nullopt denotes the primary objective.
std::vector< K > SortedMapKeys(const absl::flat_hash_map< K, V > &in_map)
absl::StatusOr< ModelSummary > ValidateModel(const ModelProto &model, const bool check_names)
In SWIG mode, we don't want anything besides these top-level includes.