polish third_party_solvers; speed up cp-sat on no_ovrlap_2d
This commit is contained in:
@@ -31,7 +31,6 @@
|
||||
#include "absl/log/check.h"
|
||||
#include "absl/log/log.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
@@ -2756,6 +2755,7 @@ absl::StatusOr<bool> GurobiSolver::Update(
|
||||
|
||||
absl::StatusOr<std::unique_ptr<GurobiSolver>> GurobiSolver::New(
|
||||
const ModelProto& input_model, const SolverInterface::InitArgs& init_args) {
|
||||
// TODO(user): Correctly load the gurobi library in open source.
|
||||
RETURN_IF_ERROR(
|
||||
ModelIsSupported(input_model, kGurobiSupportedStructures, "Gurobi"));
|
||||
if (!input_model.auxiliary_objectives().empty() &&
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
#include "ortools/third_party_solvers/gurobi_environment.h"
|
||||
#include "ortools/util/solve_interrupter.h"
|
||||
|
||||
|
||||
namespace operations_research {
|
||||
namespace math_opt {
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#include "ortools/sat/precedences.h"
|
||||
#include "ortools/sat/scheduling_helpers.h"
|
||||
#include "ortools/sat/synchronization.h"
|
||||
#include "ortools/util/bitset.h"
|
||||
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
@@ -42,22 +41,14 @@ Precedences2DPropagator::Precedences2DPropagator(
|
||||
: helper_(*helper),
|
||||
linear2_bounds_(model->GetOrCreate<Linear2Bounds>()),
|
||||
linear2_watcher_(model->GetOrCreate<Linear2Watcher>()),
|
||||
shared_stats_(model->GetOrCreate<SharedStatistics>()) {
|
||||
shared_stats_(model->GetOrCreate<SharedStatistics>()),
|
||||
non_trivial_bounds_(
|
||||
model->GetOrCreate<Linear2WithPotentialNonTrivalBounds>()) {
|
||||
model->GetOrCreate<LinearPropagator>()->SetPushAffineUbForBinaryRelation();
|
||||
}
|
||||
|
||||
void Precedences2DPropagator::CollectPairsOfBoxesWithNonTrivialDistance() {
|
||||
helper_.SynchronizeAndSetDirection();
|
||||
non_trivial_pairs_.clear();
|
||||
|
||||
struct VarUsage {
|
||||
// boxes[0=x, 1=y][0=start, 1=end]
|
||||
std::vector<int> boxes[2][2];
|
||||
};
|
||||
absl::flat_hash_map<IntegerVariable, VarUsage> var_to_box_and_coeffs;
|
||||
SparseBitset<IntegerVariable>& var_set =
|
||||
*linear2_bounds_->GetTemporyClearedAndResizedBitset();
|
||||
|
||||
void Precedences2DPropagator::UpdateVarLookups() {
|
||||
var_to_box_and_coeffs_.clear();
|
||||
for (int dim = 0; dim < 2; ++dim) {
|
||||
const SchedulingConstraintHelper& dim_helper =
|
||||
dim == 0 ? helper_.x_helper() : helper_.y_helper();
|
||||
@@ -67,41 +58,52 @@ void Precedences2DPropagator::CollectPairsOfBoxesWithNonTrivialDistance() {
|
||||
for (int i = 0; i < helper_.NumBoxes(); ++i) {
|
||||
const IntegerVariable var = interval_points[i].var;
|
||||
if (var != kNoIntegerVariable) {
|
||||
var_set.Set(PositiveVariable(var));
|
||||
var_to_box_and_coeffs[PositiveVariable(var)].boxes[dim][j].push_back(
|
||||
var_to_box_and_coeffs_[PositiveVariable(var)].boxes[dim][j].push_back(
|
||||
i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Precedences2DPropagator::CollectNewPairsOfBoxesWithNonTrivialDistance() {
|
||||
const absl::Span<const LinearExpression2> exprs =
|
||||
linear2_bounds_->GetAllExpressionsWithPotentialNonTrivialBounds(
|
||||
var_set.BitsetConstView());
|
||||
VLOG(2) << "CollectPairsOfBoxesWithNonTrivialDistance called, num_exprs: "
|
||||
<< exprs.size();
|
||||
for (const LinearExpression2& expr : exprs) {
|
||||
auto it1 = var_to_box_and_coeffs.find(PositiveVariable(expr.vars[0]));
|
||||
auto it2 = var_to_box_and_coeffs.find(PositiveVariable(expr.vars[1]));
|
||||
DCHECK(it1 != var_to_box_and_coeffs.end());
|
||||
DCHECK(it2 != var_to_box_and_coeffs.end());
|
||||
non_trivial_bounds_->GetLinear2WithPotentialNonTrivalBounds();
|
||||
if (exprs.size() != num_known_linear2_) {
|
||||
VLOG(2) << "CollectPairsOfBoxesWithNonTrivialDistance called, num_exprs: "
|
||||
<< exprs.size();
|
||||
}
|
||||
for (; num_known_linear2_ < exprs.size(); ++num_known_linear2_) {
|
||||
const LinearExpression2& positive_expr = exprs[num_known_linear2_];
|
||||
LinearExpression2 negated_expr = positive_expr;
|
||||
negated_expr.Negate();
|
||||
for (const LinearExpression2& expr : {positive_expr, negated_expr}) {
|
||||
auto it1 = var_to_box_and_coeffs_.find(PositiveVariable(expr.vars[0]));
|
||||
auto it2 = var_to_box_and_coeffs_.find(PositiveVariable(expr.vars[1]));
|
||||
if (it1 == var_to_box_and_coeffs_.end()) {
|
||||
continue;
|
||||
}
|
||||
if (it2 == var_to_box_and_coeffs_.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const VarUsage& usage1 = it1->second;
|
||||
const VarUsage& usage2 = it2->second;
|
||||
for (int dim = 0; dim < 2; ++dim) {
|
||||
const SchedulingConstraintHelper& dim_helper =
|
||||
dim == 0 ? helper_.x_helper() : helper_.y_helper();
|
||||
for (const int box1 : usage1.boxes[dim][0 /* start */]) {
|
||||
for (const int box2 : usage2.boxes[dim][1 /* end */]) {
|
||||
if (box1 == box2) continue;
|
||||
const auto [expr2, unused] = EncodeDifferenceLowerThan(
|
||||
dim_helper.Starts()[box1], dim_helper.Ends()[box2],
|
||||
/*ub=unused*/ 0);
|
||||
if (expr == expr2) {
|
||||
if (box1 < box2) {
|
||||
non_trivial_pairs_.push_back({box1, box2});
|
||||
} else {
|
||||
non_trivial_pairs_.push_back({box2, box1});
|
||||
const VarUsage& usage1 = it1->second;
|
||||
const VarUsage& usage2 = it2->second;
|
||||
for (int dim = 0; dim < 2; ++dim) {
|
||||
const SchedulingConstraintHelper& dim_helper =
|
||||
dim == 0 ? helper_.x_helper() : helper_.y_helper();
|
||||
for (const int box1 : usage1.boxes[dim][0 /* start */]) {
|
||||
for (const int box2 : usage2.boxes[dim][1 /* end */]) {
|
||||
if (box1 == box2) continue;
|
||||
const auto [expr2, unused] = EncodeDifferenceLowerThan(
|
||||
dim_helper.Starts()[box1], dim_helper.Ends()[box2],
|
||||
/*ub=unused*/ 0);
|
||||
if (expr == expr2) {
|
||||
if (box1 < box2) {
|
||||
non_trivial_pairs_.push_back({box1, box2});
|
||||
} else {
|
||||
non_trivial_pairs_.push_back({box2, box1});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,13 +116,13 @@ void Precedences2DPropagator::CollectPairsOfBoxesWithNonTrivialDistance() {
|
||||
|
||||
bool Precedences2DPropagator::Propagate() {
|
||||
if (!helper_.SynchronizeAndSetDirection()) return false;
|
||||
if (last_helper_inprocessing_count_ != helper_.InProcessingCount() ||
|
||||
helper_.x_helper().CurrentDecisionLevel() == 0 ||
|
||||
last_linear2_timestamp_ != linear2_watcher_->Timestamp()) {
|
||||
if (last_helper_inprocessing_count_ != helper_.InProcessingCount()) {
|
||||
last_helper_inprocessing_count_ = helper_.InProcessingCount();
|
||||
last_linear2_timestamp_ = linear2_watcher_->Timestamp();
|
||||
CollectPairsOfBoxesWithNonTrivialDistance();
|
||||
UpdateVarLookups();
|
||||
num_known_linear2_ = 0;
|
||||
non_trivial_pairs_.clear();
|
||||
}
|
||||
CollectNewPairsOfBoxesWithNonTrivialDistance();
|
||||
|
||||
num_calls_++;
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "ortools/sat/integer.h"
|
||||
#include "ortools/sat/integer_base.h"
|
||||
#include "ortools/sat/model.h"
|
||||
#include "ortools/sat/no_overlap_2d_helper.h"
|
||||
#include "ortools/sat/precedences.h"
|
||||
@@ -30,7 +32,7 @@ namespace sat {
|
||||
// This class implements a propagator for non_overlap_2d constraints that uses
|
||||
// the Linear2Bounds to detect precedences between pairs of boxes and
|
||||
// detect a conflict if the precedences implies an overlap between the two
|
||||
// boxes. For doing this efficiently, it keep track of pairs of boxes that have
|
||||
// boxes. For doing this efficiently, it keeps track of pairs of boxes that have
|
||||
// non-fixed precedences in the Linear2Bounds and only check those in the
|
||||
// propagation.
|
||||
class Precedences2DPropagator : public PropagatorInterface {
|
||||
@@ -43,16 +45,25 @@ class Precedences2DPropagator : public PropagatorInterface {
|
||||
int RegisterWith(GenericLiteralWatcher* watcher);
|
||||
|
||||
private:
|
||||
void CollectPairsOfBoxesWithNonTrivialDistance();
|
||||
void CollectNewPairsOfBoxesWithNonTrivialDistance();
|
||||
void UpdateVarLookups();
|
||||
|
||||
std::vector<std::pair<int, int>> non_trivial_pairs_;
|
||||
struct VarUsage {
|
||||
// boxes[0=x, 1=y][0=start, 1=end]
|
||||
std::vector<int> boxes[2][2];
|
||||
};
|
||||
|
||||
absl::flat_hash_map<IntegerVariable, VarUsage> var_to_box_and_coeffs_;
|
||||
|
||||
NoOverlap2DConstraintHelper& helper_;
|
||||
Linear2Bounds* linear2_bounds_;
|
||||
Linear2Watcher* linear2_watcher_;
|
||||
SharedStatistics* shared_stats_;
|
||||
Linear2WithPotentialNonTrivalBounds* non_trivial_bounds_;
|
||||
|
||||
int last_helper_inprocessing_count_ = -1;
|
||||
int num_known_linear2_ = 0;
|
||||
int64_t last_linear2_timestamp_ = -1;
|
||||
|
||||
int64_t num_conflicts_ = 0;
|
||||
|
||||
@@ -129,7 +129,6 @@ cc_library(
|
||||
":scheduling_helpers",
|
||||
":synchronization",
|
||||
"//ortools/base:stl_util",
|
||||
"//ortools/util:bitset",
|
||||
"@abseil-cpp//absl/container:flat_hash_map",
|
||||
"@abseil-cpp//absl/log",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
|
||||
@@ -100,6 +100,7 @@ std::pair<bool, bool> RootLevelLinear2Bounds::Add(LinearExpression2 expr,
|
||||
status_ub == AddResult::ADDED || status_ub == AddResult::UPDATED;
|
||||
if (!lb_restricted && !ub_restricted) return {false, false};
|
||||
|
||||
non_trivial_bounds_->AddOrGet(expr);
|
||||
++num_updates_;
|
||||
linear2_watcher_->NotifyBoundChanged(expr);
|
||||
|
||||
@@ -348,6 +349,7 @@ void EnforcedLinear2Bounds::PushConditionalRelation(
|
||||
const int new_index = conditional_stack_.size();
|
||||
const auto [it, inserted] = conditional_relations_.insert({expr, new_index});
|
||||
if (inserted) {
|
||||
non_trivial_bounds_->AddOrGet(expr);
|
||||
CreateLevelEntryIfNeeded();
|
||||
conditional_stack_.emplace_back(/*prev_entry=*/-1, rhs, expr, enforcements);
|
||||
|
||||
@@ -1815,7 +1817,9 @@ Linear2BoundsFromLinear3::Linear2BoundsFromLinear3(Model* model)
|
||||
linear2_watcher_(model->GetOrCreate<Linear2Watcher>()),
|
||||
watcher_(model->GetOrCreate<GenericLiteralWatcher>()),
|
||||
shared_stats_(model->GetOrCreate<SharedStatistics>()),
|
||||
root_level_bounds_(model->GetOrCreate<RootLevelLinear2Bounds>()) {}
|
||||
root_level_bounds_(model->GetOrCreate<RootLevelLinear2Bounds>()),
|
||||
non_trivial_bounds_(
|
||||
model->GetOrCreate<Linear2WithPotentialNonTrivalBounds>()) {}
|
||||
|
||||
// Note that for speed we do not compare to the trivial or root level bounds.
|
||||
//
|
||||
@@ -1857,6 +1861,7 @@ bool Linear2BoundsFromLinear3::AddAffineUpperBound(LinearExpression2 expr,
|
||||
it->second = {affine_ub, divisor}; // Overwrite.
|
||||
} else {
|
||||
// Note that this should almost never happen (only once per lin2).
|
||||
non_trivial_bounds_->AddOrGet(expr);
|
||||
best_affine_ub_[expr] = {affine_ub, divisor};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
#ifndef OR_TOOLS_SAT_PRECEDENCES_H_
|
||||
#define OR_TOOLS_SAT_PRECEDENCES_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -45,6 +45,63 @@
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
DEFINE_STRONG_INDEX_TYPE(LinearExpression2Index);
|
||||
const LinearExpression2Index kNoLinearExpression2Index(-1);
|
||||
inline LinearExpression2Index NegationOf(LinearExpression2Index i) {
|
||||
return LinearExpression2Index(i.value() ^ 1);
|
||||
}
|
||||
|
||||
inline bool Linear2IsPositive(LinearExpression2Index i) {
|
||||
return (i.value() & 1) == 0;
|
||||
}
|
||||
|
||||
inline LinearExpression2Index PositiveLinear2(LinearExpression2Index i) {
|
||||
return LinearExpression2Index(i.value() & (~1));
|
||||
}
|
||||
|
||||
// Class to hold a list of LinearExpression2 that have (potentially) non-trivial
|
||||
// bounds. This class is overzealous, in the sense that if a linear2 is in the
|
||||
// list, it does not necessarily mean that it has a non-trivial bound, but the
|
||||
// converse is true: if a linear2 is not in the list,
|
||||
// Linear2Bounds::GetUpperBound() will return a trivial bound.
|
||||
class Linear2WithPotentialNonTrivalBounds {
|
||||
public:
|
||||
Linear2WithPotentialNonTrivalBounds() = default;
|
||||
|
||||
// Returns a never-changing index for the given linear expression.
|
||||
// The expression must already be canonicalized and divided by its GCD.
|
||||
LinearExpression2Index AddOrGet(LinearExpression2 expr) {
|
||||
DCHECK(expr.IsCanonicalized());
|
||||
DCHECK_EQ(expr.DivideByGcd(), 1);
|
||||
const bool negated = expr.NegateForCanonicalization();
|
||||
auto [it, inserted] = expr_to_index_.insert({expr, exprs_.size()});
|
||||
if (inserted) {
|
||||
CHECK_LT(2 * exprs_.size() + 1,
|
||||
std::numeric_limits<LinearExpression2Index>::max());
|
||||
exprs_.push_back(expr);
|
||||
}
|
||||
const LinearExpression2Index positive_index(2 * it->second);
|
||||
if (negated) {
|
||||
return NegationOf(positive_index);
|
||||
} else {
|
||||
return positive_index;
|
||||
}
|
||||
}
|
||||
|
||||
// Return all positive linear2 expressions that have a potentially non-trivial
|
||||
// bound. When calling this code it is often a good idea to check both the
|
||||
// expression on the span and its negation. The order is fixed forever and
|
||||
// this span can only grow by appending new expressions.
|
||||
absl::Span<const LinearExpression2> GetLinear2WithPotentialNonTrivalBounds()
|
||||
const {
|
||||
return exprs_;
|
||||
}
|
||||
|
||||
private:
|
||||
util_intops::StrongVector<LinearExpression2Index, LinearExpression2> exprs_;
|
||||
absl::flat_hash_map<LinearExpression2, int> expr_to_index_;
|
||||
};
|
||||
|
||||
// Simple "watcher" class that will be notified if a linear2 bound changed. It
|
||||
// can also be queried to see if LinearExpression2 involving a specific variable
|
||||
// changed since last time.
|
||||
@@ -79,7 +136,9 @@ class RootLevelLinear2Bounds {
|
||||
explicit RootLevelLinear2Bounds(Model* model)
|
||||
: integer_trail_(model->GetOrCreate<IntegerTrail>()),
|
||||
linear2_watcher_(model->GetOrCreate<Linear2Watcher>()),
|
||||
shared_stats_(model->GetOrCreate<SharedStatistics>()) {}
|
||||
shared_stats_(model->GetOrCreate<SharedStatistics>()),
|
||||
non_trivial_bounds_(
|
||||
model->GetOrCreate<Linear2WithPotentialNonTrivalBounds>()) {}
|
||||
|
||||
~RootLevelLinear2Bounds();
|
||||
|
||||
@@ -148,6 +207,7 @@ class RootLevelLinear2Bounds {
|
||||
IntegerTrail* integer_trail_;
|
||||
Linear2Watcher* linear2_watcher_;
|
||||
SharedStatistics* shared_stats_;
|
||||
Linear2WithPotentialNonTrivalBounds* non_trivial_bounds_;
|
||||
|
||||
// Lookup table to find all the LinearExpression2 with a given variable and
|
||||
// having both coefficient 1.
|
||||
@@ -258,7 +318,9 @@ class EnforcedLinear2Bounds : public ReversibleInterface {
|
||||
integer_trail_(model->GetOrCreate<IntegerTrail>()),
|
||||
linear2_watcher_(model->GetOrCreate<Linear2Watcher>()),
|
||||
root_level_bounds_(model->GetOrCreate<RootLevelLinear2Bounds>()),
|
||||
shared_stats_(model->GetOrCreate<SharedStatistics>()) {
|
||||
shared_stats_(model->GetOrCreate<SharedStatistics>()),
|
||||
non_trivial_bounds_(
|
||||
model->GetOrCreate<Linear2WithPotentialNonTrivalBounds>()) {
|
||||
integer_trail_->RegisterReversibleClass(this);
|
||||
}
|
||||
|
||||
@@ -324,6 +386,7 @@ class EnforcedLinear2Bounds : public ReversibleInterface {
|
||||
Linear2Watcher* linear2_watcher_;
|
||||
RootLevelLinear2Bounds* root_level_bounds_;
|
||||
SharedStatistics* shared_stats_;
|
||||
Linear2WithPotentialNonTrivalBounds* non_trivial_bounds_;
|
||||
|
||||
int64_t num_conditional_relation_updates_ = 0;
|
||||
|
||||
@@ -476,6 +539,7 @@ class Linear2BoundsFromLinear3 {
|
||||
GenericLiteralWatcher* watcher_;
|
||||
SharedStatistics* shared_stats_;
|
||||
RootLevelLinear2Bounds* root_level_bounds_;
|
||||
Linear2WithPotentialNonTrivalBounds* non_trivial_bounds_;
|
||||
|
||||
int64_t num_affine_updates_ = 0;
|
||||
|
||||
@@ -558,7 +622,8 @@ class Linear2Bounds {
|
||||
GetAllExpressionsWithPotentialNonTrivialBounds(
|
||||
Bitset64<IntegerVariable>::ConstView var_set) const;
|
||||
|
||||
// Returns a temporay bitset, cleared, and resized for all existing variables.
|
||||
// Returns a temporary bitset, cleared, and resized for all existing
|
||||
// variables.
|
||||
//
|
||||
// If we have many class calling
|
||||
// GetAllExpressionsWithPotentialNonTrivialBounds() it is important that not
|
||||
|
||||
@@ -57,6 +57,7 @@ cc_library(
|
||||
"//ortools/base",
|
||||
"//ortools/base:file",
|
||||
"//ortools/base:status_macros",
|
||||
"@abseil-cpp//absl/base:core_headers",
|
||||
"@abseil-cpp//absl/status",
|
||||
"@abseil-cpp//absl/status:statusor",
|
||||
"@abseil-cpp//absl/strings",
|
||||
|
||||
@@ -32,5 +32,5 @@ target_link_libraries(${NAME} PRIVATE
|
||||
absl::status
|
||||
absl::strings
|
||||
absl::str_format
|
||||
absl::synchronization
|
||||
)
|
||||
absl::synchronization)
|
||||
|
||||
|
||||
@@ -13,16 +13,16 @@
|
||||
|
||||
#include "ortools/third_party_solvers/gurobi_environment.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/no_destructor.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_join.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/third_party_solvers/dynamic_library.h"
|
||||
|
||||
@@ -332,12 +332,14 @@ void LoadGurobiFunctions(DynamicLibrary* gurobi_dynamic_library) {
|
||||
gurobi_dynamic_library->GetFunction(&GRBplatform, "GRBplatform");
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
||||
std::vector<std::string> GurobiDynamicLibraryPotentialPaths() {
|
||||
std::vector<std::string> potential_paths;
|
||||
const std::vector<absl::string_view> kGurobiVersions = {
|
||||
"1202", "1201", "1200", "1103", "1102", "1101", "1100", "1003",
|
||||
"1002", "1001", "1000", "952", "951", "950", "911",
|
||||
"910", "903", "902", "811", "801", "752"};
|
||||
"1202", "1201", "1200", "1103", "1102", "1101", "1100",
|
||||
"1003", "1002", "1001", "1000", "952", "951", "950",
|
||||
"911", "910", "903", "902", "811", "801", "752"};
|
||||
potential_paths.reserve(kGurobiVersions.size() * 3);
|
||||
|
||||
// Look for libraries pointed by GUROBI_HOME first.
|
||||
@@ -396,7 +398,7 @@ std::vector<std::string> GurobiDynamicLibraryPotentialPaths() {
|
||||
|
||||
#if defined(__GNUC__) // path in linux64 gurobi/optimizer docker image.
|
||||
for (const absl::string_view version :
|
||||
{"12.0.2","12.0.1", "12.0.0", "11.0.3", "11.0.2", "11.0.1", "11.0.0",
|
||||
{"12.0.2", "12.0.1", "12.0.0", "11.0.3", "11.0.2", "11.0.1", "11.0.0",
|
||||
"10.0.3", "10.0.2", "10.0.1", "10.0.0", "9.5.2", "9.5.1", "9.5.0"}) {
|
||||
potential_paths.push_back(
|
||||
absl::StrCat("/opt/gurobi/linux64/lib/libgurobi.so.", version));
|
||||
@@ -407,37 +409,47 @@ std::vector<std::string> GurobiDynamicLibraryPotentialPaths() {
|
||||
|
||||
absl::Status LoadGurobiDynamicLibrary(
|
||||
std::vector<absl::string_view> potential_paths) {
|
||||
static std::once_flag gurobi_loading_done;
|
||||
static absl::Status gurobi_load_status;
|
||||
static DynamicLibrary gurobi_library;
|
||||
static absl::Mutex mutex;
|
||||
struct GurobiLibraryStruct {
|
||||
absl::Status gurobi_load_status;
|
||||
DynamicLibrary gurobi_library;
|
||||
};
|
||||
|
||||
absl::MutexLock lock(&mutex);
|
||||
|
||||
std::call_once(gurobi_loading_done, [&potential_paths]() {
|
||||
const std::vector<std::string> canonical_paths =
|
||||
GurobiDynamicLibraryPotentialPaths();
|
||||
potential_paths.insert(potential_paths.end(), canonical_paths.begin(),
|
||||
canonical_paths.end());
|
||||
static absl::NoDestructor<GurobiLibraryStruct> loaded([&potential_paths]() {
|
||||
GurobiLibraryStruct result;
|
||||
// Try to load the library from the potential paths.
|
||||
for (const absl::string_view path : potential_paths) {
|
||||
if (gurobi_library.TryToLoad(path)) {
|
||||
LOG(INFO) << "Found the Gurobi library in '" << path << ".";
|
||||
if (result.gurobi_library.TryToLoad(path)) {
|
||||
VLOG(1) << "Found the Gurobi library in '" << path << ".";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (gurobi_library.LibraryIsLoaded()) {
|
||||
LoadGurobiFunctions(&gurobi_library);
|
||||
gurobi_load_status = absl::OkStatus();
|
||||
// Fallback to the canonical paths.
|
||||
if (!result.gurobi_library.LibraryIsLoaded()) {
|
||||
const std::vector<std::string> canonical_paths =
|
||||
GurobiDynamicLibraryPotentialPaths();
|
||||
for (const absl::string_view path : canonical_paths) {
|
||||
if (result.gurobi_library.TryToLoad(path)) {
|
||||
VLOG(1) << "Found the Gurobi library in '" << path << ".";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result.gurobi_library.LibraryIsLoaded()) {
|
||||
LoadGurobiFunctions(&result.gurobi_library);
|
||||
result.gurobi_load_status = absl::OkStatus();
|
||||
} else {
|
||||
gurobi_load_status = absl::NotFoundError(absl::StrCat(
|
||||
result.gurobi_load_status = absl::NotFoundError(absl::StrCat(
|
||||
"Could not find the Gurobi shared library. Looked in: [",
|
||||
absl::StrJoin(potential_paths, "', '"),
|
||||
"]. If you know where it"
|
||||
" is, pass the full path to 'LoadGurobiDynamicLibrary()'."));
|
||||
}
|
||||
});
|
||||
return gurobi_load_status;
|
||||
return result;
|
||||
}());
|
||||
|
||||
return loaded->gurobi_load_status;
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
|
||||
typedef struct _GRBmodel GRBmodel;
|
||||
typedef struct _GRBenv GRBenv;
|
||||
typedef struct _GRBsvec {
|
||||
|
||||
Reference in New Issue
Block a user