18 #if !defined(_MSC_VER)
26 #include "absl/status/status.h"
27 #include "absl/status/statusor.h"
28 #include "absl/strings/ascii.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/str_replace.h"
33 #include "absl/synchronization/mutex.h"
48 "Systematically verify the solution when calling Solve()"
49 ", and change the return value of Solve() to ABNORMAL if"
50 " an error was detected.");
52 "If --verify_solution is set: LOG(ERROR) all errors detected"
53 " during the verification of the solution.");
54 ABSL_FLAG(
bool, linear_solver_enable_verbose_output,
false,
55 "If set, enables verbose output for the solver. Setting this flag"
56 " is the same as calling MPSolver::EnableOutput().");
58 ABSL_FLAG(
bool, mpsolver_bypass_model_validation,
false,
59 "If set, the user-provided Model won't be verified before Solve()."
60 " Invalid models will typically trigger various error responses"
61 " from the underlying solvers; sometimes crashes.");
66 switch (solver_type) {
67 case MPModelRequest::GLOP_LINEAR_PROGRAMMING:
68 case MPModelRequest::CLP_LINEAR_PROGRAMMING:
69 case MPModelRequest::GLPK_LINEAR_PROGRAMMING:
70 case MPModelRequest::GUROBI_LINEAR_PROGRAMMING:
71 case MPModelRequest::XPRESS_LINEAR_PROGRAMMING:
72 case MPModelRequest::CPLEX_LINEAR_PROGRAMMING:
75 case MPModelRequest::SCIP_MIXED_INTEGER_PROGRAMMING:
76 case MPModelRequest::GLPK_MIXED_INTEGER_PROGRAMMING:
77 case MPModelRequest::CBC_MIXED_INTEGER_PROGRAMMING:
78 case MPModelRequest::GUROBI_MIXED_INTEGER_PROGRAMMING:
79 case MPModelRequest::KNAPSACK_MIXED_INTEGER_PROGRAMMING:
80 case MPModelRequest::BOP_INTEGER_PROGRAMMING:
81 case MPModelRequest::SAT_INTEGER_PROGRAMMING:
82 case MPModelRequest::XPRESS_MIXED_INTEGER_PROGRAMMING:
83 case MPModelRequest::CPLEX_MIXED_INTEGER_PROGRAMMING:
86 LOG(DFATAL) <<
"Invalid SolverType: " << solver_type;
92 if (
var ==
nullptr)
return 0.0;
98 if (
var ==
nullptr)
return;
100 auto it = coefficients_.find(
var);
108 if (it != coefficients_.end() && it->second != 0.0) {
109 const double old_value = it->second;
115 auto insertion_result = coefficients_.insert(std::make_pair(
var, coeff));
116 const double old_value =
117 insertion_result.second ? 0.0 : insertion_result.first->second;
118 insertion_result.first->second = coeff;
124 coefficients_.clear();
128 const bool change =
lb != lb_ ||
ub != ub_;
138 LOG(DFATAL) <<
"Dual value only available for continuous problems";
147 LOG(DFATAL) <<
"Basis status only available for continuous problems";
157 bool MPConstraint::ContainsNewVariables() {
159 for (
const auto& entry : coefficients_) {
160 const int variable_index = entry.first->index();
161 if (variable_index >= last_variable_index ||
173 if (
var ==
nullptr)
return 0.0;
179 if (
var ==
nullptr)
return;
181 auto it = coefficients_.find(
var);
184 if (it == coefficients_.end() || it->second == 0.0)
return;
187 coefficients_[
var] = coeff;
199 for (
auto var_value_pair : linear_expr.
terms()) {
201 <<
"Bad MPVariable* in LinearExpr, did you try adding an integer to an "
202 "MPVariable* directly?";
208 bool is_maximization) {
209 CheckLinearExpr(*interface_->
solver_, linear_expr);
211 coefficients_.clear();
213 for (
const auto& kv : linear_expr.
terms()) {
220 CheckLinearExpr(*interface_->
solver_, linear_expr);
222 for (
const auto& kv : linear_expr.
terms()) {
229 coefficients_.clear();
269 return (integer_ && interface_->
IsMIP()) ? round(solution_value_)
275 return solution_value_;
280 LOG(DFATAL) <<
"Reduced cost only available for continuous problems";
284 return reduced_cost_;
289 LOG(DFATAL) <<
"Basis status only available for continuous problems";
300 const bool change =
lb != lb_ ||
ub != ub_;
318 if (priority == branching_priority_)
return;
319 branching_priority_ = priority;
328 return interface_->SolverVersion();
336 if (num_threads < 1) {
337 return absl::InvalidArgumentError(
"num_threads must be a positive number.");
339 const absl::Status status = interface_->SetNumThreads(num_threads);
341 num_threads_ = num_threads;
348 solver_specific_parameter_string_ =
parameters;
349 return interface_->SetSolverSpecificParametersAsString(
parameters);
354 #if defined(USE_CLP) || defined(USE_CBC)
360 #if defined(USE_GLPK)
366 #if defined(USE_SCIP)
371 #if defined(USE_CPLEX)
376 #if defined(USE_XPRESS)
383 DCHECK(solver !=
nullptr);
391 #if defined(USE_GLPK)
393 return BuildGLPKInterface(
false, solver);
395 return BuildGLPKInterface(
true, solver);
397 #if defined(USE_CLP) || defined(USE_CBC)
405 #if defined(USE_SCIP)
413 #if defined(USE_CPLEX)
415 return BuildCplexInterface(
false, solver);
417 return BuildCplexInterface(
true, solver);
419 #if defined(USE_XPRESS)
421 return BuildXpressInterface(
true, solver);
423 return BuildXpressInterface(
false, solver);
427 LOG(
FATAL) <<
"Linear solver not recognized.";
434 int NumDigits(
int n) {
437 #if defined(_MSC_VER)
438 return static_cast<int>(
std::max(1.0L, log(1.0L * n) / log(10.0L) + 1.0));
440 return static_cast<int>(
std::max(1.0, log10(
static_cast<double>(n)) + 1.0));
449 construction_time_(
absl::Now()) {
450 interface_.reset(BuildSolverInterface(
this));
451 if (absl::GetFlag(FLAGS_linear_solver_enable_verbose_output)) {
454 objective_.reset(
new MPObjective(interface_.get()));
475 return MPSolver::GurobiIsCorrectlyInstalled();
502 struct NamedOptimizationProblemType {
508 #if defined(_MSC_VER)
535 const std::string
id =
536 absl::StrReplaceAll(absl::AsciiStrToUpper(solver_id), {{
"-",
"_"}});
539 MPModelRequest::SolverType solver_type;
540 if (MPModelRequest::SolverType_Parse(
id, &solver_type)) {
546 std::string lower_id = absl::AsciiStrToLower(
id);
549 if (absl::EndsWith(lower_id,
"_mip")) {
550 lower_id = lower_id.substr(0, lower_id.size() - 4);
554 if (lower_id ==
"cp_sat") {
560 if (named_solver.name == lower_id) {
561 *type = named_solver.problem_type;
572 if (named_solver.problem_type == optimization_problem_type) {
573 return named_solver.name;
576 LOG(
FATAL) <<
"Unrecognized solver type: "
577 <<
static_cast<int>(optimization_problem_type);
583 std::string* error) {
584 DCHECK(solver_type !=
nullptr);
588 *error = absl::StrCat(
"Solver type: ", text,
" does not exist.");
595 const std::string& solver_id) {
605 LOG(
WARNING) <<
"Unrecognized solver type: " << solver_id;
610 <<
" not linked in, or the license was not found.";
617 if (!variable_name_to_index_) GenerateVariableNameIndex();
619 absl::flat_hash_map<std::string, int>::const_iterator it =
620 variable_name_to_index_->find(var_name);
621 if (it == variable_name_to_index_->end())
return nullptr;
622 return variables_[it->second];
626 const std::string& constraint_name)
const {
627 if (!constraint_name_to_index_) GenerateConstraintNameIndex();
629 const auto it = constraint_name_to_index_->find(constraint_name);
630 if (it == constraint_name_to_index_->end())
return nullptr;
631 return constraints_[it->second];
637 const MPModelProto& input_model, std::string* error_message) {
642 return LoadModelFromProtoInternal(input_model,
true,
648 const MPModelProto& input_model, std::string* error_message) {
650 GenerateVariableNameIndex();
651 GenerateConstraintNameIndex();
653 return LoadModelFromProtoInternal(input_model,
false,
659 const MPModelProto& input_model,
bool clear_names,
660 bool check_model_validity, std::string* error_message) {
661 CHECK(error_message !=
nullptr);
662 if (check_model_validity) {
664 if (!error.empty()) {
665 *error_message = error;
667 <<
"Invalid model given to LoadModelFromProto(): " << error;
668 if (absl::GetFlag(FLAGS_mpsolver_bypass_model_validation)) {
670 <<
"Ignoring the model error(s) because of"
671 <<
" --mpsolver_bypass_model_validation.";
679 if (input_model.has_quadratic_objective()) {
681 "Optimizing a quadratic objective is only supported through direct "
682 "proto solves. Please use MPSolver::SolveWithProto, or the solver's "
683 "direct proto solve function.";
689 const std::string empty;
690 for (
int i = 0; i < input_model.variable_size(); ++i) {
691 const MPVariableProto& var_proto = input_model.variable(i);
692 MPVariable* variable =
693 MakeNumVar(var_proto.lower_bound(), var_proto.upper_bound(),
694 clear_names ? empty : var_proto.name());
695 variable->SetInteger(var_proto.is_integer());
696 if (var_proto.branching_priority() != 0) {
697 variable->SetBranchingPriority(var_proto.branching_priority());
699 objective->SetCoefficient(variable, var_proto.objective_coefficient());
702 for (
const MPConstraintProto& ct_proto : input_model.constraint()) {
703 if (ct_proto.lower_bound() == -
infinity() &&
704 ct_proto.upper_bound() ==
infinity()) {
708 MPConstraint*
const ct =
710 clear_names ? empty : ct_proto.name());
711 ct->set_is_lazy(ct_proto.is_lazy());
712 for (
int j = 0; j < ct_proto.var_index_size(); ++j) {
713 ct->SetCoefficient(variables_[ct_proto.var_index(j)],
714 ct_proto.coefficient(j));
718 for (
const MPGeneralConstraintProto& general_constraint :
719 input_model.general_constraint()) {
720 switch (general_constraint.general_constraint_case()) {
721 case MPGeneralConstraintProto::kIndicatorConstraint: {
723 general_constraint.indicator_constraint().constraint();
730 MPConstraint*
const constraint =
new MPConstraint(
731 constraint_index,
proto.lower_bound(),
proto.upper_bound(),
732 clear_names ?
"" :
proto.name(), interface_.get());
733 if (constraint_name_to_index_) {
737 constraints_.push_back(constraint);
738 constraint_is_extracted_.push_back(
false);
740 constraint->set_is_lazy(
proto.is_lazy());
741 for (
int j = 0; j <
proto.var_index_size(); ++j) {
742 constraint->SetCoefficient(variables_[
proto.var_index(j)],
743 proto.coefficient(j));
746 MPVariable*
const variable =
747 variables_[general_constraint.indicator_constraint().var_index()];
748 constraint->indicator_variable_ = variable;
749 constraint->indicator_value_ =
750 general_constraint.indicator_constraint().var_value();
752 if (!interface_->AddIndicatorConstraint(constraint)) {
753 *error_message =
"Solver doesn't support indicator constraints";
759 *error_message = absl::StrFormat(
760 "Optimizing general constraints of type %i is only supported "
761 "through direct proto solves. Please use MPSolver::SolveWithProto, "
762 "or the solver's direct proto solve function.",
763 general_constraint.general_constraint_case());
768 objective->SetOptimizationDirection(input_model.maximize());
769 if (input_model.has_objective_offset()) {
770 objective->SetOffset(input_model.objective_offset());
774 solution_hint_.clear();
775 for (
int i = 0; i < input_model.solution_hint().var_index_size(); ++i) {
776 solution_hint_.push_back(
777 std::make_pair(variables_[input_model.solution_hint().var_index(i)],
778 input_model.solution_hint().var_value(i)));
810 ResultStatusToMPSolverResponseStatus(interface_->result_status_));
814 for (
int i = 0; i < variables_.size(); ++i) {
815 response->add_variable_value(variables_[i]->solution_value());
818 if (interface_->IsMIP()) {
819 response->set_best_objective_bound(interface_->best_objective_bound());
822 for (
int j = 0; j < constraints_.size(); ++j) {
823 response->add_dual_value(constraints_[j]->dual_value());
826 for (
int i = 0; i < variables_.size(); ++i) {
827 response->add_reduced_cost(variables_[i]->reduced_cost());
837 MPSolver solver(model_request.model().name(),
839 model_request.solver_type()));
840 if (model_request.enable_internal_solver_output()) {
844 auto optional_response = solver.interface_->DirectlySolveProto(model_request);
845 if (optional_response) {
846 *
response = std::move(optional_response).value();
850 const absl::optional<LazyMutableCopy<MPModelProto>> optional_model =
852 if (!optional_model) {
853 LOG_IF(
WARNING, model_request.enable_internal_solver_output())
854 <<
"Failed to extract a valid model from protocol buffer. Status: "
855 << ProtoEnumToString<MPSolverResponseStatus>(
response->status()) <<
" ("
859 std::string error_message;
860 response->set_status(solver.LoadModelFromProtoInternal(
861 optional_model->get(),
true,
862 false, &error_message));
866 response->set_status_str(error_message);
867 LOG_IF(
WARNING, model_request.enable_internal_solver_output())
868 <<
"LoadModelFromProtoInternal() failed even though the model was "
870 << ProtoEnumToString<MPSolverResponseStatus>(
response->status()) <<
" ("
871 <<
response->status() <<
"); Error: " << error_message;
874 if (model_request.has_solver_time_limit_seconds()) {
876 absl::Seconds(model_request.solver_time_limit_seconds()));
878 std::string warning_message;
879 if (model_request.has_solver_specific_parameters()) {
881 model_request.solver_specific_parameters())) {
882 if (model_request.ignore_solver_specific_parameters_failure()) {
885 "Warning: the solver specific parameters were not successfully "
895 if (!warning_message.empty()) {
896 response->set_status_str(absl::StrCat(
903 DCHECK(output_model !=
nullptr);
904 output_model->Clear();
906 output_model->set_name(
Name());
908 for (
int j = 0; j < variables_.size(); ++j) {
910 MPVariableProto*
const variable_proto = output_model->add_variable();
913 variable_proto->set_name(
var->name());
914 variable_proto->set_lower_bound(
var->lb());
915 variable_proto->set_upper_bound(
var->ub());
916 variable_proto->set_is_integer(
var->integer());
917 if (objective_->GetCoefficient(
var) != 0.0) {
918 variable_proto->set_objective_coefficient(
919 objective_->GetCoefficient(
var));
921 if (
var->branching_priority() != 0) {
922 variable_proto->set_branching_priority(
var->branching_priority());
932 absl::flat_hash_map<const MPVariable*, int> var_to_index;
933 for (
int j = 0; j < variables_.size(); ++j) {
934 var_to_index[variables_[j]] = j;
938 for (
int i = 0; i < constraints_.size(); ++i) {
940 MPConstraintProto* constraint_proto;
942 MPGeneralConstraintProto*
const general_constraint_proto =
943 output_model->add_general_constraint();
944 general_constraint_proto->set_name(constraint->
name());
945 MPIndicatorConstraint*
const indicator_constraint_proto =
946 general_constraint_proto->mutable_indicator_constraint();
947 indicator_constraint_proto->set_var_index(
949 indicator_constraint_proto->set_var_value(constraint->
indicator_value());
950 constraint_proto = indicator_constraint_proto->mutable_constraint();
952 constraint_proto = output_model->add_constraint();
954 constraint_proto->set_name(constraint->
name());
955 constraint_proto->set_lower_bound(constraint->
lb());
956 constraint_proto->set_upper_bound(constraint->
ub());
957 constraint_proto->set_is_lazy(constraint->
is_lazy());
960 std::vector<std::pair<int, double>> linear_term;
961 for (
const auto& entry : constraint->coefficients_) {
965 const double coeff = entry.second;
966 linear_term.push_back(std::pair<int, double>(var_index, coeff));
970 std::sort(linear_term.begin(), linear_term.end());
972 for (
const std::pair<int, double>& var_and_coeff : linear_term) {
973 constraint_proto->add_var_index(var_and_coeff.first);
974 constraint_proto->add_coefficient(var_and_coeff.second);
978 output_model->set_maximize(
Objective().maximization());
979 output_model->set_objective_offset(
Objective().offset());
981 if (!solution_hint_.empty()) {
982 PartialVariableAssignment*
const hint =
983 output_model->mutable_solution_hint();
984 for (
const auto& var_value_pair : solution_hint_) {
985 hint->add_var_index(var_value_pair.first->index());
986 hint->add_var_value(var_value_pair.second);
996 return absl::InvalidArgumentError(absl::StrCat(
997 "Cannot load a solution unless its status is OPTIMAL or FEASIBLE"
999 ProtoEnumToString<MPSolverResponseStatus>(
response.status()),
")"));
1004 if (
response.variable_value_size() != variables_.size()) {
1005 return absl::InvalidArgumentError(absl::StrCat(
1006 "Trying to load a solution whose number of variables (",
1008 ") does not correspond to the Solver's (", variables_.size(),
")"));
1010 interface_->ExtractModel();
1014 double largest_error = 0;
1015 int num_vars_out_of_bounds = 0;
1016 int last_offending_var = -1;
1017 for (
int i = 0; i <
response.variable_value_size(); ++i) {
1018 const double var_value =
response.variable_value(i);
1021 const double lb_error =
var->lb() - var_value;
1022 const double ub_error = var_value -
var->ub();
1023 if (lb_error > tolerance || ub_error > tolerance) {
1024 ++num_vars_out_of_bounds;
1026 last_offending_var = i;
1029 if (num_vars_out_of_bounds > 0) {
1030 return absl::InvalidArgumentError(absl::StrCat(
1031 "Loaded a solution whose variables matched the solver's, but ",
1032 num_vars_out_of_bounds,
" of ", variables_.size(),
1033 " variables were out of their bounds, by more than the primal"
1034 " tolerance which is: ",
1035 tolerance,
". Max error: ", largest_error,
", last offender var is #",
1036 last_offending_var,
": '", variables_[last_offending_var]->name(),
1041 for (
int i = 0; i <
response.variable_value_size(); ++i) {
1042 variables_[i]->set_solution_value(
response.variable_value(i));
1046 if (
response.has_objective_value()) {
1047 interface_->objective_value_ =
response.objective_value();
1049 if (
response.has_best_objective_bound()) {
1050 interface_->best_objective_bound_ =
response.best_objective_bound();
1055 return absl::OkStatus();
1063 if (variable_name_to_index_) {
1064 variable_name_to_index_->clear();
1066 variable_is_extracted_.clear();
1067 constraints_.clear();
1068 if (constraint_name_to_index_) {
1069 constraint_name_to_index_->clear();
1071 constraint_is_extracted_.clear();
1072 interface_->Reset();
1073 solution_hint_.clear();
1081 const std::vector<BasisStatus>& variable_statuses,
1082 const std::vector<BasisStatus>& constraint_statuses) {
1083 interface_->SetStartingLpBasis(variable_statuses, constraint_statuses);
1087 const std::string&
name) {
1090 new MPVariable(var_index, lb, ub, integer,
name, interface_.get());
1091 if (variable_name_to_index_) {
1094 variables_.push_back(v);
1095 variable_is_extracted_.push_back(
false);
1096 interface_->AddVariable(v);
1101 const std::string&
name) {
1106 const std::string&
name) {
1115 const std::string&
name,
1116 std::vector<MPVariable*>* vars) {
1118 if (nb <= 0)
return;
1119 const int num_digits = NumDigits(nb);
1120 for (
int i = 0; i < nb; ++i) {
1125 absl::StrFormat(
"%s%0*d",
name.c_str(), num_digits, i);
1126 vars->push_back(
MakeVar(lb, ub, integer, vname));
1132 const std::string&
name,
1133 std::vector<MPVariable*>* vars) {
1138 const std::string&
name,
1139 std::vector<MPVariable*>* vars) {
1144 std::vector<MPVariable*>* vars) {
1157 const std::string&
name) {
1161 if (constraint_name_to_index_) {
1165 constraints_.push_back(constraint);
1166 constraint_is_extracted_.push_back(
false);
1167 interface_->AddRowConstraint(constraint);
1180 const std::string&
name) {
1190 int MPSolver::ComputeMaxConstraintSize(
int min_constraint_index,
1191 int max_constraint_index)
const {
1192 int max_constraint_size = 0;
1194 DCHECK_LE(max_constraint_index, constraints_.size());
1195 for (
int i = min_constraint_index; i < max_constraint_index; ++i) {
1197 if (
ct->coefficients_.size() > max_constraint_size) {
1198 max_constraint_size =
ct->coefficients_.size();
1201 return max_constraint_size;
1204 bool MPSolver::HasInfeasibleConstraints()
const {
1205 bool hasInfeasibleConstraints =
false;
1206 for (
int i = 0; i < constraints_.size(); ++i) {
1207 if (constraints_[i]->lb() > constraints_[i]->ub()) {
1208 LOG(
WARNING) <<
"Constraint " << constraints_[i]->name() <<
" (" << i
1209 <<
") has contradictory bounds:"
1210 <<
" lower bound = " << constraints_[i]->lb()
1211 <<
" upper bound = " << constraints_[i]->ub();
1212 hasInfeasibleConstraints =
true;
1215 return hasInfeasibleConstraints;
1218 bool MPSolver::HasIntegerVariables()
const {
1219 for (
const MPVariable*
const variable : variables_) {
1220 if (variable->integer())
return true;
1227 return Solve(default_param);
1236 if (HasInfeasibleConstraints()) {
1238 return interface_->result_status_;
1242 if (absl::GetFlag(FLAGS_verify_solution)) {
1244 VLOG(1) <<
"--verify_solution enabled, but the solver did not find a"
1245 <<
" solution: skipping the verification.";
1248 absl::GetFlag(FLAGS_log_verification_errors))) {
1250 interface_->result_status_ = status;
1253 DCHECK_EQ(interface_->result_status_, status);
1258 interface_->Write(file_name);
1263 const std::string prefix =
"Variable '" +
var.
name() +
"': domain = ";
1266 return prefix +
"∅";
1270 if (
var.integer() &&
var.ub() -
var.lb() <= 1) {
1274 return prefix +
"∅";
1275 }
else if (lb == ub) {
1276 return absl::StrFormat(
"%s{ %d }", prefix.c_str(), lb);
1278 return absl::StrFormat(
"%s{ %d, %d }", prefix.c_str(), lb, ub);
1282 if (
var.lb() ==
var.ub()) {
1283 return absl::StrFormat(
"%s{ %f }", prefix.c_str(),
var.lb());
1285 return prefix + (
var.integer() ?
"Integer" :
"Real") +
" in " +
1287 ? std::string(
"]-∞")
1288 :
absl::StrFormat(
"[%f",
var.lb())) +
1290 (
var.ub() >= MPSolver::infinity() ? std::string(
"+∞[")
1291 :
absl::StrFormat(
"%f]",
var.ub()));
1294 std::string PrettyPrintConstraint(
const MPConstraint& constraint) {
1295 std::string prefix =
"Constraint '" + constraint.name() +
"': ";
1298 constraint.lb() > constraint.ub()) {
1299 return prefix +
"ALWAYS FALSE";
1303 return prefix +
"ALWAYS TRUE";
1305 prefix +=
"<linear expr>";
1307 if (constraint.lb() == constraint.ub()) {
1308 return absl::StrFormat(
"%s = %f", prefix.c_str(), constraint.lb());
1312 return absl::StrFormat(
"%s ≤ %f", prefix.c_str(), constraint.ub());
1315 return absl::StrFormat(
"%s ≥ %f", prefix.c_str(), constraint.lb());
1317 return absl::StrFormat(
"%s ∈ [%f, %f]", prefix.c_str(), constraint.lb(),
1323 interface_->ExtractModel();
1324 for (
MPVariable*
const variable : variables_) {
1325 const double value = variable->solution_value();
1326 if (std::isnan(
value)) {
1327 return absl::InvalidArgumentError(
1328 absl::StrCat(
"NaN value for ", PrettyPrintVar(*variable)));
1330 if (value < variable->lb()) {
1331 variable->set_solution_value(variable->lb());
1332 }
else if (
value > variable->ub()) {
1333 variable->set_solution_value(variable->ub());
1337 return absl::OkStatus();
1342 if (!interface_->CheckSolutionIsSynchronizedAndExists())
return {};
1343 std::vector<double> activities(constraints_.size(), 0.0);
1344 for (
int i = 0; i < constraints_.size(); ++i) {
1347 for (
const auto& entry : constraint.coefficients_) {
1348 sum.
Add(entry.first->solution_value() * entry.second);
1350 activities[i] = sum.
Value();
1357 double max_observed_error = 0;
1358 if (tolerance < 0) tolerance =
infinity();
1362 for (
int i = 0; i < variables_.size(); ++i) {
1364 const double value =
var.solution_value();
1366 if (std::isnan(
value)) {
1369 LOG_IF(
ERROR, log_errors) <<
"NaN value for " << PrettyPrintVar(
var);
1374 if (
value <
var.lb() - tolerance) {
1378 <<
"Value " <<
value <<
" too low for " << PrettyPrintVar(
var);
1383 if (
value >
var.ub() + tolerance) {
1387 <<
"Value " <<
value <<
" too high for " << PrettyPrintVar(
var);
1392 if (fabs(
value - round(
value)) > tolerance) {
1394 max_observed_error =
1397 <<
"Non-integer value " <<
value <<
" for " << PrettyPrintVar(
var);
1401 if (!
IsMIP() && HasIntegerVariables()) {
1402 LOG_IF(
INFO, log_errors) <<
"Skipped variable integrality check, because "
1403 <<
"a continuous relaxation of the model was "
1404 <<
"solved (i.e., the selected solver does not "
1405 <<
"support integer variables).";
1410 for (
int i = 0; i < constraints_.size(); ++i) {
1412 const double activity = activities[i];
1414 double inaccurate_activity = 0.0;
1415 for (
const auto& entry : constraint.coefficients_) {
1416 inaccurate_activity += entry.first->solution_value() * entry.second;
1419 if (std::isnan(activity) || std::isnan(inaccurate_activity)) {
1423 <<
"NaN value for " << PrettyPrintConstraint(constraint);
1431 if (activity < constraint.
lb() - tolerance) {
1433 max_observed_error =
1434 std::max(max_observed_error, constraint.
lb() - activity);
1436 <<
"Activity " << activity <<
" too low for "
1437 << PrettyPrintConstraint(constraint);
1438 }
else if (inaccurate_activity < constraint.
lb() - tolerance) {
1440 <<
"Activity " << activity <<
", computed with the (inaccurate)"
1441 <<
" standard sum of its terms, is too low for "
1442 << PrettyPrintConstraint(constraint);
1446 if (activity > constraint.
ub() + tolerance) {
1448 max_observed_error =
1449 std::max(max_observed_error, activity - constraint.
ub());
1451 <<
"Activity " << activity <<
" too high for "
1452 << PrettyPrintConstraint(constraint);
1453 }
else if (inaccurate_activity > constraint.
ub() + tolerance) {
1455 <<
"Activity " << activity <<
", computed with the (inaccurate)"
1456 <<
" standard sum of its terms, is too high for "
1457 << PrettyPrintConstraint(constraint);
1467 double inaccurate_objective_value = objective.
offset();
1468 for (
const auto& entry : objective.coefficients_) {
1469 const double term = entry.first->solution_value() * entry.second;
1470 objective_sum.
Add(term);
1471 inaccurate_objective_value += term;
1473 const double actual_objective_value = objective_sum.
Value();
1475 objective.
Value(), actual_objective_value, tolerance, tolerance)) {
1478 max_observed_error, fabs(actual_objective_value - objective.
Value()));
1480 <<
"Objective value " << objective.
Value() <<
" isn't accurate"
1481 <<
", it should be " << actual_objective_value
1482 <<
" (delta=" << actual_objective_value - objective.
Value() <<
").";
1484 inaccurate_objective_value,
1485 tolerance, tolerance)) {
1487 <<
"Objective value " << objective.
Value() <<
" doesn't correspond"
1488 <<
" to the value computed with the standard (and therefore inaccurate)"
1489 <<
" sum of its terms.";
1491 if (num_errors > 0) {
1493 <<
"There were " << num_errors <<
" errors above the tolerance ("
1494 << tolerance <<
"), the largest was " << max_observed_error;
1511 return interface_->ComputeExactConditionNumber();
1515 if (
var ==
nullptr)
return false;
1516 if (
var->index() >= 0 &&
var->index() < variables_.size()) {
1518 return variables_[
var->index()] ==
var;
1524 std::string* model_str)
const {
1529 const auto status_or =
1531 *model_str = status_or.value_or(
"");
1532 return status_or.ok();
1536 std::string* model_str)
const {
1546 const auto status_or =
1548 *model_str = status_or.value_or(
"");
1549 return status_or.ok();
1553 for (
const auto& var_value_pair : hint) {
1555 <<
"hint variable does not belong to this solver";
1557 solution_hint_ = std::move(hint);
1560 void MPSolver::GenerateVariableNameIndex()
const {
1561 if (variable_name_to_index_)
return;
1562 variable_name_to_index_ = absl::flat_hash_map<std::string, int>();
1568 void MPSolver::GenerateConstraintNameIndex()
const {
1569 if (constraint_name_to_index_)
return;
1570 constraint_name_to_index_ = absl::flat_hash_map<std::string, int>();
1571 for (
const MPConstraint*
const cst : constraints_) {
1579 interface_->SetCallback(mp_callback);
1583 return interface_->SupportsCallbacks();
1609 <<
"MPSolverResponseStatusIsRpcError() called with invalid status "
1610 <<
"(value: " << status <<
")";
1622 sync_status_(MODEL_SYNCHRONIZED),
1623 result_status_(
MPSolver::NOT_SOLVED),
1625 last_constraint_index_(0),
1626 last_variable_index_(0),
1627 objective_value_(0.0),
1628 best_objective_bound_(0.0),
1634 LOG(
WARNING) <<
"Writing model not implemented in this solver interface.";
1669 solver_->variable_is_extracted_.assign(
solver_->variables_.size(),
false);
1670 solver_->constraint_is_extracted_.assign(
solver_->constraints_.size(),
false);
1676 <<
"The model has been changed since the solution was last computed."
1677 <<
" MPSolverInterface::sync_status_ = " <<
sync_status_;
1688 LOG(DFATAL) <<
"No solution exists. MPSolverInterface::result_status_ = "
1701 const double trivial_worst_bound =
1702 maximize_ ? -std::numeric_limits<double>::infinity()
1703 : std::numeric_limits<double>::infinity();
1705 LOG(DFATAL) <<
"Best objective bound only available for discrete problems.";
1706 return trivial_worst_bound;
1709 return trivial_worst_bound;
1712 if (
solver_->variables_.empty() &&
solver_->constraints_.empty()) {
1726 LOG(DFATAL) <<
"ComputeExactConditionNumber not implemented for "
1727 << ProtoEnumToString<MPModelRequest::SolverType>(
1728 static_cast<MPModelRequest::SolverType
>(
1763 LOG(
WARNING) <<
"Trying to set an unsupported parameter: " << param <<
".";
1767 LOG(
WARNING) <<
"Trying to set an unsupported parameter: " << param <<
".";
1771 LOG(
WARNING) <<
"Trying to set a supported parameter: " << param
1772 <<
" to an unsupported value: " <<
value;
1776 LOG(
WARNING) <<
"Trying to set a supported parameter: " << param
1777 <<
" to an unsupported value: " <<
value;
1781 return absl::UnimplementedError(
1782 absl::StrFormat(
"SetNumThreads() not supported by %s.",
SolverVersion()));
1799 std::string filename;
1801 filename += extension;
1802 if (no_error_so_far) {
1805 if (no_error_so_far) {
1810 LOG(DFATAL) <<
"Couldn't delete temporary parameters file: " << filename;
1813 if (!no_error_so_far) {
1814 LOG(
WARNING) <<
"Error in SetSolverSpecificParametersAsString() "
1815 <<
"for solver type: "
1816 << ProtoEnumToString<MPModelRequest::SolverType>(
1817 static_cast<MPModelRequest::SolverType
>(
1820 return no_error_so_far;
1824 LOG(
WARNING) <<
"ReadParameterFile() not supported by this solver.";
1852 : relative_mip_gap_value_(kDefaultRelativeMipGap),
1854 dual_tolerance_value_(kDefaultDualTolerance),
1855 presolve_value_(kDefaultPresolve),
1856 scaling_value_(kDefaultIntegerParamValue),
1857 lp_algorithm_value_(kDefaultIntegerParamValue),
1858 incrementality_value_(kDefaultIncrementality),
1859 lp_algorithm_is_default_(true) {}
1865 relative_mip_gap_value_ =
value;
1869 primal_tolerance_value_ =
value;
1873 dual_tolerance_value_ =
value;
1877 LOG(
ERROR) <<
"Trying to set an unknown parameter: " << param <<
".";
1887 LOG(
ERROR) <<
"Trying to set a supported parameter: " << param
1888 <<
" to an unknown value: " <<
value;
1890 presolve_value_ =
value;
1895 LOG(
ERROR) <<
"Trying to set a supported parameter: " << param
1896 <<
" to an unknown value: " <<
value;
1898 scaling_value_ =
value;
1903 LOG(
ERROR) <<
"Trying to set a supported parameter: " << param
1904 <<
" to an unknown value: " <<
value;
1906 lp_algorithm_value_ =
value;
1907 lp_algorithm_is_default_ =
false;
1912 LOG(
ERROR) <<
"Trying to set a supported parameter: " << param
1913 <<
" to an unknown value: " <<
value;
1915 incrementality_value_ =
value;
1919 LOG(
ERROR) <<
"Trying to set an unknown parameter: " << param <<
".";
1940 LOG(
ERROR) <<
"Trying to reset an unknown parameter: " << param <<
".";
1957 lp_algorithm_is_default_ =
true;
1965 LOG(
ERROR) <<
"Trying to reset an unknown parameter: " << param <<
".";
1984 return relative_mip_gap_value_;
1987 return primal_tolerance_value_;
1990 return dual_tolerance_value_;
1993 LOG(
ERROR) <<
"Trying to get an unknown parameter: " << param <<
".";
2003 return presolve_value_;
2007 return lp_algorithm_value_;
2010 return incrementality_value_;
2013 return scaling_value_;
2016 LOG(
ERROR) <<
"Trying to get an unknown parameter: " << param <<
".";