Google OR-Tools: ortools/linear_solver/linear_solver.cc Source File

1

2

3

4

5

6

7

8

9

10

11

12

13

15

16#if !defined(_MSC_VER)

17#include <unistd.h>

18#endif

19

20#include <algorithm>

21#include <atomic>

22#include <cmath>

23#include <cstddef>

24#include <cstdint>

25#include <functional>

26#include <iostream>

27#include <limits>

28#include <optional>

29#include <string>

30#include <utility>

31#include <vector>

32

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"

66

68 "Systematically verify the solution when calling Solve()"

69 ", and change the return value of Solve() to ABNORMAL if"

70 " an error was detected.");

71ABSL_FLAG(bool, log_verification_errors, true,

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().");

77

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.");

82

84

86 switch (solver_type) {

95 return false;

96

107 return true;

108 }

109 LOG(DFATAL) << "Invalid SolverType: " << solver_type;

110 return false;

111}

112

114 DLOG_IF(DFATAL, !interface_->solver_->OwnsVariable(var)) << var;

115 if (var == nullptr) return 0.0;

117}

118

120 DLOG_IF(DFATAL, !interface_->solver_->OwnsVariable(var)) << var;

121 if (var == nullptr) return;

122 if (coeff == 0.0) {

123 auto it = coefficients_.find(var);

124

125

126

127

128

129

130

131 if (it != coefficients_.end() && it->second != 0.0) {

132 const double old_value = it->second;

133 it->second = 0.0;

134 interface_->SetCoefficient(this, var, 0.0, old_value);

135 }

136 return;

137 }

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);

143}

144

146 interface_->ClearConstraint(this);

147 coefficients_.clear();

148}

149

151 const bool change = lb != lb_ || ub != ub_;

152 lb_ = lb;

153 ub_ = ub;

154 if (change && interface_->constraint_is_extracted(index_)) {

155 interface_->SetConstraintBounds(index_, lb_, ub_);

156 }

157}

158

160 if (!interface_->IsContinuous()) {

161 LOG(DFATAL) << "Dual value only available for continuous problems";

162 return 0.0;

163 }

164 if (!interface_->CheckSolutionIsSynchronizedAndExists()) return 0.0;

165 return dual_value_;

166}

167

169 if (!interface_->IsContinuous()) {

170 LOG(DFATAL) << "Basis status only available for continuous problems";

172 }

173 if (!interface_->CheckSolutionIsSynchronizedAndExists()) {

175 }

176

177 return interface_->row_status(index_);

178}

179

180bool MPConstraint::ContainsNewVariables() {

182 for (const auto& entry : coefficients_) {

183 const int variable_index = entry.first->index();

184 if (variable_index >= last_variable_index ||

186 return true;

187 }

188 }

189 return false;

190}

191

192

193

195 DLOG_IF(DFATAL, !interface_->solver_->OwnsVariable(var)) << var;

196 if (var == nullptr) return 0.0;

198}

199

201 DLOG_IF(DFATAL, !interface_->solver_->OwnsVariable(var)) << var;

202 if (var == nullptr) return;

203 if (coeff == 0.0) {

204 auto it = coefficients_.find(var);

205

206

207 if (it == coefficients_.end() || it->second == 0.0) return;

208 it->second = 0.0;

209 } else {

210 coefficients_[var] = coeff;

211 }

212 interface_->SetObjectiveCoefficient(var, coeff);

213}

214

216 offset_ = value;

217 interface_->SetObjectiveOffset(offset_);

218}

219

220namespace {

221void CheckLinearExpr(const MPSolver& solver, const LinearExpr& linear_expr) {

222 for (auto var_value_pair : linear_expr.terms()) {

223 CHECK(solver.OwnsVariable(var_value_pair.first))

224 << "Bad MPVariable* in LinearExpr, did you try adding an integer to an "

225 "MPVariable* directly?";

226 }

227}

228}

229

231 bool is_maximization) {

232 CheckLinearExpr(*interface_->solver_, linear_expr);

233 interface_->ClearObjective();

234 coefficients_.clear();

236 for (const auto& kv : linear_expr.terms()) {

238 }

240}

241

243 CheckLinearExpr(*interface_->solver_, linear_expr);

245 for (const auto& kv : linear_expr.terms()) {

247 }

248}

249

251 interface_->ClearObjective();

252 coefficients_.clear();

253 offset_ = 0.0;

255}

256

258

259

260

261

262

263

264 interface_->maximize_ = maximize;

265 interface_->SetOptimizationDirection(maximize);

266}

267

269

271

273

274

275

276 return interface_->objective_value();

277}

278

280

281

282 return interface_->best_objective_bound();

283}

284

285

286

288 if (!interface_->CheckSolutionIsSynchronizedAndExists()) return 0.0;

289

290

291

292 return (integer_ && interface_->IsMIP()) ? round(solution_value_)

293 : solution_value_;

294}

295

297 if (!interface_->CheckSolutionIsSynchronizedAndExists()) return 0.0;

298 return solution_value_;

299}

300

302 if (!interface_->IsContinuous()) {

303 LOG(DFATAL) << "Reduced cost only available for continuous problems";

304 return 0.0;

305 }

306 if (!interface_->CheckSolutionIsSynchronizedAndExists()) return 0.0;

307 return reduced_cost_;

308}

309

311 if (!interface_->IsContinuous()) {

312 LOG(DFATAL) << "Basis status only available for continuous problems";

314 }

315 if (!interface_->CheckSolutionIsSynchronizedAndExists()) {

317 }

318

319 return interface_->column_status(index_);

320}

321

323 const bool change = lb != lb_ || ub != ub_;

324 lb_ = lb;

325 ub_ = ub;

326 if (change && interface_->variable_is_extracted(index_)) {

327 interface_->SetVariableBounds(index_, lb_, ub_);

328 }

329}

330

332 if (integer_ != integer) {

334 if (interface_->variable_is_extracted(index_)) {

335 interface_->SetVariableInteger(index_, integer);

336 }

337 }

338}

339

341 if (priority == branching_priority_) return;

342 branching_priority_ = priority;

343 interface_->BranchingPriorityChangedForVariable(index_);

344}

345

346

347

349

351 return interface_->SolverVersion();

352}

353

355

356

357

359 if (num_threads < 1) {

360 return absl::InvalidArgumentError("num_threads must be a positive number.");

361 }

362 const absl::Status status = interface_->SetNumThreads(num_threads);

363 if (status.ok()) {

364 num_threads_ = num_threads;

365 }

366 return status;

367}

368

370 const std::string& parameters) {

371 solver_specific_parameter_string_ = parameters;

372 return interface_->SetSolverSpecificParametersAsString(parameters);

373}

374

375

376

377namespace {

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?";

385 return interface;

386}

387}

388

389namespace {

390int NumDigits(int n) {

391

392

393#if defined(_MSC_VER)

394 return static_cast<int>(std::max(1.0L, log(1.0L * n) / log(10.0L) + 1.0));

395#else

396 return static_cast<int>(std::max(1.0, log10(static_cast<double>(n)) + 1.0));

397#endif

398}

399}

400

403 : name_(name),

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)) {

409 }

410 objective_.reset(new MPObjective(interface_.get()));

411}

412

414

415

420

421

422

423

424namespace {

425struct NamedOptimizationProblemType {

427 absl::string_view name;

428};

429}

430

431#if defined(_MSC_VER)

432const

433#else

434constexpr

435#endif

456

459

460 const std::string id =

461 absl::StrReplaceAll(absl::AsciiStrToUpper(solver_id), {{"-", "_"}});

462

463

467 return true;

468 }

469

470

471 std::string lower_id = absl::AsciiStrToLower(id);

472

473

474 if (absl::EndsWith(lower_id, "_mip")) {

475 lower_id = lower_id.substr(0, lower_id.size() - 4);

476 }

477

478

479 if (lower_id == "cp_sat") {

480 lower_id = "sat";

481 }

482

483

485 if (named_solver.name == lower_id) {

486 *type = named_solver.problem_type;

487 return true;

488 }

489 }

490

491 return false;

492}

493

497 if (named_solver.problem_type == optimization_problem_type) {

498 return named_solver.name;

499 }

500 }

501 LOG(FATAL) << "Unrecognized solver type: "

502 << static_cast<int>(optimization_problem_type);

503}

504

507 std::string* error) {

508 DCHECK(solver_type != nullptr);

509 DCHECK(error != nullptr);

511 if (!result) {

512 *error = absl::StrCat("Solver type: ", text, " does not exist.");

513 }

514 return result;

515}

516

517

519 const std::string& solver_id) {

522 return problem_type;

523}

524

525

529 LOG(WARNING) << "Unrecognized solver type: " << solver_id;

530 return nullptr;

531 }

533 LOG(WARNING) << "Support for " << solver_id

534 << " not linked in, or the license was not found.";

535 return nullptr;

536 }

538 return solver;

539}

540

542 if (!variable_name_to_index_) GenerateVariableNameIndex();

543

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];

548}

549

551 const std::string& constraint_name) const {

552 if (!constraint_name_to_index_) GenerateConstraintNameIndex();

553

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];

557}

558

559

560

562 const MPModelProto& input_model, std::string* error_message,

563 bool clear_names) {

565

566

567

568

569

570 return LoadModelFromProtoInternal(

571 input_model,

572

573 clear_names ? DEFAULT_CLEAR_NAMES

574 : INVALID_MODEL_ON_DUPLICATE_NONEMPTY_NAMES,

575 true, error_message);

576}

577

579 const MPModelProto& input_model, std::string* error_message) {

581

582

583 GenerateVariableNameIndex();

584 GenerateConstraintNameIndex();

585

586 return LoadModelFromProtoInternal(

587 input_model, DIE_ON_DUPLICATE_NONEMPTY_NAMES,

588 true, error_message);

589}

590

591namespace {

592

593class MPVariableNamesIterator {

594 public:

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);

600 }

601 void Advance() { ++index_; }

602 bool AtEnd() const { return index_ == model_.variable_size(); }

603

604 private:

605 const MPModelProto& model_;

606 int index_ = 0;

607};

608

609

610class MPConstraintNamesIterator {

611 public:

612 explicit MPConstraintNamesIterator(const MPModelProto& model)

613 : model_(model),

614

615

616

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()

621

622

623

624 : model_.general_constraint(~index_)

625 .indicator_constraint()

626 .constraint()

627 .name();

628 }

629 static std::string DescribeIndex(int index) {

630 return index >= 0

631 ? absl::StrFormat("constraint[%d]", index)

632 : absl::StrFormat(

633 "general_constraint[%d].indicator_constraint.constraint",

634 ~index);

635 }

636 void Advance() {

637 if (index_ >= 0) {

638 if (++index_ == model_.constraint_size()) index_ = ~0;

639 } else {

640 --index_;

641 }

642 }

643 bool AtEnd() const { return ~index_ == model_.general_constraint_size(); }

644

645 private:

646 const MPModelProto& model_;

647 int index_ = 0;

648};

649

650

651

652

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;

658 const int index =

659 name_to_index.insert({name_iterator.name(), name_iterator.index()})

660 .first->second;

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()));

666 }

667 }

668 return "";

669}

670}

671

673 const MPModelProto& input_model, ModelProtoNamesPolicy name_policy,

674 bool check_model_validity, std::string* error_message) {

675 CHECK(error_message != nullptr);

676 std::string error;

677 if (check_model_validity) {

679 }

680

681

682

683 if (error.empty() && name_policy != DEFAULT_CLEAR_NAMES) {

684 error = FindDuplicateNamesError(MPVariableNamesIterator(input_model));

685 if (error.empty()) {

686 error = FindDuplicateNamesError(MPConstraintNamesIterator(input_model));

687 }

688 if (!error.empty() && name_policy == DIE_ON_DUPLICATE_NONEMPTY_NAMES) {

689 LOG(FATAL) << error;

690 }

691 }

692 const bool clear_names = name_policy == DEFAULT_CLEAR_NAMES;

693

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.";

702 } else {

705 }

706 }

707

708 if (input_model.has_quadratic_objective()) {

709 *error_message =

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.";

714 }

715

717

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());

727 }

728 objective->SetCoefficient(variable, var_proto.objective_coefficient());

729 }

730

731 for (const MPConstraintProto& ct_proto : input_model.constraint()) {

732 if (ct_proto.lower_bound() == -infinity() &&

733 ct_proto.upper_bound() == infinity()) {

734 continue;

735 }

736

737 MPConstraint* const ct =

738 MakeRowConstraint(ct_proto.lower_bound(), ct_proto.upper_bound(),

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));

744 }

745 }

746

747 for (const MPGeneralConstraintProto& general_constraint :

748 input_model.general_constraint()) {

749 switch (general_constraint.general_constraint_case()) {

751 const auto& proto =

752 general_constraint.indicator_constraint().constraint();

753 if (proto.lower_bound() == -infinity() &&

754 proto.upper_bound() == infinity()) {

755 continue;

756 }

757

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_) {

764 constraint_index);

765 }

767 constraint_is_extracted_.push_back(false);

768

769 constraint->set_is_lazy(proto.is_lazy());

770 for (int j = 0; j < proto.var_index_size(); ++j) {

771 constraint->SetCoefficient(variables_[proto.var_index(j)],

772 proto.coefficient(j));

773 }

774

776 variables_[general_constraint.indicator_constraint().var_index()];

779 general_constraint.indicator_constraint().var_value();

780

781 if (!interface_->AddIndicatorConstraint(constraint)) {

782 *error_message = "Solver doesn't support indicator constraints";

784 }

785 break;

786 }

787 default:

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());

794 }

795 }

796

797 objective->SetOptimizationDirection(input_model.maximize());

798 if (input_model.has_objective_offset()) {

799 objective->SetOffset(input_model.objective_offset());

800 }

801

802

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)));

808 }

810}

811

812namespace {

815 switch (status) {

830 }

832}

833}

834

836 CHECK(response != nullptr);

837 response->Clear();

839 ResultStatusToMPSolverResponseStatus(interface_->result_status_));

841 1000.0);

847 }

848

849 if (interface_->IsMIP()) {

851 } else {

852

855 }

856

859 }

860 }

861 }

862}

863

864namespace {

865bool InCategory(int status, int category) {

867 while (status > category) status >>= 4;

868 return status == category;

869}

870

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));

875}

876

877}

878

879

882 std::atomic<bool>* interrupt) {

884}

885

886

889 std::atomic<bool>* interrupt) {

890 CHECK(response != nullptr);

891

892 if (interrupt != nullptr &&

896 "Called MPSolver::SolveWithProto with an underlying solver that "

897 "doesn't support interruption.");

898 return;

899 }

900

902 request->model().name(),

904 if (request->enable_internal_solver_output()) {

906 std::cout << "MPModelRequest info:\n"

908 }

909

910

911

912 if (solver.interface_->SupportsDirectlySolveProto(interrupt)) {

913 *response =

914 solver.interface_->DirectlySolveProto(std::move(request), interrupt);

915 return;

916 }

917

918

919 const std::optional<LazyMutableCopy<MPModelProto>> optional_model =

921 if (!optional_model) return;

922

923 std::string error_message;

924 response->set_status(solver.LoadModelFromProtoInternal(

925 **optional_model, DEFAULT_CLEAR_NAMES,

926 false, &error_message));

927

928

931 LOG_IF(WARNING, request->enable_internal_solver_output())

932 << "LoadModelFromProtoInternal() failed even though the model was "

933 << "valid! Status: "

935 << response->status() << "); Error: " << error_message;

936 return;

937 }

938 if (request->has_solver_time_limit_seconds()) {

939 solver.SetTimeLimit(absl::Seconds(request->solver_time_limit_seconds()));

940 }

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()) {

946

947 warning_message =

948 "Warning: the solver specific parameters were not successfully "

949 "applied";

950 } else {

952 return;

953 }

954 }

955 }

956

957 if (interrupt == nullptr) {

958

959

962 } else {

963 const absl::Time start_time = absl::Now();

964 absl::Time interrupt_time;

965 bool interrupted_by_user = false;

966 {

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);

972

973 while (!interrupt->load()) {

974 if (solve_finished.HasBeenNotified()) return;

975 absl::SleepFor(kPollDelay);

976 }

977

978

979

980 solver.InterruptSolve();

981 interrupt_time = absl::Now();

982 interrupted_by_user = true;

983

984

985

986

987

988

989

990

991

992

993

994

995

996

997 for (absl::Duration poll_delay = kPollDelay;

998 absl::Now() <= interrupt_time + kMaxInterruptionDelay;

999 poll_delay *= 2) {

1000 if (solve_finished.WaitForNotificationWithTimeout(poll_delay)) {

1001 return;

1002 } else {

1003 solver.InterruptSolve();

1004 }

1005 }

1006

1007 LOG(DFATAL)

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: "

1013

1014

1015

1016

1017 };

1018

1019

1020

1021

1022

1023 ThreadPool thread_pool(1);

1024 thread_pool.Schedule(polling_func);

1025

1026

1027

1028 if (!interrupt->load()) {

1029 solver.Solve();

1030 solver.FillSolutionResponseProto(response);

1031 } else {

1034 "Solve not started, because the user set the atomic<bool> in "

1035 "MPSolver::SolveWithProto() to true before solving could "

1036 "start.");

1037 }

1038 solve_finished.Notify();

1039

1040

1041 }

1042

1043 if (interrupted_by_user) {

1044

1045

1048 }

1049 AppendStatusStr(

1050 absl::StrFormat(

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)),

1055 response);

1056 }

1057 }

1058

1059 if (!warning_message.empty()) {

1060 AppendStatusStr(warning_message, response);

1061 }

1062}

1063

1065 DCHECK(output_model != nullptr);

1066 output_model->Clear();

1067

1069

1070 for (const MPVariable* var : variables_) {

1072

1073

1074 variable_proto->set_name(var->name());

1078 if (objective_->GetCoefficient(var) != 0.0) {

1080 objective_->GetCoefficient(var));

1081 }

1082 if (var->branching_priority() != 0) {

1084 }

1085 }

1086

1087

1088

1089

1090

1091

1092

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;

1096 }

1097

1098

1101 if (constraint->indicator_variable() != nullptr) {

1108 constraint->indicator_variable()->index());

1110 constraint_proto = indicator_constraint_proto->mutable_constraint();

1111 } else {

1113 }

1118

1119

1120 std::vector<std::pair<int, double>> linear_term;

1121 for (const auto& entry : constraint->coefficients_) {

1122 const MPVariable* const var = entry.first;

1124 DCHECK_NE(-1, var_index);

1125 const double coeff = entry.second;

1126 linear_term.push_back(std::pair<int, double>(var_index, coeff));

1127 }

1128

1129

1130 std::sort(linear_term.begin(), linear_term.end());

1131

1132 for (const std::pair<int, double>& var_and_coeff : linear_term) {

1133 constraint_proto->add_var_index(var_and_coeff.first);

1134 constraint_proto->add_coefficient(var_and_coeff.second);

1135 }

1136 }

1137

1140

1141 if (!solution_hint_.empty()) {

1144 for (const auto& var_value_pair : solution_hint_) {

1145 hint->add_var_index(var_value_pair.first->index());

1147 }

1148 }

1149}

1150

1152 double tolerance) {

1153 interface_->result_status_ = static_cast<ResultStatus>(response.status());

1156 return absl::InvalidArgumentError(absl::StrCat(

1157 "Cannot load a solution unless its status is OPTIMAL or FEASIBLE"

1158 " (status was: ",

1160 }

1161

1162

1163

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(), ")"));

1170 }

1171 interface_->ExtractModel();

1172

1173 if (tolerance != infinity()) {

1174

1175 double largest_error = 0;

1176 int num_vars_out_of_bounds = 0;

1177 int last_offending_var = -1;

1179 const double var_value = response.variable_value(i);

1181

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;

1188 }

1189 }

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(),

1198 "'"));

1199 }

1200 }

1202 variables_[i]->set_solution_value(response.variable_value(i));

1203 }

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(), ")"));

1211 }

1213 constraints_[i]->set_dual_value(response.dual_value(i));

1214 }

1215 }

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(), ")"));

1223 }

1225 variables_[i]->set_reduced_cost(response.reduced_cost(i));

1226 }

1227 }

1228

1229

1231 interface_->objective_value_ = response.objective_value();

1232 }

1235 }

1236

1237

1239 return absl::OkStatus();

1240}

1241

1243 {

1244 absl::MutexLock lock(global_count_mutex_);

1245 global_num_variables_ += variables_.size();

1246 global_num_constraints_ += constraints_.size();

1247 }

1251 if (variable_name_to_index_) {

1252 variable_name_to_index_->clear();

1253 }

1254 variable_is_extracted_.clear();

1255 if (constraint_name_to_index_) {

1256 constraint_name_to_index_->clear();

1257 }

1258 constraint_is_extracted_.clear();

1259 interface_->Reset();

1260 solution_hint_.clear();

1261}

1262

1264

1266

1268 const std::vector<BasisStatus>& variable_statuses,

1269 const std::vector<BasisStatus>& constraint_statuses) {

1270 interface_->SetStartingLpBasis(variable_statuses, constraint_statuses);

1271}

1272

1274

1276 const std::string& name) {

1279 new MPVariable(var_index, lb, ub, integer, name, interface_.get());

1280 if (variable_name_to_index_) {

1282 }

1283 variables_.push_back(v);

1284 variable_is_extracted_.push_back(false);

1285 interface_->AddVariable(v);

1286 return v;

1287}

1288

1290 const std::string& name) {

1291 return MakeVar(lb, ub, false, name);

1292}

1293

1295 const std::string& name) {

1296 return MakeVar(lb, ub, true, name);

1297}

1298

1300 return MakeVar(0.0, 1.0, true, name);

1301}

1302

1304 const std::string& name,

1305 std::vector<MPVariable*>* vars) {

1306 DCHECK_GE(nb, 0);

1307 if (nb <= 0) return;

1308 const int num_digits = NumDigits(nb);

1309 for (int i = 0; i < nb; ++i) {

1310 if (name.empty()) {

1311 vars->push_back(MakeVar(lb, ub, integer, name));

1312 } else {

1313 std::string vname =

1314 absl::StrFormat("%s%0*d", name.c_str(), num_digits, i);

1315 vars->push_back(MakeVar(lb, ub, integer, vname));

1316 }

1317 }

1318}

1319

1321 const std::string& name,

1322 std::vector<MPVariable*>* vars) {

1323 MakeVarArray(nb, lb, ub, false, name, vars);

1324}

1325

1327 const std::string& name,

1328 std::vector<MPVariable*>* vars) {

1330}

1331

1333 std::vector<MPVariable*>* vars) {

1334 MakeVarArray(nb, 0.0, 1.0, true, name, vars);

1335}

1336

1340

1344

1346 const std::string& name) {

1349 new MPConstraint(constraint_index, lb, ub, name, interface_.get());

1350 if (constraint_name_to_index_) {

1352 constraint_index);

1353 }

1355 constraint_is_extracted_.push_back(false);

1356 interface_->AddRowConstraint(constraint);

1358}

1359

1363

1367

1369 const std::string& name) {

1370 CheckLinearExpr(*this, range.linear_expr());

1374 constraint->SetCoefficient(kv.first, kv.second);

1375 }

1377}

1378

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();

1388 }

1389 }

1390 return max_constraint_size;

1391}

1392

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;

1402 }

1403 }

1404 return hasInfeasibleConstraints;

1405}

1406

1407bool MPSolver::HasIntegerVariables() const {

1408 for (const MPVariable* const variable : variables_) {

1409 if (variable->integer()) return true;

1410 }

1411 return false;

1412}

1413

1416 return Solve(default_param);

1417}

1418

1420

1421

1422

1423

1424

1425 if (HasInfeasibleConstraints()) {

1427 return interface_->result_status_;

1428 }

1429

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;

1440 }

1441 }

1442 DCHECK_EQ(interface_->result_status_, status);

1443 return status;

1444}

1445

1447 interface_->Write(file_name);

1448}

1449

1450namespace {

1451std::string PrettyPrintVar(const MPVariable& var) {

1452 const std::string prefix = "Variable '" + var.name() + "': domain = ";

1454 var.lb() > var.ub()) {

1455 return prefix + "∅";

1456 }

1457

1458

1459 if (var.integer() && var.ub() - var.lb() <= 1) {

1460 const int64_t lb = static_cast<int64_t>(ceil(var.lb()));

1461 const int64_t ub = static_cast<int64_t>(floor(var.ub()));

1462 if (lb > ub) {

1463 return prefix + "∅";

1464 } else if (lb == ub) {

1465 return absl::StrFormat("%s{ %d }", prefix.c_str(), lb);

1466 } else {

1467 return absl::StrFormat("%s{ %d, %d }", prefix.c_str(), lb, ub);

1468 }

1469 }

1470

1471 if (var.lb() == var.ub()) {

1472 return absl::StrFormat("%s{ %f }", prefix.c_str(), var.lb());

1473 }

1474 return prefix + (var.integer() ? "Integer" : "Real") + " in " +

1476 ? std::string("]-∞")

1477 : absl::StrFormat("[%f", var.lb())) +

1478 ", " +

1480 : absl::StrFormat("%f]", var.ub()));

1481}

1482

1483std::string PrettyPrintConstraint(const MPConstraint& constraint) {

1484 std::string prefix = "Constraint '" + constraint.name() + "': ";

1487 constraint.lb() > constraint.ub()) {

1488 return prefix + "ALWAYS FALSE";

1489 }

1492 return prefix + "ALWAYS TRUE";

1493 }

1494 prefix += "<linear expr>";

1495

1496 if (constraint.lb() == constraint.ub()) {

1497 return absl::StrFormat("%s = %f", prefix.c_str(), constraint.lb());

1498 }

1499

1501 return absl::StrFormat("%s ≤ %f", prefix.c_str(), constraint.ub());

1502 }

1504 return absl::StrFormat("%s ≥ %f", prefix.c_str(), constraint.lb());

1505 }

1506 return absl::StrFormat("%s ∈ [%f, %f]", prefix.c_str(), constraint.lb(),

1507 constraint.ub());

1508}

1509}

1510

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)));

1518 }

1519 if (value < variable->lb()) {

1521 } else if (value > variable->ub()) {

1523 }

1524 }

1526 return absl::OkStatus();

1527}

1528

1530

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);

1538 }

1539 activities[i] = sum.Value();

1540 }

1541 return activities;

1542}

1543

1544

1546 double max_observed_error = 0;

1547 if (tolerance < 0) tolerance = infinity();

1548 int num_errors = 0;

1549

1550

1554

1555 if (std::isnan(value)) {

1556 ++num_errors;

1557 max_observed_error = infinity();

1558 LOG_IF(ERROR, log_errors) << "NaN value for " << PrettyPrintVar(var);

1559 continue;

1560 }

1561

1563 if (value < var.lb() - tolerance) {

1564 ++num_errors;

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);

1568 }

1569 }

1570

1572 if (value > var.ub() + tolerance) {

1573 ++num_errors;

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);

1577 }

1578 }

1579

1581 if (fabs(value - round(value)) > tolerance) {

1582 ++num_errors;

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);

1587 }

1588 }

1589 }

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).";

1595 }

1596

1597

1599 for (int i = 0; i < static_cast<int>(constraints_.size()); ++i) {

1601 const double activity = activities[i];

1602

1603 double inaccurate_activity = 0.0;

1604 for (const auto& entry : constraint.coefficients_) {

1605 inaccurate_activity += entry.first->solution_value() * entry.second;

1606 }

1607

1608 if (std::isnan(activity) || std::isnan(inaccurate_activity)) {

1609 ++num_errors;

1610 max_observed_error = infinity();

1611 LOG_IF(ERROR, log_errors)

1612 << "NaN value for " << PrettyPrintConstraint(constraint);

1613 continue;

1614 }

1615

1616 if (constraint.indicator_variable() == nullptr ||

1617 std::round(constraint.indicator_variable()->solution_value()) ==

1620 if (activity < constraint.lb() - tolerance) {

1621 ++num_errors;

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 "

1626 << PrettyPrintConstraint(constraint);

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 "

1631 << PrettyPrintConstraint(constraint);

1632 }

1633 }

1635 if (activity > constraint.ub() + tolerance) {

1636 ++num_errors;

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 "

1641 << PrettyPrintConstraint(constraint);

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 "

1646 << PrettyPrintConstraint(constraint);

1647 }

1648 }

1649 }

1650 }

1651

1652

1655 objective_sum.Add(objective.offset());

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;

1661 }

1662 const double actual_objective_value = objective_sum.Value();

1664 objective.Value(), actual_objective_value, tolerance, tolerance)) {

1665 ++num_errors;

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.";

1679 }

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;

1684 return false;

1685 }

1686 return true;

1687}

1688

1690

1692

1694

1696

1698

1700 return interface_->ComputeExactConditionNumber();

1701}

1702

1704 if (var == nullptr) return false;

1705 if (var->index() >= 0 && var->index() < static_cast<int>(variables_.size())) {

1706

1707 return variables_[var->index()] == var;

1708 }

1709 return false;

1710}

1711

1713 std::string* model_str) const {

1718 const auto status_or =

1720 *model_str = status_or.value_or("");

1721 return status_or.ok();

1722}

1723

1725 std::string* model_str) const {

1730 const auto status_or =

1732 *model_str = status_or.value_or("");

1733 return status_or.ok();

1734}

1735

1737 for (const auto& var_value_pair : hint) {

1739 << "hint variable does not belong to this solver";

1740 }

1741 solution_hint_ = std::move(hint);

1742}

1743

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_) {

1749 }

1750}

1751

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_) {

1756 gtl::InsertOrDie(&*constraint_name_to_index_, cst->name(), cst->index());

1757 }

1758}

1759

1761

1763 interface_->SetCallback(mp_callback);

1764}

1765

1767 return interface_->SupportsCallbacks();

1768}

1769

1770

1771absl::Mutex MPSolver::global_count_mutex_(absl::kConstInit);

1772int64_t MPSolver::global_num_variables_ = 0;

1773int64_t MPSolver::global_num_constraints_ = 0;

1774

1775

1777 absl::MutexLock lock(global_count_mutex_);

1778 return global_num_variables_;

1779}

1780

1781

1783 absl::MutexLock lock(global_count_mutex_);

1784 return global_num_constraints_;

1785}

1786

1788 switch (status) {

1789

1797 return false;

1798

1799

1802 return false;

1803

1809 return true;

1810 }

1811 LOG(DFATAL)

1812 << "MPSolverResponseStatusIsRpcError() called with invalid status "

1813 << "(value: " << status << ")";

1814 return false;

1815}

1816

1817

1818

1820

1821

1822

1833

1835

1837 LOG(WARNING) << "Writing model not implemented in this solver interface.";

1838}

1839

1846

1850 break;

1851 }

1853

1856 break;

1857 }

1859

1862 break;

1863 }

1864 }

1865}

1866

1867

1872 solver_->variable_is_extracted_.assign(solver_->variables_.size(), false);

1873 solver_->constraint_is_extracted_.assign(solver_->constraints_.size(), false);

1874}

1875

1878 LOG(DFATAL)

1879 << "The model has been changed since the solution was last computed."

1880 << " MPSolverInterface::sync_status_ = " << sync_status_;

1881 return false;

1882 }

1883 return true;

1884}

1885

1886

1887

1891 LOG(DFATAL) << "No solution exists. MPSolverInterface::result_status_ = "

1893 return false;

1894 }

1895 return true;

1896}

1897

1902

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;

1910 }

1912 return trivial_worst_bound;

1913 }

1914

1915 if (solver_->variables_.empty() && solver_->constraints_.empty()) {

1916 return solver_->Objective().offset();

1917 }

1919}

1920

1926

1928

1929 LOG(DFATAL) << "ComputeExactConditionNumber not implemented for "

1932 solver_->ProblemType()));

1933 return 0.0;

1934}

1935

1937

1938

1939

1940

1941

1946 }

1948

1949

1950

1954 }

1955}

1956

1963

1966 LOG(WARNING) << "Trying to set an unsupported parameter: " << param << ".";

1967}

1970 LOG(WARNING) << "Trying to set an unsupported parameter: " << param << ".";

1971}

1974 LOG(WARNING) << "Trying to set a supported parameter: " << param

1975 << " to an unsupported value: " << value;

1976}

1979 LOG(WARNING) << "Trying to set a supported parameter: " << param

1980 << " to an unsupported value: " << value;

1981}

1982

1984 return absl::UnimplementedError(

1985 absl::StrFormat("SetNumThreads() not supported by %s.", SolverVersion()));

1986}

1987

1989 const std::string& parameters) {

1990 if (parameters.empty()) {

1991 return true;

1992 }

1993

1994 LOG(WARNING) << "SetSolverSpecificParametersAsString() not supported by "

1996 return false;

1997}

1998

1999

2000

2002

2011

2016

2017

2026 lp_algorithm_is_default_(true) {}

2027

2029 double value) {

2030 switch (param) {

2032 relative_mip_gap_value_ = value;

2033 break;

2034 }

2036 primal_tolerance_value_ = value;

2037 break;

2038 }

2040 dual_tolerance_value_ = value;

2041 break;

2042 }

2043 default: {

2044 LOG(ERROR) << "Trying to set an unknown parameter: " << param << ".";

2045 }

2046 }

2047}

2048

2050 int value) {

2051 switch (param) {

2054 LOG(ERROR) << "Trying to set a supported parameter: " << param

2055 << " to an unknown value: " << value;

2056 }

2057 presolve_value_ = value;

2058 break;

2059 }

2062 LOG(ERROR) << "Trying to set a supported parameter: " << param

2063 << " to an unknown value: " << value;

2064 }

2065 scaling_value_ = value;

2066 break;

2067 }

2070 LOG(ERROR) << "Trying to set a supported parameter: " << param

2071 << " to an unknown value: " << value;

2072 }

2073 lp_algorithm_value_ = value;

2074 lp_algorithm_is_default_ = false;

2075 break;

2076 }

2079 LOG(ERROR) << "Trying to set a supported parameter: " << param

2080 << " to an unknown value: " << value;

2081 }

2082 incrementality_value_ = value;

2083 break;

2084 }

2085 default: {

2086 LOG(ERROR) << "Trying to set an unknown parameter: " << param << ".";

2087 }

2088 }

2089}

2090

2093 switch (param) {

2096 break;

2097 }

2100 break;

2101 }

2104 break;

2105 }

2106 default: {

2107 LOG(ERROR) << "Trying to reset an unknown parameter: " << param << ".";

2108 }

2109 }

2110}

2111

2114 switch (param) {

2117 break;

2118 }

2121 break;

2122 }

2124 lp_algorithm_is_default_ = true;

2125 break;

2126 }

2129 break;

2130 }

2131 default: {

2132 LOG(ERROR) << "Trying to reset an unknown parameter: " << param << ".";

2133 }

2134 }

2135}

2136

2146

2149 switch (param) {

2151 return relative_mip_gap_value_;

2152 }

2154 return primal_tolerance_value_;

2155 }

2157 return dual_tolerance_value_;

2158 }

2159 default: {

2160 LOG(ERROR) << "Trying to get an unknown parameter: " << param << ".";

2162 }

2163 }

2164}

2165

2168 switch (param) {

2170 return presolve_value_;

2171 }

2174 return lp_algorithm_value_;

2175 }

2177 return incrementality_value_;

2178 }

2180 return scaling_value_;

2181 }

2182 default: {

2183 LOG(ERROR) << "Trying to get an unknown parameter: " << param << ".";

2185 }

2186 }

2187}

2188

2189

2192 std::string out;

2193#if !defined(__PORTABLE_PLATFORM__)

2195 abbreviated_request = request;

2198 google::protobuf::TextFormat::PrintToString(abbreviated_request, &out);

2199#else

2200 out = "<Info unavailable because: __PORTABLE_PLATFORM__>\n";

2201#endif

2203 absl::StrAppendFormat(&out, "model_name: '%s'\n", request.model().name());

2204 }

2205 return out;

2206}

2207

2210 static auto* const kInstance = new MPSolverInterfaceFactoryRepository();

2211 return kInstance;

2212}

2213

2214

2215

2216

2217MPSolverInterfaceFactoryRepository::~MPSolverInterfaceFactoryRepository() {

2218 absl::MutexLock lock(mutex_);

2219 map_.clear();

2220}

2221

2222

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),

2232 };

2233}

2234

2237 absl::MutexLock lock(mutex_);

2238 return map_.erase(problem_type) == 1;

2239}

2240

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);

2251}

2252

2255 const Entry* entry = gtl::FindOrNull(map_, problem_type);

2256 if (entry == nullptr) return false;

2257 return entry->is_runtime_ready();

2258}

2259

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);

2265 }

2266 return out;

2267}

2268

2269std::string MPSolverInterfaceFactoryRepository

2271 std::string out;

2272 for (auto it = map_.begin(); it != map_.end(); ++it) {

2275 "\n";

2276 }

2277 return out;

2278}

2279

2280}

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.

Definition linear_solver.cc:145

void SetCoefficient(const MPVariable *var, double coeff)

Definition linear_solver.cc:119

double lb() const

Returns the lower bound.

double ub() const

Returns the upper bound.

MPSolver::BasisStatus basis_status() const

Definition linear_solver.cc:168

double GetCoefficient(const MPVariable *var) const

Definition linear_solver.cc:113

void SetBounds(double lb, double ub)

Sets both the lower and upper bounds.

Definition linear_solver.cc:150

double dual_value() const

Definition linear_solver.cc:159

::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.

double BestBound() const

Definition linear_solver.cc:279

void SetCoefficient(const MPVariable *var, double coeff)

Definition linear_solver.cc:200

void Clear()

Definition linear_solver.cc:250

double GetCoefficient(const MPVariable *var) const

Definition linear_solver.cc:194

void SetOffset(double value)

Sets the constant term in the objective.

Definition linear_solver.cc:215

double Value() const

Definition linear_solver.cc:272

bool minimization() const

Is the optimization direction set to minimize?

Definition linear_solver.cc:270

bool maximization() const

Is the optimization direction set to maximize?

Definition linear_solver.cc:268

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.

Definition linear_solver.cc:242

void OptimizeLinearExpr(const LinearExpr &linear_expr, bool is_maximization)

Definition linear_solver.cc:230

void SetOptimizationDirection(bool maximize)

Sets the optimization direction (maximize: true or minimize: false).

Definition linear_solver.cc:257

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

Definition linear_solver.cc:2270

MPSolverInterface * Create(MPSolver *solver) const

Definition linear_solver.cc:2241

std::vector< MPSolver::OptimizationProblemType > ListAllRegisteredProblemTypes() const

Definition linear_solver.cc:2261

static MPSolverInterfaceFactoryRepository * GetInstance()

Definition linear_solver.cc:2209

bool Unregister(MPSolver::OptimizationProblemType problem_type)

Definition linear_solver.cc:2235

bool Supports(MPSolver::OptimizationProblemType problem_type) const

Definition linear_solver.cc:2253

void Register(MPSolverInterfaceFactory factory, MPSolver::OptimizationProblemType problem_type, std::function< bool()> is_runtime_ready={})

Definition linear_solver.cc:2223

double best_objective_bound() const

Definition linear_solver.cc:1903

bool CheckSolutionIsSynchronized() const

Definition linear_solver.cc:1876

virtual void SetPrimalTolerance(double value)=0

virtual void ExtractNewConstraints()=0

virtual void ExtractObjective()=0

void InvalidateSolutionSynchronization()

Definition linear_solver.cc:1921

virtual void SetRelativeMipGap(double value)=0

virtual bool IsMIP() const =0

void ResetExtractionInformation()

Definition linear_solver.cc:1868

virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)

Definition linear_solver.cc:1977

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)

Definition linear_solver.cc:1983

virtual bool SetSolverSpecificParametersAsString(const std::string &parameters)

Definition linear_solver.cc:1988

static const int kDummyVariableIndex

virtual bool CheckSolutionExists() const

Definition linear_solver.cc:1888

virtual void SetLpAlgorithm(int value)=0

virtual void SetUnsupportedIntegerParam(MPSolverParameters::IntegerParam param)

Definition linear_solver.cc:1968

bool variable_is_extracted(int var_index) const

void SetUnsupportedDoubleParam(MPSolverParameters::DoubleParam param)

Definition linear_solver.cc:1964

virtual void ExtractNewVariables()=0

double objective_value() const

Definition linear_solver.cc:1898

void ExtractModel()

Definition linear_solver.cc:1840

double best_objective_bound_

void SetDoubleParamToUnsupportedValue(MPSolverParameters::DoubleParam param, double value)

Definition linear_solver.cc:1972

virtual double ComputeExactConditionNumber() const

Definition linear_solver.cc:1927

virtual void Write(const std::string &filename)

Definition linear_solver.cc:1836

void SetMIPParameters(const MPSolverParameters &param)

Definition linear_solver.cc:1957

virtual ~MPSolverInterface()

Definition linear_solver.cc:1834

virtual void SetDualTolerance(double value)=0

MPSolverInterface(MPSolver *solver)

Definition linear_solver.cc:1823

void SetCommonParameters(const MPSolverParameters &param)

Definition linear_solver.cc:1936

bool CheckSolutionIsSynchronizedAndExists() const

MPSolver::ResultStatus result_status_

SynchronizationStatus sync_status_

static const PresolveValues kDefaultPresolve

void ResetIntegerParam(MPSolverParameters::IntegerParam param)

Definition linear_solver.cc:2112

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.

Definition linear_solver.cc:2147

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)

Definition linear_solver.cc:2091

static const int kDefaultIntegerParamValue

@ BARRIER

Barrier algorithm.

void SetDoubleParam(MPSolverParameters::DoubleParam param, double value)

Sets a double parameter to a specific value.

Definition linear_solver.cc:2028

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.

Definition linear_solver.cc:2018

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.

Definition linear_solver.cc:2137

@ SCALING_ON

Scaling is on.

@ SCALING_OFF

Scaling is off.

int GetIntegerParam(MPSolverParameters::IntegerParam param) const

Returns the value of an integer parameter.

Definition linear_solver.cc:2166

static const double kDefaultDoubleParamValue

void SetIntegerParam(MPSolverParameters::IntegerParam param, int value)

Sets a integer parameter to a specific value.

Definition linear_solver.cc:2049

static void SolveLazyMutableRequest(LazyMutableCopy< MPModelRequest > request, MPSolutionResponse *response, std::atomic< bool > *interrupt=nullptr)

Definition linear_solver.cc:887

int64_t iterations() const

Returns the number of simplex iterations.

Definition linear_solver.cc:1695

int NumVariables() const

Returns the number of variables.

void Reset()

Definition linear_solver.cc:1263

static MPSolver * CreateSolver(const std::string &solver_id)

Definition linear_solver.cc:526

void SetStartingLpBasis(const std::vector< MPSolver::BasisStatus > &variable_statuses, const std::vector< MPSolver::BasisStatus > &constraint_statuses)

Definition linear_solver.cc:1267

MPVariable * MakeVar(double lb, double ub, bool integer, const std::string &name)

Definition linear_solver.cc:1275

MPVariable * MakeIntVar(double lb, double ub, const std::string &name)

Creates an integer variable.

Definition linear_solver.cc:1294

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.

Definition linear_solver.cc:1414

MPSolverResponseStatus LoadModelFromProto(const MPModelProto &input_model, std::string *error_message, bool clear_names=true)

Definition linear_solver.cc:561

bool IsMIP() const

Definition linear_solver.cc:348

bool VerifySolution(double tolerance, bool log_errors) const

Definition linear_solver.cc:1545

void MakeVarArray(int nb, double lb, double ub, bool integer, const std::string &name_prefix, std::vector< MPVariable * > *vars)

Definition linear_solver.cc:1303

int64_t nodes() const

Definition linear_solver.cc:1697

bool ExportModelAsLpFormat(bool obfuscate, std::string *model_str) const

Definition linear_solver.cc:1712

const MPObjective & Objective() const

void SetCallback(MPCallback *mp_callback)

Definition linear_solver.cc:1762

@ 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

bool InterruptSolve()

Definition linear_solver.cc:1265

void SetTimeLimit(absl::Duration time_limit)

void Clear()

Definition linear_solver.cc:1242

MPSolverResponseStatus LoadModelFromProtoWithUniqueNamesOrDie(const MPModelProto &input_model, std::string *error_message)

Definition linear_solver.cc:578

bool SetSolverSpecificParametersAsString(const std::string &parameters)

Definition linear_solver.cc:369

MPVariable * MakeBoolVar(const std::string &name)

Creates a boolean variable.

Definition linear_solver.cc:1299

virtual OptimizationProblemType ProblemType() const

Returns the optimization problem type set at construction.

ABSL_MUST_USE_RESULT bool NextSolution()

Definition linear_solver.cc:1760

static bool SupportsProblemType(OptimizationProblemType problem_type)

Definition linear_solver.cc:416

void MakeIntVarArray(int nb, double lb, double ub, const std::string &name, std::vector< MPVariable * > *vars)

Creates an array of integer variables.

Definition linear_solver.cc:1326

double ComputeExactConditionNumber() const

Definition linear_solver.cc:1699

absl::Status SetNumThreads(int num_threads)

Definition linear_solver.cc:358

MPVariable * variable(int index) const

absl::Status ClampSolutionWithinBounds()

Definition linear_solver.cc:1511

bool OwnsVariable(const MPVariable *var) const

Definition linear_solver.cc:1703

void MakeNumVarArray(int nb, double lb, double ub, const std::string &name, std::vector< MPVariable * > *vars)

Creates an array of continuous variables.

Definition linear_solver.cc:1320

virtual ~MPSolver()

Definition linear_solver.cc:413

double solver_infinity()

Definition linear_solver.cc:1273

std::vector< double > ComputeConstraintActivities() const

Definition linear_solver.cc:1529

bool SupportsCallbacks() const

Definition linear_solver.cc:1766

void FillSolutionResponseProto(MPSolutionResponse *response) const

Encodes the current solution in a solution response protocol buffer.

Definition linear_solver.cc:835

int64_t wall_time() const

void ExportModelToProto(MPModelProto *output_model) const

Exports model to protocol buffer.

Definition linear_solver.cc:1064

static std::string GetMPModelRequestLoggingInfo(const MPModelRequest &request)

Definition linear_solver.cc:2190

MPConstraint * MakeRowConstraint()

Creates a constraint with -infinity and +infinity bounds.

Definition linear_solver.cc:1341

MPObjective * MutableObjective()

Returns the mutable objective object.

bool OutputIsEnabled() const

Definition linear_solver.cc:1689

static OptimizationProblemType ParseSolverTypeOrDie(const std::string &solver_id)

Definition linear_solver.cc:518

absl::Status LoadSolutionFromProto(const MPSolutionResponse &response, double tolerance=std::numeric_limits< double >::infinity())

Definition linear_solver.cc:1151

MPVariable * MakeNumVar(double lb, double ub, const std::string &name)

Creates a continuous variable.

Definition linear_solver.cc:1289

MPSolver(const std::string &name, OptimizationProblemType problem_type)

Create a solver with the given name and underlying solver backend.

Definition linear_solver.cc:401

MPConstraint * LookupConstraintOrNull(const std::string &constraint_name) const

Definition linear_solver.cc:550

MPConstraint * constraint(int index) const

void EnableOutput()

Enables solver logging.

Definition linear_solver.cc:1691

static int64_t global_num_variables()

Definition linear_solver.cc:1776

void SetHint(std::vector< std::pair< const MPVariable *, double > > hint)

Definition linear_solver.cc:1736

void SuppressOutput()

Suppresses solver logging.

Definition linear_solver.cc:1693

void * underlying_solver()

Definition linear_solver.cc:354

MPVariable * LookupVariableOrNull(const std::string &var_name) const

Definition linear_solver.cc:541

void Write(const std::string &file_name)

Definition linear_solver.cc:1446

static int64_t global_num_constraints()

Definition linear_solver.cc:1782

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

Definition linear_solver.cc:1724

static bool ParseSolverType(absl::string_view solver_id, OptimizationProblemType *type)

Definition linear_solver.cc:457

static void SolveWithProto(const MPModelRequest &model_request, MPSolutionResponse *response, std::atomic< bool > *interrupt=nullptr)

Definition linear_solver.cc:880

std::string SolverVersion() const

Returns a string describing the underlying solver and its version.

Definition linear_solver.cc:350

void MakeBoolVarArray(int nb, const std::string &name, std::vector< MPVariable * > *vars)

Creates an array of boolean variables.

Definition linear_solver.cc:1332

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

Definition linear_solver.cc:301

void SetInteger(bool integer)

Sets the integrality requirement of the variable.

Definition linear_solver.cc:331

bool integer() const

Returns the integrality requirement of the variable.

void SetBranchingPriority(int priority)

Definition linear_solver.cc:340

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.

Definition linear_solver.cc:322

const std::string & name() const

Returns the name of the variable.

double solution_value() const

Definition linear_solver.cc:287

MPSolver::BasisStatus basis_status() const

Definition linear_solver.cc:310

int index() const

Returns the index of the variable in the MPSolver::variables_.

double unrounded_solution_value() const

Definition linear_solver.cc:296

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)

Definition linear_solver.cc:1787

bool SolverTypeIsMip(MPModelRequest::SolverType solver_type)

Definition linear_solver.cc:85

std::string ProtoEnumToString(ProtoEnumType enum_value)

constexpr NamedOptimizationProblemType kOptimizationProblemTypeNames[]

Definition linear_solver.cc:436

@ 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)

Definition linear_solver.cc:505

absl::StatusOr< std::string > ExportModelAsMpsFormat(const MPModelProto &model, const MPModelExportOptions &options)

absl::string_view ToString(MPSolver::OptimizationProblemType optimization_problem_type)

Definition linear_solver.cc:494

bool obfuscate

Obfuscates variable and constraint names.