25#include "absl/container/flat_hash_map.h"
26#include "absl/container/flat_hash_set.h"
27#include "absl/status/status.h"
28#include "absl/status/statusor.h"
29#include "absl/strings/match.h"
30#include "absl/strings/str_cat.h"
31#include "absl/strings/str_format.h"
32#include "absl/strings/string_view.h"
33#include "absl/types/optional.h"
44 double, model_validator_infinity, 1e100,
45 "Anything above or equal to this magnitude will be considered infinity.");
50bool IsNanOrAbsGreaterThanOrEqual(
double value,
double abs_value_threshold) {
51 return std::isnan(value) || std::abs(value) >= abs_value_threshold;
56template <
typename BoundedElement>
57std::string FindErrorInBounds(
const BoundedElement& element,
58 double abs_value_threshold,
59 const bool accept_trivially_infeasible_bounds) {
60 if (std::isnan(element.lower_bound()) || std::isnan(element.upper_bound()) ||
61 element.lower_bound() >= abs_value_threshold ||
62 element.upper_bound() <= -abs_value_threshold ||
63 (!accept_trivially_infeasible_bounds &&
64 element.lower_bound() > element.upper_bound())) {
65 return absl::StrFormat(
"Infeasible bounds: [%f, %f]", element.lower_bound(),
66 element.upper_bound());
72std::string FindErrorInMPVariable(
74 const bool accept_trivially_infeasible_bounds) {
75 const std::string bound_error = FindErrorInBounds(
76 variable, abs_value_threshold, accept_trivially_infeasible_bounds);
77 if (!bound_error.empty())
return bound_error;
79 if (!accept_trivially_infeasible_bounds && variable.is_integer() &&
80 ceil(variable.lower_bound()) >
floor(variable.upper_bound())) {
82 "Infeasible bounds for integer variable: [", (variable.lower_bound()),
83 ", ", (variable.upper_bound()),
"]",
" translate to the empty set");
85 if (IsNanOrAbsGreaterThanOrEqual(variable.objective_coefficient(),
86 abs_value_threshold)) {
87 return absl::StrCat(
"Invalid objective_coefficient: ",
88 (variable.objective_coefficient()));
94template <
typename Iterable>
95std::string FindDuplicateVarIndex(
const Iterable& var_indices,
96 std::vector<bool>* var_mask) {
97 int duplicate_var_index = -1;
98 for (
const int var_index : var_indices) {
99 if ((*var_mask)[var_index]) duplicate_var_index = var_index;
100 (*var_mask)[var_index] =
true;
103 for (
const int var_index : var_indices) {
104 (*var_mask)[var_index] =
false;
106 if (duplicate_var_index >= 0) {
107 return absl::StrCat(
"var_index #", duplicate_var_index,
108 " appears several times");
116std::string FindErrorInMPConstraint(
118 double abs_value_threshold,
const bool accept_trivially_infeasible_bounds) {
119 const std::string bound_error = FindErrorInBounds(
120 constraint, abs_value_threshold, accept_trivially_infeasible_bounds);
121 if (!bound_error.empty())
return bound_error;
126 const int num_vars_in_model = var_mask->size();
127 const int num_vars_in_ct = constraint.var_index_size();
128 const int num_coeffs_in_ct = constraint.coefficient_size();
129 if (num_vars_in_ct != num_coeffs_in_ct) {
130 return absl::StrCat(
"var_index_size() != coefficient_size() (",
131 num_vars_in_ct,
" VS ", num_coeffs_in_ct);
133 for (
int i = 0;
i < num_vars_in_ct; ++
i) {
134 const int var_index = constraint.var_index(i);
135 if (var_index >= num_vars_in_model || var_index < 0) {
136 return absl::StrCat(
"var_index(", i,
")=", var_index,
137 " is out of bounds");
139 const double coeff = constraint.coefficient(i);
140 if (IsNanOrAbsGreaterThanOrEqual(coeff, abs_value_threshold)) {
141 return absl::StrCat(
"coefficient(", i,
")=", (coeff),
" is invalid");
145 const std::string error =
146 FindDuplicateVarIndex(constraint.var_index(), var_mask);
147 if (!error.empty())
return error;
150 return std::string();
154 const int kMaxPrintedVars = 10;
157 std::string suffix_str;
158 if (constraint.var_index_size() > kMaxPrintedVars) {
159 constraint_light.mutable_var_index()->Truncate(kMaxPrintedVars);
160 absl::StrAppend(&suffix_str,
161 " (var_index cropped; size=", constraint.var_index_size(),
164 if (constraint.coefficient_size() > kMaxPrintedVars) {
165 constraint_light.mutable_coefficient()->Truncate(kMaxPrintedVars);
166 absl::StrAppend(&suffix_str,
" (coefficient cropped; size=",
167 constraint.coefficient_size(),
").");
169 return absl::StrCat(
"Constraint proto: ",
174 if (variable.lower_bound() < 0)
return false;
175 if (variable.upper_bound() > 1)
return false;
176 return variable.is_integer();
179std::string FindErrorInMPIndicatorConstraint(
181 std::vector<bool>* var_mask,
double abs_value_threshold,
182 bool accept_trivially_infeasible_bounds) {
183 if (!indicator.has_var_index()) {
184 return "var_index is required.";
186 const int var_index = indicator.var_index();
187 if (var_index < 0 || var_index >= model.variable_size()) {
188 return absl::StrCat(
"var_index=", var_index,
" is out of bounds.");
190 if (!IsBoolean(model.variable(var_index))) {
191 return absl::StrCat(
"var_index=", var_index,
" is not Boolean.");
193 const int var_value = indicator.var_value();
194 if (var_value < 0 || var_value > 1) {
195 return absl::StrCat(
"var_value=", var_value,
" must be 0 or 1.");
199 FindErrorInMPConstraint(constraint, var_mask, abs_value_threshold,
200 accept_trivially_infeasible_bounds);
201 if (!error.empty()) {
204 return absl::StrCat(error,
" in constraint ",
205 CroppedConstraintDebugString(constraint));
210std::string FindErrorInMPSosConstraint(
const MPModelProto& model,
212 std::vector<bool>* var_mask,
213 double abs_value_threshold) {
214 if (sos.weight_size() != 0 && sos.weight_size() != sos.var_index_size()) {
215 return "weight_size() > 0 and var_index_size() != weight_size()";
217 for (
const int var_index : sos.var_index()) {
218 if (var_index < 0 || var_index >= model.variable_size()) {
219 return absl::StrCat(
"var_index=", var_index,
" is out of bounds.");
222 for (
int i = 0;
i < sos.weight_size(); ++
i) {
223 if (IsNanOrAbsGreaterThanOrEqual(sos.weight(i), abs_value_threshold)) {
224 return absl::StrCat(
"Invalid weight: ", sos.weight(i));
226 if (i == 0)
continue;
227 if (sos.weight(i - 1) >= sos.weight(i)) {
228 return "SOS weights must be strictly increasing";
232 const std::string error = FindDuplicateVarIndex(sos.var_index(), var_mask);
233 if (!error.empty())
return error;
238std::string FindErrorInMPQuadraticConstraint(
240 std::vector<bool>* var_mask,
double abs_value_threshold,
241 bool accept_trivially_infeasible_bounds) {
242 const int num_vars = model.variable_size();
244 if (qcst.var_index_size() != qcst.coefficient_size()) {
245 return "var_index_size() != coefficient_size()";
248 const std::string bound_error = FindErrorInBounds(
249 qcst, abs_value_threshold, accept_trivially_infeasible_bounds);
250 if (!bound_error.empty())
return bound_error;
252 for (
int i = 0;
i < qcst.var_index_size(); ++
i) {
253 if (qcst.var_index(i) < 0 || qcst.var_index(i) >= num_vars) {
254 return absl::StrCat(
"var_index(", i,
")=", qcst.var_index(i),
255 " is invalid.",
" It must be in [0, ", num_vars,
")");
258 if (IsNanOrAbsGreaterThanOrEqual(qcst.coefficient(i),
259 abs_value_threshold)) {
260 return absl::StrCat(
"coefficient(", i,
")=", qcst.coefficient(i),
264 const std::string duplicate_error =
265 FindDuplicateVarIndex(qcst.var_index(), var_mask);
266 if (!duplicate_error.empty())
return duplicate_error;
268 if (qcst.qvar1_index_size() != qcst.qvar2_index_size() ||
269 qcst.qvar1_index_size() != qcst.qcoefficient_size()) {
270 return "quadratic indices and coefficients must have the same size";
272 for (
int i = 0;
i < qcst.qvar1_index_size(); ++
i) {
273 if (qcst.qvar1_index(i) >= num_vars || qcst.qvar1_index(i) < 0) {
274 return absl::StrCat(
"qvar1_index(", i,
")=", qcst.qvar1_index(i),
275 " is invalid.",
" It must be in [0, ", num_vars,
")");
278 if (qcst.qvar2_index(i) >= num_vars || qcst.qvar2_index(i) < 0) {
279 return absl::StrCat(
"qvar2_index(", i,
")=", qcst.qvar2_index(i),
280 " is invalid.",
" It must be in [0, ", num_vars,
")");
283 if (IsNanOrAbsGreaterThanOrEqual(qcst.qcoefficient(i),
284 abs_value_threshold)) {
285 return absl::StrCat(
"qcoefficient(", i,
")=", qcst.qcoefficient(i),
293std::string FindErrorInMPAbsConstraint(
const MPModelProto& model,
295 if (!abs.has_var_index()) {
296 return "var_index is required.";
298 if (!abs.has_resultant_var_index()) {
299 return "resultant_var_index is required.";
302 const int num_vars = model.variable_size();
303 if (abs.var_index() < 0 || abs.var_index() >= num_vars) {
304 return absl::StrCat(
"var_index=", abs.var_index(),
" is invalid.",
305 " It must be in [0, ", num_vars,
")");
307 if (abs.resultant_var_index() < 0 || abs.resultant_var_index() >= num_vars) {
308 return absl::StrCat(
"var_index=", abs.resultant_var_index(),
" is invalid.",
309 " It must be in [0, ", num_vars,
")");
314std::string FindErrorInMPAndOrConstraint(
const MPModelProto& model,
316 if (and_or.var_index_size() == 0) {
317 return "var_index cannot be empty.";
319 if (!and_or.has_resultant_var_index()) {
320 return "resultant_var_index is required.";
323 const int num_vars = model.variable_size();
324 for (
int i = 0;
i < and_or.var_index_size(); ++
i) {
325 if (and_or.var_index(i) < 0 || and_or.var_index(i) >= num_vars) {
326 return absl::StrCat(
"var_index(", i,
")=", and_or.var_index(i),
327 " is invalid.",
" It must be in [0, ", num_vars,
")");
329 if (!IsBoolean(model.variable(and_or.var_index(i)))) {
330 return absl::StrCat(
"var_index=", i,
" is not Boolean.");
333 if (and_or.resultant_var_index() < 0 ||
334 and_or.resultant_var_index() >= num_vars) {
335 return absl::StrCat(
"resultant_var_index=", and_or.resultant_var_index(),
336 " is invalid.",
" It must be in [0, ", num_vars,
")");
338 if (!IsBoolean(model.variable(and_or.resultant_var_index()))) {
339 return absl::StrCat(
"resultant_var_index is not Boolean.");
344std::string FindErrorInMPMinMaxConstraint(
346 double abs_value_threshold) {
347 if (min_max.var_index_size() == 0) {
348 return "var_index cannot be empty.";
350 if (!min_max.has_resultant_var_index()) {
351 return "resultant_var_index is required.";
354 if (IsNanOrAbsGreaterThanOrEqual(min_max.constant(), abs_value_threshold)) {
355 return absl::StrCat(
"Invalid constant: ", (min_max.constant()));
358 const int num_vars = model.variable_size();
359 for (
int i = 0;
i < min_max.var_index_size(); ++
i) {
360 if (min_max.var_index(i) < 0 || min_max.var_index(i) >= num_vars) {
361 return absl::StrCat(
"var_index(", i,
")=", min_max.var_index(i),
362 " is invalid.",
" It must be in [0, ", num_vars,
")");
365 if (min_max.resultant_var_index() < 0 ||
366 min_max.resultant_var_index() >= num_vars) {
367 return absl::StrCat(
"resultant_var_index=", min_max.resultant_var_index(),
368 " is invalid.",
" It must be in [0, ", num_vars,
")");
375 double abs_value_threshold) {
376 if (qobj.qvar1_index_size() != qobj.qvar2_index_size() ||
377 qobj.qvar1_index_size() != qobj.coefficient_size()) {
378 return "indices and coefficients must have the same size";
381 for (
int i = 0;
i < qobj.qvar1_index_size(); ++
i) {
382 if (qobj.qvar1_index(i) >= num_vars || qobj.qvar1_index(i) < 0) {
383 return absl::StrCat(
"qvar1_index(", i,
")=", qobj.qvar1_index(i),
384 " is invalid.",
" It must be in [0, ", num_vars,
")");
387 if (qobj.qvar2_index(i) >= num_vars || qobj.qvar2_index(i) < 0) {
388 return absl::StrCat(
"qvar2_index(", i,
")=", qobj.qvar2_index(i),
389 " is invalid.",
" It must be in [0, ", num_vars,
")");
392 if (IsNanOrAbsGreaterThanOrEqual(qobj.coefficient(i),
393 abs_value_threshold)) {
394 return absl::StrCat(
"coefficient(", i,
")=", (qobj.coefficient(i)),
401std::string FindErrorInSolutionHint(
403 double abs_value_threshold) {
404 if (solution_hint.var_index_size() != solution_hint.var_value_size()) {
405 return absl::StrCat(
"var_index_size() != var_value_size() [",
406 solution_hint.var_index_size(),
" VS ",
407 solution_hint.var_value_size());
409 std::vector<bool> var_in_hint(num_vars,
false);
410 for (
int i = 0;
i < solution_hint.var_index_size(); ++
i) {
411 const int var_index = solution_hint.var_index(i);
412 if (var_index >= num_vars || var_index < 0) {
413 return absl::StrCat(
"var_index(", i,
")=", var_index,
" is invalid.",
414 " It must be in [0, ", num_vars,
")");
416 if (var_in_hint[var_index]) {
417 return absl::StrCat(
"Duplicate var_index = ", var_index);
419 var_in_hint[var_index] =
true;
420 if (IsNanOrAbsGreaterThanOrEqual(solution_hint.var_value(i),
421 abs_value_threshold)) {
422 return absl::StrCat(
"var_value(", i,
")=", (solution_hint.var_value(i)),
426 return std::string();
433template <
class NamedEntity>
434absl::flat_hash_map<std::string, int> BuildNameToIndexMap(
435 const google::protobuf::RepeatedPtrField<NamedEntity>& entities) {
436 absl::flat_hash_map<std::string, int> out;
437 for (
int i = 0;
i < entities.size(); ++
i) {
439 if (index != i) index = -1;
444class LazyMPModelNameToIndexMaps {
446 explicit LazyMPModelNameToIndexMaps(
const MPModelProto& model)
449 absl::StatusOr<int> LookupName(
450 MPModelProto::Annotation::TargetType target_type,
451 absl::string_view name) {
452 const absl::flat_hash_map<std::string, int>* map =
nullptr;
453 switch (target_type) {
454 case MPModelProto::Annotation::VARIABLE_DEFAULT:
455 if (!variable_name_to_index_) {
456 variable_name_to_index_ = BuildNameToIndexMap(model_.variable());
458 map = &variable_name_to_index_.value();
460 case MPModelProto::Annotation::CONSTRAINT:
461 if (!constraint_name_to_index_) {
462 constraint_name_to_index_ = BuildNameToIndexMap(model_.constraint());
464 map = &constraint_name_to_index_.value();
466 case MPModelProto::Annotation::GENERAL_CONSTRAINT:
467 if (!general_constraint_name_to_index_) {
468 general_constraint_name_to_index_ =
469 BuildNameToIndexMap(model_.general_constraint());
471 map = &general_constraint_name_to_index_.value();
475 if (index == -2)
return absl::NotFoundError(
"name not found");
476 if (index == -1)
return absl::InvalidArgumentError(
"name is not unique");
481 const MPModelProto& model_;
482 std::optional<absl::flat_hash_map<std::string, int>> variable_name_to_index_;
483 std::optional<absl::flat_hash_map<std::string, int>>
484 constraint_name_to_index_;
485 std::optional<absl::flat_hash_map<std::string, int>>
486 general_constraint_name_to_index_;
492 LazyMPModelNameToIndexMaps* name_maps) {
494 if (!annotation.has_target_index() && !annotation.has_target_name()) {
495 return "One of target_index or target_name must be set";
498 return "Invalid target_type";
500 int num_entitities = -1;
501 switch (annotation.target_type()) {
503 num_entitities = model.variable_size();
506 num_entitities = model.constraint_size();
509 num_entitities = model.general_constraint_size();
512 int target_index = -1;
513 if (annotation.has_target_index()) {
514 target_index = annotation.target_index();
515 if (target_index < 0 || target_index >= num_entitities) {
516 return "Invalid target_index";
519 if (annotation.has_target_name()) {
520 if (annotation.has_target_index()) {
525 switch (annotation.target_type()) {
527 name = model.variable(target_index).name();
530 name = model.constraint(target_index).name();
533 name = model.general_constraint(target_index).name();
536 if (annotation.target_name() != name) {
537 return absl::StrFormat(
538 "target_name='%s' doesn't match the name '%s' of target_index=%d",
539 annotation.target_name(), name, target_index);
542 const absl::StatusOr<int> index_or = name_maps->LookupName(
543 annotation.target_type(), annotation.target_name());
544 if (!index_or.ok()) {
545 return absl::StrCat(
"Bad target_name: ", index_or.status().message());
547 target_index = index_or.value();
560 const bool accept_trivially_infeasible_bounds) {
564 if (abs_value_threshold == 0.0) {
565 abs_value_threshold = absl::GetFlag(FLAGS_model_validator_infinity);
569 abs_value_threshold)) {
570 return absl::StrCat(
"Invalid objective_offset: ",
578 for (
int i = 0; i < num_vars; ++i) {
579 error = FindErrorInMPVariable(model.
variable(i), abs_value_threshold,
580 accept_trivially_infeasible_bounds);
581 if (!error.empty()) {
582 return absl::StrCat(
"In variable #", i,
": ", error,
". Variable proto: ",
588 std::vector<bool> variable_appears(num_vars,
false);
589 for (
int i = 0; i < num_cts; ++i) {
591 error = FindErrorInMPConstraint(constraint, &variable_appears,
593 accept_trivially_infeasible_bounds);
594 if (!error.empty()) {
596 return absl::StrCat(
"In constraint #", i,
": ", error,
". ",
597 CroppedConstraintDebugString(constraint));
608 error = FindErrorInMPIndicatorConstraint(
610 abs_value_threshold, accept_trivially_infeasible_bounds);
615 FindErrorInMPSosConstraint(model, gen_constraint.
sos_constraint(),
616 &variable_appears, abs_value_threshold);
620 error = FindErrorInMPQuadraticConstraint(
622 abs_value_threshold, accept_trivially_infeasible_bounds);
627 FindErrorInMPAbsConstraint(model, gen_constraint.
abs_constraint());
631 error = FindErrorInMPAndOrConstraint(model,
637 FindErrorInMPAndOrConstraint(model, gen_constraint.
or_constraint());
641 error = FindErrorInMPMinMaxConstraint(
646 error = FindErrorInMPMinMaxConstraint(
650 return absl::StrCat(
"Unknown general constraint type ",
653 if (!error.empty()) {
654 return absl::StrCat(
"In general constraint #", i,
": ", error);
661 abs_value_threshold);
662 if (!error.empty())
return absl::StrCat(
"In quadratic_objective: ", error);
666 error = FindErrorInSolutionHint(model.
solution_hint(), num_vars,
667 abs_value_threshold);
668 if (!error.empty()) {
669 return absl::StrCat(
"In solution_hint(): ", error);
674 LazyMPModelNameToIndexMaps name_maps(model);
676 error = FindErrorInAnnotation(model.
annotation(a), model, &name_maps);
677 if (!error.empty()) {
678 return absl::StrCat(
"In annotation #", a,
": ", error);
683 return std::string();
686std::optional<LazyMutableCopy<MPModelProto>>
695 CHECK(response !=
nullptr);
697 if (!request->has_model() && !request->has_model_delta()) {
699 response->
set_status_str(
"Requests without model are considered OPTIMAL");
702 if (request->has_model() && request->has_model_delta()) {
705 "Fields 'model' and 'model_delta' are mutually exclusive");
714 std::move(*(request.
get_mutable()->mutable_model())));
720 if (request->has_model_delta()) {
723 std::string contents;
725 request->model_delta().baseline_model_file_path(), &contents);
726 if (!file_read_status.ok()) {
729 "Error when reading model_delta.baseline_model_file_path: '" +
730 file_read_status.ToString());
733 if (!model.
get_mutable()->ParseFromString(contents)) {
736 absl::StrFormat(
"The contents of baseline model file '%s' couldn't "
737 "be parsed as a raw serialized MPModelProto",
738 request->model_delta().baseline_model_file_path()));
748 if (error.empty() && request->has_model_delta()) {
755 if (!error.empty()) {
756 if (request->enable_internal_solver_output()) {
757 LOG(ERROR) << absl::StrCat(
"Invalid model: ", error);
759 response->
set_status(absl::StrContains(error,
"Infeasible")
766 if (model->variable_size() == 0 && model->constraint_size() == 0 &&
767 model->general_constraint_size() == 0) {
772 "Requests without variables and constraints are considered OPTIMAL");
776 return std::move(model);
788 absl::GetFlag(FLAGS_model_validator_infinity));
789 if (!error.empty())
return absl::StrCat(
"Invalid solution_hint: ", error);
793 return "Empty solution_hint.";
798 return absl::StrCat(
"Partial solution_hint: only ",
800 num_vars,
" problem variables are set.");
804 std::vector<double> var_value(num_vars);
805 for (
int i = 0; i < model.
solution_hint().var_index_size(); ++i) {
808 var_value[var_index] = value;
813 return absl::StrCat(
"Variable '", model.
variable(var_index).
name(),
814 "' is set to ", (value),
815 " which is not in the variable bounds [", (lb),
", ",
816 (ub),
"] modulo a tolerance of ", (tolerance),
".");
821 for (
int cst_index = 0; cst_index < model.
constraint_size(); ++cst_index) {
833 "Constraint '", model.
constraint(cst_index).
name(),
"' has activity ",
834 (activity.
Value()),
" which is not in the constraint bounds [", (lb),
835 ", ", (ub),
"] modulo a tolerance of ", (tolerance),
".");
844 const double abs_value_threshold =
845 absl::GetFlag(FLAGS_model_validator_infinity);
849 absl::flat_hash_set<int> new_var_indices;
850 int max_var_index = num_vars - 1;
853 const int var_index = pair.first;
856 error =
"Invalid key";
857 }
else if (var_index >= num_vars) {
858 max_var_index = std::max(max_var_index, var_index);
859 new_var_indices.insert(var_index);
861 FindErrorInMPVariable(var_override_proto, abs_value_threshold,
864 tmp_var_proto = model.
variable(var_index);
867 tmp_var_proto.
MergeFrom(var_override_proto);
869 FindErrorInMPVariable(tmp_var_proto, abs_value_threshold,
872 if (!error.empty()) {
873 return absl::StrFormat(
874 "variable_overrides with key (eg. var index) = %d: %s", var_index,
878 if (max_var_index != num_vars + new_var_indices.size() - 1) {
879 return absl::StrFormat(
880 "The added and existing variable indices do not form a dense integer "
881 "interval: oldmax=%d, max=%d, num added=%d",
882 num_vars - 1, max_var_index, new_var_indices.size());
885 num_vars += new_var_indices.size();
892 std::vector<bool> variable_appears(num_vars,
false);
895 absl::flat_hash_set<int> new_ct_indices;
896 int max_ct_index = num_constraints - 1;
898 const int ct_index = pair.first;
901 error =
"Invalid constraint index";
902 }
else if (ct_index >= num_constraints) {
903 max_ct_index = std::max(max_ct_index, ct_index);
904 new_ct_indices.insert(ct_index);
905 error = FindErrorInMPConstraint(
906 constraint_override_proto, &variable_appears, abs_value_threshold,
916 tmp_constraint_proto.
Clear();
918 &tmp_constraint_proto);
919 tmp_constraint_proto.
MergeFrom(constraint_override_proto);
920 error = FindErrorInMPConstraint(
921 tmp_constraint_proto, &variable_appears, abs_value_threshold,
924 if (!error.empty()) {
925 return absl::StrFormat(
926 "constraint_overrides with key (eg. constraint index) = %d: %s",
930 if (max_ct_index != num_constraints + new_ct_indices.size() - 1) {
931 return absl::StrFormat(
932 "The added and existing constraint indices do not form a dense integer "
933 "interval: oldmax=%d, max=%d, num added=%d",
934 num_constraints - 1, max_ct_index, new_ct_indices.size());
942#define COPY_FIELD_IF_PRESENT(field) \
943 if (from.has_##field()) to->set_##field(from.field())
948#undef COPY_FIELD_IF_PRESENT
952void PruneZeroTermsInMpConstraint(MPConstraintProto* ct) {
956 while (first_zero < ct->var_index_size() &&
957 ct->coefficient(first_zero) != 0.0) {
960 int num_kept = first_zero;
961 for (
int i = first_zero;
i < ct->var_index_size(); ++
i) {
962 if (ct->coefficient(i) == 0.0)
continue;
964 ct->set_var_index(num_kept, ct->var_index(i));
965 ct->set_coefficient(num_kept, ct->coefficient(i));
969 ct->mutable_var_index()->Truncate(num_kept);
970 ct->mutable_coefficient()->Truncate(num_kept);
977void ExtendRepeatedPtrFieldToSize(
const int size, T* repeated_messages) {
978 DCHECK_GE(size, repeated_messages->size());
979 while (repeated_messages->size() < size) repeated_messages->Add();
986 int max_var_index = -1;
988 max_var_index = std::max(max_var_index, p.first);
999 int max_ct_index = -1;
1001 max_ct_index = std::max(max_ct_index, p.first);
1004 if (max_ct_index >= old_num_constraints) {
1012 if (p.first >= old_num_constraints) {
1013 *baseline = override_ct;
1020 -absl::GetFlag(FLAGS_model_validator_infinity) &&
1023 absl::GetFlag(FLAGS_model_validator_infinity)) {
1032 absl::flat_hash_map<int, double> term_overrides;
1038 auto it = term_overrides.find(baseline->
var_index(i));
1039 if (it == term_overrides.end())
continue;
1043 PruneZeroTermsInMpConstraint(baseline);
1045 for (
const auto& p : term_overrides) {
1046 if (p.second != 0.0) {
void Add(const FpNumber &value)
Adds an FpNumber to the sum.
FpNumber Value() const
Gets the value of the sum.
T * get_mutable()
This will copy the object if we don't already have ownership.
bool has_ownership() const
ABSL_ATTRIBUTE_REINITIALIZES void Clear() PROTOBUF_FINAL
double lower_bound() const
bool has_upper_bound() const
optional double upper_bound = 3 [default = inf];
const ::std::string & name() const
void MergeFrom(const MPConstraintProto &from)
int var_index_size() const
repeated int32 var_index = 6 [packed = true];
void add_coefficient(double value)
bool has_lower_bound() const
optional double lower_bound = 2 [default = -inf];
double coefficient(int index) const
void set_coefficient(int index, double value)
void add_var_index(::int32_t value)
::int32_t var_index(int index) const
double upper_bound() const
GeneralConstraintCase general_constraint_case() const
const ::operations_research::MPAbsConstraint & abs_constraint() const
const ::operations_research::MPArrayWithConstantConstraint & max_constraint() const
const ::operations_research::MPIndicatorConstraint & indicator_constraint() const
const ::operations_research::MPArrayConstraint & and_constraint() const
const ::operations_research::MPArrayWithConstantConstraint & min_constraint() const
const ::operations_research::MPArrayConstraint & or_constraint() const
const ::operations_research::MPQuadraticConstraint & quadratic_constraint() const
const ::operations_research::MPSosConstraint & sos_constraint() const
const ::google::protobuf::Map<::int32_t, ::operations_research::MPVariableProto > & variable_overrides() const
const ::google::protobuf::Map<::int32_t, ::operations_research::MPConstraintProto > & constraint_overrides() const
static bool TargetType_IsValid(int value)
static constexpr TargetType VARIABLE_DEFAULT
static constexpr TargetType CONSTRAINT
static constexpr TargetType GENERAL_CONSTRAINT
const ::operations_research::MPVariableProto & variable(int index) const
int variable_size() const
repeated .operations_research.MPVariableProto variable = 3;
MPModelProto_Annotation Annotation
nested types -------------------------------------------------—
const ::operations_research::MPGeneralConstraintProto & general_constraint(int index) const
const ::operations_research::MPQuadraticObjective & quadratic_objective() const
int annotation_size() const
repeated .operations_research.MPModelProto.Annotation annotation = 9;
::operations_research::MPConstraintProto *PROTOBUF_NONNULL mutable_constraint(int index)
const ::operations_research::MPConstraintProto & constraint(int index) const
int general_constraint_size() const
repeated .operations_research.MPGeneralConstraintProto general_constraint = 7;
const ::operations_research::MPModelProto_Annotation & annotation(int index) const
::operations_research::MPVariableProto *PROTOBUF_NONNULL mutable_variable(int index)
const ::operations_research::PartialVariableAssignment & solution_hint() const
bool has_quadratic_objective() const
optional .operations_research.MPQuadraticObjective quadratic_objective = 8;
int constraint_size() const
repeated .operations_research.MPConstraintProto constraint = 4;
double objective_offset() const
void set_objective_value(double value)
void set_best_objective_bound(double value)
void set_status_str(Arg_ &&arg, Args_... args)
double objective_value() const
void set_status(::operations_research::MPSolverResponseStatus value)
void MergeFrom(const MPVariableProto &from)
double lower_bound() const
double upper_bound() const
const ::std::string & name() const
::int32_t var_index(int index) const
double var_value(int index) const
int var_index_size() const
repeated int32 var_index = 1 [packed = true];
ABSL_FLAG(double, model_validator_infinity, 1e100, "Anything above or equal to this magnitude will be considered infinity.")
#define COPY_FIELD_IF_PRESENT(field)
Collection::value_type::second_type & LookupOrInsert(Collection *const collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
const MapUtilMappedT< Collection > & FindWithDefault(const Collection &collection, const KeyType &key, const MapUtilMappedT< Collection > &value)
In SWIG mode, we don't want anything besides these top-level includes.
bool IsSmallerWithinTolerance(FloatType x, FloatType y, FloatType tolerance)
std::optional< LazyMutableCopy< MPModelProto > > ExtractValidMPModelOrPopulateResponseStatus(const MPModelRequest &request, MPSolutionResponse *response)
std::string FindErrorInMPModelDeltaProto(const MPModelDeltaProto &delta, const MPModelProto &model)
void ApplyVerifiedMPModelDelta(const MPModelDeltaProto &delta, MPModelProto *model)
std::string ProtobufShortDebugString(const P &message)
::absl::Status PortableFileGetContents(absl::string_view file_name, std::string *output)
std::optional< LazyMutableCopy< MPModelProto > > GetMPModelOrPopulateResponse(LazyMutableCopy< MPModelRequest > &request, MPSolutionResponse *response)
std::string FindErrorInMPModelProto(const MPModelProto &model, double abs_value_threshold, const bool accept_trivially_infeasible_bounds)
std::string FindFeasibilityErrorInSolutionHint(const MPModelProto &model, double tolerance)
void MergeMPConstraintProtoExceptTerms(const MPConstraintProto &from, MPConstraintProto *to)
trees with all degrees equal to