From a2dc7e9a8efe6d4606a774e21279db36be2e68f0 Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Mon, 2 Nov 2020 18:48:31 +0100 Subject: [PATCH] big sync with internal code; mostly code reformating; a few fixes for CP-SAT; more work on bandit based concatenators for the routing library --- .../constraint_solver/routing_neighborhoods.h | 8 ++-- ortools/flatzinc/checker.cc | 7 +++ ortools/flatzinc/fz.cc | 1 + ortools/flatzinc/mznlib/fzn_diffn.mzn | 1 - ortools/flatzinc/mznlib/fzn_network_flow.mzn | 2 +- ortools/flatzinc/parser_main.cc | 1 + ortools/graph/christofides.h | 14 +++--- ortools/graph/util.h | 2 +- ortools/linear_solver/bop_interface.cc | 9 ---- ortools/linear_solver/cbc_interface.cc | 11 ----- ortools/linear_solver/clp_interface.cc | 7 --- ortools/linear_solver/glop_interface.cc | 6 --- ortools/linear_solver/glpk_interface.cc | 37 ++------------- ortools/linear_solver/gurobi_interface.cc | 45 +++++-------------- ortools/linear_solver/linear_solver.cc | 42 ++++++++++------- ortools/linear_solver/linear_solver.h | 13 +++--- ortools/linear_solver/sat_interface.cc | 9 ---- ortools/linear_solver/scip_interface.cc | 19 ++------ ortools/sat/cp_model_solver.cc | 17 +++---- ortools/sat/linear_programming_constraint.cc | 2 +- ortools/sat/presolve_context.cc | 6 +-- ortools/sat/presolve_context.h | 2 +- ortools/sat/var_domination.cc | 2 +- 23 files changed, 85 insertions(+), 178 deletions(-) diff --git a/ortools/constraint_solver/routing_neighborhoods.h b/ortools/constraint_solver/routing_neighborhoods.h index 56fa87560a..6518757932 100644 --- a/ortools/constraint_solver/routing_neighborhoods.h +++ b/ortools/constraint_solver/routing_neighborhoods.h @@ -478,7 +478,7 @@ class FilteredHeuristicExpensiveChainLNSOperator int last_route_; const int num_arcs_to_consider_; - std::vector > most_expensive_arc_starts_and_ranks_; + std::vector> 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 @@ -531,14 +531,14 @@ class FilteredHeuristicCloseNodesLNSOperator std::vector GetActiveSiblings(int64 node) const; - const std::vector, std::vector > >& + const std::vector, std::vector>>& pickup_delivery_pairs_; int current_node_; int last_node_; bool just_started_; - std::vector > close_nodes_; + std::vector> close_nodes_; /// Keep track of changes when making a neighbor. std::vector new_nexts_; SparseBitset<> changed_nexts_; @@ -578,7 +578,7 @@ class RelocateExpensiveChain : public PathOperator { int num_arcs_to_consider_; int current_path_; - std::vector > most_expensive_arc_starts_and_ranks_; + std::vector> 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 diff --git a/ortools/flatzinc/checker.cc b/ortools/flatzinc/checker.cc index 4e429827e3..9d0d21e5f1 100644 --- a/ortools/flatzinc/checker.cc +++ b/ortools/flatzinc/checker.cc @@ -1115,6 +1115,13 @@ using CallMap = absl::flat_hash_map< std::string, std::function)>>; +// 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; diff --git a/ortools/flatzinc/fz.cc b/ortools/flatzinc/fz.cc index d512645fef..41465f26c8 100644 --- a/ortools/flatzinc/fz.cc +++ b/ortools/flatzinc/fz.cc @@ -109,6 +109,7 @@ std::vector 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 residual_flags = absl::ParseCommandLine(*argc, *argv); diff --git a/ortools/flatzinc/mznlib/fzn_diffn.mzn b/ortools/flatzinc/mznlib/fzn_diffn.mzn index f283006499..f0d3d27b0d 100644 --- a/ortools/flatzinc/mznlib/fzn_diffn.mzn +++ b/ortools/flatzinc/mznlib/fzn_diffn.mzn @@ -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); - diff --git a/ortools/flatzinc/mznlib/fzn_network_flow.mzn b/ortools/flatzinc/mznlib/fzn_network_flow.mzn index e793358c9c..5fda68a8d2 100644 --- a/ortools/flatzinc/mznlib/fzn_network_flow.mzn +++ b/ortools/flatzinc/mznlib/fzn_network_flow.mzn @@ -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); diff --git a/ortools/flatzinc/parser_main.cc b/ortools/flatzinc/parser_main.cc index 9f7bdbed71..08dc2fcede 100644 --- a/ortools/flatzinc/parser_main.cc +++ b/ortools/flatzinc/parser_main.cc @@ -17,6 +17,7 @@ #include +#include "absl/flags/flag.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/timer.h" #include "ortools/flatzinc/logging.h" diff --git a/ortools/graph/christofides.h b/ortools/graph/christofides.h index 13ff581079..d78b0c9045 100644 --- a/ortools/graph/christofides.h +++ b/ortools/graph/christofides.h @@ -71,12 +71,14 @@ class ChristofidesPathSolver { void Solve(); // Safe addition operator to avoid overflows when possible. - // template - // T SafeAdd(T a, T b) { - // return a + b; - // } - // template <> - int64 SafeAdd(int64 a, int64 b) { return CapAdd(a, b); } + template + 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_; diff --git a/ortools/graph/util.h b/ortools/graph/util.h index dc43e63038..b6a4cf931b 100644 --- a/ortools/graph/util.h +++ b/ortools/graph/util.h @@ -21,9 +21,9 @@ #include #include #include -#include #include +#include "absl/container/flat_hash_map.h" #include "absl/container/inlined_vector.h" #include "ortools/base/hash.h" #include "ortools/base/map_util.h" diff --git a/ortools/linear_solver/bop_interface.cc b/ortools/linear_solver/bop_interface.cc index 57175b7f89..4e899b9cbe 100644 --- a/ortools/linear_solver/bop_interface.cc +++ b/ortools/linear_solver/bop_interface.cc @@ -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 column_status_; std::vector row_status_; bop::BopParameters parameters_; - double best_objective_bound_; std::atomic 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]; } diff --git a/ortools/linear_solver/cbc_interface.cc b/ortools/linear_solver/cbc_interface.cc index b796fc01d6..8df43e3111 100644 --- a/ortools/linear_solver/cbc_interface.cc +++ b/ortools/linear_solver/cbc_interface.cc @@ -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::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 diff --git a/ortools/linear_solver/clp_interface.cc b/ortools/linear_solver/clp_interface.cc index 6535bd12b6..ed52d2e770 100644 --- a/ortools/linear_solver/clp_interface.cc +++ b/ortools/linear_solver/clp_interface.cc @@ -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); diff --git a/ortools/linear_solver/glop_interface.cc b/ortools/linear_solver/glop_interface.cc index d9dd01edcd..7530a5c7fb 100644 --- a/ortools/linear_solver/glop_interface.cc +++ b/ortools/linear_solver/glop_interface.cc @@ -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]; } diff --git a/ortools/linear_solver/glpk_interface.cc b/ortools/linear_solver/glpk_interface.cc index 9aa4ebfd04..cf1235630d 100644 --- a/ortools/linear_solver/glpk_interface.cc +++ b/ortools/linear_solver/glpk_interface.cc @@ -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. diff --git a/ortools/linear_solver/gurobi_interface.cc b/ortools/linear_solver/gurobi_interface.cc index 2bff259887..85e66559ff 100644 --- a/ortools/linear_solver/gurobi_interface.cc +++ b/ortools/linear_solver/gurobi_interface.cc @@ -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; diff --git a/ortools/linear_solver/linear_solver.cc b/ortools/linear_solver/linear_solver.cc index 4906722d8c..bca54b191b 100644 --- a/ortools/linear_solver/linear_solver.cc +++ b/ortools/linear_solver/linear_solver.cc @@ -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::infinity() - : std::numeric_limits::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::infinity() + : std::numeric_limits::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; diff --git a/ortools/linear_solver/linear_solver.h b/ortools/linear_solver/linear_solver.h index 53562056a9..7fbd49428d 100644 --- a/ortools/linear_solver/linear_solver.h +++ b/ortools/linear_solver/linear_solver.h @@ -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_; diff --git a/ortools/linear_solver/sat_interface.cc b/ortools/linear_solver/sat_interface.cc index 7c60034fb8..7c8d494d8b 100644 --- a/ortools/linear_solver/sat_interface.cc +++ b/ortools/linear_solver/sat_interface.cc @@ -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 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 } diff --git a/ortools/linear_solver/scip_interface.cc b/ortools/linear_solver/scip_interface.cc index 512c0d6721..8a489007f4 100644 --- a/ortools/linear_solver/scip_interface.cc +++ b/ortools/linear_solver/scip_interface.cc @@ -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); diff --git a/ortools/sat/cp_model_solver.cc b/ortools/sat/cp_model_solver.cc index fea7b9f90e..e38fa9c0b1 100644 --- a/ortools/sat/cp_model_solver.cc +++ b/ortools/sat/cp_model_solver.cc @@ -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& lns_params = - GetDiverseSetOfParameters(parameters, model_proto, num_lns_strategies); + std::vector lns_params = {parameters}; + if (parameters.diversify_lns_params()) { + std::vector 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> 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()) { diff --git a/ortools/sat/linear_programming_constraint.cc b/ortools/sat/linear_programming_constraint.cc index d0aa40f5c4..9f1d8e7628 100644 --- a/ortools/sat/linear_programming_constraint.cc +++ b/ortools/sat/linear_programming_constraint.cc @@ -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; diff --git a/ortools/sat/presolve_context.cc b/ortools/sat/presolve_context.cc index 24b61a3f9b..0f668254f4 100644 --- a/ortools/sat/presolve_context.cc +++ b/ortools/sat/presolve_context.cc @@ -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) { diff --git a/ortools/sat/presolve_context.h b/ortools/sat/presolve_context.h index f087c95346..1e70eca0ee 100644 --- a/ortools/sat/presolve_context.h +++ b/ortools/sat/presolve_context.h @@ -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. diff --git a/ortools/sat/var_domination.cc b/ortools/sat/var_domination.cc index d711437443..b8d8840d8b 100644 --- a/ortools/sat/var_domination.cc +++ b/ortools/sat/var_domination.cc @@ -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;