23#include "absl/container/flat_hash_map.h"
24#include "absl/container/flat_hash_set.h"
25#include "absl/log/check.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"
37#include "ortools/math_opt/model.pb.h"
38#include "ortools/math_opt/model_update.pb.h"
39#include "ortools/math_opt/sparse_containers.pb.h"
50absl::StatusOr<std::unique_ptr<ModelStorage>> ModelStorage::FromModelProto(
51 const ModelProto& model_proto) {
58 auto storage = std::make_unique<ModelStorage>(model_proto.name(),
59 model_proto.objective().name());
62 storage->AddVariables(model_proto.variables());
66 model_proto.objective().maximize());
68 model_proto.objective().offset());
69 storage->UpdateLinearObjectiveCoefficients(
71 storage->UpdateQuadraticObjectiveCoefficients(
75 storage->AddAuxiliaryObjectives(model_proto.auxiliary_objectives());
78 storage->AddLinearConstraints(model_proto.linear_constraints());
81 storage->UpdateLinearConstraintCoefficients(
82 model_proto.linear_constraint_matrix());
85 storage->quadratic_constraints_.AddConstraints(
86 model_proto.quadratic_constraints());
89 storage->soc_constraints_.AddConstraints(
90 model_proto.second_order_cone_constraints());
93 storage->sos1_constraints_.AddConstraints(model_proto.sos1_constraints());
94 storage->sos2_constraints_.AddConstraints(model_proto.sos2_constraints());
97 storage->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(
121 set_linear_objective_coefficient(
id, VariableId(var_id),
value);
125void ModelStorage::UpdateQuadraticObjectiveCoefficients(
130 set_quadratic_objective_coefficient(
id, VariableId(
coefficients.row_ids(i)),
136void ModelStorage::UpdateLinearConstraintCoefficients(
140 set_linear_constraint_coefficient(
146std::unique_ptr<ModelStorage> ModelStorage::Clone(
147 const std::optional<absl::string_view> new_name)
const {
148 ModelProto model_proto = ExportModel();
149 if (new_name.has_value()) {
150 model_proto.set_name(std::string(*new_name));
152 absl::StatusOr<std::unique_ptr<ModelStorage>> clone =
153 ModelStorage::FromModelProto(model_proto);
156 CHECK_OK(clone.status());
160 clone.value()->ensure_next_variable_id_at_least(next_variable_id());
161 clone.value()->ensure_next_auxiliary_objective_id_at_least(
162 next_auxiliary_objective_id());
163 clone.value()->ensure_next_linear_constraint_id_at_least(
164 next_linear_constraint_id());
165 clone.value()->ensure_next_constraint_id_at_least(
166 next_constraint_id<QuadraticConstraintId>());
167 clone.value()->ensure_next_constraint_id_at_least(
168 next_constraint_id<SecondOrderConeConstraintId>());
169 clone.value()->ensure_next_constraint_id_at_least(
170 next_constraint_id<Sos1ConstraintId>());
171 clone.value()->ensure_next_constraint_id_at_least(
172 next_constraint_id<Sos2ConstraintId>());
173 clone.value()->ensure_next_constraint_id_at_least(
174 next_constraint_id<IndicatorConstraintId>());
176 return std::move(clone).value();
179VariableId ModelStorage::AddVariable(
const double lower_bound,
181 const bool is_integer,
182 const absl::string_view
name) {
186void ModelStorage::AddVariables(
const VariablesProto& variables) {
187 const bool has_names = !variables.names().empty();
188 for (
int v = 0; v < variables.ids_size(); ++v) {
192 ensure_next_variable_id_at_least(VariableId(variables.ids(v)));
193 AddVariable(variables.lower_bounds(v),
194 variables.upper_bounds(v),
195 variables.integers(v),
196 has_names ? variables.names(v) : absl::string_view());
200void ModelStorage::DeleteVariable(
const VariableId
id) {
201 CHECK(variables_.contains(
id));
202 const auto& trackers = update_trackers_.GetUpdatedTrackers();
205 objectives_.DeleteVariable(
208 linear_constraints_.DeleteVariable(
212 quadratic_constraints_.DeleteVariable(
id);
213 soc_constraints_.DeleteVariable(
id);
214 sos1_constraints_.DeleteVariable(
id);
215 sos2_constraints_.DeleteVariable(
id);
216 indicator_constraints_.DeleteVariable(
id);
222std::vector<VariableId> ModelStorage::variables()
const {
223 return variables_.Variables();
226std::vector<VariableId> ModelStorage::SortedVariables()
const {
227 return variables_.SortedVariables();
230LinearConstraintId ModelStorage::AddLinearConstraint(
232 const absl::string_view
name) {
236void ModelStorage::AddLinearConstraints(
237 const LinearConstraintsProto& linear_constraints) {
238 const bool has_names = !linear_constraints.names().empty();
239 for (
int c = 0;
c < linear_constraints.ids_size(); ++
c) {
243 ensure_next_linear_constraint_id_at_least(
244 LinearConstraintId(linear_constraints.ids(
c)));
247 linear_constraints.lower_bounds(
c),
248 linear_constraints.upper_bounds(
c),
249 has_names ? linear_constraints.names(
c) : absl::string_view());
253void ModelStorage::DeleteLinearConstraint(
const LinearConstraintId
id) {
254 CHECK(linear_constraints_.contains(
id));
255 linear_constraints_.Delete(
id, UpdateAndGetLinearConstraintDiffs());
258std::vector<LinearConstraintId> ModelStorage::LinearConstraints()
const {
259 return linear_constraints_.LinearConstraints();
262std::vector<LinearConstraintId> ModelStorage::SortedLinearConstraints()
const {
263 return linear_constraints_.SortedLinearConstraints();
266void ModelStorage::AddAuxiliaryObjectives(
267 const google::protobuf::Map<int64_t, ObjectiveProto>& objectives) {
269 const AuxiliaryObjectiveId
id = AuxiliaryObjectiveId(raw_id);
270 ensure_next_auxiliary_objective_id_at_least(
id);
271 const ObjectiveProto&
proto = objectives.at(raw_id);
272 AddAuxiliaryObjective(
proto.priority(),
proto.name());
273 set_is_maximize(
id,
proto.maximize());
274 set_objective_offset(
id,
proto.offset());
275 for (
const auto [raw_var_id, coeff] :
277 set_linear_objective_coefficient(
id, VariableId(raw_var_id), coeff);
284ModelProto ModelStorage::ExportModel(
const bool remove_names)
const {
286 result.set_name(name_);
287 *result.mutable_variables() = variables_.Proto();
289 auto [primary, auxiliary] = objectives_.Proto();
290 *result.mutable_objective() = std::move(primary);
291 *result.mutable_auxiliary_objectives() = std::move(auxiliary);
294 auto [constraints, matrix] = linear_constraints_.Proto();
295 *result.mutable_linear_constraints() = std::move(constraints);
296 *result.mutable_linear_constraint_matrix() = std::move(matrix);
298 *result.mutable_quadratic_constraints() = quadratic_constraints_.Proto();
299 *result.mutable_second_order_cone_constraints() = soc_constraints_.Proto();
300 *result.mutable_sos1_constraints() = sos1_constraints_.Proto();
301 *result.mutable_sos2_constraints() = sos2_constraints_.Proto();
302 *result.mutable_indicator_constraints() = indicator_constraints_.Proto();
312std::optional<ModelUpdateProto>
313ModelStorage::UpdateTrackerData::ExportModelUpdate(
314 const ModelStorage& storage,
const bool remove_names)
const {
318 if (storage.variables_.diff_is_empty(dirty_variables) &&
319 storage.objectives_.diff_is_empty(dirty_objective) &&
320 storage.linear_constraints_.diff_is_empty(dirty_linear_constraints) &&
321 storage.quadratic_constraints_.diff_is_empty(
322 dirty_quadratic_constraints) &&
323 storage.soc_constraints_.diff_is_empty(dirty_soc_constraints) &&
324 storage.sos1_constraints_.diff_is_empty(dirty_sos1_constraints) &&
325 storage.sos2_constraints_.diff_is_empty(dirty_sos2_constraints) &&
326 storage.indicator_constraints_.diff_is_empty(
327 dirty_indicator_constraints)) {
331 ModelUpdateProto result;
335 VariableStorage::UpdateResult variable_update =
336 storage.variables_.Update(dirty_variables);
337 *result.mutable_deleted_variable_ids() = std::move(variable_update.deleted);
338 *result.mutable_variable_updates() = std::move(variable_update.updates);
339 *result.mutable_new_variables() = std::move(variable_update.creates);
341 const std::vector<VariableId> new_variables =
342 storage.variables_.VariablesFrom(dirty_variables.checkpoint);
346 LinearConstraintStorage::UpdateResult lin_con_update =
347 storage.linear_constraints_.Update(
348 dirty_linear_constraints, dirty_variables.deleted, new_variables);
349 *result.mutable_deleted_linear_constraint_ids() =
350 std::move(lin_con_update.deleted);
351 *result.mutable_linear_constraint_updates() =
352 std::move(lin_con_update.updates);
353 *result.mutable_new_linear_constraints() =
354 std::move(lin_con_update.creates);
355 *result.mutable_linear_constraint_matrix_updates() =
356 std::move(lin_con_update.matrix_updates);
360 *result.mutable_quadratic_constraint_updates() =
361 storage.quadratic_constraints_.Update(dirty_quadratic_constraints);
364 *result.mutable_second_order_cone_constraint_updates() =
365 storage.soc_constraints_.Update(dirty_soc_constraints);
368 *result.mutable_sos1_constraint_updates() =
369 storage.sos1_constraints_.Update(dirty_sos1_constraints);
370 *result.mutable_sos2_constraint_updates() =
371 storage.sos2_constraints_.Update(dirty_sos2_constraints);
374 *result.mutable_indicator_constraint_updates() =
375 storage.indicator_constraints_.Update(dirty_indicator_constraints);
379 auto [primary, auxiliary] = storage.objectives_.Update(
380 dirty_objective, dirty_variables.deleted, new_variables);
381 *result.mutable_objective_updates() = std::move(primary);
382 *result.mutable_auxiliary_objectives_updates() = std::move(auxiliary);
388 return {std::move(result)};
391void ModelStorage::UpdateTrackerData::AdvanceCheckpoint(
392 const ModelStorage& storage) {
393 storage.variables_.AdvanceCheckpointInDiff(dirty_variables);
394 storage.objectives_.AdvanceCheckpointInDiff(dirty_variables.checkpoint,
396 storage.linear_constraints_.AdvanceCheckpointInDiff(
397 dirty_variables.checkpoint, dirty_linear_constraints);
398 storage.quadratic_constraints_.AdvanceCheckpointInDiff(
399 dirty_quadratic_constraints);
400 storage.soc_constraints_.AdvanceCheckpointInDiff(dirty_soc_constraints);
401 storage.sos1_constraints_.AdvanceCheckpointInDiff(dirty_sos1_constraints);
402 storage.sos2_constraints_.AdvanceCheckpointInDiff(dirty_sos2_constraints);
403 storage.indicator_constraints_.AdvanceCheckpointInDiff(
404 dirty_indicator_constraints);
407UpdateTrackerId ModelStorage::NewUpdateTracker() {
408 return update_trackers_.NewUpdateTracker(
409 variables_, objectives_, linear_constraints_, quadratic_constraints_,
410 soc_constraints_, sos1_constraints_, sos2_constraints_,
411 indicator_constraints_);
414void ModelStorage::DeleteUpdateTracker(
const UpdateTrackerId update_tracker) {
415 update_trackers_.DeleteUpdateTracker(update_tracker);
418std::optional<ModelUpdateProto> ModelStorage::ExportModelUpdate(
419 const UpdateTrackerId update_tracker,
const bool remove_names)
const {
420 return update_trackers_.GetData(update_tracker)
421 .ExportModelUpdate(*
this, remove_names);
424void ModelStorage::AdvanceCheckpoint(UpdateTrackerId update_tracker) {
425 update_trackers_.GetData(update_tracker).AdvanceCheckpoint(*
this);
428absl::Status ModelStorage::ApplyUpdateProto(
429 const ModelUpdateProto& update_proto) {
433 ModelSummary summary(
false);
435 for (
const VariableId
id : SortedVariables()) {
436 RETURN_IF_ERROR(summary.variables.Insert(
id.value(), variable_name(
id)))
437 <<
"invalid variable id in model";
440 summary.variables.SetNextFreeId(variables_.next_id().value()));
441 for (
const AuxiliaryObjectiveId
id : SortedAuxiliaryObjectives()) {
443 summary.auxiliary_objectives.Insert(
id.value(), objective_name(
id)))
444 <<
"invalid auxiliary objective id in model";
447 objectives_.next_id().value()));
448 for (
const LinearConstraintId
id : SortedLinearConstraints()) {
450 id.value(), linear_constraint_name(
id)))
451 <<
"invalid linear constraint id in model";
454 linear_constraints_.next_id().value()));
455 for (
const auto id : SortedConstraints<QuadraticConstraintId>()) {
457 id.value(), quadratic_constraints_.data(
id).name))
458 <<
"invalid quadratic constraint id in model";
461 quadratic_constraints_.next_id().value()));
462 for (
const auto id : SortedConstraints<SecondOrderConeConstraintId>()) {
464 id.value(), soc_constraints_.data(
id).name))
465 <<
"invalid second-order cone constraint id in model";
468 soc_constraints_.next_id().value()));
469 for (
const Sos1ConstraintId
id : SortedConstraints<Sos1ConstraintId>()) {
471 id.value(), constraint_data(
id).
name()))
472 <<
"invalid SOS1 constraint id in model";
475 sos1_constraints_.next_id().value()));
476 for (
const Sos2ConstraintId
id : SortedConstraints<Sos2ConstraintId>()) {
478 id.value(), constraint_data(
id).
name()))
479 <<
"invalid SOS2 constraint id in model";
482 sos2_constraints_.next_id().value()));
484 for (
const IndicatorConstraintId
id :
485 SortedConstraints<IndicatorConstraintId>()) {
487 id.value(), constraint_data(
id).
name));
490 indicator_constraints_.next_id().value()));
493 <<
"update not valid";
497 for (
const int64_t v_id : update_proto.deleted_variable_ids()) {
498 DeleteVariable(VariableId(v_id));
500 for (
const int64_t o_id :
501 update_proto.auxiliary_objectives_updates().deleted_objective_ids()) {
502 DeleteAuxiliaryObjective(AuxiliaryObjectiveId(o_id));
504 for (
const int64_t c_id : update_proto.deleted_linear_constraint_ids()) {
505 DeleteLinearConstraint(LinearConstraintId(c_id));
507 for (
const int64_t c_id :
508 update_proto.quadratic_constraint_updates().deleted_constraint_ids()) {
509 DeleteAtomicConstraint(QuadraticConstraintId(c_id));
511 for (
const int64_t c_id : update_proto.second_order_cone_constraint_updates()
512 .deleted_constraint_ids()) {
513 DeleteAtomicConstraint(SecondOrderConeConstraintId(c_id));
515 for (
const int64_t c_id :
516 update_proto.sos1_constraint_updates().deleted_constraint_ids()) {
517 DeleteAtomicConstraint(Sos1ConstraintId(c_id));
519 for (
const int64_t c_id :
520 update_proto.sos2_constraint_updates().deleted_constraint_ids()) {
521 DeleteAtomicConstraint(Sos2ConstraintId(c_id));
523 for (
const int64_t c_id :
524 update_proto.indicator_constraint_updates().deleted_constraint_ids()) {
525 DeleteAtomicConstraint(IndicatorConstraintId(c_id));
529 for (
const auto [v_id, lb] :
530 MakeView(update_proto.variable_updates().lower_bounds())) {
531 set_variable_lower_bound(VariableId(v_id), lb);
533 for (
const auto [v_id, ub] :
534 MakeView(update_proto.variable_updates().upper_bounds())) {
535 set_variable_upper_bound(VariableId(v_id), ub);
537 for (
const auto [v_id, is_integer] :
538 MakeView(update_proto.variable_updates().integers())) {
539 set_variable_is_integer(VariableId(v_id), is_integer);
543 for (
const auto [c_id, lb] :
544 MakeView(update_proto.linear_constraint_updates().lower_bounds())) {
545 set_linear_constraint_lower_bound(LinearConstraintId(c_id), lb);
547 for (
const auto [c_id, ub] :
548 MakeView(update_proto.linear_constraint_updates().upper_bounds())) {
549 set_linear_constraint_upper_bound(LinearConstraintId(c_id), ub);
553 AddVariables(update_proto.new_variables());
554 AddAuxiliaryObjectives(
555 update_proto.auxiliary_objectives_updates().new_objectives());
556 AddLinearConstraints(update_proto.new_linear_constraints());
557 quadratic_constraints_.AddConstraints(
558 update_proto.quadratic_constraint_updates().new_constraints());
559 soc_constraints_.AddConstraints(
560 update_proto.second_order_cone_constraint_updates().new_constraints());
561 sos1_constraints_.AddConstraints(
562 update_proto.sos1_constraint_updates().new_constraints());
563 sos2_constraints_.AddConstraints(
564 update_proto.sos2_constraint_updates().new_constraints());
565 indicator_constraints_.AddConstraints(
566 update_proto.indicator_constraint_updates().new_constraints());
572 for (
const auto& [raw_id, objective_update] :
573 update_proto.auxiliary_objectives_updates().objective_updates()) {
574 UpdateObjective(AuxiliaryObjectiveId(raw_id), objective_update);
578 UpdateLinearConstraintCoefficients(
579 update_proto.linear_constraint_matrix_updates());
581 return absl::OkStatus();
#define RETURN_IF_ERROR(expr)
CpModelProto proto
The output proto.
const std::string name
A name for logging purposes.
absl::Span< const double > coefficients
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.