28#include "absl/algorithm/container.h"
29#include "absl/container/flat_hash_map.h"
30#include "absl/container/flat_hash_set.h"
31#include "absl/log/check.h"
32#include "absl/memory/memory.h"
33#include "absl/meta/type_traits.h"
34#include "absl/status/status.h"
35#include "absl/status/statusor.h"
36#include "absl/strings/escaping.h"
37#include "absl/strings/str_cat.h"
38#include "absl/strings/str_join.h"
39#include "absl/strings/string_view.h"
40#include "absl/time/clock.h"
41#include "absl/time/time.h"
42#include "absl/types/span.h"
43#include "google/protobuf/repeated_ptr_field.h"
49#include "ortools/math_opt/callback.pb.h"
57#include "ortools/math_opt/infeasible_subsystem.pb.h"
58#include "ortools/math_opt/model.pb.h"
59#include "ortools/math_opt/model_parameters.pb.h"
60#include "ortools/math_opt/model_update.pb.h"
61#include "ortools/math_opt/parameters.pb.h"
62#include "ortools/math_opt/result.pb.h"
63#include "ortools/math_opt/solution.pb.h"
64#include "ortools/math_opt/solvers/gurobi.pb.h"
68#include "ortools/math_opt/sparse_containers.pb.h"
88absl::StatusOr<std::unique_ptr<Gurobi>> GurobiFromInitArgs(
91 return absl::FailedPreconditionError(
92 "the Gurobi library is not compatible with any sanitizer (MSAN, ASAN "
99 init_args.non_streamable !=
nullptr
102 std::unique_ptr<Gurobi> gurobi;
103 if (non_streamable_args !=
nullptr &&
104 non_streamable_args->primary_env !=
nullptr) {
106 }
else if (init_args.streamable.has_gurobi() &&
107 init_args.streamable.gurobi().has_isv_key()) {
117inline BasisStatusProto ConvertVariableStatus(
const int status) {
120 return BASIS_STATUS_BASIC;
122 return BASIS_STATUS_AT_LOWER_BOUND;
124 return BASIS_STATUS_AT_UPPER_BOUND;
126 return BASIS_STATUS_FREE;
128 return BASIS_STATUS_UNSPECIFIED;
132inline int GrbVariableStatus(
const BasisStatusProto status) {
134 case BASIS_STATUS_BASIC:
136 case BASIS_STATUS_AT_LOWER_BOUND:
137 case BASIS_STATUS_FIXED_VALUE:
139 case BASIS_STATUS_AT_UPPER_BOUND:
141 case BASIS_STATUS_FREE:
143 case BASIS_STATUS_UNSPECIFIED:
145 LOG(FATAL) <<
"Unexpected invalid initial_basis.";
152absl::StatusOr<GurobiParametersProto> MergeParameters(
153 const SolveParametersProto& solve_parameters,
154 const ModelSolveParametersProto& model_parameters,
const bool is_mip,
155 const bool is_multi_objective_mode) {
156 GurobiParametersProto merged_parameters;
159 GurobiParametersProto::Parameter*
const parameter =
160 merged_parameters.add_parameters();
162 parameter->set_value(solve_parameters.enable_output() ?
"1" :
"0");
166 absl::Duration
time_limit = absl::InfiniteDuration();
167 if (solve_parameters.has_time_limit()) {
169 solve_parameters.time_limit()));
174 if (!is_multi_objective_mode &&
175 model_parameters.primary_objective_parameters().has_time_limit()) {
177 const absl::Duration primary_objective_time_limit,
179 model_parameters.primary_objective_parameters().time_limit()));
183 GurobiParametersProto::Parameter*
const parameter =
184 merged_parameters.add_parameters();
186 parameter->set_value(absl::StrCat(absl::ToDoubleSeconds(
time_limit)));
190 if (solve_parameters.has_node_limit()) {
191 GurobiParametersProto::Parameter*
const parameter =
192 merged_parameters.add_parameters();
194 parameter->set_value(absl::StrCat(solve_parameters.node_limit()));
197 if (solve_parameters.has_threads()) {
198 const int threads = solve_parameters.threads();
199 GurobiParametersProto::Parameter*
const parameter =
200 merged_parameters.add_parameters();
202 parameter->set_value(absl::StrCat(threads));
205 if (solve_parameters.has_absolute_gap_tolerance()) {
206 const double absolute_gap_tolerance =
207 solve_parameters.absolute_gap_tolerance();
208 GurobiParametersProto::Parameter*
const parameter =
209 merged_parameters.add_parameters();
211 parameter->set_value(absl::StrCat(absolute_gap_tolerance));
214 if (solve_parameters.has_relative_gap_tolerance()) {
215 const double relative_gap_tolerance =
216 solve_parameters.relative_gap_tolerance();
217 GurobiParametersProto::Parameter*
const parameter =
218 merged_parameters.add_parameters();
220 parameter->set_value(absl::StrCat(relative_gap_tolerance));
223 if (solve_parameters.has_cutoff_limit()) {
224 GurobiParametersProto::Parameter*
const parameter =
225 merged_parameters.add_parameters();
227 parameter->set_value(absl::StrCat(solve_parameters.cutoff_limit()));
230 if (solve_parameters.has_objective_limit()) {
232 return absl::InvalidArgumentError(
233 "objective_limit is only supported for Gurobi on MIP models");
235 GurobiParametersProto::Parameter*
const parameter =
236 merged_parameters.add_parameters();
238 parameter->set_value(absl::StrCat(solve_parameters.objective_limit()));
241 if (solve_parameters.has_best_bound_limit()) {
243 return absl::InvalidArgumentError(
244 "best_bound_limit is only supported for Gurobi on MIP models");
246 GurobiParametersProto::Parameter*
const parameter =
247 merged_parameters.add_parameters();
249 parameter->set_value(absl::StrCat(solve_parameters.best_bound_limit()));
252 if (solve_parameters.has_solution_limit()) {
253 GurobiParametersProto::Parameter*
const parameter =
254 merged_parameters.add_parameters();
256 parameter->set_value(absl::StrCat(solve_parameters.solution_limit()));
259 if (solve_parameters.has_random_seed()) {
260 const int random_seed =
261 std::min(
GRB_MAXINT, std::max(solve_parameters.random_seed(), 0));
262 GurobiParametersProto::Parameter*
const parameter =
263 merged_parameters.add_parameters();
265 parameter->set_value(absl::StrCat(random_seed));
268 if (solve_parameters.has_solution_pool_size()) {
269 GurobiParametersProto::Parameter*
const solution_pool_size =
270 merged_parameters.add_parameters();
272 solution_pool_size->set_value(
273 absl::StrCat(solve_parameters.solution_pool_size()));
276 if (solve_parameters.lp_algorithm() != LP_ALGORITHM_UNSPECIFIED) {
277 GurobiParametersProto::Parameter*
const parameter =
278 merged_parameters.add_parameters();
280 switch (solve_parameters.lp_algorithm()) {
281 case LP_ALGORITHM_PRIMAL_SIMPLEX:
284 case LP_ALGORITHM_DUAL_SIMPLEX:
287 case LP_ALGORITHM_BARRIER:
290 case LP_ALGORITHM_FIRST_ORDER:
291 return absl::InvalidArgumentError(
292 "lp_algorithm FIRST_ORDER is not supported for gurobi");
294 LOG(FATAL) <<
"LPAlgorithm: "
296 <<
" unknown, error setting Gurobi parameters";
300 if (solve_parameters.scaling() != EMPHASIS_UNSPECIFIED) {
301 GurobiParametersProto::Parameter*
const parameter =
302 merged_parameters.add_parameters();
304 switch (solve_parameters.scaling()) {
306 parameter->set_value(absl::StrCat(0));
309 case EMPHASIS_MEDIUM:
310 parameter->set_value(absl::StrCat(1));
313 parameter->set_value(absl::StrCat(2));
315 case EMPHASIS_VERY_HIGH:
316 parameter->set_value(absl::StrCat(3));
319 LOG(FATAL) <<
"Scaling emphasis: "
321 <<
" unknown, error setting Gurobi parameters";
325 if (solve_parameters.cuts() != EMPHASIS_UNSPECIFIED) {
326 GurobiParametersProto::Parameter*
const parameter =
327 merged_parameters.add_parameters();
329 switch (solve_parameters.cuts()) {
331 parameter->set_value(absl::StrCat(0));
334 case EMPHASIS_MEDIUM:
335 parameter->set_value(absl::StrCat(1));
338 parameter->set_value(absl::StrCat(2));
340 case EMPHASIS_VERY_HIGH:
341 parameter->set_value(absl::StrCat(3));
344 LOG(FATAL) <<
"Cuts emphasis: "
346 <<
" unknown, error setting Gurobi parameters";
350 if (solve_parameters.heuristics() != EMPHASIS_UNSPECIFIED) {
351 GurobiParametersProto::Parameter*
const parameter =
352 merged_parameters.add_parameters();
354 switch (solve_parameters.heuristics()) {
356 parameter->set_value(absl::StrCat(0.0));
359 parameter->set_value(absl::StrCat(0.025));
361 case EMPHASIS_MEDIUM:
364 parameter->set_value(absl::StrCat(0.05));
367 parameter->set_value(absl::StrCat(0.1));
369 case EMPHASIS_VERY_HIGH:
370 parameter->set_value(absl::StrCat(0.2));
373 LOG(FATAL) <<
"Heuristics emphasis: "
375 <<
" unknown, error setting Gurobi parameters";
379 if (solve_parameters.presolve() != EMPHASIS_UNSPECIFIED) {
380 GurobiParametersProto::Parameter*
const parameter =
381 merged_parameters.add_parameters();
383 switch (solve_parameters.presolve()) {
385 parameter->set_value(absl::StrCat(0));
388 case EMPHASIS_MEDIUM:
389 parameter->set_value(absl::StrCat(1));
392 case EMPHASIS_VERY_HIGH:
393 parameter->set_value(absl::StrCat(2));
396 LOG(FATAL) <<
"Presolve emphasis: "
398 <<
" unknown, error setting Gurobi parameters";
402 if (solve_parameters.has_iteration_limit()) {
403 GurobiParametersProto::Parameter*
const iterationlimit =
404 merged_parameters.add_parameters();
406 iterationlimit->set_value(absl::StrCat(solve_parameters.iteration_limit()));
407 GurobiParametersProto::Parameter*
const bariterlimit =
408 merged_parameters.add_parameters();
410 double val = std::min<double>(std::numeric_limits<int32_t>::max(),
411 solve_parameters.iteration_limit());
412 bariterlimit->set_value(absl::StrCat(val));
415 for (
const GurobiParametersProto::Parameter& parameter :
416 solve_parameters.gurobi().parameters()) {
417 *merged_parameters.add_parameters() = parameter;
420 return merged_parameters;
423absl::StatusOr<int64_t> SafeInt64FromDouble(
const double d) {
424 const int64_t result =
static_cast<int64_t
>(d);
425 if (
static_cast<double>(result) != d) {
426 return absl::InternalError(
427 absl::StrCat(
"Expected double ", d,
" to contain an int64_t."));
432const absl::flat_hash_set<CallbackEventProto>& SupportedMIPEvents() {
433 static const auto*
const kEvents =
434 new absl::flat_hash_set<CallbackEventProto>({
435 CALLBACK_EVENT_PRESOLVE, CALLBACK_EVENT_SIMPLEX, CALLBACK_EVENT_MIP,
436 CALLBACK_EVENT_MIP_SOLUTION, CALLBACK_EVENT_MIP_NODE,
445const absl::flat_hash_set<CallbackEventProto>& SupportedLPEvents() {
446 static const auto*
const kEvents =
447 new absl::flat_hash_set<CallbackEventProto>({
448 CALLBACK_EVENT_PRESOLVE,
449 CALLBACK_EVENT_SIMPLEX,
450 CALLBACK_EVENT_BARRIER,
457constexpr std::size_t kMaxNameSize = 255;
460std::string TruncateName(
const std::string_view original_name) {
462 original_name.substr(0, std::min(kMaxNameSize, original_name.size())));
466std::vector<std::string> TruncateNames(
467 const google::protobuf::RepeatedPtrField<std::string>& original_names) {
468 std::vector<std::string> result;
469 result.reserve(original_names.size());
470 for (
const std::string& original_name : original_names) {
471 result.push_back(TruncateName(original_name));
476absl::Status SafeGurobiDouble(
const double d) {
479 <<
"finite value: " << d <<
" will be treated as infinite by Gurobi";
481 return absl::OkStatus();
484std::string EscapedNameForLogging(
const absl::string_view name) {
485 return absl::StrCat(
"\"", absl::Utf8SafeCEscape(name),
"\"");
488constexpr int kDeletedIndex = -1;
489constexpr int kUnsetIndex = -2;
496std::vector<int> IndexUpdateMap(
const int size_before_delete,
497 absl::Span<const int> deletes) {
498 std::vector<int> result(size_before_delete, kUnsetIndex);
499 for (
const int del : deletes) {
500 result[del] = kDeletedIndex;
503 for (
int& r : result) {
504 if (r != kDeletedIndex) {
508 CHECK_GT(r, kUnsetIndex);
513absl::StatusOr<bool> GetIntAttrElementAsBool(
Gurobi& gurobi,
514 const char*
const name,
517 const bool cast_value(value);
518 if (
static_cast<int>(cast_value) != value) {
520 <<
"Gurobi unexpectedly returned non-boolean value for " << name
528 OrAccumulator() =
default;
531 absl::Status Or(absl::StatusOr<bool> update) {
534 return absl::OkStatus();
536 bool value()
const {
return value_; }
544absl::Status AddMapEntryIfPresent(
545 const int64_t map_id,
546 absl::StatusOr<std::optional<ModelSubsetProto::Bounds>> maybe_value,
547 google::protobuf::Map<int64_t, ModelSubsetProto::Bounds>& target) {
549 if (maybe_value->has_value()) {
550 target[map_id] = **std::move(maybe_value);
552 return absl::OkStatus();
557absl::Status AppendEntryIfTrue(
558 const int64_t
id, absl::StatusOr<bool> should_append,
559 google::protobuf::RepeatedField<int64_t>& target) {
561 if (*should_append) {
564 return absl::OkStatus();
569GurobiSolver::GurobiSolver(std::unique_ptr<Gurobi> g_gurobi)
570 : gurobi_(std::move(g_gurobi)) {}
572GurobiSolver::GurobiModelElements
573GurobiSolver::LinearConstraintData::DependentElements()
const {
574 GurobiModelElements elements;
575 CHECK_NE(constraint_index, kUnspecifiedConstraint);
576 elements.linear_constraints.push_back(constraint_index);
577 if (slack_index != kUnspecifiedIndex) {
578 elements.variables.push_back(slack_index);
583GurobiSolver::GurobiModelElements
584GurobiSolver::SecondOrderConeConstraintData::DependentElements()
const {
585 const auto index_is_valid = [](
const auto index) {
return index >= 0; };
586 CHECK(absl::c_all_of(slack_variables, index_is_valid));
587 CHECK(absl::c_all_of(slack_constraints, index_is_valid));
588 CHECK_NE(constraint_index, kUnspecifiedConstraint);
589 GurobiModelElements elements{.variables = slack_variables,
590 .linear_constraints = slack_constraints};
591 elements.quadratic_constraints.push_back(constraint_index);
595GurobiSolver::GurobiModelElements
596GurobiSolver::SosConstraintData::DependentElements()
const {
597 const auto index_is_valid = [](
const auto index) {
return index >= 0; };
598 CHECK(absl::c_all_of(slack_variables, index_is_valid));
599 CHECK(absl::c_all_of(slack_constraints, index_is_valid));
600 CHECK_NE(constraint_index, kUnspecifiedConstraint);
601 GurobiModelElements elements{.variables = slack_variables,
602 .linear_constraints = slack_constraints};
603 elements.sos_constraints.push_back(constraint_index);
607absl::StatusOr<TerminationProto> GurobiSolver::ConvertTerminationReason(
608 const int gurobi_status,
const SolutionClaims solution_claims,
609 const double best_primal_bound,
const double best_dual_bound) {
611 switch (gurobi_status) {
620 is_maximize, solution_claims.dual_feasible_solution_exists
621 ? FEASIBILITY_STATUS_FEASIBLE
622 : FEASIBILITY_STATUS_UNDETERMINED);
626 if (solution_claims.primal_feasible_solution_exists) {
631 FEASIBILITY_STATUS_INFEASIBLE,
632 "Gurobi status GRB_UNBOUNDED");
636 FEASIBILITY_STATUS_UNDETERMINED,
637 "Gurobi status GRB_UNBOUNDED");
642 LIMIT_ITERATION, best_primal_bound, best_dual_bound,
643 solution_claims.dual_feasible_solution_exists);
646 LIMIT_NODE, best_primal_bound, best_dual_bound,
647 solution_claims.dual_feasible_solution_exists);
650 LIMIT_TIME, best_primal_bound, best_dual_bound,
651 solution_claims.dual_feasible_solution_exists);
654 LIMIT_SOLUTION, best_primal_bound, best_dual_bound,
655 solution_claims.dual_feasible_solution_exists);
658 LIMIT_INTERRUPTED, best_primal_bound, best_dual_bound,
659 solution_claims.dual_feasible_solution_exists);
662 TERMINATION_REASON_NUMERICAL_ERROR);
664 if (is_multi_objective_mode()) {
669 LIMIT_TIME, best_primal_bound, best_dual_bound,
670 solution_claims.dual_feasible_solution_exists,
671 "Gurobi returned GRB_SUBOPTIMAL for a multi-objective model, which "
672 "indicates that one or more objectives hit their per-objective "
683 LIMIT_OBJECTIVE, best_primal_bound, best_dual_bound,
684 solution_claims.dual_feasible_solution_exists);
686 return absl::InternalError(
687 "Error creating termination reason, unexpected gurobi status code "
690 return absl::InternalError(
691 "Error creating termination reason, unexpected gurobi status code "
694 return absl::InternalError(absl::StrCat(
695 "Missing Gurobi optimization status code case: ", gurobi_status));
699absl::StatusOr<bool> GurobiSolver::IsMaximize()
const {
705absl::StatusOr<bool> GurobiSolver::IsMIP()
const {
707 return static_cast<bool>(is_mip);
711absl::StatusOr<bool> GurobiSolver::IsQP()
const {
713 return static_cast<bool>(is_qp);
716absl::StatusOr<bool> GurobiSolver::IsQCP()
const {
718 return static_cast<bool>(is_qcp);
724void GurobiSolver::GurobiVectorToSparseDoubleVector(
725 const absl::Span<const double> gurobi_values,
const T& map,
726 SparseDoubleVectorProto& result,
727 const SparseVectorFilterProto& filter)
const {
728 SparseVectorFilterPredicate predicate(filter);
729 for (
auto [
id, gurobi_data] : map) {
730 const double value = gurobi_values[get_model_index(gurobi_data)];
731 if (predicate.AcceptsAndUpdate(
id, value)) {
733 result.add_values(value);
738absl::Status GurobiSolver::SetGurobiBasis(
const BasisProto& basis) {
739 std::vector<int> gurobi_variable_basis_status(num_gurobi_variables_);
740 for (
const auto [
id, value] :
MakeView(basis.variable_status())) {
741 gurobi_variable_basis_status[variables_map_.at(
id)] =
742 GrbVariableStatus(
static_cast<BasisStatusProto
>(value));
745 std::vector<int> gurobi_constraint_basis_status;
746 gurobi_constraint_basis_status.reserve(num_gurobi_lin_cons_);
747 for (
const auto [
id, value] :
MakeView(basis.constraint_status())) {
748 const LinearConstraintData& constraint_data =
749 linear_constraints_map_.at(
id);
751 if (constraint_data.slack_index == kUnspecifiedIndex) {
752 if (value == BASIS_STATUS_BASIC) {
753 gurobi_constraint_basis_status.push_back(kGrbBasicConstraint);
755 gurobi_constraint_basis_status.push_back(kGrbNonBasicConstraint);
758 }
else if (value == BASIS_STATUS_BASIC) {
762 gurobi_variable_basis_status[constraint_data.slack_index] =
GRB_BASIC;
763 gurobi_constraint_basis_status.push_back(kGrbNonBasicConstraint);
765 gurobi_variable_basis_status[constraint_data.slack_index] =
766 GrbVariableStatus(
static_cast<BasisStatusProto
>(value));
767 gurobi_constraint_basis_status.push_back(kGrbNonBasicConstraint);
771 gurobi_variable_basis_status));
773 gurobi_constraint_basis_status));
774 return absl::OkStatus();
777absl::StatusOr<BasisProto> GurobiSolver::GetGurobiBasis() {
780 const std::vector<int> gurobi_variable_basis_status,
783 for (
auto [variable_id, gurobi_variable_index] : variables_map_) {
784 basis.mutable_variable_status()->add_ids(variable_id);
785 const BasisStatusProto variable_status = ConvertVariableStatus(
786 gurobi_variable_basis_status[gurobi_variable_index]);
787 if (variable_status == BASIS_STATUS_UNSPECIFIED) {
788 return absl::InternalError(
789 absl::StrCat(
"Invalid Gurobi variable basis status: ",
790 gurobi_variable_basis_status[gurobi_variable_index]));
792 basis.mutable_variable_status()->add_values(variable_status);
796 const std::vector<int> gurobi_constraint_basis_status,
798 for (
auto [constraint_id, gurobi_data] : linear_constraints_map_) {
799 basis.mutable_constraint_status()->add_ids(constraint_id);
800 const int gurobi_constraint_status =
801 gurobi_constraint_basis_status[gurobi_data.constraint_index];
802 if ((gurobi_constraint_status != kGrbBasicConstraint) &&
803 (gurobi_constraint_status != kGrbNonBasicConstraint)) {
804 return absl::InternalError(
805 absl::StrCat(
"Invalid Gurobi constraint basis status: ",
806 gurobi_constraint_status));
811 if (gurobi_constraint_status == kGrbBasicConstraint) {
812 basis.mutable_constraint_status()->add_values(BASIS_STATUS_BASIC);
814 basis.mutable_constraint_status()->add_values(
815 BASIS_STATUS_AT_UPPER_BOUND);
820 if (gurobi_constraint_status == kGrbBasicConstraint) {
821 basis.mutable_constraint_status()->add_values(BASIS_STATUS_BASIC);
823 basis.mutable_constraint_status()->add_values(
824 BASIS_STATUS_AT_LOWER_BOUND);
827 }
else if (gurobi_data.lower_bound == gurobi_data.upper_bound) {
828 if (gurobi_constraint_status == kGrbBasicConstraint) {
829 basis.mutable_constraint_status()->add_values(BASIS_STATUS_BASIC);
833 basis.mutable_constraint_status()->add_values(BASIS_STATUS_FIXED_VALUE);
837 const BasisStatusProto slack_status = ConvertVariableStatus(
838 gurobi_variable_basis_status[gurobi_data.slack_index]);
839 if (slack_status == BASIS_STATUS_UNSPECIFIED) {
840 return absl::InternalError(absl::StrCat(
841 "Invalid Gurobi slack variable basis status: ", slack_status));
843 if ((gurobi_constraint_status == kGrbBasicConstraint) ||
844 (slack_status == BASIS_STATUS_BASIC)) {
845 basis.mutable_constraint_status()->add_values(BASIS_STATUS_BASIC);
847 basis.mutable_constraint_status()->add_values(slack_status);
854absl::StatusOr<DualRayProto> GurobiSolver::GetGurobiDualRay(
855 const SparseVectorFilterProto& linear_constraints_filter,
856 const SparseVectorFilterProto& variables_filter,
const bool is_maximize) {
860 num_gurobi_lin_cons_));
862 DualRayProto dual_ray;
866 SparseVectorFilterPredicate predicate(linear_constraints_filter);
867 for (
auto [constraint_id, gurobi_data] : linear_constraints_map_) {
869 const double value = -farkas_dual[gurobi_data.constraint_index];
870 if (predicate.AcceptsAndUpdate(constraint_id, value)) {
871 dual_ray.mutable_dual_values()->add_ids(constraint_id);
873 dual_ray.mutable_dual_values()->add_values(-value);
875 dual_ray.mutable_dual_values()->add_values(value);
883 SparseVectorFilterPredicate predicate(variables_filter);
884 for (
auto [var_id, gurobi_variable_index] : variables_map_) {
887 double reduced_cost_value = 0.0;
889 gurobi_->GetVars(gurobi_variable_index, 1));
890 for (
int i = 0;
i < column.inds.size(); ++
i) {
891 reduced_cost_value += farkas_dual[column.inds[
i]] * column.vals[
i];
893 if (predicate.AcceptsAndUpdate(var_id, reduced_cost_value)) {
894 dual_ray.mutable_reduced_costs()->add_ids(var_id);
896 dual_ray.mutable_reduced_costs()->add_values(-reduced_cost_value);
898 dual_ray.mutable_reduced_costs()->add_values(reduced_cost_value);
906absl::StatusOr<SolveResultProto> GurobiSolver::ExtractSolveResultProto(
907 const absl::Time start,
const ModelSolveParametersProto& model_parameters) {
908 SolveResultProto result;
911 SolutionClaims solution_claims;
913 GetSolutions(model_parameters));
921 solution_and_claims.solutions.clear();
922 solution_and_claims.solution_claims.primal_feasible_solution_exists =
false;
923 solution_and_claims.solution_claims.dual_feasible_solution_exists =
true;
926 GetBestPrimalBound(solution_and_claims.solutions));
928 GetBestDualBound(solution_and_claims.solutions));
929 solution_claims = solution_and_claims.solution_claims;
934 for (SolutionProto& solution : solution_and_claims.solutions) {
935 *result.add_solutions() = std::move(solution);
939 *result.mutable_termination(),
940 ConvertTerminationReason(grb_termination, solution_claims,
941 best_primal_bound, best_dual_bound));
944 return std::move(result);
947absl::StatusOr<bool> GurobiSolver::AnyElementInIIS(
948 const GurobiModelElements& grb_elements) {
949 OrAccumulator any_in_iis;
950 for (
const GurobiVariableIndex grb_index : grb_elements.variables) {
953 for (
const GurobiLinearConstraintIndex grb_index :
954 grb_elements.linear_constraints) {
958 for (
const GurobiQuadraticConstraintIndex grb_index :
959 grb_elements.quadratic_constraints) {
963 for (
const GurobiSosConstraintIndex grb_index :
964 grb_elements.sos_constraints) {
968 for (
const GurobiGeneralConstraintIndex grb_index :
969 grb_elements.general_constraints) {
973 return any_in_iis.value();
978absl::StatusOr<std::optional<ModelSubsetProto::Bounds>>
979GurobiSolver::VariableBoundsInIIS(
const GurobiVariableIndex grb_index) {
981 const bool var_lb_is_in_iis,
984 const bool var_ub_is_in_iis,
986 if (!var_lb_is_in_iis && !var_ub_is_in_iis) {
989 ModelSubsetProto::Bounds bounds;
990 bounds.set_lower(var_lb_is_in_iis);
991 bounds.set_upper(var_ub_is_in_iis);
995absl::StatusOr<bool> GurobiSolver::VariableInIIS(
996 const GurobiVariableIndex grb_index) {
998 VariableBoundsInIIS(grb_index));
999 return bounds.has_value();
1002absl::StatusOr<std::optional<ModelSubsetProto::Bounds>>
1003GurobiSolver::LinearConstraintInIIS(
const LinearConstraintData& grb_data) {
1010 AnyElementInIIS(grb_data.DependentElements()));
1011 if (!constr_in_iis) {
1012 return std::nullopt;
1014 ModelSubsetProto::Bounds result;
1015 result.set_lower(grb_data.lower_bound != -kInf);
1016 result.set_upper(grb_data.upper_bound != kInf);
1020absl::StatusOr<std::optional<ModelSubsetProto::Bounds>>
1021GurobiSolver::QuadraticConstraintInIIS(
1022 const GurobiQuadraticConstraintIndex grb_index) {
1024 const bool constr_in_iis,
1026 if (!constr_in_iis) {
1027 return std::nullopt;
1030 const char constr_sense,
1032 ModelSubsetProto::Bounds result;
1033 result.set_lower(constr_sense ==
GRB_EQUAL ||
1039absl::StatusOr<ComputeInfeasibleSubsystemResultProto>
1040GurobiSolver::ExtractComputeInfeasibleSubsystemResultProto(
1041 const bool proven_infeasible) {
1042 ComputeInfeasibleSubsystemResultProto result;
1043 if (!proven_infeasible) {
1044 result.set_feasibility(FEASIBILITY_STATUS_UNDETERMINED);
1047 result.set_feasibility(FEASIBILITY_STATUS_INFEASIBLE);
1051 result.set_is_minimal(is_minimal);
1053 for (
const auto [
id, grb_index] : variables_map_) {
1055 id, VariableBoundsInIIS(grb_index),
1056 *result.mutable_infeasible_subsystem()->mutable_variable_bounds()));
1061 const char var_type,
1064 result.mutable_infeasible_subsystem()->add_variable_integrality(
1070 for (
const auto [
id, grb_data] : linear_constraints_map_) {
1072 id, LinearConstraintInIIS(grb_data),
1073 *result.mutable_infeasible_subsystem()->mutable_linear_constraints()));
1076 for (
const auto [
id, grb_index] : quadratic_constraints_map_) {
1078 id, QuadraticConstraintInIIS(grb_index),
1079 *result.mutable_infeasible_subsystem()
1080 ->mutable_quadratic_constraints()));
1083 for (
const auto& [
id, grb_data] : soc_constraints_map_) {
1085 id, AnyElementInIIS(grb_data.DependentElements()),
1086 *result.mutable_infeasible_subsystem()
1087 ->mutable_second_order_cone_constraints()));
1090 for (
const auto& [
id, grb_data] : sos1_constraints_map_) {
1092 id, AnyElementInIIS(grb_data.DependentElements()),
1093 *result.mutable_infeasible_subsystem()->mutable_sos1_constraints()));
1096 for (
const auto& [
id, grb_data] : sos2_constraints_map_) {
1098 id, AnyElementInIIS(grb_data.DependentElements()),
1099 *result.mutable_infeasible_subsystem()->mutable_sos2_constraints()));
1102 for (
const auto& [
id, maybe_grb_data] : indicator_constraints_map_) {
1103 if (!maybe_grb_data.has_value()) {
1110 maybe_grb_data->constraint_index),
1111 *result.mutable_infeasible_subsystem()
1112 ->mutable_indicator_constraints()));
1120absl::StatusOr<GurobiSolver::SolutionsAndClaims> GurobiSolver::GetSolutions(
1121 const ModelSolveParametersProto& model_parameters) {
1128 return GetMipSolutions(model_parameters);
1129 }
else if (is_qcp) {
1130 return GetQcpSolution(model_parameters);
1132 return GetQpSolution(model_parameters);
1134 return GetLpSolution(model_parameters);
1140absl::StatusOr<SolveStatsProto> GurobiSolver::GetSolveStats(
1141 const absl::Time start)
const {
1142 SolveStatsProto solve_stats;
1145 solve_stats.mutable_solve_time()));
1151 SafeInt64FromDouble(simplex_iters_double));
1152 LOG_IF(ERROR, simplex_iters < 0)
1153 <<
"Expected GRB_DBL_ATTR_ITERCOUNT to be non-negative, got: "
1154 << simplex_iters <<
"; clamping to 0";
1155 solve_stats.set_simplex_iterations(std::max(int64_t{0}, simplex_iters));
1161 LOG_IF(ERROR, barrier_iters < 0)
1162 <<
"Expected GRB_INT_ATTR_BARITERCOUNT to be non-negative, got: "
1163 << barrier_iters <<
"; clamping to 0";
1164 solve_stats.set_barrier_iterations(std::max(0, barrier_iters));
1171 LOG_IF(ERROR, nodes < 0)
1172 <<
"Expected GRB_DBL_ATTR_NODECOUNT to be non-negative, got: " << nodes
1173 <<
"; clamping to 0";
1174 solve_stats.set_node_count(std::max(int64_t{0}, nodes));
1179absl::StatusOr<GurobiSolver::SolutionsAndClaims> GurobiSolver::GetMipSolutions(
1180 const ModelSolveParametersProto& model_parameters) {
1181 int num_solutions = 0;
1185 std::vector<SolutionProto> solutions;
1186 solutions.reserve(num_solutions);
1188 for (
int i = 0;
i < num_solutions; ++
i) {
1195 if (is_multi_objective_mode()) {
1196 for (
const auto [
id, grb_index] : multi_objectives_map_) {
1202 if (
id.has_value()) {
1213 i == 0 ? SOLUTION_STATUS_FEASIBLE : SOLUTION_STATUS_UNDETERMINED);
1215 const std::vector<double> grb_var_values,
1217 GurobiVectorToSparseDoubleVector(grb_var_values, variables_map_,
1219 model_parameters.variable_values_filter());
1220 *solutions.emplace_back(SolutionProto()).mutable_primal_solution() =
1221 std::move(primal_solution);
1239 const SolutionClaims solution_claims = {
1240 .primal_feasible_solution_exists = num_solutions > 0,
1241 .dual_feasible_solution_exists =
1242 std::isfinite(best_dual_bound) || grb_termination ==
GRB_INFEASIBLE ||
1243 (is_multi_objective_mode() && grb_termination ==
GRB_OPTIMAL)};
1246 if (grb_termination ==
GRB_OPTIMAL && num_solutions == 0) {
1247 return absl::InternalError(
1248 "GRB_INT_ATTR_STATUS == GRB_OPTIMAL, but solution pool is empty.");
1252 if (!is_multi_objective_mode() && grb_termination ==
GRB_OPTIMAL &&
1253 !std::isfinite(best_dual_bound)) {
1254 return absl::InternalError(
1255 "GRB_INT_ATTR_STATUS == GRB_OPTIMAL, but GRB_DBL_ATTR_OBJBOUND is "
1256 "unavailable or infinite.");
1259 return SolutionsAndClaims{.solutions = std::move(solutions),
1260 .solution_claims = solution_claims};
1263absl::StatusOr<GurobiSolver::SolutionAndClaim<PrimalSolutionProto>>
1264GurobiSolver::GetConvexPrimalSolutionIfAvailable(
1265 const ModelSolveParametersProto& model_parameters) {
1267 return SolutionAndClaim<PrimalSolutionProto>{
1268 .solution = std::nullopt, .feasible_solution_exists =
false};
1275 const std::vector<double> grb_var_values,
1276 gurobi_->GetDoubleAttrArray(
GRB_DBL_ATTR_X, num_gurobi_variables_));
1290 double objective_value = 0.0;
1292 const std::vector<double> linear_obj_coefs,
1294 for (
int i = 0;
i < num_gurobi_variables_; ++
i) {
1295 objective_value += linear_obj_coefs[
i] * grb_var_values[
i];
1305 }
else if (PrimalSolutionQualityAvailable()) {
1306 ASSIGN_OR_RETURN(
const double solution_quality, GetPrimalSolutionQuality());
1309 if (solution_quality <= tolerance) {
1316 GurobiVectorToSparseDoubleVector(grb_var_values, variables_map_,
1318 model_parameters.variable_values_filter());
1319 const bool primal_feasible_solution_exists =
1321 return SolutionAndClaim<PrimalSolutionProto>{
1322 .solution = std::move(primal_solution),
1323 .feasible_solution_exists = primal_feasible_solution_exists};
1326bool GurobiSolver::PrimalSolutionQualityAvailable()
const {
1335absl::StatusOr<double> GurobiSolver::GetPrimalSolutionQuality()
const {
1348 return std::max({constraint_residual, constraint_violation, bound_violation,
1349 constraint_scaled_residual, constraint_scaled_violation,
1350 bound_scaled_violation});
1353absl::StatusOr<double> GurobiSolver::GetBestPrimalBound(
1354 absl::Span<const SolutionProto> solutions)
const {
1361 double best_objective_value = is_maximize ? -
kInf :
kInf;
1362 for (
const SolutionProto& solution : solutions) {
1363 if (
solution.has_primal_solution() &&
1364 solution.primal_solution().feasibility_status() ==
1365 SOLUTION_STATUS_FEASIBLE) {
1366 const double sol_val =
solution.primal_solution().objective_value();
1367 best_objective_value = is_maximize
1368 ? std::max(best_objective_value, sol_val)
1369 :
std::min(best_objective_value, sol_val);
1372 return best_objective_value;
1375absl::StatusOr<double> GurobiSolver::GetBestDualBound(
1376 absl::Span<const SolutionProto> solutions)
const {
1382 for (
const SolutionProto& solution : solutions) {
1383 if (
solution.has_dual_solution() &&
1384 solution.dual_solution().feasibility_status() ==
1385 SOLUTION_STATUS_FEASIBLE &&
1386 solution.dual_solution().has_objective_value()) {
1387 const double sol_val =
solution.dual_solution().objective_value();
1388 best_dual_bound = is_maximize ? std::min(best_dual_bound, sol_val)
1389 :
std::max(best_dual_bound, sol_val);
1392 return best_dual_bound;
1395absl::StatusOr<double> GurobiSolver::GetGurobiBestDualBound()
const {
1400 !is_multi_objective_mode()) {
1413absl::StatusOr<std::optional<BasisProto>> GurobiSolver::GetBasisIfAvailable() {
1419 basis.set_basic_dual_feasibility(SOLUTION_STATUS_UNDETERMINED);
1421 basis.set_basic_dual_feasibility(SOLUTION_STATUS_FEASIBLE);
1423 basis.set_basic_dual_feasibility(SOLUTION_STATUS_INFEASIBLE);
1426 return std::move(basis);
1428 return std::nullopt;
1431absl::StatusOr<GurobiSolver::SolutionsAndClaims> GurobiSolver::GetLpSolution(
1432 const ModelSolveParametersProto& model_parameters) {
1434 GetConvexPrimalSolutionIfAvailable(model_parameters));
1436 GetConvexDualSolutionIfAvailable(model_parameters));
1438 const SolutionClaims solution_claims = {
1439 .primal_feasible_solution_exists =
1440 primal_solution_and_claim.feasible_solution_exists,
1441 .dual_feasible_solution_exists =
1442 dual_solution_and_claim.feasible_solution_exists};
1444 if (!primal_solution_and_claim.solution.has_value() &&
1445 !dual_solution_and_claim.solution.has_value() && !basis.has_value()) {
1446 return SolutionsAndClaims{.solution_claims = solution_claims};
1448 SolutionsAndClaims solution_and_claims{.solution_claims = solution_claims};
1450 solution_and_claims.solutions.emplace_back(SolutionProto());
1451 if (primal_solution_and_claim.solution.has_value()) {
1452 *
solution.mutable_primal_solution() =
1453 std::move(*primal_solution_and_claim.solution);
1455 if (dual_solution_and_claim.solution.has_value()) {
1456 *
solution.mutable_dual_solution() =
1457 std::move(*dual_solution_and_claim.solution);
1459 if (basis.has_value()) {
1460 *
solution.mutable_basis() = std::move(*basis);
1462 return solution_and_claims;
1465absl::StatusOr<GurobiSolver::SolutionAndClaim<DualSolutionProto>>
1466GurobiSolver::GetConvexDualSolutionIfAvailable(
1467 const ModelSolveParametersProto& model_parameters) {
1470 return SolutionAndClaim<DualSolutionProto>{
1471 .solution = std::nullopt, .feasible_solution_exists =
false};
1476 DualSolutionProto dual_solution;
1477 bool dual_feasible_solution_exists =
false;
1479 const std::vector<double> grb_constraint_duals,
1481 GurobiVectorToSparseDoubleVector(grb_constraint_duals,
1482 linear_constraints_map_,
1483 *dual_solution.mutable_dual_values(),
1484 model_parameters.dual_values_filter());
1487 const std::vector<double> grb_reduced_cost_values,
1489 GurobiVectorToSparseDoubleVector(grb_reduced_cost_values, variables_map_,
1490 *dual_solution.mutable_reduced_costs(),
1491 model_parameters.reduced_costs_filter());
1493 if (!quadratic_constraints_map_.empty() &&
1496 const std::vector<double> grb_quad_constraint_duals,
1498 GurobiVectorToSparseDoubleVector(
1499 grb_quad_constraint_duals, quadratic_constraints_map_,
1500 *dual_solution.mutable_quadratic_dual_values(),
1501 model_parameters.quadratic_dual_values_filter());
1510 dual_solution.set_objective_value(obj_val);
1513 dual_solution.set_feasibility_status(SOLUTION_STATUS_UNDETERMINED);
1515 dual_solution.set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
1516 dual_feasible_solution_exists =
true;
1518 dual_solution.set_feasibility_status(SOLUTION_STATUS_INFEASIBLE);
1534 if (dual_feasible_solution_exists || std::isfinite(best_dual_bound)) {
1535 dual_feasible_solution_exists =
true;
1537 return absl::InternalError(
1538 "GRB_INT_ATTR_STATUS == GRB_OPTIMAL, but GRB_DBL_ATTR_OBJBOUND is "
1539 "unavailable or infinite, and no dual feasible solution is returned");
1541 return SolutionAndClaim<DualSolutionProto>{
1542 .solution = std::move(dual_solution),
1543 .feasible_solution_exists = dual_feasible_solution_exists};
1546absl::Status GurobiSolver::FillRays(
1547 const ModelSolveParametersProto& model_parameters,
1548 const SolutionClaims solution_claims, SolveResultProto& result) {
1553 if (!solution_claims.dual_feasible_solution_exists &&
1554 num_gurobi_variables_ > 0 &&
1558 num_gurobi_variables_));
1559 PrimalRayProto*
const primal_ray = result.add_primal_rays();
1560 GurobiVectorToSparseDoubleVector(grb_ray_var_values, variables_map_,
1561 *primal_ray->mutable_variable_values(),
1562 model_parameters.variable_values_filter());
1567 if (!solution_claims.primal_feasible_solution_exists &&
1568 num_gurobi_lin_cons_ > 0 &&
1571 DualRayProto dual_ray,
1572 GetGurobiDualRay(model_parameters.dual_values_filter(),
1573 model_parameters.reduced_costs_filter(), is_maximize));
1574 result.mutable_dual_rays()->Add(std::move(dual_ray));
1576 return absl::OkStatus();
1579absl::StatusOr<GurobiSolver::SolutionsAndClaims> GurobiSolver::GetQpSolution(
1580 const ModelSolveParametersProto& model_parameters) {
1582 GetConvexPrimalSolutionIfAvailable(model_parameters));
1586 GetConvexDualSolutionIfAvailable(model_parameters));
1590 const SolutionClaims solution_claims = {
1591 .primal_feasible_solution_exists = found_primal_feasible_solution,
1592 .dual_feasible_solution_exists = found_dual_feasible_solution};
1595 return GurobiSolver::SolutionsAndClaims{.solution_claims = solution_claims};
1597 SolutionsAndClaims solution_and_claims{.solution_claims = solution_claims};
1599 solution_and_claims.solutions.emplace_back(SolutionProto());
1601 *
solution.mutable_primal_solution() = *std::move(primal_solution);
1603 if (dual_solution.has_value()) {
1604 *
solution.mutable_dual_solution() = *std::move(dual_solution);
1606 return solution_and_claims;
1609absl::StatusOr<GurobiSolver::SolutionsAndClaims> GurobiSolver::GetQcpSolution(
1610 const ModelSolveParametersProto& model_parameters) {
1612 GetConvexPrimalSolutionIfAvailable(model_parameters));
1614 GetConvexDualSolutionIfAvailable(model_parameters));
1618 const bool proven_feasible = grb_termination ==
GRB_OPTIMAL;
1619 const SolutionClaims solution_claims = {
1620 .primal_feasible_solution_exists = found_primal_feasible_solution,
1621 .dual_feasible_solution_exists =
1622 found_dual_feasible_solution || proven_feasible};
1624 SolutionsAndClaims solution_and_claims{.solution_claims = solution_claims};
1627 solution_and_claims.solutions.emplace_back(SolutionProto());
1629 *
solution.mutable_primal_solution() = *std::move(primal_solution);
1631 if (dual_solution.has_value()) {
1632 *
solution.mutable_dual_solution() = *std::move(dual_solution);
1635 return solution_and_claims;
1638absl::Status GurobiSolver::SetParameters(
1639 const SolveParametersProto& parameters,
1640 const ModelSolveParametersProto& model_parameters) {
1643 MergeParameters(parameters, model_parameters, is_mip,
1644 is_multi_objective_mode()));
1645 std::vector<std::string> parameter_errors;
1646 for (
const GurobiParametersProto::Parameter& parameter :
1647 gurobi_parameters.parameters()) {
1648 absl::Status param_status =
1649 gurobi_->SetParam(parameter.name().c_str(), parameter.value());
1650 if (!param_status.ok()) {
1651 parameter_errors.emplace_back(std::move(param_status).message());
1654 if (!parameter_errors.empty()) {
1655 return absl::InvalidArgumentError(absl::StrJoin(parameter_errors,
"; "));
1657 return absl::OkStatus();
1660absl::Status GurobiSolver::AddNewVariables(
1661 const VariablesProto& new_variables) {
1662 const int num_new_variables = new_variables.lower_bounds().size();
1663 std::vector<char> variable_type(num_new_variables);
1664 for (
int j = 0; j < num_new_variables; ++j) {
1665 const VariableId
id = new_variables.ids(j);
1671 const std::vector<std::string> variable_names =
1672 TruncateNames(new_variables.names());
1675 new_variables.lower_bounds(),
1676 new_variables.upper_bounds(),
1677 variable_type, variable_names));
1678 num_gurobi_variables_ += num_new_variables;
1680 return absl::OkStatus();
1683absl::Status GurobiSolver::AddSingleObjective(
const ObjectiveProto& objective) {
1688 RETURN_IF_ERROR(UpdateDoubleListAttribute(objective.linear_coefficients(),
1691 ResetQuadraticObjectiveTerms(objective.quadratic_coefficients()));
1692 return absl::OkStatus();
1695absl::Status GurobiSolver::AddMultiObjectives(
1696 const ObjectiveProto& primary_objective,
1697 const google::protobuf::Map<int64_t, ObjectiveProto>&
1698 auxiliary_objectives) {
1699 absl::flat_hash_set<int64_t> priorities = {primary_objective.priority()};
1700 for (
const auto& [
id, objective] : auxiliary_objectives) {
1701 const int64_t priority = objective.priority();
1702 if (!priorities.insert(priority).second) {
1704 <<
"repeated objective priority: " << priority;
1707 const bool is_maximize = primary_objective.maximize();
1711 primary_objective, std::nullopt, is_maximize));
1712 for (
const int64_t
id :
SortedMapKeys(auxiliary_objectives)) {
1714 AddNewMultiObjective(auxiliary_objectives.at(
id),
id, is_maximize));
1716 return absl::OkStatus();
1719absl::Status GurobiSolver::AddNewMultiObjective(
1720 const ObjectiveProto& objective,
1721 const std::optional<AuxiliaryObjectiveId> objective_id,
1722 const bool is_maximize) {
1723 std::vector<GurobiVariableIndex> var_indices;
1724 var_indices.reserve(objective.linear_coefficients().ids_size());
1725 for (
const int64_t var_id : objective.linear_coefficients().ids()) {
1726 var_indices.push_back(variables_map_.at(var_id));
1728 const GurobiMultiObjectiveIndex grb_index =
1729 static_cast<int>(multi_objectives_map_.size());
1739 grb_index,
static_cast<int>(-objective.priority()),
1740 objective.maximize() == is_maximize ? +1.0 : -1.0,
1742 0.0, objective.name(),
1743 objective.offset(), var_indices,
1744 objective.linear_coefficients().values()));
1745 multi_objectives_map_.insert({objective_id, grb_index});
1746 return absl::OkStatus();
1752absl::Status GurobiSolver::AddNewSlacks(
1753 const std::vector<LinearConstraintData*>& new_slacks) {
1760 const int num_slacks = new_slacks.size();
1761 if (num_slacks == 0) {
1762 return absl::OkStatus();
1765 const std::vector<double> column_non_zeros(num_slacks, -1.0);
1766 std::vector<double> lower_bounds;
1767 std::vector<double> upper_bounds;
1769 std::vector<GurobiLinearConstraintIndex> row_indices;
1770 std::vector<int> column_non_zero_begin;
1771 column_non_zero_begin.reserve(num_slacks);
1772 row_indices.reserve(num_slacks);
1773 lower_bounds.reserve(num_slacks);
1774 upper_bounds.reserve(num_slacks);
1775 for (
int k = 0; k < num_slacks; ++k) {
1776 CHECK_NE(new_slacks[k],
nullptr);
1777 const LinearConstraintData& constraint_data = *new_slacks[k];
1778 row_indices.push_back(constraint_data.constraint_index);
1779 lower_bounds.push_back(constraint_data.lower_bound);
1780 upper_bounds.push_back(constraint_data.upper_bound);
1781 column_non_zero_begin.push_back(k);
1786 column_non_zeros, {},
1787 lower_bounds, upper_bounds,
1789 num_gurobi_variables_ += num_slacks;
1790 return absl::OkStatus();
1793absl::Status GurobiSolver::AddNewLinearConstraints(
1794 const LinearConstraintsProto& constraints) {
1795 const int num_new_constraints = constraints.lower_bounds().size();
1799 const std::vector<std::string> constraint_names =
1800 TruncateNames(constraints.names());
1810 std::vector<double> constraint_rhs;
1811 std::vector<char> constraint_sense;
1812 std::vector<LinearConstraintData*> new_slacks;
1813 constraint_rhs.reserve(num_new_constraints);
1814 constraint_sense.reserve(num_new_constraints);
1815 new_slacks.reserve(num_new_constraints);
1816 for (
int i = 0;
i < num_new_constraints; ++
i) {
1817 const int64_t
id = constraints.ids(i);
1818 LinearConstraintData& constraint_data =
1820 const double lb = constraints.lower_bounds(i);
1821 const double ub = constraints.upper_bounds(i);
1823 <<
"lower bound for linear constraint " <<
id <<
": "
1824 << EscapedNameForLogging(
1825 constraints.names().empty() ?
"" : constraints.names(i));
1827 <<
"upper bound for linear constraint " <<
id <<
": "
1828 << EscapedNameForLogging(
1829 constraints.names().empty() ?
"" : constraints.names(i));
1830 constraint_data.lower_bound = lb;
1831 constraint_data.upper_bound = ub;
1832 constraint_data.constraint_index =
i + num_gurobi_lin_cons_;
1837 if (lb_is_grb_neg_inf && !ub_is_grb_pos_inf) {
1840 }
else if (!lb_is_grb_neg_inf && ub_is_grb_pos_inf) {
1843 }
else if (lb == ub) {
1849 constraint_data.slack_index = new_slacks.size() + num_gurobi_variables_;
1850 new_slacks.push_back(&constraint_data);
1852 constraint_rhs.emplace_back(rhs);
1853 constraint_sense.emplace_back(sense);
1857 gurobi_->AddConstrs(constraint_sense, constraint_rhs, constraint_names));
1858 num_gurobi_lin_cons_ += num_new_constraints;
1860 if (!new_slacks.empty()) {
1863 return absl::OkStatus();
1866absl::Status GurobiSolver::AddNewQuadraticConstraints(
1867 const google::protobuf::Map<QuadraticConstraintId,
1868 QuadraticConstraintProto>& constraints) {
1878 for (
const auto& [
id, constraint] : constraints) {
1881 const double lb = constraint.lower_bound();
1882 const double ub = constraint.upper_bound();
1884 <<
"lower bound for quadratic constraint " <<
id <<
": "
1885 << EscapedNameForLogging(constraint.name());
1887 <<
"upper bound for quadratic constraint " <<
id <<
": "
1888 << EscapedNameForLogging(constraint.name());
1891 if (lb_is_grb_neg_inf && ub_is_grb_pos_inf) {
1895 }
else if (lb_is_grb_neg_inf && !ub_is_grb_pos_inf) {
1898 }
else if (!lb_is_grb_neg_inf && ub_is_grb_pos_inf) {
1901 }
else if (lb == ub) {
1907 return absl::UnimplementedError(
1908 "ranged quadratic constraints are not currently supported in Gurobi "
1911 const SparseDoubleVectorProto& linear_coeffs = constraint.linear_terms();
1912 const int num_linear_coeffs = linear_coeffs.ids_size();
1913 std::vector<GurobiVariableIndex> linear_col_index(num_linear_coeffs);
1914 for (
int k = 0; k < num_linear_coeffs; ++k) {
1915 linear_col_index[k] = variables_map_.at(linear_coeffs.ids(k));
1917 const SparseDoubleMatrixProto& quad_coeffs = constraint.quadratic_terms();
1918 const int num_quad_coeffs = quad_coeffs.row_ids_size();
1919 std::vector<GurobiVariableIndex> quad_row_index(num_quad_coeffs);
1920 std::vector<GurobiVariableIndex> quad_col_index(num_quad_coeffs);
1921 for (
int k = 0; k < num_quad_coeffs; ++k) {
1922 quad_row_index[k] = variables_map_.at(quad_coeffs.row_ids(k));
1923 quad_col_index[k] = variables_map_.at(quad_coeffs.column_ids(k));
1926 linear_col_index, linear_coeffs.values(), quad_row_index,
1927 quad_col_index, quad_coeffs.coefficients(), sense, rhs,
1928 TruncateName(constraint.name())));
1930 ++num_gurobi_quad_cons_;
1932 return absl::OkStatus();
1935std::optional<GurobiSolver::VariableId> GurobiSolver::TryExtractVariable(
1936 const LinearExpressionProto& expression) {
1937 if (expression.offset() == 0 && expression.ids_size() == 1 &&
1938 expression.coefficients(0) == 1) {
1939 return expression.ids(0);
1941 return std::nullopt;
1944absl::StatusOr<GurobiSolver::VariableEqualToExpression>
1945GurobiSolver::CreateSlackVariableEqualToExpression(
1946 const LinearExpressionProto& expression) {
1947 const GurobiVariableIndex slack_variable = num_gurobi_variables_;
1948 std::vector<GurobiVariableIndex> slack_col_indices = {slack_variable};
1949 std::vector<double> slack_coeffs = {-1.0};
1950 for (
int j = 0; j < expression.ids_size(); ++j) {
1951 slack_col_indices.push_back(variables_map_.at(expression.ids(j)));
1952 slack_coeffs.push_back(expression.coefficients(j));
1956 -expression.offset(),
""));
1957 return VariableEqualToExpression{.variable_index = num_gurobi_variables_++,
1958 .constraint_index = num_gurobi_lin_cons_++};
1961absl::Status GurobiSolver::AddNewSecondOrderConeConstraints(
1962 const google::protobuf::Map<SecondOrderConeConstraintId,
1963 SecondOrderConeConstraintProto>& constraints) {
1964 for (
const auto& [
id, constraint] : constraints) {
1977 SecondOrderConeConstraintData& constraint_data =
1979 constraint_data.constraint_index = num_gurobi_quad_cons_;
1984 (
const auto [ub_var, ub_cons]),
1985 CreateSlackVariableEqualToExpression(constraint.upper_bound()));
1988 constraint_data.slack_variables.push_back(ub_var);
1989 constraint_data.slack_constraints.push_back(ub_cons);
1990 std::vector<GurobiVariableIndex> quad_var_indices = {ub_var};
1991 std::vector<double> quad_coeffs = {-1.0};
1992 for (
const LinearExpressionProto& expression :
1993 constraint.arguments_to_norm()) {
1994 quad_coeffs.push_back(1.0);
1995 if (
const std::optional<VariableId> maybe_variable =
1996 TryExtractVariable(expression);
1997 maybe_variable.has_value()) {
1998 quad_var_indices.push_back(variables_map_.at(*maybe_variable));
2002 CreateSlackVariableEqualToExpression(expression));
2003 quad_var_indices.push_back(arg_var);
2004 constraint_data.slack_variables.push_back(arg_var);
2005 constraint_data.slack_constraints.push_back(arg_cons);
2008 quad_var_indices, quad_coeffs,
2010 ++num_gurobi_quad_cons_;
2012 return absl::OkStatus();
2015absl::Status GurobiSolver::AddNewSosConstraints(
2016 const google::protobuf::Map<AnyConstraintId, SosConstraintProto>&
2019 absl::flat_hash_map<int64_t, SosConstraintData>& constraints_map) {
2020 for (
const auto& [
id, constraint] : constraints) {
2021 SosConstraintData& constraint_data =
2023 constraint_data.constraint_index = num_gurobi_sos_cons_;
2024 std::vector<GurobiVariableIndex> sos_var_indices;
2025 std::vector<double> weights;
2031 absl::flat_hash_set<VariableId> reused_variables;
2032 for (
int i = 0;
i < constraint.expressions_size(); ++
i) {
2033 weights.push_back(constraint.weights().empty() ? i + 1
2034 : constraint.weights(i));
2035 if (
const std::optional<VariableId> maybe_variable =
2036 TryExtractVariable(constraint.expressions(i));
2037 maybe_variable.has_value() &&
2038 !reused_variables.contains(*maybe_variable)) {
2039 sos_var_indices.push_back(variables_map_.at(*maybe_variable));
2040 reused_variables.insert(*maybe_variable);
2041 if (sos_type == 2) {
2045 undeletable_variables_.insert(*maybe_variable);
2050 (
const auto [var_index, cons_index]),
2051 CreateSlackVariableEqualToExpression(constraint.expressions(i)));
2052 sos_var_indices.push_back(var_index);
2053 constraint_data.slack_variables.push_back(var_index);
2054 constraint_data.slack_constraints.push_back(cons_index);
2056 RETURN_IF_ERROR(gurobi_->AddSos({sos_type}, {0}, sos_var_indices, weights));
2057 ++num_gurobi_sos_cons_;
2059 return absl::OkStatus();
2062absl::Status GurobiSolver::AddNewIndicatorConstraints(
2063 const google::protobuf::Map<IndicatorConstraintId,
2064 IndicatorConstraintProto>& constraints) {
2065 for (
const auto& [
id, constraint] : constraints) {
2066 if (!constraint.has_indicator_id()) {
2067 if (constraint.activate_on_zero()) {
2069 <<
"MathOpt does not currently support Gurobi models with "
2070 "indicator constraints that activate on zero with unset "
2071 "indicator variables; encountered one at id: "
2077 const int num_terms = constraint.expression().ids_size();
2078 std::vector<GurobiVariableIndex> grb_ids(num_terms);
2079 for (
int k = 0; k < num_terms; ++k) {
2080 grb_ids[k] = variables_map_.at(constraint.expression().ids(k));
2084 const double lb = constraint.lower_bound();
2085 const double ub = constraint.upper_bound();
2087 <<
"lower bound for indicator constraint " <<
id <<
": "
2088 << EscapedNameForLogging(constraint.name());
2090 <<
"upper bound for indicator constraint " <<
id <<
": "
2091 << EscapedNameForLogging(constraint.name());
2094 if (lb_is_grb_neg_inf && ub_is_grb_pos_inf) {
2097 }
else if (lb_is_grb_neg_inf && !ub_is_grb_pos_inf) {
2100 }
else if (!lb_is_grb_neg_inf && ub_is_grb_pos_inf) {
2103 }
else if (lb == ub) {
2109 return absl::UnimplementedError(
2110 "ranged indicator constraints are not currently supported in "
2116 variables_map_.at(constraint.indicator_id()),
2117 constraint.activate_on_zero() ? 0 : 1,
2118 grb_ids, constraint.expression().values(),
2121 IndicatorConstraintData{
2122 .constraint_index = num_gurobi_gen_cons_,
2123 .indicator_variable_id = constraint.indicator_id()});
2124 ++num_gurobi_gen_cons_;
2127 undeletable_variables_.insert(constraint.indicator_id());
2129 return absl::OkStatus();
2132absl::Status GurobiSolver::ChangeCoefficients(
2133 const SparseDoubleMatrixProto& matrix) {
2134 const int num_coefficients = matrix.row_ids().size();
2135 std::vector<GurobiLinearConstraintIndex> row_index(num_coefficients);
2136 std::vector<GurobiVariableIndex> col_index(num_coefficients);
2137 for (
int k = 0; k < num_coefficients; ++k) {
2139 linear_constraints_map_.at(matrix.row_ids(k)).constraint_index;
2140 col_index[k] = variables_map_.at(matrix.column_ids(k));
2142 return gurobi_->ChgCoeffs(row_index, col_index, matrix.coefficients());
2145absl::Status GurobiSolver::UpdateDoubleListAttribute(
2146 const SparseDoubleVectorProto& update,
const char* attribute_name,
2147 const IdHashMap& id_hash_map) {
2148 if (update.ids_size() == 0) {
2149 return absl::OkStatus();
2151 std::vector<int> index;
2152 index.reserve(update.ids_size());
2153 for (
const int64_t
id : update.ids()) {
2154 index.push_back(id_hash_map.at(
id));
2156 return gurobi_->SetDoubleAttrList(attribute_name, index, update.values());
2159absl::Status GurobiSolver::UpdateInt32ListAttribute(
2160 const SparseInt32VectorProto& update,
const char* attribute_name,
2161 const IdHashMap& id_hash_map) {
2162 if (update.ids_size() == 0) {
2163 return absl::OkStatus();
2165 std::vector<int> index;
2166 index.reserve(update.ids_size());
2167 for (
const int64_t
id : update.ids()) {
2168 index.push_back(id_hash_map.at(
id));
2170 return gurobi_->SetIntAttrList(attribute_name, index, update.values());
2173absl::Status GurobiSolver::LoadModel(
const ModelProto& input_model) {
2174 CHECK(gurobi_ !=
nullptr);
2176 TruncateName(input_model.name())));
2179 RETURN_IF_ERROR(AddNewLinearConstraints(input_model.linear_constraints()));
2181 AddNewQuadraticConstraints(input_model.quadratic_constraints()));
2183 input_model.second_order_cone_constraints()));
2189 AddNewIndicatorConstraints(input_model.indicator_constraints()));
2191 RETURN_IF_ERROR(ChangeCoefficients(input_model.linear_constraint_matrix()));
2193 if (input_model.auxiliary_objectives().empty()) {
2197 input_model.auxiliary_objectives()));
2199 return absl::OkStatus();
2202absl::Status GurobiSolver::ResetQuadraticObjectiveTerms(
2203 const SparseDoubleMatrixProto& terms) {
2204 quadratic_objective_coefficients_.clear();
2206 const int num_terms = terms.row_ids().size();
2207 if (num_terms > 0) {
2208 std::vector<GurobiVariableIndex> first_var_index(num_terms);
2209 std::vector<GurobiVariableIndex> second_var_index(num_terms);
2210 for (
int k = 0; k < num_terms; ++k) {
2211 const VariableId row_id = terms.row_ids(k);
2212 const VariableId column_id = terms.column_ids(k);
2213 first_var_index[k] = variables_map_.at(row_id);
2214 second_var_index[k] = variables_map_.at(column_id);
2215 quadratic_objective_coefficients_[{row_id, column_id}] =
2216 terms.coefficients(k);
2218 RETURN_IF_ERROR(gurobi_->AddQpTerms(first_var_index, second_var_index,
2219 terms.coefficients()));
2221 return absl::OkStatus();
2224absl::Status GurobiSolver::UpdateQuadraticObjectiveTerms(
2225 const SparseDoubleMatrixProto& terms) {
2226 CHECK(gurobi_ !=
nullptr);
2227 const int num_terms = terms.row_ids().size();
2228 if (num_terms > 0) {
2229 std::vector<GurobiVariableIndex> first_var_index(num_terms);
2230 std::vector<GurobiVariableIndex> second_var_index(num_terms);
2231 std::vector<double> coefficient_updates(num_terms);
2232 for (
int k = 0; k < num_terms; ++k) {
2233 const VariableId row_id = terms.row_ids(k);
2234 const VariableId column_id = terms.column_ids(k);
2235 first_var_index[k] = variables_map_.at(row_id);
2236 second_var_index[k] = variables_map_.at(column_id);
2237 const std::pair<VariableId, VariableId> qp_term_key(row_id, column_id);
2238 const double new_coefficient = terms.coefficients(k);
2243 coefficient_updates[k] =
2244 new_coefficient - quadratic_objective_coefficients_[qp_term_key];
2245 quadratic_objective_coefficients_[qp_term_key] = new_coefficient;
2247 RETURN_IF_ERROR(gurobi_->AddQpTerms(first_var_index, second_var_index,
2248 coefficient_updates));
2250 return absl::OkStatus();
2256absl::Status GurobiSolver::UpdateLinearConstraints(
2257 const LinearConstraintUpdatesProto& constraints_update,
2258 std::vector<GurobiVariableIndex>& deleted_variables_index) {
2259 const SparseDoubleVectorProto& constraint_lower_bounds =
2260 constraints_update.lower_bounds();
2261 const SparseDoubleVectorProto& constraint_upper_bounds =
2262 constraints_update.upper_bounds();
2265 if (constraint_lower_bounds.ids().empty() &&
2266 constraint_upper_bounds.ids().empty()) {
2267 return absl::OkStatus();
2274 struct UpdateConstraintData {
2275 LinearConstraintId constraint_id;
2276 LinearConstraintData& source;
2277 double new_lower_bound;
2278 double new_upper_bound;
2279 UpdateConstraintData(
const LinearConstraintId
id,
2280 LinearConstraintData& reference)
2281 : constraint_id(id),
2283 new_lower_bound(reference.lower_bound),
2284 new_upper_bound(reference.upper_bound) {}
2286 const int upper_bounds_size = constraint_upper_bounds.ids().size();
2287 const int lower_bounds_size = constraint_lower_bounds.ids().size();
2288 std::vector<UpdateConstraintData> update_vector;
2289 update_vector.reserve(upper_bounds_size + lower_bounds_size);
2292 for (
int lower_index = 0, upper_index = 0;
2293 lower_index < lower_bounds_size || upper_index < upper_bounds_size;) {
2294 VariableId lower_id = std::numeric_limits<int64_t>::max();
2295 if (lower_index < lower_bounds_size) {
2296 lower_id = constraint_lower_bounds.ids(lower_index);
2298 VariableId upper_id = std::numeric_limits<int64_t>::max();
2299 if (upper_index < upper_bounds_size) {
2300 upper_id = constraint_upper_bounds.ids(upper_index);
2302 const VariableId
id = std::min(lower_id, upper_id);
2303 DCHECK(
id < std::numeric_limits<int64_t>::max());
2304 UpdateConstraintData update(
id, linear_constraints_map_.at(
id));
2305 if (lower_id == upper_id) {
2306 update.new_lower_bound = constraint_lower_bounds.values(lower_index++);
2307 update.new_upper_bound = constraint_upper_bounds.values(upper_index++);
2308 }
else if (lower_id < upper_id) {
2309 update.new_lower_bound = constraint_lower_bounds.values(lower_index++);
2311 update.new_upper_bound = constraint_upper_bounds.values(upper_index++);
2313 update_vector.emplace_back(update);
2320 std::vector<char> sense_data;
2321 std::vector<double> rhs_data;
2322 std::vector<GurobiLinearConstraintIndex> rhs_index;
2324 std::vector<double> lower_bound_data;
2325 std::vector<double> upper_bound_data;
2326 std::vector<GurobiVariableIndex> bound_index;
2328 std::vector<LinearConstraintData*> new_slacks;
2330 for (UpdateConstraintData& update_data : update_vector) {
2331 const bool same_lower_bound =
2332 (update_data.source.lower_bound == update_data.new_lower_bound) ||
2335 const bool same_upper_bound =
2336 (update_data.source.upper_bound == update_data.new_upper_bound) ||
2339 if (same_upper_bound && same_lower_bound)
continue;
2342 update_data.source.lower_bound = update_data.new_lower_bound;
2343 update_data.source.upper_bound = update_data.new_upper_bound;
2344 bool delete_slack =
false;
2348 delete_slack =
true;
2349 rhs_index.emplace_back(update_data.source.constraint_index);
2350 rhs_data.emplace_back(update_data.new_upper_bound);
2352 }
else if (update_data.new_lower_bound > -
GRB_INFINITY &&
2354 delete_slack =
true;
2355 rhs_index.emplace_back(update_data.source.constraint_index);
2356 rhs_data.emplace_back(update_data.new_lower_bound);
2358 }
else if (update_data.new_lower_bound == update_data.new_upper_bound) {
2359 delete_slack =
true;
2360 rhs_index.emplace_back(update_data.source.constraint_index);
2361 rhs_data.emplace_back(update_data.new_lower_bound);
2367 if (update_data.source.slack_index != kUnspecifiedIndex) {
2368 bound_index.emplace_back(update_data.source.slack_index);
2369 lower_bound_data.emplace_back(update_data.new_lower_bound);
2370 upper_bound_data.emplace_back(update_data.new_upper_bound);
2374 rhs_index.emplace_back(update_data.source.constraint_index);
2375 rhs_data.emplace_back(0.0);
2378 update_data.source.slack_index =
2379 new_slacks.size() + num_gurobi_variables_;
2381 new_slacks.push_back(&update_data.source);
2388 if (delete_slack && update_data.source.slack_index != kUnspecifiedIndex) {
2389 deleted_variables_index.emplace_back(update_data.source.slack_index);
2390 update_data.source.slack_index = kUnspecifiedIndex;
2395 if (!rhs_index.empty()) {
2397 gurobi_->SetDoubleAttrList(GRB_DBL_ATTR_RHS, rhs_index, rhs_data));
2399 gurobi_->SetCharAttrList(GRB_CHAR_ATTR_SENSE, rhs_index, sense_data));
2401 if (!bound_index.empty()) {
2402 RETURN_IF_ERROR(gurobi_->SetDoubleAttrList(GRB_DBL_ATTR_LB, bound_index,
2404 RETURN_IF_ERROR(gurobi_->SetDoubleAttrList(GRB_DBL_ATTR_UB, bound_index,
2408 if (!new_slacks.empty()) {
2409 RETURN_IF_ERROR(AddNewSlacks(new_slacks));
2411 return absl::OkStatus();
2419void GurobiSolver::UpdateGurobiIndices(
const DeletedIndices& deleted_indices) {
2421 if (!deleted_indices.variables.empty()) {
2422 const std::vector<GurobiVariableIndex> old_to_new =
2423 IndexUpdateMap(num_gurobi_variables_, deleted_indices.variables);
2424 for (
auto& [_, grb_index] : variables_map_) {
2425 grb_index = old_to_new[grb_index];
2426 CHECK_NE(grb_index, kDeletedIndex);
2428 for (
auto& [_, lin_con_data] : linear_constraints_map_) {
2429 if (lin_con_data.slack_index != kUnspecifiedIndex) {
2430 lin_con_data.slack_index = old_to_new[lin_con_data.slack_index];
2431 CHECK_NE(lin_con_data.slack_index, kDeletedIndex);
2434 for (
auto& [_, soc_con_data] : soc_constraints_map_) {
2435 for (GurobiVariableIndex& index : soc_con_data.slack_variables) {
2436 index = old_to_new[index];
2437 CHECK_NE(index, kDeletedIndex);
2440 for (
auto& [_, sos1_con_data] : sos1_constraints_map_) {
2441 for (GurobiVariableIndex& index : sos1_con_data.slack_variables) {
2442 index = old_to_new[index];
2443 CHECK_NE(index, kDeletedIndex);
2446 for (
auto& [_, sos2_con_data] : sos2_constraints_map_) {
2447 for (GurobiVariableIndex& index : sos2_con_data.slack_variables) {
2448 index = old_to_new[index];
2449 CHECK_NE(index, kDeletedIndex);
2454 if (!deleted_indices.linear_constraints.empty()) {
2455 const std::vector<GurobiLinearConstraintIndex> old_to_new = IndexUpdateMap(
2456 num_gurobi_lin_cons_, deleted_indices.linear_constraints);
2457 for (
auto& [_, lin_con_data] : linear_constraints_map_) {
2458 lin_con_data.constraint_index = old_to_new[lin_con_data.constraint_index];
2459 CHECK_NE(lin_con_data.constraint_index, kDeletedIndex);
2461 for (
auto& [_, soc_con_data] : soc_constraints_map_) {
2462 for (GurobiLinearConstraintIndex& index :
2463 soc_con_data.slack_constraints) {
2464 index = old_to_new[index];
2465 CHECK_NE(index, kDeletedIndex);
2468 for (
auto& [_, sos1_con_data] : sos1_constraints_map_) {
2469 for (GurobiLinearConstraintIndex& index :
2470 sos1_con_data.slack_constraints) {
2471 index = old_to_new[index];
2472 CHECK_NE(index, kDeletedIndex);
2475 for (
auto& [_, sos2_con_data] : sos2_constraints_map_) {
2476 for (GurobiLinearConstraintIndex& index :
2477 sos2_con_data.slack_constraints) {
2478 index = old_to_new[index];
2479 CHECK_NE(index, kDeletedIndex);
2484 if (!deleted_indices.quadratic_constraints.empty()) {
2485 const std::vector<GurobiQuadraticConstraintIndex> old_to_new =
2486 IndexUpdateMap(num_gurobi_quad_cons_,
2487 deleted_indices.quadratic_constraints);
2488 for (
auto& [_, grb_index] : quadratic_constraints_map_) {
2489 grb_index = old_to_new[grb_index];
2490 CHECK_NE(grb_index, kDeletedIndex);
2492 for (
auto& [_, soc_con_data] : soc_constraints_map_) {
2493 GurobiQuadraticConstraintIndex& grb_index = soc_con_data.constraint_index;
2494 grb_index = old_to_new[soc_con_data.constraint_index];
2495 CHECK_NE(grb_index, kDeletedIndex);
2499 if (!deleted_indices.sos_constraints.empty()) {
2500 const std::vector<GurobiSosConstraintIndex> old_to_new =
2501 IndexUpdateMap(num_gurobi_sos_cons_, deleted_indices.sos_constraints);
2502 for (
auto& [_, sos1_data] : sos1_constraints_map_) {
2503 GurobiSosConstraintIndex& grb_index = sos1_data.constraint_index;
2504 grb_index = old_to_new[grb_index];
2505 CHECK_NE(grb_index, kDeletedIndex);
2507 for (
auto& [_, sos2_data] : sos2_constraints_map_) {
2508 GurobiSosConstraintIndex& grb_index = sos2_data.constraint_index;
2509 grb_index = old_to_new[grb_index];
2510 CHECK_NE(grb_index, kDeletedIndex);
2514 if (!deleted_indices.general_constraints.empty()) {
2515 const std::vector<GurobiGeneralConstraintIndex> old_to_new = IndexUpdateMap(
2516 num_gurobi_gen_cons_, deleted_indices.general_constraints);
2517 for (
auto& [_, indicator_data] : indicator_constraints_map_) {
2518 if (!indicator_data.has_value()) {
2521 GurobiGeneralConstraintIndex& grb_index =
2522 indicator_data->constraint_index;
2523 grb_index = old_to_new[grb_index];
2524 CHECK_NE(grb_index, kDeletedIndex);
2530 const ModelUpdateProto& model_update) {
2531 if (!undeletable_variables_.empty()) {
2532 for (
const VariableId
id : model_update.deleted_variable_ids()) {
2533 if (undeletable_variables_.contains(
id)) {
2543 if (
const AuxiliaryObjectivesUpdatesProto& objs_update =
2544 model_update.auxiliary_objectives_updates();
2545 !objs_update.deleted_objective_ids().empty() ||
2546 !objs_update.new_objectives().empty() ||
2547 !objs_update.objective_updates().empty()) {
2551 if (is_multi_objective_mode() && model_update.has_objective_updates()) {
2558 AddNewLinearConstraints(model_update.new_linear_constraints()));
2561 model_update.quadratic_constraint_updates().new_constraints()));
2563 model_update.second_order_cone_constraint_updates().new_constraints()));
2565 model_update.sos1_constraint_updates().new_constraints(),
GRB_SOS_TYPE1,
2566 sos1_constraints_map_));
2568 model_update.sos2_constraint_updates().new_constraints(),
GRB_SOS_TYPE2,
2569 sos2_constraints_map_));
2571 model_update.indicator_constraint_updates().new_constraints()));
2574 ChangeCoefficients(model_update.linear_constraint_matrix_updates()));
2576 if (model_update.objective_updates().has_direction_update()) {
2577 const int model_sense = model_update.objective_updates().direction_update()
2583 if (model_update.objective_updates().has_offset_update()) {
2593 model_update.objective_updates().quadratic_coefficients()));
2596 UpdateDoubleListAttribute(model_update.variable_updates().lower_bounds(),
2600 UpdateDoubleListAttribute(model_update.variable_updates().upper_bounds(),
2603 if (model_update.variable_updates().has_integers()) {
2604 const SparseBoolVectorProto& update =
2605 model_update.variable_updates().integers();
2606 std::vector<GurobiVariableIndex> index;
2607 index.reserve(update.ids_size());
2608 for (
const int64_t
id : update.ids()) {
2609 index.push_back(variables_map_.at(
id));
2611 std::vector<char> value;
2612 value.reserve(update.values_size());
2613 for (
const bool val : update.values()) {
2622 const absl::flat_hash_set<VariableId> variable_ids_to_be_deleted(
2623 model_update.deleted_variable_ids().begin(),
2624 model_update.deleted_variable_ids().end());
2627 for (
auto it = quadratic_objective_coefficients_.cbegin();
2628 it != quadratic_objective_coefficients_.cend();
2630 if (variable_ids_to_be_deleted.contains(it->first.first) ||
2631 variable_ids_to_be_deleted.contains(it->first.second)) {
2632 quadratic_objective_coefficients_.erase(it++);
2639 DeletedIndices deleted_indices;
2642 model_update.linear_constraint_updates(), deleted_indices.variables));
2644 for (
const VariableId
id : model_update.deleted_variable_ids()) {
2645 deleted_indices.variables.emplace_back(variables_map_.at(
id));
2646 variables_map_.erase(
id);
2649 for (
const LinearConstraintId
id :
2650 model_update.deleted_linear_constraint_ids()) {
2651 LinearConstraintData& constraint_data = linear_constraints_map_.at(
id);
2652 deleted_indices.linear_constraints.push_back(
2653 constraint_data.constraint_index);
2654 if (constraint_data.slack_index != kUnspecifiedIndex) {
2655 deleted_indices.variables.push_back(constraint_data.slack_index);
2656 constraint_data.slack_index = kUnspecifiedIndex;
2658 linear_constraints_map_.erase(
id);
2661 for (
const QuadraticConstraintId
id :
2662 model_update.quadratic_constraint_updates().deleted_constraint_ids()) {
2663 const GurobiQuadraticConstraintIndex grb_index =
2664 quadratic_constraints_map_.at(
id);
2665 deleted_indices.quadratic_constraints.push_back(grb_index);
2666 quadratic_constraints_map_.erase(
id);
2669 for (
const SecondOrderConeConstraintId
id :
2670 model_update.second_order_cone_constraint_updates()
2671 .deleted_constraint_ids()) {
2672 deleted_indices.quadratic_constraints.push_back(
2673 soc_constraints_map_.at(
id).constraint_index);
2674 for (
const GurobiVariableIndex index :
2675 soc_constraints_map_.at(
id).slack_variables) {
2676 deleted_indices.variables.push_back(index);
2678 for (
const GurobiLinearConstraintIndex index :
2679 soc_constraints_map_.at(
id).slack_constraints) {
2680 deleted_indices.linear_constraints.push_back(index);
2682 soc_constraints_map_.erase(
id);
2685 const auto sos_updater = [&](
const SosConstraintData& sos_constraint) {
2686 deleted_indices.sos_constraints.push_back(sos_constraint.constraint_index);
2687 for (
const GurobiVariableIndex index : sos_constraint.slack_variables) {
2688 deleted_indices.variables.push_back(index);
2690 for (
const GurobiLinearConstraintIndex index :
2691 sos_constraint.slack_constraints) {
2692 deleted_indices.linear_constraints.push_back(index);
2695 for (
const Sos1ConstraintId
id :
2696 model_update.sos1_constraint_updates().deleted_constraint_ids()) {
2697 sos_updater(sos1_constraints_map_.at(
id));
2698 sos1_constraints_map_.erase(
id);
2701 for (
const Sos2ConstraintId
id :
2702 model_update.sos2_constraint_updates().deleted_constraint_ids()) {
2703 sos_updater(sos2_constraints_map_.at(
id));
2704 sos2_constraints_map_.erase(
id);
2707 for (
const IndicatorConstraintId
id :
2708 model_update.indicator_constraint_updates().deleted_constraint_ids()) {
2710 const auto it = indicator_constraints_map_.find(
id);
2711 CHECK(it != indicator_constraints_map_.end()) <<
"id: " << id;
2712 if (it->second.has_value()) {
2713 deleted_indices.general_constraints.push_back(
2714 it->second->constraint_index);
2716 indicator_constraints_map_.erase(it);
2719 UpdateGurobiIndices(deleted_indices);
2726 if (!deleted_indices.linear_constraints.empty()) {
2727 RETURN_IF_ERROR(gurobi_->DelConstrs(deleted_indices.linear_constraints));
2728 num_gurobi_lin_cons_ -= deleted_indices.linear_constraints.size();
2731 if (!deleted_indices.quadratic_constraints.empty()) {
2733 gurobi_->DelQConstrs(deleted_indices.quadratic_constraints));
2734 num_gurobi_quad_cons_ -= deleted_indices.quadratic_constraints.size();
2737 if (!deleted_indices.sos_constraints.empty()) {
2741 if (!deleted_indices.general_constraints.empty()) {
2743 gurobi_->DelGenConstrs(deleted_indices.general_constraints));
2746 if (!deleted_indices.variables.empty()) {
2748 num_gurobi_variables_ -= deleted_indices.variables.size();
2760 return absl::InvalidArgumentError(
"Gurobi is not correctly installed.");
2764 if (!input_model.auxiliary_objectives().empty() &&
2765 !input_model.objective().quadratic_coefficients().row_ids().empty()) {
2767 <<
"Gurobi does not support multiple objective models with "
2768 "quadratic objectives";
2770 for (
const auto& [
id, obj] : input_model.auxiliary_objectives()) {
2771 if (!obj.quadratic_coefficients().row_ids().empty()) {
2773 <<
"Gurobi does not support multiple objective models with "
2774 "quadratic objectives";
2778 GurobiFromInitArgs(init_args));
2779 auto gurobi_solver = absl::WrapUnique(
new GurobiSolver(std::move(gurobi)));
2781 return gurobi_solver;
2784absl::StatusOr<std::unique_ptr<GurobiSolver::GurobiCallbackData>>
2785GurobiSolver::RegisterCallback(
const CallbackRegistrationProto& registration,
2788 const absl::Time start,
2790 const absl::flat_hash_set<CallbackEventProto> events =
EventSet(registration);
2801 registration, is_mip ? SupportedMIPEvents() : SupportedLPEvents()))
2802 <<
"for a " << (is_mip ?
"MIP" :
"LP") <<
" model";
2805 if (message_cb !=
nullptr) {
2810 if (registration.add_cuts() || registration.add_lazy_constraints()) {
2815 if (registration.add_lazy_constraints()) {
2820 return std::make_unique<GurobiCallbackData>(
2821 GurobiCallbackInput{
2823 .message_cb = message_cb,
2824 .variable_ids = variables_map_,
2825 .num_gurobi_vars = num_gurobi_variables_,
2827 .mip_solution_filter = registration.mip_solution_filter(),
2828 .mip_node_filter = registration.mip_node_filter(),
2833absl::StatusOr<InvertedBounds> GurobiSolver::ListInvertedBounds()
const {
2834 InvertedBounds inverted_bounds;
2837 const std::vector<double> var_lbs,
2840 const std::vector<double> var_ubs,
2842 for (
const auto& [
id, index] : variables_map_) {
2843 if (var_lbs[index] > var_ubs[index]) {
2844 inverted_bounds.variables.push_back(
id);
2848 for (
const auto& [
id, cstr_data] : linear_constraints_map_) {
2849 if (cstr_data.lower_bound > cstr_data.upper_bound) {
2850 inverted_bounds.linear_constraints.push_back(
id);
2855 std::sort(inverted_bounds.variables.begin(), inverted_bounds.variables.end());
2856 std::sort(inverted_bounds.linear_constraints.begin(),
2857 inverted_bounds.linear_constraints.end());
2858 return inverted_bounds;
2861absl::StatusOr<InvalidIndicators> GurobiSolver::ListInvalidIndicators()
const {
2862 InvalidIndicators invalid_indicators;
2863 for (
const auto& [constraint_id, indicator_data] :
2864 indicator_constraints_map_) {
2865 if (!indicator_data.has_value()) {
2868 const int64_t indicator_id = indicator_data->indicator_variable_id;
2869 const GurobiVariableIndex variable_index = variables_map_.at(indicator_id);
2875 const char var_type,
2878 (var_type ==
GRB_INTEGER && var_lb >= 0.0 && var_ub <= 1.0))) {
2879 invalid_indicators.invalid_indicators.push_back(
2880 {.variable = indicator_id, .constraint = constraint_id});
2884 invalid_indicators.Sort();
2885 return invalid_indicators;
2888bool GurobiSolver::is_multi_objective_mode()
const {
2889 return !multi_objectives_map_.empty();
2892absl::Status GurobiSolver::SetMultiObjectiveParameters(
2893 const ModelSolveParametersProto& model_parameters) {
2894 const auto set_tolerances =
2895 [&](
const GurobiMultiObjectiveIndex index,
2896 const ObjectiveParametersProto& objective_parameters)
2899 if (objective_parameters.has_objective_degradation_absolute_tolerance()) {
2902 objective_parameters.objective_degradation_absolute_tolerance()));
2904 if (objective_parameters.has_objective_degradation_relative_tolerance()) {
2907 objective_parameters.objective_degradation_relative_tolerance()));
2909 return absl::OkStatus();
2911 const auto set_time_limit =
2912 [&](
const GurobiMultiObjectiveIndex index,
2913 const ObjectiveParametersProto& objective_parameters)
2915 if (!objective_parameters.has_time_limit()) {
2917 return absl::OkStatus();
2922 return gurobi_->SetMultiObjectiveDoubleParam(
2925 if (model_parameters.has_primary_objective_parameters()) {
2926 const GurobiMultiObjectiveIndex obj_index =
2927 multi_objectives_map_.at(std::nullopt);
2929 obj_index, model_parameters.primary_objective_parameters()))
2930 <<
" for primary objective";
2932 obj_index, model_parameters.primary_objective_parameters()))
2933 <<
" for primary objective";
2935 for (
const auto& [
id, objective_parameters] :
2936 model_parameters.auxiliary_objective_parameters()) {
2937 const GurobiMultiObjectiveIndex obj_index = multi_objectives_map_.at(
id);
2939 <<
" for auxiliary objective " << id;
2941 <<
" for auxiliary objective " << id;
2943 return absl::OkStatus();
2946absl::Status GurobiSolver::ResetModelParameters(
2947 const ModelSolveParametersProto& model_parameters) {
2948 for (
int i = 0;
i < model_parameters.branching_priorities().ids_size(); ++
i) {
2949 const int64_t var_id = model_parameters.branching_priorities().ids(i);
2950 const GurobiVariableIndex grb_index = variables_map_.at(var_id);
2953 <<
"failed to reset branching priority for variable ID " << var_id
2954 <<
" (Gurobi index = " << grb_index <<
")";
2956 for (
const int64_t lazy_constraint_id :
2957 model_parameters.lazy_linear_constraint_ids()) {
2958 const GurobiLinearConstraintIndex lazy_constraint_index =
2959 linear_constraints_map_.at(lazy_constraint_id).constraint_index;
2962 <<
"failed to reset lazy constraint for lazy constraint ID "
2963 << lazy_constraint_id <<
" (Gurobi index = " << lazy_constraint_index
2966 return absl::OkStatus();
2970 const SolveParametersProto& parameters,
2971 const ModelSolveParametersProto& model_parameters,
2973 const CallbackRegistrationProto& callback_registration,
const Callback cb,
2976 model_parameters, kGurobiSupportedStructures,
"Gurobi"));
2977 const absl::Time start = absl::Now();
2989 ListInvertedBounds());
2998 ListInvalidIndicators());
3009 std::unique_ptr<SolveInterrupter> local_interrupter;
3010 if (cb !=
nullptr || interrupter !=
nullptr) {
3011 local_interrupter = std::make_unique<SolveInterrupter>();
3014 local_interrupter.get(), [&]() {
3022 gurobi_->Terminate();
3032 interrupter, [&]() { local_interrupter->Interrupt(); });
3034 if (model_parameters.has_initial_basis()) {
3038 model_parameters.solution_hints_size()));
3039 for (
int i = 0; i < model_parameters.solution_hints_size(); ++i) {
3042 model_parameters.solution_hints(i).variable_values(),
3046 UpdateInt32ListAttribute(model_parameters.branching_priorities(),
3048 if (is_multi_objective_mode()) {
3051 for (
const int64_t lazy_constraint_id :
3052 model_parameters.lazy_linear_constraint_ids()) {
3053 const GurobiLinearConstraintIndex lazy_constraint_index =
3054 linear_constraints_map_.at(lazy_constraint_id).constraint_index;
3061 lazy_constraint_index, 1));
3068 std::unique_ptr<GurobiCallbackData> gurobi_cb_data;
3069 if (cb !=
nullptr || local_interrupter !=
nullptr || message_cb !=
nullptr) {
3071 RegisterCallback(callback_registration, cb, message_cb,
3072 start, local_interrupter.get()));
3073 grb_cb = [&gurobi_cb_data](
3076 gurobi_cb_data->message_callback_data,
3077 gurobi_cb_data->local_interrupter);
3085 if (gurobi_cb_data !=
nullptr) {
3087 gurobi_cb_data->message_callback_data);
3091 ExtractSolveResultProto(start, model_parameters));
3098 return solve_result;
3102absl::StatusOr<ComputeInfeasibleSubsystemResultProto>
3106 const absl::Time start = absl::Now();
3118 ListInvalidIndicators());
3129 std::unique_ptr<SolveInterrupter> local_interrupter;
3130 if (interrupter !=
nullptr) {
3131 local_interrupter = std::make_unique<SolveInterrupter>();
3134 local_interrupter.get(), [&]() {
3143 gurobi_->Terminate();
3153 interrupter, [&]() { local_interrupter->Interrupt(); });
3159 std::unique_ptr<GurobiCallbackData> gurobi_cb_data;
3160 if (local_interrupter !=
nullptr || message_cb !=
nullptr) {
3162 RegisterCallback({},
nullptr, message_cb, start,
3163 local_interrupter.get()));
3164 grb_cb = [&gurobi_cb_data](
3167 gurobi_cb_data->message_callback_data,
3168 gurobi_cb_data->local_interrupter);
3172 ASSIGN_OR_RETURN(
const bool proven_infeasible, gurobi_->ComputeIIS(grb_cb));
3176 if (gurobi_cb_data !=
nullptr) {
3178 gurobi_cb_data->message_callback_data);
3182 ComputeInfeasibleSubsystemResultProto iis_result,
3183 ExtractComputeInfeasibleSubsystemResultProto(proven_infeasible));
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
absl::StatusOr< bool > Update(const ModelUpdateProto &model_update) override
absl::StatusOr< SolveResultProto > Solve(const SolveParametersProto ¶meters, const ModelSolveParametersProto &model_parameters, MessageCallback message_cb, const CallbackRegistrationProto &callback_registration, Callback cb, const SolveInterrupter *interrupter) override
static absl::StatusOr< std::unique_ptr< GurobiSolver > > New(const ModelProto &input_model, const SolverInterface::InitArgs &init_args)
absl::StatusOr< ComputeInfeasibleSubsystemResultProto > ComputeInfeasibleSubsystem(const SolveParametersProto ¶meters, MessageCallback message_cb, const SolveInterrupter *interrupter) override
static absl::StatusOr< std::unique_ptr< Gurobi > > NewWithSharedPrimaryEnv(GRBenv *primary_env)
std::function< absl::Status(const CallbackContext &)> Callback
static absl::StatusOr< std::unique_ptr< Gurobi > > New(GRBenvUniquePtr primary_env=nullptr)
std::function< void(const std::vector< std::string > &)> MessageCallback
std::function< absl::StatusOr< CallbackResultProto >( const CallbackDataProto &)> Callback
#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_INT_ATTR_IIS_QCONSTR
#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_OBJNUMBER
#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_DBL_ATTR_OBJNVAL
#define GRB_INT_PAR_POOLSOLUTIONS
#define GRB_CHAR_ATTR_VTYPE
#define GRB_INT_ATTR_NUMSTART
#define GRB_DBL_ATTR_BOUND_VIO
#define GRB_NONBASIC_UPPER
#define GRB_INT_ATTR_IIS_UB
#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_ATTR_IIS_GENCONSTR
#define GRB_INT_ATTR_IIS_MINIMAL
#define GRB_INT_PAR_STARTNUMBER
#define GRB_CHAR_ATTR_QCSENSE
#define GRB_INT_ATTR_IIS_SOS
#define GRB_DBL_ATTR_QCPI
#define GRB_INT_ATTR_CBASIS
#define GRB_INT_ATTR_IIS_CONSTR
#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_INT_ATTR_IIS_LB
#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
#define GRB_INT_ATTR_LAZY
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
auto & InsertKeyOrDie(Collection *const collection, const typename Collection::value_type::first_type &key)
An object oriented wrapper for quadratic constraints in ModelStorage.
TerminationProto TerminateForReason(const TerminationReasonProto reason, const absl::string_view detail)
absl::Status ModelIsSupported(const ModelProto &model, const SupportedProblemStructures &support_menu, const absl::string_view solver_name)
std::vector< bool > EventToGurobiWhere(const absl::flat_hash_set< CallbackEventProto > &events)
absl::Status ModelSolveParametersAreSupported(const ModelSolveParametersProto &model_parameters, const SupportedProblemStructures &support_menu, const absl::string_view solver_name)
absl::StatusOr< GRBenvUniquePtr > NewPrimaryEnvironment(std::optional< GurobiInitializerProto::ISVKey > proto_isv_key)
std::function< CallbackResult(const CallbackData &)> Callback
absl::flat_hash_set< CallbackEventProto > EventSet(const CallbackRegistrationProto &callback_registration)
Returns the callback_registration.request_registration as a set of enums.
TerminationProto OptimalTerminationProto(const double finite_primal_objective, const double dual_objective, const absl::string_view detail)
TerminationProto LimitTerminationProto(const bool is_maximize, const LimitProto limit, const std::optional< double > optional_finite_primal_objective, const std::optional< double > optional_dual_objective, const absl::string_view detail)
TerminationProto InfeasibleOrUnboundedTerminationProto(bool is_maximize, const FeasibilityStatusProto dual_feasibility_status, const absl::string_view detail)
bool UpdateIsSupported(const ModelUpdateProto &update, const SupportedProblemStructures &support_menu)
std::function< void(const std::vector< std::string > &)> MessageCallback
void GurobiCallbackImplFlush(const GurobiCallbackInput &callback_input, MessageCallbackData &message_callback_data)
absl::Status CheckRegisteredCallbackEvents(const CallbackRegistrationProto ®istration, const absl::flat_hash_set< CallbackEventProto > &supported_events)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
TerminationProto CutoffTerminationProto(bool is_maximize, const absl::string_view detail)
Calls NoSolutionFoundTerminationProto() with LIMIT_CUTOFF LIMIT.
absl::Status GurobiCallbackImpl(const Gurobi::CallbackContext &context, const GurobiCallbackInput &callback_input, MessageCallbackData &message_callback_data, SolveInterrupter *const local_interrupter)
std::unique_ptr< GRBenv, GurobiFreeEnv > GRBenvUniquePtr
TerminationProto InfeasibleTerminationProto(bool is_maximize, const FeasibilityStatusProto dual_feasibility_status, const absl::string_view detail)
TerminationProto UnboundedTerminationProto(const bool is_maximize, const absl::string_view detail)
std::vector< K > SortedMapKeys(const absl::flat_hash_map< K, V > &in_map)
In SWIG mode, we don't want anything besides these top-level includes.
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
bool GurobiIsCorrectlyInstalled()
std::string ProtoEnumToString(ProtoEnumType enum_value)
constexpr bool kAnyXsanEnabled
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)
StatusBuilder InternalErrorBuilder()
StatusBuilder UnimplementedErrorBuilder()
StatusBuilder InvalidArgumentErrorBuilder()
#define MATH_OPT_REGISTER_SOLVER(solver_type, solver_factory)
absl::Status ToStatus() const
absl::Status ToStatus() const
const NonStreamableGurobiInitArguments * ToNonStreamableGurobiInitArguments() const override
Initialization arguments.