From 4caaef3c23e12841c940345f9e9b91cc367daebf Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Tue, 28 Aug 2018 11:19:49 +0200 Subject: [PATCH] deep sync with base library renaming; lint checking; Speedup sat; new crash procedure for glop --- ortools/algorithms/dynamic_partition.cc | 2 +- ortools/algorithms/find_graph_symmetries.cc | 5 +- ortools/algorithms/hungarian.cc | 14 +- ortools/algorithms/knapsack_solver.cc | 20 +- ortools/algorithms/knapsack_solver.h | 3 +- ortools/base/mathutil.h | 5 + ortools/bop/bop_fs.cc | 11 +- ortools/bop/bop_lns.cc | 5 +- ortools/bop/bop_ls.cc | 15 +- ortools/bop/bop_ls.h | 6 +- ortools/bop/bop_portfolio.cc | 9 +- ortools/bop/bop_portfolio.h | 4 +- ortools/bop/integral_solver.cc | 16 +- ortools/bop/integral_solver.h | 10 +- .../com/google/ortools/sat/Constraint.java | 18 +- ortools/com/google/ortools/sat/CpModel.java | 265 +++++++++--------- ortools/com/google/ortools/sat/CpSolver.java | 82 +++--- ortools/com/google/ortools/sat/ILiteral.java | 2 +- ortools/com/google/ortools/sat/IntVar.java | 55 ++-- .../com/google/ortools/sat/IntervalVar.java | 40 +-- .../ortools/sat/NotBooleanVariable.java | 12 +- ortools/constraint_solver/sched_expr.cc | 12 +- ortools/flatzinc/constraints.cc | 4 +- ortools/flatzinc/constraints.h | 2 +- ortools/flatzinc/cp_model_fz_solver.cc | 4 +- ortools/flatzinc/flatzinc_constraints.cc | 14 +- ortools/flatzinc/model.cc | 52 ++-- ortools/flatzinc/model.h | 16 +- ortools/flatzinc/parser.tab.cc | 2 +- ortools/flatzinc/parser.yy | 2 +- ortools/flatzinc/parser_util.cc | 3 +- ortools/flatzinc/parser_util.h | 2 - ortools/flatzinc/presolve.cc | 36 +-- ortools/flatzinc/reporting.h | 6 +- ortools/flatzinc/sat_constraint.cc | 2 +- ortools/flatzinc/solver.cc | 28 +- ortools/flatzinc/solver_util.cc | 6 +- ortools/glop/initial_basis.cc | 163 ++++++++++- ortools/glop/initial_basis.h | 16 +- ortools/glop/lp_solver.cc | 18 +- ortools/glop/lp_solver.h | 2 +- ortools/glop/markowitz.cc | 5 +- ortools/glop/parameters.proto | 5 + ortools/glop/preprocessor.cc | 40 ++- ortools/glop/preprocessor.h | 18 ++ ortools/glop/revised_simplex.cc | 48 +++- ortools/glop/revised_simplex.h | 5 +- ortools/glop/variables_info.h | 6 +- ortools/linear_solver/cbc_interface.cc | 4 +- ortools/linear_solver/clp_interface.cc | 11 +- ortools/linear_solver/cplex_interface.cc | 16 +- ortools/linear_solver/glop_interface.cc | 2 +- ortools/linear_solver/glpk_interface.cc | 13 +- ortools/linear_solver/gurobi_interface.cc | 15 +- ortools/linear_solver/linear_solver.cc | 22 +- ortools/linear_solver/linear_solver.h | 28 +- ortools/linear_solver/model_exporter.cc | 70 ++--- ortools/linear_solver/model_exporter.h | 8 +- ortools/linear_solver/scip_interface.cc | 10 +- ortools/lp_data/lp_data.cc | 20 +- ortools/lp_data/lp_data.h | 2 +- ortools/lp_data/lp_decomposer.cc | 1 - ortools/lp_data/lp_print_utils.h | 8 +- ortools/lp_data/lp_utils.cc | 4 +- ortools/lp_data/lp_utils.h | 4 +- ortools/lp_data/matrix_scaler.cc | 2 +- ortools/lp_data/model_reader.h | 1 - ortools/lp_data/mps_reader.cc | 6 +- ortools/lp_data/mps_reader.h | 4 +- ortools/lp_data/proto_utils.h | 1 - ortools/lp_data/sparse.cc | 2 +- ortools/lp_data/sparse.h | 7 +- ortools/lp_data/sparse_vector.h | 7 +- ortools/sat/boolean_problem.cc | 36 +-- ortools/sat/clause.cc | 8 +- ortools/sat/cp_model.proto | 91 ++++-- ortools/sat/cp_model_objective.cc | 23 +- ortools/sat/cp_model_presolve.cc | 30 +- ortools/sat/cp_model_solver.cc | 20 +- ortools/sat/cumulative.cc | 23 +- ortools/sat/disjunctive.cc | 165 ++++++++--- ortools/sat/disjunctive.h | 17 +- ortools/sat/doc/boolean_logic.md | 18 +- ortools/sat/doc/channeling.md | 42 +-- ortools/sat/doc/index.md | 4 +- ortools/sat/doc/integer_arithmetic.md | 4 + ortools/sat/doc/scheduling.md | 55 ++-- ortools/sat/doc/solver.md | 138 +++++---- ortools/sat/drat_writer.cc | 2 +- ortools/sat/integer.cc | 20 +- ortools/sat/integer.h | 10 +- ortools/sat/integer_expr.h | 4 +- ortools/sat/optimization.cc | 16 +- ortools/sat/precedences.cc | 42 ++- ortools/sat/precedences.h | 34 +-- ortools/sat/python/cp_model.py | 4 +- ortools/sat/restart.cc | 19 +- ...ingProblem.java => BinPackingProblem.java} | 8 +- ortools/sat/samples/ChannelingSample.java | 36 +-- ortools/sat/samples/IntervalSample.java | 2 +- ortools/sat/samples/LiteralSample.java | 2 +- ortools/sat/samples/NoOverlapSample.java | 8 +- .../sat/samples/OptionalIntervalSample.java | 2 +- ortools/sat/samples/RabbitsAndPheasants.java | 4 + ortools/sat/samples/RankingSample.java | 43 +-- ortools/sat/samples/ReifiedSample.java | 16 +- ortools/sat/samples/SimpleSolve.java | 9 +- ortools/sat/samples/SolveAllSolutions.java | 57 ++-- .../SolveWithIntermediateSolutions.java | 61 ++-- ortools/sat/samples/SolveWithTimeLimit.java | 8 +- ortools/sat/samples/channeling_sample.cc | 4 +- ortools/sat/sat_base.h | 8 +- ortools/sat/sat_solver.cc | 21 +- ortools/sat/sat_solver.h | 2 +- ortools/sat/simplification.cc | 5 +- ortools/sat/simplification.h | 4 +- ortools/sat/symmetry.h | 2 +- ortools/sat/theta_tree.h | 2 +- ortools/util/fp_utils.h | 6 +- ortools/util/functions_swig_test_helpers.h | 1 + ortools/util/graph_export.cc | 12 +- ortools/util/monoid_operation_tree.h | 7 +- ortools/util/piecewise_linear_function.h | 4 +- ortools/util/sorted_interval_list.cc | 4 +- ortools/util/stats.cc | 20 +- ortools/util/stats.h | 4 +- ortools/util/time_limit.h | 23 +- ortools/util/tuple_set.h | 4 +- ortools/util/xml_helper.cc | 6 +- 129 files changed, 1584 insertions(+), 1020 deletions(-) rename ortools/sat/samples/{BinpackingProblem.java => BinPackingProblem.java} (91%) diff --git a/ortools/algorithms/dynamic_partition.cc b/ortools/algorithms/dynamic_partition.cc index 83a2e8f0c2..1bd7b5f7b8 100644 --- a/ortools/algorithms/dynamic_partition.cc +++ b/ortools/algorithms/dynamic_partition.cc @@ -183,7 +183,7 @@ void DynamicPartition::UndoRefineUntilNumPartsEqual(int original_num_parts) { std::string DynamicPartition::DebugString(DebugStringSorting sorting) const { if (sorting != SORT_LEXICOGRAPHICALLY && sorting != SORT_BY_PART) { - return StringPrintf("Unsupported sorting: %d", sorting); + return absl::StrFormat("Unsupported sorting: %d", sorting); } std::vector> parts; for (int i = 0; i < NumParts(); ++i) { diff --git a/ortools/algorithms/find_graph_symmetries.cc b/ortools/algorithms/find_graph_symmetries.cc index 4a1b590242..0ccb7cba24 100644 --- a/ortools/algorithms/find_graph_symmetries.cc +++ b/ortools/algorithms/find_graph_symmetries.cc @@ -24,6 +24,7 @@ #include "ortools/base/canonical_errors.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/join.h" +#include "ortools/base/memory.h" #include "ortools/base/stringprintf.h" #include "ortools/base/time_support.h" #include "ortools/graph/iterators.h" @@ -355,7 +356,7 @@ util::Status GraphSymmetryFinder::FindSymmetries( std::vector>* generators, std::vector* factorized_automorphism_group_size) { // Initialization. - time_limit_.reset(new TimeLimit(time_limit_seconds)); + time_limit_ = absl::make_unique(time_limit_seconds); IF_STATS_ENABLED(stats_.initialization_time.StartTimer()); generators->clear(); factorized_automorphism_group_size->clear(); @@ -1011,7 +1012,7 @@ bool GraphSymmetryFinder::ConfirmFullMatchOrFindNextMappingDecision( } std::string GraphSymmetryFinder::SearchState::DebugString() const { - return StringPrintf( + return absl::StrFormat( "SearchState{ base_node=%d, first_image_node=%d," " remaining_pruned_image_nodes=[%s]," " num_parts_before_trying_to_map_base_node=%d }", diff --git a/ortools/algorithms/hungarian.cc b/ortools/algorithms/hungarian.cc index 8c819c1598..5e07927250 100644 --- a/ortools/algorithms/hungarian.cc +++ b/ortools/algorithms/hungarian.cc @@ -39,23 +39,21 @@ class HungarianOptimizer { // Find an assignment which maximizes the total cost. // Returns the assignment in the two vectors passed as argument. - // agent[i] is assigned to task[i]. - void Maximize(std::vector* agent, std::vector* task); + // preimage[i] is assigned to image[i]. + void Maximize(std::vector* preimage, std::vector* image); - // Find an assignment which minimizes the total cost. - // Returns the assignment in the two vectors passed as argument. - // agent[i] is assigned to task[i]. - void Minimize(std::vector* agent, std::vector* task); + // Like Maximize(), but minimizing the cost instead. + void Minimize(std::vector* preimage, std::vector* image); private: typedef void (HungarianOptimizer::*Step)(); typedef enum { NONE, PRIME, STAR } Mark; - // Convert the final cost matrix into a set of assignments of agents -> tasks. + // Convert the final cost matrix into a set of assignments of preimage->image. // Returns the assignment in the two vectors passed as argument, the same as // Minimize and Maximize - void FindAssignments(std::vector* agent, std::vector* task); + void FindAssignments(std::vector* preimage, std::vector* image); // Is the cell (row, col) starred? bool IsStarred(int row, int col) const { return marks_[row][col] == STAR; } diff --git a/ortools/algorithms/knapsack_solver.cc b/ortools/algorithms/knapsack_solver.cc index 2b0c370576..41c111e74c 100644 --- a/ortools/algorithms/knapsack_solver.cc +++ b/ortools/algorithms/knapsack_solver.cc @@ -18,6 +18,7 @@ #include #include +#include "ortools/base/memory.h" #include "ortools/base/stl_util.h" #include "ortools/linear_solver/linear_solver.h" #include "ortools/util/bitset.h" @@ -1128,27 +1129,28 @@ KnapsackSolver::KnapsackSolver(SolverType solver_type, time_limit_seconds_(std::numeric_limits::infinity()) { switch (solver_type) { case KNAPSACK_BRUTE_FORCE_SOLVER: - solver_.reset(new KnapsackBruteForceSolver(solver_name)); + solver_ = absl::make_unique(solver_name); break; case KNAPSACK_64ITEMS_SOLVER: - solver_.reset(new Knapsack64ItemsSolver(solver_name)); + solver_ = absl::make_unique(solver_name); break; case KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER: - solver_.reset(new KnapsackDynamicProgrammingSolver(solver_name)); + solver_ = + absl::make_unique(solver_name); break; case KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER: - solver_.reset(new KnapsackGenericSolver(solver_name)); + solver_ = absl::make_unique(solver_name); break; #if defined(USE_CBC) case KNAPSACK_MULTIDIMENSION_CBC_MIP_SOLVER: - solver_.reset(new KnapsackMIPSolver( - MPSolver::CBC_MIXED_INTEGER_PROGRAMMING, solver_name)); + solver_ = absl::make_unique( + MPSolver::CBC_MIXED_INTEGER_PROGRAMMING, solver_name); break; #endif // USE_CBC #if defined(USE_SCIP) case KNAPSACK_MULTIDIMENSION_SCIP_MIP_SOLVER: - solver_.reset(new KnapsackMIPSolver( - MPSolver::SCIP_MIXED_INTEGER_PROGRAMMING, solver_name)); + solver_ = absl::make_unique( + MPSolver::SCIP_MIXED_INTEGER_PROGRAMMING, solver_name); break; #endif // USE_SCIP default: @@ -1161,7 +1163,7 @@ KnapsackSolver::~KnapsackSolver() {} void KnapsackSolver::Init(const std::vector& profits, const std::vector>& weights, const std::vector& capacities) { - time_limit_.reset(new TimeLimit(time_limit_seconds_)); + time_limit_ = absl::make_unique(time_limit_seconds_); is_solution_optimal_ = false; additional_profit_ = 0LL; is_problem_solved_ = false; diff --git a/ortools/algorithms/knapsack_solver.h b/ortools/algorithms/knapsack_solver.h index cc9b9e7714..71539e852c 100644 --- a/ortools/algorithms/knapsack_solver.h +++ b/ortools/algorithms/knapsack_solver.h @@ -66,6 +66,7 @@ #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" +#include "ortools/base/memory.h" #include "ortools/util/time_limit.h" namespace operations_research { @@ -149,7 +150,7 @@ class KnapsackSolver { // obtained might not be optimal if the limit is reached. void set_time_limit(double time_limit_seconds) { time_limit_seconds_ = time_limit_seconds; - time_limit_.reset(new TimeLimit(time_limit_seconds_)); + time_limit_ = absl::make_unique(time_limit_seconds_); } private: diff --git a/ortools/base/mathutil.h b/ortools/base/mathutil.h index b67529450c..efd722e8db 100644 --- a/ortools/base/mathutil.h +++ b/ortools/base/mathutil.h @@ -113,6 +113,11 @@ class MathUtil { } return x; } + + template + static T IPow(T base, int exp) { + return pow(base, exp); + } }; } // namespace operations_research diff --git a/ortools/bop/bop_fs.cc b/ortools/bop/bop_fs.cc index 7aa0a88839..3921785a6c 100644 --- a/ortools/bop/bop_fs.cc +++ b/ortools/bop/bop_fs.cc @@ -19,6 +19,7 @@ #include "google/protobuf/text_format.h" #include "ortools/algorithms/sparse_permutation.h" #include "ortools/base/commandlineflags.h" +#include "ortools/base/memory.h" #include "ortools/base/stl_util.h" #include "ortools/base/stringprintf.h" #include "ortools/glop/lp_solver.h" @@ -92,7 +93,7 @@ BopOptimizerBase::Status GuidedSatFirstSolutionGenerator::SynchronizeIfNeeded( // Create the sat_solver if not already done. if (!sat_solver_) { - sat_solver_.reset(new sat::SatSolver()); + sat_solver_ = absl::make_unique(); // Add in symmetries. if (problem_state.GetParameters() @@ -398,10 +399,10 @@ BopOptimizerBase::Status LinearRelaxation::SynchronizeIfNeeded( const ColIndex col_b(clause.b.Variable().value()); lp_model_.SetConstraintName( constraint_index, - StringPrintf((clause.a.IsPositive() ? "%s" : "not(%s)"), + absl::StrFormat((clause.a.IsPositive() ? "%s" : "not(%s)"), lp_model_.GetVariableName(col_a).c_str()) + " or " + - StringPrintf((clause.b.IsPositive() ? "%s" : "not(%s)"), + absl::StrFormat((clause.b.IsPositive() ? "%s" : "not(%s)"), lp_model_.GetVariableName(col_b).c_str())); lp_model_.SetCoefficient(constraint_index, col_a, coefficient_a); lp_model_.SetCoefficient(constraint_index, col_b, coefficient_b); @@ -440,7 +441,7 @@ BopOptimizerBase::Status LinearRelaxation::Optimize( const glop::ProblemStatus lp_status = Solve(false, time_limit); VLOG(1) << " LP: " - << StringPrintf("%.6f", lp_solver_.GetObjectiveValue()) + << absl::StrFormat("%.6f", lp_solver_.GetObjectiveValue()) << " status: " << GetProblemStatusString(lp_status); if (lp_status == glop::ProblemStatus::OPTIMAL || @@ -466,7 +467,7 @@ BopOptimizerBase::Status LinearRelaxation::Optimize( lower_bound = ComputeLowerBoundUsingStrongBranching(learned_info, time_limit); VLOG(1) << " LP: " - << StringPrintf("%.6f", lower_bound) + << absl::StrFormat("%.6f", lower_bound) << " using strong branching."; } diff --git a/ortools/bop/bop_lns.cc b/ortools/bop/bop_lns.cc index 991690f206..0c182ce643 100644 --- a/ortools/bop/bop_lns.cc +++ b/ortools/bop/bop_lns.cc @@ -20,6 +20,7 @@ #include "google/protobuf/text_format.h" #include "ortools/base/cleanup.h" #include "ortools/base/commandlineflags.h" +#include "ortools/base/memory.h" #include "ortools/base/stl_util.h" #include "ortools/base/stringprintf.h" #include "ortools/glop/lp_solver.h" @@ -70,13 +71,13 @@ BopOptimizerBase::Status BopCompleteLNSOptimizer::SynchronizeIfNeeded( state_update_stamp_ = problem_state.update_stamp(); // Load the current problem to the solver. - sat_solver_.reset(new sat::SatSolver()); + sat_solver_ = absl::make_unique(); const BopOptimizerBase::Status status = LoadStateProblemToSatSolver(problem_state, sat_solver_.get()); if (status != BopOptimizerBase::CONTINUE) return status; // Add the constraint that forces the solver to look for a solution - // at a distance <= num_relaxed_vars from the curent one. Note that not all + // at a distance <= num_relaxed_vars from the current one. Note that not all // the terms appear in this constraint. // // TODO(user): if the current solution didn't change, there is no need to diff --git a/ortools/bop/bop_ls.cc b/ortools/bop/bop_ls.cc index f38a1ffff9..6de3f7cee6 100644 --- a/ortools/bop/bop_ls.cc +++ b/ortools/bop/bop_ls.cc @@ -13,6 +13,7 @@ #include "ortools/bop/bop_ls.h" +#include "ortools/base/memory.h" #include "ortools/base/stringprintf.h" #include "ortools/bop/bop_util.h" #include "ortools/sat/boolean_problem.h" @@ -48,9 +49,9 @@ BopOptimizerBase::Status LocalSearchOptimizer::Optimize( learned_info->Clear(); if (assignment_iterator_ == nullptr) { - assignment_iterator_.reset(new LocalSearchAssignmentIterator( + assignment_iterator_ = absl::make_unique( problem_state, max_num_decisions_, - parameters.max_num_broken_constraints_in_ls(), &sat_wrapper_)); + parameters.max_num_broken_constraints_in_ls(), &sat_wrapper_); } if (state_update_stamp_ != problem_state.update_stamp()) { @@ -380,7 +381,7 @@ std::string AssignmentAndConstraintFeasibilityMaintainer::DebugString() const { str += "\nFlipped variables: "; // TODO(user): show the backtrack levels. for (const VariableIndex var : flipped_var_trail_) { - str += StringPrintf(" %d", var.value()); + str += absl::StrFormat(" %d", var.value()); } str += "\nmin curr max\n"; for (ConstraintIndex ct(0); ct < constraint_values_.size(); ++ct) { @@ -818,9 +819,9 @@ double LocalSearchAssignmentIterator::deterministic_time() const { std::string LocalSearchAssignmentIterator::DebugString() const { std::string str = "Search nodes:\n"; for (int i = 0; i < search_nodes_.size(); ++i) { - str += - StringPrintf(" %d: %d %d\n", i, search_nodes_[i].constraint.value(), - search_nodes_[i].term_index.value()); + str += absl::StrFormat(" %d: %d %d\n", i, + search_nodes_[i].constraint.value(), + search_nodes_[i].term_index.value()); } return str; } @@ -940,7 +941,7 @@ bool LocalSearchAssignmentIterator::GoDeeper() { // Add the new decision. // // TODO(user): Store the last explored term index to not start from -1 each - // time. This will be very useful when a backtrack occured due to the SAT + // time. This will be very useful when a backtrack occurred due to the SAT // propagator. Note however that this behavior is already enforced when we use // the transposition table, since we will not explore again the branches // already explored. diff --git a/ortools/bop/bop_ls.h b/ortools/bop/bop_ls.h index dfed4b61d4..e894f6e3c0 100644 --- a/ortools/bop/bop_ls.h +++ b/ortools/bop/bop_ls.h @@ -456,17 +456,17 @@ class OneFlipConstraintRepairer { // Note that if init_term_index == start_term_index, then all the terms will // be explored. Both TermIndex arguments can take values in [-1, constraint // size). - TermIndex NextRepairingTerm(ConstraintIndex constraint, + TermIndex NextRepairingTerm(ConstraintIndex ct_index, TermIndex init_term_index, TermIndex start_term_index) const; // Returns true if the constraint is infeasible and if flipping the variable // at the given index will repair it. - bool RepairIsValid(ConstraintIndex constraint, TermIndex term_index) const; + bool RepairIsValid(ConstraintIndex ct_index, TermIndex term_index) const; // Returns the literal formed by the variable at the given constraint term and // assigned to the opposite value of this variable in the current assignment. - sat::Literal GetFlip(ConstraintIndex constraint, TermIndex term_index) const; + sat::Literal GetFlip(ConstraintIndex ct_index, TermIndex term_index) const; // Local structure to represent the sparse matrix by constraint used for fast // lookups. diff --git a/ortools/bop/bop_portfolio.cc b/ortools/bop/bop_portfolio.cc index 0b0e265e15..06bfd6b19d 100644 --- a/ortools/bop/bop_portfolio.cc +++ b/ortools/bop/bop_portfolio.cc @@ -13,6 +13,7 @@ #include "ortools/bop/bop_portfolio.h" +#include "ortools/base/memory.h" #include "ortools/base/stl_util.h" #include "ortools/base/stringprintf.h" #include "ortools/bop/bop_fs.h" @@ -211,8 +212,8 @@ void PortfolioOptimizer::AddOptimizer( break; case BopOptimizerMethod::LOCAL_SEARCH: { for (int i = 1; i <= parameters.max_num_decisions_in_ls(); ++i) { - optimizers_.push_back(new LocalSearchOptimizer(StringPrintf("LS_%d", i), - i, &sat_propagator_)); + optimizers_.push_back(new LocalSearchOptimizer( + absl::StrFormat("LS_%d", i), i, &sat_propagator_)); } } break; case BopOptimizerMethod::RANDOM_FIRST_SOLUTION: @@ -296,7 +297,7 @@ void PortfolioOptimizer::AddOptimizer( void PortfolioOptimizer::CreateOptimizers( const LinearBooleanProblem& problem, const BopParameters& parameters, const BopSolverOptimizerSet& optimizer_set) { - random_.reset(new MTRandom(parameters.random_seed())); + random_ = absl::make_unique(parameters.random_seed()); if (parameters.use_symmetry()) { VLOG(1) << "Finding symmetries of the problem."; @@ -319,7 +320,7 @@ void PortfolioOptimizer::CreateOptimizers( AddOptimizer(problem, parameters, optimizer_method); } - selector_.reset(new OptimizerSelector(optimizers_)); + selector_ = absl::make_unique(optimizers_); } //------------------------------------------------------------------------------ diff --git a/ortools/bop/bop_portfolio.h b/ortools/bop/bop_portfolio.h index 77aa9a47f4..2a50c13292 100644 --- a/ortools/bop/bop_portfolio.h +++ b/ortools/bop/bop_portfolio.h @@ -99,7 +99,7 @@ class PortfolioOptimizer : public BopOptimizerBase { class OptimizerSelector { public: // Note that the list of optimizers is only used to get the names for - // debug purposes, the ownership of the optimizers is not transfered. + // debug purposes, the ownership of the optimizers is not transferred. explicit OptimizerSelector( const gtl::ITIVector& optimizers); @@ -145,7 +145,7 @@ class OptimizerSelector { // TODO(user): Maybe we should simply have the notion of selectability here // and let the client handle the logic to decide what optimizer are selectable // or not. - void SetOptimizerRunnability(OptimizerIndex optimizer_index, bool runable); + void SetOptimizerRunnability(OptimizerIndex optimizer_index, bool runnable); // Returns statistics about the given optimizer. std::string PrintStats(OptimizerIndex optimizer_index) const; diff --git a/ortools/bop/integral_solver.cc b/ortools/bop/integral_solver.cc index 26e83f40c8..7584cd90d7 100644 --- a/ortools/bop/integral_solver.cc +++ b/ortools/bop/integral_solver.cc @@ -394,7 +394,7 @@ class IntegralProblemConverter { const gtl::ITIVector& dense_weights, T* t); // Returns true when at least one element is non-zero. - bool HasNonZeroWeigths( + bool HasNonZeroWeights( const gtl::ITIVector& dense_weights) const; bool problem_is_boolean_and_has_only_integral_constraints_; @@ -558,7 +558,7 @@ void IntegralProblemConverter::ConvertAllVariables( num_boolean_variables_ += integral_var.GetNumberOfBooleanVariables(); const std::string var_name = linear_problem.GetVariableName(col); for (int i = 0; i < integral_var.bits().size(); ++i) { - boolean_problem->add_var_names(var_name + StringPrintf("_%d", i)); + boolean_problem->add_var_names(var_name + absl::StrFormat("_%d", i)); } } integral_variables_.push_back(integral_var); @@ -590,7 +590,7 @@ void IntegralProblemConverter::ConvertAllConstraints( offset += AddWeightedIntegralVariable(RowToColIndex(e.row()), e.coefficient(), &dense_weights); } - if (!HasNonZeroWeigths(dense_weights)) { + if (!HasNonZeroWeights(dense_weights)) { continue; } @@ -880,7 +880,7 @@ double IntegralProblemConverter::ScaleAndSparsifyWeights( return bound_error; } -bool IntegralProblemConverter::HasNonZeroWeigths( +bool IntegralProblemConverter::HasNonZeroWeights( const gtl::ITIVector& dense_weights) const { for (const Fractional weight : dense_weights) { if (weight != 0.0) { @@ -1045,18 +1045,18 @@ BopSolveStatus IntegralSolver::SolveWithTimeLimit( BopSolveStatus IntegralSolver::Solve( const LinearProgram& linear_problem, - const DenseRow& user_provided_intial_solution) { + const DenseRow& user_provided_initial_solution) { std::unique_ptr time_limit = TimeLimit::FromParameters(parameters_); - return SolveWithTimeLimit(linear_problem, user_provided_intial_solution, + return SolveWithTimeLimit(linear_problem, user_provided_initial_solution, time_limit.get()); } BopSolveStatus IntegralSolver::SolveWithTimeLimit( const LinearProgram& linear_problem, - const DenseRow& user_provided_intial_solution, TimeLimit* time_limit) { + const DenseRow& user_provided_initial_solution, TimeLimit* time_limit) { // We make a copy so that we can clear it if the presolve is active. - DenseRow initial_solution = user_provided_intial_solution; + DenseRow initial_solution = user_provided_initial_solution; if (initial_solution.size() > 0) { CHECK_EQ(initial_solution.size(), linear_problem.num_variables()) << "The initial solution should have the same number of variables as " diff --git a/ortools/bop/integral_solver.h b/ortools/bop/integral_solver.h index 71cf7398a5..82fc822906 100644 --- a/ortools/bop/integral_solver.h +++ b/ortools/bop/integral_solver.h @@ -47,10 +47,12 @@ class IntegralSolver { // TODO(user): Change the API to accept a partial solution instead since the // underlying solver supports it. BopSolveStatus Solve(const glop::LinearProgram& linear_problem, - const glop::DenseRow& initial_solution) MUST_USE_RESULT; - BopSolveStatus SolveWithTimeLimit(const glop::LinearProgram& linear_problem, - const glop::DenseRow& initial_solution, - TimeLimit* time_limit) MUST_USE_RESULT; + const glop::DenseRow& user_provided_initial_solution) + MUST_USE_RESULT; + BopSolveStatus SolveWithTimeLimit( + const glop::LinearProgram& linear_problem, + const glop::DenseRow& user_provided_initial_solution, + TimeLimit* time_limit) MUST_USE_RESULT; // Returns the objective value of the solution with its offset. glop::Fractional objective_value() const { return objective_value_; } diff --git a/ortools/com/google/ortools/sat/Constraint.java b/ortools/com/google/ortools/sat/Constraint.java index 793644b101..b5e611745d 100644 --- a/ortools/com/google/ortools/sat/Constraint.java +++ b/ortools/com/google/ortools/sat/Constraint.java @@ -19,28 +19,28 @@ import com.google.ortools.sat.CpModelProto; /** * Wrapper around a ConstraintProto. * - *

Constraint create by the CpModel class are automatically added to the model. One need this - * class to adds an enforcement literal to a constraint. + *

Constraints created by the CpModel class are automatically added to the model. One needs this + * class to add an enforcement literal to a constraint. */ public class Constraint { public Constraint(CpModelProto.Builder builder) { - this.index_ = builder.getConstraintsCount(); - this.constraint_ = builder.addConstraintsBuilder(); + this.constraintIndex = builder.getConstraintsCount(); + this.constraintBuilder = builder.addConstraintsBuilder(); } /** Adds a literal to the constraint. */ public void onlyEnforceIf(ILiteral lit) { - constraint_.addEnforcementLiteral(lit.getIndex()); + constraintBuilder.addEnforcementLiteral(lit.getIndex()); } int getIndex() { - return index_; + return constraintIndex; } ConstraintProto.Builder builder() { - return constraint_; + return constraintBuilder; } - private int index_; - private ConstraintProto.Builder constraint_; + private final int constraintIndex; + private final ConstraintProto.Builder constraintBuilder; } diff --git a/ortools/com/google/ortools/sat/CpModel.java b/ortools/com/google/ortools/sat/CpModel.java index 155b42b9d7..6bc2526e13 100644 --- a/ortools/com/google/ortools/sat/CpModel.java +++ b/ortools/com/google/ortools/sat/CpModel.java @@ -34,7 +34,7 @@ import com.google.ortools.sat.TableConstraintProto; /** * Main modeling class. * - *

It proposes a factory to create all modeling objects understood by the SAT solver. + *

Proposes a factory to create all modeling objects understood by the SAT solver. */ public class CpModel { @@ -58,14 +58,14 @@ public class CpModel { } public CpModel() { - builder_ = CpModelProto.newBuilder(); + modelBuilder = CpModelProto.newBuilder(); } // Integer variables. /** Creates an integer variable with domain [lb, ub]. */ public IntVar newIntVar(long lb, long ub, String name) { - return new IntVar(builder_, lb, ub, name); + return new IntVar(modelBuilder, lb, ub, name); } /** @@ -78,7 +78,7 @@ public class CpModel { * 5, 5, 7, 8]. */ public IntVar newEnumeratedIntVar(long[] bounds, String name) { - return new IntVar(builder_, bounds, name); + return new IntVar(modelBuilder, bounds, name); } /** @@ -91,12 +91,12 @@ public class CpModel { * 5, 5, 7, 8]. */ public IntVar newEnumeratedIntVar(int[] bounds, String name) { - return new IntVar(builder_, toLongArray(bounds), name); + return new IntVar(modelBuilder, toLongArray(bounds), name); } /** Creates a Boolean variable with the given name. */ public IntVar newBoolVar(String name) { - return new IntVar(builder_, 0, 1, name); + return new IntVar(modelBuilder, 0, 1, name); } /** Creates a constant variable. */ @@ -108,7 +108,7 @@ public class CpModel { /** Adds {@code Or(literals) == true}. */ public Constraint addBoolOr(ILiteral[] literals) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); BoolArgumentProto.Builder boolOr = ct.builder().getBoolOrBuilder(); for (ILiteral lit : literals) { boolOr.addLiterals(lit.getIndex()); @@ -118,7 +118,7 @@ public class CpModel { /** Adds {@code And(literals) == true}. */ public Constraint addBoolAnd(ILiteral[] literals) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); BoolArgumentProto.Builder boolOr = ct.builder().getBoolAndBuilder(); for (ILiteral lit : literals) { boolOr.addLiterals(lit.getIndex()); @@ -128,7 +128,7 @@ public class CpModel { /** Adds {@code XOr(literals) == true}. */ public Constraint addBoolXor(ILiteral[] literals) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); BoolArgumentProto.Builder boolOr = ct.builder().getBoolXorBuilder(); for (ILiteral lit : literals) { boolOr.addLiterals(lit.getIndex()); @@ -145,7 +145,7 @@ public class CpModel { /** Adds {@code lb <= sum(vars) <= ub}. */ public Constraint addLinearSum(IntVar[] vars, long lb, long ub) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); LinearConstraintProto.Builder lin = ct.builder().getLinearBuilder(); for (IntVar var : vars) { lin.addVars(var.getIndex()); @@ -161,7 +161,7 @@ public class CpModel { * enumerated integer variables. */ public Constraint addLinearSumWithBounds(IntVar[] vars, long[] bounds) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); LinearConstraintProto.Builder lin = ct.builder().getLinearBuilder(); for (IntVar var : vars) { lin.addVars(var.getIndex()); @@ -202,7 +202,7 @@ public class CpModel { /** Adds {@code lb <= sum(vars[i] * coeffs[i]) <= ub}. */ public Constraint addScalProd(IntVar[] vars, long[] coeffs, long lb, long ub) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); LinearConstraintProto.Builder lin = ct.builder().getLinearBuilder(); for (IntVar var : vars) { lin.addVars(var.getIndex()); @@ -225,7 +225,7 @@ public class CpModel { * the one for enumerated integer variables. */ public Constraint addScalProdWithBounds(IntVar[] vars, long[] coeffs, long[] bounds) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); LinearConstraintProto.Builder lin = ct.builder().getLinearBuilder(); for (IntVar var : vars) { lin.addVars(var.getIndex()); @@ -270,7 +270,7 @@ public class CpModel { /** Adds {@code var <= value}. */ public Constraint addLessOrEqual(IntVar var, long value) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); LinearConstraintProto.Builder lin = ct.builder().getLinearBuilder(); lin.addVars(var.getIndex()); lin.addCoeffs(1); @@ -279,9 +279,14 @@ public class CpModel { return ct; } + /** Adds {@code before <= after}. */ + public Constraint addLessOrEqual(IntVar before, IntVar after) { + return addLessOrEqualWithOffset(before, after, 0); + } + /** Adds {@code var >= value}. */ public Constraint addGreaterOrEqual(IntVar var, long value) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); LinearConstraintProto.Builder lin = ct.builder().getLinearBuilder(); lin.addVars(var.getIndex()); lin.addCoeffs(1); @@ -292,7 +297,7 @@ public class CpModel { /** Adds {@code var == value}. */ public Constraint addEquality(IntVar var, long value) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); LinearConstraintProto.Builder lin = ct.builder().getLinearBuilder(); lin.addVars(var.getIndex()); lin.addCoeffs(1); @@ -308,7 +313,7 @@ public class CpModel { /** Adds {@code a + offset == b}. */ public Constraint addEqualityWithOffset(IntVar a, IntVar b, long offset) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); LinearConstraintProto.Builder lin = ct.builder().getLinearBuilder(); lin.addVars(a.getIndex()); lin.addCoeffs(-1); @@ -321,7 +326,7 @@ public class CpModel { /** Adds {@code var != value}. */ public Constraint addDifferent(IntVar var, long value) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); LinearConstraintProto.Builder lin = ct.builder().getLinearBuilder(); lin.addVars(var.getIndex()); lin.addCoeffs(1); @@ -343,7 +348,7 @@ public class CpModel { /** Adds {@code a + offset != b} */ public Constraint addDifferentWithOffset(IntVar a, IntVar b, long offset) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); LinearConstraintProto.Builder lin = ct.builder().getLinearBuilder(); lin.addVars(a.getIndex()); lin.addCoeffs(-1); @@ -360,9 +365,9 @@ public class CpModel { return ct; } - /** {@code before + offset <= after}. */ + /** Adds {@code before + offset <= after}. */ public Constraint addLessOrEqualWithOffset(IntVar before, IntVar after, long offset) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); LinearConstraintProto.Builder lin = ct.builder().getLinearBuilder(); lin.addVars(before.getIndex()); lin.addCoeffs(-1); @@ -373,11 +378,6 @@ public class CpModel { return ct; } - /** Adds {@code before <= after}. */ - public Constraint addLessOrEqual(IntVar before, IntVar after) { - return addLessOrEqualWithOffset(before, after, 0); - } - // Integer constraints. /** @@ -385,11 +385,11 @@ public class CpModel { * *

This constraint forces all variables to have different values. * - * @param variables a list of integer variables. - * @return an instance of the Constraint class. + * @param variables a list of integer variables + * @return an instance of the Constraint class */ public Constraint addAllDifferent(IntVar[] variables) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); AllDifferentConstraintProto.Builder allDiff = ct.builder().getAllDiffBuilder(); for (IntVar var : variables) { allDiff.addVars(var.getIndex()); @@ -399,7 +399,7 @@ public class CpModel { /** Adds the element constraint: {@code variables[index] == target}. */ public Constraint addElement(IntVar index, IntVar[] variables, IntVar target) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); ElementConstraintProto.Builder element = ct.builder().getElementBuilder(); element.setIndex(index.getIndex()); for (IntVar var : variables) { @@ -411,7 +411,7 @@ public class CpModel { /** Adds the element constraint: {@code values[index] == target}. */ public Constraint addElement(IntVar index, long[] values, IntVar target) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); ElementConstraintProto.Builder element = ct.builder().getElementBuilder(); element.setIndex(index.getIndex()); for (long v : values) { @@ -423,7 +423,7 @@ public class CpModel { /** Adds the element constraint: {@code values[index] == target}. */ public Constraint addElement(IntVar index, int[] values, IntVar target) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); ElementConstraintProto.Builder element = ct.builder().getElementBuilder(); element.setIndex(index.getIndex()); for (long v : values) { @@ -436,17 +436,17 @@ public class CpModel { /** * Adds {@code Circuit(tails, heads, literals)}. * - *

Adds a circuit constraints from a sparse list of arcs that encode the graph. + *

Adds a circuit constraint from a sparse list of arcs that encode the graph. * *

A circuit is a unique Hamiltonian path in a subgraph of the total graph. In case a node 'i' * is not in the path, then there must be a loop arc {@code 'i -> i'} associated with a true * literal. Otherwise this constraint will fail. * - * @param tails the tails of all arcs. - * @param heads the heads of all arcs. - * @param literals the literals that control whether an arc is selected or not. - * @return an instance of the Constraint class. - * @throws MismatchedArrayLengths if the arrays have different sizes. + * @param tails the tails of all arcs + * @param heads the heads of all arcs + * @param literals the literals that control whether an arc is selected or not + * @return an instance of the Constraint class + * @throws MismatchedArrayLengths if the arrays have different sizes */ public Constraint addCircuit(int[] tails, int[] heads, ILiteral[] literals) throws MismatchedArrayLengths { @@ -457,7 +457,7 @@ public class CpModel { throw new MismatchedArrayLengths("addCircuit", "tails", "literals"); } - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); CircuitConstraintProto.Builder circuit = ct.builder().getCircuitBuilder(); for (int t : tails) { circuit.addTails(t); @@ -476,17 +476,17 @@ public class CpModel { * *

An AllowedAssignments constraint is a constraint on an array of variables that forces, when * all variables are fixed to a single value, that the corresponding list of values is equal to - * one of the tuple of the tupleList. + * one of the tuples of the tupleList. * - * @param variables a list of variables. + * @param variables a list of variables * @param tuplesList a list of admissible tuples. Each tuple must have the same length as the * variables, and the ith value of a tuple corresponds to the ith variable. - * @return an instance of the Constraint class. - * @throws WrongLength if one tuple does not have the same length as the variables. + * @return an instance of the Constraint class + * @throws WrongLength if one tuple does not have the same length as the variables */ public Constraint addAllowedAssignments(IntVar[] variables, long[][] tuplesList) throws WrongLength { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); TableConstraintProto.Builder table = ct.builder().getTableBuilder(); for (IntVar var : variables) { table.addVars(var.getIndex()); @@ -512,7 +512,7 @@ public class CpModel { */ public Constraint addAllowedAssignments(IntVar[] variables, int[][] tuplesList) throws WrongLength { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); TableConstraintProto.Builder table = ct.builder().getTableBuilder(); for (IntVar var : variables) { table.addVars(var.getIndex()); @@ -537,11 +537,11 @@ public class CpModel { *

A ForbiddenAssignments constraint is a constraint on an array of variables where the list of * impossible combinations is provided in the tuples list. * - * @param variables a list of variables. + * @param variables a list of variables * @param tuplesList a list of forbidden tuples. Each tuple must have the same length as the * variables, and the ith value of a tuple corresponds to the ith variable. - * @return an instance of the Constraint class. - * @throws WrongLength if one tuple does not have the same length as the variables. + * @return an instance of the Constraint class + * @throws WrongLength if one tuple does not have the same length as the variables */ public Constraint addForbiddenAssignments(IntVar[] variables, long[][] tuplesList) throws WrongLength { @@ -587,18 +587,18 @@ public class CpModel { * ends in one of the final states in the final phase. * * @param transitionVariables a non empty list of variables whose values correspond to the labels - * of the arcs traversed by the automata. - * @param startingState the initial state of the automata. - * @param finalStates a non empty list of admissible final states. + * of the arcs traversed by the automata + * @param startingState the initial state of the automata + * @param finalStates a non empty list of admissible final states * @param transitions a list of transition for the automata, in the following format - * (currentState, variableValue, nextState). - * @return an instance of the Constraint class. - * @throws WrongLength if one transition does not have a length of 3. + * (currentState, variableValue, nextState) + * @return an instance of the Constraint class + * @throws WrongLength if one transition does not have a length of 3 */ public Constraint addAutomaton( IntVar[] transitionVariables, long startingState, long[] finalStates, long[][] transitions) throws WrongLength { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); AutomataConstraintProto.Builder automaton = ct.builder().getAutomataBuilder(); for (IntVar var : transitionVariables) { automaton.addVars(var.getIndex()); @@ -624,17 +624,17 @@ public class CpModel { *

An inverse constraint enforces that if 'variables[i]' is assigned a value 'j', then * inverseVariables[j] is assigned a value 'i'. And vice versa. * - * @param variables an array of integer variables. - * @param inverseVariables an array of integer variables. - * @return an instance of the Constraint class. - * @throws MismatchedArrayLengths if variables and inverseVariables have different length. + * @param variables an array of integer variables + * @param inverseVariables an array of integer variables + * @return an instance of the Constraint class + * @throws MismatchedArrayLengths if variables and inverseVariables have different length */ public Constraint addInverse(IntVar[] variables, IntVar[] inverseVariables) throws MismatchedArrayLengths { if (variables.length != inverseVariables.length) { throw new MismatchedArrayLengths("addInverse", "variables", "inverseVariables"); } - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); InverseConstraintProto.Builder inverse = ct.builder().getInverseBuilder(); for (IntVar var : variables) { inverse.addFDirect(var.getIndex()); @@ -653,27 +653,27 @@ public class CpModel { * all times variables to be non negative. If the variable times[i] is assigned a value t, then * the current level changes by demands[i] (which is constant) at the time t. * - *

Note that level min can be greater than 0, or level max can be less than 0. It just forces - * some demands to be executed at time 0 to make sure that we are within those bounds with the - * executed demands. Therefore, {@code forall t >= 0: minLevel <= sum(demands[i] if times[i] <= t) - * <= maxLevel}. + *

Note that {@code minLevel} can be greater than 0, or {@code maxLevel} can be less than 0. It + * just forces some demands to be executed at time 0 to make sure that we are within those bounds + * with the executed demands. Therefore, {@code forall t >= 0: minLevel <= sum(demands[i] if + * times[i] <= t) <= maxLevel}. * * @param times a list of positive integer variables which specify the time of the filling or - * emptying the reservoir. - * @param demands a list of integer values that specifies the amount of the emptying or feeling. + * emptying the reservoir + * @param demands a list of integer values that specifies the amount of the emptying or feeling * @param minLevel at any non negative time, the level of the reservoir must be greater of equal - * than the min level. + * than the min level * @param maxLevel at any non negative time, the level of the reservoir must be less or equal than - * the max level. - * @return an instance of the Constraint class. - * @throws MismatchedArrayLengths if times and demands have different length. + * the max level + * @return an instance of the Constraint class + * @throws MismatchedArrayLengths if times and demands have different length */ public Constraint addReservoirConstraint( IntVar[] times, long[] demands, long minLevel, long maxLevel) throws MismatchedArrayLengths { if (times.length != demands.length) { throw new MismatchedArrayLengths("addReservoirConstraint", "times", "demands"); } - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); ReservoirConstraintProto.Builder reservoir = ct.builder().getReservoirBuilder(); for (IntVar var : times) { reservoir.addTimes(var.getIndex()); @@ -706,7 +706,7 @@ public class CpModel { /** Adds {@code target == Min(vars)}. */ public Constraint addMinEquality(IntVar target, IntVar[] vars) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); IntegerArgumentProto.Builder intMin = ct.builder().getIntMinBuilder(); intMin.setTarget(target.getIndex()); for (IntVar var : vars) { @@ -717,7 +717,7 @@ public class CpModel { /** Adds {@code target == Max(vars)}. */ public Constraint addMaxEquality(IntVar target, IntVar[] vars) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); IntegerArgumentProto.Builder intMax = ct.builder().getIntMaxBuilder(); intMax.setTarget(target.getIndex()); for (IntVar var : vars) { @@ -728,7 +728,7 @@ public class CpModel { /** Adds {@code target == num / denom}, rounded towards 0. */ public Constraint addDivisionEquality(IntVar target, IntVar num, IntVar denom) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); IntegerArgumentProto.Builder intDiv = ct.builder().getIntDivBuilder(); intDiv.setTarget(target.getIndex()); intDiv.addVars(num.getIndex()); @@ -738,7 +738,7 @@ public class CpModel { /** Adds {@code target == var % mod}. */ public Constraint addModuloEquality(IntVar target, IntVar var, IntVar mod) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); IntegerArgumentProto.Builder intMod = ct.builder().getIntModBuilder(); intMod.setTarget(target.getIndex()); intMod.addVars(var.getIndex()); @@ -748,7 +748,7 @@ public class CpModel { /** Adds {@code target == var % mod}. */ public Constraint addModuloEquality(IntVar target, IntVar var, long mod) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); IntegerArgumentProto.Builder intMod = ct.builder().getIntModBuilder(); intMod.setTarget(target.getIndex()); intMod.addVars(var.getIndex()); @@ -758,7 +758,7 @@ public class CpModel { /** Adds {@code target == Product(vars)}. */ public Constraint addProductEquality(IntVar target, IntVar[] vars) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); IntegerArgumentProto.Builder intProd = ct.builder().getIntProdBuilder(); intProd.setTarget(target.getIndex()); for (IntVar var : vars) { @@ -770,57 +770,57 @@ public class CpModel { // Scheduling support. /** - * Creates an interval variables from start, size, and end. + * Creates an interval variable from start, size, and end. * *

An interval variable is a constraint, that is itself used in other constraints like * NoOverlap. * *

Internally, it ensures that {@code start + size == end}. * - * @param start the start of the interval. - * @param size the size of the interval. - * @param end the end of the interval. - * @param name the name of the interval variable. - * @return An IntervalVar object. + * @param start the start of the interval + * @param size the size of the interval + * @param end the end of the interval + * @param name the name of the interval variable + * @return An IntervalVar object */ public IntervalVar newIntervalVar(IntVar start, IntVar size, IntVar end, String name) { - return new IntervalVar(builder_, start.getIndex(), size.getIndex(), end.getIndex(), name); + return new IntervalVar(modelBuilder, start.getIndex(), size.getIndex(), end.getIndex(), name); } /** - * Creates an interval variables with a fixed end. + * Creates an interval variable with a fixed end. * * @see #newIntervalVar(IntVar, IntVar, IntVar, String) newIntervalVar */ public IntervalVar newIntervalVar(IntVar start, IntVar size, long end, String name) { return new IntervalVar( - builder_, start.getIndex(), size.getIndex(), indexFromConstant(end), name); + modelBuilder, start.getIndex(), size.getIndex(), indexFromConstant(end), name); } /** - * Creates an interval variables with a fixed size. + * Creates an interval variable with a fixed size. * * @see #newIntervalVar(IntVar, IntVar, IntVar, String) newIntervalVar */ public IntervalVar newIntervalVar(IntVar start, long size, IntVar end, String name) { return new IntervalVar( - builder_, start.getIndex(), indexFromConstant(size), end.getIndex(), name); + modelBuilder, start.getIndex(), indexFromConstant(size), end.getIndex(), name); } /** - * Creates an interval variables with a fixed start. + * Creates an interval variable with a fixed start. * * @see #newIntervalVar(IntVar, IntVar, IntVar, String) newIntervalVar */ public IntervalVar newIntervalVar(long start, IntVar size, IntVar end, String name) { return new IntervalVar( - builder_, indexFromConstant(start), size.getIndex(), end.getIndex(), name); + modelBuilder, indexFromConstant(start), size.getIndex(), end.getIndex(), name); } /** Creates a fixed interval from its start and its size. */ public IntervalVar newFixedInterval(long start, long size, String name) { return new IntervalVar( - builder_, + modelBuilder, indexFromConstant(start), indexFromConstant(size), indexFromConstant(start + size), @@ -828,11 +828,11 @@ public class CpModel { } /** - * Creates an optional interval var from start, size, end. and isPresent. + * Creates an optional interval variable from start, size, end, and isPresent. * *

An optional interval variable is a constraint, that is itself used in other constraints like - * NoOverlap. This constraint is protected by an is_present literal that indicates if it is active - * or not. + * NoOverlap. This constraint is protected by an {@code isPresent} literal that indicates if it is + * active or not. * *

Internally, it ensures that {@code isPresent => start + size == end}. * @@ -841,13 +841,18 @@ public class CpModel { * @param end the end of the interval. It can be an integer value, or an integer variable. * @param isPresent a literal that indicates if the interval is active or not. A inactive interval * is simply ignored by all constraints. - * @param name The name of the interval variable. - * @return an IntervalVar object. + * @param name The name of the interval variable + * @return an IntervalVar object */ public IntervalVar newOptionalIntervalVar( IntVar start, IntVar size, IntVar end, ILiteral isPresent, String name) { return new IntervalVar( - builder_, start.getIndex(), size.getIndex(), end.getIndex(), isPresent.getIndex(), name); + modelBuilder, + start.getIndex(), + size.getIndex(), + end.getIndex(), + isPresent.getIndex(), + name); } /** @@ -858,7 +863,7 @@ public class CpModel { public IntervalVar newOptionalIntervalVar( IntVar start, IntVar size, long end, ILiteral isPresent, String name) { return new IntervalVar( - builder_, + modelBuilder, start.getIndex(), size.getIndex(), indexFromConstant(end), @@ -874,7 +879,7 @@ public class CpModel { public IntervalVar newOptionalIntervalVar( IntVar start, long size, IntVar end, ILiteral isPresent, String name) { return new IntervalVar( - builder_, + modelBuilder, start.getIndex(), indexFromConstant(size), end.getIndex(), @@ -886,7 +891,7 @@ public class CpModel { public IntervalVar newOptionalIntervalVar( long start, IntVar size, IntVar end, ILiteral isPresent, String name) { return new IntervalVar( - builder_, + modelBuilder, indexFromConstant(start), size.getIndex(), end.getIndex(), @@ -902,7 +907,7 @@ public class CpModel { public IntervalVar newOptionalFixedInterval( long start, long size, ILiteral isPresent, String name) { return new IntervalVar( - builder_, + modelBuilder, indexFromConstant(start), indexFromConstant(size), indexFromConstant(start + size), @@ -915,11 +920,11 @@ public class CpModel { * *

A NoOverlap constraint ensures that all present intervals do not overlap in time. * - * @param intervalVars the list of interval variables to constrain. - * @return an instance of the Constraint class. + * @param intervalVars the list of interval variables to constrain + * @return an instance of the Constraint class */ public Constraint addNoOverlap(IntervalVar[] intervalVars) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); NoOverlapConstraintProto.Builder noOverlap = ct.builder().getNoOverlapBuilder(); for (IntervalVar var : intervalVars) { noOverlap.addIntervals(var.getIndex()); @@ -934,12 +939,12 @@ public class CpModel { * rectangle is aligned with the X and Y axis, and is defined by two intervals which represent its * projection onto the X and Y axis. * - * @param xIntervals the X coordinates of the rectangles. - * @param yIntervals the Y coordinates of the rectangles. - * @return an instance of the Constraint class. + * @param xIntervals the X coordinates of the rectangles + * @param yIntervals the Y coordinates of the rectangles + * @return an instance of the Constraint class */ public Constraint addNoOverlap2D(IntervalVar[] xIntervals, IntervalVar[] yIntervals) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); NoOverlap2DConstraintProto.Builder noOverlap2d = ct.builder().getNoOverlap2DBuilder(); for (IntervalVar x : xIntervals) { noOverlap2d.addXIntervals(x.getIndex()); @@ -958,15 +963,15 @@ public class CpModel { *

{@code forall t: sum(demands[i] if (start(intervals[t]) <= t < end(intervals[t])) and (t is * present)) <= capacity}. * - * @param intervals the list of intervals. + * @param intervals the list of intervals * @param demands the list of demands for each interval. Each demand must be a positive integer * variable. * @param capacity the maximum capacity of the cumulative constraint. It must be a positive * integer variable. - * @return An instance of the Constraint class. + * @return an instance of the Constraint class */ public Constraint addCumulative(IntervalVar[] intervals, IntVar[] demands, IntVar capacity) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); CumulativeConstraintProto.Builder cumul = ct.builder().getCumulativeBuilder(); for (IntervalVar interval : intervals) { cumul.addIntervals(interval.getIndex()); @@ -979,12 +984,12 @@ public class CpModel { } /** - * Adds {@code Cumulative(intervals, demands, capacity)}. with fixed demands. + * Adds {@code Cumulative(intervals, demands, capacity)} with fixed demands. * * @see #addCumulative(IntervalVar[], IntVar[], IntVar) AddCumulative */ public Constraint addCumulative(IntervalVar[] intervals, long[] demands, IntVar capacity) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); CumulativeConstraintProto.Builder cumul = ct.builder().getCumulativeBuilder(); for (IntervalVar interval : intervals) { cumul.addIntervals(interval.getIndex()); @@ -997,7 +1002,7 @@ public class CpModel { } /** - * Adds {@code Cumulative(intervals, demands, capacity)}. with fixed demands. + * Adds {@code Cumulative(intervals, demands, capacity)} with fixed demands. * * @see #addCumulative(IntervalVar[], IntVar[], IntVar) AddCumulative */ @@ -1006,12 +1011,12 @@ public class CpModel { } /** - * Adds {@code Cumulative(intervals, demands, capacity)}. with fixed capacity. + * Adds {@code Cumulative(intervals, demands, capacity)} with fixed capacity. * * @see #addCumulative(IntervalVar[], IntVar[], IntVar) AddCumulative */ public Constraint addCumulative(IntervalVar[] intervals, IntVar[] demands, long capacity) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); CumulativeConstraintProto.Builder cumul = ct.builder().getCumulativeBuilder(); for (IntervalVar interval : intervals) { cumul.addIntervals(interval.getIndex()); @@ -1024,12 +1029,12 @@ public class CpModel { } /** - * Adds {@code Cumulative(intervals, demands, capacity)}. with fixed demands and fixed capacity. + * Adds {@code Cumulative(intervals, demands, capacity)} with fixed demands and fixed capacity. * * @see #addCumulative(IntervalVar[], IntVar[], IntVar) AddCumulative */ public Constraint addCumulative(IntervalVar[] intervals, long[] demands, long capacity) { - Constraint ct = new Constraint(builder_); + Constraint ct = new Constraint(modelBuilder); CumulativeConstraintProto.Builder cumul = ct.builder().getCumulativeBuilder(); for (IntervalVar interval : intervals) { cumul.addIntervals(interval.getIndex()); @@ -1042,7 +1047,7 @@ public class CpModel { } /** - * Adds {@code Cumulative(intervals, demands, capacity)}. with fixed demands and fixed capacity. + * Adds {@code Cumulative(intervals, demands, capacity)} with fixed demands and fixed capacity. * * @see #addCumulative(IntervalVar[], IntVar[], IntVar) AddCumulative */ @@ -1054,14 +1059,14 @@ public class CpModel { /** Adds a minimization objective of a single variable. */ public void minimize(IntVar var) { - CpObjectiveProto.Builder obj = builder_.getObjectiveBuilder(); + CpObjectiveProto.Builder obj = modelBuilder.getObjectiveBuilder(); obj.addVars(var.getIndex()); obj.addCoeffs(1); } /** Adds a minimization objective of a sum of variables. */ public void minimizeSum(IntVar[] vars) { - CpObjectiveProto.Builder obj = builder_.getObjectiveBuilder(); + CpObjectiveProto.Builder obj = modelBuilder.getObjectiveBuilder(); for (IntVar var : vars) { obj.addVars(var.getIndex()); obj.addCoeffs(1); @@ -1070,7 +1075,7 @@ public class CpModel { /** Adds a minimization objective of a scalar product of variables. */ public void minimizeScalProd(IntVar[] vars, long[] coeffs) { - CpObjectiveProto.Builder obj = builder_.getObjectiveBuilder(); + CpObjectiveProto.Builder obj = modelBuilder.getObjectiveBuilder(); for (IntVar var : vars) { obj.addVars(var.getIndex()); } @@ -1086,7 +1091,7 @@ public class CpModel { /** Adds a maximization objective of a single variable. */ public void maximize(IntVar var) { - CpObjectiveProto.Builder obj = builder_.getObjectiveBuilder(); + CpObjectiveProto.Builder obj = modelBuilder.getObjectiveBuilder(); obj.addVars(negated(var.getIndex())); obj.addCoeffs(1); obj.setScalingFactor(-1.0); @@ -1094,7 +1099,7 @@ public class CpModel { /** Adds a maximization objective of a sum of variables. */ public void maximizeSum(IntVar[] vars) { - CpObjectiveProto.Builder obj = builder_.getObjectiveBuilder(); + CpObjectiveProto.Builder obj = modelBuilder.getObjectiveBuilder(); for (IntVar var : vars) { obj.addVars(negated(var.getIndex())); obj.addCoeffs(1); @@ -1104,7 +1109,7 @@ public class CpModel { /** Adds a maximization objective of a scalar product of variables. */ public void maximizeScalProd(IntVar[] vars, long[] coeffs) { - CpObjectiveProto.Builder obj = builder_.getObjectiveBuilder(); + CpObjectiveProto.Builder obj = modelBuilder.getObjectiveBuilder(); for (IntVar var : vars) { obj.addVars(negated(var.getIndex())); } @@ -1126,7 +1131,7 @@ public class CpModel { IntVar[] variables, DecisionStrategyProto.VariableSelectionStrategy varStr, DecisionStrategyProto.DomainReductionStrategy domStr) { - DecisionStrategyProto.Builder ds = builder_.addSearchStrategyBuilder(); + DecisionStrategyProto.Builder ds = modelBuilder.addSearchStrategyBuilder(); for (IntVar var : variables) { ds.addVariables(var.getIndex()); } @@ -1145,8 +1150,8 @@ public class CpModel { } int indexFromConstant(long constant) { - int index = builder_.getVariablesCount(); - IntegerVariableProto.Builder cst = builder_.addVariablesBuilder(); + int index = modelBuilder.getVariablesCount(); + IntegerVariableProto.Builder cst = modelBuilder.addVariablesBuilder(); cst.addDomain(constant); cst.addDomain(constant); return index; @@ -1155,12 +1160,12 @@ public class CpModel { // Getters. public CpModelProto model() { - return builder_.build(); + return modelBuilder.build(); } public int negated(int index) { return -index - 1; } - private CpModelProto.Builder builder_; + private final CpModelProto.Builder modelBuilder; } diff --git a/ortools/com/google/ortools/sat/CpSolver.java b/ortools/com/google/ortools/sat/CpSolver.java index b3163b047f..b7f3aa28f7 100644 --- a/ortools/com/google/ortools/sat/CpSolver.java +++ b/ortools/com/google/ortools/sat/CpSolver.java @@ -13,7 +13,6 @@ package com.google.ortools.sat; -import com.google.ortools.sat.CpModelProto; import com.google.ortools.sat.CpSolverResponse; import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.SatParameters; @@ -25,93 +24,94 @@ import com.google.ortools.sat.SatParameters; * variables in the best solution, as well as general statistics of the search. */ public class CpSolver { - /** Main constructionof the CpSolver class. */ + /** Main construction of the CpSolver class. */ public CpSolver() { - this.parameters_ = SatParameters.newBuilder(); + this.solveParameters = SatParameters.newBuilder(); } /** Solves the given module, and returns the solve status. */ public CpSolverStatus solve(CpModel model) { - response_ = SatHelper.solveWithParameters(model.model(), parameters_.build()); - return response_.getStatus(); + solveResponse = SatHelper.solveWithParameters(model.model(), solveParameters.build()); + return solveResponse.getStatus(); } - /** Solves a problem and pass each solution found to the callback. */ + /** Solves a problem and passes each solution found to the callback. */ public CpSolverStatus solveWithSolutionCallback(CpModel model, CpSolverSolutionCallback cb) { - response_ = - SatHelper.solveWithParametersAndSolutionCallback(model.model(), parameters_.build(), cb); - return response_.getStatus(); + solveResponse = + SatHelper.solveWithParametersAndSolutionCallback( + model.model(), solveParameters.build(), cb); + return solveResponse.getStatus(); } /** - * Search for all solutions of a satisfiability problem. + * Searches for all solutions of a satisfiability problem. * - *

This method searches for all feasible solution of a given model. Then it feeds the solution - * to the callback. + *

This method searches for all feasible solutions of a given model. Then it feeds the + * solutions to the callback. * - * @param model the model to solve. - * @param cb the callback that will be called at each solution. - * @return the status of the solve (FEASIBLE, INFEASIBLE...). + * @param model the model to solve + * @param cb the callback that will be called at each solution + * @return the status of the solve (FEASIBLE, INFEASIBLE...) */ public CpSolverStatus searchAllSolutions(CpModel model, CpSolverSolutionCallback cb) { - parameters_.setEnumerateAllSolutions(true); - response_ = - SatHelper.solveWithParametersAndSolutionCallback(model.model(), parameters_.build(), cb); - parameters_.setEnumerateAllSolutions(true); - return response_.getStatus(); + solveParameters.setEnumerateAllSolutions(true); + solveResponse = + SatHelper.solveWithParametersAndSolutionCallback( + model.model(), solveParameters.build(), cb); + solveParameters.setEnumerateAllSolutions(true); + return solveResponse.getStatus(); } - /** The best objective value found during search. */ + /** Returns the best objective value found during search. */ public double objectiveValue() { - return response_.getObjectiveValue(); + return solveResponse.getObjectiveValue(); } - /** The value of a variable in the last solution found. */ + /** Returns the value of a variable in the last solution found. */ public long value(IntVar var) { - return response_.getSolution(var.getIndex()); + return solveResponse.getSolution(var.getIndex()); } - /** The Boolean value of a literal in the last solution found. */ + /** Returns the Boolean value of a literal in the last solution found. */ public Boolean booleanValue(ILiteral var) { int index = var.getIndex(); if (index >= 0) { - return response_.getSolution(index) != 0; + return solveResponse.getSolution(index) != 0; } else { - return response_.getSolution(-index - 1) == 0; + return solveResponse.getSolution(-index - 1) == 0; } } - /** The internal response protobuf that is returned internally by the SAT solver. */ + /** Returns the internal response protobuf that is returned internally by the SAT solver. */ public CpSolverResponse response() { - return response_; + return solveResponse; } - /** The number of branches explored during search. */ + /** Returns the number of branches explored during search. */ public long numBranches() { - return response_.getNumBranches(); + return solveResponse.getNumBranches(); } - /** The number of conflicts created during search. */ + /** Returns the number of conflicts created during search. */ public long numConflicts() { - return response_.getNumConflicts(); + return solveResponse.getNumConflicts(); } - /** The wall time of the search. */ + /** Returns the wall time of the search. */ public double wallTime() { - return response_.getWallTime(); + return solveResponse.getWallTime(); } - /** The user time of the search. */ + /** Returns the user time of the search. */ public double userTime() { - return response_.getUserTime(); + return solveResponse.getUserTime(); } /** Returns the builder of the parameters of the SAT solver for modification. */ public SatParameters.Builder getParameters() { - return parameters_; + return solveParameters; } - private CpModelProto model_; - private CpSolverResponse response_; - private SatParameters.Builder parameters_; + private CpSolverResponse solveResponse; + private final SatParameters.Builder solveParameters; } diff --git a/ortools/com/google/ortools/sat/ILiteral.java b/ortools/com/google/ortools/sat/ILiteral.java index e7dd769a97..5fa67c23b0 100644 --- a/ortools/com/google/ortools/sat/ILiteral.java +++ b/ortools/com/google/ortools/sat/ILiteral.java @@ -13,7 +13,7 @@ package com.google.ortools.sat; -/** Interface to descrive a boolean variable or its negation. */ +/** Interface to describe a boolean variable or its negation. */ public interface ILiteral { int getIndex(); diff --git a/ortools/com/google/ortools/sat/IntVar.java b/ortools/com/google/ortools/sat/IntVar.java index c89c1abe80..de15fa4985 100644 --- a/ortools/com/google/ortools/sat/IntVar.java +++ b/ortools/com/google/ortools/sat/IntVar.java @@ -19,46 +19,48 @@ import com.google.ortools.sat.IntegerVariableProto; /** An integer variable. */ public class IntVar implements ILiteral { IntVar(CpModelProto.Builder builder, long lb, long ub, String name) { - this.builder_ = builder; - this.index_ = builder_.getVariablesCount(); - this.var_ = builder_.addVariablesBuilder(); - this.var_.setName(name); - this.var_.addDomain(lb); - this.var_.addDomain(ub); + this.modelBuilder = builder; + this.variableIndex = modelBuilder.getVariablesCount(); + this.varBuilder = modelBuilder.addVariablesBuilder(); + this.varBuilder.setName(name); + this.varBuilder.addDomain(lb); + this.varBuilder.addDomain(ub); this.negation_ = null; } IntVar(CpModelProto.Builder builder, long[] bounds, String name) { - this.builder_ = builder; - this.index_ = builder_.getVariablesCount(); - this.var_ = builder_.addVariablesBuilder(); - this.var_.setName(name); + this.modelBuilder = builder; + this.variableIndex = modelBuilder.getVariablesCount(); + this.varBuilder = modelBuilder.addVariablesBuilder(); + this.varBuilder.setName(name); for (long b : bounds) { - this.var_.addDomain(b); + this.varBuilder.addDomain(b); } this.negation_ = null; } @Override public String toString() { - return var_.toString(); + return varBuilder.toString(); } - /** Internal, return the index of the variable in the underlying CpModelProto. */ + /** Internal, returns the index of the variable in the underlying CpModelProto. */ + @Override public int getIndex() { - return index_; + return variableIndex; } /** Returns the name of the variable given upon creation. */ public String getName() { - return var_.getName(); + return varBuilder.getName(); } /** Returns a short string describing the variable. */ + @Override public String getShortString() { - if (var_.getName().isEmpty()) { - if (var_.getDomainCount() == 2 && var_.getDomain(0) == var_.getDomain(1)) { - return String.format("%d", var_.getDomain(0)); + if (varBuilder.getName().isEmpty()) { + if (varBuilder.getDomainCount() == 2 && varBuilder.getDomain(0) == varBuilder.getDomain(1)) { + return String.format("%d", varBuilder.getDomain(0)); } else { return String.format("var_%d(%s)", getIndex(), displayBounds()); } @@ -67,23 +69,24 @@ public class IntVar implements ILiteral { } } - /** Return the domain as a string without the enclosing []. */ + /** Returns the domain as a string without the enclosing []. */ public String displayBounds() { String out = ""; - for (int i = 0; i < var_.getDomainCount(); i += 2) { + for (int i = 0; i < varBuilder.getDomainCount(); i += 2) { if (i != 0) { out += ", "; } - if (var_.getDomain(i) == var_.getDomain(i + 1)) { - out += String.format("%d", var_.getDomain(i)); + if (varBuilder.getDomain(i) == varBuilder.getDomain(i + 1)) { + out += String.format("%d", varBuilder.getDomain(i)); } else { - out += String.format("%d..%d", var_.getDomain(i), var_.getDomain(i + 1)); + out += String.format("%d..%d", varBuilder.getDomain(i), varBuilder.getDomain(i + 1)); } } return out; } /** Returns the negation of a boolean variable. */ + @Override public ILiteral not() { if (negation_ == null) { negation_ = new NotBooleanVariable(this); @@ -91,8 +94,8 @@ public class IntVar implements ILiteral { return negation_; } - private CpModelProto.Builder builder_; - private int index_; - private IntegerVariableProto.Builder var_; + private final CpModelProto.Builder modelBuilder; + private final int variableIndex; + private final IntegerVariableProto.Builder varBuilder; private NotBooleanVariable negation_; } diff --git a/ortools/com/google/ortools/sat/IntervalVar.java b/ortools/com/google/ortools/sat/IntervalVar.java index 887de86002..3cbbd5d0cb 100644 --- a/ortools/com/google/ortools/sat/IntervalVar.java +++ b/ortools/com/google/ortools/sat/IntervalVar.java @@ -21,14 +21,14 @@ import com.google.ortools.sat.IntervalConstraintProto; public class IntervalVar { IntervalVar( CpModelProto.Builder builder, int startIndex, int sizeIndex, int endIndex, String name) { - this.builder_ = builder; - this.index_ = builder_.getConstraintsCount(); - ConstraintProto.Builder ct = builder_.addConstraintsBuilder(); + this.modelBuilder = builder; + this.constraintIndex = modelBuilder.getConstraintsCount(); + ConstraintProto.Builder ct = modelBuilder.addConstraintsBuilder(); ct.setName(name); - this.var_ = ct.getIntervalBuilder(); - this.var_.setStart(startIndex); - this.var_.setSize(sizeIndex); - this.var_.setEnd(endIndex); + this.intervalBuilder = ct.getIntervalBuilder(); + this.intervalBuilder.setStart(startIndex); + this.intervalBuilder.setSize(sizeIndex); + this.intervalBuilder.setEnd(endIndex); } IntervalVar( @@ -38,32 +38,32 @@ public class IntervalVar { int endIndex, int isPresentIndex, String name) { - this.builder_ = builder; - this.index_ = builder_.getConstraintsCount(); - ConstraintProto.Builder ct = builder_.addConstraintsBuilder(); + this.modelBuilder = builder; + this.constraintIndex = modelBuilder.getConstraintsCount(); + ConstraintProto.Builder ct = modelBuilder.addConstraintsBuilder(); ct.setName(name); ct.addEnforcementLiteral(isPresentIndex); - this.var_ = ct.getIntervalBuilder(); - this.var_.setStart(startIndex); - this.var_.setSize(sizeIndex); - this.var_.setEnd(endIndex); + this.intervalBuilder = ct.getIntervalBuilder(); + this.intervalBuilder.setStart(startIndex); + this.intervalBuilder.setSize(sizeIndex); + this.intervalBuilder.setEnd(endIndex); } @Override public String toString() { - return builder_.getConstraints(index_).toString(); + return modelBuilder.getConstraints(constraintIndex).toString(); } int getIndex() { - return index_; + return constraintIndex; } /** Returns the name passed in the constructor. */ public String getName() { - return builder_.getConstraints(index_).getName(); + return modelBuilder.getConstraints(constraintIndex).getName(); } - private CpModelProto.Builder builder_; - private int index_; - private IntervalConstraintProto.Builder var_; + private final CpModelProto.Builder modelBuilder; + private final int constraintIndex; + private final IntervalConstraintProto.Builder intervalBuilder; } diff --git a/ortools/com/google/ortools/sat/NotBooleanVariable.java b/ortools/com/google/ortools/sat/NotBooleanVariable.java index 1b18083ac6..8700aa55db 100644 --- a/ortools/com/google/ortools/sat/NotBooleanVariable.java +++ b/ortools/com/google/ortools/sat/NotBooleanVariable.java @@ -19,26 +19,26 @@ package com.google.ortools.sat; */ public class NotBooleanVariable implements ILiteral { public NotBooleanVariable(IntVar boolvar) { - boolvar_ = boolvar; + boolVar = boolvar; } - /** Internal: return the index in the literal in the underlying CpModelProto. */ + /** Internal: returns the index in the literal in the underlying CpModelProto. */ @Override public int getIndex() { - return -boolvar_.getIndex() - 1; + return -boolVar.getIndex() - 1; } /** Returns the negation of this literal. */ @Override public ILiteral not() { - return boolvar_; + return boolVar; } /** Returns a short string describing this literal. */ @Override public String getShortString() { - return "not(" + boolvar_.getShortString() + ")"; + return "not(" + boolVar.getShortString() + ")"; } - private IntVar boolvar_; + private final IntVar boolVar; } diff --git a/ortools/constraint_solver/sched_expr.cc b/ortools/constraint_solver/sched_expr.cc index 4eb5352451..99036bddd0 100644 --- a/ortools/constraint_solver/sched_expr.cc +++ b/ortools/constraint_solver/sched_expr.cc @@ -52,7 +52,7 @@ class IntervalVarStartExpr : public BaseIntExpr { void WhenRange(Demon* d) override { interval_->WhenStartRange(d); } std::string DebugString() const override { - return StringPrintf("start(%s)", interval_->DebugString().c_str()); + return absl::StrFormat("start(%s)", interval_->DebugString().c_str()); } void Accept(ModelVisitor* const visitor) const override { @@ -91,7 +91,7 @@ class IntervalVarEndExpr : public BaseIntExpr { void WhenRange(Demon* d) override { interval_->WhenEndRange(d); } std::string DebugString() const override { - return StringPrintf("end(%s)", interval_->DebugString().c_str()); + return absl::StrFormat("end(%s)", interval_->DebugString().c_str()); } void Accept(ModelVisitor* const visitor) const override { @@ -132,7 +132,7 @@ class IntervalVarDurationExpr : public BaseIntExpr { void WhenRange(Demon* d) override { interval_->WhenDurationRange(d); } std::string DebugString() const override { - return StringPrintf("duration(%s)", interval_->DebugString().c_str()); + return absl::StrFormat("duration(%s)", interval_->DebugString().c_str()); } void Accept(ModelVisitor* const visitor) const override { @@ -154,7 +154,7 @@ IntExpr* BuildStartExpr(IntervalVar* var) { IntExpr* const expr = s->RegisterIntExpr(s->RevAlloc(new IntervalVarStartExpr(var))); if (var->HasName()) { - expr->set_name(StringPrintf("start<%s>", var->name().c_str())); + expr->set_name(absl::StrFormat("start<%s>", var->name().c_str())); } return expr; } @@ -164,7 +164,7 @@ IntExpr* BuildDurationExpr(IntervalVar* var) { IntExpr* const expr = s->RegisterIntExpr(s->RevAlloc(new IntervalVarDurationExpr(var))); if (var->HasName()) { - expr->set_name(StringPrintf("duration<%s>", var->name().c_str())); + expr->set_name(absl::StrFormat("duration<%s>", var->name().c_str())); } return expr; } @@ -174,7 +174,7 @@ IntExpr* BuildEndExpr(IntervalVar* var) { IntExpr* const expr = s->RegisterIntExpr(s->RevAlloc(new IntervalVarEndExpr(var))); if (var->HasName()) { - expr->set_name(StringPrintf("end<%s>", var->name().c_str())); + expr->set_name(absl::StrFormat("end<%s>", var->name().c_str())); } return expr; } diff --git a/ortools/flatzinc/constraints.cc b/ortools/flatzinc/constraints.cc index 236bff27e9..fdf96b89bd 100644 --- a/ortools/flatzinc/constraints.cc +++ b/ortools/flatzinc/constraints.cc @@ -1404,7 +1404,7 @@ void ExtractIntEqReif(fz::SolverData* data, fz::Constraint* ct) { IntExpr* const left = data->GetOrCreateExpression(ct->arguments[0]); IntExpr* const right = data->GetOrCreateExpression(ct->arguments[1]); IntVar* tmp_var = nullptr; - bool tmp_neg = 0; + bool tmp_neg = false; bool success = false; if (FLAGS_fz_use_sat && solver->IsBooleanVar(left, &tmp_var, &tmp_neg) && solver->IsBooleanVar(right, &tmp_var, &tmp_neg)) { @@ -2386,7 +2386,7 @@ void ExtractIntNeReif(fz::SolverData* data, fz::Constraint* ct) { } else { IntExpr* const right = data->GetOrCreateExpression(ct->arguments[1]); IntVar* tmp_var = nullptr; - bool tmp_neg = 0; + bool tmp_neg = false; bool success = false; if (FLAGS_fz_use_sat && solver->IsBooleanVar(left, &tmp_var, &tmp_neg) && solver->IsBooleanVar(right, &tmp_var, &tmp_neg)) { diff --git a/ortools/flatzinc/constraints.h b/ortools/flatzinc/constraints.h index f3c42c319c..df127f9c57 100644 --- a/ortools/flatzinc/constraints.h +++ b/ortools/flatzinc/constraints.h @@ -23,7 +23,7 @@ namespace operations_research { namespace fz { -void ExtractConstraint(SolverData* data, Constraint* constraint); +void ExtractConstraint(SolverData* data, Constraint* ct); } // namespace fz } // namespace operations_research diff --git a/ortools/flatzinc/cp_model_fz_solver.cc b/ortools/flatzinc/cp_model_fz_solver.cc index 94e16fb6b8..d5fbc5c343 100644 --- a/ortools/flatzinc/cp_model_fz_solver.cc +++ b/ortools/flatzinc/cp_model_fz_solver.cc @@ -229,10 +229,10 @@ void CpModelProtoWithMapping::FillConstraint(const fz::Constraint& fz_ct, } else if (fz_ct.type == "bool_ne" || fz_ct.type == "bool_not" || fz_ct.type == "int_ne") { FillAMinusBInDomain({kint64min, -1, 1, kint64max}, fz_ct, ct); - } else if (fz_ct.type == "int_lin_eq") { + } else if (fz_ct.type == "int_lin_eq" || fz_ct.type == "bool_lin_eq") { const int64 rhs = fz_ct.arguments[2].values[0]; FillLinearConstraintWithGivenDomain({rhs, rhs}, fz_ct, ct); - } else if (fz_ct.type == "int_lin_le") { + } else if (fz_ct.type == "int_lin_le" || fz_ct.type == "bool_lin_le") { const int64 rhs = fz_ct.arguments[2].values[0]; FillLinearConstraintWithGivenDomain({kint64min, rhs}, fz_ct, ct); } else if (fz_ct.type == "int_lin_lt") { diff --git a/ortools/flatzinc/flatzinc_constraints.cc b/ortools/flatzinc/flatzinc_constraints.cc index e13cf671a9..c5f17260c2 100644 --- a/ortools/flatzinc/flatzinc_constraints.cc +++ b/ortools/flatzinc/flatzinc_constraints.cc @@ -106,8 +106,8 @@ class BooleanSumOdd : public Constraint { } std::string DebugString() const override { - return StringPrintf("BooleanSumOdd([%s])", - JoinDebugStringPtr(vars_, ", ").c_str()); + return absl::StrFormat("BooleanSumOdd([%s])", + JoinDebugStringPtr(vars_, ", ").c_str()); } void Accept(ModelVisitor* const visitor) const override { @@ -202,7 +202,7 @@ class VariableParity : public Constraint { } std::string DebugString() const override { - return StringPrintf("VarParity(%s, %d)", var_->DebugString().c_str(), odd_); + return absl::StrFormat("VarParity(%s, %d)", var_->DebugString().c_str(), odd_); } void Accept(ModelVisitor* const visitor) const override { @@ -465,7 +465,7 @@ class BooleanSumInRange : public Constraint { class StartVarDurationVarPerformedIntervalVar : public IntervalVar { public: - StartVarDurationVarPerformedIntervalVar(Solver* const s, IntVar* const start, + StartVarDurationVarPerformedIntervalVar(Solver* const s, IntVar* const var, IntVar* const duration, const std::string& name); ~StartVarDurationVarPerformedIntervalVar() override {} @@ -623,9 +623,9 @@ std::string StartVarDurationVarPerformedIntervalVar::DebugString() const { } else { out = "IntervalVar(start = "; } - StringAppendF(&out, "%s, duration = %s, performed = true)", - start_->DebugString().c_str(), - duration_->DebugString().c_str()); + absl::StrAppendFormat(&out, "%s, duration = %s, performed = true)", + start_->DebugString().c_str(), + duration_->DebugString().c_str()); return out; } diff --git a/ortools/flatzinc/model.cc b/ortools/flatzinc/model.cc index a95669b9e0..b7f8d537c9 100644 --- a/ortools/flatzinc/model.cc +++ b/ortools/flatzinc/model.cc @@ -377,7 +377,7 @@ std::string Domain::DebugString() const { } else if (values.size() == 1) { return absl::StrCat(values.back()); } else { - return StringPrintf("[%s]", absl::StrJoin(values, ", ").c_str()); + return absl::StrFormat("[%s]", absl::StrJoin(values, ", ").c_str()); } } @@ -452,9 +452,9 @@ std::string Argument::DebugString() const { return absl::StrFormat("[%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d]", values[0], values[1]); case INT_LIST: - return StringPrintf("[%s]", absl::StrJoin(values, ", ").c_str()); + return absl::StrFormat("[%s]", absl::StrJoin(values, ", ").c_str()); case DOMAIN_LIST: - return StringPrintf("[%s]", JoinDebugString(domains, ", ").c_str()); + return absl::StrFormat("[%s]", JoinDebugString(domains, ", ").c_str()); case INT_VAR_REF: return variables[0]->name; case INT_VAR_REF_ARRAY: { @@ -542,7 +542,7 @@ bool Argument::Contains(int64 value) const { } default: { LOG(FATAL) << "Cannot call Contains() on " << DebugString(); - return 0; + return false; } } } @@ -620,7 +620,7 @@ std::string IntegerVariable::DebugString() const { if (!domain.is_interval && domain.values.size() == 1) { return absl::StrFormat("% " GG_LL_FORMAT "d", domain.values.back()); } else { - return StringPrintf( + return absl::StrFormat( "%s(%s%s%s)%s", name.c_str(), domain.DebugString().c_str(), temporary ? ", temporary" : "", defining_constraint != nullptr ? ", target_variable" : "", @@ -638,11 +638,11 @@ std::string Constraint::DebugString() const { : "[removed during presolve]"); const std::string target = target_variable != nullptr - ? StringPrintf(" => %s", target_variable->name.c_str()) + ? absl::StrFormat(" => %s", target_variable->name.c_str()) : ""; - return StringPrintf("%s(%s)%s %s %s", type.c_str(), - JoinDebugString(arguments, ", ").c_str(), target.c_str(), - strong.c_str(), presolve_status_str.c_str()); + return absl::StrFormat("%s(%s)%s %s %s", type.c_str(), + JoinDebugString(arguments, ", ").c_str(), target.c_str(), + strong.c_str(), presolve_status_str.c_str()); } void Constraint::RemoveArg(int arg_pos) { @@ -777,14 +777,14 @@ void Annotation::AppendAllIntegerVariables( std::string Annotation::DebugString() const { switch (type) { case ANNOTATION_LIST: { - return StringPrintf("[%s]", JoinDebugString(annotations, ", ").c_str()); + return absl::StrFormat("[%s]", JoinDebugString(annotations, ", ").c_str()); } case IDENTIFIER: { return id; } case FUNCTION_CALL: { - return StringPrintf("%s(%s)", id.c_str(), - JoinDebugString(annotations, ", ").c_str()); + return absl::StrFormat("%s(%s)", id.c_str(), + JoinDebugString(annotations, ", ").c_str()); } case INTERVAL: { return absl::StrFormat("%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d", @@ -805,7 +805,7 @@ std::string Annotation::DebugString() const { return result; } case STRING_VALUE: { - return StringPrintf("\"%s\"", string_value.c_str()); + return absl::StrFormat("\"%s\"", string_value.c_str()); } } LOG(FATAL) << "Unhandled case in DebugString " << static_cast(type); @@ -850,11 +850,11 @@ SolutionOutputSpecs SolutionOutputSpecs::VoidOutput() { std::string SolutionOutputSpecs::DebugString() const { if (variable != nullptr) { - return StringPrintf("output_var(%s)", variable->name.c_str()); + return absl::StrFormat("output_var(%s)", variable->name.c_str()); } else { - return StringPrintf("output_array([%s] [%s])", - JoinDebugString(bounds, ", ").c_str(), - JoinNameFieldPtr(flat_variables, ", ").c_str()); + return absl::StrFormat("output_array([%s] [%s])", + JoinDebugString(bounds, ", ").c_str(), + JoinNameFieldPtr(flat_variables, ", ").c_str()); } } @@ -920,27 +920,27 @@ void Model::Maximize(IntegerVariable* obj, } std::string Model::DebugString() const { - std::string output = StringPrintf("Model %s\nVariables\n", name_.c_str()); + std::string output = absl::StrFormat("Model %s\nVariables\n", name_.c_str()); for (int i = 0; i < variables_.size(); ++i) { - StringAppendF(&output, " %s\n", variables_[i]->DebugString().c_str()); + absl::StrAppendFormat(&output, " %s\n", variables_[i]->DebugString().c_str()); } output.append("Constraints\n"); for (int i = 0; i < constraints_.size(); ++i) { if (constraints_[i] != nullptr) { - StringAppendF(&output, " %s\n", constraints_[i]->DebugString().c_str()); + absl::StrAppendFormat(&output, " %s\n", constraints_[i]->DebugString().c_str()); } } if (objective_ != nullptr) { - StringAppendF(&output, "%s %s\n %s\n", maximize_ ? "Maximize" : "Minimize", - objective_->name.c_str(), - JoinDebugString(search_annotations_, ", ").c_str()); + absl::StrAppendFormat(&output, "%s %s\n %s\n", maximize_ ? "Maximize" : "Minimize", + objective_->name.c_str(), + JoinDebugString(search_annotations_, ", ").c_str()); } else { - StringAppendF(&output, "Satisfy\n %s\n", - JoinDebugString(search_annotations_, ", ").c_str()); + absl::StrAppendFormat(&output, "Satisfy\n %s\n", + JoinDebugString(search_annotations_, ", ").c_str()); } output.append("Output\n"); for (int i = 0; i < output_.size(); ++i) { - StringAppendF(&output, " %s\n", output_[i].DebugString().c_str()); + absl::StrAppendFormat(&output, " %s\n", output_[i].DebugString().c_str()); } return output; diff --git a/ortools/flatzinc/model.h b/ortools/flatzinc/model.h index 7ee499b6e9..d7d2076c28 100644 --- a/ortools/flatzinc/model.h +++ b/ortools/flatzinc/model.h @@ -77,7 +77,7 @@ struct Domain { // Various inclusion tests on a domain. bool Contains(int64 value) const; - bool OverlapsIntList(const std::vector& values) const; + bool OverlapsIntList(const std::vector& vec) const; bool OverlapsIntInterval(int64 lb, int64 ub) const; bool OverlapsDomain(const Domain& other) const; @@ -176,7 +176,7 @@ struct Argument { bool HasOneValue() const; // Returns the value of the argument. Does DCHECK(HasOneValue()). int64 Value() const; - // Returns true if if it an integer list, or an array of integer + // Returns true if it an integer list, or an array of integer // variables (or domain) each having only one value. bool IsArrayOfValues() const; // Returns true if the argument is an integer value, an integer @@ -271,7 +271,7 @@ struct Annotation { static Annotation Interval(int64 interval_min, int64 interval_max); static Annotation IntegerValue(int64 value); static Annotation Variable(IntegerVariable* const var); - static Annotation VariableList(std::vector vars); + static Annotation VariableList(std::vector variables); static Annotation String(const std::string& str); std::string DebugString() const; @@ -338,14 +338,12 @@ class Model { // The objects returned by AddVariable(), AddConstant(), and AddConstraint() // are owned by the model and will remain live for its lifetime. IntegerVariable* AddVariable(const std::string& name, const Domain& domain, - bool temporary); + bool defined); IntegerVariable* AddConstant(int64 value); // Creates and add a constraint to the model. - // The parameter strong is an indication from the model that prefers stronger - // (and more expensive version of the propagator). - void AddConstraint(const std::string& type, std::vector arguments, - bool strong, IntegerVariable* target_variable); - void AddConstraint(const std::string& type, std::vector arguments); + void AddConstraint(const std::string& id, std::vector arguments, + bool is_domain, IntegerVariable* defines); + void AddConstraint(const std::string& id, std::vector arguments); void AddOutput(SolutionOutputSpecs output); // Set the search annotations and the objective: either simply satisfy the diff --git a/ortools/flatzinc/parser.tab.cc b/ortools/flatzinc/parser.tab.cc index 126ca3c0e2..245bcd5f9d 100644 --- a/ortools/flatzinc/parser.tab.cc +++ b/ortools/flatzinc/parser.tab.cc @@ -1486,7 +1486,7 @@ yyreduce: for (int i = 0; i < num_vars; ++i) { const std::string var_name = - StringPrintf("%s[%d]", identifier.c_str(), i + 1); + absl::StrFormat("%s[%d]", identifier.c_str(), i + 1); if (assignments == nullptr) { vars[i] = model->AddVariable(var_name, domain, introduced); } else if (assignments->variables[i] == nullptr) { diff --git a/ortools/flatzinc/parser.yy b/ortools/flatzinc/parser.yy index c9b40e2c50..5c3be52092 100644 --- a/ortools/flatzinc/parser.yy +++ b/ortools/flatzinc/parser.yy @@ -295,7 +295,7 @@ variable_or_constant_declaration: std::vector vars(num_vars, nullptr); for (int i = 0; i < num_vars; ++i) { - const std::string var_name = StringPrintf("%s[%d]", identifier.c_str(), i + 1); + const std::string var_name = absl::StrFormat("%s[%d]", identifier.c_str(), i + 1); if (assignments == nullptr) { vars[i] = model->AddVariable(var_name, domain, introduced); } else if (assignments->variables[i] == nullptr) { diff --git a/ortools/flatzinc/parser_util.cc b/ortools/flatzinc/parser_util.cc index 070fa66aea..7658c90635 100644 --- a/ortools/flatzinc/parser_util.cc +++ b/ortools/flatzinc/parser_util.cc @@ -19,6 +19,7 @@ #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" +#include "ortools/base/mathutil.h" #include "ortools/base/stl_util.h" #include "ortools/flatzinc/model.h" #include "ortools/flatzinc/parser.tab.hh" @@ -64,7 +65,7 @@ bool AllDomainsHaveOneValue(const std::vector& domains) { int64 ConvertAsIntegerOrDie(double d) { const double rounded = std::round(d); const int64 i = static_cast(rounded); - CHECK_NEAR(d, i, 1e-9); + CHECK_LE(std::abs(static_cast(i) - rounded), 1e-9); return i; } diff --git a/ortools/flatzinc/parser_util.h b/ortools/flatzinc/parser_util.h index 4640a58508..57b30316e9 100644 --- a/ortools/flatzinc/parser_util.h +++ b/ortools/flatzinc/parser_util.h @@ -23,8 +23,6 @@ #include "ortools/base/map_util.h" #include "ortools/flatzinc/model.h" -using operations_research::StringPrintf; - namespace operations_research { namespace fz { // This is the context used during parsing. diff --git a/ortools/flatzinc/presolve.cc b/ortools/flatzinc/presolve.cc index 1e7a2458d2..81d8e91b50 100644 --- a/ortools/flatzinc/presolve.cc +++ b/ortools/flatzinc/presolve.cc @@ -1200,7 +1200,7 @@ Presolver::RuleStatus Presolver::CreateLinearTarget(Constraint* ct, ct->arguments[1].variables[var_index]->defining_constraint == nullptr && !ct->arguments[1].variables[var_index]->domain.HasOneValue()) { // Rule 1. - StringAppendF(log, "mark variable index %i as target", var_index); + absl::StrAppendFormat(log, "mark variable index %i as target", var_index); IntegerVariable* const var = ct->arguments[1].variables[var_index]; var->defining_constraint = ct; ct->target_variable = var; @@ -1302,8 +1302,8 @@ Presolver::RuleStatus Presolver::PresolveArrayIntElement(Constraint* ct, } const std::string after = ct->arguments[2].Var()->DebugString(); if (before != after) { - log->append(StringPrintf(", reduce target variable from %s to %s", - before.c_str(), after.c_str())); + log->append(absl::StrFormat(", reduce target variable from %s to %s", + before.c_str(), after.c_str())); ct->presolve_propagation_done = true; return CONSTRAINT_REWRITTEN; } @@ -2015,9 +2015,8 @@ Presolver::RuleStatus Presolver::PresolveSimplifyElement(Constraint* ct, to_keep)) { MarkChangedVariable(ct->arguments[0].Var()); } - log->append( - StringPrintf("reduce index domain to %s", - ct->arguments[0].Var()->DebugString().c_str())); + log->append(absl::StrFormat("reduce index domain to %s", + ct->arguments[0].Var()->DebugString().c_str())); } } } @@ -2151,8 +2150,8 @@ Presolver::RuleStatus Presolver::PresolveSimplifyExprElement(Constraint* ct, if (ct->arguments[0].Var()->domain.IntersectWithListOfIntegers(to_keep)) { MarkChangedVariable(ct->arguments[0].Var()); } - log->append(StringPrintf("reduce index domain to %s", - ct->arguments[0].Var()->DebugString().c_str())); + log->append(absl::StrFormat("reduce index domain to %s", + ct->arguments[0].Var()->DebugString().c_str())); } } @@ -2220,7 +2219,8 @@ Presolver::RuleStatus Presolver::PropagateReifiedComparisons(Constraint* ct, state = (a >= b); } if (state != 2) { - StringAppendF(log, "assign boolvar to %s", state == 0 ? "false" : "true"); + absl::StrAppendFormat(log, "assign boolvar to %s", + state == 0 ? "false" : "true"); IntersectVarWithSingleton(ct->arguments[2].Var(), state); return CONSTRAINT_ALWAYS_TRUE; } @@ -2309,8 +2309,8 @@ Presolver::RuleStatus Presolver::PropagateReifiedComparisons(Constraint* ct, } } if (state != 2) { - StringAppendF(log, "assign boolvar to %s", - state == 0 ? "false" : "true"); + absl::StrAppendFormat(log, "assign boolvar to %s", + state == 0 ? "false" : "true"); IntersectVarWithSingleton(ct->arguments[2].Var(), state); return CONSTRAINT_ALWAYS_TRUE; } @@ -2357,7 +2357,8 @@ Presolver::RuleStatus Presolver::PropagateReifiedComparisons(Constraint* ct, } } if (state != 2) { - StringAppendF(log, "assign boolvar to %s", state == 0 ? "false" : "true"); + absl::StrAppendFormat(log, "assign boolvar to %s", + state == 0 ? "false" : "true"); IntersectVarWithSingleton(ct->arguments[2].Var(), state); return CONSTRAINT_ALWAYS_TRUE; } @@ -2816,7 +2817,7 @@ Presolver::RuleStatus Presolver::PresolveTableInt(Constraint* ct, } // Removed invalid tuples. if (ignored_tuples > 0) { - log->append(StringPrintf("removed %i tuples", ignored_tuples)); + log->append(absl::StrFormat("removed %i tuples", ignored_tuples)); ct->arguments[1].values.swap(new_tuples); return CONSTRAINT_REWRITTEN; } @@ -2936,8 +2937,9 @@ Presolver::RuleStatus Presolver::PresolveRegular(Constraint* ct, domain.IntersectWithListOfIntegers(to_keep); MarkChangedVariable(vars[time]); if (HASVLOG) { - StringAppendF(log, "reduce domain of variable %d from %s to %s; ", time, - before.c_str(), vars[time]->DebugString().c_str()); + absl::StrAppendFormat(log, + "reduce domain of variable %d from %s to %s; ", + time, before.c_str(), vars[time]->DebugString().c_str()); } } } @@ -3215,11 +3217,11 @@ CliqueResponse StoreClique(const std::vector& vec, std::vector* out) { void PrintGraph(const std::vector>& neighbors, int num_variables) { for (int i = 0; i < num_variables; ++i) { - std::string out = StringPrintf("%i : [", i); + std::string out = absl::StrFormat("%i : [", i); bool found_one = false; for (int j = 0; j < num_variables; ++j) { if (neighbors[i][j]) { - StringAppendF(&out, "%s %i", found_one ? "," : "", j); + absl::StrAppendFormat(&out, "%s %i", found_one ? "," : "", j); found_one = true; } } diff --git a/ortools/flatzinc/reporting.h b/ortools/flatzinc/reporting.h index 6cb1114c00..b943ebd250 100644 --- a/ortools/flatzinc/reporting.h +++ b/ortools/flatzinc/reporting.h @@ -134,7 +134,7 @@ class MonoThreadReporting : public SearchReportingInterface { const std::string& solution_string) override; void OnOptimizeSolution(int thread_id, int64 value, const std::string& solution_string) override; - void Log(int thread_id, const std::string& message) const override; + void Log(int thread_id, const std::string& final_output) const override; void Print(int thread_id, const std::string& final_output) const override; bool ShouldFinish() const override; void OnSearchEnd(int thread_id, bool interrupted) override; @@ -174,12 +174,12 @@ class MultiThreadReporting : public SearchReportingInterface { void OnOptimizeSolution(int thread_id, int64 value, const std::string& solution_string) override; void Log(int thread_id, const std::string& message) const override; - void Print(int thread_id, const std::string& final_out) const override; + void Print(int thread_id, const std::string& message) const override; bool ShouldFinish() const override; void OnSearchEnd(int thread_id, bool interrupted) override; int64 BestSolution() const override; OptimizeVar* CreateObjective(Solver* s, bool maximize, IntVar* var, - int64 step, int w) const override; + int64 step, int thread_id) const override; SearchLimit* CreateLimit(Solver* s, int thread_id) const override; bool Interrupted() const override; diff --git a/ortools/flatzinc/sat_constraint.cc b/ortools/flatzinc/sat_constraint.cc index 144ce61f09..10899bf20e 100644 --- a/ortools/flatzinc/sat_constraint.cc +++ b/ortools/flatzinc/sat_constraint.cc @@ -165,7 +165,7 @@ class SatPropagator : public Constraint { sat::SatSolver* sat() { return &sat_; } std::string DebugString() const override { - return StringPrintf("SatConstraint(%d variables)", sat_.NumVariables()); + return absl::StrFormat("SatConstraint(%d variables)", sat_.NumVariables()); } void Accept(ModelVisitor* visitor) const override { diff --git a/ortools/flatzinc/solver.cc b/ortools/flatzinc/solver.cc index d2efcebbad..e08171e38e 100644 --- a/ortools/flatzinc/solver.cc +++ b/ortools/flatzinc/solver.cc @@ -93,8 +93,8 @@ std::string Solver::SolutionString(const SolutionOutputSpecs& output) const { if (output.variable != nullptr) { const int64 value = SolutionValue(output.variable); if (output.display_as_boolean) { - return StringPrintf("%s = %s;", output.name.c_str(), - value == 1 ? "true" : "false"); + return absl::StrFormat("%s = %s;", output.name.c_str(), + value == 1 ? "true" : "false"); } else { return absl::StrFormat("%s = %" GG_LL_FORMAT "d;", output.name.c_str(), value); @@ -102,7 +102,7 @@ std::string Solver::SolutionString(const SolutionOutputSpecs& output) const { } else { const int bound_size = output.bounds.size(); std::string result = - StringPrintf("%s = array%dd(", output.name.c_str(), bound_size); + absl::StrFormat("%s = array%dd(", output.name.c_str(), bound_size); for (int i = 0; i < bound_size; ++i) { if (output.bounds[i].max_value != 0) { result.append(absl::StrFormat( @@ -116,7 +116,7 @@ std::string Solver::SolutionString(const SolutionOutputSpecs& output) const { for (int i = 0; i < output.flat_variables.size(); ++i) { const int64 value = SolutionValue(output.flat_variables[i]); if (output.display_as_boolean) { - result.append(StringPrintf(value ? "true" : "false")); + result.append(absl::StrFormat(value ? "true" : "false")); } else { absl::StrAppend(&result, value); } @@ -166,8 +166,8 @@ struct ConstraintsWithRequiredVariables { } std::string DebugString() const { - return StringPrintf("Ctio(%s, %d, deps_size = %lu)", ct->type.c_str(), - index, required.size()); + return absl::StrFormat("Ctio(%s, %d, deps_size = %lu)", ct->type.c_str(), + index, required.size()); } }; @@ -714,7 +714,7 @@ void Solver::ReportInconsistentModel(const Model& model, FlatzincParameters p, std::string solver_status = "%% name, status, obj, solns, s_time, b_time, br, " "fails, cts, demon, delayed, mem, search\n"; - StringAppendF( + absl::StrAppendFormat( &solver_status, "%%%% csv: %s, **unsat**, , 0, 0 ms, 0 ms, 0, 0, 0, 0, 0, %s, free", model.name().c_str(), MemoryUsage().c_str()); @@ -865,9 +865,9 @@ void Solver::Solve(FlatzincParameters p, SearchReportingInterface* report) { solver_status.append(absl::StrFormat( "%%%% solve time: %" GG_LL_FORMAT "d ms\n", solve_time)); solver_status.append( - StringPrintf("%%%% solutions: %d\n", num_solutions)); - solver_status.append(StringPrintf("%%%% constraints: %d\n", - solver_->constraints())); + absl::StrFormat("%%%% solutions: %d\n", num_solutions)); + solver_status.append(absl::StrFormat("%%%% constraints: %d\n", + solver_->constraints())); solver_status.append(absl::StrFormat( "%%%% normal propagations: %" GG_LL_FORMAT "d\n", solver_->demon_runs(operations_research::Solver::NORMAL_PRIORITY))); @@ -880,8 +880,8 @@ void Solver::Solve(FlatzincParameters p, SearchReportingInterface* report) { solver_status.append( absl::StrFormat("%%%% failures: %" GG_LL_FORMAT "d\n", solver_->failures())); - solver_status.append(StringPrintf("%%%% memory: %s\n", - MemoryUsage().c_str())); + solver_status.append( + absl::StrFormat("%%%% memory: %s\n", MemoryUsage().c_str())); const int64 best = report->BestSolution(); if (model_.objective() != nullptr) { if (!model_.maximize() && num_solutions > 0) { @@ -899,8 +899,8 @@ void Solver::Solve(FlatzincParameters p, SearchReportingInterface* report) { const std::string default_search_stats = DefaultPhaseStatString(default_phase_); if (!default_search_stats.empty()) { - solver_status.append(StringPrintf("%%%% free search stats: %s\n", - default_search_stats.c_str())); + solver_status.append(absl::StrFormat("%%%% free search stats: %s\n", + default_search_stats.c_str())); } } diff --git a/ortools/flatzinc/solver_util.cc b/ortools/flatzinc/solver_util.cc index 91a3668934..70995be375 100644 --- a/ortools/flatzinc/solver_util.cc +++ b/ortools/flatzinc/solver_util.cc @@ -189,11 +189,11 @@ std::string MemoryUsage() { static const int64 kGigaByte = kMegaByte * kKiloByte; const int64 memory_usage = operations_research::Solver::MemoryUsage(); if (memory_usage > kDisplayThreshold * kGigaByte) { - return StringPrintf("%.2lf GB", memory_usage * 1.0 / kGigaByte); + return absl::StrFormat("%.2f GB", memory_usage * 1.0 / kGigaByte); } else if (memory_usage > kDisplayThreshold * kMegaByte) { - return StringPrintf("%.2lf MB", memory_usage * 1.0 / kMegaByte); + return absl::StrFormat("%.2f MB", memory_usage * 1.0 / kMegaByte); } else if (memory_usage > kDisplayThreshold * kKiloByte) { - return StringPrintf("%2lf KB", memory_usage * 1.0 / kKiloByte); + return absl::StrFormat("%2f KB", memory_usage * 1.0 / kKiloByte); } else { return absl::StrCat(memory_usage); } diff --git a/ortools/glop/initial_basis.cc b/ortools/glop/initial_basis.cc index 4498f8d558..dd6bc14ca5 100644 --- a/ortools/glop/initial_basis.cc +++ b/ortools/glop/initial_basis.cc @@ -95,6 +95,16 @@ void InitialBasis::CompleteBixbyBasis(ColIndex num_cols, } } +void InitialBasis::GetPrimalMarosBasis(ColIndex num_cols, + RowToColMapping* basis) { + return GetMarosBasis(num_cols, basis); +} + +void InitialBasis::GetDualMarosBasis(ColIndex num_cols, + RowToColMapping* basis) { + return GetMarosBasis(num_cols, basis); +} + void InitialBasis::CompleteTriangularPrimalBasis(ColIndex num_cols, RowToColMapping* basis) { return CompleteTriangularBasis(num_cols, basis); @@ -188,6 +198,156 @@ void InitialBasis::CompleteTriangularBasis(ColIndex num_cols, } } +int InitialBasis::GetMarosPriority(ColIndex col) const { + // Priority values for columns as defined in Maros's book. + switch (variable_type_[col]) { + case VariableType::UNCONSTRAINED: + return 3; + case VariableType::LOWER_BOUNDED: + return 2; + case VariableType::UPPER_BOUNDED: + return 2; + case VariableType::UPPER_AND_LOWER_BOUNDED: + return 1; + case VariableType::FIXED_VARIABLE: + return 0; + } +} + +int InitialBasis::GetMarosPriority(RowIndex row) const { + // Priority values for rows are equal to + // 3 - row priority values as defined in Maros's book + ColIndex slack_index(RowToColIndex(row) + matrix_.num_cols() - + RowToColIndex(matrix_.num_rows())); + + return GetMarosPriority(slack_index); +} + +template +void InitialBasis::GetMarosBasis(ColIndex num_cols, RowToColMapping* basis) { + VLOG(1) << "Starting Maros crash procedure."; + + // Initialize basis to the all-slack basis. + const RowIndex num_rows = matrix_.num_rows(); + const ColIndex first_slack = num_cols - RowToColIndex(num_rows); + DCHECK_EQ(num_rows, basis->size()); + basis->resize(num_rows); + for (RowIndex row(0); row < num_rows; row++) { + (*basis)[row] = first_slack + RowToColIndex(row); + } + + // Initialize the set of available rows and columns. + DenseBooleanRow available(num_cols, true); + for (ColIndex col(0); col < first_slack; ++col) { + if (variable_type_[col] == VariableType::FIXED_VARIABLE || + (only_allow_zero_cost_column && objective_[col] != 0.0)) { + available[col] = false; + } + } + for (ColIndex col = first_slack; col < num_cols; ++col) { + if (variable_type_[col] == VariableType::UNCONSTRAINED) { + available[col] = false; + } + } + + // Initialize the residual non-zero pattern for the active part of the matrix. + MatrixNonZeroPattern residual_pattern; + residual_pattern.Reset(num_rows, num_cols); + for (ColIndex col(0); col < first_slack; ++col) { + for (const SparseColumn::Entry e : matrix_.column(col)) { + if (available[RowToColIndex(e.row())] && available[col]) { + residual_pattern.AddEntry(e.row(), col); + } + } + } + + // Go over residual pattern and mark rows as unavailable. + for (RowIndex row(0); row < num_rows; row++) { + if (residual_pattern.RowDegree(row) == 0) { + available[RowToColIndex(row) + first_slack] = false; + } + } + + for (;;) { + // Make row selection by the Row Priority Function (RPF) from Maros's + // book. + int max_row_priority_function = std::numeric_limits::min(); + RowIndex max_rpf_row = kInvalidRow; + for (RowIndex row(0); row < num_rows; row++) { + if (available[RowToColIndex(row) + first_slack]) { + const int rpf = + 10 * (3 - GetMarosPriority(row)) - residual_pattern.RowDegree(row); + if (rpf > max_row_priority_function) { + max_row_priority_function = rpf; + max_rpf_row = row; + } + } + } + if (max_rpf_row == kInvalidRow) break; + + // Trace row for nonzero entries and pick one with best Column Priority + // Function (cpf). + const Fractional kStabilityThreshold = 1e-3; + ColIndex max_cpf_col(kInvalidCol); + int max_col_priority_function(std::numeric_limits::min()); + Fractional pivot_absolute_value = 0.0; + for (const ColIndex col : residual_pattern.RowNonZero(max_rpf_row)) { + if (!available[col]) continue; + const int cpf = + 10 * GetMarosPriority(col) - residual_pattern.ColDegree(col); + if (cpf > max_col_priority_function) { + // Make sure that the pivotal entry is not too small in magnitude. + Fractional max_magnitude = 0; + pivot_absolute_value = 0.0; + const SparseColumn& column_values = matrix_.column(col); + for (const SparseColumn::Entry e : column_values) { + const Fractional absolute_value = std::fabs(e.coefficient()); + if (e.row() == max_rpf_row) pivot_absolute_value = absolute_value; + max_magnitude = std::max(max_magnitude, absolute_value); + } + if (pivot_absolute_value >= kStabilityThreshold * max_magnitude) { + max_col_priority_function = cpf; + max_cpf_col = col; + } + } + } + + if (max_cpf_col == kInvalidCol) { + available[RowToColIndex(max_rpf_row) + first_slack] = false; + continue; + } + + // Ensure that the row leaving the basis has a lower priority than the + // column entering the basis. If the best column is not good enough mark + // row as unavailable and choose another one. + const int row_priority = GetMarosPriority(max_rpf_row); + const int column_priority = GetMarosPriority(max_cpf_col); + if (row_priority >= column_priority) { + available[RowToColIndex(max_rpf_row) + first_slack] = false; + continue; + } + + // Use this candidate column in the basis. Update residual pattern and row + // counts list. + (*basis)[max_rpf_row] = max_cpf_col; + + VLOG(2) << "Slack variable " << max_rpf_row << " replaced by column " + << max_cpf_col + << ". Pivot coefficient magnitude: " << pivot_absolute_value << "."; + + available[max_cpf_col] = false; + available[first_slack + RowToColIndex(max_rpf_row)] = false; + + // Maintain the invariant that all the still available columns will have + // zeros on the rows we already replaced. This ensures the lower-triangular + // nature (after permutation) of the returned basis. + residual_pattern.DeleteRowAndColumn(max_rpf_row, max_cpf_col); + for (const ColIndex col : residual_pattern.RowNonZero(max_rpf_row)) { + available[col] = false; + } + } +} + void InitialBasis::ComputeCandidates(ColIndex num_cols, std::vector* candidates) { candidates->clear(); @@ -220,9 +380,6 @@ int InitialBasis::GetColumnCategory(ColIndex col) const { return 4; case VariableType::FIXED_VARIABLE: return 5; - default: - LOG(DFATAL) << "Column " << col << " has no meaningful type."; - return 6; } } diff --git a/ortools/glop/initial_basis.h b/ortools/glop/initial_basis.h index 62de54a7db..d8c18a4f5a 100644 --- a/ortools/glop/initial_basis.h +++ b/ortools/glop/initial_basis.h @@ -65,22 +65,34 @@ class InitialBasis { void CompleteTriangularPrimalBasis(ColIndex num_cols, RowToColMapping* basis); void CompleteTriangularDualBasis(ColIndex num_cols, RowToColMapping* basis); + // Use Maros's LTSF crash from the book "Computational Techniques of the + // Simplex Method". Unlike the other crashes this does not use the initial + // content of the basis parameter. + void GetPrimalMarosBasis(ColIndex num_cols, RowToColMapping* basis); + void GetDualMarosBasis(ColIndex num_cols, RowToColMapping* basis); + // Visible for testing. Computes a list of candidate column indices out of the // fist num_candidate_columns of A and sorts them using the // bixby_column_comparator_. This also fills max_scaled_abs_cost_. - void ComputeCandidates(ColIndex num_candidate_columns, - std::vector* candidates); + void ComputeCandidates(ColIndex num_cols, std::vector* candidates); private: // Internal implementation of the Primal/Dual CompleteTriangularBasis(). template void CompleteTriangularBasis(ColIndex num_cols, RowToColMapping* basis); + template + void GetMarosBasis(ColIndex num_cols, RowToColMapping* basis); + // Returns an integer representing the order (the lower the better) // between column categories (known as C2, C3 or C4 in the paper). // Also returns a greater index for fixed columns. int GetColumnCategory(ColIndex col) const; + // Row and column priorities for Maros crash. + int GetMarosPriority(RowIndex row) const; + int GetMarosPriority(ColIndex col) const; + // Returns the penalty (the lower the better) of a column. This is 'q_j' for a // column 'j' in the paper. Fractional GetColumnPenalty(ColIndex col) const; diff --git a/ortools/glop/lp_solver.cc b/ortools/glop/lp_solver.cc index d8bc35b91b..1b3690ff81 100644 --- a/ortools/glop/lp_solver.cc +++ b/ortools/glop/lp_solver.cc @@ -23,6 +23,8 @@ #include "ortools/base/timer.h" #include "ortools/base/join.h" +#include "ortools/base/memory.h" +#include "ortools/base/stringprintf.h" #include "ortools/base/strutil.h" #include "ortools/glop/preprocessor.h" #include "ortools/glop/status.h" @@ -87,7 +89,7 @@ void DumpLinearProgramIfRequiredByFlags(const LinearProgram& linear_program, } const int file_num = FLAGS_lp_dump_file_number >= 0 ? FLAGS_lp_dump_file_number : num; - StringAppendF(&filename, "-%06d.pb", file_num); + absl::StrAppendFormat(&filename, "-%06d.pb", file_num); const std::string filespec = absl::StrCat(FLAGS_lp_dump_dir, "/", filename); MPModelProto proto; LinearProgramToMPModelProto(linear_program, &proto); @@ -241,7 +243,7 @@ void LPSolver::SetInitialBasis( } } if (revised_simplex_ == nullptr) { - revised_simplex_.reset(new RevisedSimplex()); + revised_simplex_ = absl::make_unique(); } revised_simplex_->LoadStateForNextSolve(state); if (parameters_.use_preprocessing()) { @@ -288,11 +290,11 @@ ProblemStatus LPSolver::LoadAndVerifySolution(const LinearProgram& lp, const Fractional primal_objective_value = ComputeObjective(lp); const Fractional dual_objective_value = ComputeDualObjective(lp); VLOG(1) << "Primal objective (before moving primal/dual values) = " - << StringPrintf("%.15E", - ProblemObjectiveValue(lp, primal_objective_value)); + << absl::StrFormat("%.15E", + ProblemObjectiveValue(lp, primal_objective_value)); VLOG(1) << "Dual objective (before moving primal/dual values) = " - << StringPrintf("%.15E", - ProblemObjectiveValue(lp, dual_objective_value)); + << absl::StrFormat("%.15E", + ProblemObjectiveValue(lp, dual_objective_value)); // Eventually move the primal/dual values inside their bounds. if (status == ProblemStatus::OPTIMAL && @@ -304,7 +306,7 @@ ProblemStatus LPSolver::LoadAndVerifySolution(const LinearProgram& lp, // The reported objective to the user. problem_objective_value_ = ProblemObjectiveValue(lp, ComputeObjective(lp)); VLOG(1) << "Primal objective (after moving primal/dual values) = " - << StringPrintf("%.15E", problem_objective_value_); + << absl::StrFormat("%.15E", problem_objective_value_); ComputeReducedCosts(lp); ComputeConstraintActivities(lp); @@ -524,7 +526,7 @@ void LPSolver::RunRevisedSimplexIfNeeded(ProblemSolution* solution, current_linear_program_.ClearTransposeMatrix(); if (solution->status != ProblemStatus::INIT) return; if (revised_simplex_ == nullptr) { - revised_simplex_.reset(new RevisedSimplex()); + revised_simplex_ = absl::make_unique(); } revised_simplex_->SetParameters(parameters_); if (revised_simplex_->Solve(current_linear_program_, time_limit).ok()) { diff --git a/ortools/glop/lp_solver.h b/ortools/glop/lp_solver.h index 829f8a0af7..f9b963fe92 100644 --- a/ortools/glop/lp_solver.h +++ b/ortools/glop/lp_solver.h @@ -152,7 +152,7 @@ class LPSolver { // Resizes all the solution vectors to the given sizes. // This is used in case of error to make sure all the getter functions will // not crash when given row/col inside the initial linear program dimension. - void ResizeSolution(RowIndex row, ColIndex col); + void ResizeSolution(RowIndex num_rows, ColIndex num_cols); // Make sure the primal and dual values are within their bounds in order to // have a strong guarantee on the optimal solution. See diff --git a/ortools/glop/markowitz.cc b/ortools/glop/markowitz.cc index fa2d1e5622..7bb8243adc 100644 --- a/ortools/glop/markowitz.cc +++ b/ortools/glop/markowitz.cc @@ -79,8 +79,9 @@ Status Markowitz::ComputeRowAndColumnPermutation(const MatrixView& basis_matrix, if (pivot_row == kInvalidRow || pivot_col == kInvalidCol || std::abs(pivot_coefficient) <= singularity_threshold) { GLOP_RETURN_AND_LOG_ERROR( - Status::ERROR_LU, StringPrintf("The matrix is singular! pivot = %E", - pivot_coefficient)); + Status::ERROR_LU, + absl::StrFormat("The matrix is singular! pivot = %E", + pivot_coefficient)); } DCHECK_EQ((*row_perm)[pivot_row], kInvalidRow); DCHECK_EQ((*col_perm)[pivot_col], kInvalidCol); diff --git a/ortools/glop/parameters.proto b/ortools/glop/parameters.proto index fa46c3224a..486a31ec74 100644 --- a/ortools/glop/parameters.proto +++ b/ortools/glop/parameters.proto @@ -79,6 +79,11 @@ message GlopParameters { // GLPK uses by default. Both algorithm produce a triangular initial basis, // however the heuristics used are not exactly the same. TRIANGULAR = 2; + + // Use a version of Maros's triangular feasibility crash + // https://books.google.fr/books?isbn=1461502578 + // Chapter 9.8.2.1 + MAROS = 3; } diff --git a/ortools/glop/preprocessor.cc b/ortools/glop/preprocessor.cc index 878aa81299..32208e0a89 100644 --- a/ortools/glop/preprocessor.cc +++ b/ortools/glop/preprocessor.cc @@ -28,7 +28,7 @@ using ::util::Reverse; namespace { // Returns an interval as an human readable std::string for debugging. std::string IntervalString(Fractional lb, Fractional ub) { - return StringPrintf("[%g, %g]", lb, ub); + return absl::StrFormat("[%g, %g]", lb, ub); } #if defined(_MSC_VER) @@ -145,8 +145,8 @@ void MainLpPreprocessor::RunAndPushIfRelevant( const EntryIndex new_num_entries = lp->num_entries(); const double preprocess_time = time_limit->GetElapsedTime() - start_time; VLOG(1) << absl::StrFormat( - "%s(%fs): %d(%d) rows, %d(%d) columns, %lld(%lld) entries.", - name.c_str(), preprocess_time, lp->num_constraints().value(), + "%s(%fs): %d(%d) rows, %d(%d) columns, %d(%d) entries.", name.c_str(), + preprocess_time, lp->num_constraints().value(), (lp->num_constraints() - initial_num_rows_).value(), lp->num_variables().value(), (lp->num_variables() - initial_num_cols_).value(), @@ -2859,6 +2859,14 @@ void SingletonColumnSignPreprocessor::RecoverSolution( bool DoubletonEqualityRowPreprocessor::Run(LinearProgram* lp) { SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); + + // This is needed at postsolve. + // + // TODO(user): Get rid of the FIXED status instead to avoid spending + // time/memory for no good reason here. + saved_row_lower_bounds_ = lp->constraint_lower_bounds(); + saved_row_upper_bounds_ = lp->constraint_upper_bounds(); + // Note that we don't update the transpose during this preprocessor run. const SparseMatrix& original_transpose = lp->GetTransposeSparseMatrix(); @@ -3095,6 +3103,32 @@ void DoubletonEqualityRowPreprocessor::RecoverSolution( PreciseScalarProduct(solution->dual_values, r.column[col_choice]); solution->dual_values[r.row] = current_reduced_cost / r.coeff[col_choice]; } + + // Fix potential bad ConstraintStatus::FIXED_VALUE statuses. + FixConstraintWithFixedStatuses(saved_row_lower_bounds_, + saved_row_upper_bounds_, solution); +} + +void FixConstraintWithFixedStatuses(const DenseColumn& row_lower_bounds, + const DenseColumn& row_upper_bounds, + ProblemSolution* solution) { + const RowIndex num_rows = solution->constraint_statuses.size(); + DCHECK_EQ(row_lower_bounds.size(), num_rows); + DCHECK_EQ(row_upper_bounds.size(), num_rows); + for (RowIndex row(0); row < num_rows; ++row) { + if (solution->constraint_statuses[row] != ConstraintStatus::FIXED_VALUE) { + continue; + } + if (row_lower_bounds[row] == row_upper_bounds[row]) continue; + + // We need to fix the status and we just need to make sure that the bound we + // choose satisfies the LP optimality conditions. + if (solution->dual_values[row] > 0) { + solution->constraint_statuses[row] = ConstraintStatus::AT_LOWER_BOUND; + } else { + solution->constraint_statuses[row] = ConstraintStatus::AT_UPPER_BOUND; + } + } } void DoubletonEqualityRowPreprocessor:: diff --git a/ortools/glop/preprocessor.h b/ortools/glop/preprocessor.h index a028303f83..d1d95911fd 100644 --- a/ortools/glop/preprocessor.h +++ b/ortools/glop/preprocessor.h @@ -816,10 +816,28 @@ class DoubletonEqualityRowPreprocessor : public Preprocessor { void SwapDeletedAndModifiedVariableRestoreInfo(RestoreInfo* r); std::vector restore_stack_; + DenseColumn saved_row_lower_bounds_; + DenseColumn saved_row_upper_bounds_; DISALLOW_COPY_AND_ASSIGN(DoubletonEqualityRowPreprocessor); }; +// Because of numerical imprecision, a preprocessor like +// DoubletonEqualityRowPreprocessor can transform a constraint/variable domain +// like [1, 1+1e-7] to a fixed domain (for ex by multiplying the above domain by +// 1e9). This causes an issue because at postsolve, a FIXED_VALUE status now +// needs to be transformed to a AT_LOWER_BOUND/AT_UPPER_BOUND status. This is +// what this function is doing for the constraint statuses only. +// +// TODO(user): A better solution would simply be to get rid of the FIXED status +// altogether, it is better to simply use AT_LOWER_BOUND/AT_UPPER_BOUND +// depending on the constraining bound in the optimal solution. Note that we can +// always at the end transform any variable/constraint with a fixed domain to +// FIXED_VALUE if needed to keep the same external API. +void FixConstraintWithFixedStatuses(const DenseColumn& row_lower_bounds, + const DenseColumn& row_upper_bounds, + ProblemSolution* solution); + // -------------------------------------------------------- // DualizerPreprocessor // -------------------------------------------------------- diff --git a/ortools/glop/revised_simplex.cc b/ortools/glop/revised_simplex.cc index 57210ba12b..26d4279313 100644 --- a/ortools/glop/revised_simplex.cc +++ b/ortools/glop/revised_simplex.cc @@ -465,11 +465,11 @@ void RevisedSimplex::SetVariableNames() { variable_name_.resize(num_cols_, ""); for (ColIndex col(0); col < first_slack_col_; ++col) { const ColIndex var_index = col + 1; - variable_name_[col] = StringPrintf("x%d", ColToIntIndex(var_index)); + variable_name_[col] = absl::StrFormat("x%d", ColToIntIndex(var_index)); } for (ColIndex col(first_slack_col_); col < num_cols_; ++col) { const ColIndex var_index = col - first_slack_col_ + 1; - variable_name_[col] = StringPrintf("s%d", ColToIntIndex(var_index)); + variable_name_[col] = absl::StrFormat("s%d", ColToIntIndex(var_index)); } } @@ -909,7 +909,7 @@ void RevisedSimplex::InitializeVariableStatusesForWarmStart( } } -// This implementation starts with an initial matrix B equal to the the identity +// This implementation starts with an initial matrix B equal to the identity // matrix (modulo a column permutation). For that it uses either the slack // variables or the singleton columns present in the problem. Afterwards, the // fixed slacks in the basis are exchanged with normal columns of A if possible @@ -939,6 +939,7 @@ Status RevisedSimplex::CreateInitialBasis() { // If possible, for the primal simplex we replace some slack variables with // some singleton columns present in the problem. if (!parameters_.use_dual_simplex() && + parameters_.initial_basis() != GlopParameters::MAROS && parameters_.exploit_singleton_column_in_initial_basis()) { // For UseSingletonColumnInInitialBasis() to work better, we change // the value of the boxed singleton column with a non-zero cost to the best @@ -980,7 +981,28 @@ Status RevisedSimplex::CreateInitialBasis() { } // Use an advanced initial basis to remove the fixed variables from the basis. - if (parameters_.initial_basis() != GlopParameters::NONE) { + if (parameters_.initial_basis() == GlopParameters::NONE) { + return InitializeFirstBasis(basis); + } + if (parameters_.initial_basis() == GlopParameters::MAROS) { + InitialBasis initial_basis(matrix_with_slack_, objective_, lower_bound_, + upper_bound_, variables_info_.GetTypeRow()); + if (parameters_.use_dual_simplex()) { + // This dual version only uses zero-cost columns to complete the + // basis. + initial_basis.GetDualMarosBasis(num_cols_, &basis); + } else { + initial_basis.GetPrimalMarosBasis(num_cols_, &basis); + } + int number_changed = 0; + for (RowIndex row(0); row < num_rows_; ++row) { + if (basis[row] != SlackColIndex(row)) { + number_changed++; + } + } + VLOG(1) << "Number of Maros basis changes: " << number_changed; + } else if (parameters_.initial_basis() == GlopParameters::BIXBY || + parameters_.initial_basis() == GlopParameters::TRIANGULAR) { // First unassign the fixed variables from basis. int num_fixed_variables = 0; for (RowIndex row(0); row < basis.size(); ++row) { @@ -991,7 +1013,11 @@ Status RevisedSimplex::CreateInitialBasis() { } } - if (num_fixed_variables > 0) { + if (num_fixed_variables == 0) { + VLOG(1) << "Crash is set to " << parameters_.initial_basis() + << " but there is no equality rows to remove from initial all " + "slack basis."; + } else { // Then complete the basis with an advanced initial basis algorithm. VLOG(1) << "Trying to remove " << num_fixed_variables << " fixed variables from the initial basis."; @@ -1017,6 +1043,7 @@ Status RevisedSimplex::CreateInitialBasis() { } else { initial_basis.CompleteTriangularPrimalBasis(num_cols_, &basis); } + const Status status = InitializeFirstBasis(basis); // Check that the upper bound on the condition number of LU is below @@ -1032,12 +1059,13 @@ Status RevisedSimplex::CreateInitialBasis() { VLOG(1) << "Reverting to all slack basis."; basis = basis_copy; } - } else { - VLOG(1) << "Unsupported initial_basis parameters: " - << parameters_.initial_basis(); } } + } else { + LOG(WARNING) << "Unsupported initial_basis parameters: " + << parameters_.initial_basis(); } + return InitializeFirstBasis(basis); } @@ -2869,7 +2897,7 @@ void RevisedSimplex::DisplayIterationInfo() const { : variable_values_.ComputeSumOfPrimalInfeasibilities()); VLOG(1) << (feasibility_phase_ ? "Feasibility" : "Optimization") << " phase, iteration # " << iter - << ", objective = " << StringPrintf("%.15E", objective); + << ", objective = " << absl::StrFormat("%.15E", objective); } } @@ -2906,7 +2934,7 @@ std::string RevisedSimplex::SimpleVariableInfo(ColIndex col) const { std::string output; VariableType variable_type = variables_info_.GetTypeRow()[col]; VariableStatus variable_status = variables_info_.GetStatusRow()[col]; - StringAppendF(&output, "%d (%s) = %s, %s, %s, [%s,%s]", col.value(), + absl::StrAppendFormat(&output, "%d (%s) = %s, %s, %s, [%s,%s]", col.value(), variable_name_[col].c_str(), StringifyWithFlags(variable_values_.Get(col)).c_str(), GetVariableStatusString(variable_status).c_str(), diff --git a/ortools/glop/revised_simplex.h b/ortools/glop/revised_simplex.h index b18b388de6..837b07e149 100644 --- a/ortools/glop/revised_simplex.h +++ b/ortools/glop/revised_simplex.h @@ -164,8 +164,7 @@ class RevisedSimplex { // and try to use the previously computed solution as a warm-start. To disable // this behavior or give explicit warm-start data, use one of the State*() // functions below. - Status Solve(const LinearProgram& linear_program, - TimeLimit* time_limit) MUST_USE_RESULT; + Status Solve(const LinearProgram& lp, TimeLimit* time_limit) MUST_USE_RESULT; // Do not use the current solution as a warm-start for the next Solve(). The // next Solve() will behave as if the class just got created. @@ -416,7 +415,7 @@ class RevisedSimplex { Fractional ComputeDirectionError(ColIndex col); // Computes the ratio of the basic variable corresponding to 'row'. A target - // bound (upper or lower) is choosen depending on the sign of the entering + // bound (upper or lower) is chosen depending on the sign of the entering // reduced cost and the sign of the direction 'd_[row]'. The ratio is such // that adding 'ratio * d_[row]' to the variable value changes it to its // target bound. diff --git a/ortools/glop/variables_info.h b/ortools/glop/variables_info.h index d53ec7bff8..e65c71a228 100644 --- a/ortools/glop/variables_info.h +++ b/ortools/glop/variables_info.h @@ -66,10 +66,10 @@ class VariablesInfo { // Changes whether or not a non-basic boxed variable is 'relevant' and will be // returned as such by GetIsRelevantBitRow(). - void MakeBoxedVariableRelevant(bool are_boxed_variables_relevant); + void MakeBoxedVariableRelevant(bool value); - // This is used in UpdateRow to decide wheter to compute it using the row-wise - // or column-wise representation. + // This is used in UpdateRow to decide whether to compute it using the + // row-wise or column-wise representation. EntryIndex GetNumEntriesInRelevantColumns() const; // Returns the distance between the upper and lower bound of the given column. diff --git a/ortools/linear_solver/cbc_interface.cc b/ortools/linear_solver/cbc_interface.cc index 94943f2270..f707a2ab18 100644 --- a/ortools/linear_solver/cbc_interface.cc +++ b/ortools/linear_solver/cbc_interface.cc @@ -335,7 +335,7 @@ MPSolver::ResultStatus CBCInterface::Solve(const MPSolverParameters& param) { osi_.setObjSense(maximize_ ? -1 : 1); sync_status_ = MODEL_SYNCHRONIZED; - VLOG(1) << StringPrintf("Model built in %.3f seconds.", timer.Get()); + VLOG(1) << absl::StrFormat("Model built in %.3f seconds.", timer.Get()); ResetBestObjectiveBound(); @@ -382,7 +382,7 @@ MPSolver::ResultStatus CBCInterface::Solve(const MPSolverParameters& param) { CHECK_NE(kBadReturnStatus, return_status); // Should never happen according // to the CBC source - VLOG(1) << StringPrintf("Solved in %.3f seconds.", timer.Get()); + VLOG(1) << absl::StrFormat("Solved in %.3f seconds.", timer.Get()); // Check the status: optimal, infeasible, etc. int tmp_status = model.status(); diff --git a/ortools/linear_solver/clp_interface.cc b/ortools/linear_solver/clp_interface.cc index 0e1e3cc6ad..f7805e1722 100644 --- a/ortools/linear_solver/clp_interface.cc +++ b/ortools/linear_solver/clp_interface.cc @@ -23,6 +23,7 @@ #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" +#include "ortools/base/memory.h" #include "ortools/base/port.h" #include "ortools/base/stringprintf.h" #include "ortools/base/strutil.h" @@ -77,7 +78,7 @@ class CLPInterface : public MPSolverInterface { void SetObjectiveCoefficient(const MPVariable* const variable, double coefficient) override; // Change the constant term in the linear objective. - void SetObjectiveOffset(double value) override; + void SetObjectiveOffset(double offset) override; // Clear the objective from all its terms. void ClearObjective() override; @@ -148,7 +149,7 @@ CLPInterface::CLPInterface(MPSolver* const solver) CLPInterface::~CLPInterface() {} void CLPInterface::Reset() { - clp_.reset(new ClpSimplex); + clp_ = absl::make_unique(); clp_->setOptimizationDirection(maximize_ ? -1 : 1); ResetExtractionInformation(); } @@ -434,7 +435,7 @@ MPSolver::ResultStatus CLPInterface::Solve(const MPSolverParameters& param) { } ExtractModel(); - VLOG(1) << StringPrintf("Model built in %.3f seconds.", timer.Get()); + VLOG(1) << absl::StrFormat("Model built in %.3f seconds.", timer.Get()); // Time limit. if (solver_->time_limit() != 0) { @@ -446,13 +447,13 @@ MPSolver::ResultStatus CLPInterface::Solve(const MPSolverParameters& param) { // Start from a fresh set of default parameters and set them to // specified values. - options_.reset(new ClpSolve); + options_ = absl::make_unique(); SetParameters(param); // Solve timer.Restart(); clp_->initialSolve(*options_); - VLOG(1) << StringPrintf("Solved in %.3f seconds.", timer.Get()); + VLOG(1) << absl::StrFormat("Solved in %.3f seconds.", timer.Get()); // Check the status: optimal, infeasible, etc. int tmp_status = clp_->status(); diff --git a/ortools/linear_solver/cplex_interface.cc b/ortools/linear_solver/cplex_interface.cc index 71e0351e2e..05ef29136a 100644 --- a/ortools/linear_solver/cplex_interface.cc +++ b/ortools/linear_solver/cplex_interface.cc @@ -257,7 +257,7 @@ std::string CplexInterface::SolverVersion() const { version -= mod * 100; int const fix = version; - return StringPrintf("CPLEX library version %d.%02d.%02d.%02d", major, release, + return absl::StrFormat("CPLEX library version %d.%02d.%02d.%02d", major, release, mod, fix); } @@ -1125,7 +1125,7 @@ MPSolver::ResultStatus CplexInterface::Solve(MPSolverParameters const ¶m) { // solve. if (!supportIncrementalExtraction && sync_status_ == MUST_RELOAD) Reset(); ExtractModel(); - VLOG(1) << StringPrintf("Model build in %.3f seconds.", timer.Get()); + VLOG(1) << absl::StrFormat("Model build in %.3f seconds.", timer.Get()); // Set log level. CHECK_STATUS( @@ -1159,15 +1159,15 @@ MPSolver::ResultStatus CplexInterface::Solve(MPSolverParameters const ¶m) { (void)CPXXsetintparam(mEnv, CPX_PARAM_SCRIND, CPX_OFF); if (status) { - VLOG(1) << StringPrintf("Failed to optimize MIP. Error %d", status); + VLOG(1) << absl::StrFormat("Failed to optimize MIP. Error %d", status); // NOTE: We do not return immediately since there may be information // to grab (for example an incumbent) } else { - VLOG(1) << StringPrintf("Solved in %.3f seconds.", timer.Get()); + VLOG(1) << absl::StrFormat("Solved in %.3f seconds.", timer.Get()); } int const cpxstat = CPXXgetstat(mEnv, mLp); - VLOG(1) << StringPrintf("CPLEX solution status %d.", cpxstat); + VLOG(1) << absl::StrFormat("CPLEX solution status %d.", cpxstat); // Figure out what solution we have. int solnmethod, solntype, pfeas, dfeas; @@ -1231,8 +1231,8 @@ MPSolver::ResultStatus CplexInterface::Solve(MPSolverParameters const ¶m) { } else var->set_reduced_cost(CPX_NAN); VLOG(3) << var->name() << ":" - << (value ? StringPrintf(" value = %f", x[i]) : "") - << (dual ? StringPrintf(" reduced cost = %f", dj[i]) : ""); + << (value ? absl::StrFormat(" value = %f", x[i]) : "") + << (dual ? absl::StrFormat(" reduced cost = %f", dj[i]) : ""); } } @@ -1248,7 +1248,7 @@ MPSolver::ResultStatus CplexInterface::Solve(MPSolverParameters const ¶m) { } else ct->set_dual_value(CPX_NAN); VLOG(4) << "row " << ct->index() << ":" - << (dual ? StringPrintf(" dual = %f", pi[i]) : ""); + << (dual ? absl::StrFormat(" dual = %f", pi[i]) : ""); } } } diff --git a/ortools/linear_solver/glop_interface.cc b/ortools/linear_solver/glop_interface.cc index 5b2dff4c54..a169851664 100644 --- a/ortools/linear_solver/glop_interface.cc +++ b/ortools/linear_solver/glop_interface.cc @@ -251,7 +251,7 @@ int64 GLOPInterface::nodes() const { } double GLOPInterface::best_objective_bound() const { - LOG(DFATAL) << "Best objective bound only available for discrete problems"; + // TODO(user): report a better bound when we can. return trivial_worst_objective_bound(); } diff --git a/ortools/linear_solver/glpk_interface.cc b/ortools/linear_solver/glpk_interface.cc index 85faec6896..f1128016f9 100644 --- a/ortools/linear_solver/glpk_interface.cc +++ b/ortools/linear_solver/glpk_interface.cc @@ -28,6 +28,7 @@ #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" +#include "ortools/base/memory.h" #include "ortools/base/port.h" #include "ortools/base/stringprintf.h" #include "ortools/base/timer.h" @@ -161,7 +162,7 @@ class GLPKInterface : public MPSolverInterface { void ExtractObjective() override; std::string SolverVersion() const override { - return StringPrintf("GLPK %s", glp_version()); + return absl::StrFormat("GLPK %s", glp_version()); } void* underlying_solver() override { return reinterpret_cast(lp_); } @@ -218,7 +219,7 @@ GLPKInterface::GLPKInterface(MPSolver* const solver, bool mip) lp_ = glp_create_prob(); glp_set_prob_name(lp_, solver_->name_.c_str()); glp_set_obj_dir(lp_, GLP_MIN); - mip_callback_info_.reset(new GLPKInformation(maximize_)); + mip_callback_info_ = absl::make_unique(maximize_); } // Frees the LP memory allocations. @@ -454,7 +455,7 @@ void GLPKInterface::ExtractNewConstraints() { set_constraint_as_extracted(i, true); if (ct->name().empty()) { glp_set_row_name(lp_, MPSolverIndexToGlpkIndex(i), - StringPrintf("ct_%i", i).c_str()); + absl::StrFormat("ct_%i", i).c_str()); } else { glp_set_row_name(lp_, MPSolverIndexToGlpkIndex(i), ct->name().c_str()); } @@ -534,7 +535,7 @@ MPSolver::ResultStatus GLPKInterface::Solve(const MPSolverParameters& param) { } ExtractModel(); - VLOG(1) << StringPrintf("Model built in %.3f seconds.", timer.Get()); + VLOG(1) << absl::StrFormat("Model built in %.3f seconds.", timer.Get()); // Configure parameters at every solve, even when the model has not // been changed, in case some of the parameters such as the time @@ -562,8 +563,8 @@ MPSolver::ResultStatus GLPKInterface::Solve(const MPSolverParameters& param) { return result_status_; } } - VLOG(1) << StringPrintf("GLPK Status: %i (time spent: %.3f seconds).", - solver_status, timer.Get()); + VLOG(1) << absl::StrFormat("GLPK Status: %i (time spent: %.3f seconds).", + solver_status, timer.Get()); // Get the results. if (mip_) { diff --git a/ortools/linear_solver/gurobi_interface.cc b/ortools/linear_solver/gurobi_interface.cc index 51deb68740..9ef3b04092 100644 --- a/ortools/linear_solver/gurobi_interface.cc +++ b/ortools/linear_solver/gurobi_interface.cc @@ -109,8 +109,8 @@ class GurobiInterface : public MPSolverInterface { std::string SolverVersion() const override { int major, minor, technical; GRBversion(&major, &minor, &technical); - return StringPrintf("Gurobi library version %d.%d.%d\n", major, minor, - technical); + return absl::StrFormat("Gurobi library version %d.%d.%d\n", major, minor, + technical); } bool InterruptSolve() override { @@ -165,10 +165,7 @@ class GurobiInterface : public MPSolverInterface { MPSolver::BasisStatus TransformGRBConstraintBasisStatus( int gurobi_basis_status, int constraint_index) const; - void CheckedGurobiCall(int err) const { - CHECK_EQ(0, err) << "Fatal error with code " << err << ", due to " - << GRBgeterrormsg(env_); - } + void CheckedGurobiCall(int err) const; int SolutionCount() const; @@ -656,7 +653,7 @@ MPSolver::ResultStatus GurobiInterface::Solve(const MPSolverParameters& param) { ExtractModel(); // Sync solver. CheckedGurobiCall(GRBupdatemodel(model_)); - VLOG(1) << StringPrintf("Model built in %.3f seconds.", timer.Get()); + VLOG(1) << absl::StrFormat("Model built in %.3f seconds.", timer.Get()); // Set solution hints if any. for (const std::pair& p : solver_->solution_hint_) { @@ -687,14 +684,14 @@ MPSolver::ResultStatus GurobiInterface::Solve(const MPSolverParameters& param) { if (status) { VLOG(1) << "Failed to optimize MIP." << GRBgeterrormsg(env_); } else { - VLOG(1) << StringPrintf("Solved in %.3f seconds.", timer.Get()); + VLOG(1) << absl::StrFormat("Solved in %.3f seconds.", timer.Get()); } // Get the status. int optimization_status = 0; CheckedGurobiCall( GRBgetintattr(model_, GRB_INT_ATTR_STATUS, &optimization_status)); - VLOG(1) << StringPrintf("Solution status %d.\n", optimization_status); + VLOG(1) << absl::StrFormat("Solution status %d.\n", optimization_status); const int solution_count = SolutionCount(); switch (optimization_status) { diff --git a/ortools/linear_solver/linear_solver.cc b/ortools/linear_solver/linear_solver.cc index c8ebc4bbbd..7ff9967cd9 100644 --- a/ortools/linear_solver/linear_solver.cc +++ b/ortools/linear_solver/linear_solver.cc @@ -305,10 +305,6 @@ bool MPSolver::SetSolverSpecificParametersAsString( return interface_->SetSolverSpecificParametersAsString(parameters); } -void MPSolver::SetHint(const PartialVariableAssignment& hint) { - interface_->SetHint(hint); -} - // ----- Solver ----- #if defined(USE_CLP) || defined(USE_CBC) @@ -736,8 +732,18 @@ void MPSolver::ExportModelToProto(MPModelProto* output_model) const { constraint_proto->add_coefficient(var_and_coeff.second); } } + output_model->set_maximize(Objective().maximization()); output_model->set_objective_offset(Objective().offset()); + + if (!solution_hint_.empty()) { + PartialVariableAssignment* const hint = + output_model->mutable_solution_hint(); + for (const auto& var_value_pair : solution_hint_) { + hint->add_var_index(var_value_pair.first->index()); + hint->add_var_value(var_value_pair.second); + } + } } util::Status MPSolver::LoadSolutionFromProto( @@ -1259,6 +1265,14 @@ bool MPSolver::ExportModelAsMpsFormat(bool fixed_format, bool obfuscate, return exporter.ExportModelAsMpsFormat(fixed_format, obfuscate, model_str); } +void MPSolver::SetHint(std::vector > hint) { + for (const auto& var_value_pair : hint) { + CHECK(OwnsVariable(var_value_pair.first)) + << "hint variable does not belong to this solver"; + } + solution_hint_ = std::move(hint); +} + void MPSolver::GenerateVariableNameIndex() const { if (variable_name_to_index_) return; variable_name_to_index_ = std::unordered_map(); diff --git a/ortools/linear_solver/linear_solver.h b/ortools/linear_solver/linear_solver.h index 919d12f715..77b1bae01f 100644 --- a/ortools/linear_solver/linear_solver.h +++ b/ortools/linear_solver/linear_solver.h @@ -473,10 +473,21 @@ class MPSolver { return solver_specific_parameter_string_; } - // Advanced usage: starting hint. This instructs the solver to first pin some - // variables to particular values and use that to quickly get an upper bound - // on the solution quality. Currently, only GLIP supports this. - void SetHint(const PartialVariableAssignment& hint); + // Set an hint for solution. + // + // If a feasible or almost-feasible solution to the problem is already known, + // it may be helpful to pass it to the solver so that it can be used. A solver + // that supports this feature will try to use this information to create its + // initial feasible solution. + // + // Note that it may not always be faster to give a hint like this to the + // solver. There is also no guarantee that the solver will use this hint or + // try to return a solution "close" to this assignment in case of multiple + // optimal solutions. + // + // As of 2018-08, this is only used by SCIP, BOP, Gurobi, with various + // behaviors, and ignored by other solvers. Contact or-core-team@ for details. + void SetHint(std::vector > hint); // Advanced usage: possible basis status values for a variable and the // slack variable of a linear constraint. @@ -658,6 +669,9 @@ class MPSolver { // exploited as a starting hint by a solver. // // Note(user): as of 05/05/2015, we can't use >> because of some SWIG errors. + // + // TODO(user): replace by two vectors, a std::vector to indicate if a + // hint is provided and a std::vector for the hint value. std::vector > solution_hint_; // Time limit in milliseconds (0 = no limit). @@ -709,7 +723,7 @@ class MPObjective { // Resets the current objective to take the value of linear_expr, and sets // the objective direction to maximize if "is_maximize", otherwise minimizes. - void OptimizeLinearExpr(const LinearExpr& linear_expr, bool is_maximize); + void OptimizeLinearExpr(const LinearExpr& linear_expr, bool is_maximization); void MaximizeLinearExpr(const LinearExpr& linear_expr) { OptimizeLinearExpr(linear_expr, true); } @@ -1315,10 +1329,6 @@ class MPSolverInterface { LOG(FATAL) << "Not supported by this solver."; } - virtual void SetHint(const PartialVariableAssignment& hint) { - LOG(FATAL) << "Not supported by this solver."; - } - virtual bool InterruptSolve() { return false; } // See MPSolver::NextSolution() for contract. diff --git a/ortools/linear_solver/model_exporter.cc b/ortools/linear_solver/model_exporter.cc index 714bfd7277..80a25bab19 100644 --- a/ortools/linear_solver/model_exporter.cc +++ b/ortools/linear_solver/model_exporter.cc @@ -106,7 +106,7 @@ std::vector MPModelProtoExporter::ExtractAndProcessNames( int i = 0; for (const auto& item : proto) { const std::string obfuscated_name = - StringPrintf("%s%0*d", prefix.c_str(), num_digits, i); + absl::StrFormat("%s%0*d", prefix.c_str(), num_digits, i); if (obfuscate || !item.has_name()) { result[i] = namer.MakeUniqueName(obfuscated_name); LOG_IF(WARNING, FLAGS_lp_log_invalid_name && !item.has_name()) @@ -148,23 +148,23 @@ std::vector MPModelProtoExporter::ExtractAndProcessNames( void MPModelProtoExporter::AppendComments(const std::string& separator, std::string* output) const { const char* const sep = separator.c_str(); - StringAppendF(output, "%s Generated by MPModelProtoExporter\n", sep); - StringAppendF(output, "%s %-16s : %s\n", sep, "Name", - proto_.has_name() ? proto_.name().c_str() : "NoName"); - StringAppendF(output, "%s %-16s : %s\n", sep, "Format", - use_fixed_mps_format_ ? "Fixed" : "Free"); - StringAppendF(output, "%s %-16s : %d\n", sep, "Constraints", - proto_.constraint_size()); - StringAppendF(output, "%s %-16s : %d\n", sep, "Variables", - proto_.variable_size()); - StringAppendF(output, "%s %-14s : %d\n", sep, "Binary", - num_binary_variables_); - StringAppendF(output, "%s %-14s : %d\n", sep, "Integer", - num_integer_variables_); - StringAppendF(output, "%s %-14s : %d\n", sep, "Continuous", - num_continuous_variables_); + absl::StrAppendFormat(output, "%s Generated by MPModelProtoExporter\n", sep); + absl::StrAppendFormat(output, "%s %-16s : %s\n", sep, "Name", + proto_.has_name() ? proto_.name().c_str() : "NoName"); + absl::StrAppendFormat(output, "%s %-16s : %s\n", sep, "Format", + use_fixed_mps_format_ ? "Fixed" : "Free"); + absl::StrAppendFormat(output, "%s %-16s : %d\n", sep, "Constraints", + proto_.constraint_size()); + absl::StrAppendFormat(output, "%s %-16s : %d\n", sep, "Variables", + proto_.variable_size()); + absl::StrAppendFormat(output, "%s %-14s : %d\n", sep, "Binary", + num_binary_variables_); + absl::StrAppendFormat(output, "%s %-14s : %d\n", sep, "Integer", + num_integer_variables_); + absl::StrAppendFormat(output, "%s %-14s : %d\n", sep, "Continuous", + num_continuous_variables_); if (FLAGS_lp_shows_unused_variables) { - StringAppendF(output, "%s Unused variables are shown\n", sep); + absl::StrAppendFormat(output, "%s Unused variables are shown\n", sep); } } @@ -350,8 +350,8 @@ bool MPModelProtoExporter::ExportModelAsLpFormat(bool obfuscated, const double lb = var_proto.lower_bound(); const double ub = var_proto.upper_bound(); if (var_proto.is_integer() && lb == round(lb) && ub == round(ub)) { - StringAppendF(output, " %.0f <= %s <= %.0f\n", lb, - exported_variable_names_[var_index].c_str(), ub); + absl::StrAppendFormat(output, " %.0f <= %s <= %.0f\n", lb, + exported_variable_names_[var_index].c_str(), ub); } else { absl::StrAppend(output, " "); if (lb == -std::numeric_limits::infinity() && @@ -377,8 +377,8 @@ bool MPModelProtoExporter::ExportModelAsLpFormat(bool obfuscated, if (!show_variable[var_index]) continue; const MPVariableProto& var_proto = proto_.variable(var_index); if (IsBoolean(var_proto)) { - StringAppendF(output, " %s\n", - exported_variable_names_[var_index].c_str()); + absl::StrAppendFormat(output, " %s\n", + exported_variable_names_[var_index].c_str()); } } } @@ -403,16 +403,16 @@ void MPModelProtoExporter::AppendMpsPair(const std::string& name, double value, const int kFixedMpsDoubleWidth = 12; if (use_fixed_mps_format_) { int precision = kFixedMpsDoubleWidth; - std::string value_str = StringPrintf("%.*G", precision, value); + std::string value_str = absl::StrFormat("%.*G", precision, value); // Use the largest precision that can fit into the field witdh. while (value_str.size() > kFixedMpsDoubleWidth) { --precision; - value_str = StringPrintf("%.*g", precision, value); + value_str = absl::StrFormat("%.*g", precision, value); } - StringAppendF(output, " %-8s %*s ", name.c_str(), kFixedMpsDoubleWidth, + absl::StrAppendFormat(output, " %-8s %*s ", name.c_str(), kFixedMpsDoubleWidth, value_str.c_str()); } else { - StringAppendF(output, " %-16s %21s ", name.c_str(), + absl::StrAppendFormat(output, " %-16s %21s ", name.c_str(), DoubleToString(value).c_str()); } } @@ -420,7 +420,7 @@ void MPModelProtoExporter::AppendMpsPair(const std::string& name, double value, void MPModelProtoExporter::AppendMpsLineHeader(const std::string& id, const std::string& name, std::string* output) const { - StringAppendF(output, use_fixed_mps_format_ ? " %-2s %-8s" : " %-2s %-16s", + absl::StrAppendFormat(output, use_fixed_mps_format_ ? " %-2s %-8s" : " %-2s %-16s", id.c_str(), name.c_str()); } @@ -495,11 +495,15 @@ bool MPModelProtoExporter::ExportModelAsMpsFormat(bool fixed_format, // use_fixed_mps_format_ was possibly modified by ExtractAndProcessNames(). LOG_IF(WARNING, fixed_format && !use_fixed_mps_format_) << "Cannot use fixed format. Falling back to free format"; + if (proto_.maximize()) { + LOG(DFATAL) << "MPS cannot represent maximization objectives."; + return false; + } // Comments. AppendComments("*", output); // NAME section. - StringAppendF(output, "%-14s%s\n", "NAME", proto_.name().c_str()); + absl::StrAppendFormat(output, "%-14s%s\n", "NAME", proto_.name().c_str()); // ROWS section. current_mps_column_ = 0; @@ -552,10 +556,10 @@ bool MPModelProtoExporter::ExportModelAsMpsFormat(bool fixed_format, if (!columns_section.empty()) { const char* const kIntMarkerFormat = " %-10s%-36s%-10s\n"; columns_section = - StringPrintf(kIntMarkerFormat, "INTSTART", "'MARKER'", "'INTORG'") + + absl::StrFormat(kIntMarkerFormat, "INTSTART", "'MARKER'", "'INTORG'") + columns_section; - StringAppendF(&columns_section, kIntMarkerFormat, "INTEND", "'MARKER'", - "'INTEND'"); + absl::StrAppendFormat(&columns_section, kIntMarkerFormat, "INTEND", + "'MARKER'", "'INTEND'"); } AppendMpsColumns(/*integrality=*/false, transpose, &columns_section); if (!columns_section.empty()) { @@ -608,7 +612,7 @@ bool MPModelProtoExporter::ExportModelAsMpsFormat(bool fixed_format, if (var_proto.is_integer()) { if (IsBoolean(var_proto)) { AppendMpsLineHeader("BV", "BOUND", &bounds_section); - StringAppendF(&bounds_section, " %s\n", var_name.c_str()); + absl::StrAppendFormat(&bounds_section, " %s\n", var_name.c_str()); } else { if (lb != 0.0) { AppendMpsBound("LI", var_name, lb, &bounds_section); @@ -621,7 +625,7 @@ bool MPModelProtoExporter::ExportModelAsMpsFormat(bool fixed_format, if (lb == -std::numeric_limits::infinity() && ub == +std::numeric_limits::infinity()) { AppendMpsLineHeader("FR", "BOUND", &bounds_section); - StringAppendF(&bounds_section, " %s\n", var_name.c_str()); + absl::StrAppendFormat(&bounds_section, " %s\n", var_name.c_str()); } else if (lb == ub) { AppendMpsBound("FX", var_name, lb, &bounds_section); } else { @@ -629,7 +633,7 @@ bool MPModelProtoExporter::ExportModelAsMpsFormat(bool fixed_format, AppendMpsBound("LO", var_name, lb, &bounds_section); } else if (ub == +std::numeric_limits::infinity()) { AppendMpsLineHeader("PL", "BOUND", &bounds_section); - StringAppendF(&bounds_section, " %s\n", var_name.c_str()); + absl::StrAppendFormat(&bounds_section, " %s\n", var_name.c_str()); } if (ub != +std::numeric_limits::infinity()) { AppendMpsBound("UP", var_name, ub, &bounds_section); diff --git a/ortools/linear_solver/model_exporter.h b/ortools/linear_solver/model_exporter.h index 795930a8fe..050874ce81 100644 --- a/ortools/linear_solver/model_exporter.h +++ b/ortools/linear_solver/model_exporter.h @@ -53,13 +53,15 @@ class MPModelProtoExporter { // http://lpsolve.sourceforge.net/5.5/CPLEX-format.htm // http://tinyurl.com/cplex-lp-format // http://www.gurobi.com/documentation/5.1/reference-manual/node871 - bool ExportModelAsLpFormat(bool obfuscated, std::string* model_str); + bool ExportModelAsLpFormat(bool obfuscated, std::string* output); // Outputs the current model (variables, constraints, objective) as a // std::string encoded in MPS file format, using the "fixed" MPS format if // possible, and the "free" MPS format otherwise. // - // Returns false if some error has occurred during execution. + // Returns false if some error has occurred during execution. Models with + // maximization objectives trigger an error, because MPS can encode only + // minimization problems. // // If fixed_format is true, the method tries to use the MPS fixed format (the // use of which is discouraged as coefficients are printed with less @@ -83,7 +85,7 @@ class MPModelProtoExporter { // Gurobi's description: // http://www.gurobi.com/documentation/5.1/reference-manual/node869 bool ExportModelAsMpsFormat(bool fixed_format, bool obfuscated, - std::string* model_str); + std::string* output); private: // Computes the number of continuous, integer and binary variables. diff --git a/ortools/linear_solver/scip_interface.cc b/ortools/linear_solver/scip_interface.cc index e76a4947f8..e954989410 100644 --- a/ortools/linear_solver/scip_interface.cc +++ b/ortools/linear_solver/scip_interface.cc @@ -90,9 +90,9 @@ class SCIPInterface : public MPSolverInterface { void ExtractObjective() override; std::string SolverVersion() const override { - return StringPrintf("SCIP %d.%d.%d [LP solver: %s]", SCIPmajorVersion(), - SCIPminorVersion(), SCIPtechVersion(), - SCIPlpiGetSolverName()); + return absl::StrFormat("SCIP %d.%d.%d [LP solver: %s]", SCIPmajorVersion(), + SCIPminorVersion(), SCIPtechVersion(), + SCIPlpiGetSolverName()); } bool InterruptSolve() override { @@ -440,7 +440,7 @@ MPSolver::ResultStatus SCIPInterface::Solve(const MPSolverParameters& param) { } ExtractModel(); - VLOG(1) << StringPrintf("Model built in %.3f seconds.", timer.Get()); + VLOG(1) << absl::StrFormat("Model built in %.3f seconds.", timer.Get()); // Time limit. if (solver_->time_limit() != 0) { @@ -516,7 +516,7 @@ MPSolver::ResultStatus SCIPInterface::Solve(const MPSolverParameters& param) { result_status_ = MPSolver::ABNORMAL; return result_status_; } - VLOG(1) << StringPrintf("Solved in %.3f seconds.", timer.Get()); + VLOG(1) << absl::StrFormat("Solved in %.3f seconds.", timer.Get()); // Get the results. SCIP_SOL* const solution = SCIPgetBestSol(scip_); diff --git a/ortools/lp_data/lp_data.cc b/ortools/lp_data/lp_data.cc index 7592a5ce7a..d268eed7fa 100644 --- a/ortools/lp_data/lp_data.cc +++ b/ortools/lp_data/lp_data.cc @@ -351,13 +351,13 @@ bool LinearProgram::IsCleanedUp() const { std::string LinearProgram::GetVariableName(ColIndex col) const { return col >= variable_names_.size() || variable_names_[col].empty() - ? StringPrintf("c%d", col.value()) + ? absl::StrFormat("c%d", col.value()) : variable_names_[col]; } std::string LinearProgram::GetConstraintName(RowIndex row) const { return row >= constraint_names_.size() || constraint_names_[row].empty() - ? StringPrintf("r%d", row.value()) + ? absl::StrFormat("r%d", row.value()) : constraint_names_[row]; } @@ -1181,8 +1181,8 @@ Fractional LinearProgram::ScaleBounds() { return bound_scaling_factor; } -void LinearProgram::DeleteRows(const DenseBooleanColumn& row_to_delete) { - if (row_to_delete.empty()) return; +void LinearProgram::DeleteRows(const DenseBooleanColumn& rows_to_delete) { + if (rows_to_delete.empty()) return; // Deal with row-indexed data and construct the row mapping that will need to // be applied to every column entry. @@ -1190,7 +1190,7 @@ void LinearProgram::DeleteRows(const DenseBooleanColumn& row_to_delete) { RowPermutation permutation(num_rows); RowIndex new_index(0); for (RowIndex row(0); row < num_rows; ++row) { - if (row >= row_to_delete.size() || !row_to_delete[row]) { + if (row >= rows_to_delete.size() || !rows_to_delete[row]) { constraint_lower_bounds_[new_index] = constraint_lower_bounds_[row]; constraint_upper_bounds_[new_index] = constraint_upper_bounds_[row]; constraint_names_[new_index].swap(constraint_names_[row]); @@ -1224,7 +1224,7 @@ void LinearProgram::DeleteRows(const DenseBooleanColumn& row_to_delete) { // Eventually update transpose_matrix_. if (transpose_matrix_is_consistent_) { transpose_matrix_.DeleteColumns( - reinterpret_cast(row_to_delete)); + reinterpret_cast(rows_to_delete)); } } @@ -1334,7 +1334,7 @@ std::string LinearProgram::ProblemStatFormatter(const char* format) const { const int num_non_binary_variables = NonBinaryVariablesList().size(); const int num_continuous_variables = ColToIntIndex(num_variables()) - num_integer_variables; - return StringPrintf( + return absl::StrFormat( format, RowToIntIndex(num_constraints()), ColToIntIndex(num_variables()), matrix_.num_entries().value(), num_objective_non_zeros, num_rhs_non_zeros, num_less_than_constraints, num_greater_than_constraints, @@ -1367,7 +1367,7 @@ std::string LinearProgram::NonZeroStatFormatter(const char* format) const { const double fill_rate = 100.0 * static_cast(num_entries.value()) / static_cast(height * width); - return StringPrintf( + return absl::StrFormat( format, fill_rate, GetMaxElement(num_entries_in_row).value(), Average(num_entries_in_row), StandardDeviation(num_entries_in_row), GetMaxElement(num_entries_in_column).value(), @@ -1455,13 +1455,13 @@ bool LinearProgram::BoundsOfIntegerConstraintsAreInteger( std::string ProblemSolution::DebugString() const { std::string s = "Problem status: " + GetProblemStatusString(status); for (ColIndex col(0); col < primal_values.size(); ++col) { - StringAppendF(&s, "\n Var #%d: %s %g", col.value(), + absl::StrAppendFormat(&s, "\n Var #%d: %s %g", col.value(), GetVariableStatusString(variable_statuses[col]).c_str(), primal_values[col]); } s += "\n------------------------------"; for (RowIndex row(0); row < dual_values.size(); ++row) { - StringAppendF(&s, "\n Constraint #%d: %s %g", row.value(), + absl::StrAppendFormat(&s, "\n Constraint #%d: %s %g", row.value(), GetConstraintStatusString(constraint_statuses[row]).c_str(), dual_values[row]); } diff --git a/ortools/lp_data/lp_data.h b/ortools/lp_data/lp_data.h index deba8279ee..a3da61a8d0 100644 --- a/ortools/lp_data/lp_data.h +++ b/ortools/lp_data/lp_data.h @@ -414,7 +414,7 @@ class LinearProgram { // compute the solution of a maximization problem given as an argument. // // TODO(user): Do not interpret as a minimization problem? - void PopulateFromDual(const LinearProgram& linear_program, + void PopulateFromDual(const LinearProgram& dual, RowToColMapping* duplicated_rows); // Populates the calling object with the given LinearProgram. diff --git a/ortools/lp_data/lp_decomposer.cc b/ortools/lp_data/lp_decomposer.cc index dc7d184424..bac0103399 100644 --- a/ortools/lp_data/lp_decomposer.cc +++ b/ortools/lp_data/lp_decomposer.cc @@ -17,7 +17,6 @@ #include "ortools/algorithms/dynamic_partition.h" #include "ortools/base/mutex.h" -#include "ortools/glop/parameters.pb.h" #include "ortools/lp_data/lp_data.h" #include "ortools/lp_data/lp_utils.h" diff --git a/ortools/lp_data/lp_print_utils.h b/ortools/lp_data/lp_print_utils.h index 74959eb58f..9ca391800c 100644 --- a/ortools/lp_data/lp_print_utils.h +++ b/ortools/lp_data/lp_print_utils.h @@ -27,14 +27,16 @@ namespace glop { // Returns a std::string representing a floating-point number in decimal, // with a precision corresponding to the type of the argument. -inline std::string Stringify(const float a) { return StringPrintf("%.7g", a); } +inline std::string Stringify(const float a) { + return absl::StrFormat("%.7g", a); +} inline std::string Stringify(const double a) { - return StringPrintf("%.16g", a); + return absl::StrFormat("%.16g", a); } inline std::string Stringify(const long double a) { - return StringPrintf("%.19Lg", a); + return absl::StrFormat("%.19g", a); } // Returns a std::string "num/den" representing the rational approximation of x. diff --git a/ortools/lp_data/lp_utils.cc b/ortools/lp_data/lp_utils.cc index 7d7415705f..02bb752027 100644 --- a/ortools/lp_data/lp_utils.cc +++ b/ortools/lp_data/lp_utils.cc @@ -111,11 +111,11 @@ void RemoveNearZeroEntries(Fractional threshold, DenseColumn* column) { } Fractional RestrictedInfinityNorm(const SparseColumn& column, - const DenseBooleanColumn& row_to_consider, + const DenseBooleanColumn& rows_to_consider, RowIndex* row_index) { Fractional infinity_norm = 0.0; for (const SparseColumn::Entry e : column) { - if (row_to_consider[e.row()] && fabs(e.coefficient()) > infinity_norm) { + if (rows_to_consider[e.row()] && fabs(e.coefficient()) > infinity_norm) { infinity_norm = fabs(e.coefficient()); *row_index = e.row(); } diff --git a/ortools/lp_data/lp_utils.h b/ortools/lp_data/lp_utils.h index d9e8abc212..e14253deed 100644 --- a/ortools/lp_data/lp_utils.h +++ b/ortools/lp_data/lp_utils.h @@ -141,9 +141,9 @@ Fractional PartialScalarProduct(const DenseRowOrColumn& u, // Returns the norm^2 (sum of the square of the entries) of the given column. // The precise version uses KahanSum and are about two times slower. Fractional SquaredNorm(const SparseColumn& v); -Fractional SquaredNorm(const DenseColumn& v); +Fractional SquaredNorm(const DenseColumn& column); Fractional PreciseSquaredNorm(const SparseColumn& v); -Fractional PreciseSquaredNorm(const DenseColumn& v); +Fractional PreciseSquaredNorm(const DenseColumn& column); Fractional PreciseSquaredNorm(const ScatteredColumn& v); // Returns the maximum of the |coefficients| of 'v'. diff --git a/ortools/lp_data/matrix_scaler.cc b/ortools/lp_data/matrix_scaler.cc index b87a14a0a7..38e8556e87 100644 --- a/ortools/lp_data/matrix_scaler.cc +++ b/ortools/lp_data/matrix_scaler.cc @@ -64,7 +64,7 @@ std::string SparseMatrixScaler::DebugInformationString() const { Fractional min_magnitude; matrix_->ComputeMinAndMaxMagnitudes(&min_magnitude, &max_magnitude); const Fractional dynamic_range = max_magnitude / min_magnitude; - std::string output = StringPrintf( + std::string output = absl::StrFormat( "Min magnitude = %g, max magnitude = %g\n" "Dynamic range = %g\n" "Variance = %g\n" diff --git a/ortools/lp_data/model_reader.h b/ortools/lp_data/model_reader.h index 516d1833e5..1143b0fd67 100644 --- a/ortools/lp_data/model_reader.h +++ b/ortools/lp_data/model_reader.h @@ -15,7 +15,6 @@ #define OR_TOOLS_LP_DATA_MODEL_READER_H_ #include "ortools/linear_solver/linear_solver.pb.h" -#include "ortools/glop/parameters.pb.h" #include "ortools/lp_data/lp_data.h" namespace operations_research { diff --git a/ortools/lp_data/mps_reader.cc b/ortools/lp_data/mps_reader.cc index 4c761ea018..e9c03cf73f 100644 --- a/ortools/lp_data/mps_reader.cc +++ b/ortools/lp_data/mps_reader.cc @@ -24,14 +24,14 @@ #include "ortools/base/file.h" #include "ortools/base/filelineiter.h" #include "ortools/base/logging.h" -#include "ortools/base/map_util.h" // for FindOrNull, gtl::FindWithDefault -#include "ortools/base/numbers.h" // for safe_strtod +#include "ortools/base/map_util.h" // for FindOrNull, FindWithDefault +#include "ortools/base/match.h" +#include "ortools/base/numbers.h" // for safe_strtod #include "ortools/base/split.h" #include "ortools/base/status.h" #include "ortools/base/stringprintf.h" #include "ortools/base/strutil.h" #include "ortools/lp_data/lp_print_utils.h" -#include "ortools/glop/parameters.pb.h" DEFINE_bool(mps_free_form, false, "Read MPS files in free form."); DEFINE_bool(mps_stop_after_first_error, true, "Stop after the first error."); diff --git a/ortools/lp_data/mps_reader.h b/ortools/lp_data/mps_reader.h index 873f7b9c80..ae7c1a8bb7 100644 --- a/ortools/lp_data/mps_reader.h +++ b/ortools/lp_data/mps_reader.h @@ -68,7 +68,7 @@ class MPSReader { bool LoadFile(const std::string& file_name, LinearProgram* data); // Loads instance from a file, specifying if free or fixed format is used. - bool LoadFileWithMode(const std::string& source, bool free_form, + bool LoadFileWithMode(const std::string& file_name, bool free_form, LinearProgram* data); // Same as load file, but try free form mps on fail. @@ -174,7 +174,7 @@ class MPSReader { const std::string& row_value); // Stores a range constraint of value row_value for a row name. - void StoreRange(const std::string& row_name, const std::string& row_value); + void StoreRange(const std::string& row_name, const std::string& range_value); // Boolean set to true if the reader expects a free-form MPS file. bool free_form_; diff --git a/ortools/lp_data/proto_utils.h b/ortools/lp_data/proto_utils.h index b21efee438..a4d3a38333 100644 --- a/ortools/lp_data/proto_utils.h +++ b/ortools/lp_data/proto_utils.h @@ -15,7 +15,6 @@ #define OR_TOOLS_LP_DATA_PROTO_UTILS_H_ #include "ortools/linear_solver/linear_solver.pb.h" -#include "ortools/glop/parameters.pb.h" #include "ortools/lp_data/lp_data.h" namespace operations_research { diff --git a/ortools/lp_data/sparse.cc b/ortools/lp_data/sparse.cc index 985cf78f32..bbcbbdb2e6 100644 --- a/ortools/lp_data/sparse.cc +++ b/ortools/lp_data/sparse.cc @@ -400,7 +400,7 @@ std::string SparseMatrix::Dump() const { for (RowIndex row(0); row < num_rows_; ++row) { result.append("{ "); for (ColIndex col(0); col < num_cols; ++col) { - StringAppendF(&result, "%g ", ToDouble(LookUpValue(row, col))); + absl::StrAppendFormat(&result, "%g ", ToDouble(LookUpValue(row, col))); } result.append("}\n"); } diff --git a/ortools/lp_data/sparse.h b/ortools/lp_data/sparse.h index e50d3eabf5..4d2bc4207e 100644 --- a/ortools/lp_data/sparse.h +++ b/ortools/lp_data/sparse.h @@ -309,14 +309,15 @@ class CompactSparseMatrix { // Adds a dense column to the CompactSparseMatrix (only the non-zero will be // actually stored). This work in O(input.size()) and returns the index of the // added column. - ColIndex AddDenseColumn(const DenseColumn& input); + ColIndex AddDenseColumn(const DenseColumn& dense_column); // Same as AddDenseColumn(), but only adds the non-zero from the given start. - ColIndex AddDenseColumnPrefix(const DenseColumn& input, RowIndex start); + ColIndex AddDenseColumnPrefix(const DenseColumn& dense_column, + RowIndex start); // Same as AddDenseColumn(), but uses the given non_zeros pattern of input. // If non_zeros is empty, this actually calls AddDenseColumn(). - ColIndex AddDenseColumnWithNonZeros(const DenseColumn& input, + ColIndex AddDenseColumnWithNonZeros(const DenseColumn& dense_column, const std::vector& non_zeros); // Adds a dense column for which we know the non-zero positions and clears it. diff --git a/ortools/lp_data/sparse_vector.h b/ortools/lp_data/sparse_vector.h index 2021f3da5b..0a46c09533 100644 --- a/ortools/lp_data/sparse_vector.h +++ b/ortools/lp_data/sparse_vector.h @@ -134,7 +134,7 @@ class SparseVector { void ClearAndRelease(); // Reserve the underlying storage for the given number of entries. - void Reserve(EntryIndex size); + void Reserve(EntryIndex new_capacity); // Returns true if the vector is empty. bool IsEmpty() const; @@ -250,7 +250,7 @@ class SparseVector { // Same as AddMultipleToSparseVectorAndDeleteCommonIndex() but instead of // deleting the common index, leave it unchanged. void AddMultipleToSparseVectorAndIgnoreCommonIndex( - Fractional multiplier, Index ignored_common_index, + Fractional multiplier, Index removed_common_index, SparseVector* accumulator_vector) const; // Applies the index permutation to all entries: index = index_perm[index]; @@ -1042,7 +1042,8 @@ std::string SparseVector::DebugString() const { std::string s; for (const EntryIndex i : AllEntryIndices()) { if (i != 0) s += ", "; - StringAppendF(&s, "[%d]=%g", GetIndex(i).value(), GetCoefficient(i)); + absl::StrAppendFormat(&s, "[%d]=%g", GetIndex(i).value(), + GetCoefficient(i)); } return s; } diff --git a/ortools/sat/boolean_problem.cc b/ortools/sat/boolean_problem.cc index cbf9a2d648..82711a4fa0 100644 --- a/ortools/sat/boolean_problem.cc +++ b/ortools/sat/boolean_problem.cc @@ -47,10 +47,10 @@ namespace sat { using util::RemapGraph; void ExtractAssignment(const LinearBooleanProblem& problem, - const SatSolver& solver, std::vector* assignemnt) { - assignemnt->clear(); + const SatSolver& solver, std::vector* assignment) { + assignment->clear(); for (int i = 0; i < problem.num_variables(); ++i) { - assignemnt->push_back( + assignment->push_back( solver.Assignment().LiteralIsTrue(Literal(BooleanVariable(i), true))); } } @@ -71,24 +71,24 @@ std::string ValidateLinearTerms(const LinearTerms& terms, for (int i = 0; i < terms.literals_size(); ++i) { if (terms.literals(i) == 0) { if (++num_errs <= max_num_errs) { - err_str += StringPrintf("Zero literal at position %d\n", i); + err_str += absl::StrFormat("Zero literal at position %d\n", i); } } if (terms.coefficients(i) == 0) { if (++num_errs <= max_num_errs) { - err_str += StringPrintf("Literal %d has a zero coefficient\n", - terms.literals(i)); + err_str += absl::StrFormat("Literal %d has a zero coefficient\n", + terms.literals(i)); } } const int var = Literal(terms.literals(i)).Variable().value(); if (var >= variable_seen->size()) { if (++num_errs <= max_num_errs) { - err_str += StringPrintf("Out of bound variable %d\n", var); + err_str += absl::StrFormat("Out of bound variable %d\n", var); } } if ((*variable_seen)[var]) { if (++num_errs <= max_num_errs) { - err_str += StringPrintf("Duplicated variable %d\n", var); + err_str += absl::StrFormat("Duplicated variable %d\n", var); } } (*variable_seen)[var] = true; @@ -100,11 +100,12 @@ std::string ValidateLinearTerms(const LinearTerms& terms, } if (num_errs) { if (num_errs <= max_num_errs) { - err_str = StringPrintf("%d validation errors:\n", num_errs) + err_str; + err_str = absl::StrFormat("%d validation errors:\n", num_errs) + err_str; } else { - err_str = StringPrintf("%d validation errors; here are the first %d:\n", - num_errs, max_num_errs) + - err_str; + err_str = + absl::StrFormat("%d validation errors; here are the first %d:\n", + num_errs, max_num_errs) + + err_str; } } return err_str; @@ -132,15 +133,16 @@ util::Status ValidateBooleanProblem(const LinearBooleanProblem& problem) { const LinearBooleanConstraint& constraint = problem.constraints(i); const std::string error = ValidateLinearTerms(constraint, &variable_seen); if (!error.empty()) { - return util::Status(util::error::INVALID_ARGUMENT, - StringPrintf("Invalid constraint %i: ", i) + error); + return util::Status( + util::error::INVALID_ARGUMENT, + absl::StrFormat("Invalid constraint %i: ", i) + error); } } const std::string error = ValidateLinearTerms(problem.objective(), &variable_seen); if (!error.empty()) { return util::Status(util::error::INVALID_ARGUMENT, - StringPrintf("Invalid objective: ") + error); + absl::StrFormat("Invalid objective: ") + error); } return util::Status::OK; } @@ -432,8 +434,8 @@ std::string LinearBooleanProblemToCnfString( non_slack_objective.size()), hard_weight); } else { - output += StringPrintf("p cnf %d %d\n", problem.num_variables(), - problem.constraints_size()); + output += absl::StrFormat("p cnf %d %d\n", problem.num_variables(), + problem.constraints_size()); } std::string constraint_output; diff --git a/ortools/sat/clause.cc b/ortools/sat/clause.cc index ae34ffc02e..05ce16dd70 100644 --- a/ortools/sat/clause.cc +++ b/ortools/sat/clause.cc @@ -69,11 +69,13 @@ void LiteralWatchers::Resize(int num_variables) { // Note that this is the only place where we add Watcher so the DCHECK // guarantees that there are no duplicates. -void LiteralWatchers::AttachOnFalse(Literal a, Literal b, SatClause* clause) { +void LiteralWatchers::AttachOnFalse(Literal literal, Literal blocking_literal, + SatClause* clause) { SCOPED_TIME_STAT(&stats_); DCHECK(is_clean_); - DCHECK(!WatcherListContains(watchers_on_false_[a.Index()], *clause)); - watchers_on_false_[a.Index()].push_back(Watcher(clause, b)); + DCHECK(!WatcherListContains(watchers_on_false_[literal.Index()], *clause)); + watchers_on_false_[literal.Index()].push_back( + Watcher(clause, blocking_literal)); } bool LiteralWatchers::PropagateOnFalse(Literal false_literal, Trail* trail) { diff --git a/ortools/sat/cp_model.proto b/ortools/sat/cp_model.proto index 82a3787469..91348da1a4 100644 --- a/ortools/sat/cp_model.proto +++ b/ortools/sat/cp_model.proto @@ -131,12 +131,12 @@ message NoOverlap2DConstraintProto { repeated int32 y_intervals = 2; // Same size as x_intervals. } -// The sum of the demands * durations of the intervals at each interval point -// cannot exceed a capacity. Note that intervals are interpreted as [start, end) -// and as such intervals like [2,3) and [3,4) do not overlap for the point of -// view of this constraint. +// The sum of the demands of the intervals at each interval point +// cannot exceed a capacity. Note that intervals are interpreted as +// [start, end) and as such intervals like [2,3) and [3,4) do not +// overlap for the point of view of this constraint. // -// Note: this enforce that all start <= end. +// Note: this enforces that all start <= end. message CumulativeConstraintProto { int32 capacity = 1; repeated int32 intervals = 2; @@ -186,8 +186,8 @@ message CircuitConstraintProto { // - #incoming arcs == 1 except for node 0. // - #outgoing arcs == 1 except for node 0. // - for node zero, #incoming arcs == #outgoing arcs. -// - There is no duplicate arcs. -// - Self-arc are allowed except for node 0. +// - There are no duplicate arcs. +// - Self-arcs are allowed except for node 0. // - There is no cycle in this graph, except through node 0. // // TODO(user): It is probably possible to generalize this constraint to a @@ -268,29 +268,71 @@ message ConstraintProto { // The actual constraint with its arguments. oneof constraint { - BoolArgumentProto bool_or = 3; // OR(literals) is true. - BoolArgumentProto bool_and = 4; // AND(literals) is true. - // XOR(literals) is true. For more than two literals, note that - // (a_1 XOR a_2 XOR ... XOR a_n) is equivalent to an odd number the a_i - // being true. + // The bool_or constraint forces at least one literal to be true. + BoolArgumentProto bool_or = 3; + + // The bool_and constraint forces all of the literals to be true. + BoolArgumentProto bool_and = 4; + + // The bool_xor constraint forces an odd number of the literals to be true. BoolArgumentProto bool_xor = 5; - IntegerArgumentProto int_div = 7; // target = vars[0] / vars[1] - IntegerArgumentProto int_mod = 8; // target = vars[0] % vars[1] - IntegerArgumentProto int_max = 9; // target = MAX(vars) - IntegerArgumentProto int_min = 10; // target = MIN(vars) - IntegerArgumentProto int_prod = 11; // target = PROD(vars) + // The int_div constraint forces the target to equal vars[0] / vars[1]. + IntegerArgumentProto int_div = 7; + // The int_mod constraint forces the target to equal vars[0] % vars[1]. + IntegerArgumentProto int_mod = 8; + + // The int_max constraint forces the target to equal the maximum of all + // variables. + IntegerArgumentProto int_max = 9; + + // The int_min constraint forces the target to equal the minimum of all + // variables. + IntegerArgumentProto int_min = 10; + + // The int_min constraint forces the target to equal the product of all + // variables. + IntegerArgumentProto int_prod = 11; + + // The linear constraint enforces a linear inequality among the variables, + // such as 0 <= x + 2y <= 10. LinearConstraintProto linear = 12; + + // The all_diff constraint forces all variables to take different values. AllDifferentConstraintProto all_diff = 13; + + // The element constraint forces the variable with the given index + // to be equal to the target. ElementConstraintProto element = 14; + + // The circuit constraint takes a graph and forces the arcs present + // (with arc presence indicated by a literal) to form a unique cycle. CircuitConstraintProto circuit = 15; + + // The routes constraint implements the vehicle routing problem. RoutesConstraintProto routes = 23; + + // The circuit_covering constraint is similar to the circuit constraint, + // but allows multiple non-overlapping cycles instead of just one. CircuitCoveringConstraintProto circuit_covering = 25; + + // The table constraint enforces what values a tuple of variables may + // take. TableConstraintProto table = 16; + + // The automata constraint forces a sequence of variables to be accepted + // by an automaton. AutomataConstraintProto automata = 17; + + // The inverse constraint forces two arrays to be inverses of each other: + // the values of one are the indices of the other, and vice versa. InverseConstraintProto inverse = 18; + + // The reservoir constraint forces the sum of a set of active demands + // to always be between a specified minimum and maximum value during + // specific times. ReservoirConstraintProto reservoir = 24; // Constraints on intervals. @@ -300,10 +342,23 @@ message ConstraintProto { // enforcement_literal set to false are ignored by these constraints. // // TODO(user): Explain what happen for intervals of size zero. Some - // constraints ignore them, other do take them into account. + // constraints ignore them; others do take them into account. + + // The interval constraint takes a start, end, and size, and forces + // start + size == end. IntervalConstraintProto interval = 19; + + // The no_overlap constraint prevents a set of intervals from + // overlapping; in scheduling, this is called a disjunctive + // constraint. NoOverlapConstraintProto no_overlap = 20; + + // The no_overlap_2d constraint prevents a set of boxes from overlapping. NoOverlap2DConstraintProto no_overlap_2d = 21; + + // The cumulative constraint ensures that for any integer point, the sum + // of the demands of the intervals containing that point does not exceed + // the capacity. CumulativeConstraintProto cumulative = 22; } } diff --git a/ortools/sat/cp_model_objective.cc b/ortools/sat/cp_model_objective.cc index a9b2a94bbf..5fe06ac4c5 100644 --- a/ortools/sat/cp_model_objective.cc +++ b/ortools/sat/cp_model_objective.cc @@ -40,13 +40,32 @@ void EncodeObjectiveAsSingleVariable(CpModelProto* cp_model) { return; } + // Compute trivial bounds on the objective, this is needed otherwise the + // overflow checker might not be happy with the new constraint we are about + // to create. Note that the model validator should make sure that there is no + // overflow in the computation below. + int64 min_obj = 0; + int64 max_obj = 0; + for (int i = 0; i < cp_model->objective().vars_size(); ++i) { + const int ref = cp_model->objective().vars(i); + const int var = PositiveRef(ref); + const int64 coeff = + cp_model->objective().coeffs(i) * (RefIsPositive(ref) ? 1 : -1); + const int64 value1 = cp_model->variables(var).domain(0) * coeff; + const int64 value2 = cp_model->variables(var).domain( + cp_model->variables(var).domain_size() - 1) * + coeff; + min_obj += std::min(value1, value2); + max_obj += std::max(value1, value2); + } + // Create the new objective var. const int obj_ref = cp_model->variables_size(); { IntegerVariableProto* obj = cp_model->add_variables(); if (cp_model->objective().domain().empty()) { - obj->add_domain(kint64min); - obj->add_domain(kint64max); + obj->add_domain(min_obj); + obj->add_domain(max_obj); } else { *(obj->mutable_domain()) = cp_model->objective().domain(); cp_model->mutable_objective()->clear_domain(); diff --git a/ortools/sat/cp_model_presolve.cc b/ortools/sat/cp_model_presolve.cc index 2981f14142..aa323b1dac 100644 --- a/ortools/sat/cp_model_presolve.cc +++ b/ortools/sat/cp_model_presolve.cc @@ -1351,6 +1351,8 @@ bool PresolveCircuit(ConstraintProto* ct, PresolveContext* context) { if (HasEnforcementLiteral(*ct)) return false; CircuitConstraintProto& proto = *ct->mutable_circuit(); + // Convert the flat structure to a graph, note that we includes all the arcs + // here (even if they are at false). std::vector> incoming_arcs; std::vector> outgoing_arcs; const int num_arcs = proto.literals_size(); @@ -1360,7 +1362,6 @@ bool PresolveCircuit(ConstraintProto* ct, PresolveContext* context) { const int tail = proto.tails(i); const int head = proto.heads(i); num_nodes = std::max(num_nodes, std::max(tail, head) + 1); - if (context->LiteralIsFalse(ref)) continue; if (std::max(tail, head) >= incoming_arcs.size()) { incoming_arcs.resize(std::max(tail, head) + 1); outgoing_arcs.resize(std::max(tail, head) + 1); @@ -1404,15 +1405,12 @@ bool PresolveCircuit(ConstraintProto* ct, PresolveContext* context) { } // Remove false arcs. - // - // TODO(user): all the outgoing/incoming arc of a node should not be all false - // at the same time. Report unsat in this case. Note however that this part is - // not well defined since if a node have no incoming/outgoing arcs in the - // initial proto, it will just be ignored. int new_size = 0; int num_true = 0; int circuit_start = -1; std::vector next(num_nodes, -1); + std::vector new_in_degree(num_nodes, 0); + std::vector new_out_degree(num_nodes, 0); for (int i = 0; i < num_arcs; ++i) { const int ref = proto.literals(i); if (context->LiteralIsFalse(ref)) continue; @@ -1427,12 +1425,30 @@ bool PresolveCircuit(ConstraintProto* ct, PresolveContext* context) { } ++num_true; } + ++new_out_degree[proto.tails(i)]; + ++new_in_degree[proto.heads(i)]; proto.set_tails(new_size, proto.tails(i)); proto.set_heads(new_size, proto.heads(i)); proto.set_literals(new_size, proto.literals(i)); ++new_size; } + // Detect infeasibility due to a node having no more incoming or outgoing arc. + // This is a bit tricky because for now the meaning of the constraint says + // that all nodes that appear in at least one of the arcs must be in the + // circuit or have a self-arc. So if any such node ends up with an incoming or + // outgoing degree of zero once we remove false arcs then the constraint is + // infeasible! + for (int i = 0; i < num_nodes; ++i) { + // Skip initially ignored node. + if (incoming_arcs[i].empty() && outgoing_arcs[i].empty()) continue; + + if (new_in_degree[i] == 0 || new_out_degree[i] == 0) { + context->is_unsat = true; + return true; + } + } + // Test if a subcircuit is already present. if (circuit_start != -1) { std::vector visited(num_nodes, false); @@ -1990,7 +2006,7 @@ void PresolveCpModel(bool log_info, CpModelProto* presolved_model, const int var = PositiveRef(ref); if (var == objective_var) continue; - int coeff = -ct.linear().coeffs(i) * factor; + int64 coeff = -ct.linear().coeffs(i) * factor; if (!RefIsPositive(ref)) coeff = -coeff; if (!gtl::ContainsKey(objective_map, var)) { context.var_to_constraints[var].insert(-1); diff --git a/ortools/sat/cp_model_solver.cc b/ortools/sat/cp_model_solver.cc index d4003d14ae..1fc6e143ff 100644 --- a/ortools/sat/cp_model_solver.cc +++ b/ortools/sat/cp_model_solver.cc @@ -1784,22 +1784,6 @@ void TryToLinearizeConstraint( const int target = ct.int_prod().target(); const int size = ct.int_prod().vars_size(); - // We just linearize x = y^2 by x >= y which is far from ideal but at - // least pushes x when y moves away from zero. Note that if y is negative, - // we should probably also add x >= -y, but then this do not happen in - // our test set. - if (size == 2 && ct.int_prod().vars(0) == ct.int_prod().vars(1)) { - LinearConstraintBuilder lc(m->model(), -kInfinity, 0.0); - lc.AddTerm(m->Integer(ct.int_prod().vars(0)), 1.0); - lc.AddTerm(m->Integer(target), -1.0); - linear_constraints->push_back(lc.Build()); - } - } else if (ct.constraint_case() == - ConstraintProto::ConstraintCase::kIntProd) { - if (HasEnforcementLiteral(ct)) return; - const int target = ct.int_prod().target(); - const int size = ct.int_prod().vars_size(); - // We just linearize x = y^2 by x >= y which is far from ideal but at // least pushes x when y moves away from zero. Note that if y is negative, // we should probably also add x >= -y, but then this do not happen in @@ -1897,8 +1881,8 @@ void TryToLinearizeConstraint( [m](std::map>* node_map, int node) { if (!gtl::ContainsKey(*node_map, node)) { - (*node_map)[node].reset( - new LinearConstraintBuilder(m->model(), 1, 1)); + (*node_map)[node] = + absl::make_unique(m->model(), 1, 1); } return (*node_map)[node].get(); }; diff --git a/ortools/sat/cumulative.cc b/ortools/sat/cumulative.cc index 1778e1e504..16acef23e6 100644 --- a/ortools/sat/cumulative.cc +++ b/ortools/sat/cumulative.cc @@ -33,7 +33,7 @@ namespace sat { std::function Cumulative( const std::vector& vars, - const std::vector& demands, + const std::vector& demand_vars, const IntegerVariable& capacity) { return [=](Model* model) { if (vars.empty()) return; @@ -45,16 +45,16 @@ std::function Cumulative( // for each task. Also ensure that no task consumes more resource than what // is available. This is useful because the subsequent propagators do not // filter the capacity variable very well. - for (int i = 0; i < demands.size(); ++i) { + for (int i = 0; i < demand_vars.size(); ++i) { if (intervals->MaxSize(vars[i]) == 0) continue; if (intervals->MinSize(vars[i]) > 0) { - if (demands[i] == capacity) continue; + if (demand_vars[i] == capacity) continue; if (intervals->IsOptional(vars[i])) { model->Add(ConditionalLowerOrEqual( - demands[i], capacity, intervals->IsPresentLiteral(vars[i]))); + demand_vars[i], capacity, intervals->IsPresentLiteral(vars[i]))); } else { - model->Add(LowerOrEqual(demands[i], capacity)); + model->Add(LowerOrEqual(demand_vars[i], capacity)); } continue; } @@ -69,10 +69,11 @@ std::function Cumulative( Literal(model->Add(NewBooleanVariable()), true); model->Add(ReifiedBoolAnd( {size_condition, intervals->IsPresentLiteral(vars[i])}, condition)); - model->Add(ConditionalLowerOrEqual(demands[i], capacity, condition)); + model->Add( + ConditionalLowerOrEqual(demand_vars[i], capacity, condition)); } else { model->Add( - ConditionalLowerOrEqual(demands[i], capacity, size_condition)); + ConditionalLowerOrEqual(demand_vars[i], capacity, size_condition)); } } @@ -93,7 +94,7 @@ std::function Cumulative( std::vector in_disjunction; for (int i = 0; i < vars.size(); ++i) { if (intervals->MinSize(vars[i]) > 0 && - 2 * model->Get(LowerBound(demands[i])) > + 2 * model->Get(LowerBound(demand_vars[i])) > model->Get(UpperBound(capacity))) { in_disjunction.push_back(vars[i]); } @@ -130,7 +131,7 @@ std::function Cumulative( // increases the minimum of the start variables, decrease the maximum of the // end variables, and increase the minimum of the capacity variable. TimeTablingPerTask* time_tabling = - new TimeTablingPerTask(demands, capacity, integer_trail, helper); + new TimeTablingPerTask(demand_vars, capacity, integer_trail, helper); time_tabling->RegisterWith(model->GetOrCreate()); model->TakeOwnership(time_tabling); @@ -138,7 +139,7 @@ std::function Cumulative( // It increases the minimum of the capacity variable. if (parameters.use_overload_checker_in_cumulative_constraint()) { OverloadChecker* overload_checker = new OverloadChecker( - vars, demands, capacity, trail, integer_trail, intervals); + vars, demand_vars, capacity, trail, integer_trail, intervals); overload_checker->RegisterWith( model->GetOrCreate()); model->TakeOwnership(overload_checker); @@ -149,7 +150,7 @@ std::function Cumulative( // maximum of the end variables, if (parameters.use_timetable_edge_finding_in_cumulative_constraint()) { TimeTableEdgeFinding* time_table_edge_finding = new TimeTableEdgeFinding( - vars, demands, capacity, trail, integer_trail, intervals); + vars, demand_vars, capacity, trail, integer_trail, intervals); time_table_edge_finding->RegisterWith( model->GetOrCreate()); model->TakeOwnership(time_table_edge_finding); diff --git a/ortools/sat/disjunctive.cc b/ortools/sat/disjunctive.cc index 70f88b0896..3ea41d4f32 100644 --- a/ortools/sat/disjunctive.cc +++ b/ortools/sat/disjunctive.cc @@ -52,37 +52,47 @@ std::function Disjunctive( GenericLiteralWatcher* watcher = model->GetOrCreate(); - // We decided to create the propagators in this particular order, but it - // shouldn't matter much because of the different priorities used. - { - // Only one direction is needed by this one. - DisjunctiveOverloadChecker* overload_checker = - new DisjunctiveOverloadChecker(true, helper); - const int id = overload_checker->RegisterWith(watcher); - watcher->SetPropagatorPriority(id, 1); - model->TakeOwnership(overload_checker); - } - for (const bool time_direction : {true, false}) { - DisjunctiveDetectablePrecedences* detectable_precedences = - new DisjunctiveDetectablePrecedences(time_direction, helper); - const int id = detectable_precedences->RegisterWith(watcher); - watcher->SetPropagatorPriority(id, 2); - model->TakeOwnership(detectable_precedences); - } - for (const bool time_direction : {true, false}) { - DisjunctiveNotLast* not_last = - new DisjunctiveNotLast(time_direction, helper); - const int id = not_last->RegisterWith(watcher); - watcher->SetPropagatorPriority(id, 3); - model->TakeOwnership(not_last); - } - for (const bool time_direction : {true, false}) { - DisjunctiveEdgeFinding* edge_finding = - new DisjunctiveEdgeFinding(time_direction, helper); - const int id = edge_finding->RegisterWith(watcher); - watcher->SetPropagatorPriority(id, 4); - model->TakeOwnership(edge_finding); + if (vars.size() == 2) { + DisjunctiveWithTwoItems* propagator = new DisjunctiveWithTwoItems(helper); + propagator->RegisterWith(watcher); + model->TakeOwnership(propagator); + } else { + // We decided to create the propagators in this particular order, but it + // shouldn't matter much because of the different priorities used. + { + // Only one direction is needed by this one. + DisjunctiveOverloadChecker* overload_checker = + new DisjunctiveOverloadChecker(true, helper); + const int id = overload_checker->RegisterWith(watcher); + watcher->SetPropagatorPriority(id, 1); + model->TakeOwnership(overload_checker); + } + for (const bool time_direction : {true, false}) { + DisjunctiveDetectablePrecedences* detectable_precedences = + new DisjunctiveDetectablePrecedences(time_direction, helper); + const int id = detectable_precedences->RegisterWith(watcher); + watcher->SetPropagatorPriority(id, 2); + model->TakeOwnership(detectable_precedences); + } + for (const bool time_direction : {true, false}) { + DisjunctiveNotLast* not_last = + new DisjunctiveNotLast(time_direction, helper); + const int id = not_last->RegisterWith(watcher); + watcher->SetPropagatorPriority(id, 3); + model->TakeOwnership(not_last); + } + for (const bool time_direction : {true, false}) { + DisjunctiveEdgeFinding* edge_finding = + new DisjunctiveEdgeFinding(time_direction, helper); + const int id = edge_finding->RegisterWith(watcher); + watcher->SetPropagatorPriority(id, 4); + model->TakeOwnership(edge_finding); + } } + + // Note that we keep this one even when there is just two intervals. This is + // because it might push a variable that is after both of the intervals + // using the fact that they are in disjunction. if (model->GetOrCreate() ->use_precedences_in_disjunctive_constraint()) { for (const bool time_direction : {true, false}) { @@ -186,6 +196,71 @@ IntegerValue TaskSet::ComputeEndMin(int task_to_ignore, return end_min; } +bool DisjunctiveWithTwoItems::Propagate() { + DCHECK_EQ(helper_->NumTasks(), 2); + + // We can't propagate anything if one of the interval is absent for sure. + if (helper_->IsAbsent(0) || helper_->IsAbsent(1)) return true; + + // Note that this propagation also take care of the "overload checker" part. + // It also propagates as much as possible, even in the presence of task with + // variable durations. + // + // TODO(user): For optional interval whose presense in unknown and without + // optional variable, the end-min may not be propagated to at least (start_min + // + duration_min). Consider that into the computation so we may decide the + // interval forced absence? Same for the start-max. + int task_before = 0; + int task_after = 1; + if (helper_->StartMax(0) < helper_->EndMin(1)) { + // Task 0 must be before task 1. + } else if (helper_->StartMax(1) < helper_->EndMin(0)) { + // Task 1 must be before task 0. + std::swap(task_before, task_after); + } else { + return true; + } + + if (helper_->IsPresent(task_before) && + helper_->StartMin(task_after) < helper_->EndMin(task_before)) { + // Reason for precedences if both present. + helper_->ClearReason(); + helper_->AddStartMaxReason(task_before, helper_->EndMin(task_after) - 1); + helper_->AddEndMinReason(task_after, helper_->EndMin(task_after)); + + // Reason for the bound push. + helper_->AddPresenceReason(task_before); + helper_->AddEndMinReason(task_before, helper_->EndMin(task_before)); + if (!helper_->IncreaseStartMin(task_after, helper_->EndMin(task_before))) { + return false; + } + } + + if (helper_->IsPresent(task_after) && + helper_->EndMax(task_before) > helper_->StartMax(task_after)) { + // Reason for precedences if both present. + helper_->ClearReason(); + helper_->AddStartMaxReason(task_before, helper_->EndMin(task_after) - 1); + helper_->AddEndMinReason(task_after, helper_->EndMin(task_after)); + + // Reason for the bound push. + helper_->AddPresenceReason(task_after); + helper_->AddStartMaxReason(task_after, helper_->StartMax(task_after)); + if (!helper_->DecreaseEndMax(task_before, helper_->StartMax(task_after))) { + return false; + } + } + + return true; +} + +int DisjunctiveWithTwoItems::RegisterWith(GenericLiteralWatcher* watcher) { + // This propagator reach the fix point in one pass. + const int id = watcher->Register(this); + helper_->WatchAllTasks(id, watcher); + return id; +} + // TODO(user): Improve the Overload Checker using delayed insertion. // We insert events at the cost of O(log n) per insertion, and this is where // the algorithm spends most of its time, thus it is worth improving. @@ -403,29 +478,39 @@ bool DisjunctivePrecedences::Propagate() { task_is_currently_present_, &before_); // We don't care about the initial content of this vector. - reason_for_being_before_.resize(num_tasks, absl::InlinedVector()); + task_to_arc_index_.resize(num_tasks); int critical_index; const int size = before_.size(); for (int i = 0; i < size;) { const IntegerVariable var = before_[i].var; DCHECK_NE(var, kNoIntegerVariable); task_set_.Clear(); + const int initial_i = i; + IntegerValue min_offset = before_[i].offset; for (; i < size && before_[i].var == var; ++i) { const int task = before_[i].index; - reason_for_being_before_[task] = before_[i].reason; + min_offset = std::min(min_offset, before_[i].offset); task_set_.AddUnsortedEntry( {task, helper_->StartMin(task), helper_->DurationMin(task)}); } - if (task_set_.SortedTasks().size() < 2) continue; + DCHECK_GE(task_set_.SortedTasks().size(), 2); if (integer_trail_->IsCurrentlyIgnored(var)) continue; task_set_.Sort(); - const IntegerValue end_min = - task_set_.ComputeEndMin(/*task_to_ignore=*/-1, &critical_index); - if (end_min > integer_trail_->LowerBound(var)) { + // TODO(user): Only use the min_offset of the critical task? Or maybe do a + // more general computation to find by how much we can push var? + const IntegerValue new_lb = + task_set_.ComputeEndMin(/*task_to_ignore=*/-1, &critical_index) + + min_offset; + if (new_lb > integer_trail_->LowerBound(var)) { const std::vector& sorted_tasks = task_set_.SortedTasks(); helper_->ClearReason(); + // Fill task_to_arc_index_ since we need it for the reason. + for (int j = initial_i; j < i; ++j) { + task_to_arc_index_[before_[j].index] = before_[j].arc_index; + } + const IntegerValue window_start = sorted_tasks[critical_index].start_min; for (int i = critical_index; i < sorted_tasks.size(); ++i) { const int ct = sorted_tasks[i].task; @@ -434,15 +519,15 @@ bool DisjunctivePrecedences::Propagate() { helper_->AddPresenceReason(ct); helper_->AddDurationMinReason(ct); helper_->AddStartMinReason(ct, window_start); - for (const Literal l : reason_for_being_before_[ct]) { - helper_->MutableLiteralReason()->push_back(l.Negated()); - } + precedences_->AddPrecedenceReason(task_to_arc_index_[ct], min_offset, + helper_->MutableLiteralReason(), + helper_->MutableIntegerReason()); } // TODO(user): If var is actually a start-min of an interval, we // could push the end-min and check the interval consistency right away. if (!helper_->PushIntegerLiteral( - IntegerLiteral::GreaterOrEqual(var, end_min))) { + IntegerLiteral::GreaterOrEqual(var, new_lb))) { return false; } } diff --git a/ortools/sat/disjunctive.h b/ortools/sat/disjunctive.h index 1ea6075766..e87529501f 100644 --- a/ortools/sat/disjunctive.h +++ b/ortools/sat/disjunctive.h @@ -218,10 +218,25 @@ class DisjunctivePrecedences : public PropagatorInterface { TaskSet task_set_; std::vector task_is_currently_present_; - std::vector> reason_for_being_before_; + std::vector task_to_arc_index_; std::vector before_; }; +// This is an optimization for the case when we have a big number of such +// pairwise constraints. This should be roughtly equivalent to what the general +// disjunctive case is doing, but it dealt with variable size better and has a +// lot less overhead. +class DisjunctiveWithTwoItems : public PropagatorInterface { + public: + explicit DisjunctiveWithTwoItems(SchedulingConstraintHelper* helper) + : helper_(helper) {} + bool Propagate() final; + int RegisterWith(GenericLiteralWatcher* watcher); + + private: + SchedulingConstraintHelper* helper_; +}; + } // namespace sat } // namespace operations_research diff --git a/ortools/sat/doc/boolean_logic.md b/ortools/sat/doc/boolean_logic.md index e760909366..97e9062cd5 100644 --- a/ortools/sat/doc/boolean_logic.md +++ b/ortools/sat/doc/boolean_logic.md @@ -90,7 +90,7 @@ public class LiteralSample { CpModel model = new CpModel(); IntVar x = model.newBoolVar("x"); ILiteral notX = x.not(); - System.out.println(notX.shortString()); + System.out.println(notX.getShortString()); } } ``` @@ -359,6 +359,16 @@ import com.google.ortools.sat.CpModel; import com.google.ortools.sat.ILiteral; import com.google.ortools.sat.IntVar; +/** + * Reification is the action of associating a Boolean variable to a constraint. This boolean + * enforces or prohibits the constraint according to the value the Boolean variable is fixed to. + * + *

Half-reification is defined as a simple implication: If the Boolean variable is true, then the + * constraint holds, instead of an complete equivalence. + * + *

The SAT solver offers half-reification. To implement full reification, two half-reified + * constraints must be used. + */ public class ReifiedSample { static { System.loadLibrary("jniortools"); } @@ -370,14 +380,14 @@ public class ReifiedSample { IntVar y = model.newBoolVar("y"); IntVar b = model.newBoolVar("b"); - // First version using a half-reified bool and. + // Version 1: a half-reified boolean and. model.addBoolAnd(new ILiteral[] {x, y.not()}).onlyEnforceIf(b); - // Second version using implications. + // Version 2: implications. model.addImplication(b, x); model.addImplication(b, y.not()); - // Third version using bool or. + // Version 3: boolean or. model.addBoolOr(new ILiteral[] {b.not(), x}); model.addBoolOr(new ILiteral[] {b.not(), y.not()}); } diff --git a/ortools/sat/doc/channeling.md b/ortools/sat/doc/channeling.md index 70dd1153b1..39191203e0 100644 --- a/ortools/sat/doc/channeling.md +++ b/ortools/sat/doc/channeling.md @@ -105,11 +105,11 @@ ChannelingSample() ### C++ code ```cpp -#include "ortools/sat/cp_model.proto.h" +#include "ortools/sat/cp_model.pb.h" #include "ortools/sat/cp_model_solver.h" #include "ortools/sat/cp_model_utils.h" #include "ortools/sat/model.h" -#include "ortools/sat/sat_parameters.proto.h" +#include "ortools/sat/sat_parameters.pb.h" namespace operations_research { namespace sat { @@ -186,7 +186,6 @@ int main() { return EXIT_SUCCESS; } - ``` ### Java code @@ -228,29 +227,31 @@ public class ChannelingSample { DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_FIRST, DecisionStrategyProto.DomainReductionStrategy.SELECT_MIN_VALUE); - // Creates a solver and solve with a fixed search. + // Creates the solver. CpSolver solver = new CpSolver(); - // Forces solver to follow the decision strategy exactly. + // Forces the solver to follow the decision strategy exactly. solver.getParameters().setSearchBranching(SatParameters.SearchBranching.FIXED_SEARCH); - // And solves the problem with the printer callback. - solver.searchAllSolutions(model, new CpSolverSolutionCallback() { - public CpSolverSolutionCallback init(IntVar[] variables) { - variables_ = variables; - return this; - } - - @Override - public void onSolutionCallback() { - for (IntVar v : variables_) { - System.out.print(String.format("%s=%d ", v.getName(), value(v))); + // Solves the problem with the printer callback. + solver.searchAllSolutions( + model, + new CpSolverSolutionCallback() { + public CpSolverSolutionCallback init(IntVar[] variables) { + variableArray = variables; + return this; } - System.out.println(); - } - private IntVar[] variables_; - }.init(new IntVar[] {x, y, b})); + @Override + public void onSolutionCallback() { + for (IntVar v : variableArray) { + System.out.printf("%s=%d ", v.getName(), value(v)); + } + System.out.println(); + } + + private IntVar[] variableArray; + }.init(new IntVar[] {x, y, b})); } } ``` @@ -326,7 +327,6 @@ public class CodeSamplesSat ChannelingSample(); } } - ``` ## A bin-packing problem diff --git a/ortools/sat/doc/index.md b/ortools/sat/doc/index.md index 9210af16ff..dfad3ae50a 100644 --- a/ortools/sat/doc/index.md +++ b/ortools/sat/doc/index.md @@ -88,7 +88,7 @@ int main() { ## Java code samples The Java code implements the same interface as the Python code, with a -**CpModel**, and a **CpSolver** class. +**CpModel** and a **CpSolver** class. ```java import com.google.ortools.sat.CpModel; @@ -111,7 +111,7 @@ public class CodeSample { ## C\# code samples The C\# code implements the same interface as the Python code, with a -**CpModel**, and a **CpSolver** class. +**CpModel** and a **CpSolver** class. ```cs diff --git a/ortools/sat/doc/integer_arithmetic.md b/ortools/sat/doc/integer_arithmetic.md index 102c1118d4..ae8e59dd7b 100644 --- a/ortools/sat/doc/integer_arithmetic.md +++ b/ortools/sat/doc/integer_arithmetic.md @@ -159,6 +159,10 @@ import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.IntVar; +/** + * In a field of rabbits and pheasants, there are 20 heads and 56 legs. How many rabbits and + * pheasants are there? + */ public class RabbitsAndPheasants { static { System.loadLibrary("jniortools"); } diff --git a/ortools/sat/doc/scheduling.md b/ortools/sat/doc/scheduling.md index 8ebaf13094..0f3d365a0d 100644 --- a/ortools/sat/doc/scheduling.md +++ b/ortools/sat/doc/scheduling.md @@ -116,9 +116,9 @@ public class IntervalSample { CpModel model = new CpModel(); int horizon = 100; IntVar startVar = model.newIntVar(0, horizon, "start"); + IntVar endVar = model.newIntVar(0, horizon, "end"); // Java code supports IntVar or integer constants in intervals. int duration = 10; - IntVar endVar = model.newIntVar(0, horizon, "end"); IntervalVar interval = model.newIntervalVar(startVar, duration, endVar, "interval"); System.out.println(interval); @@ -263,9 +263,9 @@ public class OptionalIntervalSample { CpModel model = new CpModel(); int horizon = 100; IntVar startVar = model.newIntVar(0, horizon, "start"); + IntVar endVar = model.newIntVar(0, horizon, "end"); // Java code supports IntVar or integer constants in intervals. int duration = 10; - IntVar endVar = model.newIntVar(0, horizon, "end"); ILiteral presence = model.newBoolVar("presence"); IntervalVar interval = model.newOptionalIntervalVar(startVar, duration, endVar, presence, "interval"); @@ -510,6 +510,10 @@ import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.IntVar; import com.google.ortools.sat.IntervalVar; +/** + * We want to schedule 3 tasks on 3 weeks excluding weekends, making the final day as early as + * possible. + */ public class NoOverlapSample { static { System.loadLibrary("jniortools"); } @@ -542,7 +546,9 @@ public class NoOverlapSample { IntervalVar weekend1 = model.newFixedInterval(12, 2, "weekend1"); IntervalVar weekend2 = model.newFixedInterval(19, 2, "weekend2"); - // No Overlap constraint. + // No Overlap constraint. This constraint enforces that no two intervals can overlap. + // In this example, as we use 3 fixed intervals that span over weekends, this constraint makes + // sure that all tasks are executed on weekdays. model.addNoOverlap(new IntervalVar[] {task0, task1, task2, weekend0, weekend1, weekend2}); // Makespan objective. @@ -1063,6 +1069,10 @@ import com.google.ortools.sat.IntervalVar; import java.util.ArrayList; import java.util.List; +// This code takes a list of interval variables in a noOverlap constraint, and a parallel list of +// integer variables and enforces the following constraint: +// - rank[i] == -1 iff interval[i] is not active. +// - rank[i] == number of active intervals that precede interval[i]. public class RankingSample { static { System.loadLibrary("jniortools"); } @@ -1079,15 +1089,16 @@ public class RankingSample { } else { IntVar prec = model.newBoolVar(String.format("%d before %d", i, j)); precedences[i][j] = prec; + // Ensure that task i precedes task j if prec is true. model.addLessOrEqualWithOffset(starts[i], starts[j], 1).onlyEnforceIf(prec); } } } - // Treats optional intervals. + // Create optional intervals. for (int i = 0; i < numTasks - 1; ++i) { for (int j = i + 1; j < numTasks; ++j) { - List list = new ArrayList(); + List list = new ArrayList<>(); list.add(precedences[i][j]); list.add(precedences[j][i]); list.add(presences[i].not()); @@ -1098,12 +1109,13 @@ public class RankingSample { // Makes sure that if j is not performed, all precedences are false. model.addImplication(presences[j].not(), precedences[i][j].not()); model.addImplication(presences[j].not(), precedences[j][i].not()); - // The following bool_or will enforce that for any two intervals: + // The following boolOr will enforce that for any two intervals: // i precedes j or j precedes i or at least one interval is not // performed. - model.addBoolOr(list.toArray(new ILiteral[list.size()])); - // Redundant constraint: it propagates early that at most one precedence - // is true. + model.addBoolOr(list.toArray(new ILiteral[0])); + // For efficiency, we add a redundant constraint declaring that only one of i precedes j and + // j precedes i are true. This will speed up the solve because the reason of this + // propagation is shorter that using interval bounds is true. model.addImplication(precedences[i][j], precedences[j][i].not()); model.addImplication(precedences[j][i], precedences[i][j].not()); } @@ -1126,7 +1138,6 @@ public class RankingSample { public static void main(String[] args) throws Exception { CpModel model = new CpModel(); - // Three weeks. int horizon = 100; int numTasks = 4; @@ -1153,7 +1164,7 @@ public class RankingSample { starts[t], duration, ends[t], presences[t], "o_interval_" + t); } - // Ranks = -1 if and only if the tasks is not performed. + // The rank will be -1 iff the task is not performed. ranks[t] = model.newIntVar(-1, numTasks - 1, "rank_" + t); } @@ -1171,9 +1182,13 @@ public class RankingSample { for (int t = 0; t < numTasks; ++t) { model.addLessOrEqual(ends[t], makespan).onlyEnforceIf(presences[t]); } - // Minimizes makespan - fixed gain per tasks performed. - // As the fixed cost is less that the duration of the last interval, - // the solver will not perform the last interval. + // The objective function is a mix of a fixed gain per task performed, and a fixed cost for each + // additional day of activity. + // The solver will balance both cost and gain and minimize makespan * per-day-penalty - number + // of tasks performed * per-task-gain. + // + // On this problem, as the fixed cost is less that the duration of the last interval, the solver + // will not perform the last interval. IntVar[] objectiveVars = new IntVar[numTasks + 1]; int[] objectiveCoefs = new int[numTasks + 1]; for (int t = 0; t < numTasks; ++t) { @@ -1193,14 +1208,12 @@ public class RankingSample { System.out.println("Makespan: " + solver.value(makespan)); for (int t = 0; t < numTasks; ++t) { if (solver.booleanValue(presences[t])) { - System.out.println( - String.format( - "Task %d starts at %d with rank %d", - t, solver.value(starts[t]), solver.value(ranks[t]))); + System.out.printf( + "Task %d starts at %d with rank %d%n", + t, solver.value(starts[t]), solver.value(ranks[t])); } else { - System.out.println( - String.format( - "Task %d in not performed and ranked at %d", t, solver.value(ranks[t]))); + System.out.printf( + "Task %d in not performed and ranked at %d%n", t, solver.value(ranks[t])); } } } else { diff --git a/ortools/sat/doc/solver.md b/ortools/sat/doc/solver.md index 11c0de2fb3..928cf5a473 100644 --- a/ortools/sat/doc/solver.md +++ b/ortools/sat/doc/solver.md @@ -102,8 +102,7 @@ int main() { ### Java code -As in python, the CpSolver class encapsulates searching for a solution of a -model. +As in Python, the CpSolver class encapsulates searching for a solution. ```java import com.google.ortools.sat.CpSolverStatus; @@ -111,23 +110,24 @@ import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.IntVar; +/** Solve a simple problem with three variables and one different constraint. */ public class SimpleSolve { static { System.loadLibrary("jniortools"); } public static void main(String[] args) throws Exception { - // Creates the model. + // Create the model. CpModel model = new CpModel(); - // Creates the variables. + // Create the variables. int numVals = 3; IntVar x = model.newIntVar(0, numVals - 1, "x"); IntVar y = model.newIntVar(0, numVals - 1, "y"); IntVar z = model.newIntVar(0, numVals - 1, "z"); - // Creates the constraints. + // Create the constraints. model.addDifferent(x, y); - // Creates a solver and solves the model. + // Create a solver and solve the model. CpSolver solver = new CpSolver(); CpSolverStatus status = solver.solve(model); @@ -299,18 +299,18 @@ public class SolveWithTimeLimit { static { System.loadLibrary("jniortools"); } public static void main(String[] args) throws Exception { - // Creates the model. + // Create the model. CpModel model = new CpModel(); - // Creates the variables. + // Create the variables. int numVals = 3; IntVar x = model.newIntVar(0, numVals - 1, "x"); IntVar y = model.newIntVar(0, numVals - 1, "y"); IntVar z = model.newIntVar(0, numVals - 1, "z"); - // Creates the constraints. + // Create the constraint. model.addDifferent(x, y); - // Creates a solver and solves the model. + // Create a solver and solve the model. CpSolver solver = new CpSolver(); solver.getParameters().setMaxTimeInSeconds(10.0); CpSolverStatus status = solver.solve(model); @@ -518,61 +518,60 @@ int main() { ### Java code ```java -import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.CpSolverSolutionCallback; import com.google.ortools.sat.IntVar; -class VarArraySolutionPrinterWithObjective extends CpSolverSolutionCallback { - public VarArraySolutionPrinterWithObjective(IntVar[] variables) { - variables_ = variables; - } - - @Override - public void onSolutionCallback() { - System.out.println(String.format("Solution #%d: time = %.02f s", solutionCount_, wallTime())); - System.out.println(String.format(" objective value = %d", objectiveValue())); - for (IntVar v : variables_) { - System.out.println(String.format(" %s = %d", v.getName(), value(v))); - } - solutionCount_++; - } - - public int solutionCount() { - return solutionCount_; - } - - private int solutionCount_; - private IntVar[] variables_; -} - public class SolveWithIntermediateSolutions { static { System.loadLibrary("jniortools"); } + static class VarArraySolutionPrinterWithObjective extends CpSolverSolutionCallback { + public VarArraySolutionPrinterWithObjective(IntVar[] variables) { + variableArray = variables; + } + + @Override + public void onSolutionCallback() { + System.out.printf("Solution #%d: time = %.02f s%n", solutionCount, wallTime()); + System.out.printf(" objective value = %d%n", objectiveValue()); + for (IntVar v : variableArray) { + System.out.printf(" %s = %d%n", v.getName(), value(v)); + } + solutionCount++; + } + + public int getSolutionCount() { + return solutionCount; + } + + private int solutionCount; + private final IntVar[] variableArray; + } + public static void main(String[] args) throws Exception { - // Creates the model. + // Create the model. CpModel model = new CpModel(); - // Creates the variables. + // Create the variables. int numVals = 3; IntVar x = model.newIntVar(0, numVals - 1, "x"); IntVar y = model.newIntVar(0, numVals - 1, "y"); IntVar z = model.newIntVar(0, numVals - 1, "z"); - // Creates the constraints. + // Create the constraint. model.addDifferent(x, y); - // Maximizes a linear combination of variables. + // Maximize a linear combination of variables. model.maximizeScalProd(new IntVar[] {x, y, z}, new int[] {1, 2, 3}); - // Creates a solver and solves the model. + // Create a solver and solve the model. CpSolver solver = new CpSolver(); VarArraySolutionPrinterWithObjective cb = new VarArraySolutionPrinterWithObjective(new IntVar[] {x, y, z}); - CpSolverStatus status = solver.solveWithSolutionCallback(model, cb); + solver.solveWithSolutionCallback(model, cb); - System.out.println(cb.solutionCount() + " solutions found."); + System.out.println(cb.getSolutionCount() + " solutions found."); } } ``` @@ -789,56 +788,55 @@ int main() { ### Java code ```java -import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.CpSolverSolutionCallback; import com.google.ortools.sat.IntVar; -class VarArraySolutionPrinter extends CpSolverSolutionCallback { - public VarArraySolutionPrinter(IntVar[] variables) { - variables_ = variables; - } - - @Override - public void onSolutionCallback() { - System.out.println(String.format("Solution #%d: time = %.02f s", solutionCount_, wallTime())); - for (IntVar v : variables_) { - System.out.println(String.format(" %s = %d", v.getName(), value(v))); - } - solutionCount_++; - } - - public int solutionCount() { - return solutionCount_; - } - - private int solutionCount_; - private IntVar[] variables_; -} - public class SolveAllSolutions { static { System.loadLibrary("jniortools"); } + static class VarArraySolutionPrinter extends CpSolverSolutionCallback { + public VarArraySolutionPrinter(IntVar[] variables) { + variableArray = variables; + } + + @Override + public void onSolutionCallback() { + System.out.printf("Solution #%d: time = %.02f s%n", solutionCount, wallTime()); + for (IntVar v : variableArray) { + System.out.printf(" %s = %d%n", v.getName(), value(v)); + } + solutionCount++; + } + + public int getSolutionCount() { + return solutionCount; + } + + private int solutionCount; + private final IntVar[] variableArray; + } + public static void main(String[] args) throws Exception { - // Creates the model. + // Create the model. CpModel model = new CpModel(); - // Creates the variables. + // Create the variables. int numVals = 3; IntVar x = model.newIntVar(0, numVals - 1, "x"); IntVar y = model.newIntVar(0, numVals - 1, "y"); IntVar z = model.newIntVar(0, numVals - 1, "z"); - // Creates the constraints. + // Create the constraints. model.addDifferent(x, y); - // Creates a solver and solves the model. + // Create a solver and solve the model. CpSolver solver = new CpSolver(); VarArraySolutionPrinter cb = new VarArraySolutionPrinter(new IntVar[] {x, y, z}); - CpSolverStatus status = solver.searchAllSolutions(model, cb); + solver.searchAllSolutions(model, cb); - System.out.println(cb.solutionCount() + " solutions found."); + System.out.println(cb.getSolutionCount() + " solutions found."); } } ``` diff --git a/ortools/sat/drat_writer.cc b/ortools/sat/drat_writer.cc index 4fefd75140..eb39a61816 100644 --- a/ortools/sat/drat_writer.cc +++ b/ortools/sat/drat_writer.cc @@ -42,7 +42,7 @@ void DratWriter::DeleteClause(absl::Span clause) { void DratWriter::WriteClause(absl::Span clause) { for (const Literal literal : clause) { - StringAppendF(&buffer_, "%d ", literal.SignedValue()); + absl::StrAppendFormat(&buffer_, "%d ", literal.SignedValue()); } buffer_ += "0\n"; if (buffer_.size() > 10000) { diff --git a/ortools/sat/integer.cc b/ortools/sat/integer.cc index 392cff51cb..f946d04fb3 100644 --- a/ortools/sat/integer.cc +++ b/ortools/sat/integer.cc @@ -19,7 +19,6 @@ #include "ortools/base/iterator_adaptors.h" #include "ortools/base/stl_util.h" -#include "ortools/sat/sat_parameters.pb.h" #include "ortools/util/time_limit.h" namespace operations_research { @@ -160,7 +159,8 @@ IntegerEncoder::PartialDomainEncoding(IntegerVariable var) const { // Note that by not inserting the literal in "order" we can in the worst case // use twice as much implication (2 by literals) instead of only one between // consecutive literals. -void IntegerEncoder::AddImplications(IntegerLiteral i_lit, Literal literal) { +void IntegerEncoder::AddImplications(IntegerLiteral i_lit, + Literal associated_lit) { if (i_lit.var >= encoding_by_var_.size()) { encoding_by_var_.resize(i_lit.var.value() + 1); } @@ -172,27 +172,29 @@ void IntegerEncoder::AddImplications(IntegerLiteral i_lit, Literal literal) { if (add_implications_) { auto after_it = map_ref.lower_bound(i_lit.bound); if (after_it != map_ref.end()) { - // Literal(after) => literal + // Literal(after) => associated_lit if (sat_solver_->CurrentDecisionLevel() == 0) { - sat_solver_->AddBinaryClause(after_it->second.Negated(), literal); + sat_solver_->AddBinaryClause(after_it->second.Negated(), + associated_lit); } else { sat_solver_->AddBinaryClauseDuringSearch(after_it->second.Negated(), - literal); + associated_lit); } } if (after_it != map_ref.begin()) { - // literal => Literal(before) + // associated_lit => Literal(before) if (sat_solver_->CurrentDecisionLevel() == 0) { - sat_solver_->AddBinaryClause(literal.Negated(), (--after_it)->second); + sat_solver_->AddBinaryClause(associated_lit.Negated(), + (--after_it)->second); } else { - sat_solver_->AddBinaryClauseDuringSearch(literal.Negated(), + sat_solver_->AddBinaryClauseDuringSearch(associated_lit.Negated(), (--after_it)->second); } } } // Add the new entry. - map_ref[i_lit.bound] = literal; + map_ref[i_lit.bound] = associated_lit; } void IntegerEncoder::AddAllImplicationsBetweenAssociatedLiterals() { diff --git a/ortools/sat/integer.h b/ortools/sat/integer.h index f1609cc9f2..55a4d886ab 100644 --- a/ortools/sat/integer.h +++ b/ortools/sat/integer.h @@ -571,11 +571,11 @@ class IntegerTrail : public SatPropagator { // Returns the reason (as set of Literal currently false) for a given integer // literal. Note that the bound must be less restrictive than the current // bound (checked). - std::vector ReasonFor(IntegerLiteral bound) const; + std::vector ReasonFor(IntegerLiteral literal) const; // Appends the reason for the given integer literals to the output and call // STLSortAndRemoveDuplicates() on it. - void MergeReasonInto(absl::Span bounds, + void MergeReasonInto(absl::Span literals, std::vector* output) const; // Returns the number of enqueues that changed a variable bounds. We don't @@ -649,7 +649,7 @@ class IntegerTrail : public SatPropagator { // Returns the lowest trail index of a TrailEntry that can be used to explain // the given IntegerLiteral. The literal must be currently true (CHECKed). // Returns -1 if the explanation is trivial. - int FindLowestTrailIndexThatExplainBound(IntegerLiteral bound) const; + int FindLowestTrailIndexThatExplainBound(IntegerLiteral i_lit) const; // Helper function to return the "dependencies" of a bound assignment. // All the TrailEntry at these indices are part of the reason for this @@ -843,8 +843,8 @@ class GenericLiteralWatcher : public SatPropagator { // Doing this will cause IncrementalPropagate() to be called (see the // documentation of this interface for more detail). void WatchLiteral(Literal l, int id, int watch_index = -1); - void WatchLowerBound(IntegerVariable i, int id, int watch_index = -1); - void WatchUpperBound(IntegerVariable i, int id, int watch_index = -1); + void WatchLowerBound(IntegerVariable var, int id, int watch_index = -1); + void WatchUpperBound(IntegerVariable var, int id, int watch_index = -1); void WatchIntegerVariable(IntegerVariable i, int id, int watch_index = -1); // No-op overload for "constant" IntegerVariable that are sometimes templated diff --git a/ortools/sat/integer_expr.h b/ortools/sat/integer_expr.h index 74a74abf0a..f6668ed3de 100644 --- a/ortools/sat/integer_expr.h +++ b/ortools/sat/integer_expr.h @@ -51,7 +51,7 @@ class IntegerSumLE : public PropagatorInterface { // another IntegerSumLE constraint on the negated variables. IntegerSumLE(LiteralIndex reified_literal, const std::vector& vars, - const std::vector& coefficients, + const std::vector& coeffs, IntegerValue upper_bound, Model* model); // We propagate: @@ -385,7 +385,7 @@ inline std::function WeightedSumNotEqual( // Model-based function to create an IntegerVariable that corresponds to the // given weighted sum of other IntegerVariables. // -// Note that this is templated so that it can seemlessly accept std::vector +// Note that this is templated so that it can seamlessly accept std::vector // or std::vector. // // TODO(user): invert the coefficients/vars arguments. diff --git a/ortools/sat/optimization.cc b/ortools/sat/optimization.cc index 13ea4145e6..31304e6556 100644 --- a/ortools/sat/optimization.cc +++ b/ortools/sat/optimization.cc @@ -340,9 +340,9 @@ SatSolver::Status SolveWithFuMalik(LogBehavior log, } // Print the number of variable with a non-zero cost. - logger.Log(StringPrintf("c #weights:%zu #vars:%d #constraints:%d", - assumptions.size(), problem.num_variables(), - problem.constraints_size())); + logger.Log(absl::StrFormat("c #weights:%u #vars:%d #constraints:%d", + assumptions.size(), problem.num_variables(), + problem.constraints_size())); // Starts the algorithm. Each loop will solve the problem under the given // assumptions, and if unsat, will relax exactly one of the objective @@ -371,7 +371,7 @@ SatSolver::Status SolveWithFuMalik(LogBehavior log, solver->Backtrack(0); // Print the search progress. - logger.Log(StringPrintf("c iter:%d core:%zu", iter, core.size())); + logger.Log(absl::StrFormat("c iter:%d core:%zu", iter, core.size())); // Special case for a singleton core. if (core.size() == 1) { @@ -531,7 +531,7 @@ SatSolver::Status SolveWithWPM1(LogBehavior log, *std::max_element(costs.begin(), costs.end()); // Print the number of variables with a non-zero cost. - logger.Log(StringPrintf("c #weights:%zu #vars:%d #constraints:%d", + logger.Log(absl::StrFormat("c #weights:%zu #vars:%d #constraints:%d", assumptions.size(), problem.num_variables(), problem.constraints_size())); @@ -939,7 +939,7 @@ SatSolver::Status SolveWithCardinalityEncoding( } // Print the number of variables with a non-zero cost. - logger.Log(StringPrintf("c #weights:%zu #vars:%d #constraints:%d", + logger.Log(absl::StrFormat("c #weights:%zu #vars:%d #constraints:%d", nodes.size(), problem.num_variables(), problem.constraints_size())); @@ -947,7 +947,7 @@ SatSolver::Status SolveWithCardinalityEncoding( solver->Backtrack(0); EncodingNode* root = MergeAllNodesWithDeque(upper_bound, nodes, solver, &repository); - logger.Log(StringPrintf("c encoding depth:%d", root->depth())); + logger.Log(absl::StrFormat("c encoding depth:%d", root->depth())); while (true) { if (objective != kCoefficientMax) { @@ -1003,7 +1003,7 @@ SatSolver::Status SolveWithCardinalityEncodingAndCore( } // Print the number of variables with a non-zero cost. - logger.Log(StringPrintf("c #weights:%zu #vars:%d #constraints:%d", + logger.Log(absl::StrFormat("c #weights:%zu #vars:%d #constraints:%d", nodes.size(), problem.num_variables(), problem.constraints_size())); diff --git a/ortools/sat/precedences.cc b/ortools/sat/precedences.cc index bb277c0220..0e863f2bff 100644 --- a/ortools/sat/precedences.cc +++ b/ortools/sat/precedences.cc @@ -142,15 +142,17 @@ void PrecedencesPropagator::ComputePrecedences( const ArcInfo& arc = arcs_[arc_index]; if (integer_trail_->IsCurrentlyIgnored(arc.head_var)) continue; - IntegerValue min_offset = arc.offset; + IntegerValue offset = arc.offset; if (arc.offset_var != kNoIntegerVariable) { - min_offset += integer_trail_->LowerBound(arc.offset_var); + offset += integer_trail_->LowerBound(arc.offset_var); } - // We don't handle offsets and just care about "is before or at", so we - // skip negative offsets and just treat a positive one as zero. Because of - // this, we may miss some propagation opportunities. - if (min_offset < 0) continue; + // TODO(user): it seems better to ignore negative min offset as we will + // often have relation of the form interval_start >= interval_end - + // offset, and such relation are usually not useful. Revisit this in case + // we see problems where we can propagate more without this test. + if (offset < 0) continue; + if (var_to_degree_[arc.head_var] == 0) { tmp_sorted_vars_.push_back( {arc.head_var, integer_trail_->LowerBound(arc.head_var)}); @@ -163,7 +165,8 @@ void PrecedencesPropagator::ComputePrecedences( } var_to_last_index_[arc.head_var] = index; var_to_degree_[arc.head_var]++; - tmp_precedences_.push_back({index, arc.head_var, arc.presence_literals}); + tmp_precedences_.push_back( + {index, arc.head_var, arc_index.value(), offset}); } } @@ -182,11 +185,17 @@ void PrecedencesPropagator::ComputePrecedences( int start = 0; for (const SortedVar pair : tmp_sorted_vars_) { const int degree = var_to_degree_[pair.var]; - var_to_degree_[pair.var] = start; - start += degree; + if (degree > 1) { + var_to_degree_[pair.var] = start; + start += degree; + } else { + // Optimization: we remove degree one relations. + var_to_degree_[pair.var] = -1; + } } output->resize(start); for (const IntegerPrecedences& precedence : tmp_precedences_) { + if (var_to_degree_[precedence.var] < 0) continue; (*output)[var_to_degree_[precedence.var]++] = precedence; } @@ -197,6 +206,21 @@ void PrecedencesPropagator::ComputePrecedences( } } +void PrecedencesPropagator::AddPrecedenceReason( + int arc_index, IntegerValue min_offset, + std::vector* literal_reason, + std::vector* integer_reason) const { + const ArcInfo& arc = arcs_[ArcIndex(arc_index)]; + for (const Literal l : arc.presence_literals) { + literal_reason->push_back(l.Negated()); + } + if (arc.offset_var != kNoIntegerVariable) { + // Reason for ArcOffset(arc) to be >= min_offset. + integer_reason->push_back(IntegerLiteral::GreaterOrEqual( + arc.offset_var, min_offset - arc.offset)); + } +} + void PrecedencesPropagator::AdjustSizeFor(IntegerVariable i) { const int index = std::max(i.value(), NegationOf(i).value()); if (index >= impacted_arcs_.size()) { diff --git a/ortools/sat/precedences.h b/ortools/sat/precedences.h index 982ae8d0bf..6a6fb3cc47 100644 --- a/ortools/sat/precedences.h +++ b/ortools/sat/precedences.h @@ -86,38 +86,32 @@ class PrecedencesPropagator : public SatPropagator, PropagatorInterface { // Generic function that cover all of the above case and more. void AddPrecedenceWithAllOptions(IntegerVariable i1, IntegerVariable i2, IntegerValue offset, - IntegerVariable offset_var, LiteralIndex l); + IntegerVariable offset_var, LiteralIndex r); - // Finds all the IntegerVariable that are "after" one of the IntegerVariable - // in vars. Returns a vector of these precedences relation sorted by - // IntegerPrecedences.var so that it is efficient to find all the + // Finds all the IntegerVariable that are "after" at least two of the + // IntegerVariable in vars. Returns a vector of these precedences relation + // sorted by IntegerPrecedences.var so that it is efficient to find all the // IntegerVariable "before" another one. // // Note that we only consider direct precedences here. Given our usage, it may // be better to compute the full reachability in the precedence graph, but in - // pratice that may be too slow. On a good note, because we have all the - // potential precedences between tasks in disjunctions, on a single machine, - // both notion should be the same since we automatically work on the - // transitive closure. + // pratice that may be too slow. // // Note that the IntegerVariable in the vector are also returned in // topological order for a more efficient propagation in - // DisjunctiveConstraint::PrecedencesPass() where this is used. + // DisjunctivePrecedences::Propagate() where this is used. struct IntegerPrecedences { - int index; // in vars. + int index; // position in vars. IntegerVariable var; // An IntegerVariable that is >= to vars[index]. - - // The reason for it to be >=. - absl::InlinedVector reason; - - // Only needed for testing. - bool operator==(const IntegerPrecedences& o) const { - return index == o.index && var == o.var && reason == o.reason; - } + int arc_index; // Used by AddPrecedenceReason(). + IntegerValue offset; // we have: input_vars[index] + offset <= var }; void ComputePrecedences(const std::vector& vars, const std::vector& to_consider, std::vector* output); + void AddPrecedenceReason(int arc_index, IntegerValue min_offset, + std::vector* literal_reason, + std::vector* integer_reason) const; // Advanced usage. To be called once all the constraints have been added to // the model. This will loop over all "node" in this class, and if one of its @@ -157,7 +151,7 @@ class PrecedencesPropagator : public SatPropagator, PropagatorInterface { // on their negation. void AdjustSizeFor(IntegerVariable i); void AddArc(IntegerVariable tail, IntegerVariable head, IntegerValue offset, - IntegerVariable offset_var, LiteralIndex l); + IntegerVariable offset_var, LiteralIndex presence_literal_index); // Enqueue a new lower bound for the variable arc.head_lb that was deduced // from the current value of arc.tail_lb and the offset of this arc. @@ -258,7 +252,7 @@ class PrecedencesPropagator : public SatPropagator, PropagatorInterface { std::vector integer_reason_; // Temp vectors for the Bellman-Ford algorithm. The graph in which this - // algorithm works is in one to one correspondance with the IntegerVariable in + // algorithm works is in one to one correspondence with the IntegerVariable in // impacted_arcs_. std::deque bf_queue_; std::vector bf_in_queue_; diff --git a/ortools/sat/python/cp_model.py b/ortools/sat/python/cp_model.py index 28920c613f..2541c7e9e6 100644 --- a/ortools/sat/python/cp_model.py +++ b/ortools/sat/python/cp_model.py @@ -623,7 +623,7 @@ class CpModel(object): def AddCircuit(self, arcs): """Adds Circuit(arcs). - Adds a circuit constraints from a sparse list of arcs that encode the graph. + Adds a circuit constraint from a sparse list of arcs that encode the graph. A circuit is a unique Hamiltonian path in a subgraph of the total graph. In case a node 'i' is not in the path, then there must be a @@ -1020,7 +1020,7 @@ class CpModel(object): # Scheduling support def NewIntervalVar(self, start, size, end, name): - """Creates an interval variables from start, size, and end. + """Creates an interval variable from start, size, and end. An interval variable is a constraint, that is itself used in other constraints like NoOverlap. diff --git a/ortools/sat/restart.cc b/ortools/sat/restart.cc index b243d3e2c7..a009650c97 100644 --- a/ortools/sat/restart.cc +++ b/ortools/sat/restart.cc @@ -15,6 +15,7 @@ #include "ortools/base/stringprintf.h" #include "ortools/base/split.h" +#include "ortools/base/stringprintf.h" namespace operations_research { namespace sat { @@ -112,8 +113,8 @@ bool RestartPolicy::ShouldRestart() { if (conflicts_until_next_strategy_change_ == 0) { strategy_counter_++; strategy_change_conflicts_ += - parameters_.strategy_change_increase_ratio() * - strategy_change_conflicts_; + static_cast(parameters_.strategy_change_increase_ratio() * + strategy_change_conflicts_); conflicts_until_next_strategy_change_ = strategy_change_conflicts_; } @@ -160,13 +161,13 @@ void RestartPolicy::OnConflict(int conflict_trail_index, std::string RestartPolicy::InfoString() const { std::string result = - StringPrintf(" num restarts: %d\n", num_restarts_) + - StringPrintf(" conflict decision level avg: %f\n", - dl_running_average_.GlobalAverage()) + - StringPrintf(" conflict lbd avg: %f\n", - lbd_running_average_.GlobalAverage()) + - StringPrintf(" conflict trail size avg: %f\n", - trail_size_running_average_.GlobalAverage()); + absl::StrFormat(" num restarts: %d\n", num_restarts_) + + absl::StrFormat(" conflict decision level avg: %f\n", + dl_running_average_.GlobalAverage()) + + absl::StrFormat(" conflict lbd avg: %f\n", + lbd_running_average_.GlobalAverage()) + + absl::StrFormat(" conflict trail size avg: %f\n", + trail_size_running_average_.GlobalAverage()); return result; } diff --git a/ortools/sat/samples/BinpackingProblem.java b/ortools/sat/samples/BinPackingProblem.java similarity index 91% rename from ortools/sat/samples/BinpackingProblem.java rename to ortools/sat/samples/BinPackingProblem.java index 7cdba76376..8f8a1dcc72 100644 --- a/ortools/sat/samples/BinpackingProblem.java +++ b/ortools/sat/samples/BinPackingProblem.java @@ -16,7 +16,7 @@ import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.IntVar; -public class BinpackingProblem { +public class BinPackingProblem { static { System.loadLibrary("jniortools"); } @@ -92,11 +92,11 @@ public class BinpackingProblem { CpSolverStatus status = solver.solve(model); System.out.println("Solve status: " + status); if (status == CpSolverStatus.OPTIMAL) { - System.out.println(String.format("Optimal objective value: %f", solver.objectiveValue())); + System.out.printf("Optimal objective value: %f%n", solver.objectiveValue()); for (int b = 0; b < numBins; ++b) { - System.out.println(String.format("load_%d = %d", b, solver.value(load[b]))); + System.out.printf("load_%d = %d%n", b, solver.value(load[b])); for (int i = 0; i < numItems; ++i) { - System.out.println(String.format(" item_%d_%d = %d", i, b, solver.value(x[i][b]))); + System.out.printf(" item_%d_%d = %d%n", i, b, solver.value(x[i][b])); } } } diff --git a/ortools/sat/samples/ChannelingSample.java b/ortools/sat/samples/ChannelingSample.java index 334aa818ea..b7ca01871b 100644 --- a/ortools/sat/samples/ChannelingSample.java +++ b/ortools/sat/samples/ChannelingSample.java @@ -47,28 +47,30 @@ public class ChannelingSample { DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_FIRST, DecisionStrategyProto.DomainReductionStrategy.SELECT_MIN_VALUE); - // Creates a solver and solve with a fixed search. + // Creates the solver. CpSolver solver = new CpSolver(); - // Forces solver to follow the decision strategy exactly. + // Forces the solver to follow the decision strategy exactly. solver.getParameters().setSearchBranching(SatParameters.SearchBranching.FIXED_SEARCH); - // And solves the problem with the printer callback. - solver.searchAllSolutions(model, new CpSolverSolutionCallback() { - public CpSolverSolutionCallback init(IntVar[] variables) { - variables_ = variables; - return this; - } - - @Override - public void onSolutionCallback() { - for (IntVar v : variables_) { - System.out.print(String.format("%s=%d ", v.getName(), value(v))); + // Solves the problem with the printer callback. + solver.searchAllSolutions( + model, + new CpSolverSolutionCallback() { + public CpSolverSolutionCallback init(IntVar[] variables) { + variableArray = variables; + return this; } - System.out.println(); - } - private IntVar[] variables_; - }.init(new IntVar[] {x, y, b})); + @Override + public void onSolutionCallback() { + for (IntVar v : variableArray) { + System.out.printf("%s=%d ", v.getName(), value(v)); + } + System.out.println(); + } + + private IntVar[] variableArray; + }.init(new IntVar[] {x, y, b})); } } diff --git a/ortools/sat/samples/IntervalSample.java b/ortools/sat/samples/IntervalSample.java index 5f05d9e2db..62c5def7ee 100644 --- a/ortools/sat/samples/IntervalSample.java +++ b/ortools/sat/samples/IntervalSample.java @@ -23,9 +23,9 @@ public class IntervalSample { CpModel model = new CpModel(); int horizon = 100; IntVar startVar = model.newIntVar(0, horizon, "start"); + IntVar endVar = model.newIntVar(0, horizon, "end"); // Java code supports IntVar or integer constants in intervals. int duration = 10; - IntVar endVar = model.newIntVar(0, horizon, "end"); IntervalVar interval = model.newIntervalVar(startVar, duration, endVar, "interval"); System.out.println(interval); diff --git a/ortools/sat/samples/LiteralSample.java b/ortools/sat/samples/LiteralSample.java index 0d96f56a32..723fc5ae77 100644 --- a/ortools/sat/samples/LiteralSample.java +++ b/ortools/sat/samples/LiteralSample.java @@ -23,6 +23,6 @@ public class LiteralSample { CpModel model = new CpModel(); IntVar x = model.newBoolVar("x"); ILiteral notX = x.not(); - System.out.println(notX.shortString()); + System.out.println(notX.getShortString()); } } diff --git a/ortools/sat/samples/NoOverlapSample.java b/ortools/sat/samples/NoOverlapSample.java index 58252f9716..5bb4ac0787 100644 --- a/ortools/sat/samples/NoOverlapSample.java +++ b/ortools/sat/samples/NoOverlapSample.java @@ -17,6 +17,10 @@ import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.IntVar; import com.google.ortools.sat.IntervalVar; +/** + * We want to schedule 3 tasks on 3 weeks excluding weekends, making the final day as early as + * possible. + */ public class NoOverlapSample { static { System.loadLibrary("jniortools"); } @@ -49,7 +53,9 @@ public class NoOverlapSample { IntervalVar weekend1 = model.newFixedInterval(12, 2, "weekend1"); IntervalVar weekend2 = model.newFixedInterval(19, 2, "weekend2"); - // No Overlap constraint. + // No Overlap constraint. This constraint enforces that no two intervals can overlap. + // In this example, as we use 3 fixed intervals that span over weekends, this constraint makes + // sure that all tasks are executed on weekdays. model.addNoOverlap(new IntervalVar[] {task0, task1, task2, weekend0, weekend1, weekend2}); // Makespan objective. diff --git a/ortools/sat/samples/OptionalIntervalSample.java b/ortools/sat/samples/OptionalIntervalSample.java index e3c1da32ad..bf5fc60872 100644 --- a/ortools/sat/samples/OptionalIntervalSample.java +++ b/ortools/sat/samples/OptionalIntervalSample.java @@ -24,9 +24,9 @@ public class OptionalIntervalSample { CpModel model = new CpModel(); int horizon = 100; IntVar startVar = model.newIntVar(0, horizon, "start"); + IntVar endVar = model.newIntVar(0, horizon, "end"); // Java code supports IntVar or integer constants in intervals. int duration = 10; - IntVar endVar = model.newIntVar(0, horizon, "end"); ILiteral presence = model.newBoolVar("presence"); IntervalVar interval = model.newOptionalIntervalVar(startVar, duration, endVar, presence, "interval"); diff --git a/ortools/sat/samples/RabbitsAndPheasants.java b/ortools/sat/samples/RabbitsAndPheasants.java index bfce5af284..46b294dc23 100644 --- a/ortools/sat/samples/RabbitsAndPheasants.java +++ b/ortools/sat/samples/RabbitsAndPheasants.java @@ -16,6 +16,10 @@ import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.IntVar; +/** + * In a field of rabbits and pheasants, there are 20 heads and 56 legs. How many rabbits and + * pheasants are there? + */ public class RabbitsAndPheasants { static { System.loadLibrary("jniortools"); } diff --git a/ortools/sat/samples/RankingSample.java b/ortools/sat/samples/RankingSample.java index 7d9f9cc4c2..8476b43ed6 100644 --- a/ortools/sat/samples/RankingSample.java +++ b/ortools/sat/samples/RankingSample.java @@ -20,6 +20,10 @@ import com.google.ortools.sat.IntervalVar; import java.util.ArrayList; import java.util.List; +// This code takes a list of interval variables in a noOverlap constraint, and a parallel list of +// integer variables and enforces the following constraint: +// - rank[i] == -1 iff interval[i] is not active. +// - rank[i] == number of active intervals that precede interval[i]. public class RankingSample { static { System.loadLibrary("jniortools"); } @@ -36,15 +40,16 @@ public class RankingSample { } else { IntVar prec = model.newBoolVar(String.format("%d before %d", i, j)); precedences[i][j] = prec; + // Ensure that task i precedes task j if prec is true. model.addLessOrEqualWithOffset(starts[i], starts[j], 1).onlyEnforceIf(prec); } } } - // Treats optional intervals. + // Create optional intervals. for (int i = 0; i < numTasks - 1; ++i) { for (int j = i + 1; j < numTasks; ++j) { - List list = new ArrayList(); + List list = new ArrayList<>(); list.add(precedences[i][j]); list.add(precedences[j][i]); list.add(presences[i].not()); @@ -55,12 +60,13 @@ public class RankingSample { // Makes sure that if j is not performed, all precedences are false. model.addImplication(presences[j].not(), precedences[i][j].not()); model.addImplication(presences[j].not(), precedences[j][i].not()); - // The following bool_or will enforce that for any two intervals: + // The following boolOr will enforce that for any two intervals: // i precedes j or j precedes i or at least one interval is not // performed. - model.addBoolOr(list.toArray(new ILiteral[list.size()])); - // Redundant constraint: it propagates early that at most one precedence - // is true. + model.addBoolOr(list.toArray(new ILiteral[0])); + // For efficiency, we add a redundant constraint declaring that only one of i precedes j and + // j precedes i are true. This will speed up the solve because the reason of this + // propagation is shorter that using interval bounds is true. model.addImplication(precedences[i][j], precedences[j][i].not()); model.addImplication(precedences[j][i], precedences[i][j].not()); } @@ -83,7 +89,6 @@ public class RankingSample { public static void main(String[] args) throws Exception { CpModel model = new CpModel(); - // Three weeks. int horizon = 100; int numTasks = 4; @@ -110,7 +115,7 @@ public class RankingSample { starts[t], duration, ends[t], presences[t], "o_interval_" + t); } - // Ranks = -1 if and only if the tasks is not performed. + // The rank will be -1 iff the task is not performed. ranks[t] = model.newIntVar(-1, numTasks - 1, "rank_" + t); } @@ -128,9 +133,13 @@ public class RankingSample { for (int t = 0; t < numTasks; ++t) { model.addLessOrEqual(ends[t], makespan).onlyEnforceIf(presences[t]); } - // Minimizes makespan - fixed gain per tasks performed. - // As the fixed cost is less that the duration of the last interval, - // the solver will not perform the last interval. + // The objective function is a mix of a fixed gain per task performed, and a fixed cost for each + // additional day of activity. + // The solver will balance both cost and gain and minimize makespan * per-day-penalty - number + // of tasks performed * per-task-gain. + // + // On this problem, as the fixed cost is less that the duration of the last interval, the solver + // will not perform the last interval. IntVar[] objectiveVars = new IntVar[numTasks + 1]; int[] objectiveCoefs = new int[numTasks + 1]; for (int t = 0; t < numTasks; ++t) { @@ -150,14 +159,12 @@ public class RankingSample { System.out.println("Makespan: " + solver.value(makespan)); for (int t = 0; t < numTasks; ++t) { if (solver.booleanValue(presences[t])) { - System.out.println( - String.format( - "Task %d starts at %d with rank %d", - t, solver.value(starts[t]), solver.value(ranks[t]))); + System.out.printf( + "Task %d starts at %d with rank %d%n", + t, solver.value(starts[t]), solver.value(ranks[t])); } else { - System.out.println( - String.format( - "Task %d in not performed and ranked at %d", t, solver.value(ranks[t]))); + System.out.printf( + "Task %d in not performed and ranked at %d%n", t, solver.value(ranks[t])); } } } else { diff --git a/ortools/sat/samples/ReifiedSample.java b/ortools/sat/samples/ReifiedSample.java index efc19a5bfb..9a825c028a 100644 --- a/ortools/sat/samples/ReifiedSample.java +++ b/ortools/sat/samples/ReifiedSample.java @@ -15,6 +15,16 @@ import com.google.ortools.sat.CpModel; import com.google.ortools.sat.ILiteral; import com.google.ortools.sat.IntVar; +/** + * Reification is the action of associating a Boolean variable to a constraint. This boolean + * enforces or prohibits the constraint according to the value the Boolean variable is fixed to. + * + *

Half-reification is defined as a simple implication: If the Boolean variable is true, then the + * constraint holds, instead of an complete equivalence. + * + *

The SAT solver offers half-reification. To implement full reification, two half-reified + * constraints must be used. + */ public class ReifiedSample { static { System.loadLibrary("jniortools"); } @@ -26,14 +36,14 @@ public class ReifiedSample { IntVar y = model.newBoolVar("y"); IntVar b = model.newBoolVar("b"); - // First version using a half-reified bool and. + // Version 1: a half-reified boolean and. model.addBoolAnd(new ILiteral[] {x, y.not()}).onlyEnforceIf(b); - // Second version using implications. + // Version 2: implications. model.addImplication(b, x); model.addImplication(b, y.not()); - // Third version using bool or. + // Version 3: boolean or. model.addBoolOr(new ILiteral[] {b.not(), x}); model.addBoolOr(new ILiteral[] {b.not(), y.not()}); } diff --git a/ortools/sat/samples/SimpleSolve.java b/ortools/sat/samples/SimpleSolve.java index 1c131e77b3..0ed48a785d 100644 --- a/ortools/sat/samples/SimpleSolve.java +++ b/ortools/sat/samples/SimpleSolve.java @@ -16,23 +16,24 @@ import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.IntVar; +/** Solve a simple problem with three variables and one different constraint. */ public class SimpleSolve { static { System.loadLibrary("jniortools"); } public static void main(String[] args) throws Exception { - // Creates the model. + // Create the model. CpModel model = new CpModel(); - // Creates the variables. + // Create the variables. int numVals = 3; IntVar x = model.newIntVar(0, numVals - 1, "x"); IntVar y = model.newIntVar(0, numVals - 1, "y"); IntVar z = model.newIntVar(0, numVals - 1, "z"); - // Creates the constraints. + // Create the constraints. model.addDifferent(x, y); - // Creates a solver and solves the model. + // Create a solver and solve the model. CpSolver solver = new CpSolver(); CpSolverStatus status = solver.solve(model); diff --git a/ortools/sat/samples/SolveAllSolutions.java b/ortools/sat/samples/SolveAllSolutions.java index d0c8d7255e..b4ec7e2c5b 100644 --- a/ortools/sat/samples/SolveAllSolutions.java +++ b/ortools/sat/samples/SolveAllSolutions.java @@ -11,55 +11,54 @@ // See the License for the specific language governing permissions and // limitations under the License. -import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.CpSolverSolutionCallback; import com.google.ortools.sat.IntVar; -class VarArraySolutionPrinter extends CpSolverSolutionCallback { - public VarArraySolutionPrinter(IntVar[] variables) { - variables_ = variables; - } - - @Override - public void onSolutionCallback() { - System.out.println(String.format("Solution #%d: time = %.02f s", solutionCount_, wallTime())); - for (IntVar v : variables_) { - System.out.println(String.format(" %s = %d", v.getName(), value(v))); - } - solutionCount_++; - } - - public int solutionCount() { - return solutionCount_; - } - - private int solutionCount_; - private IntVar[] variables_; -} - public class SolveAllSolutions { static { System.loadLibrary("jniortools"); } + static class VarArraySolutionPrinter extends CpSolverSolutionCallback { + public VarArraySolutionPrinter(IntVar[] variables) { + variableArray = variables; + } + + @Override + public void onSolutionCallback() { + System.out.printf("Solution #%d: time = %.02f s%n", solutionCount, wallTime()); + for (IntVar v : variableArray) { + System.out.printf(" %s = %d%n", v.getName(), value(v)); + } + solutionCount++; + } + + public int getSolutionCount() { + return solutionCount; + } + + private int solutionCount; + private final IntVar[] variableArray; + } + public static void main(String[] args) throws Exception { - // Creates the model. + // Create the model. CpModel model = new CpModel(); - // Creates the variables. + // Create the variables. int numVals = 3; IntVar x = model.newIntVar(0, numVals - 1, "x"); IntVar y = model.newIntVar(0, numVals - 1, "y"); IntVar z = model.newIntVar(0, numVals - 1, "z"); - // Creates the constraints. + // Create the constraints. model.addDifferent(x, y); - // Creates a solver and solves the model. + // Create a solver and solve the model. CpSolver solver = new CpSolver(); VarArraySolutionPrinter cb = new VarArraySolutionPrinter(new IntVar[] {x, y, z}); - CpSolverStatus status = solver.searchAllSolutions(model, cb); + solver.searchAllSolutions(model, cb); - System.out.println(cb.solutionCount() + " solutions found."); + System.out.println(cb.getSolutionCount() + " solutions found."); } } diff --git a/ortools/sat/samples/SolveWithIntermediateSolutions.java b/ortools/sat/samples/SolveWithIntermediateSolutions.java index f8a983f784..e8d7ad6a87 100644 --- a/ortools/sat/samples/SolveWithIntermediateSolutions.java +++ b/ortools/sat/samples/SolveWithIntermediateSolutions.java @@ -11,60 +11,59 @@ // See the License for the specific language governing permissions and // limitations under the License. -import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.CpSolverSolutionCallback; import com.google.ortools.sat.IntVar; -class VarArraySolutionPrinterWithObjective extends CpSolverSolutionCallback { - public VarArraySolutionPrinterWithObjective(IntVar[] variables) { - variables_ = variables; - } - - @Override - public void onSolutionCallback() { - System.out.println(String.format("Solution #%d: time = %.02f s", solutionCount_, wallTime())); - System.out.println(String.format(" objective value = %d", objectiveValue())); - for (IntVar v : variables_) { - System.out.println(String.format(" %s = %d", v.getName(), value(v))); - } - solutionCount_++; - } - - public int solutionCount() { - return solutionCount_; - } - - private int solutionCount_; - private IntVar[] variables_; -} - public class SolveWithIntermediateSolutions { static { System.loadLibrary("jniortools"); } + static class VarArraySolutionPrinterWithObjective extends CpSolverSolutionCallback { + public VarArraySolutionPrinterWithObjective(IntVar[] variables) { + variableArray = variables; + } + + @Override + public void onSolutionCallback() { + System.out.printf("Solution #%d: time = %.02f s%n", solutionCount, wallTime()); + System.out.printf(" objective value = %d%n", objectiveValue()); + for (IntVar v : variableArray) { + System.out.printf(" %s = %d%n", v.getName(), value(v)); + } + solutionCount++; + } + + public int getSolutionCount() { + return solutionCount; + } + + private int solutionCount; + private final IntVar[] variableArray; + } + public static void main(String[] args) throws Exception { - // Creates the model. + // Create the model. CpModel model = new CpModel(); - // Creates the variables. + // Create the variables. int numVals = 3; IntVar x = model.newIntVar(0, numVals - 1, "x"); IntVar y = model.newIntVar(0, numVals - 1, "y"); IntVar z = model.newIntVar(0, numVals - 1, "z"); - // Creates the constraints. + // Create the constraint. model.addDifferent(x, y); - // Maximizes a linear combination of variables. + // Maximize a linear combination of variables. model.maximizeScalProd(new IntVar[] {x, y, z}, new int[] {1, 2, 3}); - // Creates a solver and solves the model. + // Create a solver and solve the model. CpSolver solver = new CpSolver(); VarArraySolutionPrinterWithObjective cb = new VarArraySolutionPrinterWithObjective(new IntVar[] {x, y, z}); - CpSolverStatus status = solver.solveWithSolutionCallback(model, cb); + solver.solveWithSolutionCallback(model, cb); - System.out.println(cb.solutionCount() + " solutions found."); + System.out.println(cb.getSolutionCount() + " solutions found."); } } diff --git a/ortools/sat/samples/SolveWithTimeLimit.java b/ortools/sat/samples/SolveWithTimeLimit.java index 52b70256a3..3158ab6d66 100644 --- a/ortools/sat/samples/SolveWithTimeLimit.java +++ b/ortools/sat/samples/SolveWithTimeLimit.java @@ -21,18 +21,18 @@ public class SolveWithTimeLimit { static { System.loadLibrary("jniortools"); } public static void main(String[] args) throws Exception { - // Creates the model. + // Create the model. CpModel model = new CpModel(); - // Creates the variables. + // Create the variables. int numVals = 3; IntVar x = model.newIntVar(0, numVals - 1, "x"); IntVar y = model.newIntVar(0, numVals - 1, "y"); IntVar z = model.newIntVar(0, numVals - 1, "z"); - // Creates the constraints. + // Create the constraint. model.addDifferent(x, y); - // Creates a solver and solves the model. + // Create a solver and solve the model. CpSolver solver = new CpSolver(); solver.getParameters().setMaxTimeInSeconds(10.0); CpSolverStatus status = solver.solve(model); diff --git a/ortools/sat/samples/channeling_sample.cc b/ortools/sat/samples/channeling_sample.cc index c21e992d08..9772dd074e 100644 --- a/ortools/sat/samples/channeling_sample.cc +++ b/ortools/sat/samples/channeling_sample.cc @@ -11,11 +11,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "ortools/sat/cp_model.proto.h" +#include "ortools/sat/cp_model.pb.h" #include "ortools/sat/cp_model_solver.h" #include "ortools/sat/cp_model_utils.h" #include "ortools/sat/model.h" -#include "ortools/sat/sat_parameters.proto.h" +#include "ortools/sat/sat_parameters.pb.h" namespace operations_research { namespace sat { diff --git a/ortools/sat/sat_base.h b/ortools/sat/sat_base.h index f35beaa797..406de5fb71 100644 --- a/ortools/sat/sat_base.h +++ b/ortools/sat/sat_base.h @@ -91,7 +91,9 @@ class Literal { Literal Negated() const { return Literal(NegatedIndex()); } - std::string DebugString() const { return StringPrintf("%+d", SignedValue()); } + std::string DebugString() const { + return absl::StrFormat("%+d", SignedValue()); + } bool operator==(Literal other) const { return index_ == other.index_; } bool operator!=(Literal other) const { return index_ != other.index_; } @@ -200,8 +202,8 @@ struct AssignmentInfo { int32 trail_index; std::string DebugString() const { - return StringPrintf("level:%d type:%d trail_index:%d", level, type, - trail_index); + return absl::StrFormat("level:%d type:%d trail_index:%d", level, type, + trail_index); } }; COMPILE_ASSERT(sizeof(AssignmentInfo) == 8, diff --git a/ortools/sat/sat_solver.cc b/ortools/sat/sat_solver.cc index c1b09e83aa..845612783d 100644 --- a/ortools/sat/sat_solver.cc +++ b/ortools/sat/sat_solver.cc @@ -1369,16 +1369,15 @@ int SatSolver::ComputeBacktrackLevel(const std::vector& literals) { } template -int SatSolver::ComputeLbd(const LiteralList& conflict) { +int SatSolver::ComputeLbd(const LiteralList& literals) { SCOPED_TIME_STAT(&stats_); const int limit = parameters_->count_assumption_levels_in_lbd() ? 0 : assumption_level_; - // We know that the first literal of the conflict is always of the highest - // level. + // We know that the first literal is always of the highest level. is_level_marked_.ClearAndResize( - SatDecisionLevel(DecisionLevel(conflict.begin()->Variable()) + 1)); - for (const Literal literal : conflict) { + SatDecisionLevel(DecisionLevel(literals.begin()->Variable()) + 1)); + for (const Literal literal : literals) { const SatDecisionLevel level(DecisionLevel(literal.Variable())); DCHECK_GE(level, 0); if (level > limit && !is_level_marked_[level]) { @@ -1390,9 +1389,9 @@ int SatSolver::ComputeLbd(const LiteralList& conflict) { std::string SatSolver::StatusString(Status status) const { const double time_in_s = timer_.Get(); - return StringPrintf("\n status: %s\n", SatStatusString(status).c_str()) + - StringPrintf(" time: %fs\n", time_in_s) + - StringPrintf(" memory: %s\n", MemoryUsage().c_str()) + + return absl::StrFormat("\n status: %s\n", SatStatusString(status).c_str()) + + absl::StrFormat(" time: %fs\n", time_in_s) + + absl::StrFormat(" memory: %s\n", MemoryUsage().c_str()) + absl::StrFormat( " num failures: %" GG_LL_FORMAT "d (%.0f /sec)\n", counters_.num_failures, @@ -1454,7 +1453,7 @@ std::string SatSolver::StatusString(Status status) const { absl::StrFormat(" pb num inspected constraint literals: %d\n", pb_constraints_.num_inspected_constraint_literals()) + restart_->InfoString() + - StringPrintf(" deterministic time: %f\n", deterministic_time()); + absl::StrFormat(" deterministic time: %f\n", deterministic_time()); } std::string SatSolver::RunningStatisticsString() const { @@ -1709,7 +1708,7 @@ std::string SatSolver::DebugString(const SatClause& clause) const { : (trail_->Assignment().LiteralIsFalse(literal) ? "false" : "undef"); result.append( - StringPrintf("%s(%s)", literal.DebugString().c_str(), value.c_str())); + absl::StrFormat("%s(%s)", literal.DebugString().c_str(), value.c_str())); } return result; } @@ -2428,7 +2427,7 @@ void SatSolver::CleanClauseDatabaseIfNeeded() { int num_deleted_clauses = entries.size() - num_kept_clauses; // Tricky: Because the order of the clauses_info iteration is NOT - // deterministic (pointer keys), we also keep all the clauses which have the + // deterministic (pointer keys), we also keep all the clauses wich have the // same LBD and activity as the last one so the behavior is deterministic. while (num_deleted_clauses > 0) { const ClauseInfo& a = entries[num_deleted_clauses].second; diff --git a/ortools/sat/sat_solver.h b/ortools/sat/sat_solver.h index 1ee3df29e6..a06c8993d8 100644 --- a/ortools/sat/sat_solver.h +++ b/ortools/sat/sat_solver.h @@ -524,7 +524,7 @@ class SatSolver { // // Returns the LBD of the clause. int AddLearnedClauseAndEnqueueUnitPropagation( - const std::vector& literals, bool must_be_kept); + const std::vector& literals, bool is_redundant); // Creates a new decision which corresponds to setting the given literal to // True and Enqueue() this change. diff --git a/ortools/sat/simplification.cc b/ortools/sat/simplification.cc index 55fa27c314..956f88ee60 100644 --- a/ortools/sat/simplification.cc +++ b/ortools/sat/simplification.cc @@ -21,6 +21,7 @@ #include "ortools/algorithms/dynamic_partition.h" #include "ortools/base/adjustable_priority_queue-inl.h" #include "ortools/base/logging.h" +#include "ortools/base/memory.h" #include "ortools/base/random.h" #include "ortools/base/stl_util.h" #include "ortools/base/timer.h" @@ -1222,7 +1223,7 @@ SatSolver::Status SolveWithPresolve(std::unique_ptr* solver, VLOG(1) << "UNSAT during presolve."; // This is just here to reset the SatSolver::Solve() satistics. - (*solver).reset(new SatSolver()); + (*solver) = absl::make_unique(); return SatSolver::INFEASIBLE; } @@ -1232,7 +1233,7 @@ SatSolver::Status SolveWithPresolve(std::unique_ptr* solver, } // Load the presolved problem in a new solver. - (*solver).reset(new SatSolver()); + (*solver) = absl::make_unique(); (*solver)->SetDratProofHandler(drat_proof_handler); (*solver)->SetParameters(parameters); presolver.LoadProblemIntoSatSolver((*solver).get()); diff --git a/ortools/sat/simplification.h b/ortools/sat/simplification.h index 9dafa65251..9af0efbbde 100644 --- a/ortools/sat/simplification.h +++ b/ortools/sat/simplification.h @@ -280,8 +280,8 @@ class SatPresolver { // Literal priority queue for BVA. The literals are ordered by descending // number of occurrences in clauses. void InitializeBvaPriorityQueue(); - void UpdateBvaPriorityQueue(LiteralIndex var); - void AddToBvaPriorityQueue(LiteralIndex var); + void UpdateBvaPriorityQueue(LiteralIndex lit); + void AddToBvaPriorityQueue(LiteralIndex lit); struct BvaPqElement { BvaPqElement() : heap_index(-1), literal(-1), weight(0.0) {} diff --git a/ortools/sat/symmetry.h b/ortools/sat/symmetry.h index 4b0aa910f4..37ee1bede2 100644 --- a/ortools/sat/symmetry.h +++ b/ortools/sat/symmetry.h @@ -135,7 +135,7 @@ class SymmetryPropagator : public SatPropagator { // Returns false if there is a non-symmetric literal in this trail with its // image not already assigned to true by the solver. bool Enqueue(const Trail& trail, Literal literal, Literal image, - std::vector* permutation_trail); + std::vector* p_trail); // The identity permutation over all the literals. // This is temporary modified to encode a sparse permutation and then always diff --git a/ortools/sat/theta_tree.h b/ortools/sat/theta_tree.h index fd13f3328a..46125e6d38 100644 --- a/ortools/sat/theta_tree.h +++ b/ortools/sat/theta_tree.h @@ -179,7 +179,7 @@ class ThetaLambdaTree { int GetEventFromLeaf(int leaf) const; // Propagates the change of leaf energies and envelopes towards the root. - void RefreshNode(int leaf); + void RefreshNode(int node); // Finds the maximum leaf under node such that // initial_envelope(leaf) + sum_{leaf' >= leaf} energy_min(leaf') diff --git a/ortools/util/fp_utils.h b/ortools/util/fp_utils.h index 91c70f3375..78c40527a1 100644 --- a/ortools/util/fp_utils.h +++ b/ortools/util/fp_utils.h @@ -179,7 +179,7 @@ inline bool IsIntegerWithinTolerance(FloatType x, FloatType tolerance) { // Given an array of doubles, this computes a positive scaling factor such that // the scaled doubles can then be rounded to integers with little or no loss of // precision, and so that the L1 norm of these integers is <= max_sum. More -// precisely, the following formulas will hold: +// precisely, the following formulas will hold (x[i] is input[i], for brevity): // - For all i, |round(factor * x[i]) / factor - x[i]| <= error * |x[i]| // - The sum over i of |round(factor * x[i])| <= max_sum. // @@ -199,7 +199,7 @@ inline bool IsIntegerWithinTolerance(FloatType x, FloatType tolerance) { // // TODO(user): incorporate the gcd computation here? The issue is that I am // not sure if I just do factor /= gcd that round(x * factor) will be the same. -void GetBestScalingOfDoublesToInt64(const std::vector& x, +void GetBestScalingOfDoublesToInt64(const std::vector& input, int64 max_absolute_sum, double* scaling_factor, double* max_relative_coeff_error); @@ -213,7 +213,7 @@ void GetBestScalingOfDoublesToInt64(const std::vector& x, // difference between the exact scaled sum and the rounded one. One needs to // divide this by scaling_factor to have the maximum absolute error on the // original sum. -void GetBestScalingOfDoublesToInt64(const std::vector& x, +void GetBestScalingOfDoublesToInt64(const std::vector& input, const std::vector& lb, const std::vector& ub, int64 max_absolute_sum, diff --git a/ortools/util/functions_swig_test_helpers.h b/ortools/util/functions_swig_test_helpers.h index 0eaa6c4fdf..f26dfdb2d9 100644 --- a/ortools/util/functions_swig_test_helpers.h +++ b/ortools/util/functions_swig_test_helpers.h @@ -23,6 +23,7 @@ #include #include +#include #include "ortools/base/integral_types.h" namespace operations_research { diff --git a/ortools/util/graph_export.cc b/ortools/util/graph_export.cc index 2f9fec86a4..e9c600a4dc 100644 --- a/ortools/util/graph_export.cc +++ b/ortools/util/graph_export.cc @@ -52,20 +52,20 @@ class DotSyntax : public GraphSyntax { std::string Node(const std::string& name, const std::string& label, const std::string& shape, const std::string& color) override { - return StringPrintf("%s [shape=%s label=\"%s\" color=%s]\n", name.c_str(), + return absl::StrFormat("%s [shape=%s label=\"%s\" color=%s]\n", name.c_str(), shape.c_str(), label.c_str(), color.c_str()); } // Adds one link in the generated graph. std::string Link(const std::string& source, const std::string& destination, const std::string& label) override { - return StringPrintf("%s -> %s [label=%s]\n", source.c_str(), + return absl::StrFormat("%s -> %s [label=%s]\n", source.c_str(), destination.c_str(), label.c_str()); } // File header. std::string Header(const std::string& name) override { - return StringPrintf("graph %s {\n", name.c_str()); + return absl::StrFormat("graph %s {\n", name.c_str()); } // File footer. @@ -79,7 +79,7 @@ class GmlSyntax : public GraphSyntax { std::string Node(const std::string& name, const std::string& label, const std::string& shape, const std::string& color) override { - return StringPrintf( + return absl::StrFormat( " node [\n" " name \"%s\"\n" " label \"%s\"\n" @@ -94,7 +94,7 @@ class GmlSyntax : public GraphSyntax { // Adds one link in the generated graph. std::string Link(const std::string& source, const std::string& destination, const std::string& label) override { - return StringPrintf( + return absl::StrFormat( " edge [\n" " label \"%s\"\n" " source \"%s\"\n" @@ -105,7 +105,7 @@ class GmlSyntax : public GraphSyntax { // File header. std::string Header(const std::string& name) override { - return StringPrintf( + return absl::StrFormat( "graph [\n" " name \"%s\"\n", name.c_str()); diff --git a/ortools/util/monoid_operation_tree.h b/ortools/util/monoid_operation_tree.h index 38052f4f00..b2043bf734 100644 --- a/ortools/util/monoid_operation_tree.h +++ b/ortools/util/monoid_operation_tree.h @@ -210,11 +210,12 @@ std::string MonoidOperationTree::DebugString() const { for (int i = 0; i < num_nodes_; ++i) { if (((i + 1) & i) == 0) { // New layer - StringAppendF(&out, "-------------- Layer %d ---------------\n", layer); + absl::StrAppendFormat(&out, "-------------- Layer %d ---------------\n", + layer); ++layer; } - StringAppendF(&out, "Position %d: %s\n", i, - nodes_[i].DebugString().c_str()); + absl::StrAppendFormat(&out, "Position %d: %s\n", i, + nodes_[i].DebugString().c_str()); } return out; } diff --git a/ortools/util/piecewise_linear_function.h b/ortools/util/piecewise_linear_function.h index 8f746bf3c1..381818d28d 100644 --- a/ortools/util/piecewise_linear_function.h +++ b/ortools/util/piecewise_linear_function.h @@ -232,11 +232,11 @@ class PiecewiseLinearFunction { // Adds the function to the existing one. The domain of the resulting // function is the intersection of the two domains. The overflows and // the underflows are sticky. - void Add(const PiecewiseLinearFunction& function); + void Add(const PiecewiseLinearFunction& other); // Subtracts the function to the existing one. The domain of the // resulting function is the intersection of the two domains. The // overflows and the underflows are sticky. - void Subtract(const PiecewiseLinearFunction& function); + void Subtract(const PiecewiseLinearFunction& other); // Decomposes the piecewise linear function in a set of convex piecewise // linear functions. The objects in the vector are owned by the client code. std::vector DecomposeToConvexFunctions() const; diff --git a/ortools/util/sorted_interval_list.cc b/ortools/util/sorted_interval_list.cc index e236f88d48..a674d5cf67 100644 --- a/ortools/util/sorted_interval_list.cc +++ b/ortools/util/sorted_interval_list.cc @@ -310,8 +310,8 @@ SortedDisjointIntervalList::SortedDisjointIntervalList( } SortedDisjointIntervalList::SortedDisjointIntervalList( - const std::vector& vector_representation) { - for (ClosedInterval interval : vector_representation) { + const std::vector& intervals) { + for (ClosedInterval interval : intervals) { InsertInterval(interval.start, interval.end); } } diff --git a/ortools/util/stats.cc b/ortools/util/stats.cc index 2d5a8c4bbf..88b308dce4 100644 --- a/ortools/util/stats.cc +++ b/ortools/util/stats.cc @@ -30,13 +30,13 @@ std::string MemoryUsage() { static const int64 kMegaByte = kKiloByte * kKiloByte; static const int64 kGigaByte = kMegaByte * kKiloByte; if (mem > kDisplayThreshold * kGigaByte) { - return StringPrintf("%.2lf GB", mem * 1.0 / kGigaByte); + return absl::StrFormat("%.2lf GB", mem * 1.0 / kGigaByte); } else if (mem > kDisplayThreshold * kMegaByte) { - return StringPrintf("%.2lf MB", mem * 1.0 / kMegaByte); + return absl::StrFormat("%.2lf MB", mem * 1.0 / kMegaByte); } else if (mem > kDisplayThreshold * kKiloByte) { - return StringPrintf("%2lf KB", mem * 1.0 / kKiloByte); + return absl::StrFormat("%2lf KB", mem * 1.0 / kKiloByte); } else { - return StringPrintf("%" GG_LL_FORMAT "d", mem); + return absl::StrFormat("%" GG_LL_FORMAT "d", mem); } } @@ -185,12 +185,12 @@ std::string TimeDistribution::PrintCyclesAsTime(double cycles) { // This epsilon is just to avoid displaying 1000.00ms instead of 1.00s. double eps1 = 1 + 1e-3; double sec = CyclesToSeconds(cycles); - if (sec * eps1 >= 3600.0) return StringPrintf("%.2fh", sec / 3600.0); - if (sec * eps1 >= 60.0) return StringPrintf("%.2fm", sec / 60.0); - if (sec * eps1 >= 1.0) return StringPrintf("%.2fs", sec); - if (sec * eps1 >= 1e-3) return StringPrintf("%.2fms", sec * 1e3); - if (sec * eps1 >= 1e-6) return StringPrintf("%.2fus", sec * 1e6); - return StringPrintf("%.2fns", sec * 1e9); + if (sec * eps1 >= 3600.0) return absl::StrFormat("%.2fh", sec / 3600.0); + if (sec * eps1 >= 60.0) return absl::StrFormat("%.2fm", sec / 60.0); + if (sec * eps1 >= 1.0) return absl::StrFormat("%.2fs", sec); + if (sec * eps1 >= 1e-3) return absl::StrFormat("%.2fms", sec * 1e3); + if (sec * eps1 >= 1e-6) return absl::StrFormat("%.2fus", sec * 1e6); + return absl::StrFormat("%.2fns", sec * 1e9); } void TimeDistribution::AddTimeInSec(double seconds) { diff --git a/ortools/util/stats.h b/ortools/util/stats.h index dd490e2423..77e9c59982 100644 --- a/ortools/util/stats.h +++ b/ortools/util/stats.h @@ -232,10 +232,10 @@ class TimeDistribution : public DistributionStat { static double CyclesToSeconds(double num_cycles); // Adds a time in seconds to this distribution. - void AddTimeInSec(double value); + void AddTimeInSec(double seconds); // Adds a time in CPU cycles to this distribution. - void AddTimeInCycles(double value); + void AddTimeInCycles(double cycles); // Starts the timer in preparation of a StopTimerAndAddElapsedTime(). inline void StartTimer() { timer_.Restart(); } diff --git a/ortools/util/time_limit.h b/ortools/util/time_limit.h index 8fa06f9388..9cdecff03f 100644 --- a/ortools/util/time_limit.h +++ b/ortools/util/time_limit.h @@ -27,6 +27,7 @@ #include "ortools/base/commandlineflags.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" +#include "ortools/base/memory.h" #include "ortools/base/port.h" #include "ortools/base/time_support.h" #include "ortools/base/timer.h" @@ -123,18 +124,18 @@ class TimeLimit { // Creates a time limit object that uses infinite time for wall time, // deterministic time and instruction count limit. static std::unique_ptr Infinite() { - return std::unique_ptr( - new TimeLimit(std::numeric_limits::infinity(), - std::numeric_limits::infinity(), - std::numeric_limits::infinity())); + return absl::make_unique( + std::numeric_limits::infinity(), + std::numeric_limits::infinity(), + std::numeric_limits::infinity()); } // Creates a time limit object that puts limit only on the deterministic time. static std::unique_ptr FromDeterministicTime( double deterministic_limit) { - return std::unique_ptr(new TimeLimit( + return absl::make_unique( std::numeric_limits::infinity(), deterministic_limit, - std::numeric_limits::infinity())); + std::numeric_limits::infinity()); } // Creates a time limit object initialized from an object that provides @@ -145,9 +146,9 @@ class TimeLimit { template static std::unique_ptr FromParameters( const Parameters& parameters) { - return std::unique_ptr(new TimeLimit( + return absl::make_unique( parameters.max_time_in_seconds(), parameters.max_deterministic_time(), - std::numeric_limits::infinity())); + std::numeric_limits::infinity()); } // Sets the instruction limit. We need this method since the static @@ -342,9 +343,9 @@ class NestedTimeLimit { template static std::unique_ptr FromBaseTimeLimitAndParameters( TimeLimit* time_limit, const Parameters& parameters) { - return std::unique_ptr( - new NestedTimeLimit(time_limit, parameters.max_time_in_seconds(), - parameters.max_deterministic_time())); + return absl::make_unique( + time_limit, parameters.max_time_in_seconds(), + parameters.max_deterministic_time()); } // Returns a time limit object that represents the combination of the overall diff --git a/ortools/util/tuple_set.h b/ortools/util/tuple_set.h index da543454e0..4e36d6c924 100644 --- a/ortools/util/tuple_set.h +++ b/ortools/util/tuple_set.h @@ -132,14 +132,14 @@ class IntTupleSet { int index; IntTupleSet::Data* data; IndexData(int i, IntTupleSet::Data* const d) : index(i), data(d) {} - static bool Compare(const IndexData& tuple1, const IndexData& tuple2); + static bool Compare(const IndexData& a, const IndexData& b); }; struct IndexValue { int index; int64 value; IndexValue(int i, int64 v) : index(i), value(v) {} - static bool Compare(const IndexValue& tuple1, const IndexValue& tuple2); + static bool Compare(const IndexValue& a, const IndexValue& b); }; mutable Data* data_; diff --git a/ortools/util/xml_helper.cc b/ortools/util/xml_helper.cc index 535d814e60..2be6e9061f 100644 --- a/ortools/util/xml_helper.cc +++ b/ortools/util/xml_helper.cc @@ -34,7 +34,7 @@ void XmlHelper::StartElement(const std::string& name) { content_.append(">\n"); } tags_.push(name); - StringAppendF(&content_, "<%s", name.c_str()); + absl::StrAppendFormat(&content_, "<%s", name.c_str()); direction_down_ = true; } @@ -70,7 +70,7 @@ void XmlHelper::AddAttribute(const std::string& key, const std::string& value) { } } - StringAppendF(&content_, " %s=\"%s\"", key.c_str(), + absl::StrAppendFormat(&content_, " %s=\"%s\"", key.c_str(), escaped_value.str().c_str()); } @@ -80,7 +80,7 @@ void XmlHelper::EndElement() { if (direction_down_) { content_.append(" />\n"); } else { - StringAppendF(&content_, "\n", tag.c_str()); + absl::StrAppendFormat(&content_, "\n", tag.c_str()); } direction_down_ = false;