big sync with internal code; mostly code reformating; a few fixes for CP-SAT; more work on bandit based concatenators for the routing library
This commit is contained in:
@@ -478,7 +478,7 @@ class FilteredHeuristicExpensiveChainLNSOperator
|
||||
int last_route_;
|
||||
|
||||
const int num_arcs_to_consider_;
|
||||
std::vector<std::pair<int64, int> > most_expensive_arc_starts_and_ranks_;
|
||||
std::vector<std::pair<int64, int>> most_expensive_arc_starts_and_ranks_;
|
||||
/// Indices in most_expensive_arc_starts_and_ranks_ corresponding to the first
|
||||
/// and second arcs currently being considered for removal.
|
||||
std::pair</*first_arc_index*/ int, /*second_arc_index*/ int>
|
||||
@@ -531,14 +531,14 @@ class FilteredHeuristicCloseNodesLNSOperator
|
||||
|
||||
std::vector<int64> GetActiveSiblings(int64 node) const;
|
||||
|
||||
const std::vector<std::pair<std::vector<int64>, std::vector<int64> > >&
|
||||
const std::vector<std::pair<std::vector<int64>, std::vector<int64>>>&
|
||||
pickup_delivery_pairs_;
|
||||
|
||||
int current_node_;
|
||||
int last_node_;
|
||||
bool just_started_;
|
||||
|
||||
std::vector<std::vector<int64> > close_nodes_;
|
||||
std::vector<std::vector<int64>> close_nodes_;
|
||||
/// Keep track of changes when making a neighbor.
|
||||
std::vector<int64> new_nexts_;
|
||||
SparseBitset<> changed_nexts_;
|
||||
@@ -578,7 +578,7 @@ class RelocateExpensiveChain : public PathOperator {
|
||||
|
||||
int num_arcs_to_consider_;
|
||||
int current_path_;
|
||||
std::vector<std::pair<int64, int> > most_expensive_arc_starts_and_ranks_;
|
||||
std::vector<std::pair<int64, int>> most_expensive_arc_starts_and_ranks_;
|
||||
/// Indices in most_expensive_arc_starts_and_ranks_ corresponding to the first
|
||||
/// and second arcs currently being considered for removal.
|
||||
std::pair</*first_arc_index*/ int, /*second_arc_index*/ int>
|
||||
|
||||
@@ -1115,6 +1115,13 @@ using CallMap = absl::flat_hash_map<
|
||||
std::string, std::function<bool(const Constraint& ct,
|
||||
std::function<int64(IntegerVariable*)>)>>;
|
||||
|
||||
// Creates a map between flatzinc predicates and CP-SAT builders.
|
||||
//
|
||||
// Predicates starting with fzn_ are predicates with the same name in flatzinc
|
||||
// and in minizinc. The fzn_ prefix is added to differentiate them.
|
||||
//
|
||||
// Predicates starting with ortools_ are predicates defined only in or-tools.
|
||||
// They are created at compilation time when using the or-tools mzn library.
|
||||
CallMap CreateCallMap() {
|
||||
CallMap m;
|
||||
m["fzn_all_different_int"] = CheckAllDifferentInt;
|
||||
|
||||
@@ -109,6 +109,7 @@ std::vector<char*> FixAndParseParameters(int* argc, char*** argv) {
|
||||
}
|
||||
const char kUsage[] =
|
||||
"Usage: see flags.\nThis program parses and solve a flatzinc problem.";
|
||||
|
||||
absl::SetProgramUsageMessage(kUsage);
|
||||
const std::vector<char*> residual_flags =
|
||||
absl::ParseCommandLine(*argc, *argv);
|
||||
|
||||
@@ -7,4 +7,3 @@ predicate fzn_diffn(array[int] of var int: x,
|
||||
array[int] of var int: y,
|
||||
array[int] of var int: dx,
|
||||
array[int] of var int: dy);
|
||||
|
||||
|
||||
@@ -4,5 +4,5 @@ predicate ortools_network_flow(array [int] of int: arc,
|
||||
|
||||
predicate fzn_network_flow(array [int,int] of int: arc,
|
||||
array [int] of int: balance,
|
||||
array [int] of var int: flow) =
|
||||
array [int] of var int: flow) =
|
||||
ortools_network_flow(array1d(arc),balance,flow);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/flags/flag.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/timer.h"
|
||||
#include "ortools/flatzinc/logging.h"
|
||||
|
||||
@@ -71,12 +71,14 @@ class ChristofidesPathSolver {
|
||||
void Solve();
|
||||
|
||||
// Safe addition operator to avoid overflows when possible.
|
||||
// template <typename T>
|
||||
// T SafeAdd(T a, T b) {
|
||||
// return a + b;
|
||||
// }
|
||||
// template <>
|
||||
int64 SafeAdd(int64 a, int64 b) { return CapAdd(a, b); }
|
||||
template <typename T>
|
||||
T SafeAdd(T a, T b) {
|
||||
return a + b;
|
||||
}
|
||||
template <>
|
||||
int64 SafeAdd(int64 a, int64 b) {
|
||||
return CapAdd(a, b);
|
||||
}
|
||||
|
||||
// Matching algorithm to use.
|
||||
MatchingAlgorithm matching_;
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/container/inlined_vector.h"
|
||||
#include "ortools/base/hash.h"
|
||||
#include "ortools/base/map_util.h"
|
||||
|
||||
@@ -75,7 +75,6 @@ class BopInterface : public MPSolverInterface {
|
||||
// ------ Query statistics on the solution and the solve ------
|
||||
int64 iterations() const override;
|
||||
int64 nodes() const override;
|
||||
double best_objective_bound() const override;
|
||||
MPSolver::BasisStatus row_status(int constraint_index) const override;
|
||||
MPSolver::BasisStatus column_status(int variable_index) const override;
|
||||
|
||||
@@ -110,7 +109,6 @@ class BopInterface : public MPSolverInterface {
|
||||
std::vector<MPSolver::BasisStatus> column_status_;
|
||||
std::vector<MPSolver::BasisStatus> row_status_;
|
||||
bop::BopParameters parameters_;
|
||||
double best_objective_bound_;
|
||||
std::atomic<bool> interrupt_solver_;
|
||||
};
|
||||
|
||||
@@ -262,13 +260,6 @@ int64 BopInterface::nodes() const {
|
||||
return kUnknownNumberOfNodes;
|
||||
}
|
||||
|
||||
double BopInterface::best_objective_bound() const {
|
||||
if (!CheckSolutionIsSynchronized() || !CheckBestObjectiveBoundExists()) {
|
||||
return trivial_worst_objective_bound();
|
||||
}
|
||||
return best_objective_bound_;
|
||||
}
|
||||
|
||||
MPSolver::BasisStatus BopInterface::row_status(int constraint_index) const {
|
||||
return row_status_[constraint_index];
|
||||
}
|
||||
|
||||
@@ -108,8 +108,6 @@ class CBCInterface : public MPSolverInterface {
|
||||
int64 iterations() const override;
|
||||
// Number of branch-and-bound nodes. Only available for discrete problems.
|
||||
int64 nodes() const override;
|
||||
// Best objective bound. Only available for discrete problems.
|
||||
double best_objective_bound() const override;
|
||||
|
||||
// Returns the basis status of a row.
|
||||
MPSolver::BasisStatus row_status(int constraint_index) const override {
|
||||
@@ -152,7 +150,6 @@ class CBCInterface : public MPSolverInterface {
|
||||
// TODO(user): remove and query number of iterations directly from CbcModel
|
||||
int64 iterations_;
|
||||
int64 nodes_;
|
||||
double best_objective_bound_;
|
||||
// Special way to handle the relative MIP gap parameter.
|
||||
double relative_mip_gap_;
|
||||
int num_threads_ = 1;
|
||||
@@ -165,7 +162,6 @@ CBCInterface::CBCInterface(MPSolver* const solver)
|
||||
: MPSolverInterface(solver),
|
||||
iterations_(0),
|
||||
nodes_(0),
|
||||
best_objective_bound_(-std::numeric_limits<double>::infinity()),
|
||||
relative_mip_gap_(MPSolverParameters::kDefaultRelativeMipGap) {
|
||||
osi_.setStrParam(OsiProbName, solver_->name_);
|
||||
osi_.setObjSense(1);
|
||||
@@ -481,13 +477,6 @@ int64 CBCInterface::nodes() const {
|
||||
return nodes_;
|
||||
}
|
||||
|
||||
double CBCInterface::best_objective_bound() const {
|
||||
if (!CheckSolutionIsSynchronized() || !CheckBestObjectiveBoundExists()) {
|
||||
return trivial_worst_objective_bound();
|
||||
}
|
||||
return best_objective_bound_;
|
||||
}
|
||||
|
||||
// ----- Parameters -----
|
||||
|
||||
// The support for parameters in CBC is intentionally sparse. There is
|
||||
|
||||
@@ -85,8 +85,6 @@ class CLPInterface : public MPSolverInterface {
|
||||
int64 iterations() const override;
|
||||
// Number of branch-and-bound nodes. Only available for discrete problems.
|
||||
int64 nodes() const override;
|
||||
// Best objective bound. Only available for discrete problems.
|
||||
double best_objective_bound() const override;
|
||||
|
||||
// Returns the basis status of a row.
|
||||
MPSolver::BasisStatus row_status(int constraint_index) const override;
|
||||
@@ -544,11 +542,6 @@ int64 CLPInterface::nodes() const {
|
||||
return kUnknownNumberOfNodes;
|
||||
}
|
||||
|
||||
double CLPInterface::best_objective_bound() const {
|
||||
LOG(DFATAL) << "Best objective bound only available for discrete problems";
|
||||
return trivial_worst_objective_bound();
|
||||
}
|
||||
|
||||
MPSolver::BasisStatus CLPInterface::row_status(int constraint_index) const {
|
||||
DCHECK_LE(0, constraint_index);
|
||||
DCHECK_GT(last_constraint_index_, constraint_index);
|
||||
|
||||
@@ -60,7 +60,6 @@ class GLOPInterface : public MPSolverInterface {
|
||||
// ------ Query statistics on the solution and the solve ------
|
||||
int64 iterations() const override;
|
||||
int64 nodes() const override;
|
||||
double best_objective_bound() const override;
|
||||
MPSolver::BasisStatus row_status(int constraint_index) const override;
|
||||
MPSolver::BasisStatus column_status(int variable_index) const override;
|
||||
|
||||
@@ -245,11 +244,6 @@ int64 GLOPInterface::nodes() const {
|
||||
return kUnknownNumberOfNodes;
|
||||
}
|
||||
|
||||
double GLOPInterface::best_objective_bound() const {
|
||||
// TODO(user): report a better bound when we can.
|
||||
return trivial_worst_objective_bound();
|
||||
}
|
||||
|
||||
MPSolver::BasisStatus GLOPInterface::row_status(int constraint_index) const {
|
||||
return row_status_[constraint_index];
|
||||
}
|
||||
|
||||
@@ -136,8 +136,6 @@ class GLPKInterface : public MPSolverInterface {
|
||||
int64 iterations() const override;
|
||||
// Number of branch-and-bound nodes. Only available for discrete problems.
|
||||
int64 nodes() const override;
|
||||
// Best objective bound. Only available for discrete problems.
|
||||
double best_objective_bound() const override;
|
||||
|
||||
// Returns the basis status of a row.
|
||||
MPSolver::BasisStatus row_status(int constraint_index) const override;
|
||||
@@ -146,8 +144,6 @@ class GLPKInterface : public MPSolverInterface {
|
||||
|
||||
// Checks whether a feasible solution exists.
|
||||
bool CheckSolutionExists() const override;
|
||||
// Checks whether information on the best objective bound exists.
|
||||
bool CheckBestObjectiveBoundExists() const override;
|
||||
|
||||
// ----- Misc -----
|
||||
// Query problem type.
|
||||
@@ -567,10 +563,12 @@ MPSolver::ResultStatus GLPKInterface::Solve(const MPSolverParameters& param) {
|
||||
// Get the results.
|
||||
if (mip_) {
|
||||
objective_value_ = glp_mip_obj_val(lp_);
|
||||
best_objective_bound_ = mip_callback_info_->best_objective_bound_;
|
||||
} else {
|
||||
objective_value_ = glp_get_obj_val(lp_);
|
||||
}
|
||||
VLOG(1) << "objective=" << objective_value_;
|
||||
VLOG(1) << "objective=" << objective_value_
|
||||
<< ", bound=" << best_objective_bound_;
|
||||
for (int i = 0; i < solver_->variables_.size(); ++i) {
|
||||
MPVariable* const var = solver_->variables_[i];
|
||||
double val;
|
||||
@@ -693,23 +691,6 @@ int64 GLPKInterface::nodes() const {
|
||||
}
|
||||
}
|
||||
|
||||
double GLPKInterface::best_objective_bound() const {
|
||||
if (mip_) {
|
||||
if (!CheckSolutionIsSynchronized() || !CheckBestObjectiveBoundExists()) {
|
||||
return trivial_worst_objective_bound();
|
||||
}
|
||||
if (solver_->variables_.empty() && solver_->constraints_.empty()) {
|
||||
// Special case for empty model.
|
||||
return solver_->Objective().offset();
|
||||
} else {
|
||||
return mip_callback_info_->best_objective_bound_;
|
||||
}
|
||||
} else {
|
||||
LOG(DFATAL) << "Best objective bound only available for discrete problems";
|
||||
return trivial_worst_objective_bound();
|
||||
}
|
||||
}
|
||||
|
||||
MPSolver::BasisStatus GLPKInterface::row_status(int constraint_index) const {
|
||||
DCHECK_GE(constraint_index, 0);
|
||||
DCHECK_LT(constraint_index, last_constraint_index_);
|
||||
@@ -737,18 +718,6 @@ bool GLPKInterface::CheckSolutionExists() const {
|
||||
}
|
||||
}
|
||||
|
||||
bool GLPKInterface::CheckBestObjectiveBoundExists() const {
|
||||
if (result_status_ == MPSolver::ABNORMAL) {
|
||||
LOG(WARNING) << "Ignoring ABNORMAL status from GLPK: This status may or may"
|
||||
<< " not indicate that information is available on the best"
|
||||
<< " objective bound.";
|
||||
return true;
|
||||
} else {
|
||||
// Call default implementation
|
||||
return MPSolverInterface::CheckBestObjectiveBoundExists();
|
||||
}
|
||||
}
|
||||
|
||||
double GLPKInterface::ComputeExactConditionNumber() const {
|
||||
if (!IsContinuous()) {
|
||||
// TODO(user): support MIP.
|
||||
|
||||
@@ -114,7 +114,6 @@ class GurobiInterface : public MPSolverInterface {
|
||||
void SetObjectiveOffset(double value) override;
|
||||
// Clears the objective from all its terms.
|
||||
void ClearObjective() override;
|
||||
bool CheckBestObjectiveBoundExists() const override;
|
||||
void BranchingPriorityChangedForVariable(int var_index) override;
|
||||
|
||||
// ------ Query statistics on the solution and the solve ------
|
||||
@@ -122,8 +121,6 @@ class GurobiInterface : public MPSolverInterface {
|
||||
int64 iterations() const override;
|
||||
// Number of branch-and-bound nodes. Only available for discrete problems.
|
||||
int64 nodes() const override;
|
||||
// Best objective bound. Only available for discrete problems.
|
||||
double best_objective_bound() const override;
|
||||
|
||||
// Returns the basis status of a row.
|
||||
MPSolver::BasisStatus row_status(int constraint_index) const override;
|
||||
@@ -788,38 +785,6 @@ int64 GurobiInterface::nodes() const {
|
||||
}
|
||||
}
|
||||
|
||||
bool GurobiInterface::CheckBestObjectiveBoundExists() const {
|
||||
double value;
|
||||
const int error = GRBgetdblattr(model_, GRB_DBL_ATTR_OBJBOUND, &value);
|
||||
return error == 0;
|
||||
}
|
||||
|
||||
// Returns the best objective bound. Only available for discrete problems.
|
||||
double GurobiInterface::best_objective_bound() const {
|
||||
if (mip_) {
|
||||
if (!CheckSolutionIsSynchronized() || !CheckBestObjectiveBoundExists()) {
|
||||
return trivial_worst_objective_bound();
|
||||
}
|
||||
if (solver_->variables_.empty() && solver_->constraints_.empty()) {
|
||||
// Special case for empty model.
|
||||
return solver_->Objective().offset();
|
||||
}
|
||||
double value;
|
||||
const int error = GRBgetdblattr(model_, GRB_DBL_ATTR_OBJBOUND, &value);
|
||||
if (result_status_ == MPSolver::OPTIMAL &&
|
||||
error == GRB_ERROR_DATA_NOT_AVAILABLE) {
|
||||
// Special case for when presolve removes all the variables so the model
|
||||
// becomes empty after the presolve phase.
|
||||
return objective_value_;
|
||||
}
|
||||
CheckedGurobiCall(error);
|
||||
return value;
|
||||
} else {
|
||||
LOG(DFATAL) << "Best objective bound only available for discrete problems.";
|
||||
return trivial_worst_objective_bound();
|
||||
}
|
||||
}
|
||||
|
||||
MPSolver::BasisStatus GurobiInterface::TransformGRBVarBasisStatus(
|
||||
int gurobi_basis_status) const {
|
||||
switch (gurobi_basis_status) {
|
||||
@@ -1272,6 +1237,16 @@ MPSolver::ResultStatus GurobiInterface::Solve(const MPSolverParameters& param) {
|
||||
}
|
||||
}
|
||||
|
||||
if (IsMIP() && (result_status_ != MPSolver::UNBOUNDED &&
|
||||
result_status_ != MPSolver::INFEASIBLE)) {
|
||||
const int error =
|
||||
GRBgetdblattr(model_, GRB_DBL_ATTR_OBJBOUND, &best_objective_bound_);
|
||||
LOG_IF(WARNING, error != 0)
|
||||
<< "Best objective bound is not available, error=" << error
|
||||
<< ", message=" << GRBgeterrormsg(env_);
|
||||
VLOG(1) << "best bound = " << best_objective_bound_;
|
||||
}
|
||||
|
||||
if (solution_count > 0 && (result_status_ == MPSolver::FEASIBLE ||
|
||||
result_status_ == MPSolver::OPTIMAL)) {
|
||||
current_solution_index_ = 0;
|
||||
|
||||
@@ -1037,6 +1037,7 @@ absl::Status MPSolver::LoadSolutionFromProto(const MPSolutionResponse& response,
|
||||
"'"));
|
||||
}
|
||||
}
|
||||
// TODO(user): Load the reduced costs too, if available.
|
||||
for (int i = 0; i < response.variable_value_size(); ++i) {
|
||||
variables_[i]->set_solution_value(response.variable_value(i));
|
||||
}
|
||||
@@ -1045,6 +1046,9 @@ absl::Status MPSolver::LoadSolutionFromProto(const MPSolutionResponse& response,
|
||||
if (response.has_objective_value()) {
|
||||
interface_->objective_value_ = response.objective_value();
|
||||
}
|
||||
if (response.has_best_objective_bound()) {
|
||||
interface_->best_objective_bound_ = response.best_objective_bound();
|
||||
}
|
||||
// Mark the status as SOLUTION_SYNCHRONIZED, so that users may inspect the
|
||||
// solution normally.
|
||||
interface_->sync_status_ = MPSolverInterface::SOLUTION_SYNCHRONIZED;
|
||||
@@ -1611,6 +1615,8 @@ bool MPSolverResponseStatusIsRpcError(MPSolverResponseStatus status) {
|
||||
|
||||
const int MPSolverInterface::kDummyVariableIndex = 0;
|
||||
|
||||
// TODO(user): Initialize objective value and bound to +/- inf (depending on
|
||||
// optimization direction).
|
||||
MPSolverInterface::MPSolverInterface(MPSolver* const solver)
|
||||
: solver_(solver),
|
||||
sync_status_(MODEL_SYNCHRONIZED),
|
||||
@@ -1619,6 +1625,7 @@ MPSolverInterface::MPSolverInterface(MPSolver* const solver)
|
||||
last_constraint_index_(0),
|
||||
last_variable_index_(0),
|
||||
objective_value_(0.0),
|
||||
best_objective_bound_(0.0),
|
||||
quiet_(true) {}
|
||||
|
||||
MPSolverInterface::~MPSolverInterface() {}
|
||||
@@ -1685,28 +1692,29 @@ bool MPSolverInterface::CheckSolutionExists() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Default version that can be overwritten by a solver-specific
|
||||
// version to accommodate for the quirks of each solver.
|
||||
bool MPSolverInterface::CheckBestObjectiveBoundExists() const {
|
||||
if (result_status_ != MPSolver::OPTIMAL &&
|
||||
result_status_ != MPSolver::FEASIBLE) {
|
||||
LOG(DFATAL) << "No information is available for the best objective bound."
|
||||
<< " MPSolverInterface::result_status_ = " << result_status_;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
double MPSolverInterface::trivial_worst_objective_bound() const {
|
||||
return maximize_ ? -std::numeric_limits<double>::infinity()
|
||||
: std::numeric_limits<double>::infinity();
|
||||
}
|
||||
|
||||
double MPSolverInterface::objective_value() const {
|
||||
if (!CheckSolutionIsSynchronizedAndExists()) return 0;
|
||||
return objective_value_;
|
||||
}
|
||||
|
||||
double MPSolverInterface::best_objective_bound() const {
|
||||
const double trivial_worst_bound =
|
||||
maximize_ ? -std::numeric_limits<double>::infinity()
|
||||
: std::numeric_limits<double>::infinity();
|
||||
if (!IsMIP()) {
|
||||
LOG(DFATAL) << "Best objective bound only available for discrete problems.";
|
||||
return trivial_worst_bound;
|
||||
}
|
||||
if (!CheckSolutionIsSynchronized()) {
|
||||
return trivial_worst_bound;
|
||||
}
|
||||
// Special case for empty model.
|
||||
if (solver_->variables_.empty() && solver_->constraints_.empty()) {
|
||||
return solver_->Objective().offset();
|
||||
}
|
||||
return best_objective_bound_;
|
||||
}
|
||||
|
||||
void MPSolverInterface::InvalidateSolutionSynchronization() {
|
||||
if (sync_status_ == SOLUTION_SYNCHRONIZED) {
|
||||
sync_status_ = MODEL_SYNCHRONIZED;
|
||||
|
||||
@@ -1611,11 +1611,8 @@ class MPSolverInterface {
|
||||
// otherwise it crashes, or returns kUnknownNumberOfNodes in NDEBUG mode.
|
||||
virtual int64 nodes() const = 0;
|
||||
// Returns the best objective bound. The problem must be discrete, otherwise
|
||||
// it crashes, or returns trivial_worst_objective_bound() in NDEBUG mode.
|
||||
virtual double best_objective_bound() const = 0;
|
||||
// A trivial objective bound: the worst possible value of the objective,
|
||||
// which will be +infinity if minimizing and -infinity if maximing.
|
||||
double trivial_worst_objective_bound() const;
|
||||
// it crashes, or returns trivial bound (+/- inf) in NDEBUG mode.
|
||||
double best_objective_bound() const;
|
||||
// Returns the objective value of the best solution found so far.
|
||||
double objective_value() const;
|
||||
|
||||
@@ -1635,9 +1632,6 @@ class MPSolverInterface {
|
||||
bool CheckSolutionIsSynchronizedAndExists() const {
|
||||
return CheckSolutionIsSynchronized() && CheckSolutionExists();
|
||||
}
|
||||
// Checks whether information on the best objective bound exists. The behavior
|
||||
// is similar to CheckSolutionIsSynchronized() above.
|
||||
virtual bool CheckBestObjectiveBoundExists() const;
|
||||
|
||||
// ----- Misc -----
|
||||
// Queries problem type. For simplicity, the distinction between
|
||||
@@ -1732,6 +1726,9 @@ class MPSolverInterface {
|
||||
// The value of the objective function.
|
||||
double objective_value_;
|
||||
|
||||
// The value of the best objective bound. Used only for MIP solvers.
|
||||
double best_objective_bound_;
|
||||
|
||||
// Boolean indicator for the verbosity of the solver output.
|
||||
bool quiet_;
|
||||
|
||||
|
||||
@@ -69,7 +69,6 @@ class SatInterface : public MPSolverInterface {
|
||||
// ------ Query statistics on the solution and the solve ------
|
||||
int64 iterations() const override;
|
||||
int64 nodes() const override;
|
||||
double best_objective_bound() const override;
|
||||
MPSolver::BasisStatus row_status(int constraint_index) const override;
|
||||
MPSolver::BasisStatus column_status(int variable_index) const override;
|
||||
|
||||
@@ -102,7 +101,6 @@ class SatInterface : public MPSolverInterface {
|
||||
std::atomic<bool> interrupt_solve_;
|
||||
sat::SatParameters parameters_;
|
||||
int num_threads_ = 8;
|
||||
double best_objective_bound_ = 0.0;
|
||||
};
|
||||
|
||||
SatInterface::SatInterface(MPSolver* const solver)
|
||||
@@ -246,13 +244,6 @@ int64 SatInterface::iterations() const {
|
||||
|
||||
int64 SatInterface::nodes() const { return 0; }
|
||||
|
||||
double SatInterface::best_objective_bound() const {
|
||||
if (!CheckSolutionIsSynchronized() || !CheckBestObjectiveBoundExists()) {
|
||||
return trivial_worst_objective_bound();
|
||||
}
|
||||
return best_objective_bound_;
|
||||
}
|
||||
|
||||
MPSolver::BasisStatus SatInterface::row_status(int constraint_index) const {
|
||||
return MPSolver::BasisStatus::FREE; // FIXME
|
||||
}
|
||||
|
||||
@@ -84,7 +84,6 @@ class SCIPInterface : public MPSolverInterface {
|
||||
|
||||
int64 iterations() const override;
|
||||
int64 nodes() const override;
|
||||
double best_objective_bound() const override;
|
||||
MPSolver::BasisStatus row_status(int constraint_index) const override {
|
||||
LOG(DFATAL) << "Basis status only available for continuous problems";
|
||||
return MPSolver::FREE;
|
||||
@@ -656,6 +655,7 @@ MPSolver::ResultStatus SCIPInterface::Solve(const MPSolverParameters& param) {
|
||||
sync_status_ = SOLUTION_SYNCHRONIZED;
|
||||
result_status_ = MPSolver::OPTIMAL;
|
||||
objective_value_ = solver_->Objective().offset();
|
||||
best_objective_bound_ = solver_->Objective().offset();
|
||||
return result_status_;
|
||||
}
|
||||
|
||||
@@ -797,7 +797,9 @@ MPSolver::ResultStatus SCIPInterface::Solve(const MPSolverParameters& param) {
|
||||
|
||||
void SCIPInterface::SetSolution(SCIP_SOL* solution) {
|
||||
objective_value_ = SCIPgetSolOrigObj(scip_, solution);
|
||||
VLOG(1) << "objective=" << objective_value_;
|
||||
best_objective_bound_ = SCIPgetDualbound(scip_);
|
||||
VLOG(1) << "objective=" << objective_value_
|
||||
<< ", bound=" << best_objective_bound_;
|
||||
for (int i = 0; i < solver_->variables_.size(); ++i) {
|
||||
MPVariable* const var = solver_->variables_[i];
|
||||
const int var_index = var->index();
|
||||
@@ -860,19 +862,6 @@ int64 SCIPInterface::nodes() const {
|
||||
return SCIPgetNTotalNodes(scip_);
|
||||
}
|
||||
|
||||
double SCIPInterface::best_objective_bound() const {
|
||||
// NOTE(user): Same story as iterations(): it's OK to crash here.
|
||||
if (!CheckSolutionIsSynchronized() || !CheckBestObjectiveBoundExists()) {
|
||||
return trivial_worst_objective_bound();
|
||||
}
|
||||
if (solver_->variables_.empty() && solver_->constraints_.empty()) {
|
||||
// Special case for empty model.
|
||||
return solver_->Objective().offset();
|
||||
} else {
|
||||
return SCIPgetDualbound(scip_);
|
||||
}
|
||||
}
|
||||
|
||||
void SCIPInterface::SetParameters(const MPSolverParameters& param) {
|
||||
SetCommonParameters(param);
|
||||
SetMIPParameters(param);
|
||||
|
||||
@@ -2723,9 +2723,11 @@ void SolveCpModelParallel(const CpModelProto& model_proto,
|
||||
NeighborhoodGeneratorHelper* helper = unique_helper.get();
|
||||
subsolvers.push_back(std::move(unique_helper));
|
||||
|
||||
const int num_lns_strategies = parameters.diversify_lns_params() ? 6 : 1;
|
||||
const std::vector<SatParameters>& lns_params =
|
||||
GetDiverseSetOfParameters(parameters, model_proto, num_lns_strategies);
|
||||
std::vector<SatParameters> lns_params = {parameters};
|
||||
if (parameters.diversify_lns_params()) {
|
||||
std::vector<SatParameters> lns_params =
|
||||
GetDiverseSetOfParameters(parameters, model_proto, 6);
|
||||
}
|
||||
for (const SatParameters& local_params : lns_params) {
|
||||
// Only register following LNS SubSolver if there is an objective.
|
||||
if (model_proto.has_objective()) {
|
||||
@@ -2865,15 +2867,14 @@ CpSolverResponse SolveCpModel(const CpModelProto& model_proto, Model* model) {
|
||||
CHECK_OK(file::SetTextProto(file, model_proto, file::Defaults()));
|
||||
}
|
||||
|
||||
absl::Cleanup<std::function<void()>> dump_response_cleanup;
|
||||
if (absl::GetFlag(FLAGS_cp_model_dump_response)) {
|
||||
dump_response_cleanup = absl::MakeCleanup([&final_response] {
|
||||
auto dump_response_cleanup = absl::MakeCleanup([&final_response] {
|
||||
if (absl::GetFlag(FLAGS_cp_model_dump_response)) {
|
||||
const std::string file = absl::StrCat(
|
||||
absl::GetFlag(FLAGS_cp_model_dump_prefix), "response.pbtxt");
|
||||
LOG(INFO) << "Dumping response proto to '" << file << "'.";
|
||||
CHECK_OK(file::SetTextProto(file, final_response, file::Defaults()));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Override parameters?
|
||||
if (!absl::GetFlag(FLAGS_cp_model_params).empty()) {
|
||||
|
||||
@@ -1635,7 +1635,7 @@ void LinearProgrammingConstraint::PreventOverflow(LinearConstraint* constraint,
|
||||
sum_min += std::min(0.0, std::min(prod1, prod2));
|
||||
sum_max += std::max(0.0, std::max(prod1, prod2));
|
||||
}
|
||||
const double max_value = std::max(sum_max, -sum_min);
|
||||
const double max_value = std::max({sum_max, -sum_min, sum_max - sum_min});
|
||||
|
||||
const IntegerValue divisor(std::ceil(std::ldexp(max_value, -max_pow)));
|
||||
if (divisor <= 1) return;
|
||||
|
||||
@@ -282,12 +282,12 @@ ABSL_MUST_USE_RESULT bool PresolveContext::SetLiteralToTrue(int lit) {
|
||||
return SetLiteralToFalse(NegatedRef(lit));
|
||||
}
|
||||
|
||||
void PresolveContext::UpdateRuleStats(const std::string& name) {
|
||||
void PresolveContext::UpdateRuleStats(const std::string& name, int num_times) {
|
||||
if (enable_stats) {
|
||||
VLOG(1) << num_presolve_operations << " : " << name;
|
||||
stats_by_rule_name[name]++;
|
||||
stats_by_rule_name[name] += num_times;
|
||||
}
|
||||
num_presolve_operations++;
|
||||
num_presolve_operations += num_times;
|
||||
}
|
||||
|
||||
void PresolveContext::UpdateLinear1Usage(const ConstraintProto& ct, int c) {
|
||||
|
||||
@@ -151,7 +151,7 @@ class PresolveContext {
|
||||
|
||||
// Stores a description of a rule that was just applied to have a summary of
|
||||
// what the presolve did at the end.
|
||||
void UpdateRuleStats(const std::string& name);
|
||||
void UpdateRuleStats(const std::string& name, int num_times = 1);
|
||||
|
||||
// Updates the constraints <-> variables graph. This needs to be called each
|
||||
// time a constraint is modified.
|
||||
|
||||
@@ -1115,7 +1115,7 @@ bool ExploitDominanceRelations(const VarDomination& var_domination,
|
||||
if (num_added > 0) {
|
||||
VLOG(1) << "Added " << num_added << " domination implications.";
|
||||
context->UpdateNewConstraintsVariableUsage();
|
||||
context->UpdateRuleStats("domination: added implications.");
|
||||
context->UpdateRuleStats("domination: added implications", num_added);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user