31#include "absl/container/flat_hash_set.h"
32#include "absl/memory/memory.h"
33#include "absl/status/status.h"
34#include "absl/status/statusor.h"
35#include "absl/strings/str_cat.h"
36#include "absl/strings/str_format.h"
37#include "absl/strings/str_join.h"
38#include "absl/strings/string_view.h"
39#include "absl/time/clock.h"
40#include "absl/time/time.h"
41#include "absl/types/span.h"
48#include "ortools/math_opt/callback.pb.h"
54#include "ortools/math_opt/model.pb.h"
55#include "ortools/math_opt/model_parameters.pb.h"
56#include "ortools/math_opt/model_update.pb.h"
57#include "ortools/math_opt/parameters.pb.h"
58#include "ortools/math_opt/result.pb.h"
59#include "ortools/math_opt/solution.pb.h"
60#include "ortools/math_opt/solvers/gurobi.pb.h"
63#include "ortools/math_opt/sparse_containers.pb.h"
71absl::StatusOr<std::unique_ptr<Gurobi>> GurobiFromInitArgs(
72 const SolverInterface::InitArgs& init_args) {
75 const NonStreamableGurobiInitArguments*
const non_streamable_args =
76 init_args.non_streamable !=
nullptr
77 ? init_args.non_streamable->ToNonStreamableGurobiInitArguments()
79 std::unique_ptr<Gurobi>
gurobi;
80 if (non_streamable_args !=
nullptr &&
81 non_streamable_args->master_env !=
nullptr) {
83 }
else if (init_args.streamable.has_gurobi() &&
84 init_args.streamable.gurobi().has_isv_key()) {
94inline BasisStatusProto ConvertVariableStatus(
const int status) {
97 return BASIS_STATUS_BASIC;
99 return BASIS_STATUS_AT_LOWER_BOUND;
101 return BASIS_STATUS_AT_UPPER_BOUND;
103 return BASIS_STATUS_FREE;
105 return BASIS_STATUS_UNSPECIFIED;
109inline int GrbVariableStatus(
const BasisStatusProto
status) {
111 case BASIS_STATUS_BASIC:
113 case BASIS_STATUS_AT_LOWER_BOUND:
114 case BASIS_STATUS_FIXED_VALUE:
116 case BASIS_STATUS_AT_UPPER_BOUND:
118 case BASIS_STATUS_FREE:
120 case BASIS_STATUS_UNSPECIFIED:
122 LOG(
FATAL) <<
"Unexpected invalid initial_basis.";
127GurobiParametersProto MergeParameters(
128 const SolveParametersProto& solve_parameters) {
129 GurobiParametersProto merged_parameters;
132 GurobiParametersProto::Parameter*
const parameter =
133 merged_parameters.add_parameters();
135 parameter->set_value(solve_parameters.enable_output() ?
"1" :
"0");
138 if (solve_parameters.has_time_limit()) {
139 const double time_limit = absl::ToDoubleSeconds(
141 GurobiParametersProto::Parameter*
const parameter =
142 merged_parameters.add_parameters();
144 parameter->set_value(absl::StrCat(
time_limit));
147 if (solve_parameters.has_node_limit()) {
148 GurobiParametersProto::Parameter*
const parameter =
149 merged_parameters.add_parameters();
151 parameter->set_value(absl::StrCat(solve_parameters.node_limit()));
154 if (solve_parameters.has_threads()) {
155 const int threads = solve_parameters.threads();
156 GurobiParametersProto::Parameter*
const parameter =
157 merged_parameters.add_parameters();
159 parameter->set_value(absl::StrCat(threads));
162 if (solve_parameters.has_absolute_gap_tolerance()) {
163 const double absolute_gap_tolerance =
164 solve_parameters.absolute_gap_tolerance();
165 GurobiParametersProto::Parameter*
const parameter =
166 merged_parameters.add_parameters();
168 parameter->set_value(absl::StrCat(absolute_gap_tolerance));
171 if (solve_parameters.has_relative_gap_tolerance()) {
172 const double relative_gap_tolerance =
173 solve_parameters.relative_gap_tolerance();
174 GurobiParametersProto::Parameter*
const parameter =
175 merged_parameters.add_parameters();
177 parameter->set_value(absl::StrCat(relative_gap_tolerance));
180 if (solve_parameters.has_cutoff_limit()) {
181 GurobiParametersProto::Parameter*
const parameter =
182 merged_parameters.add_parameters();
184 parameter->set_value(absl::StrCat(solve_parameters.cutoff_limit()));
187 if (solve_parameters.has_objective_limit()) {
188 GurobiParametersProto::Parameter*
const parameter =
189 merged_parameters.add_parameters();
191 parameter->set_value(absl::StrCat(solve_parameters.objective_limit()));
194 if (solve_parameters.has_best_bound_limit()) {
195 GurobiParametersProto::Parameter*
const parameter =
196 merged_parameters.add_parameters();
198 parameter->set_value(absl::StrCat(solve_parameters.best_bound_limit()));
201 if (solve_parameters.has_solution_limit()) {
202 GurobiParametersProto::Parameter*
const parameter =
203 merged_parameters.add_parameters();
205 parameter->set_value(absl::StrCat(solve_parameters.solution_limit()));
208 if (solve_parameters.has_random_seed()) {
209 const int random_seed =
211 GurobiParametersProto::Parameter*
const parameter =
212 merged_parameters.add_parameters();
214 parameter->set_value(absl::StrCat(random_seed));
217 if (solve_parameters.lp_algorithm() != LP_ALGORITHM_UNSPECIFIED) {
218 GurobiParametersProto::Parameter*
const parameter =
219 merged_parameters.add_parameters();
221 switch (solve_parameters.lp_algorithm()) {
222 case LP_ALGORITHM_PRIMAL_SIMPLEX:
225 case LP_ALGORITHM_DUAL_SIMPLEX:
228 case LP_ALGORITHM_BARRIER:
234 <<
" unknown, error setting Gurobi parameters";
238 if (solve_parameters.scaling() != EMPHASIS_UNSPECIFIED) {
239 GurobiParametersProto::Parameter*
const parameter =
240 merged_parameters.add_parameters();
242 switch (solve_parameters.scaling()) {
244 parameter->set_value(absl::StrCat(0));
247 case EMPHASIS_MEDIUM:
248 parameter->set_value(absl::StrCat(1));
251 parameter->set_value(absl::StrCat(2));
253 case EMPHASIS_VERY_HIGH:
254 parameter->set_value(absl::StrCat(3));
259 <<
" unknown, error setting Gurobi parameters";
263 if (solve_parameters.cuts() != EMPHASIS_UNSPECIFIED) {
264 GurobiParametersProto::Parameter*
const parameter =
265 merged_parameters.add_parameters();
267 switch (solve_parameters.cuts()) {
269 parameter->set_value(absl::StrCat(0));
272 case EMPHASIS_MEDIUM:
273 parameter->set_value(absl::StrCat(1));
276 parameter->set_value(absl::StrCat(2));
278 case EMPHASIS_VERY_HIGH:
279 parameter->set_value(absl::StrCat(3));
284 <<
" unknown, error setting Gurobi parameters";
288 if (solve_parameters.heuristics() != EMPHASIS_UNSPECIFIED) {
289 GurobiParametersProto::Parameter*
const parameter =
290 merged_parameters.add_parameters();
292 switch (solve_parameters.heuristics()) {
294 parameter->set_value(absl::StrCat(0.0));
297 parameter->set_value(absl::StrCat(0.025));
299 case EMPHASIS_MEDIUM:
302 parameter->set_value(absl::StrCat(0.05));
305 parameter->set_value(absl::StrCat(0.1));
307 case EMPHASIS_VERY_HIGH:
308 parameter->set_value(absl::StrCat(0.2));
311 LOG(
FATAL) <<
"Heuristics emphasis: "
313 <<
" unknown, error setting Gurobi parameters";
317 if (solve_parameters.presolve() != EMPHASIS_UNSPECIFIED) {
318 GurobiParametersProto::Parameter*
const parameter =
319 merged_parameters.add_parameters();
321 switch (solve_parameters.presolve()) {
323 parameter->set_value(absl::StrCat(0));
326 case EMPHASIS_MEDIUM:
327 parameter->set_value(absl::StrCat(1));
330 case EMPHASIS_VERY_HIGH:
331 parameter->set_value(absl::StrCat(2));
336 <<
" unknown, error setting Gurobi parameters";
340 if (solve_parameters.has_iteration_limit()) {
341 GurobiParametersProto::Parameter*
const iterationlimit =
342 merged_parameters.add_parameters();
344 iterationlimit->set_value(absl::StrCat(solve_parameters.iteration_limit()));
345 GurobiParametersProto::Parameter*
const bariterlimit =
346 merged_parameters.add_parameters();
349 solve_parameters.iteration_limit());
350 bariterlimit->set_value(absl::StrCat(val));
353 for (
const GurobiParametersProto::Parameter& parameter :
354 solve_parameters.gurobi().parameters()) {
355 *merged_parameters.add_parameters() = parameter;
358 return merged_parameters;
361absl::StatusOr<int64_t> SafeInt64FromDouble(
const double d) {
362 const int64_t result =
static_cast<int64_t
>(d);
363 if (
static_cast<double>(result) != d) {
364 return absl::InternalError(
365 absl::StrCat(
"Expected double ", d,
" to contain an int64_t."));
370const absl::flat_hash_set<CallbackEventProto>& SupportedMIPEvents() {
371 static const auto*
const kEvents =
372 new absl::flat_hash_set<CallbackEventProto>({
373 CALLBACK_EVENT_PRESOLVE, CALLBACK_EVENT_SIMPLEX, CALLBACK_EVENT_MIP,
374 CALLBACK_EVENT_MIP_SOLUTION, CALLBACK_EVENT_MIP_NODE,
383const absl::flat_hash_set<CallbackEventProto>& SupportedLPEvents() {
384 static const auto*
const kEvents =
385 new absl::flat_hash_set<CallbackEventProto>({
386 CALLBACK_EVENT_PRESOLVE,
387 CALLBACK_EVENT_SIMPLEX,
388 CALLBACK_EVENT_BARRIER,
395constexpr std::size_t kMaxNameSize = 255;
398std::string TruncateName(
const std::string_view original_name) {
400 original_name.substr(0,
std::min(kMaxNameSize, original_name.size())));
404std::vector<std::string> TruncateNames(
405 const google::protobuf::RepeatedPtrField<std::string>& original_names) {
406 std::vector<std::string> result;
407 result.reserve(original_names.size());
408 for (
const std::string& original_name : original_names) {
409 result.push_back(TruncateName(original_name));
416GurobiSolver::GurobiSolver(std::unique_ptr<Gurobi> g_gurobi)
417 : gurobi_(
std::move(g_gurobi)) {}
419int GurobiSolver::num_gurobi_constraints()
const {
420 return linear_constraints_map_.size();
423absl::StatusOr<TerminationProto> GurobiSolver::ConvertTerminationReason(
424 const int gurobi_status,
const SolutionClaims solution_claims) {
425 switch (gurobi_status) {
431 if (solution_claims.primal_feasible_solution_exists) {
435 "Gurobi status GRB_UNBOUNDED");
438 "Gurobi status GRB_INF_OR_UNBD");
441 false,
"Gurobi status GRB_CUTOFF");
445 solution_claims.primal_feasible_solution_exists);
449 solution_claims.primal_feasible_solution_exists);
453 solution_claims.primal_feasible_solution_exists);
457 solution_claims.primal_feasible_solution_exists);
461 solution_claims.primal_feasible_solution_exists);
473 solution_claims.primal_feasible_solution_exists);
475 return absl::InternalError(
476 "Error creating termination reason, unexpected gurobi status code "
479 return absl::InternalError(
480 "Error creating termination reason, unexpected gurobi status code "
483 return absl::InternalError(absl::StrCat(
484 "Missing Gurobi optimization status code case: ", gurobi_status));
488absl::StatusOr<bool> GurobiSolver::IsMaximize()
const {
494absl::StatusOr<bool> GurobiSolver::IsLP()
const {
498 return !
static_cast<bool>(is_mip) && !
static_cast<bool>(is_qp) &&
499 !
static_cast<bool>(is_qcp);
503absl::StatusOr<bool> GurobiSolver::IsQP()
const {
507 return !
static_cast<bool>(is_mip) &&
static_cast<bool>(is_qp) &&
508 !
static_cast<bool>(is_qcp);
514void GurobiSolver::GurobiVectorToSparseDoubleVector(
515 const absl::Span<const double> gurobi_values,
const T& map,
516 SparseDoubleVectorProto& result,
517 const SparseVectorFilterProto& filter)
const {
518 SparseVectorFilterPredicate predicate(filter);
519 for (
auto [
id, gurobi_data] : map) {
520 const double value = gurobi_values[get_model_index(gurobi_data)];
521 if (predicate.AcceptsAndUpdate(
id,
value)) {
523 result.add_values(
value);
528absl::Status GurobiSolver::SetGurobiBasis(
const BasisProto& basis) {
529 std::vector<int> gurobi_variable_basis_status(num_gurobi_variables_);
530 for (
const auto [
id,
value] :
MakeView(basis.variable_status())) {
531 gurobi_variable_basis_status[variables_map_.at(
id)] =
532 GrbVariableStatus(
static_cast<BasisStatusProto
>(
value));
535 std::vector<int> gurobi_constraint_basis_status;
536 gurobi_constraint_basis_status.reserve(num_gurobi_constraints());
537 for (
const auto [
id,
value] :
MakeView(basis.constraint_status())) {
538 const ConstraintData& constraint_data = linear_constraints_map_.at(
id);
540 if (constraint_data.slack_index == kUnspecifiedIndex) {
541 if (
value == BASIS_STATUS_BASIC) {
542 gurobi_constraint_basis_status.push_back(kGrbBasicConstraint);
544 gurobi_constraint_basis_status.push_back(kGrbNonBasicConstraint);
547 }
else if (
value == BASIS_STATUS_BASIC) {
551 gurobi_variable_basis_status[constraint_data.slack_index] =
GRB_BASIC;
552 gurobi_constraint_basis_status.push_back(kGrbNonBasicConstraint);
554 gurobi_variable_basis_status[constraint_data.slack_index] =
555 GrbVariableStatus(
static_cast<BasisStatusProto
>(
value));
556 gurobi_constraint_basis_status.push_back(kGrbNonBasicConstraint);
560 gurobi_variable_basis_status));
562 gurobi_constraint_basis_status));
563 return absl::OkStatus();
566absl::StatusOr<BasisProto> GurobiSolver::GetGurobiBasis() {
569 const std::vector<int> gurobi_variable_basis_status,
572 for (
auto [variable_id, gurobi_variable_index] : variables_map_) {
573 basis.mutable_variable_status()->add_ids(variable_id);
574 const BasisStatusProto variable_status = ConvertVariableStatus(
575 gurobi_variable_basis_status[gurobi_variable_index]);
576 if (variable_status == BASIS_STATUS_UNSPECIFIED) {
577 return absl::InternalError(
578 absl::StrCat(
"Invalid Gurobi variable basis status: ",
579 gurobi_variable_basis_status[gurobi_variable_index]));
581 basis.mutable_variable_status()->add_values(variable_status);
585 const std::vector<int> gurobi_constraint_basis_status,
587 for (
auto [constraint_id, gurobi_data] : linear_constraints_map_) {
588 basis.mutable_constraint_status()->add_ids(constraint_id);
589 const int gurobi_constraint_status =
590 gurobi_constraint_basis_status[gurobi_data.constraint_index];
591 if ((gurobi_constraint_status != kGrbBasicConstraint) &&
592 (gurobi_constraint_status != kGrbNonBasicConstraint)) {
593 return absl::InternalError(
594 absl::StrCat(
"Invalid Gurobi constraint basis status: ",
595 gurobi_constraint_status));
600 if (gurobi_constraint_status == kGrbBasicConstraint) {
601 basis.mutable_constraint_status()->add_values(BASIS_STATUS_BASIC);
603 basis.mutable_constraint_status()->add_values(
604 BASIS_STATUS_AT_UPPER_BOUND);
609 if (gurobi_constraint_status == kGrbBasicConstraint) {
610 basis.mutable_constraint_status()->add_values(BASIS_STATUS_BASIC);
612 basis.mutable_constraint_status()->add_values(
613 BASIS_STATUS_AT_LOWER_BOUND);
616 }
else if (gurobi_data.lower_bound == gurobi_data.upper_bound) {
617 if (gurobi_constraint_status == kGrbBasicConstraint) {
618 basis.mutable_constraint_status()->add_values(BASIS_STATUS_BASIC);
622 basis.mutable_constraint_status()->add_values(BASIS_STATUS_FIXED_VALUE);
626 const BasisStatusProto slack_status = ConvertVariableStatus(
627 gurobi_variable_basis_status[gurobi_data.slack_index]);
628 if (slack_status == BASIS_STATUS_UNSPECIFIED) {
629 return absl::InternalError(absl::StrCat(
630 "Invalid Gurobi slack variable basis status: ", slack_status));
632 if ((gurobi_constraint_status == kGrbBasicConstraint) ||
633 (slack_status == BASIS_STATUS_BASIC)) {
634 basis.mutable_constraint_status()->add_values(BASIS_STATUS_BASIC);
636 basis.mutable_constraint_status()->add_values(slack_status);
645absl::StatusOr<DualRayProto> GurobiSolver::GetGurobiDualRay(
646 const SparseVectorFilterProto& linear_constraints_filter,
647 const SparseVectorFilterProto& variables_filter,
const bool is_maximize) {
651 num_gurobi_constraints()));
653 DualRayProto dual_ray;
657 SparseVectorFilterPredicate predicate(linear_constraints_filter);
658 for (
auto [constraint_id, gurobi_data] : linear_constraints_map_) {
660 const double value = -farkas_dual[gurobi_data.constraint_index];
661 if (predicate.AcceptsAndUpdate(constraint_id,
value)) {
662 dual_ray.mutable_dual_values()->add_ids(constraint_id);
664 dual_ray.mutable_dual_values()->add_values(-
value);
666 dual_ray.mutable_dual_values()->add_values(
value);
674 SparseVectorFilterPredicate predicate(variables_filter);
675 for (
auto [var_id, gurobi_variable_index] : variables_map_) {
678 double reduced_cost_value = 0.0;
680 gurobi_->GetVars(gurobi_variable_index, 1));
681 for (
int i = 0; i < column.inds.size(); ++i) {
682 reduced_cost_value += farkas_dual[column.inds[i]] * column.vals[i];
684 if (predicate.AcceptsAndUpdate(var_id, reduced_cost_value)) {
685 dual_ray.mutable_reduced_costs()->add_ids(var_id);
687 dual_ray.mutable_reduced_costs()->add_values(-reduced_cost_value);
689 dual_ray.mutable_reduced_costs()->add_values(reduced_cost_value);
697absl::StatusOr<ProblemStatusProto> GurobiSolver::GetProblemStatus(
698 const int grb_termination,
const SolutionClaims solution_claims) {
699 ProblemStatusProto problem_status;
702 problem_status.set_primal_status(FEASIBILITY_STATUS_UNDETERMINED);
703 problem_status.set_dual_status(FEASIBILITY_STATUS_UNDETERMINED);
706 if (solution_claims.primal_feasible_solution_exists) {
707 problem_status.set_primal_status(FEASIBILITY_STATUS_FEASIBLE);
709 if (solution_claims.dual_feasible_solution_exists) {
710 problem_status.set_dual_status(FEASIBILITY_STATUS_FEASIBLE);
714 switch (grb_termination) {
716 problem_status.set_primal_status(FEASIBILITY_STATUS_INFEASIBLE);
717 if (solution_claims.primal_feasible_solution_exists) {
718 return absl::InternalError(
719 "GRB_INT_ATTR_STATUS == GRB_INFEASIBLE, but a primal feasible "
720 "solution was returned.");
726 problem_status.set_dual_status(FEASIBILITY_STATUS_INFEASIBLE);
727 if (solution_claims.dual_feasible_solution_exists) {
728 return absl::InternalError(
729 "GRB_INT_ATTR_STATUS == GRB_UNBOUNDED, but a dual feasible "
730 "solution was returned or exists.");
734 problem_status.set_primal_or_dual_infeasible(
true);
735 if (solution_claims.primal_feasible_solution_exists) {
736 return absl::InternalError(
737 "GRB_INT_ATTR_STATUS == GRB_INF_OR_UNBD, but a primal feasible "
738 "solution was returned.");
740 if (solution_claims.dual_feasible_solution_exists) {
741 return absl::InternalError(
742 "GRB_INT_ATTR_STATUS == GRB_INF_OR_UNBD, but a dual feasible "
743 "solution was returned or exists.");
747 return problem_status;
750absl::StatusOr<SolveResultProto> GurobiSolver::ExtractSolveResultProto(
751 const absl::Time
start,
const ModelSolveParametersProto& model_parameters) {
752 SolveResultProto result;
758 GetSolutions(model_parameters));
760 for (
auto& solution : solutions) {
761 *result.add_solutions() = std::move(solution);
765 GetSolveStats(
start, solution_claims));
770 ConvertTerminationReason(grb_termination, solution_claims));
771 return std::move(result);
774absl::StatusOr<GurobiSolver::SolutionsAndClaims> GurobiSolver::GetSolutions(
775 const ModelSolveParametersProto& model_parameters) {
780 return GetLpSolution(model_parameters);
782 return GetQpSolution(model_parameters);
784 return GetMipSolutions(model_parameters);
788absl::StatusOr<SolveStatsProto> GurobiSolver::GetSolveStats(
789 const absl::Time
start,
const SolutionClaims solution_claims) {
790 SolveStatsProto solve_stats;
793 solve_stats.mutable_solve_time()));
798 .primal_feasible_solution_exists));
799 solve_stats.set_best_primal_bound(best_primal_bound);
802 solve_stats.set_best_dual_bound(best_dual_bound);
807 GetProblemStatus(grb_termination, solution_claims));
813 SafeInt64FromDouble(simplex_iters_double));
814 solve_stats.set_simplex_iterations(simplex_iters);
820 solve_stats.set_barrier_iterations(barrier_iters);
827 solve_stats.set_node_count(
nodes);
832absl::StatusOr<GurobiSolver::SolutionsAndClaims> GurobiSolver::GetMipSolutions(
833 const ModelSolveParametersProto& model_parameters) {
834 int num_solutions = 0;
838 std::vector<SolutionProto> solutions;
839 solutions.reserve(num_solutions);
840 for (
int i = 0; i < num_solutions; ++i) {
843 PrimalSolutionProto primal_solution;
846 primal_solution.set_objective_value(sol_val);
847 primal_solution.set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
849 const std::vector<double> grb_var_values,
851 GurobiVectorToSparseDoubleVector(grb_var_values, variables_map_,
852 *primal_solution.mutable_variable_values(),
853 model_parameters.variable_values_filter());
854 *solutions.emplace_back(SolutionProto()).mutable_primal_solution() =
855 std::move(primal_solution);
867 const SolutionClaims solution_claims = {
868 .primal_feasible_solution_exists = num_solutions > 0,
869 .dual_feasible_solution_exists = std::isfinite(best_dual_bound)};
874 if (grb_termination ==
GRB_OPTIMAL && num_solutions == 0) {
875 return absl::InternalError(
876 "GRB_INT_ATTR_STATUS == GRB_OPTIMAL, but solution pool is empty.");
878 if (grb_termination ==
GRB_OPTIMAL && !std::isfinite(best_dual_bound)) {
879 return absl::InternalError(
880 "GRB_INT_ATTR_STATUS == GRB_OPTIMAL, but GRB_DBL_ATTR_OBJBOUND is "
881 "unavailable or infinite.");
884 return SolutionsAndClaims{.solutions = std::move(solutions),
885 .solution_claims = solution_claims};
888absl::StatusOr<GurobiSolver::SolutionAndClaim<PrimalSolutionProto>>
889GurobiSolver::GetConvexPrimalSolutionIfAvailable(
890 const ModelSolveParametersProto& model_parameters) {
892 return SolutionAndClaim<PrimalSolutionProto>{
893 .solution = std::nullopt, .feasible_solution_exists =
false};
900 const std::vector<double> grb_var_values,
901 gurobi_->GetDoubleAttrArray(
GRB_DBL_ATTR_X, num_gurobi_variables_));
903 PrimalSolutionProto primal_solution;
913 primal_solution.set_objective_value(sol_val);
917 const std::vector<double> linear_obj_coefs,
919 for (
int i = 0; i < num_gurobi_variables_; ++i) {
925 primal_solution.set_feasibility_status(SOLUTION_STATUS_UNDETERMINED);
927 primal_solution.set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
929 primal_solution.set_feasibility_status(SOLUTION_STATUS_INFEASIBLE);
930 }
else if (PrimalSolutionQualityAvailable()) {
931 ASSIGN_OR_RETURN(
const double solution_quality, GetPrimalSolutionQuality());
934 if (solution_quality <= tolerance) {
935 primal_solution.set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
937 primal_solution.set_feasibility_status(SOLUTION_STATUS_INFEASIBLE);
941 GurobiVectorToSparseDoubleVector(grb_var_values, variables_map_,
942 *primal_solution.mutable_variable_values(),
943 model_parameters.variable_values_filter());
944 const bool primal_feasible_solution_exists =
945 (primal_solution.feasibility_status() == SOLUTION_STATUS_FEASIBLE);
946 return SolutionAndClaim<PrimalSolutionProto>{
947 .solution = std::move(primal_solution),
948 .feasible_solution_exists = primal_feasible_solution_exists};
951bool GurobiSolver::PrimalSolutionQualityAvailable()
const {
960absl::StatusOr<double> GurobiSolver::GetPrimalSolutionQuality()
const {
973 return std::max({constraint_residual, constraint_violation, bound_violation,
974 constraint_scaled_residual, constraint_scaled_violation,
975 bound_scaled_violation});
978absl::StatusOr<double> GurobiSolver::GetBestPrimalBound(
979 const bool has_primal_feasible_solution) {
984 if (has_primal_feasible_solution &&
1003absl::StatusOr<double> GurobiSolver::GetBestDualBound() {
1017absl::StatusOr<std::optional<BasisProto>> GurobiSolver::GetBasisIfAvailable() {
1023 basis.set_basic_dual_feasibility(SOLUTION_STATUS_UNDETERMINED);
1025 basis.set_basic_dual_feasibility(SOLUTION_STATUS_FEASIBLE);
1027 basis.set_basic_dual_feasibility(SOLUTION_STATUS_INFEASIBLE);
1030 return std::move(basis);
1032 return std::nullopt;
1035absl::StatusOr<GurobiSolver::SolutionsAndClaims> GurobiSolver::GetLpSolution(
1036 const ModelSolveParametersProto& model_parameters) {
1038 GetConvexPrimalSolutionIfAvailable(model_parameters));
1040 GetLpDualSolutionIfAvailable(model_parameters));
1042 const SolutionClaims solution_claims = {
1043 .primal_feasible_solution_exists =
1044 primal_solution_and_claim.feasible_solution_exists,
1045 .dual_feasible_solution_exists =
1046 dual_solution_and_claim.feasible_solution_exists};
1048 if (!primal_solution_and_claim.solution.has_value() &&
1049 !dual_solution_and_claim.solution.has_value() && !basis.has_value()) {
1050 return SolutionsAndClaims{.solution_claims = solution_claims};
1052 SolutionsAndClaims solution_and_claims{.solution_claims = solution_claims};
1053 SolutionProto& solution =
1054 solution_and_claims.solutions.emplace_back(SolutionProto());
1055 if (primal_solution_and_claim.solution.has_value()) {
1056 *solution.mutable_primal_solution() =
1057 std::move(*primal_solution_and_claim.solution);
1059 if (dual_solution_and_claim.solution.has_value()) {
1060 *solution.mutable_dual_solution() =
1061 std::move(*dual_solution_and_claim.solution);
1063 if (basis.has_value()) {
1064 *solution.mutable_basis() = std::move(*basis);
1066 return solution_and_claims;
1069absl::StatusOr<GurobiSolver::SolutionAndClaim<DualSolutionProto>>
1070GurobiSolver::GetLpDualSolutionIfAvailable(
1071 const ModelSolveParametersProto& model_parameters) {
1074 return SolutionAndClaim<DualSolutionProto>{
1075 .solution = std::nullopt, .feasible_solution_exists =
false};
1081 DualSolutionProto dual_solution;
1082 bool dual_feasible_solution_exists =
false;
1084 const std::vector<double> grb_constraint_duals,
1085 gurobi_->GetDoubleAttrArray(
GRB_DBL_ATTR_PI, num_gurobi_constraints()));
1086 GurobiVectorToSparseDoubleVector(grb_constraint_duals,
1087 linear_constraints_map_,
1088 *dual_solution.mutable_dual_values(),
1089 model_parameters.dual_values_filter());
1092 const std::vector<double> grb_reduced_cost_values,
1094 GurobiVectorToSparseDoubleVector(grb_reduced_cost_values, variables_map_,
1095 *dual_solution.mutable_reduced_costs(),
1096 model_parameters.reduced_costs_filter());
1104 dual_solution.set_objective_value(obj_val);
1110 dual_solution.set_feasibility_status(SOLUTION_STATUS_UNDETERMINED);
1112 dual_solution.set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
1113 dual_feasible_solution_exists =
true;
1115 dual_solution.set_feasibility_status(SOLUTION_STATUS_INFEASIBLE);
1136 if (dual_feasible_solution_exists || std::isfinite(best_dual_bound)) {
1137 dual_feasible_solution_exists =
true;
1139 return absl::InternalError(
1140 "GRB_INT_ATTR_STATUS == GRB_OPTIMAL, but GRB_DBL_ATTR_OBJBOUND is "
1141 "unavailable or infinite, and no dual feasible solution is returned");
1143 return SolutionAndClaim<DualSolutionProto>{
1144 .solution = std::move(dual_solution),
1145 .feasible_solution_exists = dual_feasible_solution_exists};
1148absl::Status GurobiSolver::FillRays(
1149 const ModelSolveParametersProto& model_parameters,
1150 SolveResultProto& result) {
1153 num_gurobi_variables_ > 0) {
1156 num_gurobi_variables_));
1157 PrimalRayProto*
const primal_ray = result.add_primal_rays();
1158 GurobiVectorToSparseDoubleVector(grb_ray_var_values, variables_map_,
1159 *primal_ray->mutable_variable_values(),
1160 model_parameters.variable_values_filter());
1163 num_gurobi_constraints() + num_gurobi_variables_ > 0) {
1165 DualRayProto dual_ray,
1166 GetGurobiDualRay(model_parameters.dual_values_filter(),
1167 model_parameters.reduced_costs_filter(), is_maximize));
1168 result.mutable_dual_rays()->Add(std::move(dual_ray));
1170 return absl::OkStatus();
1173absl::StatusOr<GurobiSolver::SolutionsAndClaims> GurobiSolver::GetQpSolution(
1174 const ModelSolveParametersProto& model_parameters) {
1176 GetConvexPrimalSolutionIfAvailable(model_parameters));
1183 bool dual_feasible_solution_exists =
false;
1185 if (grb_termination ==
GRB_OPTIMAL || std::isfinite(best_dual_bound)) {
1186 dual_feasible_solution_exists =
true;
1194 const SolutionClaims solution_claims = {
1195 .primal_feasible_solution_exists = found_primal_feasible_solution,
1196 .dual_feasible_solution_exists = dual_feasible_solution_exists};
1198 if (!primal_solution.has_value() && !basis.has_value()) {
1199 return GurobiSolver::SolutionsAndClaims{.solution_claims = solution_claims};
1201 SolutionsAndClaims solution_and_claims{.solution_claims = solution_claims};
1202 SolutionProto& solution =
1203 solution_and_claims.solutions.emplace_back(SolutionProto());
1204 if (primal_solution.has_value()) {
1205 *solution.mutable_primal_solution() = std::move(*primal_solution);
1207 if (basis.has_value()) {
1208 *solution.mutable_basis() = std::move(*basis);
1210 return solution_and_claims;
1213absl::Status GurobiSolver::SetParameters(
1215 const GurobiParametersProto gurobi_parameters = MergeParameters(
parameters);
1216 std::vector<std::string> parameter_errors;
1217 for (
const GurobiParametersProto::Parameter& parameter :
1218 gurobi_parameters.parameters()) {
1219 absl::Status param_status =
1220 gurobi_->SetParam(parameter.name().c_str(), parameter.value());
1221 if (!param_status.ok()) {
1222 parameter_errors.emplace_back(std::move(param_status).
message());
1225 if (!parameter_errors.empty()) {
1226 return absl::InvalidArgumentError(absl::StrJoin(parameter_errors,
"; "));
1228 return absl::OkStatus();
1231absl::Status GurobiSolver::AddNewVariables(
1232 const VariablesProto& new_variables) {
1233 const int num_new_variables = new_variables.lower_bounds().size();
1234 std::vector<char> variable_type(num_new_variables);
1235 for (
int j = 0; j < num_new_variables; ++j) {
1236 const VariableId
id = new_variables.ids(j);
1242 const std::vector<std::string> variable_names =
1243 TruncateNames(new_variables.names());
1246 new_variables.lower_bounds(),
1247 new_variables.upper_bounds(),
1248 variable_type, variable_names));
1249 num_gurobi_variables_ += num_new_variables;
1251 return absl::OkStatus();
1257absl::Status GurobiSolver::AddNewSlacks(
1258 const std::vector<SlackInfo>& new_slacks) {
1265 const int num_slacks = new_slacks.size();
1266 if (num_slacks == 0) {
1267 return absl::OkStatus();
1270 const std::vector<double> column_non_zeros(num_slacks, -1.0);
1274 std::vector<GurobiLinearConstraintIndex> row_indices;
1275 std::vector<int> column_non_zero_begin;
1276 column_non_zero_begin.reserve(num_slacks);
1277 row_indices.reserve(num_slacks);
1280 for (
int k = 0; k < num_slacks; ++k) {
1281 auto& [id, constraint_data] = new_slacks[k];
1283 row_indices.emplace_back(constraint_data.constraint_index);
1284 lower_bounds.emplace_back(constraint_data.lower_bound);
1285 upper_bounds.emplace_back(constraint_data.upper_bound);
1286 column_non_zero_begin.emplace_back(k);
1291 column_non_zeros, {},
1294 num_gurobi_variables_ += num_slacks;
1295 return absl::OkStatus();
1298absl::Status GurobiSolver::AddNewConstraints(
1299 const LinearConstraintsProto& constraints) {
1300 const int num_model_constraints = num_gurobi_constraints();
1301 const int num_new_constraints = constraints.lower_bounds().size();
1305 const std::vector<std::string> constraint_names =
1306 TruncateNames(constraints.names());
1316 std::vector<double> constraint_rhs;
1317 std::vector<char> constraint_sense;
1318 std::vector<SlackInfo> new_slacks;
1319 constraint_rhs.reserve(num_new_constraints);
1320 constraint_sense.reserve(num_new_constraints);
1321 new_slacks.reserve(num_new_constraints);
1322 for (
int i = 0; i < num_new_constraints; ++i) {
1323 const int64_t
id = constraints.ids(i);
1324 ConstraintData& constraint_data =
1326 constraint_data.lower_bound = constraints.lower_bounds(i);
1327 constraint_data.upper_bound = constraints.upper_bounds(i);
1328 constraint_data.constraint_index = i + num_model_constraints;
1334 rhs = constraint_data.upper_bound;
1336 }
else if (constraint_data.lower_bound > -
GRB_INFINITY &&
1338 rhs = constraint_data.lower_bound;
1340 }
else if (constraint_data.lower_bound == constraint_data.upper_bound) {
1341 rhs = constraint_data.lower_bound;
1346 constraint_data.slack_index = new_slacks.size() + num_gurobi_variables_;
1347 new_slacks.emplace_back(
id, constraint_data);
1349 constraint_rhs.emplace_back(rhs);
1350 constraint_sense.emplace_back(sense);
1354 gurobi_->AddConstrs(constraint_sense, constraint_rhs, constraint_names));
1356 if (!new_slacks.empty()) {
1359 return absl::OkStatus();
1362absl::Status GurobiSolver::ChangeCoefficients(
1363 const SparseDoubleMatrixProto& matrix) {
1364 const int num_coefficients = matrix.row_ids().size();
1365 std::vector<GurobiLinearConstraintIndex> row_index(num_coefficients);
1366 std::vector<GurobiVariableIndex> col_index(num_coefficients);
1367 for (
int k = 0; k < num_coefficients; ++k) {
1369 linear_constraints_map_.at(matrix.row_ids(k)).constraint_index;
1370 col_index[k] = variables_map_.at(matrix.column_ids(k));
1372 return gurobi_->ChgCoeffs(row_index, col_index, matrix.coefficients());
1375absl::Status GurobiSolver::UpdateDoubleListAttribute(
1376 const SparseDoubleVectorProto& update,
const char* attribute_name,
1377 const IdHashMap& id_hash_map) {
1378 if (update.ids_size() == 0) {
1379 return absl::OkStatus();
1381 std::vector<int>
index;
1382 index.reserve(update.ids_size());
1383 for (
const int64_t
id : update.ids()) {
1384 index.push_back(id_hash_map.at(
id));
1386 return gurobi_->SetDoubleAttrList(attribute_name,
index, update.values());
1389absl::Status GurobiSolver::UpdateInt32ListAttribute(
1390 const SparseInt32VectorProto& update,
const char* attribute_name,
1391 const IdHashMap& id_hash_map) {
1392 if (update.ids_size() == 0) {
1393 return absl::OkStatus();
1395 std::vector<int>
index;
1396 index.reserve(update.ids_size());
1397 for (
const int64_t
id : update.ids()) {
1398 index.push_back(id_hash_map.at(
id));
1400 return gurobi_->SetIntAttrList(attribute_name,
index, update.values());
1403absl::Status GurobiSolver::LoadModel(
const ModelProto& input_model) {
1404 CHECK(gurobi_ !=
nullptr);
1406 TruncateName(input_model.name())));
1411 RETURN_IF_ERROR(ChangeCoefficients(input_model.linear_constraint_matrix()));
1413 const int model_sense =
1417 input_model.objective().offset()));
1420 UpdateDoubleListAttribute(input_model.objective().linear_coefficients(),
1423 input_model.objective().quadratic_coefficients()));
1424 return absl::OkStatus();
1427absl::Status GurobiSolver::ResetQuadraticObjectiveTerms(
1428 const SparseDoubleMatrixProto& terms) {
1429 quadratic_objective_coefficients_.clear();
1431 const int num_terms = terms.row_ids().size();
1432 if (num_terms > 0) {
1433 std::vector<GurobiVariableIndex> first_var_index(num_terms);
1434 std::vector<GurobiVariableIndex> second_var_index(num_terms);
1435 for (
int k = 0; k < num_terms; ++k) {
1436 const VariableId row_id = terms.row_ids(k);
1437 const VariableId column_id = terms.column_ids(k);
1438 first_var_index[k] = variables_map_.at(row_id);
1439 second_var_index[k] = variables_map_.at(column_id);
1440 quadratic_objective_coefficients_[{row_id, column_id}] =
1441 terms.coefficients(k);
1443 RETURN_IF_ERROR(gurobi_->AddQpTerms(first_var_index, second_var_index,
1444 terms.coefficients()));
1446 return absl::OkStatus();
1449absl::Status GurobiSolver::UpdateQuadraticObjectiveTerms(
1450 const SparseDoubleMatrixProto& terms) {
1451 CHECK(gurobi_ !=
nullptr);
1452 const int num_terms = terms.row_ids().size();
1453 if (num_terms > 0) {
1454 std::vector<GurobiVariableIndex> first_var_index(num_terms);
1455 std::vector<GurobiVariableIndex> second_var_index(num_terms);
1456 std::vector<double> coefficient_updates(num_terms);
1457 for (
int k = 0; k < num_terms; ++k) {
1458 const VariableId row_id = terms.row_ids(k);
1459 const VariableId column_id = terms.column_ids(k);
1460 first_var_index[k] = variables_map_.at(row_id);
1461 second_var_index[k] = variables_map_.at(column_id);
1462 const std::pair<VariableId, VariableId> qp_term_key(row_id, column_id);
1463 const double new_coefficient = terms.coefficients(k);
1468 coefficient_updates[k] =
1469 new_coefficient - quadratic_objective_coefficients_[qp_term_key];
1470 quadratic_objective_coefficients_[qp_term_key] = new_coefficient;
1472 RETURN_IF_ERROR(gurobi_->AddQpTerms(first_var_index, second_var_index,
1473 coefficient_updates));
1475 return absl::OkStatus();
1481absl::Status GurobiSolver::UpdateLinearConstraints(
1482 const LinearConstraintUpdatesProto& constraints_update,
1483 std::vector<GurobiVariableIndex>& deleted_variables_index) {
1484 const SparseDoubleVectorProto& constraint_lower_bounds =
1485 constraints_update.lower_bounds();
1486 const SparseDoubleVectorProto& constraint_upper_bounds =
1487 constraints_update.upper_bounds();
1490 if (constraint_lower_bounds.ids().empty() &&
1491 constraint_upper_bounds.ids().empty()) {
1492 return absl::OkStatus();
1499 struct UpdateConstraintData {
1500 LinearConstraintId constraint_id;
1501 ConstraintData& source;
1502 double new_lower_bound;
1503 double new_upper_bound;
1504 UpdateConstraintData(
const LinearConstraintId
id, ConstraintData& reference)
1505 : constraint_id(id),
1510 const int upper_bounds_size = constraint_upper_bounds.ids().size();
1511 const int lower_bounds_size = constraint_lower_bounds.ids().size();
1512 std::vector<UpdateConstraintData> update_vector;
1513 update_vector.reserve(upper_bounds_size + lower_bounds_size);
1516 for (
int lower_index = 0, upper_index = 0;
1517 lower_index < lower_bounds_size || upper_index < upper_bounds_size;) {
1519 if (lower_index < lower_bounds_size) {
1520 lower_id = constraint_lower_bounds.ids(lower_index);
1523 if (upper_index < upper_bounds_size) {
1524 upper_id = constraint_upper_bounds.ids(upper_index);
1526 const VariableId
id =
std::min(lower_id, upper_id);
1528 UpdateConstraintData update(
id, linear_constraints_map_.at(
id));
1529 if (lower_id == upper_id) {
1530 update.new_lower_bound = constraint_lower_bounds.values(lower_index++);
1531 update.new_upper_bound = constraint_upper_bounds.values(upper_index++);
1532 }
else if (lower_id < upper_id) {
1533 update.new_lower_bound = constraint_lower_bounds.values(lower_index++);
1535 update.new_upper_bound = constraint_upper_bounds.values(upper_index++);
1537 update_vector.emplace_back(update);
1544 std::vector<char> sense_data;
1545 std::vector<double> rhs_data;
1546 std::vector<GurobiLinearConstraintIndex> rhs_index;
1548 std::vector<double> lower_bound_data;
1549 std::vector<double> upper_bound_data;
1550 std::vector<GurobiVariableIndex> bound_index;
1552 std::vector<SlackInfo> new_slacks;
1554 for (UpdateConstraintData& update_data : update_vector) {
1555 const bool same_lower_bound =
1556 (update_data.source.lower_bound == update_data.new_lower_bound) ||
1559 const bool same_upper_bound =
1560 (update_data.source.upper_bound == update_data.new_upper_bound) ||
1563 if (same_upper_bound && same_lower_bound)
continue;
1566 update_data.source.lower_bound = update_data.new_lower_bound;
1567 update_data.source.upper_bound = update_data.new_upper_bound;
1568 bool delete_slack =
false;
1572 delete_slack =
true;
1573 rhs_index.emplace_back(update_data.source.constraint_index);
1574 rhs_data.emplace_back(update_data.new_upper_bound);
1576 }
else if (update_data.new_lower_bound > -
GRB_INFINITY &&
1578 delete_slack =
true;
1579 rhs_index.emplace_back(update_data.source.constraint_index);
1580 rhs_data.emplace_back(update_data.new_lower_bound);
1582 }
else if (update_data.new_lower_bound == update_data.new_upper_bound) {
1583 delete_slack =
true;
1584 rhs_index.emplace_back(update_data.source.constraint_index);
1585 rhs_data.emplace_back(update_data.new_lower_bound);
1591 if (update_data.source.slack_index != kUnspecifiedIndex) {
1592 bound_index.emplace_back(update_data.source.slack_index);
1593 lower_bound_data.emplace_back(update_data.new_lower_bound);
1594 upper_bound_data.emplace_back(update_data.new_upper_bound);
1598 rhs_index.emplace_back(update_data.source.constraint_index);
1599 rhs_data.emplace_back(0.0);
1602 update_data.source.slack_index =
1603 new_slacks.size() + num_gurobi_variables_;
1605 new_slacks.emplace_back(update_data.constraint_id, update_data.source);
1612 if (delete_slack && update_data.source.slack_index != kUnspecifiedIndex) {
1613 deleted_variables_index.emplace_back(update_data.source.slack_index);
1614 update_data.source.slack_index = kUnspecifiedIndex;
1615 slack_map_.erase(update_data.constraint_id);
1620 if (!rhs_index.empty()) {
1626 if (!bound_index.empty()) {
1633 if (!new_slacks.empty()) {
1636 return absl::OkStatus();
1645absl::Status GurobiSolver::UpdateGurobiIndices() {
1647 GurobiVariableIndex next_index = 0;
1648 GurobiVariableIndex prev_index = kUnspecifiedIndex;
1649 auto variable_it = variables_map_.begin();
1650 auto slack_it = slack_map_.begin();
1651 while (variable_it != variables_map_.end() ||
1652 slack_it != slack_map_.end()) {
1654 if (variable_it != variables_map_.end()) {
1655 variable_index = variable_it->second;
1658 if (slack_it != slack_map_.end()) {
1659 slack_index = slack_it->second.slack_index;
1664 if (slack_index < variable_index) {
1665 prev_index = slack_index;
1666 slack_it->second.slack_index = next_index++;
1669 prev_index = variable_index;
1670 variable_it->second = next_index++;
1674 DCHECK_EQ(next_index, num_gurobi_variables_);
1677 GurobiLinearConstraintIndex next_constraint = 0;
1678 GurobiLinearConstraintIndex prev_constraint = kUnspecifiedConstraint;
1679 for (
auto& constraint_iterator : linear_constraints_map_) {
1680 DCHECK_LT(prev_constraint, constraint_iterator.second.constraint_index);
1681 prev_constraint = constraint_iterator.second.constraint_index;
1682 constraint_iterator.second.constraint_index = next_constraint++;
1684 DCHECK_EQ(next_constraint, num_gurobi_constraints());
1686 return absl::OkStatus();
1689absl::Status GurobiSolver::Update(
const ModelUpdateProto& model_update) {
1692 RETURN_IF_ERROR(AddNewConstraints(model_update.new_linear_constraints()));
1695 ChangeCoefficients(model_update.linear_constraint_matrix_updates()));
1697 if (model_update.objective_updates().has_direction_update()) {
1698 const int model_sense = model_update.objective_updates().direction_update()
1704 if (model_update.objective_updates().has_offset_update()) {
1714 model_update.objective_updates().quadratic_coefficients()));
1717 UpdateDoubleListAttribute(model_update.variable_updates().lower_bounds(),
1721 UpdateDoubleListAttribute(model_update.variable_updates().upper_bounds(),
1724 if (model_update.variable_updates().has_integers()) {
1725 const SparseBoolVectorProto& update =
1726 model_update.variable_updates().integers();
1727 std::vector<GurobiVariableIndex>
index;
1728 index.reserve(update.ids_size());
1729 for (
const int64_t
id : update.ids()) {
1730 index.push_back(variables_map_.at(
id));
1732 std::vector<char>
value;
1733 value.reserve(update.values_size());
1734 for (
const bool val : update.values()) {
1743 const absl::flat_hash_set<VariableId> variable_ids_to_be_deleted(
1744 model_update.deleted_variable_ids().begin(),
1745 model_update.deleted_variable_ids().end());
1748 for (
auto it = quadratic_objective_coefficients_.cbegin();
1749 it != quadratic_objective_coefficients_.cend();
1751 if (variable_ids_to_be_deleted.contains(it->first.first) ||
1752 variable_ids_to_be_deleted.contains(it->first.second)) {
1753 quadratic_objective_coefficients_.erase(it++);
1760 std::vector<GurobiVariableIndex> deleted_variables_index;
1761 std::vector<GurobiLinearConstraintIndex> deleted_constraints_index;
1764 model_update.linear_constraint_updates(), deleted_variables_index));
1766 for (
const VariableId
id : model_update.deleted_variable_ids()) {
1767 deleted_variables_index.emplace_back(variables_map_.at(
id));
1768 variables_map_.erase(
id);
1771 for (
const LinearConstraintId
id :
1772 model_update.deleted_linear_constraint_ids()) {
1773 ConstraintData& constraint_data = linear_constraints_map_.at(
id);
1774 deleted_constraints_index.emplace_back(constraint_data.constraint_index);
1775 if (constraint_data.slack_index != kUnspecifiedIndex) {
1776 deleted_variables_index.emplace_back(constraint_data.slack_index);
1777 constraint_data.slack_index = kUnspecifiedIndex;
1778 slack_map_.erase(
id);
1780 linear_constraints_map_.erase(
id);
1784 if (deleted_variables_index.empty() && deleted_constraints_index.empty()) {
1785 return absl::OkStatus();
1792 if (!deleted_constraints_index.empty()) {
1796 if (!deleted_variables_index.empty()) {
1798 num_gurobi_variables_ -= deleted_variables_index.size();
1808 return absl::OkStatus();
1811absl::StatusOr<std::unique_ptr<GurobiSolver>> GurobiSolver::New(
1814 return absl::InvalidArgumentError(
"Gurobi is not correctly installed.");
1817 GurobiFromInitArgs(init_args));
1820 return gurobi_solver;
1823absl::StatusOr<std::unique_ptr<GurobiSolver::GurobiCallbackData>>
1824GurobiSolver::RegisterCallback(
const CallbackRegistrationProto& registration,
1827 const absl::Time
start,
1829 const absl::flat_hash_set<CallbackEventProto> events =
EventSet(registration);
1840 registration, is_mip ? SupportedMIPEvents() : SupportedLPEvents()))
1841 <<
"for a " << (is_mip ?
"MIP" :
"LP") <<
" model";
1844 if (message_cb !=
nullptr) {
1849 if (registration.add_cuts() || registration.add_lazy_constraints()) {
1854 if (registration.add_lazy_constraints()) {
1859 return absl::make_unique<GurobiCallbackData>(
1860 GurobiCallbackInput{
1862 .message_cb = message_cb,
1863 .variable_ids = variables_map_,
1864 .num_gurobi_vars = num_gurobi_variables_,
1866 .mip_solution_filter = registration.mip_solution_filter(),
1867 .mip_node_filter = registration.mip_node_filter(),
1872absl::StatusOr<InvertedBounds> GurobiSolver::ListInvertedBounds()
const {
1873 InvertedBounds inverted_bounds;
1876 const std::vector<double> var_lbs,
1879 const std::vector<double> var_ubs,
1881 for (
const auto& [
id,
index] : variables_map_) {
1883 inverted_bounds.variables.push_back(
id);
1887 for (
const auto& [
id, cstr_data] : linear_constraints_map_) {
1888 if (cstr_data.lower_bound > cstr_data.upper_bound) {
1889 inverted_bounds.linear_constraints.push_back(
id);
1894 std::sort(inverted_bounds.variables.begin(), inverted_bounds.variables.end());
1895 std::sort(inverted_bounds.linear_constraints.begin(),
1896 inverted_bounds.linear_constraints.end());
1897 return inverted_bounds;
1902 const ModelSolveParametersProto& model_parameters,
1904 const CallbackRegistrationProto& callback_registration,
const Callback cb,
1906 const absl::Time
start = absl::Now();
1914 std::unique_ptr<SolveInterrupter> local_interrupter;
1915 if (cb !=
nullptr || interrupter !=
nullptr) {
1916 local_interrupter = std::make_unique<SolveInterrupter>();
1919 local_interrupter.get(), [&]() {
1927 gurobi_->Terminate();
1937 interrupter, [&]() { local_interrupter->
Interrupt(); });
1943 if (model_parameters.has_initial_basis()) {
1947 model_parameters.solution_hints_size()));
1948 for (
int i = 0; i < model_parameters.solution_hints_size(); ++i) {
1951 model_parameters.solution_hints(i).variable_values(),
1955 UpdateInt32ListAttribute(model_parameters.branching_priorities(),
1962 std::unique_ptr<GurobiCallbackData> gurobi_cb_data;
1963 if (cb !=
nullptr || local_interrupter !=
nullptr || message_cb !=
nullptr) {
1965 RegisterCallback(callback_registration, cb, message_cb,
1966 start, local_interrupter.get()));
1967 grb_cb = [&gurobi_cb_data](
1970 gurobi_cb_data->message_callback_data,
1971 gurobi_cb_data->local_interrupter);
1978 ListInvertedBounds());
1986 if (gurobi_cb_data !=
nullptr) {
1988 gurobi_cb_data->message_callback_data);
1992 ExtractSolveResultProto(
start, model_parameters));
1998 return solve_result;
2001bool GurobiSolver::CanUpdate(
const ModelUpdateProto& model_update) {
#define DCHECK_NE(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK(condition)
#define DCHECK_EQ(val1, val2)
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
static absl::StatusOr< std::unique_ptr< Gurobi > > New(GRBenvUniquePtr master_env=nullptr)
std::function< absl::Status(const CallbackContext &)> Callback
static absl::StatusOr< std::unique_ptr< Gurobi > > NewWithSharedMasterEnv(GRBenv *master_env)
std::function< void(const std::vector< std::string > &)> MessageCallback
std::function< absl::StatusOr< CallbackResultProto >(const CallbackDataProto &)> Callback
ModelSharedTimeLimit * time_limit
#define GRB_INT_PAR_BARITERLIMIT
#define GRB_INT_PAR_LOGTOCONSOLE
#define GRB_INT_ATTR_BRANCHPRIORITY
#define GRB_DBL_ATTR_START
#define GRB_DBL_PAR_MIPGAP
#define GRB_DBL_PAR_FEASIBILITYTOL
#define GRB_SOLUTION_LIMIT
#define GRB_INT_PAR_SOLUTIONLIMIT
#define GRB_NONBASIC_LOWER
#define GRB_INT_ATTR_MODELSENSE
#define GRB_INT_ATTR_VBASIS
#define GRB_GREATER_EQUAL
#define GRB_DBL_ATTR_NODECOUNT
#define GRB_INT_PAR_PRESOLVE
#define GRB_DBL_PAR_MIPGAPABS
#define GRB_DBL_ATTR_ITERCOUNT
#define GRB_INT_PAR_THREADS
#define GRB_DBL_ATTR_BOUND_SVIO
#define GRB_DBL_PAR_CUTOFF
#define GRB_DBL_PAR_ITERATIONLIMIT
#define GRB_INT_PAR_METHOD
#define GRB_INT_ATTR_IS_QP
#define GRB_DBL_ATTR_OBJVAL
#define GRB_INT_PAR_LAZYCONSTRAINTS
#define GRB_INT_PAR_SCALEFLAG
#define GRB_DBL_PAR_HEURISTICS
#define GRB_METHOD_BARRIER
#define GRB_INT_ATTR_IS_MIP
#define GRB_DBL_ATTR_CONSTR_RESIDUAL
#define GRB_CHAR_ATTR_VTYPE
#define GRB_INT_ATTR_NUMSTART
#define GRB_DBL_ATTR_BOUND_VIO
#define GRB_NONBASIC_UPPER
#define GRB_DBL_ATTR_OBJCON
#define GRB_DBL_PAR_BESTBDSTOP
#define GRB_STR_ATTR_MODELNAME
#define GRB_DBL_ATTR_CONSTR_VIO
#define GRB_DBL_ATTR_CONSTR_SRESIDUAL
#define GRB_DBL_ATTR_CONSTR_SVIO
#define GRB_INT_ATTR_IS_QCP
#define GRB_DBL_PAR_BESTOBJSTOP
#define GRB_INT_PAR_STARTNUMBER
#define GRB_CHAR_ATTR_SENSE
#define GRB_INT_ATTR_CBASIS
#define GRB_INT_ATTR_BARITERCOUNT
#define GRB_INT_ATTR_STATUS
#define GRB_DBL_ATTR_FARKASDUAL
#define GRB_DBL_ATTR_POOLOBJVAL
#define GRB_INT_PAR_SOLUTIONNUMBER
#define GRB_ITERATION_LIMIT
#define GRB_INT_ATTR_SOLCOUNT
#define GRB_METHOD_PRIMAL
#define GRB_DBL_PAR_TIMELIMIT
#define GRB_DBL_PAR_NODELIMIT
#define GRB_USER_OBJ_LIMIT
#define GRB_DBL_ATTR_UNBDRAY
#define GRB_DBL_ATTR_OBJBOUND
#define GRB_INT_PAR_PRECRUSH
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
auto & InsertKeyOrDie(Collection *const collection, const typename Collection::value_type::first_type &key)
absl::Status CheckRegisteredCallbackEvents(const CallbackRegistrationProto ®istration, const absl::flat_hash_set< CallbackEventProto > &supported_events)
void GurobiCallbackImplFlush(const GurobiCallbackInput &callback_input, MessageCallbackData &message_callback_data)
absl::StatusOr< GRBenvUniquePtr > NewMasterEnvironment(std::optional< GurobiInitializerProto::ISVKey > proto_isv_key)
absl::StatusOr< SolveResult > Solve(const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolverInitArguments &init_args)
absl::Status GurobiCallbackImpl(const Gurobi::CallbackContext &context, const GurobiCallbackInput &callback_input, MessageCallbackData &message_callback_data, SolveInterrupter *const local_interrupter)
TerminationProto TerminateForLimit(const LimitProto limit, const bool feasible, const absl::string_view detail)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
std::vector< bool > EventToGurobiWhere(const absl::flat_hash_set< CallbackEventProto > &events)
std::function< CallbackResult(const CallbackData &)> Callback
TerminationProto TerminateForReason(const TerminationReasonProto reason, const absl::string_view detail)
std::function< void(const std::vector< std::string > &)> MessageCallback
std::unique_ptr< GRBenv, GurobiFreeEnv > GRBenvUniquePtr
absl::flat_hash_set< CallbackEventProto > EventSet(const CallbackRegistrationProto &callback_registration)
Collection of objects used to extend the Constraint Solver library.
std::string ProtoEnumToString(ProtoEnumType enum_value)
bool GurobiIsCorrectlyInstalled()
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)
std::vector< double > lower_bounds
std::vector< double > upper_bounds
#define MATH_OPT_REGISTER_SOLVER(solver_type, solver_factory)
absl::Status ToStatus() const