26#include "absl/container/flat_hash_map.h"
27#include "absl/container/flat_hash_set.h"
28#include "absl/memory/memory.h"
29#include "absl/status/status.h"
30#include "absl/status/statusor.h"
31#include "absl/strings/str_cat.h"
32#include "absl/strings/str_join.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"
44#include "ortools/gscip/gscip.pb.h"
47#include "ortools/math_opt/callback.pb.h"
53#include "ortools/math_opt/model.pb.h"
54#include "ortools/math_opt/model_parameters.pb.h"
55#include "ortools/math_opt/model_update.pb.h"
56#include "ortools/math_opt/parameters.pb.h"
57#include "ortools/math_opt/result.pb.h"
58#include "ortools/math_opt/solution.pb.h"
61#include "ortools/math_opt/sparse_containers.pb.h"
65#include "scip/type_cons.h"
66#include "scip/type_event.h"
67#include "scip/type_var.h"
74constexpr double kInf = std::numeric_limits<double>::infinity();
76int64_t SafeId(
const VariablesProto& variables,
int index) {
77 if (variables.ids().empty()) {
80 return variables.ids(
index);
83const std::string& EmptyString() {
84 static const std::string*
const empty_string =
new std::string;
88const std::string& SafeName(
const VariablesProto& variables,
int index) {
89 if (variables.names().empty()) {
92 return variables.names(
index);
95int64_t SafeId(
const LinearConstraintsProto& linear_constraints,
int index) {
96 if (linear_constraints.ids().empty()) {
99 return linear_constraints.ids(
index);
102const std::string& SafeName(
const LinearConstraintsProto& linear_constraints,
104 if (linear_constraints.names().empty()) {
105 return EmptyString();
107 return linear_constraints.names(
index);
110absl::flat_hash_map<int64_t, double> SparseDoubleVectorAsMap(
111 const SparseDoubleVectorProto& vector) {
112 CHECK_EQ(vector.ids_size(), vector.values_size());
113 absl::flat_hash_map<int64_t, double> result;
114 result.reserve(vector.ids_size());
115 for (
int i = 0; i < vector.ids_size(); ++i) {
116 result[vector.ids(i)] = vector.values(i);
125inline int FindRowStart(
const SparseDoubleMatrixProto& matrix,
126 const int64_t row_id,
const int scan_start) {
127 int result = scan_start;
128 while (result < matrix.row_ids_size() && matrix.row_ids(result) < row_id) {
134struct LinearConstraintView {
149class LinearConstraintIterator {
151 LinearConstraintIterator(
152 const LinearConstraintsProto* linear_constraints,
153 const SparseDoubleMatrixProto* linear_constraint_matrix)
157 const int64_t first_constraint = SafeId(*linear_constraints_, 0);
159 FindRowStart(*linear_constraint_matrix_, first_constraint, 0);
160 matrix_end_ = FindRowStart(*linear_constraint_matrix_,
161 first_constraint + 1, matrix_start_);
164 matrix_end_ = matrix_start_;
168 bool IsDone()
const {
173 LinearConstraintView Current()
const {
175 LinearConstraintView result;
176 result.lower_bound = linear_constraints_->lower_bounds(current_con_);
177 result.upper_bound = linear_constraints_->upper_bounds(current_con_);
178 result.name = SafeName(*linear_constraints_, current_con_);
179 result.linear_constraint_id = SafeId(*linear_constraints_, current_con_);
181 const auto vars_begin = linear_constraint_matrix_->column_ids().data();
182 result.variable_ids = absl::MakeConstSpan(vars_begin + matrix_start_,
183 vars_begin + matrix_end_);
184 const auto coefficients_begins =
185 linear_constraint_matrix_->coefficients().data();
186 result.coefficients = absl::MakeConstSpan(
187 coefficients_begins + matrix_start_, coefficients_begins + matrix_end_);
197 matrix_end_ = matrix_start_;
200 const int64_t current_row_id = SafeId(*linear_constraints_, current_con_);
202 FindRowStart(*linear_constraint_matrix_, current_row_id, matrix_end_);
204 matrix_end_ = FindRowStart(*linear_constraint_matrix_, current_row_id + 1,
210 const LinearConstraintsProto*
const linear_constraints_;
212 const SparseDoubleMatrixProto*
const linear_constraint_matrix_;
215 int current_con_ = 0;
228 int matrix_start_ = 0;
232inline GScipVarType GScipVarTypeFromIsInteger(
const bool is_integer) {
259class LazyInitialized {
262 explicit LazyInitialized(std::function<T()> initializer)
266 const T& GetOrCreate() {
268 value_ = initializer_();
274 const std::function<T()> initializer_;
275 std::optional<T> value_;
280 const std::vector<int64_t>& ids_in_order,
281 const absl::flat_hash_map<int64_t, T>& id_map,
282 const absl::flat_hash_map<T, double>& value_map,
283 const SparseVectorFilterProto& filter) {
284 SparseVectorFilterPredicate predicate(filter);
285 SparseDoubleVectorProto result;
286 for (
const int64_t variable_id : ids_in_order) {
287 const double value = value_map.at(id_map.at(variable_id));
288 if (predicate.AcceptsAndUpdate(variable_id,
value)) {
289 result.add_ids(variable_id);
290 result.add_values(
value);
298absl::Status GScipSolver::AddVariables(
299 const VariablesProto& variables,
300 const absl::flat_hash_map<int64_t, double>& linear_objective_coefficients) {
302 const int64_t
id = SafeId(variables, i);
307 const bool inverted_bounds =
308 variables.lower_bounds(i) > variables.upper_bounds(i);
312 variables.lower_bounds(i),
313 inverted_bounds ? variables.lower_bounds(i)
314 : variables.upper_bounds(i),
316 GScipVarTypeFromIsInteger(variables.integers(i)),
317 SafeName(variables, i)));
318 if (inverted_bounds) {
323 return absl::OkStatus();
326absl::Status GScipSolver::UpdateVariables(
327 const VariableUpdatesProto& variable_updates) {
328 for (
const auto [
id, lb] :
MakeView(variable_updates.lower_bounds())) {
331 for (
const auto [
id, ub] :
MakeView(variable_updates.upper_bounds())) {
334 for (
const auto [
id, is_integer] :
MakeView(variable_updates.integers())) {
336 GScipVarTypeFromIsInteger(is_integer)));
338 return absl::OkStatus();
341absl::Status GScipSolver::AddLinearConstraints(
342 const LinearConstraintsProto& linear_constraints,
343 const SparseDoubleMatrixProto& linear_constraint_matrix) {
344 for (LinearConstraintIterator lin_con_it(&linear_constraints,
345 &linear_constraint_matrix);
346 !lin_con_it.IsDone(); lin_con_it.Next()) {
347 const LinearConstraintView current = lin_con_it.Current();
349 GScipLinearRange range;
350 range.lower_bound = current.lower_bound;
351 range.upper_bound = current.upper_bound;
352 range.coefficients = std::vector<double>(current.coefficients.begin(),
353 current.coefficients.end());
354 range.variables.reserve(current.variable_ids.size());
355 for (
const int64_t var_id : current.variable_ids) {
356 range.variables.push_back(variables_.at(var_id));
359 SCIP_CONS*
const scip_con,
360 gscip_->AddLinearConstraint(range, std::string(current.name)));
364 return absl::OkStatus();
367absl::Status GScipSolver::UpdateLinearConstraints(
368 const LinearConstraintUpdatesProto linear_constraint_updates,
369 const SparseDoubleMatrixProto& linear_constraint_matrix,
370 const std::optional<int64_t> first_new_var_id,
371 const std::optional<int64_t> first_new_cstr_id) {
372 for (
const auto [
id, lb] :
373 MakeView(linear_constraint_updates.lower_bounds())) {
375 gscip_->SetLinearConstraintLb(linear_constraints_.at(
id), lb));
377 for (
const auto [
id, ub] :
378 MakeView(linear_constraint_updates.upper_bounds())) {
380 gscip_->SetLinearConstraintUb(linear_constraints_.at(
id), ub));
383 linear_constraint_matrix, 0,
384 first_new_cstr_id, 0,
386 for (
const auto& [var_id,
value] : var_coeffs) {
388 linear_constraints_.at(lin_con_id), variables_.at(var_id),
value));
391 return absl::OkStatus();
397 return GScipParameters::OFF;
399 return GScipParameters::FAST;
400 case EMPHASIS_MEDIUM:
401 case EMPHASIS_UNSPECIFIED:
402 return GScipParameters::DEFAULT_META_PARAM_VALUE;
404 case EMPHASIS_VERY_HIGH:
405 return GScipParameters::AGGRESSIVE;
407 LOG(
FATAL) <<
"Unsupported MathOpt Emphasis value: "
409 <<
" unknown, error setting gSCIP parameters";
414 const SolveParametersProto& solve_parameters) {
419 GScipParameters result;
420 std::vector<std::string> warnings;
426 if (solve_parameters.has_time_limit()) {
432 if (solve_parameters.has_threads()) {
436 if (solve_parameters.has_relative_gap_tolerance()) {
437 (*result.mutable_real_params())[
"limits/gap"] =
438 solve_parameters.relative_gap_tolerance();
441 if (solve_parameters.has_absolute_gap_tolerance()) {
442 (*result.mutable_real_params())[
"limits/absgap"] =
443 solve_parameters.absolute_gap_tolerance();
445 if (solve_parameters.has_node_limit()) {
446 (*result.mutable_long_params())[
"limits/totalnodes"] =
447 solve_parameters.node_limit();
450 if (solve_parameters.has_objective_limit()) {
451 warnings.push_back(
"parameter objective_limit not supported for gSCIP.");
453 if (solve_parameters.has_best_bound_limit()) {
454 warnings.push_back(
"parameter best_bound_limit not supported for gSCIP.");
457 if (solve_parameters.has_cutoff_limit()) {
458 result.set_objective_limit(solve_parameters.cutoff_limit());
461 if (solve_parameters.has_solution_limit()) {
462 (*result.mutable_int_params())[
"limits/solutions"] =
463 solve_parameters.solution_limit();
472 result.set_silence_output(!solve_parameters.enable_output());
474 if (solve_parameters.has_random_seed()) {
478 if (solve_parameters.lp_algorithm() != LP_ALGORITHM_UNSPECIFIED) {
480 switch (solve_parameters.lp_algorithm()) {
481 case LP_ALGORITHM_PRIMAL_SIMPLEX:
484 case LP_ALGORITHM_DUAL_SIMPLEX:
487 case LP_ALGORITHM_BARRIER:
493 <<
" unknown, error setting gSCIP parameters";
495 (*result.mutable_char_params())[
"lp/initalgorithm"] = alg;
498 if (solve_parameters.cuts() != EMPHASIS_UNSPECIFIED) {
501 if (solve_parameters.heuristics() != EMPHASIS_UNSPECIFIED) {
502 result.set_heuristics(
505 if (solve_parameters.presolve() != EMPHASIS_UNSPECIFIED) {
508 if (solve_parameters.scaling() != EMPHASIS_UNSPECIFIED) {
510 switch (solve_parameters.scaling()) {
515 case EMPHASIS_MEDIUM:
519 case EMPHASIS_VERY_HIGH:
525 <<
" unknown, error setting gSCIP parameters";
527 (*result.mutable_int_params())[
"lp/scaling"] = scaling_value;
530 result.MergeFrom(solve_parameters.gscip());
532 if (!warnings.empty()) {
533 return absl::InvalidArgumentError(absl::StrJoin(warnings,
"; "));
540std::string JoinDetails(
const std::string& gscip_detail,
541 const std::string& math_opt_detail) {
542 if (gscip_detail.empty()) {
543 return math_opt_detail;
545 if (math_opt_detail.empty()) {
548 return absl::StrCat(gscip_detail,
"; ", math_opt_detail);
551ProblemStatusProto GetProblemStatusProto(
const GScipOutput::Status gscip_status,
552 const bool has_feasible_solution,
553 const bool has_finite_dual_bound,
554 const bool was_cutoff) {
555 ProblemStatusProto problem_status;
556 if (has_feasible_solution) {
557 problem_status.set_primal_status(FEASIBILITY_STATUS_FEASIBLE);
559 problem_status.set_primal_status(FEASIBILITY_STATUS_UNDETERMINED);
561 problem_status.set_dual_status(FEASIBILITY_STATUS_UNDETERMINED);
563 switch (gscip_status) {
564 case GScipOutput::OPTIMAL:
565 problem_status.set_dual_status(FEASIBILITY_STATUS_FEASIBLE);
567 case GScipOutput::INFEASIBLE:
569 problem_status.set_primal_status(FEASIBILITY_STATUS_INFEASIBLE);
572 case GScipOutput::UNBOUNDED:
573 problem_status.set_dual_status(FEASIBILITY_STATUS_INFEASIBLE);
575 case GScipOutput::INF_OR_UNBD:
576 problem_status.set_primal_or_dual_infeasible(
true);
581 if (has_finite_dual_bound) {
582 problem_status.set_dual_status(FEASIBILITY_STATUS_FEASIBLE);
584 return problem_status;
587absl::StatusOr<TerminationProto> ConvertTerminationReason(
588 const GScipOutput::Status gscip_status,
589 const std::string& gscip_status_detail,
const bool has_feasible_solution,
590 const bool had_cutoff) {
591 switch (gscip_status) {
592 case GScipOutput::USER_INTERRUPT:
594 LIMIT_INTERRUPTED, has_feasible_solution,
595 JoinDetails(gscip_status_detail,
596 "underlying gSCIP status: USER_INTERRUPT"));
597 case GScipOutput::NODE_LIMIT:
599 LIMIT_NODE, has_feasible_solution,
600 JoinDetails(gscip_status_detail,
601 "underlying gSCIP status: NODE_LIMIT"));
602 case GScipOutput::TOTAL_NODE_LIMIT:
604 LIMIT_NODE, has_feasible_solution,
605 JoinDetails(gscip_status_detail,
606 "underlying gSCIP status: TOTAL_NODE_LIMIT"));
607 case GScipOutput::STALL_NODE_LIMIT:
609 has_feasible_solution,
610 gscip_status_detail);
611 case GScipOutput::TIME_LIMIT:
613 gscip_status_detail);
614 case GScipOutput::MEM_LIMIT:
616 gscip_status_detail);
617 case GScipOutput::SOL_LIMIT:
619 LIMIT_SOLUTION, has_feasible_solution,
620 JoinDetails(gscip_status_detail,
621 "underlying gSCIP status: SOL_LIMIT"));
622 case GScipOutput::BEST_SOL_LIMIT:
624 LIMIT_SOLUTION, has_feasible_solution,
625 JoinDetails(gscip_status_detail,
626 "underlying gSCIP status: BEST_SOL_LIMIT"));
627 case GScipOutput::RESTART_LIMIT:
629 LIMIT_OTHER, has_feasible_solution,
630 JoinDetails(gscip_status_detail,
631 "underlying gSCIP status: RESTART_LIMIT"));
632 case GScipOutput::OPTIMAL:
634 TERMINATION_REASON_OPTIMAL,
635 JoinDetails(gscip_status_detail,
"underlying gSCIP status: OPTIMAL"));
636 case GScipOutput::GAP_LIMIT:
638 TERMINATION_REASON_OPTIMAL,
639 JoinDetails(gscip_status_detail,
640 "underlying gSCIP status: GAP_LIMIT"));
641 case GScipOutput::INFEASIBLE:
644 false, gscip_status_detail);
647 gscip_status_detail);
649 case GScipOutput::UNBOUNDED: {
650 if (has_feasible_solution) {
652 TERMINATION_REASON_UNBOUNDED,
653 JoinDetails(gscip_status_detail,
654 "underlying gSCIP status was UNBOUNDED, both primal "
655 "ray and feasible solution are present"));
658 TERMINATION_REASON_INFEASIBLE_OR_UNBOUNDED,
661 "underlying gSCIP status was UNBOUNDED, but only primal ray "
662 "was given, no feasible solution was found"));
666 case GScipOutput::INF_OR_UNBD:
668 TERMINATION_REASON_INFEASIBLE_OR_UNBOUNDED,
669 JoinDetails(gscip_status_detail,
670 "underlying gSCIP status: INF_OR_UNBD"));
672 case GScipOutput::TERMINATE:
674 LIMIT_INTERRUPTED, has_feasible_solution,
675 JoinDetails(gscip_status_detail,
676 "underlying gSCIP status: TERMINATE"));
677 case GScipOutput::INVALID_SOLVER_PARAMETERS:
678 return absl::InvalidArgumentError(gscip_status_detail);
679 case GScipOutput::UNKNOWN:
680 return absl::InternalError(JoinDetails(
681 gscip_status_detail,
"Unexpected GScipOutput.status: UNKNOWN"));
683 return absl::InternalError(JoinDetails(
684 gscip_status_detail, absl::StrCat(
"Missing GScipOutput.status case: ",
691absl::StatusOr<SolveResultProto> GScipSolver::CreateSolveResultProto(
692 GScipResult gscip_result,
const ModelSolveParametersProto& model_parameters,
693 const std::optional<double> cutoff) {
694 SolveResultProto solve_result;
695 const bool is_maximize = gscip_->ObjectiveIsMaximize();
698 const auto meets_cutoff = [cutoff, is_maximize](
const double obj_value) {
699 if (!cutoff.has_value()) {
703 return obj_value >= *cutoff;
705 return obj_value <= *cutoff;
709 LazyInitialized<std::vector<int64_t>> sorted_variables([&]() {
710 std::vector<int64_t> sorted;
711 sorted.reserve(variables_.size());
712 for (
const auto& entry : variables_) {
713 sorted.emplace_back(entry.first);
715 std::sort(sorted.begin(), sorted.end());
718 CHECK_EQ(gscip_result.solutions.size(), gscip_result.objective_values.size());
719 for (
int i = 0; i < gscip_result.solutions.size(); ++i) {
721 if (!meets_cutoff(gscip_result.objective_values[i])) {
724 SolutionProto*
const solution = solve_result.add_solutions();
725 PrimalSolutionProto*
const primal_solution =
726 solution->mutable_primal_solution();
727 primal_solution->set_objective_value(gscip_result.objective_values[i]);
728 primal_solution->set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
730 sorted_variables.GetOrCreate(), variables_, gscip_result.solutions[i],
731 model_parameters.variable_values_filter());
733 if (!gscip_result.primal_ray.empty()) {
734 *solve_result.add_primal_rays()->mutable_variable_values() =
736 gscip_result.primal_ray,
737 model_parameters.variable_values_filter());
739 const bool has_feasible_solution = solve_result.solutions_size() > 0;
741 *solve_result.mutable_termination(),
742 ConvertTerminationReason(gscip_result.gscip_output.status(),
743 gscip_result.gscip_output.status_detail(),
744 has_feasible_solution,
745 cutoff.has_value()));
746 *solve_result.mutable_solve_stats()->mutable_problem_status() =
747 GetProblemStatusProto(
748 gscip_result.gscip_output.status(),
749 has_feasible_solution,
751 std::isfinite(gscip_result.gscip_output.stats().best_bound()),
752 solve_result.termination().limit() == LIMIT_CUTOFF);
753 SolveStatsProto*
const common_stats = solve_result.mutable_solve_stats();
754 const GScipSolvingStats& gscip_stats = gscip_result.gscip_output.stats();
755 common_stats->set_best_dual_bound(gscip_stats.best_bound());
757 if (has_feasible_solution) {
758 common_stats->set_best_primal_bound(gscip_stats.best_objective());
760 common_stats->set_best_primal_bound(is_maximize ? -
kInf :
kInf);
763 common_stats->set_node_count(gscip_stats.node_count());
764 common_stats->set_simplex_iterations(gscip_stats.primal_simplex_iterations() +
765 gscip_stats.dual_simplex_iterations());
766 common_stats->set_barrier_iterations(gscip_stats.total_lp_iterations() -
767 common_stats->simplex_iterations());
768 *solve_result.mutable_gscip_output() = std::move(gscip_result.gscip_output);
772GScipSolver::GScipSolver(std::unique_ptr<GScip> gscip)
774 interrupt_event_handler_.Register(gscip_.get());
783 if (!
model.objective().quadratic_coefficients().row_ids().empty()) {
784 return absl::InvalidArgumentError(
785 "MathOpt does not currently support SCIP models with quadratic "
790 auto solver = absl::WrapUnique(
new GScipSolver(std::move(gscip)));
794 SparseDoubleVectorAsMap(
model.objective().linear_coefficients())));
796 model.linear_constraints(),
model.linear_constraint_matrix()));
803 const ModelSolveParametersProto& model_parameters,
805 const CallbackRegistrationProto& callback_registration,
const Callback cb,
807 const absl::Time
start = absl::Now();
812 const std::unique_ptr<GScipSolverCallbackHandler> callback_handler =
814 start, gscip_->scip());
816 std::unique_ptr<GScipSolverMessageCallbackHandler> message_cb_handler;
817 if (message_cb !=
nullptr) {
819 std::make_unique<GScipSolverMessageCallbackHandler>(message_cb);
824 for (
const SolutionHintProto& hint : model_parameters.solution_hints()) {
825 absl::flat_hash_map<SCIP_VAR*, double> partial_solution;
826 for (
const auto [
id, val] :
MakeView(hint.variable_values())) {
827 partial_solution.insert({variables_.at(
id), val});
831 for (
const auto [
id,
value] :
832 MakeView(model_parameters.branching_priorities())) {
838 if (interrupter !=
nullptr) {
839 interrupt_event_handler_.interrupter = interrupter;
842 [&]() { interrupt_event_handler_.interrupter =
nullptr; });
848 gscip_->Solve(gscip_parameters,
850 message_cb_handler !=
nullptr
851 ? message_cb_handler->MessageHandler()
855 message_cb_handler.reset();
857 if (callback_handler) {
862 SolveResultProto result,
863 CreateSolveResultProto(std::move(gscip_result), model_parameters,
865 ? std::make_optional(
parameters.cutoff_limit())
868 absl::Now() -
start, result.mutable_solve_stats()->mutable_solve_time()));
872absl::flat_hash_set<SCIP_VAR*> GScipSolver::LookupAllVariables(
874 absl::flat_hash_set<SCIP_VAR*> result;
877 result.insert(variables_.at(var_id));
883InvertedBounds GScipSolver::ListInvertedBounds()
const {
885 InvertedBounds inverted_bounds;
886 for (
const auto& [
id,
var] : variables_) {
887 if (gscip_->Lb(
var) > gscip_->Ub(
var)) {
888 inverted_bounds.variables.push_back(
id);
891 for (
const auto& [
id, cstr] : linear_constraints_) {
892 if (gscip_->LinearConstraintLb(cstr) > gscip_->LinearConstraintUb(cstr)) {
893 inverted_bounds.linear_constraints.push_back(
id);
898 std::sort(inverted_bounds.variables.begin(), inverted_bounds.variables.end());
899 std::sort(inverted_bounds.linear_constraints.begin(),
900 inverted_bounds.linear_constraints.end());
901 return inverted_bounds;
907 LookupAllVariables(model_update.deleted_variable_ids()))
909 model_update.objective_updates()
910 .quadratic_coefficients()
916 for (
const int64_t constraint_id :
917 model_update.deleted_linear_constraint_ids()) {
918 SCIP_CONS*
const scip_cons = linear_constraints_.at(constraint_id);
919 linear_constraints_.erase(constraint_id);
923 const absl::flat_hash_set<SCIP_VAR*> vars_to_delete =
924 LookupAllVariables(model_update.deleted_variable_ids());
925 for (
const int64_t deleted_variable_id :
926 model_update.deleted_variable_ids()) {
927 variables_.erase(deleted_variable_id);
932 const std::optional<int64_t> first_new_var_id =
934 const std::optional<int64_t> first_new_cstr_id =
937 if (model_update.objective_updates().has_direction_update()) {
939 model_update.objective_updates().direction_update()));
941 if (model_update.objective_updates().has_offset_update()) {
943 model_update.objective_updates().offset_update()));
946 const absl::flat_hash_map<int64_t, double> linear_objective_updates =
947 SparseDoubleVectorAsMap(
948 model_update.objective_updates().linear_coefficients());
949 for (
const auto& obj_pair : linear_objective_updates) {
951 if (!first_new_var_id.has_value() || obj_pair.first < *first_new_var_id) {
953 gscip_->SetObjCoef(variables_.at(obj_pair.first), obj_pair.second));
985 AddVariables(model_update.new_variables(), linear_objective_updates));
989 UpdateLinearConstraints(model_update.linear_constraint_updates(),
990 model_update.linear_constraint_matrix_updates(),
995 const std::optional first_new_variable_id =
997 if (first_new_variable_id.has_value()) {
998 for (
const auto& [lin_con_id, var_coeffs] :
1002 *first_new_variable_id,
1004 for (
const auto& [var_id,
value] : var_coeffs) {
1007 linear_constraints_.at(lin_con_id), variables_.at(var_id),
value));
1014 AddLinearConstraints(model_update.new_linear_constraints(),
1015 model_update.linear_constraint_matrix_updates()));
1016 return absl::OkStatus();
1019GScipSolver::InterruptEventHandler::InterruptEventHandler()
1021 {.name =
"interrupt event handler",
1022 .description =
"Event handler to call SCIPinterruptSolve() when a "
1023 "user SolveInterrupter is triggered."}) {}
1025SCIP_RETCODE GScipSolver::InterruptEventHandler::Init(GScip*
const gscip) {
1027 if (interrupter ==
nullptr) {
1033 CatchEvent(SCIP_EVENTTYPE_PRESOLVEROUND);
1034 CatchEvent(SCIP_EVENTTYPE_NODEEVENT);
1036 return TryCallInterruptIfNeeded(gscip);
1039SCIP_RETCODE GScipSolver::InterruptEventHandler::Execute(
1040 const GScipEventHandlerContext
context) {
1041 return TryCallInterruptIfNeeded(
context.gscip());
1044SCIP_RETCODE GScipSolver::InterruptEventHandler::TryCallInterruptIfNeeded(
1045 GScip*
const gscip) {
1046 if (interrupter ==
nullptr) {
1047 LOG(
WARNING) <<
"TryCallInterruptIfNeeded() called after interrupter has "
1052 if (!interrupter->IsInterrupted()) {
1056 const SCIP_STAGE stage = SCIPgetStage(gscip->scip());
1058 case SCIP_STAGE_INIT:
1059 case SCIP_STAGE_FREE:
1062 LOG(DFATAL) <<
"TryCallInterruptIfNeeded() called in stage "
1063 << (stage == SCIP_STAGE_INIT ?
"INIT" :
"FREE");
1065 case SCIP_STAGE_INITSOLVE:
1066 LOG(
WARNING) <<
"TryCallInterruptIfNeeded() called in INITSOLVE stage; "
1067 "we can't call SCIPinterruptSolve() in this stage.";
1070 return SCIPinterruptSolve(gscip->scip());
#define CHECK_EQ(val1, val2)
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
static absl::StatusOr< std::unique_ptr< GScip > > Create(const std::string &problem_name)
static std::unique_ptr< GScipSolverCallbackHandler > RegisterIfNeeded(const CallbackRegistrationProto &callback_registration, SolverInterface::Callback callback, absl::Time solve_start, SCIP *scip)
bool CanUpdate(const ModelUpdateProto &model_update) override
absl::Status Update(const ModelUpdateProto &model_update) override
static absl::StatusOr< std::unique_ptr< SolverInterface > > New(const ModelProto &model, const InitArgs &init_args)
absl::StatusOr< SolveResultProto > Solve(const SolveParametersProto ¶meters, const ModelSolveParametersProto &model_parameters, MessageCallback message_cb, const CallbackRegistrationProto &callback_registration, Callback cb, SolveInterrupter *interrupter) override
static absl::StatusOr< GScipParameters > MergeParameters(const SolveParametersProto &solve_parameters)
std::function< void(const std::vector< std::string > &)> MessageCallback
std::function< absl::StatusOr< CallbackResultProto >(const CallbackDataProto &)> Callback
int64_t linear_constraint_id
absl::Span< const int64_t > variable_ids
absl::Span< const double > coefficients
GurobiMPCallbackContext * context
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
std::optional< int64_t > FirstLinearConstraintId(const LinearConstraintsProto &linear_constraints)
absl::Status CheckRegisteredCallbackEvents(const CallbackRegistrationProto ®istration, const absl::flat_hash_set< CallbackEventProto > &supported_events)
MATH_OPT_REGISTER_SOLVER(SOLVER_TYPE_CP_SAT, CpSatSolver::New)
int NumMatrixNonzeros(const SparseDoubleMatrixProto &matrix)
int NumVariables(const VariablesProto &variables)
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)
TerminationProto TerminateForLimit(const LimitProto limit, const bool feasible, const absl::string_view detail)
SparseSubmatrixRowsView SparseSubmatrixByRows(const SparseDoubleMatrixProto &matrix, const int64_t start_row_id, const std::optional< int64_t > end_row_id, const int64_t start_col_id, const std::optional< int64_t > end_col_id)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
int NumConstraints(const LinearConstraintsProto &linear_constraints)
TerminationProto TerminateForReason(const TerminationReasonProto reason, const absl::string_view detail)
GScipParameters::MetaParamValue ConvertMathOptEmphasis(EmphasisProto emphasis)
std::optional< int64_t > FirstVariableId(const VariablesProto &variables)
Collection of objects used to extend the Constraint Solver library.
void GScipSetCatchCtrlC(const bool catch_ctrl_c, GScipParameters *const parameters)
void GScipSetTimeLimit(absl::Duration time_limit, GScipParameters *parameters)
void GScipSetRandomSeed(GScipParameters *parameters, int random_seed)
std::string ProtoEnumToString(ProtoEnumType enum_value)
void GScipSetMaxNumThreads(int num_threads, GScipParameters *parameters)
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)