26 #include "absl/container/flat_hash_map.h"
27 #include "absl/memory/memory.h"
28 #include "absl/status/status.h"
29 #include "absl/status/statusor.h"
30 #include "absl/strings/str_cat.h"
31 #include "absl/strings/str_join.h"
32 #include "absl/strings/str_split.h"
33 #include "absl/strings/string_view.h"
34 #include "absl/time/clock.h"
35 #include "absl/time/time.h"
36 #include "absl/types/span.h"
46 #include "ortools/glop/parameters.pb.h"
50 #include "ortools/math_opt/callback.pb.h"
55 #include "ortools/math_opt/model.pb.h"
56 #include "ortools/math_opt/model_parameters.pb.h"
57 #include "ortools/math_opt/model_update.pb.h"
58 #include "ortools/math_opt/parameters.pb.h"
59 #include "ortools/math_opt/result.pb.h"
60 #include "ortools/math_opt/solution.pb.h"
61 #include "ortools/math_opt/sparse_containers.pb.h"
71 constexpr
double kInf = std::numeric_limits<double>::infinity();
73 constexpr SupportedProblemStructures kGlopSupportedStructures = {};
75 absl::string_view SafeName(
const VariablesProto& variables,
int index) {
76 if (variables.names().empty()) {
79 return variables.names(
index);
82 absl::string_view SafeName(
const LinearConstraintsProto& linear_constraints,
84 if (linear_constraints.names().empty()) {
87 return linear_constraints.names(
index);
90 absl::StatusOr<TerminationProto> BuildTermination(
92 const SolveInterrupter*
const interrupter) {
112 interrupter->IsInterrupted()
114 : LIMIT_UNDETERMINED);
122 interrupter->IsInterrupted()
124 : LIMIT_UNDETERMINED);
129 return absl::InternalError(
130 absl::StrCat(
"Unexpected GLOP termination reason: ",
133 LOG(FATAL) <<
"Unimplemented GLOP termination reason: "
138 absl::Status ValidateGlopParameters(
const glop::GlopParameters&
parameters) {
140 if (!error.empty()) {
142 <<
"invalid GlopParameters: " << error;
144 return absl::OkStatus();
149 GlopSolver::GlopSolver() : linear_program_(), lp_solver_() {}
151 void GlopSolver::AddVariables(
const VariablesProto& variables) {
153 const glop::ColIndex col_index = linear_program_.CreateNewVariable();
154 linear_program_.SetVariableBounds(col_index, variables.lower_bounds(i),
155 variables.upper_bounds(i));
156 linear_program_.SetVariableName(col_index, SafeName(variables, i));
164 template <
typename IndexType>
167 IndexType num_indices,
168 absl::flat_hash_map<int64_t, IndexType>& id_index_map) {
171 IndexType new_index(0);
173 if (indices_to_delete[
index]) {
175 new_indices[
index] = IndexType(-1);
177 new_indices[
index] = new_index;
181 for (
auto it = id_index_map.begin(); it != id_index_map.end();) {
182 IndexType
index = it->second;
183 if (indices_to_delete[
index]) {
185 id_index_map.erase(it++);
187 it->second = new_indices[
index];
193 void GlopSolver::DeleteVariables(absl::Span<const int64_t> ids_to_delete) {
194 const glop::ColIndex num_cols = linear_program_.num_variables();
197 for (
const int64_t deleted_variable_id : ids_to_delete) {
198 columns_to_delete[variables_.at(deleted_variable_id)] =
true;
200 linear_program_.DeleteColumns(columns_to_delete);
201 UpdateIdIndexMap<glop::ColIndex>(columns_to_delete, num_cols, variables_);
204 void GlopSolver::DeleteLinearConstraints(
205 absl::Span<const int64_t> ids_to_delete) {
206 const glop::RowIndex num_rows = linear_program_.num_constraints();
208 for (
const int64_t deleted_constraint_id : ids_to_delete) {
209 rows_to_delete[linear_constraints_.at(deleted_constraint_id)] =
true;
211 linear_program_.DeleteRows(rows_to_delete);
212 UpdateIdIndexMap<glop::RowIndex>(rows_to_delete, num_rows,
213 linear_constraints_);
216 void GlopSolver::AddLinearConstraints(
217 const LinearConstraintsProto& linear_constraints) {
219 const glop::RowIndex row_index = linear_program_.CreateNewConstraint();
220 linear_program_.SetConstraintBounds(row_index,
221 linear_constraints.lower_bounds(i),
222 linear_constraints.upper_bounds(i));
223 linear_program_.SetConstraintName(row_index,
224 SafeName(linear_constraints, i));
230 void GlopSolver::SetOrUpdateObjectiveCoefficients(
231 const SparseDoubleVectorProto& linear_objective_coefficients) {
232 for (
int i = 0; i < linear_objective_coefficients.ids_size(); ++i) {
233 const glop::ColIndex col_index =
234 variables_.at(linear_objective_coefficients.ids(i));
235 linear_program_.SetObjectiveCoefficient(
236 col_index, linear_objective_coefficients.values(i));
240 void GlopSolver::SetOrUpdateConstraintMatrix(
241 const SparseDoubleMatrixProto& linear_constraint_matrix) {
243 const glop::ColIndex col_index =
244 variables_.at(linear_constraint_matrix.column_ids(j));
245 const glop::RowIndex row_index =
246 linear_constraints_.at(linear_constraint_matrix.row_ids(j));
247 const double coefficient = linear_constraint_matrix.coefficients(j);
248 linear_program_.SetCoefficient(row_index, col_index,
coefficient);
252 void GlopSolver::UpdateVariableBounds(
253 const VariableUpdatesProto& variable_updates) {
254 for (
const auto [
id, lb] :
MakeView(variable_updates.lower_bounds())) {
255 const auto col_index = variables_.at(
id);
256 linear_program_.SetVariableBounds(
257 col_index, lb, linear_program_.variable_upper_bounds()[col_index]);
259 for (
const auto [
id, ub] :
MakeView(variable_updates.upper_bounds())) {
260 const auto col_index = variables_.at(
id);
261 linear_program_.SetVariableBounds(
262 col_index, linear_program_.variable_lower_bounds()[col_index], ub);
266 void GlopSolver::UpdateLinearConstraintBounds(
267 const LinearConstraintUpdatesProto& linear_constraint_updates) {
268 for (
const auto [
id, lb] :
269 MakeView(linear_constraint_updates.lower_bounds())) {
270 const auto row_index = linear_constraints_.at(
id);
271 linear_program_.SetConstraintBounds(
272 row_index, lb, linear_program_.constraint_upper_bounds()[row_index]);
274 for (
const auto [
id, ub] :
275 MakeView(linear_constraint_updates.upper_bounds())) {
276 const auto row_index = linear_constraints_.at(
id);
277 linear_program_.SetConstraintBounds(
278 row_index, linear_program_.constraint_lower_bounds()[row_index], ub);
282 absl::StatusOr<glop::GlopParameters> GlopSolver::MergeSolveParameters(
283 const SolveParametersProto& solve_parameters,
284 const bool setting_initial_basis,
const bool has_message_callback) {
287 <<
"invalid SolveParametersProto.glop value";
289 glop::GlopParameters result = solve_parameters.glop();
290 std::vector<std::string> warnings;
291 if (!result.has_max_time_in_seconds() && solve_parameters.has_time_limit()) {
294 result.set_max_time_in_seconds(absl::ToDoubleSeconds(
time_limit));
296 if (has_message_callback) {
300 result.set_log_search_progress(
true);
306 result.set_log_to_stdout(
false);
307 }
else if (!result.has_log_search_progress()) {
308 result.set_log_search_progress(solve_parameters.enable_output());
310 if (!result.has_num_omp_threads() && solve_parameters.has_threads()) {
311 result.set_num_omp_threads(solve_parameters.threads());
313 if (!result.has_random_seed() && solve_parameters.has_random_seed()) {
314 const int random_seed =
std::max(0, solve_parameters.random_seed());
315 result.set_random_seed(random_seed);
317 if (!result.has_max_number_of_iterations() &&
318 solve_parameters.iteration_limit()) {
319 result.set_max_number_of_iterations(solve_parameters.iteration_limit());
321 if (solve_parameters.has_node_limit()) {
322 warnings.emplace_back(
"GLOP does snot support 'node_limit' parameter");
324 if (!result.has_use_dual_simplex() &&
325 solve_parameters.lp_algorithm() != LP_ALGORITHM_UNSPECIFIED) {
326 switch (solve_parameters.lp_algorithm()) {
327 case LP_ALGORITHM_PRIMAL_SIMPLEX:
328 result.set_use_dual_simplex(
false);
330 case LP_ALGORITHM_DUAL_SIMPLEX:
331 result.set_use_dual_simplex(
true);
333 case LP_ALGORITHM_BARRIER:
334 warnings.emplace_back(
335 "GLOP does not support 'LP_ALGORITHM_BARRIER' value for "
336 "'lp_algorithm' parameter.");
339 LOG(FATAL) <<
"LPAlgorithm: "
341 <<
" unknown, error setting GLOP parameters";
344 if (!result.has_use_scaling() && !result.has_scaling_method() &&
345 solve_parameters.scaling() != EMPHASIS_UNSPECIFIED) {
346 switch (solve_parameters.scaling()) {
348 result.set_use_scaling(
false);
351 case EMPHASIS_MEDIUM:
353 case EMPHASIS_VERY_HIGH:
354 result.set_use_scaling(
true);
355 result.set_scaling_method(glop::GlopParameters::EQUILIBRATION);
358 LOG(FATAL) <<
"Scaling emphasis: "
360 <<
" unknown, error setting GLOP parameters";
363 if (setting_initial_basis) {
364 result.set_use_preprocessing(
false);
365 }
else if (!result.has_use_preprocessing() &&
366 solve_parameters.presolve() != EMPHASIS_UNSPECIFIED) {
367 switch (solve_parameters.presolve()) {
369 result.set_use_preprocessing(
false);
372 case EMPHASIS_MEDIUM:
374 case EMPHASIS_VERY_HIGH:
375 result.set_use_preprocessing(
true);
378 LOG(FATAL) <<
"Presolve emphasis: "
380 <<
" unknown, error setting GLOP parameters";
383 if (solve_parameters.cuts() != EMPHASIS_UNSPECIFIED) {
384 warnings.push_back(absl::StrCat(
385 "GLOP does not support 'cuts' parameters, but cuts was set to: ",
388 if (solve_parameters.heuristics() != EMPHASIS_UNSPECIFIED) {
390 absl::StrCat(
"GLOP does not support 'heuristics' parameter, but "
391 "heuristics was set to: ",
394 if (solve_parameters.has_cutoff_limit()) {
395 warnings.push_back(
"GLOP does not support 'cutoff_limit' parameter");
397 if (solve_parameters.has_objective_limit()) {
398 warnings.push_back(
"GLOP does not support 'objective_limit' parameter");
400 if (solve_parameters.has_best_bound_limit()) {
401 warnings.push_back(
"GLOP does not support 'best_bound_limit' parameter");
403 if (solve_parameters.has_solution_limit()) {
404 warnings.push_back(
"GLOP does not support 'solution_limit' parameter");
406 if (!warnings.empty()) {
407 return absl::InvalidArgumentError(absl::StrJoin(warnings,
"; "));
416 <<
"invalid GlopParameters generated from SolveParametersProto";
421 template <
typename IndexType>
423 const std::vector<int64_t>& ids_in_order,
424 const absl::flat_hash_map<int64_t, IndexType>& id_map,
426 const SparseVectorFilterProto& filter) {
428 SparseDoubleVectorProto result;
429 for (
const int64_t variable_id : ids_in_order) {
430 const double value = values[id_map.at(variable_id)];
432 result.add_ids(variable_id);
433 result.add_values(
value);
440 template <
typename ValueType>
442 switch (glop_basis_status) {
443 case ValueType::BASIC:
444 return BasisStatusProto::BASIS_STATUS_BASIC;
445 case ValueType::FIXED_VALUE:
446 return BasisStatusProto::BASIS_STATUS_FIXED_VALUE;
447 case ValueType::AT_LOWER_BOUND:
448 return BasisStatusProto::BASIS_STATUS_AT_LOWER_BOUND;
449 case ValueType::AT_UPPER_BOUND:
450 return BasisStatusProto::BASIS_STATUS_AT_UPPER_BOUND;
451 case ValueType::FREE:
452 return BasisStatusProto::BASIS_STATUS_FREE;
454 return BasisStatusProto::BASIS_STATUS_UNSPECIFIED;
457 template <
typename IndexType,
typename ValueType>
459 const std::vector<int64_t>& ids_in_order,
460 const absl::flat_hash_map<int64_t, IndexType>& id_map,
462 SparseBasisStatusVector result;
463 for (
const int64_t variable_id : ids_in_order) {
464 const ValueType
value = values[id_map.at(variable_id)];
465 result.add_ids(variable_id);
472 template <
typename ValueType>
474 switch (basis_status) {
475 case BASIS_STATUS_BASIC:
476 return ValueType::BASIC;
477 case BASIS_STATUS_FIXED_VALUE:
478 return ValueType::FIXED_VALUE;
479 case BASIS_STATUS_AT_LOWER_BOUND:
480 return ValueType::AT_LOWER_BOUND;
481 case BASIS_STATUS_AT_UPPER_BOUND:
482 return ValueType::AT_UPPER_BOUND;
483 case BASIS_STATUS_FREE:
484 return ValueType::FREE;
486 LOG(FATAL) <<
"Unexpected invalid initial_basis.";
487 return ValueType::FREE;
491 template <
typename T>
493 const absl::flat_hash_map<int64_t, T>& id_map) {
494 std::vector<int64_t> sorted;
495 sorted.reserve(id_map.size());
496 for (
const auto& entry : id_map) {
497 sorted.emplace_back(entry.first);
499 std::sort(sorted.begin(), sorted.end());
506 template <
typename T>
508 const absl::flat_hash_map<int64_t, T>& id_map) {
510 constexpr int64_t kEmptyId = -1;
512 for (
const auto& [
id,
index] : id_map) {
514 CHECK_EQ(index_to_id[
index], kEmptyId);
515 index_to_id[
index] = id;
525 InvertedBounds GlopSolver::ListInvertedBounds()
const {
527 std::vector<glop::ColIndex> inverted_columns;
528 const glop::ColIndex num_cols = linear_program_.num_variables();
529 for (glop::ColIndex
col(0);
col < num_cols; ++
col) {
530 if (linear_program_.variable_lower_bounds()[
col] >
531 linear_program_.variable_upper_bounds()[
col]) {
535 std::vector<glop::RowIndex> inverted_rows;
536 const glop::RowIndex num_rows = linear_program_.num_constraints();
537 for (glop::RowIndex
row(0);
row < num_rows; ++
row) {
538 if (linear_program_.constraint_lower_bounds()[
row] >
539 linear_program_.constraint_upper_bounds()[
row]) {
540 inverted_rows.push_back(
row);
546 InvertedBounds inverted_bounds;
547 if (!inverted_columns.empty()) {
548 const glop::StrictITIVector<glop::ColIndex, int64_t> ids =
550 CHECK_EQ(ids.size(), num_cols);
551 inverted_bounds.variables.reserve(inverted_columns.size());
552 for (
const glop::ColIndex
col : inverted_columns) {
553 inverted_bounds.variables.push_back(ids[
col]);
556 if (!inverted_rows.empty()) {
557 const glop::StrictITIVector<glop::RowIndex, int64_t> ids =
559 CHECK_EQ(ids.size(), num_rows);
560 inverted_bounds.linear_constraints.reserve(inverted_rows.size());
561 for (
const glop::RowIndex
row : inverted_rows) {
562 inverted_bounds.linear_constraints.push_back(ids[
row]);
566 return inverted_bounds;
570 const ModelSolveParametersProto& model_parameters,
571 SolveResultProto& solve_result) {
576 const bool phase_I_solution_available =
577 (
status == glop::ProblemStatus::INIT) &&
578 (lp_solver_.GetNumberOfSimplexIterations() > 0);
580 status != glop::ProblemStatus::PRIMAL_FEASIBLE &&
581 status != glop::ProblemStatus::DUAL_FEASIBLE &&
582 status != glop::ProblemStatus::PRIMAL_UNBOUNDED &&
583 status != glop::ProblemStatus::DUAL_UNBOUNDED &&
584 !phase_I_solution_available) {
588 auto sorted_constraints =
GetSortedIs(linear_constraints_);
589 SolutionProto*
const solution = solve_result.add_solutions();
590 BasisProto*
const basis = solution->mutable_basis();
591 PrimalSolutionProto*
const primal_solution =
592 solution->mutable_primal_solution();
593 DualSolutionProto*
const dual_solution = solution->mutable_dual_solution();
599 primal_solution->set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
600 basis->set_basic_dual_feasibility(SOLUTION_STATUS_FEASIBLE);
601 dual_solution->set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
602 }
else if (
status == glop::ProblemStatus::PRIMAL_FEASIBLE) {
607 primal_solution->set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
608 dual_solution->set_feasibility_status(SOLUTION_STATUS_UNDETERMINED);
609 basis->set_basic_dual_feasibility(SOLUTION_STATUS_INFEASIBLE);
610 }
else if (
status == glop::ProblemStatus::DUAL_FEASIBLE) {
617 primal_solution->set_feasibility_status(SOLUTION_STATUS_INFEASIBLE);
618 dual_solution->set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
619 basis->set_basic_dual_feasibility(SOLUTION_STATUS_FEASIBLE);
622 if (lp_solver_.GetParameters().use_dual_simplex()) {
628 primal_solution->set_feasibility_status(SOLUTION_STATUS_UNDETERMINED);
629 dual_solution->set_feasibility_status(SOLUTION_STATUS_INFEASIBLE);
630 basis->set_basic_dual_feasibility(SOLUTION_STATUS_INFEASIBLE);
633 primal_solution->set_feasibility_status(SOLUTION_STATUS_INFEASIBLE);
634 dual_solution->set_feasibility_status(SOLUTION_STATUS_UNDETERMINED);
635 basis->set_basic_dual_feasibility(SOLUTION_STATUS_UNDETERMINED);
640 primal_solution->set_objective_value(lp_solver_.GetObjectiveValue());
641 if (basis->basic_dual_feasibility() == SOLUTION_STATUS_FEASIBLE) {
644 dual_solution->set_objective_value(primal_solution->objective_value());
648 *basis->mutable_constraint_status() = *basis->mutable_variable_status() =
650 lp_solver_.variable_statuses());
651 *basis->mutable_constraint_status() =
653 lp_solver_.constraint_statuses());
656 sorted_variables, variables_, lp_solver_.variable_values(),
657 model_parameters.variable_values_filter());
660 sorted_constraints, linear_constraints_, lp_solver_.dual_values(),
661 model_parameters.dual_values_filter());
663 sorted_variables, variables_, lp_solver_.reduced_costs(),
664 model_parameters.reduced_costs_filter());
666 if (!lp_solver_.primal_ray().empty()) {
667 PrimalRayProto*
const primal_ray = solve_result.add_primal_rays();
670 sorted_variables, variables_, lp_solver_.primal_ray(),
671 model_parameters.variable_values_filter());
673 if (!lp_solver_.constraints_dual_ray().empty() &&
674 !lp_solver_.variable_bounds_dual_ray().empty()) {
675 DualRayProto*
const dual_ray = solve_result.add_dual_rays();
676 *dual_ray->mutable_dual_values() =
678 lp_solver_.constraints_dual_ray(),
679 model_parameters.dual_values_filter());
681 sorted_variables, variables_, lp_solver_.variable_bounds_dual_ray(),
682 model_parameters.reduced_costs_filter());
687 const absl::Duration solve_time,
688 SolveStatsProto& solve_stats) {
689 const bool is_maximize = linear_program_.IsMaximizationProblem();
692 solve_stats.mutable_problem_status()->set_primal_status(
693 FEASIBILITY_STATUS_UNDETERMINED);
694 solve_stats.set_best_primal_bound(is_maximize ? -
kInf :
kInf);
695 solve_stats.mutable_problem_status()->set_dual_status(
696 FEASIBILITY_STATUS_UNDETERMINED);
697 solve_stats.set_best_dual_bound(is_maximize ?
kInf : -
kInf);
702 solve_stats.mutable_problem_status()->set_primal_status(
703 FEASIBILITY_STATUS_FEASIBLE);
704 solve_stats.mutable_problem_status()->set_dual_status(
705 FEASIBILITY_STATUS_FEASIBLE);
706 solve_stats.set_best_primal_bound(lp_solver_.GetObjectiveValue());
707 solve_stats.set_best_dual_bound(lp_solver_.GetObjectiveValue());
709 case glop::ProblemStatus::PRIMAL_INFEASIBLE:
710 solve_stats.mutable_problem_status()->set_primal_status(
711 FEASIBILITY_STATUS_INFEASIBLE);
713 case glop::ProblemStatus::DUAL_UNBOUNDED:
714 solve_stats.mutable_problem_status()->set_primal_status(
715 FEASIBILITY_STATUS_INFEASIBLE);
716 solve_stats.mutable_problem_status()->set_dual_status(
717 FEASIBILITY_STATUS_FEASIBLE);
718 solve_stats.set_best_dual_bound(is_maximize ? -
kInf :
kInf);
720 case glop::ProblemStatus::PRIMAL_UNBOUNDED:
721 solve_stats.mutable_problem_status()->set_primal_status(
722 FEASIBILITY_STATUS_FEASIBLE);
723 solve_stats.mutable_problem_status()->set_dual_status(
724 FEASIBILITY_STATUS_INFEASIBLE);
725 solve_stats.set_best_primal_bound(is_maximize ?
kInf : -
kInf);
727 case glop::ProblemStatus::DUAL_INFEASIBLE:
728 solve_stats.mutable_problem_status()->set_dual_status(
729 FEASIBILITY_STATUS_INFEASIBLE);
731 case glop::ProblemStatus::INFEASIBLE_OR_UNBOUNDED:
732 solve_stats.mutable_problem_status()->set_primal_or_dual_infeasible(
true);
734 case glop::ProblemStatus::PRIMAL_FEASIBLE:
735 solve_stats.mutable_problem_status()->set_primal_status(
736 FEASIBILITY_STATUS_FEASIBLE);
737 solve_stats.set_best_primal_bound(lp_solver_.GetObjectiveValue());
739 case glop::ProblemStatus::DUAL_FEASIBLE:
740 solve_stats.mutable_problem_status()->set_dual_status(
741 FEASIBILITY_STATUS_FEASIBLE);
742 solve_stats.set_best_dual_bound(lp_solver_.GetObjectiveValue());
744 case glop::ProblemStatus::INIT:
745 case glop::ProblemStatus::IMPRECISE:
750 case glop::ProblemStatus::INVALID_PROBLEM:
751 return absl::InternalError(
752 absl::StrCat(
"Unexpected GLOP termination reason: ",
757 solve_stats.set_simplex_iterations(lp_solver_.GetNumberOfSimplexIterations());
759 solve_time, solve_stats.mutable_solve_time()));
761 return absl::OkStatus();
764 absl::StatusOr<SolveResultProto> GlopSolver::MakeSolveResult(
766 const ModelSolveParametersProto& model_parameters,
767 const SolveInterrupter*
const interrupter,
768 const absl::Duration solve_time) {
769 SolveResultProto solve_result;
771 BuildTermination(
status, interrupter));
772 FillSolution(
status, model_parameters, solve_result);
774 FillSolveStats(
status, solve_time, *solve_result.mutable_solve_stats()));
778 void GlopSolver::SetGlopBasis(
const BasisProto& basis) {
780 for (
const auto [
id,
value] :
MakeView(basis.variable_status())) {
781 variable_statuses[variables_.at(
id)] =
782 ToGlopBasisStatus<glop::VariableStatus>(
783 static_cast<BasisStatusProto
>(
value));
786 linear_program_.num_constraints());
787 for (
const auto [
id,
value] :
MakeView(basis.constraint_status())) {
788 constraint_statuses[linear_constraints_.at(
id)] =
789 ToGlopBasisStatus<glop::ConstraintStatus>(
790 static_cast<BasisStatusProto
>(
value));
792 lp_solver_.SetInitialBasis(variable_statuses, constraint_statuses);
797 const ModelSolveParametersProto& model_parameters,
799 const CallbackRegistrationProto& callback_registration,
const Callback cb,
804 const absl::Time
start = absl::Now();
806 const glop::GlopParameters glop_parameters,
807 MergeSolveParameters(
809 model_parameters.has_initial_basis(),
810 message_cb !=
nullptr));
811 lp_solver_.SetParameters(glop_parameters);
813 if (model_parameters.has_initial_basis()) {
814 SetGlopBasis(model_parameters.initial_basis());
817 std::atomic<bool> interrupt_solve =
false;
819 TimeLimit::FromParameters(lp_solver_.GetParameters());
820 time_limit->RegisterExternalBooleanAsLimit(&interrupt_solve);
823 CHECK_NE(interrupter,
nullptr);
824 interrupt_solve =
true;
827 if (message_cb !=
nullptr) {
835 CHECK_EQ(lp_solver_.GetSolverLogger().NumInfoLoggingCallbacks(), 0);
836 lp_solver_.GetSolverLogger().AddInfoLoggingCallback(
837 [&](
const std::string&
message) {
838 message_cb(absl::StrSplit(
message,
'\n'));
842 if (message_cb !=
nullptr) {
844 CHECK_EQ(lp_solver_.GetSolverLogger().NumInfoLoggingCallbacks(), 1);
845 lp_solver_.GetSolverLogger().ClearInfoLoggingCallbacks();
855 lp_solver_.SolveWithTimeLimit(linear_program_,
time_limit.get());
856 const absl::Duration solve_time = absl::Now() -
start;
857 return MakeSolveResult(
status, model_parameters, interrupter, solve_time);
860 absl::StatusOr<std::unique_ptr<SolverInterface>> GlopSolver::New(
863 auto solver = absl::WrapUnique(
new GlopSolver);
867 solver->linear_program_.SetDcheckBounds(
false);
869 solver->linear_program_.SetName(
model.name());
870 solver->linear_program_.SetMaximizationProblem(
model.objective().maximize());
871 solver->linear_program_.SetObjectiveOffset(
model.objective().offset());
873 solver->AddVariables(
model.variables());
874 solver->SetOrUpdateObjectiveCoefficients(
875 model.objective().linear_coefficients());
877 solver->AddLinearConstraints(
model.linear_constraints());
878 solver->SetOrUpdateConstraintMatrix(
model.linear_constraint_matrix());
879 solver->linear_program_.CleanUp();
883 absl::StatusOr<bool> GlopSolver::Update(
const ModelUpdateProto& model_update) {
888 if (model_update.objective_updates().has_direction_update()) {
889 linear_program_.SetMaximizationProblem(
890 model_update.objective_updates().direction_update());
892 if (model_update.objective_updates().has_offset_update()) {
893 linear_program_.SetObjectiveOffset(
894 model_update.objective_updates().offset_update());
897 DeleteVariables(model_update.deleted_variable_ids());
898 AddVariables(model_update.new_variables());
900 SetOrUpdateObjectiveCoefficients(
901 model_update.objective_updates().linear_coefficients());
902 UpdateVariableBounds(model_update.variable_updates());
904 DeleteLinearConstraints(model_update.deleted_linear_constraint_ids());
905 AddLinearConstraints(model_update.new_linear_constraints());
906 UpdateLinearConstraintBounds(model_update.linear_constraint_updates());
908 SetOrUpdateConstraintMatrix(model_update.linear_constraint_matrix_updates());
910 linear_program_.CleanUp();
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
void push_back(const value_type &x)
std::function< void(const std::vector< std::string > &)> MessageCallback
std::function< absl::StatusOr< CallbackResultProto >(const CallbackDataProto &)> Callback
bool AcceptsAndUpdate(const int64_t id, const Value &value)
ModelSharedTimeLimit * time_limit
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
std::string ValidateParameters(const GlopParameters ¶ms)
std::string GetProblemStatusString(ProblemStatus problem_status)
StrictITIVector< ColIndex, VariableStatus > VariableStatusRow
StrictITIVector< RowIndex, ConstraintStatus > ConstraintStatusColumn
@ INFEASIBLE_OR_UNBOUNDED
StrictITIVector< RowIndex, bool > DenseBooleanColumn
TerminationProto FeasibleTermination(const LimitProto limit, const absl::string_view detail)
absl::Status CheckRegisteredCallbackEvents(const CallbackRegistrationProto ®istration, const absl::flat_hash_set< CallbackEventProto > &supported_events)
int NumMatrixNonzeros(const SparseDoubleMatrixProto &matrix)
void UpdateIdIndexMap(glop::StrictITIVector< IndexType, bool > indices_to_delete, IndexType num_indices, absl::flat_hash_map< int64_t, IndexType > &id_index_map)
int NumVariables(const VariablesProto &variables)
absl::Status ModelIsSupported(const ModelProto &model, const SupportedProblemStructures &support_menu, const absl::string_view solver_name)
SparseDoubleVectorProto FillSparseDoubleVector(const std::vector< int64_t > &ids_in_order, const absl::flat_hash_map< int64_t, IndexType > &id_map, const glop::StrictITIVector< IndexType, glop::Fractional > &values, const SparseVectorFilterProto &filter)
BasisStatusProto FromGlopBasisStatus(const ValueType glop_basis_status)
absl::StatusOr< SolveResult > Solve(const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolverInitArguments &init_args)
ValueType ToGlopBasisStatus(const BasisStatusProto basis_status)
bool UpdateIsSupported(const ModelUpdateProto &update, const SupportedProblemStructures &support_menu)
std::vector< int64_t > GetSortedIs(const absl::flat_hash_map< int64_t, T > &id_map)
TerminationProto NoSolutionFoundTermination(const LimitProto limit, const absl::string_view detail)
int NumConstraints(const LinearConstraintsProto &linear_constraints)
TerminationProto TerminateForReason(const TerminationReasonProto reason, const absl::string_view detail)
glop::StrictITIVector< T, int64_t > IndexToId(const absl::flat_hash_map< int64_t, T > &id_map)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
SparseBasisStatusVector FillSparseBasisStatusVector(const std::vector< int64_t > &ids_in_order, const absl::flat_hash_map< int64_t, IndexType > &id_map, const glop::StrictITIVector< IndexType, ValueType > &values)
Collection of objects used to extend the Constraint Solver library.
std::string ProtoEnumToString(ProtoEnumType enum_value)
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)
StatusBuilder InvalidArgumentErrorBuilder()
#define MATH_OPT_REGISTER_SOLVER(solver_type, solver_factory)