20 #include "absl/container/flat_hash_map.h"
21 #include "absl/container/flat_hash_set.h"
22 #include "absl/status/status.h"
23 #include "absl/strings/match.h"
24 #include "absl/strings/str_cat.h"
25 #include "absl/strings/str_format.h"
26 #include "absl/types/optional.h"
36 double, model_validator_infinity, 1e100,
37 "Anything above or equal to this magnitude will be considered infinity.");
43 return std::isfinite(
value) &&
44 value < absl::GetFlag(FLAGS_model_validator_infinity) &&
45 value > -absl::GetFlag(FLAGS_model_validator_infinity);
50 template <
typename BoundedElement>
51 std::string FindErrorInBounds(
const BoundedElement& element) {
52 if (std::isnan(element.lower_bound()) || std::isnan(element.upper_bound()) ||
53 element.lower_bound() >= absl::GetFlag(FLAGS_model_validator_infinity) ||
54 element.upper_bound() <= -absl::GetFlag(FLAGS_model_validator_infinity) ||
55 element.lower_bound() > element.upper_bound()) {
56 return absl::StrFormat(
"Infeasible bounds: [%f, %f]", element.lower_bound(),
57 element.upper_bound());
63 std::string FindErrorInMPVariable(
const MPVariableProto& variable) {
64 const std::string bound_error = FindErrorInBounds(variable);
65 if (!bound_error.empty())
return bound_error;
67 if (variable.is_integer() &&
68 ceil(variable.lower_bound()) > floor(variable.upper_bound())) {
70 "Infeasible bounds for integer variable: [", (variable.lower_bound()),
71 ", ", (variable.upper_bound()),
"]",
" translate to the empty set");
73 if (!
IsFinite(variable.objective_coefficient())) {
74 return absl::StrCat(
"Invalid objective_coefficient: ",
75 (variable.objective_coefficient()));
81 template <
typename Iterable>
82 std::string FindDuplicateVarIndex(
const Iterable&
var_indices,
83 std::vector<bool>* var_mask) {
84 int duplicate_var_index = -1;
86 if ((*var_mask)[var_index]) duplicate_var_index = var_index;
87 (*var_mask)[var_index] =
true;
91 (*var_mask)[var_index] =
false;
93 if (duplicate_var_index >= 0) {
94 return absl::StrCat(
"var_index #", duplicate_var_index,
95 " appears several times");
103 std::string FindErrorInMPConstraint(
const MPConstraintProto& constraint,
104 std::vector<bool>* var_mask) {
105 const std::string bound_error = FindErrorInBounds(constraint);
106 if (!bound_error.empty())
return bound_error;
111 const int num_vars_in_model = var_mask->size();
112 const int num_vars_in_ct = constraint.var_index_size();
113 const int num_coeffs_in_ct = constraint.coefficient_size();
114 if (num_vars_in_ct != num_coeffs_in_ct) {
115 return absl::StrCat(
"var_index_size() != coefficient_size() (",
116 num_vars_in_ct,
" VS ", num_coeffs_in_ct);
118 for (
int i = 0; i < num_vars_in_ct; ++i) {
119 const int var_index = constraint.var_index(i);
120 if (var_index >= num_vars_in_model || var_index < 0) {
121 return absl::StrCat(
"var_index(", i,
")=", var_index,
122 " is out of bounds");
124 const double coeff = constraint.coefficient(i);
126 return absl::StrCat(
"coefficient(", i,
")=", (coeff),
" is invalid");
130 const std::string error =
131 FindDuplicateVarIndex(constraint.var_index(), var_mask);
132 if (!error.empty())
return error;
135 return std::string();
138 std::string CroppedConstraintDebugString(
const MPConstraintProto& constraint) {
139 const int kMaxPrintedVars = 10;
141 MPConstraintProto constraint_light = constraint;
142 std::string suffix_str;
143 if (constraint.var_index_size() > kMaxPrintedVars) {
144 constraint_light.mutable_var_index()->Truncate(kMaxPrintedVars);
145 absl::StrAppend(&suffix_str,
146 " (var_index cropped; size=", constraint.var_index_size(),
149 if (constraint.coefficient_size() > kMaxPrintedVars) {
150 constraint_light.mutable_coefficient()->Truncate(kMaxPrintedVars);
151 absl::StrAppend(&suffix_str,
" (coefficient cropped; size=",
152 constraint.coefficient_size(),
").");
154 return absl::StrCat(
"Constraint proto: ",
158 bool IsBoolean(
const MPVariableProto& variable) {
159 if (variable.lower_bound() < 0)
return false;
160 if (variable.upper_bound() > 1)
return false;
161 return variable.is_integer();
164 std::string FindErrorInMPIndicatorConstraint(
165 const MPModelProto&
model,
const MPIndicatorConstraint& indicator,
166 std::vector<bool>* var_mask) {
167 if (!indicator.has_var_index()) {
168 return "var_index is required.";
170 const int var_index = indicator.var_index();
171 if (var_index < 0 || var_index >=
model.variable_size()) {
172 return absl::StrCat(
"var_index=", var_index,
" is out of bounds.");
174 if (!IsBoolean(
model.variable(var_index))) {
175 return absl::StrCat(
"var_index=", var_index,
" is not Boolean.");
177 const int var_value = indicator.var_value();
178 if (var_value < 0 || var_value > 1) {
179 return absl::StrCat(
"var_value=", var_value,
" must be 0 or 1.");
181 const MPConstraintProto& constraint = indicator.constraint();
182 std::string error = FindErrorInMPConstraint(constraint, var_mask);
183 if (!error.empty()) {
186 return absl::StrCat(error,
" in constraint ",
187 CroppedConstraintDebugString(constraint));
192 std::string FindErrorInMPSosConstraint(
const MPModelProto&
model,
193 const MPSosConstraint& sos,
194 std::vector<bool>* var_mask) {
195 if (sos.weight_size() != 0 && sos.weight_size() != sos.var_index_size()) {
196 return "weight_size() > 0 and var_index_size() != weight_size()";
198 for (
const int var_index : sos.var_index()) {
199 if (var_index < 0 || var_index >=
model.variable_size()) {
200 return absl::StrCat(
"var_index=", var_index,
" is out of bounds.");
203 for (
int i = 0; i < sos.weight_size(); ++i) {
205 return absl::StrCat(
"Invalid weight: ", sos.weight(i));
207 if (i == 0)
continue;
208 if (sos.weight(i - 1) >= sos.weight(i)) {
209 return "SOS weights must be strictly increasing";
213 const std::string error = FindDuplicateVarIndex(sos.var_index(), var_mask);
214 if (!error.empty())
return error;
219 std::string FindErrorInMPQuadraticConstraint(
const MPModelProto&
model,
220 const MPQuadraticConstraint& qcst,
221 std::vector<bool>* var_mask) {
222 const int num_vars =
model.variable_size();
224 if (qcst.var_index_size() != qcst.coefficient_size()) {
225 return "var_index_size() != coefficient_size()";
228 const std::string bound_error = FindErrorInBounds(qcst);
229 if (!bound_error.empty())
return bound_error;
231 for (
int i = 0; i < qcst.var_index_size(); ++i) {
232 if (qcst.var_index(i) < 0 || qcst.var_index(i) >= num_vars) {
233 return absl::StrCat(
"var_index(", i,
")=", qcst.var_index(i),
234 " is invalid.",
" It must be in [0, ", num_vars,
")");
237 if (!
IsFinite(qcst.coefficient(i))) {
238 return absl::StrCat(
"coefficient(", i,
")=", qcst.coefficient(i),
242 const std::string duplicate_error =
243 FindDuplicateVarIndex(qcst.var_index(), var_mask);
244 if (!duplicate_error.empty())
return duplicate_error;
246 if (qcst.qvar1_index_size() != qcst.qvar2_index_size() ||
247 qcst.qvar1_index_size() != qcst.qcoefficient_size()) {
248 return "quadratic indices and coefficients must have the same size";
250 for (
int i = 0; i < qcst.qvar1_index_size(); ++i) {
251 if (qcst.qvar1_index(i) >= num_vars || qcst.qvar1_index(i) < 0) {
252 return absl::StrCat(
"qvar1_index(", i,
")=", qcst.qvar1_index(i),
253 " is invalid.",
" It must be in [0, ", num_vars,
")");
256 if (qcst.qvar2_index(i) >= num_vars || qcst.qvar2_index(i) < 0) {
257 return absl::StrCat(
"qvar2_index(", i,
")=", qcst.qvar2_index(i),
258 " is invalid.",
" It must be in [0, ", num_vars,
")");
261 if (!
IsFinite(qcst.qcoefficient(i))) {
262 return absl::StrCat(
"qcoefficient(", i,
")=", qcst.qcoefficient(i),
270 std::string FindErrorInMPAbsConstraint(
const MPModelProto&
model,
271 const MPAbsConstraint& abs) {
272 if (!abs.has_var_index()) {
273 return "var_index is required.";
275 if (!abs.has_resultant_var_index()) {
276 return "resultant_var_index is required.";
279 const int num_vars =
model.variable_size();
280 if (abs.var_index() < 0 || abs.var_index() >= num_vars) {
281 return absl::StrCat(
"var_index=", abs.var_index(),
" is invalid.",
282 " It must be in [0, ", num_vars,
")");
284 if (abs.resultant_var_index() < 0 || abs.resultant_var_index() >= num_vars) {
285 return absl::StrCat(
"var_index=", abs.resultant_var_index(),
" is invalid.",
286 " It must be in [0, ", num_vars,
")");
291 std::string FindErrorInMPAndOrConstraint(
const MPModelProto&
model,
292 const MPArrayConstraint& and_or) {
293 if (and_or.var_index_size() == 0) {
294 return "var_index cannot be empty.";
296 if (!and_or.has_resultant_var_index()) {
297 return "resultant_var_index is required.";
300 const int num_vars =
model.variable_size();
301 for (
int i = 0; i < and_or.var_index_size(); ++i) {
302 if (and_or.var_index(i) < 0 || and_or.var_index(i) >= num_vars) {
303 return absl::StrCat(
"var_index(", i,
")=", and_or.var_index(i),
304 " is invalid.",
" It must be in [0, ", num_vars,
")");
306 if (!IsBoolean(
model.variable(and_or.var_index(i)))) {
307 return absl::StrCat(
"var_index=", i,
" is not Boolean.");
310 if (and_or.resultant_var_index() < 0 ||
311 and_or.resultant_var_index() >= num_vars) {
312 return absl::StrCat(
"resultant_var_index=", and_or.resultant_var_index(),
313 " is invalid.",
" It must be in [0, ", num_vars,
")");
315 if (!IsBoolean(
model.variable(and_or.resultant_var_index()))) {
316 return absl::StrCat(
"resultant_var_index is not Boolean.");
321 std::string FindErrorInMPMinMaxConstraint(
322 const MPModelProto&
model,
const MPArrayWithConstantConstraint& min_max) {
323 if (min_max.var_index_size() == 0) {
324 return "var_index cannot be empty.";
326 if (!min_max.has_resultant_var_index()) {
327 return "resultant_var_index is required.";
330 if (!
IsFinite(min_max.constant())) {
331 return absl::StrCat(
"Invalid constant: ", (min_max.constant()));
334 const int num_vars =
model.variable_size();
335 for (
int i = 0; i < min_max.var_index_size(); ++i) {
336 if (min_max.var_index(i) < 0 || min_max.var_index(i) >= num_vars) {
337 return absl::StrCat(
"var_index(", i,
")=", min_max.var_index(i),
338 " is invalid.",
" It must be in [0, ", num_vars,
")");
341 if (min_max.resultant_var_index() < 0 ||
342 min_max.resultant_var_index() >= num_vars) {
343 return absl::StrCat(
"resultant_var_index=", min_max.resultant_var_index(),
344 " is invalid.",
" It must be in [0, ", num_vars,
")");
349 std::string FindErrorInQuadraticObjective(
const MPQuadraticObjective& qobj,
351 if (qobj.qvar1_index_size() != qobj.qvar2_index_size() ||
352 qobj.qvar1_index_size() != qobj.coefficient_size()) {
353 return "indices and coefficients must have the same size";
356 for (
int i = 0; i < qobj.qvar1_index_size(); ++i) {
357 if (qobj.qvar1_index(i) >= num_vars || qobj.qvar1_index(i) < 0) {
358 return absl::StrCat(
"qvar1_index(", i,
")=", qobj.qvar1_index(i),
359 " is invalid.",
" It must be in [0, ", num_vars,
")");
362 if (qobj.qvar2_index(i) >= num_vars || qobj.qvar2_index(i) < 0) {
363 return absl::StrCat(
"qvar2_index(", i,
")=", qobj.qvar2_index(i),
364 " is invalid.",
" It must be in [0, ", num_vars,
")");
367 if (!
IsFinite(qobj.coefficient(i))) {
368 return absl::StrCat(
"coefficient(", i,
")=", (qobj.coefficient(i)),
375 std::string FindErrorInSolutionHint(
376 const PartialVariableAssignment& solution_hint,
int num_vars) {
377 if (solution_hint.var_index_size() != solution_hint.var_value_size()) {
378 return absl::StrCat(
"var_index_size() != var_value_size() [",
379 solution_hint.var_index_size(),
" VS ",
380 solution_hint.var_value_size());
382 std::vector<bool> var_in_hint(num_vars,
false);
383 for (
int i = 0; i < solution_hint.var_index_size(); ++i) {
384 const int var_index = solution_hint.var_index(i);
385 if (var_index >= num_vars || var_index < 0) {
386 return absl::StrCat(
"var_index(", i,
")=", var_index,
" is invalid.",
387 " It must be in [0, ", num_vars,
")");
389 if (var_in_hint[var_index]) {
390 return absl::StrCat(
"Duplicate var_index = ", var_index);
392 var_in_hint[var_index] =
true;
393 if (!
IsFinite(solution_hint.var_value(i))) {
394 return absl::StrCat(
"var_value(", i,
")=", (solution_hint.var_value(i)),
398 return std::string();
408 return absl::StrCat(
"Invalid objective_offset: ",
409 (
model.objective_offset()));
411 const int num_vars =
model.variable_size();
412 const int num_cts =
model.constraint_size();
416 for (
int i = 0; i < num_vars; ++i) {
417 error = FindErrorInMPVariable(
model.variable(i));
418 if (!error.empty()) {
419 return absl::StrCat(
"In variable #", i,
": ", error,
". Variable proto: ",
425 std::vector<bool> variable_appears(num_vars,
false);
426 for (
int i = 0; i < num_cts; ++i) {
427 const MPConstraintProto& constraint =
model.constraint(i);
428 error = FindErrorInMPConstraint(constraint, &variable_appears);
429 if (!error.empty()) {
431 return absl::StrCat(
"In constraint #", i,
": ", error,
". ",
432 CroppedConstraintDebugString(constraint));
437 for (
int i = 0; i <
model.general_constraint_size(); ++i) {
438 const MPGeneralConstraintProto& gen_constraint =
439 model.general_constraint(i);
441 switch (gen_constraint.general_constraint_case()) {
442 case MPGeneralConstraintProto::kIndicatorConstraint:
443 error = FindErrorInMPIndicatorConstraint(
444 model, gen_constraint.indicator_constraint(), &variable_appears);
447 case MPGeneralConstraintProto::kSosConstraint:
448 error = FindErrorInMPSosConstraint(
449 model, gen_constraint.sos_constraint(), &variable_appears);
452 case MPGeneralConstraintProto::kQuadraticConstraint:
453 error = FindErrorInMPQuadraticConstraint(
454 model, gen_constraint.quadratic_constraint(), &variable_appears);
457 case MPGeneralConstraintProto::kAbsConstraint:
459 FindErrorInMPAbsConstraint(
model, gen_constraint.abs_constraint());
462 case MPGeneralConstraintProto::kAndConstraint:
463 error = FindErrorInMPAndOrConstraint(
model,
464 gen_constraint.and_constraint());
467 case MPGeneralConstraintProto::kOrConstraint:
469 FindErrorInMPAndOrConstraint(
model, gen_constraint.or_constraint());
472 case MPGeneralConstraintProto::kMinConstraint:
473 error = FindErrorInMPMinMaxConstraint(
model,
474 gen_constraint.min_constraint());
477 case MPGeneralConstraintProto::kMaxConstraint:
478 error = FindErrorInMPMinMaxConstraint(
model,
479 gen_constraint.max_constraint());
482 return absl::StrCat(
"Unknown general constraint type ",
483 gen_constraint.general_constraint_case());
485 if (!error.empty()) {
486 return absl::StrCat(
"In general constraint #", i,
": ", error);
491 if (
model.has_quadratic_objective()) {
493 FindErrorInQuadraticObjective(
model.quadratic_objective(), num_vars);
494 if (!error.empty())
return absl::StrCat(
"In quadratic_objective: ", error);
498 error = FindErrorInSolutionHint(
model.solution_hint(), num_vars);
499 if (!error.empty()) {
500 return absl::StrCat(
"In solution_hint(): ", error);
503 return std::string();
506 absl::optional<LazyMutableCopy<MPModelProto>>
511 if (!request.has_model() && !request.has_model_delta()) {
513 response->set_status_str(
"Requests without model are considered OPTIMAL");
514 return absl::nullopt;
516 if (request.has_model() && request.has_model_delta()) {
519 "Fields 'model' and 'model_delta' are mutually exclusive");
520 return absl::nullopt;
525 if (request.has_model_delta()) {
528 std::string contents;
530 request.model_delta().baseline_model_file_path(), &contents);
531 if (!file_read_status.ok()) {
534 "Error when reading model_delta.baseline_model_file_path: '" +
535 file_read_status.ToString());
536 return absl::nullopt;
538 if (!
model.get_mutable()->ParseFromString(contents)) {
541 absl::StrFormat(
"The contents of baseline model file '%s' couldn't "
542 "be parsed as a raw serialized MPModelProto",
543 request.model_delta().baseline_model_file_path()));
544 return absl::nullopt;
553 if (error.empty() && request.has_model_delta()) {
554 const MPModelDeltaProto&
delta = request.model_delta();
560 if (!error.empty()) {
561 if (request.enable_internal_solver_output()) {
562 LOG(
ERROR) << absl::StrCat(
"Invalid model: ", error);
564 response->set_status(absl::StrContains(error,
"Infeasible")
568 return absl::nullopt;
571 if (
model.get().variable_size() == 0 &&
model.get().constraint_size() == 0 &&
572 model.get().general_constraint_size() == 0) {
574 response->set_objective_value(
model.get().objective_offset());
577 "Requests without variables and constraints are considered OPTIMAL");
578 return absl::nullopt;
581 return std::move(
model);
585 MPModelRequest* request, MPSolutionResponse*
response) {
586 absl::optional<LazyMutableCopy<MPModelProto>> lazy_copy =
588 if (!lazy_copy)
return false;
589 if (lazy_copy->was_copied()) {
590 lazy_copy->get_mutable()->Swap(request->mutable_model());
599 const int num_vars =
model.variable_size();
602 std::string error = FindErrorInSolutionHint(
model.solution_hint(), num_vars);
603 if (!error.empty())
return absl::StrCat(
"Invalid solution_hint: ", error);
606 if (num_vars > 0 &&
model.solution_hint().var_index_size() == 0) {
607 return "Empty solution_hint.";
611 if (
model.solution_hint().var_index_size() != num_vars) {
612 return absl::StrCat(
"Partial solution_hint: only ",
613 model.solution_hint().var_index_size(),
" out of the ",
614 num_vars,
" problem variables are set.");
618 std::vector<double> var_value(num_vars);
619 for (
int i = 0; i <
model.solution_hint().var_index_size(); ++i) {
620 const int var_index =
model.solution_hint().var_index(i);
621 const double value =
model.solution_hint().var_value(i);
622 var_value[var_index] =
value;
623 const double lb =
model.variable(var_index).lower_bound();
624 const double ub =
model.variable(var_index).upper_bound();
627 return absl::StrCat(
"Variable '",
model.variable(var_index).name(),
628 "' is set to ", (
value),
629 " which is not in the variable bounds [", (lb),
", ",
630 (ub),
"] modulo a tolerance of ", (tolerance),
".");
635 for (
int cst_index = 0; cst_index <
model.constraint_size(); ++cst_index) {
636 const MPConstraintProto& constraint =
model.constraint(cst_index);
638 for (
int j = 0; j < constraint.var_index_size(); ++j) {
639 activity.
Add(constraint.coefficient(j) *
640 var_value[constraint.var_index(j)]);
642 const double lb =
model.constraint(cst_index).lower_bound();
643 const double ub =
model.constraint(cst_index).upper_bound();
647 "Constraint '",
model.constraint(cst_index).name(),
"' has activity ",
648 (activity.
Value()),
" which is not in the constraint bounds [", (lb),
649 ", ", (ub),
"] modulo a tolerance of ", (tolerance),
".");
657 const MPModelProto&
model) {
658 int num_vars =
model.variable_size();
661 absl::flat_hash_set<int> new_var_indices;
662 int max_var_index = num_vars - 1;
663 MPVariableProto tmp_var_proto;
664 for (
const auto& pair :
delta.variable_overrides()) {
665 const int var_index = pair.first;
666 const MPVariableProto& var_override_proto = pair.second;
668 error =
"Invalid key";
669 }
else if (var_index >= num_vars) {
670 max_var_index =
std::max(max_var_index, var_index);
671 new_var_indices.insert(var_index);
672 error = FindErrorInMPVariable(var_override_proto);
674 tmp_var_proto =
model.variable(var_index);
677 tmp_var_proto.MergeFrom(var_override_proto);
678 error = FindErrorInMPVariable(tmp_var_proto);
680 if (!error.empty()) {
681 return absl::StrFormat(
682 "variable_overrides with key (eg. var index) = %d: %s", var_index,
686 if (max_var_index != num_vars + new_var_indices.size() - 1) {
687 return absl::StrFormat(
688 "The added and existing variable indices do not form a dense integer "
689 "interval: oldmax=%d, max=%d, num added=%d",
690 num_vars - 1, max_var_index, new_var_indices.size());
693 num_vars += new_var_indices.size();
700 std::vector<bool> variable_appears(num_vars,
false);
701 MPConstraintProto tmp_constraint_proto;
702 const int num_constraints =
model.constraint_size();
703 absl::flat_hash_set<int> new_ct_indices;
704 int max_ct_index = num_constraints - 1;
705 for (
const auto& pair :
delta.constraint_overrides()) {
706 const int ct_index = pair.first;
707 const MPConstraintProto& constraint_override_proto = pair.second;
709 error =
"Invalid constraint index";
710 }
else if (ct_index >= num_constraints) {
711 max_ct_index =
std::max(max_ct_index, ct_index);
712 new_ct_indices.insert(ct_index);
714 FindErrorInMPConstraint(constraint_override_proto, &variable_appears);
723 tmp_constraint_proto.Clear();
725 &tmp_constraint_proto);
726 tmp_constraint_proto.MergeFrom(constraint_override_proto);
727 error = FindErrorInMPConstraint(tmp_constraint_proto, &variable_appears);
729 if (!error.empty()) {
730 return absl::StrFormat(
731 "constraint_overrides with key (eg. constraint index) = %d: %s",
735 if (max_ct_index != num_constraints + new_ct_indices.size() - 1) {
736 return absl::StrFormat(
737 "The added and existing constraint indices do not form a dense integer "
738 "interval: oldmax=%d, max=%d, num added=%d",
739 num_constraints - 1, max_ct_index, new_ct_indices.size());
746 MPConstraintProto* to) {
747 #define COPY_FIELD_IF_PRESENT(field) \
748 if (from.has_##field()) to->set_##field(from.field())
753 #undef COPY_FIELD_IF_PRESENT
757 void PruneZeroTermsInMpConstraint(MPConstraintProto*
ct) {
761 while (first_zero < ct->var_index_size() &&
762 ct->coefficient(first_zero) != 0.0) {
765 int num_kept = first_zero;
766 for (
int i = first_zero; i <
ct->var_index_size(); ++i) {
767 if (
ct->coefficient(i) == 0.0)
continue;
769 ct->set_var_index(num_kept,
ct->var_index(i));
770 ct->set_coefficient(num_kept,
ct->coefficient(i));
774 ct->mutable_var_index()->Truncate(num_kept);
775 ct->mutable_coefficient()->Truncate(num_kept);
782 void ExtendRepeatedPtrFieldToSize(
const int size, T* repeated_messages) {
783 DCHECK_GE(size, repeated_messages->size());
784 while (repeated_messages->size() < size) repeated_messages->Add();
789 MPModelProto*
model) {
791 int max_var_index = -1;
792 for (
const auto& p :
delta.variable_overrides()) {
793 max_var_index =
std::max(max_var_index, p.first);
795 if (max_var_index >=
model->variable_size()) {
796 ExtendRepeatedPtrFieldToSize(max_var_index + 1,
model->mutable_variable());
799 for (
const auto& p :
delta.variable_overrides()) {
800 model->mutable_variable(p.first)->MergeFrom(p.second);
804 int max_ct_index = -1;
805 for (
const auto& p :
delta.constraint_overrides()) {
806 max_ct_index =
std::max(max_ct_index, p.first);
808 const int old_num_constraints =
model->constraint_size();
809 if (max_ct_index >= old_num_constraints) {
810 ExtendRepeatedPtrFieldToSize(max_ct_index + 1,
model->mutable_constraint());
813 for (
const auto& p :
delta.constraint_overrides()) {
814 const MPConstraintProto& override_ct = p.second;
815 MPConstraintProto* baseline =
model->mutable_constraint(p.first);
817 if (p.first >= old_num_constraints) {
818 *baseline = override_ct;
823 if (override_ct.has_lower_bound() &&
824 override_ct.lower_bound() <=
825 -absl::GetFlag(FLAGS_model_validator_infinity) &&
826 override_ct.has_upper_bound() &&
827 override_ct.upper_bound() >=
828 absl::GetFlag(FLAGS_model_validator_infinity)) {
829 baseline->clear_var_index();
830 baseline->clear_coefficient();
837 absl::flat_hash_map<int, double> term_overrides;
838 term_overrides.reserve(override_ct.var_index_size());
839 for (
int i = 0; i < override_ct.var_index_size(); ++i) {
840 term_overrides[override_ct.var_index(i)] = override_ct.coefficient(i);
842 for (
int i = 0; i < baseline->var_index_size(); ++i) {
843 auto it = term_overrides.find(baseline->var_index(i));
844 if (it == term_overrides.end())
continue;
845 baseline->set_coefficient(i, it->second);
848 PruneZeroTermsInMpConstraint(baseline);
850 for (
const auto& p : term_overrides) {
851 if (p.second != 0.0) {
852 baseline->add_var_index(p.first);
853 baseline->add_coefficient(p.second);