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/log/log.h"
39#include "absl/status/status.h"
40#include "absl/status/statusor.h"
41#include "absl/strings/ascii.h"
42#include "absl/strings/match.h"
43#include "absl/strings/str_cat.h"
44#include "absl/strings/str_format.h"
45#include "absl/strings/str_replace.h"
46#include "absl/strings/string_view.h"
47#include "absl/synchronization/mutex.h"
48#include "absl/synchronization/notification.h"
49#include "absl/time/clock.h"
50#include "absl/time/time.h"
51#include "google/protobuf/text_format.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) {
109 LOG(DFATAL) <<
"Invalid SolverType: " << solver_type;
114 DLOG_IF(DFATAL, !interface_->solver_->OwnsVariable(var)) << var;
115 if (var ==
nullptr)
return 0.0;
120 DLOG_IF(DFATAL, !interface_->solver_->OwnsVariable(var)) << var;
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;
134 interface_->SetCoefficient(
this, var, 0.0, old_value);
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;
142 interface_->SetCoefficient(
this, var, coeff, old_value);
146 interface_->ClearConstraint(
this);
147 coefficients_.clear();
151 const bool change =
lb != lb_ ||
ub != ub_;
154 if (change && interface_->constraint_is_extracted(index_)) {
155 interface_->SetConstraintBounds(index_, lb_, ub_);
160 if (!interface_->IsContinuous()) {
161 LOG(DFATAL) <<
"Dual value only available for continuous problems";
164 if (!interface_->CheckSolutionIsSynchronizedAndExists())
return 0.0;
169 if (!interface_->IsContinuous()) {
170 LOG(DFATAL) <<
"Basis status only available for continuous problems";
173 if (!interface_->CheckSolutionIsSynchronizedAndExists()) {
177 return interface_->row_status(index_);
180bool MPConstraint::ContainsNewVariables() {
182 for (
const auto& entry : coefficients_) {
183 const int variable_index = entry.first->index();
184 if (variable_index >= last_variable_index ||
195 DLOG_IF(DFATAL, !interface_->solver_->OwnsVariable(var)) << var;
196 if (var ==
nullptr)
return 0.0;
201 DLOG_IF(DFATAL, !interface_->solver_->OwnsVariable(var)) << var;
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;
212 interface_->SetObjectiveCoefficient(var, coeff);
217 interface_->SetObjectiveOffset(offset_);
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);
233 interface_->ClearObjective();
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()) {
251 interface_->ClearObjective();
252 coefficients_.clear();
264 interface_->maximize_ = maximize;
265 interface_->SetOptimizationDirection(maximize);
276 return interface_->objective_value();
282 return interface_->best_objective_bound();
288 if (!interface_->CheckSolutionIsSynchronizedAndExists())
return 0.0;
292 return (integer_ && interface_->IsMIP()) ? round(solution_value_)
297 if (!interface_->CheckSolutionIsSynchronizedAndExists())
return 0.0;
298 return solution_value_;
302 if (!interface_->IsContinuous()) {
303 LOG(DFATAL) <<
"Reduced cost only available for continuous problems";
306 if (!interface_->CheckSolutionIsSynchronizedAndExists())
return 0.0;
307 return reduced_cost_;
311 if (!interface_->IsContinuous()) {
312 LOG(DFATAL) <<
"Basis status only available for continuous problems";
315 if (!interface_->CheckSolutionIsSynchronizedAndExists()) {
319 return interface_->column_status(index_);
323 const bool change =
lb != lb_ ||
ub != ub_;
326 if (change && interface_->variable_is_extracted(index_)) {
327 interface_->SetVariableBounds(index_, lb_, ub_);
334 if (interface_->variable_is_extracted(index_)) {
335 interface_->SetVariableInteger(index_,
integer);
341 if (priority == branching_priority_)
return;
342 branching_priority_ = priority;
343 interface_->BranchingPriorityChangedForVariable(index_);
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;
370 const std::string& parameters) {
371 solver_specific_parameter_string_ = parameters;
372 return interface_->SetSolverSpecificParametersAsString(parameters);
379 DCHECK(solver !=
nullptr);
382 QCHECK(interface !=
nullptr)
383 <<
"Unsupported problem type: '" << solver->
ProblemType()
384 <<
"'. Did you forget to link the library for this solver?";
390int NumDigits(
int n) {
394 return static_cast<int>(std::max(1.0L, log(1.0L * n) / log(10.0L) + 1.0));
396 return static_cast<int>(std::max(1.0, log10(
static_cast<double>(n)) + 1.0));
404 problem_type_(problem_type),
405 construction_time_(
absl::Now()) {
406 interface_.reset(BuildSolverInterface(
this));
407 if (absl::GetFlag(FLAGS_linear_solver_enable_verbose_output)) {
410 objective_.reset(
new MPObjective(interface_.get()));
425struct NamedOptimizationProblemType {
427 absl::string_view name;
460 const std::string
id =
461 absl::StrReplaceAll(absl::AsciiStrToUpper(solver_id), {{
"-",
"_"}});
471 std::string lower_id = absl::AsciiStrToLower(
id);
474 if (absl::EndsWith(lower_id,
"_mip")) {
475 lower_id = lower_id.substr(0, lower_id.size() - 4);
479 if (lower_id ==
"cp_sat") {
485 if (named_solver.name == lower_id) {
486 *type = named_solver.problem_type;
497 if (named_solver.problem_type == optimization_problem_type) {
498 return named_solver.name;
501 LOG(FATAL) <<
"Unrecognized solver type: "
502 <<
static_cast<int>(optimization_problem_type);
507 std::string* error) {
508 DCHECK(solver_type !=
nullptr);
509 DCHECK(error !=
nullptr);
512 *error = absl::StrCat(
"Solver type: ", text,
" does not exist.");
519 const std::string& solver_id) {
529 LOG(WARNING) <<
"Unrecognized solver type: " << solver_id;
533 LOG(WARNING) <<
"Support for " << solver_id
534 <<
" not linked in, or the license was not found.";
542 if (!variable_name_to_index_) GenerateVariableNameIndex();
544 absl::flat_hash_map<std::string, int>::const_iterator it =
545 variable_name_to_index_->find(var_name);
546 if (it == variable_name_to_index_->end())
return nullptr;
547 return variables_[it->second];
551 const std::string& constraint_name)
const {
552 if (!constraint_name_to_index_) GenerateConstraintNameIndex();
554 const auto it = constraint_name_to_index_->find(constraint_name);
555 if (it == constraint_name_to_index_->end())
return nullptr;
556 return constraints_[it->second];
562 const MPModelProto& input_model, std::string* error_message,
570 return LoadModelFromProtoInternal(
573 clear_names ? DEFAULT_CLEAR_NAMES
574 : INVALID_MODEL_ON_DUPLICATE_NONEMPTY_NAMES,
575 true, error_message);
579 const MPModelProto& input_model, std::string* error_message) {
583 GenerateVariableNameIndex();
584 GenerateConstraintNameIndex();
586 return LoadModelFromProtoInternal(
587 input_model, DIE_ON_DUPLICATE_NONEMPTY_NAMES,
588 true, error_message);
593class MPVariableNamesIterator {
595 explicit MPVariableNamesIterator(
const MPModelProto& model) : model_(model) {}
596 int index()
const {
return index_; }
597 absl::string_view name()
const {
return model_.variable(index_).name(); }
598 static std::string DescribeIndex(
int index) {
599 return absl::StrFormat(
"variable[%d]", index);
601 void Advance() { ++index_; }
602 bool AtEnd()
const {
return index_ == model_.variable_size(); }
605 const MPModelProto& model_;
610class MPConstraintNamesIterator {
612 explicit MPConstraintNamesIterator(
const MPModelProto& model)
617 index_(model_.constraint().empty() ? ~0 : 0) {}
618 int index()
const {
return index_; }
619 absl::string_view name()
const {
620 return index_ >= 0 ? model_.constraint(index_).name()
624 : model_.general_constraint(~index_)
625 .indicator_constraint()
629 static std::string DescribeIndex(
int index) {
631 ? absl::StrFormat(
"constraint[%d]", index)
633 "general_constraint[%d].indicator_constraint.constraint",
638 if (++index_ == model_.constraint_size()) index_ = ~0;
643 bool AtEnd()
const {
return ~index_ == model_.general_constraint_size(); }
646 const MPModelProto& model_;
653template <
class NameIterator>
654std::string FindDuplicateNamesError(NameIterator name_iterator) {
655 absl::flat_hash_map<absl::string_view, int> name_to_index;
656 for (; !name_iterator.AtEnd(); name_iterator.Advance()) {
657 if (name_iterator.name().empty())
continue;
659 name_to_index.insert({name_iterator.name(), name_iterator.index()})
661 if (index != name_iterator.index()) {
662 return absl::StrFormat(
663 "Duplicate name '%s' in %s.name() and %s.name()",
664 name_iterator.name(), NameIterator::DescribeIndex(index),
665 NameIterator::DescribeIndex(name_iterator.index()));
673 const MPModelProto& input_model, ModelProtoNamesPolicy name_policy,
674 bool check_model_validity, std::string* error_message) {
675 CHECK(error_message !=
nullptr);
677 if (check_model_validity) {
683 if (error.empty() && name_policy != DEFAULT_CLEAR_NAMES) {
684 error = FindDuplicateNamesError(MPVariableNamesIterator(input_model));
686 error = FindDuplicateNamesError(MPConstraintNamesIterator(input_model));
688 if (!error.empty() && name_policy == DIE_ON_DUPLICATE_NONEMPTY_NAMES) {
692 const bool clear_names = name_policy == DEFAULT_CLEAR_NAMES;
694 if (!error.empty()) {
695 *error_message = error;
697 <<
"Invalid model given to LoadModelFromProto(): " << error;
698 if (absl::GetFlag(FLAGS_mpsolver_bypass_model_validation)) {
700 <<
"Ignoring the model error(s) because of"
701 <<
" --mpsolver_bypass_model_validation.";
708 if (input_model.has_quadratic_objective()) {
710 "Optimizing a quadratic objective is only supported through direct "
711 "proto solves. Please use MPSolver::SolveWithProto, or the solver's "
712 "direct proto solve function.";
718 const std::string empty;
719 for (
int i = 0;
i < input_model.variable_size(); ++
i) {
720 const MPVariableProto& var_proto = input_model.variable(i);
722 MakeNumVar(var_proto.lower_bound(), var_proto.upper_bound(),
723 clear_names ? empty : var_proto.name());
724 variable->SetInteger(var_proto.is_integer());
725 if (var_proto.branching_priority() != 0) {
726 variable->SetBranchingPriority(var_proto.branching_priority());
728 objective->SetCoefficient(
variable, var_proto.objective_coefficient());
731 for (
const MPConstraintProto& ct_proto : input_model.constraint()) {
732 if (ct_proto.lower_bound() == -
infinity() &&
733 ct_proto.upper_bound() ==
infinity()) {
737 MPConstraint*
const ct =
739 clear_names ? empty : ct_proto.name());
740 ct->set_is_lazy(ct_proto.is_lazy());
741 for (
int j = 0; j < ct_proto.var_index_size(); ++j) {
742 ct->SetCoefficient(variables_[ct_proto.var_index(j)],
743 ct_proto.coefficient(j));
747 for (
const MPGeneralConstraintProto& general_constraint :
748 input_model.general_constraint()) {
749 switch (general_constraint.general_constraint_case()) {
752 general_constraint.indicator_constraint().constraint();
753 if (proto.lower_bound() == -
infinity() &&
754 proto.upper_bound() ==
infinity()) {
759 MPConstraint*
const constraint =
new MPConstraint(
760 constraint_index, proto.lower_bound(), proto.upper_bound(),
761 clear_names ?
"" : proto.name(), interface_.get());
762 if (constraint_name_to_index_) {
767 constraint_is_extracted_.push_back(
false);
770 for (
int j = 0; j < proto.var_index_size(); ++j) {
771 constraint->SetCoefficient(variables_[proto.var_index(j)],
772 proto.coefficient(j));
776 variables_[general_constraint.indicator_constraint().var_index()];
779 general_constraint.indicator_constraint().var_value();
781 if (!interface_->AddIndicatorConstraint(
constraint)) {
782 *error_message =
"Solver doesn't support indicator constraints";
788 *error_message = absl::StrFormat(
789 "Optimizing general constraints of type %i is only supported "
790 "through direct proto solves. Please use MPSolver::SolveWithProto, "
791 "or the solver's direct proto solve function.",
792 general_constraint.general_constraint_case());
797 objective->SetOptimizationDirection(input_model.maximize());
798 if (input_model.has_objective_offset()) {
799 objective->SetOffset(input_model.objective_offset());
803 solution_hint_.clear();
804 for (
int i = 0;
i < input_model.solution_hint().var_index_size(); ++
i) {
805 solution_hint_.push_back(
806 std::make_pair(variables_[input_model.solution_hint().var_index(i)],
807 input_model.solution_hint().var_value(i)));
836 CHECK(response !=
nullptr);
839 ResultStatusToMPSolverResponseStatus(interface_->result_status_));
849 if (interface_->IsMIP()) {
865bool InCategory(
int status,
int category) {
867 while (status > category) status >>= 4;
868 return status == category;
871void AppendStatusStr(absl::string_view msg, MPSolutionResponse* response) {
872 response->set_status_str(
873 absl::StrCat(response->status_str(),
874 (response->status_str().empty() ?
"" :
"\n"), msg));
882 std::atomic<bool>* interrupt) {
889 std::atomic<bool>* interrupt) {
890 CHECK(response !=
nullptr);
892 if (interrupt !=
nullptr &&
896 "Called MPSolver::SolveWithProto with an underlying solver that "
897 "doesn't support interruption.");
902 request->model().name(),
904 if (request->enable_internal_solver_output()) {
906 std::cout <<
"MPModelRequest info:\n"
912 if (solver.interface_->SupportsDirectlySolveProto(interrupt)) {
914 solver.interface_->DirectlySolveProto(std::move(request), interrupt);
919 const std::optional<LazyMutableCopy<MPModelProto>> optional_model =
921 if (!optional_model)
return;
923 std::string error_message;
924 response->
set_status(solver.LoadModelFromProtoInternal(
925 **optional_model, DEFAULT_CLEAR_NAMES,
926 false, &error_message));
931 LOG_IF(WARNING, request->enable_internal_solver_output())
932 <<
"LoadModelFromProtoInternal() failed even though the model was "
935 << response->
status() <<
"); Error: " << error_message;
938 if (request->has_solver_time_limit_seconds()) {
939 solver.
SetTimeLimit(absl::Seconds(request->solver_time_limit_seconds()));
941 std::string warning_message;
942 if (request->has_solver_specific_parameters()) {
944 request->solver_specific_parameters())) {
945 if (request->ignore_solver_specific_parameters_failure()) {
948 "Warning: the solver specific parameters were not successfully "
957 if (interrupt ==
nullptr) {
963 const absl::Time start_time = absl::Now();
964 absl::Time interrupt_time;
965 bool interrupted_by_user =
false;
967 absl::Notification solve_finished;
968 auto polling_func = [&interrupt, &solve_finished, &solver,
969 &interrupted_by_user, &interrupt_time, &request]() {
970 constexpr absl::Duration kPollDelay = absl::Microseconds(100);
971 constexpr absl::Duration kMaxInterruptionDelay = absl::Seconds(10);
973 while (!interrupt->load()) {
974 if (solve_finished.HasBeenNotified())
return;
975 absl::SleepFor(kPollDelay);
980 solver.InterruptSolve();
981 interrupt_time = absl::Now();
982 interrupted_by_user =
true;
997 for (absl::Duration poll_delay = kPollDelay;
998 absl::Now() <= interrupt_time + kMaxInterruptionDelay;
1000 if (solve_finished.WaitForNotificationWithTimeout(poll_delay)) {
1003 solver.InterruptSolve();
1008 <<
"MPSolver::InterruptSolve() seems to be ignored by the "
1009 "underlying solver, despite repeated calls over at least "
1010 << absl::FormatDuration(kMaxInterruptionDelay)
1011 <<
". Solver type used: "
1024 thread_pool.
Schedule(polling_func);
1028 if (!interrupt->load()) {
1030 solver.FillSolutionResponseProto(response);
1034 "Solve not started, because the user set the atomic<bool> in "
1035 "MPSolver::SolveWithProto() to true before solving could "
1038 solve_finished.Notify();
1043 if (interrupted_by_user) {
1051 "User interrupted MPSolver::SolveWithProto() by setting the "
1052 "atomic<bool> to true at %s (%s after solving started.)",
1053 absl::FormatTime(interrupt_time),
1054 absl::FormatDuration(interrupt_time - start_time)),
1059 if (!warning_message.empty()) {
1060 AppendStatusStr(warning_message, response);
1065 DCHECK(output_model !=
nullptr);
1066 output_model->
Clear();
1074 variable_proto->
set_name(var->name());
1078 if (objective_->GetCoefficient(var) != 0.0) {
1080 objective_->GetCoefficient(var));
1082 if (var->branching_priority() != 0) {
1093 absl::flat_hash_map<const MPVariable*, int> var_to_index;
1094 for (
int j = 0; j < static_cast<int>(variables_.size()); ++j) {
1095 var_to_index[variables_[j]] = j;
1101 if (
constraint->indicator_variable() !=
nullptr) {
1120 std::vector<std::pair<int, double>> linear_term;
1121 for (
const auto& entry :
constraint->coefficients_) {
1124 DCHECK_NE(-1, var_index);
1125 const double coeff = entry.second;
1126 linear_term.push_back(std::pair<int, double>(var_index, coeff));
1130 std::sort(linear_term.begin(), linear_term.end());
1132 for (
const std::pair<int, double>& var_and_coeff : linear_term) {
1141 if (!solution_hint_.empty()) {
1144 for (
const auto& var_value_pair : solution_hint_) {
1156 return absl::InvalidArgumentError(absl::StrCat(
1157 "Cannot load a solution unless its status is OPTIMAL or FEASIBLE"
1165 variables_.size()) {
1166 return absl::InvalidArgumentError(absl::StrCat(
1167 "Trying to load a solution whose number of variables (",
1169 ") does not correspond to the Solver's (", variables_.size(),
")"));
1171 interface_->ExtractModel();
1175 double largest_error = 0;
1176 int num_vars_out_of_bounds = 0;
1177 int last_offending_var = -1;
1182 const double lb_error = var->
lb() - var_value;
1183 const double ub_error = var_value - var->
ub();
1184 if (lb_error > tolerance || ub_error > tolerance) {
1185 ++num_vars_out_of_bounds;
1186 largest_error = std::max(largest_error, std::max(lb_error, ub_error));
1187 last_offending_var = i;
1190 if (num_vars_out_of_bounds > 0) {
1191 return absl::InvalidArgumentError(absl::StrCat(
1192 "Loaded a solution whose variables matched the solver's, but ",
1193 num_vars_out_of_bounds,
" of ", variables_.size(),
1194 " variables were out of their bounds, by more than the primal"
1195 " tolerance which is: ",
1196 tolerance,
". Max error: ", largest_error,
", last offender var is #",
1197 last_offending_var,
": '", variables_[last_offending_var]->name(),
1206 constraints_.size()) {
1207 return absl::InvalidArgumentError(absl::StrCat(
1208 "Trying to load a dual solution whose number of entries (",
1209 response.
dual_value_size(),
") does not correspond to the Solver's (",
1210 constraints_.size(),
")"));
1213 constraints_[i]->set_dual_value(response.
dual_value(i));
1218 variables_.size()) {
1219 return absl::InvalidArgumentError(absl::StrCat(
1220 "Trying to load a reduced cost solution whose number of entries (",
1222 ") does not correspond to the Solver's (", variables_.size(),
")"));
1225 variables_[i]->set_reduced_cost(response.
reduced_cost(i));
1239 return absl::OkStatus();
1244 absl::MutexLock lock(global_count_mutex_);
1245 global_num_variables_ += variables_.size();
1246 global_num_constraints_ += constraints_.size();
1251 if (variable_name_to_index_) {
1252 variable_name_to_index_->clear();
1254 variable_is_extracted_.clear();
1255 if (constraint_name_to_index_) {
1256 constraint_name_to_index_->clear();
1258 constraint_is_extracted_.clear();
1259 interface_->Reset();
1260 solution_hint_.clear();
1268 const std::vector<BasisStatus>& variable_statuses,
1269 const std::vector<BasisStatus>& constraint_statuses) {
1270 interface_->SetStartingLpBasis(variable_statuses, constraint_statuses);
1276 const std::string& name) {
1279 new MPVariable(var_index, lb, ub, integer, name, interface_.get());
1280 if (variable_name_to_index_) {
1283 variables_.push_back(v);
1284 variable_is_extracted_.push_back(
false);
1285 interface_->AddVariable(v);
1290 const std::string& name) {
1291 return MakeVar(lb, ub,
false, name);
1295 const std::string& name) {
1296 return MakeVar(lb, ub,
true, name);
1300 return MakeVar(0.0, 1.0,
true, name);
1304 const std::string& name,
1305 std::vector<MPVariable*>* vars) {
1307 if (nb <= 0)
return;
1308 const int num_digits = NumDigits(nb);
1309 for (
int i = 0; i < nb; ++i) {
1311 vars->push_back(
MakeVar(lb, ub, integer, name));
1314 absl::StrFormat(
"%s%0*d", name.c_str(), num_digits, i);
1315 vars->push_back(
MakeVar(lb, ub, integer, vname));
1321 const std::string& name,
1322 std::vector<MPVariable*>* vars) {
1327 const std::string& name,
1328 std::vector<MPVariable*>* vars) {
1333 std::vector<MPVariable*>* vars) {
1346 const std::string& name) {
1349 new MPConstraint(constraint_index, lb, ub, name, interface_.get());
1350 if (constraint_name_to_index_) {
1355 constraint_is_extracted_.push_back(
false);
1369 const std::string& name) {
1374 constraint->SetCoefficient(kv.first, kv.second);
1379int MPSolver::ComputeMaxConstraintSize(
int min_constraint_index,
1380 int max_constraint_index)
const {
1381 int max_constraint_size = 0;
1382 DCHECK_GE(min_constraint_index, 0);
1383 DCHECK_LE(max_constraint_index, constraints_.size());
1384 for (
int i = min_constraint_index; i < max_constraint_index; ++i) {
1386 if (
static_cast<int>(ct->coefficients_.size()) > max_constraint_size) {
1387 max_constraint_size = ct->coefficients_.size();
1390 return max_constraint_size;
1393bool MPSolver::HasInfeasibleConstraints()
const {
1394 bool hasInfeasibleConstraints =
false;
1395 for (
int i = 0; i < static_cast<int>(constraints_.size()); ++
i) {
1396 if (constraints_[i]->lb() > constraints_[i]->ub()) {
1397 LOG(WARNING) <<
"Constraint " << constraints_[
i]->name() <<
" (" <<
i
1398 <<
") has contradictory bounds:" <<
" lower bound = "
1399 << constraints_[
i]->lb()
1400 <<
" upper bound = " << constraints_[
i]->ub();
1401 hasInfeasibleConstraints =
true;
1404 return hasInfeasibleConstraints;
1407bool MPSolver::HasIntegerVariables()
const {
1408 for (
const MPVariable*
const variable : variables_) {
1409 if (
variable->integer())
return true;
1416 return Solve(default_param);
1425 if (HasInfeasibleConstraints()) {
1427 return interface_->result_status_;
1431 if (absl::GetFlag(FLAGS_verify_solution)) {
1433 VLOG(1) <<
"--verify_solution enabled, but the solver did not find a"
1434 <<
" solution: skipping the verification.";
1437 absl::GetFlag(FLAGS_log_verification_errors))) {
1439 interface_->result_status_ = status;
1442 DCHECK_EQ(interface_->result_status_, status);
1447 interface_->Write(file_name);
1451std::string PrettyPrintVar(
const MPVariable& var) {
1452 const std::string prefix =
"Variable '" + var.
name() +
"': domain = ";
1454 var.
lb() > var.
ub()) {
1455 return prefix +
"∅";
1460 const int64_t lb =
static_cast<int64_t
>(ceil(var.
lb()));
1461 const int64_t ub =
static_cast<int64_t
>(floor(var.
ub()));
1463 return prefix +
"∅";
1464 }
else if (lb == ub) {
1465 return absl::StrFormat(
"%s{ %d }", prefix.c_str(), lb);
1467 return absl::StrFormat(
"%s{ %d, %d }", prefix.c_str(), lb, ub);
1471 if (var.
lb() == var.
ub()) {
1472 return absl::StrFormat(
"%s{ %f }", prefix.c_str(), var.
lb());
1474 return prefix + (var.
integer() ?
"Integer" :
"Real") +
" in " +
1476 ? std::string(
"]-∞")
1477 : absl::StrFormat(
"[%f", var.
lb())) +
1480 : absl::StrFormat(
"%f]", var.
ub()));
1483std::string PrettyPrintConstraint(
const MPConstraint& constraint) {
1484 std::string prefix =
"Constraint '" + constraint.name() +
"': ";
1487 constraint.lb() > constraint.ub()) {
1488 return prefix +
"ALWAYS FALSE";
1492 return prefix +
"ALWAYS TRUE";
1494 prefix +=
"<linear expr>";
1496 if (constraint.lb() == constraint.ub()) {
1497 return absl::StrFormat(
"%s = %f", prefix.c_str(), constraint.lb());
1501 return absl::StrFormat(
"%s ≤ %f", prefix.c_str(), constraint.ub());
1504 return absl::StrFormat(
"%s ≥ %f", prefix.c_str(), constraint.lb());
1506 return absl::StrFormat(
"%s ∈ [%f, %f]", prefix.c_str(), constraint.lb(),
1512 interface_->ExtractModel();
1514 const double value =
variable->solution_value();
1515 if (std::isnan(value)) {
1516 return absl::InvalidArgumentError(
1517 absl::StrCat(
"NaN value for ", PrettyPrintVar(*
variable)));
1519 if (value < variable->lb()) {
1521 }
else if (value >
variable->ub()) {
1526 return absl::OkStatus();
1531 if (!interface_->CheckSolutionIsSynchronizedAndExists())
return {};
1532 std::vector<double> activities(constraints_.size(), 0.0);
1533 for (
int i = 0; i < static_cast<int>(constraints_.size()); ++i) {
1536 for (
const auto& entry :
constraint.coefficients_) {
1537 sum.
Add(entry.first->solution_value() * entry.second);
1539 activities[i] = sum.
Value();
1546 double max_observed_error = 0;
1547 if (tolerance < 0) tolerance =
infinity();
1555 if (std::isnan(value)) {
1558 LOG_IF(ERROR, log_errors) <<
"NaN value for " << PrettyPrintVar(var);
1563 if (value < var.
lb() - tolerance) {
1565 max_observed_error = std::max(max_observed_error, var.
lb() - value);
1566 LOG_IF(ERROR, log_errors)
1567 <<
"Value " << value <<
" too low for " << PrettyPrintVar(var);
1572 if (value > var.
ub() + tolerance) {
1574 max_observed_error = std::max(max_observed_error, value - var.
ub());
1575 LOG_IF(ERROR, log_errors)
1576 <<
"Value " << value <<
" too high for " << PrettyPrintVar(var);
1581 if (fabs(value - round(value)) > tolerance) {
1583 max_observed_error =
1584 std::max(max_observed_error, fabs(value - round(value)));
1585 LOG_IF(ERROR, log_errors)
1586 <<
"Non-integer value " << value <<
" for " << PrettyPrintVar(var);
1590 if (!
IsMIP() && HasIntegerVariables()) {
1591 LOG_IF(INFO, log_errors) <<
"Skipped variable integrality check, because "
1592 <<
"a continuous relaxation of the model was "
1593 <<
"solved (i.e., the selected solver does not "
1594 <<
"support integer variables).";
1599 for (
int i = 0; i < static_cast<int>(constraints_.size()); ++i) {
1601 const double activity = activities[i];
1603 double inaccurate_activity = 0.0;
1604 for (
const auto& entry :
constraint.coefficients_) {
1605 inaccurate_activity += entry.first->solution_value() * entry.second;
1608 if (std::isnan(activity) || std::isnan(inaccurate_activity)) {
1611 LOG_IF(ERROR, log_errors)
1612 <<
"NaN value for " << PrettyPrintConstraint(
constraint);
1616 if (
constraint.indicator_variable() ==
nullptr ||
1617 std::round(
constraint.indicator_variable()->solution_value()) ==
1620 if (activity <
constraint.lb() - tolerance) {
1622 max_observed_error =
1623 std::max(max_observed_error,
constraint.lb() - activity);
1624 LOG_IF(ERROR, log_errors)
1625 <<
"Activity " << activity <<
" too low for "
1627 }
else if (inaccurate_activity <
constraint.lb() - tolerance) {
1628 LOG_IF(WARNING, log_errors)
1629 <<
"Activity " << activity <<
", computed with the (inaccurate)"
1630 <<
" standard sum of its terms, is too low for "
1635 if (activity >
constraint.ub() + tolerance) {
1637 max_observed_error =
1638 std::max(max_observed_error, activity -
constraint.ub());
1639 LOG_IF(ERROR, log_errors)
1640 <<
"Activity " << activity <<
" too high for "
1642 }
else if (inaccurate_activity >
constraint.ub() + tolerance) {
1643 LOG_IF(WARNING, log_errors)
1644 <<
"Activity " << activity <<
", computed with the (inaccurate)"
1645 <<
" standard sum of its terms, is too high for "
1656 double inaccurate_objective_value = objective.
offset();
1657 for (
const auto& entry : objective.coefficients_) {
1658 const double term = entry.first->solution_value() * entry.second;
1659 objective_sum.
Add(term);
1660 inaccurate_objective_value += term;
1662 const double actual_objective_value = objective_sum.
Value();
1664 objective.
Value(), actual_objective_value, tolerance, tolerance)) {
1666 max_observed_error = std::max(
1667 max_observed_error, fabs(actual_objective_value - objective.
Value()));
1668 LOG_IF(ERROR, log_errors)
1669 <<
"Objective value " << objective.
Value() <<
" isn't accurate"
1670 <<
", it should be " << actual_objective_value
1671 <<
" (delta=" << actual_objective_value - objective.
Value() <<
").";
1673 inaccurate_objective_value,
1674 tolerance, tolerance)) {
1675 LOG_IF(WARNING, log_errors)
1676 <<
"Objective value " << objective.
Value() <<
" doesn't correspond"
1677 <<
" to the value computed with the standard (and therefore inaccurate)"
1678 <<
" sum of its terms.";
1680 if (num_errors > 0) {
1681 LOG_IF(ERROR, log_errors)
1682 <<
"There were " << num_errors <<
" errors above the tolerance ("
1683 << tolerance <<
"), the largest was " << max_observed_error;
1700 return interface_->ComputeExactConditionNumber();
1704 if (var ==
nullptr)
return false;
1705 if (var->
index() >= 0 && var->
index() <
static_cast<int>(variables_.size())) {
1707 return variables_[var->
index()] == var;
1713 std::string* model_str)
const {
1718 const auto status_or =
1720 *model_str = status_or.value_or(
"");
1721 return status_or.ok();
1725 std::string* model_str)
const {
1730 const auto status_or =
1732 *model_str = status_or.value_or(
"");
1733 return status_or.ok();
1737 for (
const auto& var_value_pair : hint) {
1739 <<
"hint variable does not belong to this solver";
1741 solution_hint_ = std::move(hint);
1744void MPSolver::GenerateVariableNameIndex()
const {
1745 if (variable_name_to_index_)
return;
1746 variable_name_to_index_ = absl::flat_hash_map<std::string, int>();
1747 for (
const MPVariable*
const var : variables_) {
1752void MPSolver::GenerateConstraintNameIndex()
const {
1753 if (constraint_name_to_index_)
return;
1754 constraint_name_to_index_ = absl::flat_hash_map<std::string, int>();
1755 for (
const MPConstraint*
const cst : constraints_) {
1763 interface_->SetCallback(mp_callback);
1767 return interface_->SupportsCallbacks();
1771absl::Mutex MPSolver::global_count_mutex_(absl::kConstInit);
1772int64_t MPSolver::global_num_variables_ = 0;
1773int64_t MPSolver::global_num_constraints_ = 0;
1777 absl::MutexLock lock(global_count_mutex_);
1778 return global_num_variables_;
1783 absl::MutexLock lock(global_count_mutex_);
1784 return global_num_constraints_;
1812 <<
"MPSolverResponseStatusIsRpcError() called with invalid status "
1813 <<
"(value: " << status <<
")";
1837 LOG(WARNING) <<
"Writing model not implemented in this solver interface.";
1872 solver_->variable_is_extracted_.assign(
solver_->variables_.size(),
false);
1873 solver_->constraint_is_extracted_.assign(
solver_->constraints_.size(),
false);
1879 <<
"The model has been changed since the solution was last computed."
1880 <<
" MPSolverInterface::sync_status_ = " <<
sync_status_;
1891 LOG(DFATAL) <<
"No solution exists. MPSolverInterface::result_status_ = "
1904 const double trivial_worst_bound =
1905 maximize_ ? -std::numeric_limits<double>::infinity()
1906 : std::numeric_limits<double>::infinity();
1908 VLOG(1) <<
"Best objective bound only available for discrete problems.";
1909 return trivial_worst_bound;
1912 return trivial_worst_bound;
1915 if (
solver_->variables_.empty() &&
solver_->constraints_.empty()) {
1916 return solver_->Objective().offset();
1929 LOG(DFATAL) <<
"ComputeExactConditionNumber not implemented for "
1966 LOG(WARNING) <<
"Trying to set an unsupported parameter: " << param <<
".";
1970 LOG(WARNING) <<
"Trying to set an unsupported parameter: " << param <<
".";
1974 LOG(WARNING) <<
"Trying to set a supported parameter: " << param
1975 <<
" to an unsupported value: " << value;
1979 LOG(WARNING) <<
"Trying to set a supported parameter: " << param
1980 <<
" to an unsupported value: " << value;
1984 return absl::UnimplementedError(
1985 absl::StrFormat(
"SetNumThreads() not supported by %s.",
SolverVersion()));
1989 const std::string& parameters) {
1990 if (parameters.empty()) {
1994 LOG(WARNING) <<
"SetSolverSpecificParametersAsString() not supported by "
2026 lp_algorithm_is_default_(true) {}
2032 relative_mip_gap_value_ = value;
2036 primal_tolerance_value_ = value;
2040 dual_tolerance_value_ = value;
2044 LOG(ERROR) <<
"Trying to set an unknown parameter: " << param <<
".";
2054 LOG(ERROR) <<
"Trying to set a supported parameter: " << param
2055 <<
" to an unknown value: " << value;
2057 presolve_value_ = value;
2062 LOG(ERROR) <<
"Trying to set a supported parameter: " << param
2063 <<
" to an unknown value: " << value;
2065 scaling_value_ = value;
2070 LOG(ERROR) <<
"Trying to set a supported parameter: " << param
2071 <<
" to an unknown value: " << value;
2073 lp_algorithm_value_ = value;
2074 lp_algorithm_is_default_ =
false;
2079 LOG(ERROR) <<
"Trying to set a supported parameter: " << param
2080 <<
" to an unknown value: " << value;
2082 incrementality_value_ = value;
2086 LOG(ERROR) <<
"Trying to set an unknown parameter: " << param <<
".";
2107 LOG(ERROR) <<
"Trying to reset an unknown parameter: " << param <<
".";
2124 lp_algorithm_is_default_ =
true;
2132 LOG(ERROR) <<
"Trying to reset an unknown parameter: " << param <<
".";
2151 return relative_mip_gap_value_;
2154 return primal_tolerance_value_;
2157 return dual_tolerance_value_;
2160 LOG(ERROR) <<
"Trying to get an unknown parameter: " << param <<
".";
2170 return presolve_value_;
2174 return lp_algorithm_value_;
2177 return incrementality_value_;
2180 return scaling_value_;
2183 LOG(ERROR) <<
"Trying to get an unknown parameter: " << param <<
".";
2193#if !defined(__PORTABLE_PLATFORM__)
2195 abbreviated_request = request;
2198 google::protobuf::TextFormat::PrintToString(abbreviated_request, &out);
2200 out =
"<Info unavailable because: __PORTABLE_PLATFORM__>\n";
2203 absl::StrAppendFormat(&out,
"model_name: '%s'\n", request.
model().
name());
2210 static auto*
const kInstance =
new MPSolverInterfaceFactoryRepository();
2217MPSolverInterfaceFactoryRepository::~MPSolverInterfaceFactoryRepository() {
2218 absl::MutexLock lock(mutex_);
2226 std::function<
bool()> is_runtime_ready) {
2227 absl::MutexLock lock(mutex_);
2228 if (!is_runtime_ready) is_runtime_ready = []() {
return true; };
2229 map_[problem_type] = Entry{
2230 .factory = std::move(factory),
2231 .is_runtime_ready = std::move(is_runtime_ready),
2237 absl::MutexLock lock(mutex_);
2238 return map_.erase(problem_type) == 1;
2243 absl::MutexLock lock(mutex_);
2245 CHECK(entry !=
nullptr) <<
"No factory registered for problem type "
2247 CHECK(entry->is_runtime_ready())
2249 <<
" is not ready.";
2250 return entry->factory(solver);
2256 if (entry ==
nullptr)
return false;
2257 return entry->is_runtime_ready();
2260std::vector<MPSolver::OptimizationProblemType>
2262 std::vector<MPSolver::OptimizationProblemType> out;
2263 for (
auto it = map_.begin(); it != map_.end(); ++it) {
2264 out.push_back(it->first);
2269std::string MPSolverInterfaceFactoryRepository
2272 for (
auto it = map_.begin(); it != map_.end(); ++it) {
void Add(const FpNumber &value)
const absl::flat_hash_map< const MPVariable *, double > & terms() const
double upper_bound() const
const LinearExpr & linear_expr() const
double lower_bound() const
void set_name(Arg_ &&arg, Args_... args)
void set_lower_bound(double value)
void set_is_lazy(bool value)
void add_coefficient(double value)
void set_upper_bound(double value)
void add_var_index(::int32_t value)
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
double GetCoefficient(const MPVariable *var) const
void SetBounds(double lb, double ub)
Sets both the lower and upper bounds.
double dual_value() const
::operations_research::MPIndicatorConstraint *PROTOBUF_NONNULL mutable_indicator_constraint()
void set_name(Arg_ &&arg, Args_... args)
void set_var_index(::int32_t value)
void set_var_value(::int32_t value)
::operations_research::MPConstraintProto *PROTOBUF_NONNULL mutable_constraint()
void set_maximize(bool value)
::operations_research::MPConstraintProto *PROTOBUF_NONNULL add_constraint()
::operations_research::MPVariableProto *PROTOBUF_NONNULL add_variable()
::operations_research::MPGeneralConstraintProto *PROTOBUF_NONNULL add_general_constraint()
::operations_research::PartialVariableAssignment *PROTOBUF_NONNULL mutable_solution_hint()
void set_objective_offset(double value)
ABSL_ATTRIBUTE_REINITIALIZES void Clear() PROTOBUF_FINAL
void set_name(Arg_ &&arg, Args_... args)
const ::std::string & name() const
static constexpr SolverType CPLEX_LINEAR_PROGRAMMING
static constexpr SolverType CBC_MIXED_INTEGER_PROGRAMMING
MPModelRequest_SolverType SolverType
static constexpr SolverType CLP_LINEAR_PROGRAMMING
static constexpr SolverType HIGHS_LINEAR_PROGRAMMING
static constexpr SolverType SAT_INTEGER_PROGRAMMING
static constexpr SolverType HIGHS_MIXED_INTEGER_PROGRAMMING
static constexpr SolverType BOP_INTEGER_PROGRAMMING
static constexpr SolverType GUROBI_MIXED_INTEGER_PROGRAMMING
static constexpr SolverType PDLP_LINEAR_PROGRAMMING
static constexpr SolverType XPRESS_LINEAR_PROGRAMMING
static constexpr SolverType KNAPSACK_MIXED_INTEGER_PROGRAMMING
static constexpr SolverType GLPK_MIXED_INTEGER_PROGRAMMING
static bool SolverType_Parse(::absl::string_view name, SolverType *PROTOBUF_NONNULL value)
static constexpr SolverType GLPK_LINEAR_PROGRAMMING
static constexpr SolverType GUROBI_LINEAR_PROGRAMMING
static constexpr SolverType CPLEX_MIXED_INTEGER_PROGRAMMING
const ::operations_research::MPModelProto & model() const
static constexpr SolverType GLOP_LINEAR_PROGRAMMING
static constexpr SolverType SCIP_MIXED_INTEGER_PROGRAMMING
static constexpr SolverType XPRESS_MIXED_INTEGER_PROGRAMMING
A class to express a linear objective.
void SetCoefficient(const MPVariable *var, double coeff)
double GetCoefficient(const MPVariable *var) const
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.
void add_variable_value(double value)
::operations_research::MPSolveInfo *PROTOBUF_NONNULL mutable_solve_info()
bool has_best_objective_bound() const
double dual_value(int index) const
::operations_research::MPSolverResponseStatus status() const
double best_objective_bound() const
int reduced_cost_size() const
void add_reduced_cost(double value)
void set_objective_value(double value)
double variable_value(int index) const
bool has_objective_value() const
void set_best_objective_bound(double value)
double reduced_cost(int index) const
void set_status_str(Arg_ &&arg, Args_... args)
double objective_value() const
ABSL_ATTRIBUTE_REINITIALIZES void Clear() PROTOBUF_FINAL
void add_dual_value(double value)
int dual_value_size() const
void set_status(::operations_research::MPSolverResponseStatus value)
int variable_value_size() const
void set_solve_wall_time_seconds(double value)
std::string PrettyPrintAllRegisteredProblemTypes() const
MPSolverInterface * Create(MPSolver *solver) const
std::vector< MPSolver::OptimizationProblemType > ListAllRegisteredProblemTypes() const
static MPSolverInterfaceFactoryRepository * GetInstance()
bool Unregister(MPSolver::OptimizationProblemType problem_type)
bool Supports(MPSolver::OptimizationProblemType problem_type) const
void Register(MPSolverInterfaceFactory factory, MPSolver::OptimizationProblemType problem_type, std::function< bool()> is_runtime_ready={})
double best_objective_bound() const
bool CheckSolutionIsSynchronized() const
virtual void SetPrimalTolerance(double value)=0
virtual void ExtractNewConstraints()=0
virtual void ExtractObjective()=0
void InvalidateSolutionSynchronization()
virtual void SetRelativeMipGap(double value)=0
virtual bool IsMIP() const =0
void ResetExtractionInformation()
virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)
int last_variable_index() const
virtual void SetPresolveMode(int value)=0
int last_constraint_index_
virtual std::string SolverVersion() const =0
virtual absl::Status SetNumThreads(int num_threads)
virtual bool SetSolverSpecificParametersAsString(const std::string ¶meters)
static const int kDummyVariableIndex
virtual bool CheckSolutionExists() const
virtual void SetLpAlgorithm(int value)=0
virtual void SetUnsupportedIntegerParam(MPSolverParameters::IntegerParam param)
bool variable_is_extracted(int var_index) const
void SetUnsupportedDoubleParam(MPSolverParameters::DoubleParam param)
virtual void ExtractNewVariables()=0
double objective_value() const
double best_objective_bound_
void SetDoubleParamToUnsupportedValue(MPSolverParameters::DoubleParam param, double value)
virtual double ComputeExactConditionNumber() const
virtual void Write(const std::string &filename)
void SetMIPParameters(const MPSolverParameters ¶m)
virtual ~MPSolverInterface()
virtual void SetDualTolerance(double value)=0
MPSolverInterface(MPSolver *solver)
void SetCommonParameters(const MPSolverParameters ¶m)
bool CheckSolutionIsSynchronizedAndExists() const
MPSolver::ResultStatus result_status_
SynchronizationStatus sync_status_
static const PresolveValues kDefaultPresolve
void ResetIntegerParam(MPSolverParameters::IntegerParam param)
static const double kDefaultDualTolerance
static const double kDefaultPrimalTolerance
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
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
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)
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)
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
@ 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
@ GUROBI_LINEAR_PROGRAMMING
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)
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)
MPVariable * variable(int index) const
absl::Status ClampSolutionWithinBounds()
bool OwnsVariable(const MPVariable *var) const
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)
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()
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()
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 void SolveWithProto(const MPModelRequest &model_request, MPSolutionResponse *response, std::atomic< bool > *interrupt=nullptr)
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.
void set_upper_bound(double value)
void set_name(Arg_ &&arg, Args_... args)
void set_is_integer(bool value)
void set_objective_coefficient(double value)
void set_branching_priority(::int32_t value)
void set_lower_bound(double value)
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.
double solution_value() const
MPSolver::BasisStatus basis_status() const
int index() const
Returns the index of the variable in the MPSolver::variables_.
double unrounded_solution_value() const
void add_var_index(::int32_t value)
void add_var_value(double value)
void Schedule(absl::AnyInvocable< void() && > callback)
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.")
void STLDeleteElements(T *container)
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
const Collection::value_type::second_type * FindOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
const MapUtilMappedT< Collection > & FindWithDefault(const Collection &collection, const KeyType &key, const MapUtilMappedT< Collection > &value)
const ::std::string & MPModelRequest_SolverType_Name(T value)
constexpr double kDefaultPrimalTolerance
std::function< MPSolverInterface *(MPSolver *)> MPSolverInterfaceFactory
bool AreWithinAbsoluteOrRelativeTolerances(FloatType x, FloatType y, FloatType relative_tolerance, FloatType absolute_tolerance)
absl::StatusOr< std::string > ExportModelAsLpFormat(const MPModelProto &model, const MPModelExportOptions &options)
bool SolverTypeSupportsInterruption(const MPModelRequest::SolverType solver)
bool MPSolverResponseStatusIsRpcError(MPSolverResponseStatus status)
bool SolverTypeIsMip(MPModelRequest::SolverType solver_type)
std::string ProtoEnumToString(ProtoEnumType enum_value)
constexpr NamedOptimizationProblemType kOptimizationProblemTypeNames[]
@ MPSOLVER_CANCELLED_BY_USER
@ MPSOLVER_MODEL_INVALID_SOLUTION_HINT
@ MPSOLVER_UNKNOWN_STATUS
@ MPSOLVER_MODEL_IS_VALID
@ MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS
@ MPSOLVER_INCOMPATIBLE_OPTIONS
@ MPSOLVER_SOLVER_TYPE_UNAVAILABLE
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)
bool obfuscate
Obfuscates variable and constraint names.