19#include "absl/algorithm/container.h"
20#include "absl/log/log.h"
21#include "absl/status/status.h"
22#include "absl/status/statusor.h"
23#include "absl/strings/string_view.h"
24#include "google/protobuf/map.h"
25#include "google/protobuf/repeated_field.h"
26#include "google/protobuf/repeated_ptr_field.h"
35#include "ortools/math_opt/model.pb.h"
36#include "ortools/math_opt/model_update.pb.h"
37#include "ortools/math_opt/sparse_containers.pb.h"
43absl::string_view SafeName(
44 const google::protobuf::RepeatedPtrField<std::string>& names,
46 if (index >= names.size() || index < 0) {
52template <ElementType e>
53ElementId<e> SafeAddElement(
const int64_t
id,
const absl::string_view name,
55 elemental.EnsureNextElementIdAtLeastUntyped(e,
id);
56 return elemental.AddElement<e>(name);
60std::vector<std::pair<int64_t, const T*>> SortMapByKey(
61 const google::protobuf::Map<int64_t, T>& proto_map) {
62 std::vector<std::pair<int64_t, const T*>> result;
63 result.reserve(proto_map.size());
64 for (
const auto& [
id, val] : proto_map) {
65 result.push_back({id, &val});
71void AddVariables(
const VariablesProto& variables,
Elemental& elemental) {
72 for (
int i = 0;
i < variables.ids_size(); ++
i) {
73 const VariableId id = SafeAddElement<ElementType::kVariable>(
74 variables.ids(i), SafeName(variables.names(), i), elemental);
76 variables.integers(i));
78 variables.lower_bounds(i));
80 variables.upper_bounds(i));
84void AddLinearConstraints(
const LinearConstraintsProto& linear_constraints,
86 for (
int i = 0;
i < linear_constraints.ids_size(); ++
i) {
88 SafeAddElement<ElementType::kLinearConstraint>(
89 linear_constraints.ids(i), SafeName(linear_constraints.names(), i),
92 linear_constraints.lower_bounds(i));
94 linear_constraints.upper_bounds(i));
99 const SparseDoubleVectorProto& vec,
101 for (
int i = 0;
i < vec.ids_size(); ++
i) {
107void SetBoolAttr1FromProto(
const BoolAttr1 attr,
108 const SparseBoolVectorProto& vec,
110 for (
int i = 0;
i < vec.ids_size(); ++
i) {
117template <
typename AttrType>
118void SetDoubleAttr2FromProto(
const AttrType attr,
119 const SparseDoubleMatrixProto& mat,
122 "Attribute must have key size two");
124 std::is_same_v<double, typename AttrTypeDescriptorT<AttrType>::ValueType>,
125 "Attribute must be double valued");
126 for (
int i = 0;
i < mat.row_ids_size(); ++
i) {
129 mat.coefficients(i));
134template <
typename AttrType>
135void SetDoubleAttr2SliceFromProto(
const AttrType attr,
const int64_t first_id,
136 const SparseDoubleVectorProto& slice,
139 "Attribute must have key size two");
141 std::is_same_v<double, typename AttrTypeDescriptorT<AttrType>::ValueType>,
142 "Attribute must be double valued");
143 for (
int i = 0;
i < slice.ids_size(); ++
i) {
145 attr,
AttrKey(first_id, slice.ids(i)), slice.values(i));
150template <
typename AttrType>
151void SetDoubleAttr3SliceFromProto(
const AttrType attr,
const int64_t first_id,
152 const SparseDoubleMatrixProto& slice,
155 "Attribute must have key size three");
157 std::is_same_v<double, typename AttrTypeDescriptorT<AttrType>::ValueType>,
158 "Attribute must be double valued");
159 for (
int i = 0;
i < slice.row_ids_size(); ++
i) {
163 slice.coefficients(i));
167absl::Status SetAuxiliaryObjectives(
168 const google::protobuf::Map<int64_t, ObjectiveProto>& aux_objectives,
170 for (
const auto [proto_id, obj_ptr] : SortMapByKey(aux_objectives)) {
171 const ObjectiveProto& objective = *obj_ptr;
173 SafeAddElement<ElementType::kAuxiliaryObjective>(
174 proto_id, objective.name(), elemental);
175 elemental.SetAttr(BoolAttr1::kAuxObjMaximize,
AttrKey(
id),
176 objective.maximize());
177 elemental.SetAttr(DoubleAttr1::kAuxObjOffset,
AttrKey(
id),
179 SetDoubleAttr2SliceFromProto(DoubleAttr2::kAuxObjLinCoef,
id.value(),
180 objective.linear_coefficients(), elemental);
181 elemental.SetAttr(IntAttr1::kAuxObjPriority,
AttrKey(
id),
182 objective.priority());
183 if (objective.quadratic_coefficients().row_ids_size() > 0) {
185 <<
"quadratic coefficients not supported for auxiliary "
186 "objectives, but found them in objective with id: "
187 <<
id <<
" and name: " << objective.name();
190 return absl::OkStatus();
193void AddQuadraticConstraints(
194 const google::protobuf::Map<int64_t, QuadraticConstraintProto>&
195 quadratic_constraints,
197 for (
const auto [proto_id, quad_con_ptr] :
198 SortMapByKey(quadratic_constraints)) {
199 const QuadraticConstraintProto& quad_con = *quad_con_ptr;
201 SafeAddElement<ElementType::kQuadraticConstraint>(
202 proto_id, quad_con.name(), elemental);
204 quad_con.lower_bound());
206 quad_con.upper_bound());
207 SetDoubleAttr2SliceFromProto(DoubleAttr2::kQuadConLinCoef,
id.value(),
208 quad_con.linear_terms(), elemental);
209 SetDoubleAttr3SliceFromProto(SymmetricDoubleAttr3::kQuadConQuadCoef,
210 id.value(), quad_con.quadratic_terms(),
215void AddIndicatorConstraints(
216 const google::protobuf::Map<int64_t, IndicatorConstraintProto>&
217 indicator_constraints,
219 for (
const auto [proto_id, ind_con_ptr] :
220 SortMapByKey(indicator_constraints)) {
221 const IndicatorConstraintProto& ind_con = *ind_con_ptr;
223 SafeAddElement<ElementType::kIndicatorConstraint>(
224 proto_id, ind_con.name(), elemental);
226 ind_con.lower_bound());
228 ind_con.upper_bound());
229 SetDoubleAttr2SliceFromProto(DoubleAttr2::kIndConLinCoef,
id.value(),
230 ind_con.expression(), elemental);
233 ind_con.activate_on_zero());
234 if (ind_con.has_indicator_id()) {
242absl::StatusOr<Elemental> ElementalFromModelProtoImpl(
const ModelProto& proto) {
244 if (proto.second_order_cone_constraints_size() > 0) {
245 return absl::UnimplementedError(
246 "Elemental does not support second order cone constraints yet");
248 if (proto.sos1_constraints_size() > 0) {
249 return absl::UnimplementedError(
250 "Elemental does not support sos1 constraints yet");
252 if (proto.sos2_constraints_size() > 0) {
253 return absl::UnimplementedError(
254 "Elemental does not support sos2 constraints yet");
256 Elemental elemental(proto.name(), proto.objective().name());
257 AddVariables(proto.variables(), elemental);
259 const ObjectiveProto& objective = proto.objective();
260 elemental.SetAttr(BoolAttr0::kMaximize,
AttrKey(), objective.maximize());
261 elemental.SetAttr(DoubleAttr0::kObjOffset,
AttrKey(), objective.offset());
262 SetDoubleAttr1FromProto(DoubleAttr1::kObjLinCoef,
263 objective.linear_coefficients(), elemental);
264 SetDoubleAttr2FromProto(SymmetricDoubleAttr2::kObjQuadCoef,
265 objective.quadratic_coefficients(), elemental);
266 elemental.SetAttr(IntAttr0::kObjPriority,
AttrKey(), objective.priority());
269 SetAuxiliaryObjectives(proto.auxiliary_objectives(), elemental));
270 AddLinearConstraints(proto.linear_constraints(), elemental);
271 SetDoubleAttr2FromProto(DoubleAttr2::kLinConCoef,
272 proto.linear_constraint_matrix(), elemental);
273 AddQuadraticConstraints(proto.quadratic_constraints(), elemental);
274 AddIndicatorConstraints(proto.indicator_constraints(), elemental);
285 return summary.variables;
287 return summary.linear_constraints;
289 return summary.auxiliary_objectives;
291 return summary.quadratic_constraints;
293 return summary.indicator_constraints;
295 LOG(FATAL) <<
"unreachable";
298absl::StatusOr<ModelSummary> MakeSummary(
const Elemental& elemental) {
300 summary.primary_objective_name = elemental.primary_objective_name();
301 summary.maximize = elemental.GetAttr(BoolAttr0::kMaximize, {});
304 std::vector<int64_t> ids = elemental.AllElementsUntyped(e);
306 for (
const int64_t
id : ids) {
308 elemental.GetElementNameUntyped(e,
id));
316const google::protobuf::RepeatedField<int64_t>& GetDeletedIds(
317 const ElementType e,
const ModelUpdateProto& update_proto) {
320 return update_proto.deleted_variable_ids();
322 return update_proto.deleted_linear_constraint_ids();
324 return update_proto.quadratic_constraint_updates()
325 .deleted_constraint_ids();
327 return update_proto.auxiliary_objectives_updates()
328 .deleted_objective_ids();
330 return update_proto.indicator_constraint_updates()
331 .deleted_constraint_ids();
333 LOG(FATAL) <<
"unreachable";
336template <
typename AtomicConstra
intUpdatesProto>
337bool AtomicConstraintUpdateIsEmpty(
338 const AtomicConstraintUpdatesProto& message) {
339 return message.deleted_constraint_ids_size() == 0 &&
340 message.new_constraints().empty();
343absl::Status ValidateModelUpdateProto(
const Elemental& elemental,
344 const ModelUpdateProto& update_proto) {
345 if (!AtomicConstraintUpdateIsEmpty(
346 update_proto.second_order_cone_constraint_updates())) {
347 return absl::UnimplementedError(
348 "Elemental does not support second order cone constraints yet");
350 if (!AtomicConstraintUpdateIsEmpty(update_proto.sos1_constraint_updates())) {
351 return absl::UnimplementedError(
352 "Elemental does not support sos1 constraints yet");
354 if (!AtomicConstraintUpdateIsEmpty(update_proto.sos2_constraint_updates())) {
355 return absl::UnimplementedError(
356 "Elemental does not support sos2 constraints yet");
363void ApplyObjectiveUpdates(
const ObjectiveUpdatesProto& objective_updates,
365 if (objective_updates.has_direction_update()) {
366 elemental.SetAttr(BoolAttr0::kMaximize, {},
367 objective_updates.direction_update());
369 if (objective_updates.has_offset_update()) {
370 elemental.SetAttr(DoubleAttr0::kObjOffset, {},
371 objective_updates.offset_update());
373 if (objective_updates.has_priority_update()) {
374 elemental.SetAttr(IntAttr0::kObjPriority, {},
375 objective_updates.priority_update());
377 SetDoubleAttr1FromProto(DoubleAttr1::kObjLinCoef,
378 objective_updates.linear_coefficients(), elemental);
379 SetDoubleAttr2FromProto(SymmetricDoubleAttr2::kObjQuadCoef,
380 objective_updates.quadratic_coefficients(),
385absl::Status ApplyAuxiliaryObjectiveUpdates(
386 const ObjectiveUpdatesProto& objective_updates,
const int64_t aux_obj_id,
388 if (objective_updates.quadratic_coefficients().row_ids_size() > 0) {
389 return absl::InvalidArgumentError(
390 "quadratic coefficients are not supported for auxiliary objectives");
392 if (objective_updates.has_direction_update()) {
393 elemental.SetAttr(BoolAttr1::kAuxObjMaximize,
AttrKey(aux_obj_id),
394 objective_updates.direction_update());
396 if (objective_updates.has_offset_update()) {
397 elemental.SetAttr(DoubleAttr1::kAuxObjOffset,
AttrKey(aux_obj_id),
398 objective_updates.offset_update());
400 if (objective_updates.has_priority_update()) {
401 elemental.SetAttr(IntAttr1::kAuxObjPriority,
AttrKey(aux_obj_id),
402 objective_updates.priority_update());
404 SetDoubleAttr2SliceFromProto(DoubleAttr2::kAuxObjLinCoef, aux_obj_id,
405 objective_updates.linear_coefficients(),
407 return absl::OkStatus();
410absl::Status ElementalApplyUpdateProto(
const ModelUpdateProto& update_proto,
414 for (
const int64_t
id : GetDeletedIds(e, update_proto)) {
415 elemental.DeleteElementUntyped(e,
id);
420 SetDoubleAttr1FromProto(DoubleAttr1::kVarLb,
421 update_proto.variable_updates().lower_bounds(),
423 SetDoubleAttr1FromProto(DoubleAttr1::kVarUb,
424 update_proto.variable_updates().upper_bounds(),
426 SetBoolAttr1FromProto(BoolAttr1::kVarInteger,
427 update_proto.variable_updates().integers(), elemental);
429 AddVariables(update_proto.new_variables(), elemental);
432 ApplyObjectiveUpdates(update_proto.objective_updates(), elemental);
433 for (
const auto& [
id, aux_obj_update] :
434 update_proto.auxiliary_objectives_updates().objective_updates()) {
436 ApplyAuxiliaryObjectiveUpdates(aux_obj_update,
id, elemental));
439 update_proto.auxiliary_objectives_updates().new_objectives(), elemental));
442 SetDoubleAttr1FromProto(
443 DoubleAttr1::kLinConLb,
444 update_proto.linear_constraint_updates().lower_bounds(), elemental);
445 SetDoubleAttr1FromProto(
446 DoubleAttr1::kLinConUb,
447 update_proto.linear_constraint_updates().upper_bounds(), elemental);
449 AddLinearConstraints(update_proto.new_linear_constraints(), elemental);
452 SetDoubleAttr2FromProto(DoubleAttr2::kLinConCoef,
453 update_proto.linear_constraint_matrix_updates(),
457 AddQuadraticConstraints(
458 update_proto.quadratic_constraint_updates().new_constraints(), elemental);
459 AddIndicatorConstraints(
460 update_proto.indicator_constraint_updates().new_constraints(), elemental);
461 return absl::OkStatus();
469 return ElementalFromModelProtoImpl(proto);
473 return ElementalApplyUpdateProto(update_proto, *
this);
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
A strongly typed element id. Value type.
absl::Status ApplyUpdateProto(const ModelUpdateProto &update_proto)
Applies the changes to the model in update_proto.
static absl::StatusOr< Elemental > FromModelProto(const ModelProto &proto)
Creates an equivalent Elemental to proto.
An object oriented wrapper for quadratic constraints in ModelStorage.
absl::Status ValidateModelUpdate(const ModelUpdateProto &model_update, ModelSummary &model_summary)
BoolAttr1TypeDescriptor::AttrType BoolAttr1
AttrKey< AttrTypeDescriptorT< AttrType >::kNumKeyElements, typename AttrTypeDescriptorT< AttrType >::Symmetry > AttrKeyFor
The type of the AttrKey for attribute type AttrType.
ElementId< ElementType::kAuxiliaryObjective > AuxiliaryObjectiveId
ElementId< ElementType::kVariable > VariableId
AttrKey(Ints... dims) -> AttrKey< sizeof...(Ints), NoSymmetry >
CTAD for AttrKey(1,2).
DoubleAttr1TypeDescriptor::AttrType DoubleAttr1
ElementId< ElementType::kQuadraticConstraint > QuadraticConstraintId
ElementId< ElementType::kLinearConstraint > LinearConstraintId
constexpr int GetAttrKeySize()
ElementId< ElementType::kIndicatorConstraint > IndicatorConstraintId
absl::StatusOr< ModelSummary > ValidateModel(const ModelProto &model, const bool check_names)
StatusBuilder InvalidArgumentErrorBuilder()