33#include "absl/base/const_init.h"
34#include "absl/container/flat_hash_map.h"
35#include "absl/container/flat_hash_set.h"
36#include "absl/flags/flag.h"
37#include "absl/log/check.h"
38#include "absl/status/status.h"
39#include "absl/status/statusor.h"
40#include "absl/strings/ascii.h"
41#include "absl/strings/match.h"
42#include "absl/strings/str_cat.h"
43#include "absl/strings/str_format.h"
44#include "absl/strings/str_replace.h"
45#include "absl/strings/string_view.h"
46#include "absl/synchronization/mutex.h"
47#include "absl/synchronization/notification.h"
48#include "absl/time/clock.h"
49#include "absl/time/time.h"
50#include "google/protobuf/text_format.h"
57#include "ortools/linear_solver/linear_solver.pb.h"
68 "Systematically verify the solution when calling Solve()"
69 ", and change the return value of Solve() to ABNORMAL if"
70 " an error was detected.");
72 "If --verify_solution is set: LOG(ERROR) all errors detected"
73 " during the verification of the solution.");
74ABSL_FLAG(
bool, linear_solver_enable_verbose_output,
false,
75 "If set, enables verbose output for the solver. Setting this flag"
76 " is the same as calling MPSolver::EnableOutput().");
78ABSL_FLAG(
bool, mpsolver_bypass_model_validation,
false,
79 "If set, the user-provided Model won't be verified before Solve()."
80 " Invalid models will typically trigger various error responses"
81 " from the underlying solvers; sometimes crashes.");
86 switch (solver_type) {
87 case MPModelRequest::PDLP_LINEAR_PROGRAMMING:
88 case MPModelRequest::GLOP_LINEAR_PROGRAMMING:
89 case MPModelRequest::CLP_LINEAR_PROGRAMMING:
90 case MPModelRequest::GLPK_LINEAR_PROGRAMMING:
91 case MPModelRequest::GUROBI_LINEAR_PROGRAMMING:
92 case MPModelRequest::HIGHS_LINEAR_PROGRAMMING:
93 case MPModelRequest::XPRESS_LINEAR_PROGRAMMING:
94 case MPModelRequest::CPLEX_LINEAR_PROGRAMMING:
97 case MPModelRequest::SCIP_MIXED_INTEGER_PROGRAMMING:
98 case MPModelRequest::GLPK_MIXED_INTEGER_PROGRAMMING:
99 case MPModelRequest::CBC_MIXED_INTEGER_PROGRAMMING:
100 case MPModelRequest::GUROBI_MIXED_INTEGER_PROGRAMMING:
101 case MPModelRequest::KNAPSACK_MIXED_INTEGER_PROGRAMMING:
102 case MPModelRequest::BOP_INTEGER_PROGRAMMING:
103 case MPModelRequest::SAT_INTEGER_PROGRAMMING:
104 case MPModelRequest::HIGHS_MIXED_INTEGER_PROGRAMMING:
105 case MPModelRequest::XPRESS_MIXED_INTEGER_PROGRAMMING:
106 case MPModelRequest::CPLEX_MIXED_INTEGER_PROGRAMMING:
109 LOG(DFATAL) <<
"Invalid SolverType: " << solver_type;
115 if (
var ==
nullptr)
return 0.0;
121 if (
var ==
nullptr)
return;
123 auto it = coefficients_.find(
var);
131 if (it != coefficients_.end() && it->second != 0.0) {
132 const double old_value = it->second;
138 auto insertion_result = coefficients_.insert(std::make_pair(
var, coeff));
139 const double old_value =
140 insertion_result.second ? 0.0 : insertion_result.first->second;
141 insertion_result.first->second = coeff;
147 coefficients_.clear();
151 const bool change =
lb != lb_ ||
ub != ub_;
161 LOG(DFATAL) <<
"Dual value only available for continuous problems";
170 LOG(DFATAL) <<
"Basis status only available for continuous problems";
180bool MPConstraint::ContainsNewVariables() {
182 for (
const auto& entry : coefficients_) {
183 const int variable_index = entry.first->index();
184 if (variable_index >= last_variable_index ||
196 if (
var ==
nullptr)
return 0.0;
202 if (
var ==
nullptr)
return;
204 auto it = coefficients_.find(
var);
207 if (it == coefficients_.end() || it->second == 0.0)
return;
210 coefficients_[
var] = coeff;
222 for (
auto var_value_pair : linear_expr.
terms()) {
224 <<
"Bad MPVariable* in LinearExpr, did you try adding an integer to an "
225 "MPVariable* directly?";
231 bool is_maximization) {
232 CheckLinearExpr(*interface_->
solver_, linear_expr);
234 coefficients_.clear();
236 for (
const auto& kv : linear_expr.
terms()) {
243 CheckLinearExpr(*interface_->
solver_, linear_expr);
245 for (
const auto& kv : linear_expr.
terms()) {
252 coefficients_.clear();
292 return (integer_ && interface_->
IsMIP()) ? round(solution_value_)
298 return solution_value_;
303 LOG(DFATAL) <<
"Reduced cost only available for continuous problems";
307 return reduced_cost_;
312 LOG(DFATAL) <<
"Basis status only available for continuous problems";
323 const bool change =
lb != lb_ ||
ub != ub_;
341 if (priority == branching_priority_)
return;
342 branching_priority_ = priority;
351 return interface_->SolverVersion();
359 if (num_threads < 1) {
360 return absl::InvalidArgumentError(
"num_threads must be a positive number.");
362 const absl::Status
status = interface_->SetNumThreads(num_threads);
364 num_threads_ = num_threads;
371 solver_specific_parameter_string_ =
parameters;
372 return interface_->SetSolverSpecificParametersAsString(
parameters);
383#if defined(USE_CLP) || defined(USE_CBC)
392#if defined(USE_HIGHS)
404#if defined(USE_CPLEX)
412 DCHECK(solver !=
nullptr);
422#if defined(USE_CLP) || defined(USE_CBC)
436#if defined(USE_HIGHS)
456#if defined(USE_CPLEX)
468 LOG(FATAL) <<
"Linear solver not recognized.";
475int NumDigits(
int n) {
479 return static_cast<int>(std::max(1.0L, log(1.0L * n) / log(10.0L) + 1.0));
481 return static_cast<int>(std::max(1.0, log10(
static_cast<double>(n)) + 1.0));
490 construction_time_(
absl::Now()) {
491 interface_.reset(BuildSolverInterface(
this));
492 if (absl::GetFlag(FLAGS_linear_solver_enable_verbose_output)) {
495 objective_.reset(
new MPObjective(interface_.get()));
558struct NamedOptimizationProblemType {
593 const std::string
id =
594 absl::StrReplaceAll(absl::AsciiStrToUpper(solver_id), {{
"-",
"_"}});
597 MPModelRequest::SolverType solver_type;
598 if (MPModelRequest::SolverType_Parse(
id, &solver_type)) {
604 std::string lower_id = absl::AsciiStrToLower(
id);
607 if (absl::EndsWith(lower_id,
"_mip")) {
608 lower_id = lower_id.substr(0, lower_id.size() - 4);
612 if (lower_id ==
"cp_sat") {
618 if (named_solver.name == lower_id) {
619 *type = named_solver.problem_type;
630 if (named_solver.problem_type == optimization_problem_type) {
631 return named_solver.name;
634 LOG(FATAL) <<
"Unrecognized solver type: "
635 <<
static_cast<int>(optimization_problem_type);
641 std::string* error) {
642 DCHECK(solver_type !=
nullptr);
643 DCHECK(error !=
nullptr);
646 *error = absl::StrCat(
"Solver type: ", text,
" does not exist.");
653 const std::string& solver_id) {
663 LOG(WARNING) <<
"Unrecognized solver type: " << solver_id;
667 LOG(WARNING) <<
"Support for " << solver_id
668 <<
" not linked in, or the license was not found.";
676 if (!variable_name_to_index_) GenerateVariableNameIndex();
678 absl::flat_hash_map<std::string, int>::const_iterator it =
679 variable_name_to_index_->find(var_name);
680 if (it == variable_name_to_index_->end())
return nullptr;
681 return variables_[it->second];
685 const std::string& constraint_name)
const {
686 if (!constraint_name_to_index_) GenerateConstraintNameIndex();
688 const auto it = constraint_name_to_index_->find(constraint_name);
689 if (it == constraint_name_to_index_->end())
return nullptr;
690 return constraints_[it->second];
696 const MPModelProto& input_model, std::string* error_message,
704 return LoadModelFromProtoInternal(
707 clear_names ? DEFAULT_CLEAR_NAMES
708 : INVALID_MODEL_ON_DUPLICATE_NONEMPTY_NAMES,
709 true, error_message);
713 const MPModelProto& input_model, std::string* error_message) {
717 GenerateVariableNameIndex();
718 GenerateConstraintNameIndex();
720 return LoadModelFromProtoInternal(
721 input_model, DIE_ON_DUPLICATE_NONEMPTY_NAMES,
722 true, error_message);
727class MPVariableNamesIterator {
729 explicit MPVariableNamesIterator(
const MPModelProto&
model) : model_(
model) {}
730 int index()
const {
return index_; }
731 absl::string_view
name()
const {
return model_.variable(index_).name(); }
732 static std::string DescribeIndex(
int index) {
733 return absl::StrFormat(
"variable[%d]",
index);
735 void Advance() { ++index_; }
736 bool AtEnd()
const {
return index_ == model_.variable_size(); }
739 const MPModelProto& model_;
744class MPConstraintNamesIterator {
746 explicit MPConstraintNamesIterator(
const MPModelProto&
model)
751 index_(model_.constraint().empty() ? ~0 : 0) {}
752 int index()
const {
return index_; }
753 absl::string_view
name()
const {
754 return index_ >= 0 ? model_.constraint(index_).name()
758 : model_.general_constraint(~index_)
759 .indicator_constraint()
763 static std::string DescribeIndex(
int index) {
765 ? absl::StrFormat(
"constraint[%d]",
index)
767 "general_constraint[%d].indicator_constraint.constraint",
772 if (++index_ == model_.constraint_size()) index_ = ~0;
777 bool AtEnd()
const {
return ~index_ == model_.general_constraint_size(); }
780 const MPModelProto& model_;
787template <
class NameIterator>
788std::string FindDuplicateNamesError(NameIterator name_iterator) {
789 absl::flat_hash_map<absl::string_view, int> name_to_index;
790 for (; !name_iterator.AtEnd(); name_iterator.Advance()) {
791 if (name_iterator.name().empty())
continue;
793 name_to_index.insert({name_iterator.name(), name_iterator.index()})
795 if (
index != name_iterator.index()) {
796 return absl::StrFormat(
797 "Duplicate name '%s' in %s.name() and %s.name()",
798 name_iterator.name(), NameIterator::DescribeIndex(
index),
799 NameIterator::DescribeIndex(name_iterator.index()));
806MPSolverResponseStatus MPSolver::LoadModelFromProtoInternal(
807 const MPModelProto& input_model, ModelProtoNamesPolicy name_policy,
808 bool check_model_validity, std::string* error_message) {
809 CHECK(error_message !=
nullptr);
811 if (check_model_validity) {
817 if (error.empty() && name_policy != DEFAULT_CLEAR_NAMES) {
818 error = FindDuplicateNamesError(MPVariableNamesIterator(input_model));
820 error = FindDuplicateNamesError(MPConstraintNamesIterator(input_model));
822 if (!error.empty() && name_policy == DIE_ON_DUPLICATE_NONEMPTY_NAMES) {
826 const bool clear_names = name_policy == DEFAULT_CLEAR_NAMES;
828 if (!error.empty()) {
829 *error_message = error;
831 <<
"Invalid model given to LoadModelFromProto(): " << error;
832 if (absl::GetFlag(FLAGS_mpsolver_bypass_model_validation)) {
834 <<
"Ignoring the model error(s) because of"
835 <<
" --mpsolver_bypass_model_validation.";
837 return absl::StrContains(error,
"Infeasible") ? MPSOLVER_INFEASIBLE
838 : MPSOLVER_MODEL_INVALID;
842 if (input_model.has_quadratic_objective()) {
844 "Optimizing a quadratic objective is only supported through direct "
845 "proto solves. Please use MPSolver::SolveWithProto, or the solver's "
846 "direct proto solve function.";
847 return MPSOLVER_MODEL_INVALID;
852 const std::string empty;
853 for (
int i = 0;
i < input_model.variable_size(); ++
i) {
854 const MPVariableProto& var_proto = input_model.variable(i);
856 MakeNumVar(var_proto.lower_bound(), var_proto.upper_bound(),
857 clear_names ? empty : var_proto.name());
859 if (var_proto.branching_priority() != 0) {
862 objective->SetCoefficient(
variable, var_proto.objective_coefficient());
865 for (
const MPConstraintProto& ct_proto : input_model.constraint()) {
866 if (ct_proto.lower_bound() == -
infinity() &&
867 ct_proto.upper_bound() ==
infinity()) {
871 MPConstraint*
const ct =
873 clear_names ? empty : ct_proto.name());
874 ct->set_is_lazy(ct_proto.is_lazy());
875 for (
int j = 0; j < ct_proto.var_index_size(); ++j) {
876 ct->SetCoefficient(variables_[ct_proto.var_index(j)],
877 ct_proto.coefficient(j));
881 for (
const MPGeneralConstraintProto& general_constraint :
882 input_model.general_constraint()) {
883 switch (general_constraint.general_constraint_case()) {
884 case MPGeneralConstraintProto::kIndicatorConstraint: {
886 general_constraint.indicator_constraint().constraint();
893 MPConstraint*
const constraint =
new MPConstraint(
895 clear_names ?
"" :
proto.name(), interface_.get());
896 if (constraint_name_to_index_) {
901 constraint_is_extracted_.push_back(
false);
904 for (
int j = 0; j <
proto.var_index_size(); ++j) {
906 proto.coefficient(j));
910 variables_[general_constraint.indicator_constraint().var_index()];
913 general_constraint.indicator_constraint().var_value();
915 if (!interface_->AddIndicatorConstraint(
constraint)) {
916 *error_message =
"Solver doesn't support indicator constraints";
917 return MPSOLVER_MODEL_INVALID;
922 *error_message = absl::StrFormat(
923 "Optimizing general constraints of type %i is only supported "
924 "through direct proto solves. Please use MPSolver::SolveWithProto, "
925 "or the solver's direct proto solve function.",
926 general_constraint.general_constraint_case());
927 return MPSOLVER_MODEL_INVALID;
931 objective->SetOptimizationDirection(input_model.maximize());
932 if (input_model.has_objective_offset()) {
933 objective->SetOffset(input_model.objective_offset());
937 solution_hint_.clear();
938 for (
int i = 0;
i < input_model.solution_hint().var_index_size(); ++
i) {
939 solution_hint_.push_back(
940 std::make_pair(variables_[input_model.solution_hint().var_index(i)],
941 input_model.solution_hint().var_value(i)));
943 return MPSOLVER_MODEL_IS_VALID;
947MPSolverResponseStatus ResultStatusToMPSolverResponseStatus(
951 return MPSOLVER_OPTIMAL;
953 return MPSOLVER_FEASIBLE;
955 return MPSOLVER_INFEASIBLE;
957 return MPSOLVER_UNBOUNDED;
959 return MPSOLVER_ABNORMAL;
961 return MPSOLVER_MODEL_INVALID;
963 return MPSOLVER_NOT_SOLVED;
965 return MPSOLVER_UNKNOWN_STATUS;
970 CHECK(response !=
nullptr);
972 response->set_status(
973 ResultStatusToMPSolverResponseStatus(interface_->result_status_));
974 response->mutable_solve_info()->set_solve_wall_time_seconds(
wall_time() /
978 response->set_objective_value(
Objective().Value());
983 if (interface_->IsMIP()) {
984 response->set_best_objective_bound(interface_->best_objective_bound());
999bool InCategory(
int status,
int category) {
1000 if (category == MPSOLVER_OPTIMAL)
return status == MPSOLVER_OPTIMAL;
1002 return status == category;
1005void AppendStatusStr(absl::string_view msg, MPSolutionResponse* response) {
1006 response->set_status_str(
1007 absl::StrCat(response->status_str(),
1008 (response->status_str().empty() ?
"" :
"\n"), msg));
1015 MPSolutionResponse* response,
1016 std::atomic<bool>* interrupt) {
1022 MPSolutionResponse* response,
1023 std::atomic<bool>* interrupt) {
1024 CHECK(response !=
nullptr);
1026 if (interrupt !=
nullptr &&
1028 response->set_status(MPSOLVER_INCOMPATIBLE_OPTIONS);
1029 response->set_status_str(
1030 "Called MPSolver::SolveWithProto with an underlying solver that "
1031 "doesn't support interruption.");
1036 request->model().name(),
1038 if (request->enable_internal_solver_output()) {
1040 std::cout <<
"MPModelRequest info:\n"
1046 if (solver.interface_->SupportsDirectlySolveProto(interrupt)) {
1048 solver.interface_->DirectlySolveProto(std::move(request), interrupt);
1053 const std::optional<LazyMutableCopy<MPModelProto>> optional_model =
1055 if (!optional_model)
return;
1057 std::string error_message;
1058 response->set_status(solver.LoadModelFromProtoInternal(
1059 **optional_model, DEFAULT_CLEAR_NAMES,
1060 false, &error_message));
1063 if (response->status() != MPSOLVER_MODEL_IS_VALID) {
1064 response->set_status_str(error_message);
1065 LOG_IF(WARNING, request->enable_internal_solver_output())
1066 <<
"LoadModelFromProtoInternal() failed even though the model was "
1067 <<
"valid! Status: "
1069 << response->status() <<
"); Error: " << error_message;
1072 if (request->has_solver_time_limit_seconds()) {
1073 solver.
SetTimeLimit(absl::Seconds(request->solver_time_limit_seconds()));
1075 std::string warning_message;
1076 if (request->has_solver_specific_parameters()) {
1078 request->solver_specific_parameters())) {
1079 if (request->ignore_solver_specific_parameters_failure()) {
1082 "Warning: the solver specific parameters were not successfully "
1085 response->set_status(MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS);
1091 if (interrupt ==
nullptr) {
1097 const absl::Time start_time = absl::Now();
1098 absl::Time interrupt_time;
1099 bool interrupted_by_user =
false;
1101 absl::Notification solve_finished;
1102 auto polling_func = [&interrupt, &solve_finished, &solver,
1103 &interrupted_by_user, &interrupt_time, &request]() {
1104 constexpr absl::Duration kPollDelay = absl::Microseconds(100);
1105 constexpr absl::Duration kMaxInterruptionDelay = absl::Seconds(10);
1107 while (!interrupt->load()) {
1108 if (solve_finished.HasBeenNotified())
return;
1109 absl::SleepFor(kPollDelay);
1114 solver.InterruptSolve();
1115 interrupt_time = absl::Now();
1116 interrupted_by_user =
true;
1131 for (absl::Duration poll_delay = kPollDelay;
1132 absl::Now() <= interrupt_time + kMaxInterruptionDelay;
1134 if (solve_finished.WaitForNotificationWithTimeout(poll_delay)) {
1137 solver.InterruptSolve();
1142 <<
"MPSolver::InterruptSolve() seems to be ignored by the "
1143 "underlying solver, despite repeated calls over at least "
1144 << absl::FormatDuration(kMaxInterruptionDelay)
1145 <<
". Solver type used: "
1146 << MPModelRequest_SolverType_Name(request->solver_type());
1159 thread_pool.
Schedule(polling_func);
1163 if (!interrupt->load()) {
1165 solver.FillSolutionResponseProto(response);
1167 response->set_status(MPSOLVER_CANCELLED_BY_USER);
1168 response->set_status_str(
1169 "Solve not started, because the user set the atomic<bool> in "
1170 "MPSolver::SolveWithProto() to true before solving could "
1173 solve_finished.Notify();
1178 if (interrupted_by_user) {
1181 if (InCategory(response->status(), MPSOLVER_NOT_SOLVED)) {
1182 response->set_status(MPSOLVER_CANCELLED_BY_USER);
1186 "User interrupted MPSolver::SolveWithProto() by setting the "
1187 "atomic<bool> to true at %s (%s after solving started.)",
1188 absl::FormatTime(interrupt_time),
1189 absl::FormatDuration(interrupt_time - start_time)),
1194 if (!warning_message.empty()) {
1195 AppendStatusStr(warning_message, response);
1200 DCHECK(output_model !=
nullptr);
1201 output_model->Clear();
1203 output_model->set_name(
Name());
1206 MPVariableProto*
const variable_proto = output_model->add_variable();
1209 variable_proto->set_name(
var->name());
1210 variable_proto->set_lower_bound(
var->lb());
1211 variable_proto->set_upper_bound(
var->ub());
1212 variable_proto->set_is_integer(
var->integer());
1213 if (objective_->GetCoefficient(
var) != 0.0) {
1214 variable_proto->set_objective_coefficient(
1215 objective_->GetCoefficient(
var));
1217 if (
var->branching_priority() != 0) {
1218 variable_proto->set_branching_priority(
var->branching_priority());
1228 absl::flat_hash_map<const MPVariable*, int> var_to_index;
1229 for (
int j = 0; j < static_cast<int>(variables_.size()); ++j) {
1230 var_to_index[variables_[j]] = j;
1235 MPConstraintProto* constraint_proto;
1237 MPGeneralConstraintProto*
const general_constraint_proto =
1238 output_model->add_general_constraint();
1240 MPIndicatorConstraint*
const indicator_constraint_proto =
1241 general_constraint_proto->mutable_indicator_constraint();
1242 indicator_constraint_proto->set_var_index(
1245 constraint_proto = indicator_constraint_proto->mutable_constraint();
1247 constraint_proto = output_model->add_constraint();
1255 std::vector<std::pair<int, double>> linear_term;
1256 for (
const auto& entry :
constraint->coefficients_) {
1260 const double coeff = entry.second;
1261 linear_term.push_back(std::pair<int, double>(
var_index, coeff));
1265 std::sort(linear_term.begin(), linear_term.end());
1267 for (
const std::pair<int, double>& var_and_coeff : linear_term) {
1268 constraint_proto->add_var_index(var_and_coeff.first);
1269 constraint_proto->add_coefficient(var_and_coeff.second);
1273 output_model->set_maximize(
Objective().maximization());
1274 output_model->set_objective_offset(
Objective().offset());
1276 if (!solution_hint_.empty()) {
1277 PartialVariableAssignment*
const hint =
1278 output_model->mutable_solution_hint();
1279 for (
const auto& var_value_pair : solution_hint_) {
1280 hint->add_var_index(var_value_pair.first->index());
1281 hint->add_var_value(var_value_pair.second);
1288 interface_->result_status_ =
static_cast<ResultStatus>(response.status());
1289 if (response.status() != MPSOLVER_OPTIMAL &&
1290 response.status() != MPSOLVER_FEASIBLE) {
1291 return absl::InvalidArgumentError(absl::StrCat(
1292 "Cannot load a solution unless its status is OPTIMAL or FEASIBLE"
1299 if (
static_cast<size_t>(response.variable_value_size()) !=
1300 variables_.size()) {
1301 return absl::InvalidArgumentError(absl::StrCat(
1302 "Trying to load a solution whose number of variables (",
1303 response.variable_value_size(),
1304 ") does not correspond to the Solver's (", variables_.size(),
")"));
1306 interface_->ExtractModel();
1310 double largest_error = 0;
1311 int num_vars_out_of_bounds = 0;
1312 int last_offending_var = -1;
1313 for (
int i = 0; i < response.variable_value_size(); ++i) {
1314 const double var_value = response.variable_value(i);
1317 const double lb_error =
var->lb() - var_value;
1318 const double ub_error = var_value -
var->ub();
1319 if (lb_error > tolerance || ub_error > tolerance) {
1320 ++num_vars_out_of_bounds;
1321 largest_error = std::max(largest_error, std::max(lb_error, ub_error));
1322 last_offending_var = i;
1325 if (num_vars_out_of_bounds > 0) {
1326 return absl::InvalidArgumentError(absl::StrCat(
1327 "Loaded a solution whose variables matched the solver's, but ",
1328 num_vars_out_of_bounds,
" of ", variables_.size(),
1329 " variables were out of their bounds, by more than the primal"
1330 " tolerance which is: ",
1331 tolerance,
". Max error: ", largest_error,
", last offender var is #",
1332 last_offending_var,
": '", variables_[last_offending_var]->name(),
1336 for (
int i = 0; i < response.variable_value_size(); ++i) {
1337 variables_[i]->set_solution_value(response.variable_value(i));
1339 if (response.dual_value_size() > 0) {
1340 if (
static_cast<size_t>(response.dual_value_size()) !=
1341 constraints_.size()) {
1342 return absl::InvalidArgumentError(absl::StrCat(
1343 "Trying to load a dual solution whose number of entries (",
1344 response.dual_value_size(),
") does not correspond to the Solver's (",
1345 constraints_.size(),
")"));
1347 for (
int i = 0; i < response.dual_value_size(); ++i) {
1348 constraints_[i]->set_dual_value(response.dual_value(i));
1351 if (response.reduced_cost_size() > 0) {
1352 if (
static_cast<size_t>(response.reduced_cost_size()) !=
1353 variables_.size()) {
1354 return absl::InvalidArgumentError(absl::StrCat(
1355 "Trying to load a reduced cost solution whose number of entries (",
1356 response.reduced_cost_size(),
1357 ") does not correspond to the Solver's (", variables_.size(),
")"));
1359 for (
int i = 0; i < response.reduced_cost_size(); ++i) {
1360 variables_[i]->set_reduced_cost(response.reduced_cost(i));
1365 if (response.has_objective_value()) {
1366 interface_->objective_value_ = response.objective_value();
1368 if (response.has_best_objective_bound()) {
1369 interface_->best_objective_bound_ = response.best_objective_bound();
1374 return absl::OkStatus();
1379 absl::MutexLock lock(&global_count_mutex_);
1380 global_num_variables_ += variables_.size();
1381 global_num_constraints_ += constraints_.size();
1386 if (variable_name_to_index_) {
1387 variable_name_to_index_->clear();
1389 variable_is_extracted_.clear();
1390 if (constraint_name_to_index_) {
1391 constraint_name_to_index_->clear();
1393 constraint_is_extracted_.clear();
1394 interface_->Reset();
1395 solution_hint_.clear();
1403 const std::vector<BasisStatus>& variable_statuses,
1404 const std::vector<BasisStatus>& constraint_statuses) {
1405 interface_->SetStartingLpBasis(variable_statuses, constraint_statuses);
1411 const std::string&
name) {
1415 if (variable_name_to_index_) {
1418 variables_.push_back(v);
1419 variable_is_extracted_.push_back(
false);
1420 interface_->AddVariable(v);
1425 const std::string&
name) {
1430 const std::string&
name) {
1439 const std::string&
name,
1440 std::vector<MPVariable*>* vars) {
1442 if (nb <= 0)
return;
1443 const int num_digits = NumDigits(nb);
1444 for (
int i = 0; i < nb; ++i) {
1449 absl::StrFormat(
"%s%0*d",
name.c_str(), num_digits, i);
1450 vars->push_back(
MakeVar(lb, ub, integer, vname));
1456 const std::string&
name,
1457 std::vector<MPVariable*>* vars) {
1462 const std::string&
name,
1463 std::vector<MPVariable*>* vars) {
1468 std::vector<MPVariable*>* vars) {
1481 const std::string&
name) {
1485 if (constraint_name_to_index_) {
1490 constraint_is_extracted_.push_back(
false);
1504 const std::string&
name) {
1505 CheckLinearExpr(*
this,
range.linear_expr());
1508 for (
const auto& kv :
range.linear_expr().terms()) {
1514int MPSolver::ComputeMaxConstraintSize(
int min_constraint_index,
1515 int max_constraint_index)
const {
1516 int max_constraint_size = 0;
1517 DCHECK_GE(min_constraint_index, 0);
1518 DCHECK_LE(max_constraint_index, constraints_.size());
1519 for (
int i = min_constraint_index; i < max_constraint_index; ++i) {
1521 if (
static_cast<int>(
ct->coefficients_.size()) > max_constraint_size) {
1522 max_constraint_size =
ct->coefficients_.size();
1525 return max_constraint_size;
1528bool MPSolver::HasInfeasibleConstraints()
const {
1529 bool hasInfeasibleConstraints =
false;
1530 for (
int i = 0; i < static_cast<int>(constraints_.size()); ++
i) {
1531 if (constraints_[i]->lb() > constraints_[i]->ub()) {
1532 LOG(WARNING) <<
"Constraint " << constraints_[
i]->name() <<
" (" <<
i
1533 <<
") has contradictory bounds:" <<
" lower bound = "
1534 << constraints_[
i]->lb()
1535 <<
" upper bound = " << constraints_[
i]->ub();
1536 hasInfeasibleConstraints =
true;
1539 return hasInfeasibleConstraints;
1542bool MPSolver::HasIntegerVariables()
const {
1543 for (
const MPVariable*
const variable : variables_) {
1551 return Solve(default_param);
1560 if (HasInfeasibleConstraints()) {
1562 return interface_->result_status_;
1566 if (absl::GetFlag(FLAGS_verify_solution)) {
1568 VLOG(1) <<
"--verify_solution enabled, but the solver did not find a"
1569 <<
" solution: skipping the verification.";
1572 absl::GetFlag(FLAGS_log_verification_errors))) {
1574 interface_->result_status_ =
status;
1577 DCHECK_EQ(interface_->result_status_,
status);
1582 interface_->Write(file_name);
1587 const std::string prefix =
"Variable '" +
var.
name() +
"': domain = ";
1590 return prefix +
"∅";
1594 if (
var.integer() &&
var.ub() -
var.lb() <= 1) {
1595 const int64_t lb =
static_cast<int64_t
>(ceil(
var.lb()));
1596 const int64_t ub =
static_cast<int64_t
>(floor(
var.ub()));
1598 return prefix +
"∅";
1599 }
else if (lb == ub) {
1600 return absl::StrFormat(
"%s{ %d }", prefix.c_str(), lb);
1602 return absl::StrFormat(
"%s{ %d, %d }", prefix.c_str(), lb, ub);
1606 if (
var.lb() ==
var.ub()) {
1607 return absl::StrFormat(
"%s{ %f }", prefix.c_str(),
var.lb());
1609 return prefix + (
var.integer() ?
"Integer" :
"Real") +
" in " +
1611 ? std::string(
"]-∞")
1612 : absl::StrFormat(
"[%f",
var.lb())) +
1615 : absl::StrFormat(
"%f]",
var.ub()));
1618std::string PrettyPrintConstraint(
const MPConstraint& constraint) {
1619 std::string prefix =
"Constraint '" + constraint.name() +
"': ";
1622 constraint.lb() > constraint.ub()) {
1623 return prefix +
"ALWAYS FALSE";
1627 return prefix +
"ALWAYS TRUE";
1629 prefix +=
"<linear expr>";
1631 if (constraint.lb() == constraint.ub()) {
1632 return absl::StrFormat(
"%s = %f", prefix.c_str(), constraint.lb());
1636 return absl::StrFormat(
"%s ≤ %f", prefix.c_str(), constraint.ub());
1639 return absl::StrFormat(
"%s ≥ %f", prefix.c_str(), constraint.lb());
1641 return absl::StrFormat(
"%s ∈ [%f, %f]", prefix.c_str(), constraint.lb(),
1647 interface_->ExtractModel();
1650 if (std::isnan(
value)) {
1651 return absl::InvalidArgumentError(
1652 absl::StrCat(
"NaN value for ", PrettyPrintVar(*
variable)));
1661 return absl::OkStatus();
1666 if (!interface_->CheckSolutionIsSynchronizedAndExists())
return {};
1667 std::vector<double> activities(constraints_.size(), 0.0);
1668 for (
int i = 0; i < static_cast<int>(constraints_.size()); ++i) {
1671 for (
const auto& entry :
constraint.coefficients_) {
1672 sum.
Add(entry.first->solution_value() * entry.second);
1674 activities[i] = sum.
Value();
1681 double max_observed_error = 0;
1682 if (tolerance < 0) tolerance =
infinity();
1690 if (std::isnan(
value)) {
1693 LOG_IF(ERROR, log_errors) <<
"NaN value for " << PrettyPrintVar(
var);
1698 if (
value <
var.lb() - tolerance) {
1700 max_observed_error = std::max(max_observed_error,
var.lb() -
value);
1701 LOG_IF(ERROR, log_errors)
1702 <<
"Value " <<
value <<
" too low for " << PrettyPrintVar(
var);
1707 if (
value >
var.ub() + tolerance) {
1709 max_observed_error = std::max(max_observed_error,
value -
var.ub());
1710 LOG_IF(ERROR, log_errors)
1711 <<
"Value " <<
value <<
" too high for " << PrettyPrintVar(
var);
1716 if (fabs(
value - round(
value)) > tolerance) {
1718 max_observed_error =
1719 std::max(max_observed_error, fabs(
value - round(
value)));
1720 LOG_IF(ERROR, log_errors)
1721 <<
"Non-integer value " <<
value <<
" for " << PrettyPrintVar(
var);
1725 if (!
IsMIP() && HasIntegerVariables()) {
1726 LOG_IF(INFO, log_errors) <<
"Skipped variable integrality check, because "
1727 <<
"a continuous relaxation of the model was "
1728 <<
"solved (i.e., the selected solver does not "
1729 <<
"support integer variables).";
1734 for (
int i = 0; i < static_cast<int>(constraints_.size()); ++i) {
1736 const double activity = activities[i];
1738 double inaccurate_activity = 0.0;
1739 for (
const auto& entry :
constraint.coefficients_) {
1740 inaccurate_activity += entry.first->solution_value() * entry.second;
1743 if (std::isnan(activity) || std::isnan(inaccurate_activity)) {
1746 LOG_IF(ERROR, log_errors)
1747 <<
"NaN value for " << PrettyPrintConstraint(
constraint);
1757 max_observed_error =
1758 std::max(max_observed_error,
constraint.
lb() - activity);
1759 LOG_IF(ERROR, log_errors)
1760 <<
"Activity " << activity <<
" too low for "
1762 }
else if (inaccurate_activity <
constraint.
lb() - tolerance) {
1763 LOG_IF(WARNING, log_errors)
1764 <<
"Activity " << activity <<
", computed with the (inaccurate)"
1765 <<
" standard sum of its terms, is too low for "
1772 max_observed_error =
1773 std::max(max_observed_error, activity -
constraint.
ub());
1774 LOG_IF(ERROR, log_errors)
1775 <<
"Activity " << activity <<
" too high for "
1777 }
else if (inaccurate_activity >
constraint.
ub() + tolerance) {
1778 LOG_IF(WARNING, log_errors)
1779 <<
"Activity " << activity <<
", computed with the (inaccurate)"
1780 <<
" standard sum of its terms, is too high for "
1791 double inaccurate_objective_value = objective.
offset();
1792 for (
const auto& entry : objective.coefficients_) {
1793 const double term = entry.first->solution_value() * entry.second;
1794 objective_sum.
Add(term);
1795 inaccurate_objective_value += term;
1797 const double actual_objective_value = objective_sum.
Value();
1799 objective.
Value(), actual_objective_value, tolerance, tolerance)) {
1801 max_observed_error = std::max(
1802 max_observed_error, fabs(actual_objective_value - objective.
Value()));
1803 LOG_IF(ERROR, log_errors)
1804 <<
"Objective value " << objective.
Value() <<
" isn't accurate"
1805 <<
", it should be " << actual_objective_value
1806 <<
" (delta=" << actual_objective_value - objective.
Value() <<
").";
1808 inaccurate_objective_value,
1809 tolerance, tolerance)) {
1810 LOG_IF(WARNING, log_errors)
1811 <<
"Objective value " << objective.
Value() <<
" doesn't correspond"
1812 <<
" to the value computed with the standard (and therefore inaccurate)"
1813 <<
" sum of its terms.";
1815 if (num_errors > 0) {
1816 LOG_IF(ERROR, log_errors)
1817 <<
"There were " << num_errors <<
" errors above the tolerance ("
1818 << tolerance <<
"), the largest was " << max_observed_error;
1835 return interface_->ComputeExactConditionNumber();
1839 if (
var ==
nullptr)
return false;
1840 if (
var->index() >= 0 &&
var->index() <
static_cast<int>(variables_.size())) {
1842 return variables_[
var->index()] ==
var;
1848 std::string* model_str)
const {
1853 const auto status_or =
1855 *model_str = status_or.value_or(
"");
1856 return status_or.ok();
1860 std::string* model_str)
const {
1865 const auto status_or =
1867 *model_str = status_or.value_or(
"");
1868 return status_or.ok();
1872 for (
const auto& var_value_pair :
hint) {
1874 <<
"hint variable does not belong to this solver";
1876 solution_hint_ = std::move(
hint);
1879void MPSolver::GenerateVariableNameIndex()
const {
1880 if (variable_name_to_index_)
return;
1881 variable_name_to_index_ = absl::flat_hash_map<std::string, int>();
1887void MPSolver::GenerateConstraintNameIndex()
const {
1888 if (constraint_name_to_index_)
return;
1889 constraint_name_to_index_ = absl::flat_hash_map<std::string, int>();
1890 for (
const MPConstraint*
const cst : constraints_) {
1898 interface_->SetCallback(mp_callback);
1902 return interface_->SupportsCallbacks();
1906absl::Mutex MPSolver::global_count_mutex_(absl::kConstInit);
1907int64_t MPSolver::global_num_variables_ = 0;
1908int64_t MPSolver::global_num_constraints_ = 0;
1912 absl::MutexLock lock(&global_count_mutex_);
1913 return global_num_variables_;
1918 absl::MutexLock lock(&global_count_mutex_);
1919 return global_num_constraints_;
1925 case MPSOLVER_OPTIMAL:
1926 case MPSOLVER_FEASIBLE:
1927 case MPSOLVER_INFEASIBLE:
1928 case MPSOLVER_NOT_SOLVED:
1929 case MPSOLVER_UNBOUNDED:
1930 case MPSOLVER_ABNORMAL:
1931 case MPSOLVER_UNKNOWN_STATUS:
1935 case MPSOLVER_MODEL_IS_VALID:
1936 case MPSOLVER_CANCELLED_BY_USER:
1939 case MPSOLVER_MODEL_INVALID:
1940 case MPSOLVER_MODEL_INVALID_SOLUTION_HINT:
1941 case MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS:
1942 case MPSOLVER_SOLVER_TYPE_UNAVAILABLE:
1943 case MPSOLVER_INCOMPATIBLE_OPTIONS:
1947 <<
"MPSolverResponseStatusIsRpcError() called with invalid status "
1948 <<
"(value: " <<
status <<
")";
1960 sync_status_(MODEL_SYNCHRONIZED),
1963 last_constraint_index_(0),
1964 last_variable_index_(0),
1965 objective_value_(0.0),
1966 best_objective_bound_(0.0),
1972 LOG(WARNING) <<
"Writing model not implemented in this solver interface.";
2007 solver_->variable_is_extracted_.assign(
solver_->variables_.size(),
false);
2008 solver_->constraint_is_extracted_.assign(
solver_->constraints_.size(),
false);
2014 <<
"The model has been changed since the solution was last computed."
2015 <<
" MPSolverInterface::sync_status_ = " <<
sync_status_;
2026 LOG(DFATAL) <<
"No solution exists. MPSolverInterface::result_status_ = "
2039 const double trivial_worst_bound =
2040 maximize_ ? -std::numeric_limits<double>::infinity()
2041 : std::numeric_limits<double>::infinity();
2043 VLOG(1) <<
"Best objective bound only available for discrete problems.";
2044 return trivial_worst_bound;
2047 return trivial_worst_bound;
2050 if (
solver_->variables_.empty() &&
solver_->constraints_.empty()) {
2064 LOG(DFATAL) <<
"ComputeExactConditionNumber not implemented for "
2066 static_cast<MPModelRequest::SolverType
>(
2101 LOG(WARNING) <<
"Trying to set an unsupported parameter: " << param <<
".";
2105 LOG(WARNING) <<
"Trying to set an unsupported parameter: " << param <<
".";
2109 LOG(WARNING) <<
"Trying to set a supported parameter: " << param
2110 <<
" to an unsupported value: " <<
value;
2114 LOG(WARNING) <<
"Trying to set a supported parameter: " << param
2115 <<
" to an unsupported value: " <<
value;
2119 return absl::UnimplementedError(
2120 absl::StrFormat(
"SetNumThreads() not supported by %s.",
SolverVersion()));
2129 LOG(WARNING) <<
"SetSolverSpecificParametersAsString() not supported by "
2154 : relative_mip_gap_value_(kDefaultRelativeMipGap),
2156 dual_tolerance_value_(kDefaultDualTolerance),
2157 presolve_value_(kDefaultPresolve),
2158 scaling_value_(kDefaultIntegerParamValue),
2159 lp_algorithm_value_(kDefaultIntegerParamValue),
2160 incrementality_value_(kDefaultIncrementality),
2161 lp_algorithm_is_default_(true) {}
2167 relative_mip_gap_value_ =
value;
2171 primal_tolerance_value_ =
value;
2175 dual_tolerance_value_ =
value;
2179 LOG(ERROR) <<
"Trying to set an unknown parameter: " << param <<
".";
2189 LOG(ERROR) <<
"Trying to set a supported parameter: " << param
2190 <<
" to an unknown value: " <<
value;
2192 presolve_value_ =
value;
2197 LOG(ERROR) <<
"Trying to set a supported parameter: " << param
2198 <<
" to an unknown value: " <<
value;
2200 scaling_value_ =
value;
2205 LOG(ERROR) <<
"Trying to set a supported parameter: " << param
2206 <<
" to an unknown value: " <<
value;
2208 lp_algorithm_value_ =
value;
2209 lp_algorithm_is_default_ =
false;
2214 LOG(ERROR) <<
"Trying to set a supported parameter: " << param
2215 <<
" to an unknown value: " <<
value;
2217 incrementality_value_ =
value;
2221 LOG(ERROR) <<
"Trying to set an unknown parameter: " << param <<
".";
2242 LOG(ERROR) <<
"Trying to reset an unknown parameter: " << param <<
".";
2259 lp_algorithm_is_default_ =
true;
2267 LOG(ERROR) <<
"Trying to reset an unknown parameter: " << param <<
".";
2286 return relative_mip_gap_value_;
2289 return primal_tolerance_value_;
2292 return dual_tolerance_value_;
2295 LOG(ERROR) <<
"Trying to get an unknown parameter: " << param <<
".";
2305 return presolve_value_;
2309 return lp_algorithm_value_;
2312 return incrementality_value_;
2315 return scaling_value_;
2318 LOG(ERROR) <<
"Trying to get an unknown parameter: " << param <<
".";
2326 const MPModelRequest& request) {
2328#if !defined(__PORTABLE_PLATFORM__)
2329 MPModelRequest abbreviated_request;
2330 abbreviated_request = request;
2331 abbreviated_request.clear_model();
2332 abbreviated_request.clear_model_delta();
2333 google::protobuf::TextFormat::PrintToString(abbreviated_request, &out);
2335 out =
"<Info unavailable because: __PORTABLE_PLATFORM__>\n";
2337 if (request.model().has_name()) {
2338 absl::StrAppendFormat(&out,
"model_name: '%s'\n", request.model().name());
void Add(const FpNumber &value)
Adds an FpNumber to the sum.
FpNumber Value() const
Gets the value of the sum.
const absl::flat_hash_map< const MPVariable *, double > & terms() const
bool is_lazy() const
Advanced usage: returns true if the constraint is "lazy" (see below).
void Clear()
Clears all variables and coefficients. Does not clear the bounds.
void SetCoefficient(const MPVariable *var, double coeff)
double lb() const
Returns the lower bound.
double ub() const
Returns the upper bound.
MPSolver::BasisStatus basis_status() const
const MPVariable * indicator_variable() const
void set_is_lazy(bool laziness)
double GetCoefficient(const MPVariable *var) const
const std::string & name() const
Returns the name of the constraint.
void SetBounds(double lb, double ub)
Sets both the lower and upper bounds.
bool indicator_value() const
double dual_value() const
A class to express a linear objective.
void SetCoefficient(const MPVariable *var, double coeff)
double GetCoefficient(const MPVariable *var) const
--— MPObjective --—
void SetOffset(double value)
Sets the constant term in the objective.
bool minimization() const
Is the optimization direction set to minimize?
bool maximization() const
Is the optimization direction set to maximize?
double offset() const
Gets the constant term in the objective.
void AddLinearExpr(const LinearExpr &linear_expr)
Adds linear_expr to the current objective, does not change the direction.
void OptimizeLinearExpr(const LinearExpr &linear_expr, bool is_maximization)
void SetOptimizationDirection(bool maximize)
Sets the optimization direction (maximize: true or minimize: false).
void SetMinimization()
Sets the optimization direction to minimize.
double best_objective_bound() const
bool CheckSolutionIsSynchronized() const
virtual void SetPrimalTolerance(double value)=0
virtual void ExtractNewConstraints()=0
Extracts the constraints that have not been extracted yet.
virtual void ClearObjective()=0
Clears the objective from all its terms.
virtual void ExtractObjective()=0
Extracts the objective.
void InvalidateSolutionSynchronization()
virtual void SetRelativeMipGap(double value)=0
Sets each parameter in the underlying solver.
virtual void SetObjectiveCoefficient(const MPVariable *variable, double coefficient)=0
Changes a coefficient in the linear objective.
virtual bool IsMIP() const =0
Returns true if the problem is discrete and linear.
void ResetExtractionInformation()
Resets the extraction information.
int last_variable_index_
Index in MPSolver::constraints_ of last variable extracted.
virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)
Sets a supported integer parameter to an unsupported value.
int last_variable_index() const
Returns the index of the last variable extracted.
virtual void SetPresolveMode(int value)=0
int last_constraint_index_
Index in MPSolver::variables_ of last constraint extracted.
virtual MPSolver::BasisStatus column_status(int variable_index) const =0
Returns the basis status of a constraint.
virtual void SetCoefficient(MPConstraint *constraint, const MPVariable *variable, double new_value, double old_value)=0
Changes a coefficient in a constraint.
virtual std::string SolverVersion() const =0
Returns a string describing the underlying solver and its version.
virtual void SetVariableInteger(int index, bool integer)=0
Modifies integrality of an extracted variable.
virtual absl::Status SetNumThreads(int num_threads)
Sets the number of threads to be used by the solver.
virtual void BranchingPriorityChangedForVariable(int)
virtual bool SetSolverSpecificParametersAsString(const std::string ¶meters)
static const int kDummyVariableIndex
-------— MPSolverInterface -------—
virtual void SetObjectiveOffset(double value)=0
Changes the constant term in the linear objective.
virtual bool CheckSolutionExists() const
virtual bool IsContinuous() const =0
virtual void SetLpAlgorithm(int value)=0
virtual void SetUnsupportedIntegerParam(MPSolverParameters::IntegerParam param)
Sets an unsupported integer parameter.
bool variable_is_extracted(int var_index) const
void SetUnsupportedDoubleParam(MPSolverParameters::DoubleParam param)
Sets an unsupported double parameter.
bool constraint_is_extracted(int ct_index) const
virtual void ExtractNewVariables()=0
Extracts the variables that have not been extracted yet.
double objective_value() const
Returns the objective value of the best solution found so far.
void ExtractModel()
Extracts model stored in MPSolver.
double objective_value_
The value of the objective function.
double best_objective_bound_
The value of the best objective bound. Used only for MIP solvers.
void SetDoubleParamToUnsupportedValue(MPSolverParameters::DoubleParam param, double value)
Sets a supported double parameter to an unsupported value.
bool maximize_
Optimization direction.
virtual double ComputeExactConditionNumber() const
virtual MPSolver::BasisStatus row_status(int constraint_index) const =0
Returns the basis status of a row.
virtual void Write(const std::string &filename)
void SetMIPParameters(const MPSolverParameters ¶m)
Sets MIP specific parameters in the underlying solver.
virtual ~MPSolverInterface()
virtual void SetDualTolerance(double value)=0
MPSolverInterface(MPSolver *solver)
virtual void SetOptimizationDirection(bool maximize)=0
Sets the optimization direction (min/max).
void SetCommonParameters(const MPSolverParameters ¶m)
Sets parameters common to LP and MIP in the underlying solver.
virtual void ClearConstraint(MPConstraint *constraint)=0
Clears a constraint from all its terms.
bool CheckSolutionIsSynchronizedAndExists() const
Handy shortcut to do both checks above (it is often used).
MPSolver::ResultStatus result_status_
virtual void SetConstraintBounds(int index, double lb, double ub)=0
Modify bounds of an extracted variable.
SynchronizationStatus sync_status_
Indicates whether the model and the solution are synchronized.
virtual void SetVariableBounds(int index, double lb, double ub)=0
Modifies bounds of an extracted variable.
static const PresolveValues kDefaultPresolve
void ResetIntegerParam(MPSolverParameters::IntegerParam param)
static const double kDefaultDualTolerance
static const double kDefaultPrimalTolerance
For the primal and dual tolerances, choose the same default as CLP and GLPK.
DoubleParam
Enumeration of parameters that take continuous values.
@ DUAL_TOLERANCE
Advanced usage: tolerance for dual feasibility of basic solutions.
@ RELATIVE_MIP_GAP
Limit for relative MIP gap.
double GetDoubleParam(MPSolverParameters::DoubleParam param) const
Returns the value of a double parameter.
PresolveValues
For each categorical parameter, enumeration of possible values.
@ PRESOLVE_OFF
Presolve is off.
@ PRESOLVE_ON
Presolve is on.
static const double kUnknownDoubleParamValue
Placeholder value to indicate that a parameter is unknown.
static const int kUnknownIntegerParamValue
void ResetDoubleParam(MPSolverParameters::DoubleParam param)
static const int kDefaultIntegerParamValue
@ BARRIER
Barrier algorithm.
void SetDoubleParam(MPSolverParameters::DoubleParam param, double value)
Sets a double parameter to a specific value.
IntegerParam
Enumeration of parameters that take integer or categorical values.
@ INCREMENTALITY
Advanced usage: incrementality from one solve to the next.
@ PRESOLVE
Advanced usage: presolve mode.
@ LP_ALGORITHM
Algorithm to solve linear programs.
@ SCALING
Advanced usage: enable or disable matrix scaling.
MPSolverParameters()
The constructor sets all parameters to their default value.
static const IncrementalityValues kDefaultIncrementality
IncrementalityValues
Advanced usage: Incrementality options.
@ INCREMENTALITY_OFF
Start solve from scratch.
static const double kDefaultRelativeMipGap
-------— MPSolverParameters -------—
void Reset()
Sets all parameters to their default value.
@ SCALING_ON
Scaling is on.
@ SCALING_OFF
Scaling is off.
int GetIntegerParam(MPSolverParameters::IntegerParam param) const
Returns the value of an integer parameter.
static const double kDefaultDoubleParamValue
void SetIntegerParam(MPSolverParameters::IntegerParam param, int value)
Sets a integer parameter to a specific value.
static void SolveLazyMutableRequest(LazyMutableCopy< MPModelRequest > request, MPSolutionResponse *response, std::atomic< bool > *interrupt=nullptr)
static
int64_t iterations() const
Returns the number of simplex iterations.
int NumVariables() const
Returns the number of variables.
static MPSolver * CreateSolver(const std::string &solver_id)
void SetStartingLpBasis(const std::vector< MPSolver::BasisStatus > &variable_statuses, const std::vector< MPSolver::BasisStatus > &constraint_statuses)
MPVariable * MakeVar(double lb, double ub, bool integer, const std::string &name)
MPVariable * MakeIntVar(double lb, double ub, const std::string &name)
Creates an integer variable.
int NumConstraints() const
Returns the number of constraints.
@ MODEL_INVALID
the model is trivially invalid (NaN coefficients, etc).
@ FEASIBLE
feasible, or stopped by limit.
@ NOT_SOLVED
not been solved yet.
@ INFEASIBLE
proven infeasible.
@ UNBOUNDED
proven unbounded.
@ ABNORMAL
abnormal, i.e., error of some kind.
ResultStatus Solve()
Solves the problem using the default parameter values.
MPSolverResponseStatus LoadModelFromProto(const MPModelProto &input_model, std::string *error_message, bool clear_names=true)
--— Methods using protocol buffers --—
bool IsMIP() const
--— Interface shortcuts --—
bool VerifySolution(double tolerance, bool log_errors) const
void MakeVarArray(int nb, double lb, double ub, bool integer, const std::string &name_prefix, std::vector< MPVariable * > *vars)
bool ExportModelAsLpFormat(bool obfuscate, std::string *model_str) const
const MPObjective & Objective() const
void SetCallback(MPCallback *mp_callback)
@ GLPK_MIXED_INTEGER_PROGRAMMING
@ GLOP_LINEAR_PROGRAMMING
@ SCIP_MIXED_INTEGER_PROGRAMMING
Recommended default value for MIP problems.
@ CPLEX_LINEAR_PROGRAMMING
@ SAT_INTEGER_PROGRAMMING
@ XPRESS_MIXED_INTEGER_PROGRAMMING
@ CBC_MIXED_INTEGER_PROGRAMMING
@ HIGHS_MIXED_INTEGER_PROGRAMMING
@ GUROBI_MIXED_INTEGER_PROGRAMMING
@ XPRESS_LINEAR_PROGRAMMING
@ PDLP_LINEAR_PROGRAMMING
@ HIGHS_LINEAR_PROGRAMMING
@ BOP_INTEGER_PROGRAMMING
@ GLPK_LINEAR_PROGRAMMING
@ CPLEX_MIXED_INTEGER_PROGRAMMING
@ KNAPSACK_MIXED_INTEGER_PROGRAMMING
Dedicated knapsack solvers.
@ GUROBI_LINEAR_PROGRAMMING
Commercial software (need license).
void SetTimeLimit(absl::Duration time_limit)
MPSolverResponseStatus LoadModelFromProtoWithUniqueNamesOrDie(const MPModelProto &input_model, std::string *error_message)
bool SetSolverSpecificParametersAsString(const std::string ¶meters)
MPVariable * MakeBoolVar(const std::string &name)
Creates a boolean variable.
virtual OptimizationProblemType ProblemType() const
Returns the optimization problem type set at construction.
ABSL_MUST_USE_RESULT bool NextSolution()
static bool SupportsProblemType(OptimizationProblemType problem_type)
static
void MakeIntVarArray(int nb, double lb, double ub, const std::string &name, std::vector< MPVariable * > *vars)
Creates an array of integer variables.
double ComputeExactConditionNumber() const
absl::Status SetNumThreads(int num_threads)
-— Solver-specific parameters -—
MPVariable * variable(int index) const
absl::Status ClampSolutionWithinBounds()
bool OwnsVariable(const MPVariable *var) const
Debugging: verify that the given MPVariable* belongs to this solver.
void MakeNumVarArray(int nb, double lb, double ub, const std::string &name, std::vector< MPVariable * > *vars)
Creates an array of continuous variables.
std::vector< double > ComputeConstraintActivities() const
bool SupportsCallbacks() const
void FillSolutionResponseProto(MPSolutionResponse *response) const
Encodes the current solution in a solution response protocol buffer.
int64_t wall_time() const
void ExportModelToProto(MPModelProto *output_model) const
Exports model to protocol buffer.
static std::string GetMPModelRequestLoggingInfo(const MPModelRequest &request)
static
MPConstraint * MakeRowConstraint()
Creates a constraint with -infinity and +infinity bounds.
MPObjective * MutableObjective()
Returns the mutable objective object.
bool OutputIsEnabled() const
static OptimizationProblemType ParseSolverTypeOrDie(const std::string &solver_id)
absl::Status LoadSolutionFromProto(const MPSolutionResponse &response, double tolerance=std::numeric_limits< double >::infinity())
MPVariable * MakeNumVar(double lb, double ub, const std::string &name)
Creates a continuous variable.
MPSolver(const std::string &name, OptimizationProblemType problem_type)
Create a solver with the given name and underlying solver backend.
MPConstraint * LookupConstraintOrNull(const std::string &constraint_name) const
MPConstraint * constraint(int index) const
void EnableOutput()
Enables solver logging.
static int64_t global_num_variables()
static
void SetHint(std::vector< std::pair< const MPVariable *, double > > hint)
void SuppressOutput()
Suppresses solver logging.
void * underlying_solver()
MPVariable * LookupVariableOrNull(const std::string &var_name) const
void Write(const std::string &file_name)
static int64_t global_num_constraints()
static
const std::string & Name() const
Returns the name of the model set at construction.
bool ExportModelAsMpsFormat(bool fixed_format, bool obfuscate, std::string *model_str) const
static bool ParseSolverType(absl::string_view solver_id, OptimizationProblemType *type)
static
static void SolveWithProto(const MPModelRequest &model_request, MPSolutionResponse *response, std::atomic< bool > *interrupt=nullptr)
static
std::string SolverVersion() const
Returns a string describing the underlying solver and its version.
void MakeBoolVarArray(int nb, const std::string &name, std::vector< MPVariable * > *vars)
Creates an array of boolean variables.
The class for variables of a Mathematical Programming (MP) model.
double reduced_cost() const
void SetInteger(bool integer)
Sets the integrality requirement of the variable.
bool integer() const
Returns the integrality requirement of the variable.
void SetBranchingPriority(int priority)
double lb() const
Returns the lower bound.
double ub() const
Returns the upper bound.
void SetBounds(double lb, double ub)
Sets both the lower and upper bounds.
const std::string & name() const
Returns the name of the variable.
void set_solution_value(double value)
double solution_value() const
--— MPVariable --—
MPSolver::BasisStatus basis_status() const
int index() const
Returns the index of the variable in the MPSolver::variables_.
double unrounded_solution_value() const
virtual std::string name() const
Object naming.
void Schedule(std::function< void()> closure)
CpModelProto proto
The output proto.
const std::string name
A name for logging purposes.
ABSL_FLAG(bool, verify_solution, false, "Systematically verify the solution when calling Solve()" ", and change the return value of Solve() to ABNORMAL if" " an error was detected.")
MPSolver::OptimizationProblemType problem_type
std::optional< ModelSolveParameters::SolutionHint > hint
void STLDeleteElements(T *container)
void InsertOrDie(Collection *const collection, const typename Collection::value_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.
constexpr double kDefaultPrimalTolerance
MPSolverInterface * BuildCLPInterface(MPSolver *const solver)
MPSolverInterface * BuildCBCInterface(MPSolver *const solver)
bool AreWithinAbsoluteOrRelativeTolerances(FloatType x, FloatType y, FloatType relative_tolerance, FloatType absolute_tolerance)
absl::StatusOr< std::string > ExportModelAsLpFormat(const MPModelProto &model, const MPModelExportOptions &options)
bool GurobiIsCorrectlyInstalled()
bool SolverTypeSupportsInterruption(const MPModelRequest::SolverType solver)
bool MPSolverResponseStatusIsRpcError(MPSolverResponseStatus status)
MPSolverInterface * BuildPdlpInterface(MPSolver *const solver)
Register PDLP in the global linear solver factory.
bool SolverTypeIsMip(MPModelRequest::SolverType solver_type)
There is a homonymous version taking a MPSolver::OptimizationProblemType.
MPSolverInterface * BuildHighsInterface(bool mip, MPSolver *const solver)
Register PDLP in the global linear solver factory.
std::string ProtoEnumToString(ProtoEnumType enum_value)
constexpr NamedOptimizationProblemType kOptimizationProblemTypeNames[]
MPSolverInterface * BuildBopInterface(MPSolver *const solver)
Register BOP in the global linear solver factory.
bool XpressIsCorrectlyInstalled()
MPSolverInterface * BuildGLPKInterface(bool mip, MPSolver *const solver)
MPSolverInterface * BuildSatInterface(MPSolver *const solver)
Register Sat in the global linear solver factory.
MPSolverInterface * BuildXpressInterface(bool mip, MPSolver *const solver)
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)
bool AbslParseFlag(const absl::string_view text, MPSolver::OptimizationProblemType *solver_type, std::string *error)
absl::StatusOr< std::string > ExportModelAsMpsFormat(const MPModelProto &model, const MPModelExportOptions &options)
absl::string_view ToString(MPSolver::OptimizationProblemType optimization_problem_type)
MPSolverInterface * BuildGurobiInterface(bool mip, MPSolver *const solver)
MPSolverInterface * BuildSCIPInterface(MPSolver *const solver)
MPSolverInterface * BuildCplexInterface(bool mip, MPSolver *const solver)
MPSolverInterface * BuildGLOPInterface(MPSolver *const solver)
Register GLOP in the global linear solver factory.
const std::optional< Range > & range
bool obfuscate
Obfuscates variable and constraint names.