From 8d7320b962e0681ccb2b8b90ede81ccb6a66cdbb Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Fri, 8 Dec 2017 14:52:49 +0100 Subject: [PATCH] deep sync with base library; prepare for abseil.io integration --- examples/cpp/fap_parser.cc | 10 +- examples/cpp/flexible_jobshop.h | 3 +- examples/cpp/jobshop.cc | 3 +- examples/cpp/jobshop.h | 2 +- examples/cpp/jobshop_earlytardy.h | 2 +- examples/cpp/multidim_knapsack.cc | 4 +- examples/cpp/network_routing.cc | 2 +- examples/cpp/opb_reader.h | 7 +- examples/cpp/pdptw.cc | 6 +- examples/cpp/print_dimacs_assignment.h | 3 +- examples/cpp/rcpsp_sat.cc | 15 +- examples/cpp/sat_cnf_reader.h | 10 +- examples/cpp/sat_runner.cc | 41 +- examples/cpp/shift_minimization_sat.cc | 12 +- examples/cpp/tsp.cc | 1 + examples/cpp/weighted_tardiness_sat.cc | 9 +- makefiles/Makefile.gen.mk | 148 +++-- makefiles/Makefile.python.mk | 5 +- ortools/algorithms/dynamic_partition.cc | 4 +- ortools/algorithms/find_graph_symmetries.cc | 19 +- ortools/algorithms/find_graph_symmetries.h | 13 +- ortools/algorithms/sparse_permutation.cc | 3 +- ortools/base/canonical_errors.h | 17 + ortools/base/casts.h | 3 - ortools/base/file.cc | 91 +-- ortools/base/file.h | 60 +- ortools/base/filelineiter.h | 138 +++++ ortools/base/filelinereader.cc | 69 --- ortools/base/filelinereader.h | 52 -- ortools/base/inlined_vector.h | 4 +- ortools/base/join.h | 148 ++++- ortools/base/numbers.cc | 4 - ortools/base/numbers.h | 2 - ortools/base/ptr_util.h | 19 + ortools/base/span.h | 183 +----- ortools/base/split.cc | 13 +- ortools/base/split.h | 30 +- ortools/base/string_view.cc | 4 +- ortools/base/string_view.h | 4 +- ortools/base/stringpiece_utils.h | 6 +- ortools/base/strutil.h | 6 +- ortools/base/time_support.cc | 4 +- ortools/base/time_support.h | 6 +- ortools/base/timer.h | 8 +- ortools/bop/bop_lns.h | 2 +- ortools/bop/complete_optimizer.cc | 7 - ortools/bop/complete_optimizer.h | 1 + ortools/bop/integral_solver.cc | 2 + ortools/constraint_solver/assignment.cc | 72 ++- .../constraint_solver/constraint_solver.cc | 25 +- ortools/constraint_solver/constraint_solver.h | 557 ++++++++++-------- .../constraint_solver/constraint_solveri.h | 47 +- ortools/constraint_solver/constraints.cc | 2 +- ortools/constraint_solver/count_cst.cc | 56 +- .../csharp/constraint_solver.i | 2 +- ortools/constraint_solver/demon_profiler.cc | 5 +- ortools/constraint_solver/element.cc | 26 +- ortools/constraint_solver/expr_array.cc | 9 +- ortools/constraint_solver/expr_cst.cc | 64 +- ortools/constraint_solver/expressions.cc | 470 +++++++-------- .../constraint_solver/graph_constraints.cc | 11 +- ortools/constraint_solver/hybrid.h | 2 +- ortools/constraint_solver/interval.cc | 7 +- ortools/constraint_solver/io.cc | 3 +- ortools/constraint_solver/local_search.cc | 77 +-- ortools/constraint_solver/model_cache.cc | 2 +- ortools/constraint_solver/pack.cc | 17 +- ortools/constraint_solver/resource.cc | 11 +- ortools/constraint_solver/routing.cc | 4 - ortools/constraint_solver/search.cc | 428 +++++++++----- ortools/constraint_solver/table.cc | 3 +- ortools/constraint_solver/trace.cc | 11 +- ortools/constraint_solver/utilities.cc | 3 +- ortools/data/rcpsp_parser.cc | 6 +- ortools/flatzinc/cp_model_fz_solver.cc | 10 +- ortools/flatzinc/model.cc | 4 +- ortools/flatzinc/parser_util.h | 2 - ortools/glop/preprocessor.cc | 74 +-- ortools/glop/revised_simplex.cc | 12 +- ortools/graph/connected_components.cc | 136 +++++ ortools/graph/connected_components.h | 255 ++++++++ ortools/graph/io.h | 22 +- ortools/graph/max_flow.cc | 6 +- ortools/graph/min_cost_flow.cc | 8 +- ortools/graph/util.h | 18 +- ortools/linear_solver/glop_interface.cc | 13 +- ortools/linear_solver/linear_solver.cc | 71 +-- ortools/linear_solver/linear_solver.h | 9 +- ortools/linear_solver/linear_solver.proto | 13 +- ortools/linear_solver/model_validator.cc | 47 +- ortools/lp_data/lp_data.cc | 8 +- ortools/lp_data/lp_print_utils.cc | 12 +- ortools/lp_data/mps_reader.cc | 29 +- ortools/lp_data/mps_reader.h | 2 +- ortools/lp_data/sparse_vector.h | 2 +- ortools/port/file.h | 33 ++ ortools/port/file_nonport.cc | 34 ++ ortools/port/proto_utils.h | 6 +- ortools/sat/clause.cc | 103 +--- ortools/sat/clause.h | 62 +- ortools/sat/cp_model_checker.cc | 32 +- ortools/sat/cp_model_presolve.cc | 1 + ortools/sat/cp_model_search.h | 1 + ortools/sat/cp_model_solver.cc | 68 +-- ortools/sat/drat.cc | 6 +- ortools/sat/drat.h | 6 +- ortools/sat/integer.cc | 180 +----- ortools/sat/integer.h | 79 +-- ortools/sat/integer_search.cc | 244 ++++++++ ortools/sat/integer_search.h | 102 ++++ ortools/sat/linear_programming_constraint.cc | 77 +-- ortools/sat/linear_programming_constraint.h | 60 +- ortools/sat/optimization.cc | 4 +- ortools/sat/optimization.h | 1 + ortools/sat/pb_constraint.cc | 2 +- ortools/sat/pb_constraint.h | 2 +- ortools/sat/restart.cc | 2 +- ortools/sat/sat_base.h | 12 +- ortools/sat/sat_decision.cc | 72 ++- ortools/sat/sat_decision.h | 17 +- ortools/sat/sat_parameters.proto | 21 +- ortools/sat/sat_solver.cc | 26 +- ortools/sat/sat_solver.h | 14 +- ortools/sat/simplification.cc | 4 +- ortools/sat/simplification.h | 6 +- ortools/sat/symmetry.cc | 6 +- ortools/sat/symmetry.h | 4 +- ortools/util/file_util.cc | 6 +- ortools/util/file_util.h | 14 +- ortools/util/filelineiter.h | 1 - ortools/util/integer_pq.h | 190 ++++++ ortools/util/optional_boolean.proto | 30 + ortools/util/proto_tools.cc | 8 +- ortools/util/sort.h | 29 +- ortools/util/sorted_interval_list.cc | 2 +- ortools/util/sorted_interval_list.h | 2 +- ortools/util/stats.cc | 34 +- ortools/util/stats.h | 43 +- ortools/util/time_limit.cc | 12 +- ortools/util/time_limit.h | 8 +- 140 files changed, 3342 insertions(+), 2202 deletions(-) create mode 100644 ortools/base/canonical_errors.h create mode 100644 ortools/base/filelineiter.h delete mode 100644 ortools/base/filelinereader.cc delete mode 100644 ortools/base/filelinereader.h create mode 100644 ortools/base/ptr_util.h create mode 100644 ortools/graph/connected_components.cc create mode 100644 ortools/graph/connected_components.h create mode 100644 ortools/port/file.h create mode 100644 ortools/port/file_nonport.cc create mode 100644 ortools/sat/integer_search.cc create mode 100644 ortools/sat/integer_search.h create mode 100644 ortools/util/integer_pq.h create mode 100644 ortools/util/optional_boolean.proto diff --git a/examples/cpp/fap_parser.cc b/examples/cpp/fap_parser.cc index c8ec18f9bf..2d9d8012b8 100644 --- a/examples/cpp/fap_parser.cc +++ b/examples/cpp/fap_parser.cc @@ -27,7 +27,7 @@ void ParseFileByLines(const std::string& filename, std::vector* lin CHECK_NOTNULL(lines); std::string result; CHECK_OK(file::GetContents(filename, &result, file::Defaults())); - *lines = strings::Split(result, '\n', strings::SkipEmpty()); + *lines = strings::Split(result, '\n', absl::SkipEmpty()); } // VariableParser Implementation @@ -41,7 +41,7 @@ void VariableParser::Parse() { ParseFileByLines(filename_, &lines); for (const std::string& line : lines) { std::vector tokens = - strings::Split(line, ' ', strings::SkipEmpty()); + strings::Split(line, ' ', absl::SkipEmpty()); if (tokens.empty()) { continue; } @@ -68,7 +68,7 @@ void DomainParser::Parse() { ParseFileByLines(filename_, &lines); for (const std::string& line : lines) { std::vector tokens = - strings::Split(line, ' ', strings::SkipEmpty()); + strings::Split(line, ' ', absl::SkipEmpty()); if (tokens.empty()) { continue; } @@ -99,7 +99,7 @@ void ConstraintParser::Parse() { ParseFileByLines(filename_, &lines); for (const std::string& line : lines) { std::vector tokens = - strings::Split(line, ' ', strings::SkipEmpty()); + strings::Split(line, ' ', absl::SkipEmpty()); if (tokens.empty()) { continue; } @@ -158,7 +158,7 @@ void ParametersParser::Parse() { objective = false; if (line.find("=") != std::string::npos) { std::vector tokens = - strings::Split(line, ' ', strings::SkipEmpty()); + absl::StrSplit(line, ' ', absl::SkipEmpty()); CHECK_GE(tokens.size(), 3); coefficients.push_back(atoi32(tokens[2].c_str())); } diff --git a/examples/cpp/flexible_jobshop.h b/examples/cpp/flexible_jobshop.h index 2cfbd6f44e..8357a0bd9e 100644 --- a/examples/cpp/flexible_jobshop.h +++ b/examples/cpp/flexible_jobshop.h @@ -132,8 +132,7 @@ class FlexibleJobShopData { private: void ProcessNewLine(const std::string& line) { static const char kWordDelimiters[] = " "; - std::vector words = - strings::Split(line, " ", strings::SkipEmpty()); + std::vector words = absl::StrSplit(line, " ", absl::SkipEmpty()); if (machine_count_ == -1 && words.size() > 1) { job_count_ = atoi32(words[0]); machine_count_ = atoi32(words[1]); diff --git a/examples/cpp/jobshop.cc b/examples/cpp/jobshop.cc index a906ec1d26..3a0972228d 100644 --- a/examples/cpp/jobshop.cc +++ b/examples/cpp/jobshop.cc @@ -43,6 +43,7 @@ #include "ortools/base/logging.h" #include "ortools/base/stringprintf.h" #include "ortools/base/join.h" +#include "ortools/base/join.h" #include "ortools/constraint_solver/constraint_solver.h" DEFINE_string( @@ -164,7 +165,7 @@ void Jobshop(const JobShopData& data) { for (int m = 0; m < machine_count; ++m) { SequenceVar* const seq = all_sequences[m]; LOG(INFO) << seq->name() << ": " - << strings::Join(collector->ForwardSequence(0, seq), ", "); + << absl::StrJoin(collector->ForwardSequence(0, seq), ", "); } } } diff --git a/examples/cpp/jobshop.h b/examples/cpp/jobshop.h index 9e5d21cbc1..537c3c6b0f 100644 --- a/examples/cpp/jobshop.h +++ b/examples/cpp/jobshop.h @@ -121,7 +121,7 @@ class JobShopData { void ProcessNewLine(const std::string& line) { // TODO(user): more robust logic to support single-task jobs. const std::vector words = - strings::Split(line, ' ', strings::SkipEmpty()); + absl::StrSplit(line, ' ', absl::SkipEmpty()); switch (problem_type_) { case UNDEFINED: { if (words.size() == 2 && words[0] == "instance") { diff --git a/examples/cpp/jobshop_earlytardy.h b/examples/cpp/jobshop_earlytardy.h index 20da495138..6b8fd66602 100644 --- a/examples/cpp/jobshop_earlytardy.h +++ b/examples/cpp/jobshop_earlytardy.h @@ -156,7 +156,7 @@ class EtJobShopData { // TODO(user): more robust logic to support single-task jobs. static const char kWordDelimiters[] = " "; std::vector words = - strings::Split(line, " ", strings::SkipEmpty()); + absl::StrSplit(line, " ", absl::SkipEmpty()); if (words.size() == 2) { job_count_ = atoi32(words[0]); diff --git a/examples/cpp/multidim_knapsack.cc b/examples/cpp/multidim_knapsack.cc index 4a872f60f2..c04f5dfd83 100644 --- a/examples/cpp/multidim_knapsack.cc +++ b/examples/cpp/multidim_knapsack.cc @@ -91,7 +91,7 @@ class MultiDimKnapsackData { // Used internally. void ProcessNewLine(const std::string& line) { const std::vector words = - strings::Split(line, ' ', strings::SkipEmpty()); + strings::Split(line, ' ', absl::SkipEmpty()); line_read_++; if (problem_type_ == -1) { if (words.size() == 1) { @@ -326,7 +326,7 @@ void SolveKnapsack(MultiDimKnapsackData* const data) { assigned_items += ", " + std::to_string(i); } } - if (assigned_items == "") { + if (assigned_items.empty()) { LOG(INFO) << "No items were assigned"; } else { assigned_items.erase(0, 2); diff --git a/examples/cpp/network_routing.cc b/examples/cpp/network_routing.cc index ce7441563d..c8c0a83419 100644 --- a/examples/cpp/network_routing.cc +++ b/examples/cpp/network_routing.cc @@ -398,7 +398,7 @@ class NetworkRoutingSolver { // This method will fill the all_paths_ data structure. all_paths // contains, for each demand, a vector of possible paths, stored as - // a std::unordered_set of arc indices. + // a hash_set of arc indices. int ComputeAllPaths(int extra_hops, int max_paths) { int num_paths = 0; for (int demand_index = 0; demand_index < demands_array_.size(); diff --git a/examples/cpp/opb_reader.h b/examples/cpp/opb_reader.h index d6e3e07449..047b861b5c 100644 --- a/examples/cpp/opb_reader.h +++ b/examples/cpp/opb_reader.h @@ -14,13 +14,14 @@ #ifndef OR_TOOLS_SAT_OPB_READER_H_ #define OR_TOOLS_SAT_OPB_READER_H_ +#include +#include #include #include -#include "ortools/base/integral_types.h" #include "ortools/base/logging.h" +#include "ortools/base/macros.h" #include "ortools/base/strtoint.h" -#include "ortools/base/file.h" #include "ortools/base/split.h" #include "ortools/sat/boolean_problem.pb.h" #include "ortools/util/filelineiter.h" @@ -65,7 +66,7 @@ class OpbReader { void ProcessNewLine(LinearBooleanProblem* problem, const std::string& line) { const std::vector words = - strings::Split(line, ' ', strings::SkipEmpty()); + strings::Split(line, ' ', absl::SkipEmpty()); if (words.empty() || words[0].empty() || words[0][0] == '*') { return; } diff --git a/examples/cpp/pdptw.cc b/examples/cpp/pdptw.cc index 3e1500a301..4c8db1af94 100644 --- a/examples/cpp/pdptw.cc +++ b/examples/cpp/pdptw.cc @@ -151,8 +151,8 @@ namespace { // of integers. Returns true iff the input std::string was entirely valid and parsed. bool SafeParseInt64Array(const std::string& str, std::vector* parsed_int) { static const char kWhiteSpaces[] = " \t\n\v\f\r"; - std::vector items = strings::Split( - str, strings::delimiter::AnyOf(kWhiteSpaces), strings::SkipEmpty()); + std::vector items = absl::StrSplit( + str, strings::delimiter::AnyOf(kWhiteSpaces), absl::SkipEmpty()); parsed_int->assign(items.size(), 0); for (int i = 0; i < items.size(); ++i) { const char* item = items[i].c_str(); @@ -179,7 +179,7 @@ bool LoadAndSolve(const std::string& pdp_file) { << kMaxInputFileSize << " bytes)."; return false; } - lines = strings::Split(contents, '\n', strings::SkipEmpty()); + lines = absl::StrSplit(contents, '\n', absl::SkipEmpty()); } // Reading header. if (lines.empty()) { diff --git a/examples/cpp/print_dimacs_assignment.h b/examples/cpp/print_dimacs_assignment.h index 5b7bb01dfb..b2a5dfa594 100644 --- a/examples/cpp/print_dimacs_assignment.h +++ b/examples/cpp/print_dimacs_assignment.h @@ -48,7 +48,8 @@ void PrintDimacsAssignmentProblem( const LinearSumAssignment& assignment, const TailArrayManager& tail_array_manager, const std::string& output_filename) { - File* output = File::Open(output_filename, "w"); + File* output; + CHECK_OK(file::Open(output_filename, "w", &output, file::Defaults())); const GraphType& graph(assignment.Graph()); std::string output_line = StringPrintf("p asn %d %d\n", graph.num_nodes(), graph.num_arcs()); diff --git a/examples/cpp/rcpsp_sat.cc b/examples/cpp/rcpsp_sat.cc index 07180425ac..88b02d66c6 100644 --- a/examples/cpp/rcpsp_sat.cc +++ b/examples/cpp/rcpsp_sat.cc @@ -17,6 +17,8 @@ #include "ortools/base/commandlineflags.h" #include "ortools/base/logging.h" #include "ortools/base/timer.h" +#include "ortools/data/rcpsp.pb.h" +#include "ortools/data/rcpsp_parser.h" #include "ortools/sat/cp_model_solver.h" #include "ortools/sat/cumulative.h" #include "ortools/sat/disjunctive.h" @@ -25,17 +27,17 @@ #include "ortools/sat/model.h" #include "ortools/sat/optimization.h" #include "ortools/sat/precedences.h" -#include "ortools/data/rcpsp_parser.h" -#include "ortools/data/rcpsp.pb.h" DEFINE_string(input, "", "Input file."); DEFINE_string(params, "", "Sat parameters in text proto format."); +// TODO(user): Convert to cp_model.proto + using operations_research::data::rcpsp::RcpspParser; using operations_research::data::rcpsp::RcpspProblem; -using operations_research::data::rcpsp::Task; -using operations_research::data::rcpsp::Resource; using operations_research::data::rcpsp::Recipe; +using operations_research::data::rcpsp::Resource; +using operations_research::data::rcpsp::Task; namespace operations_research { namespace sat { @@ -80,7 +82,7 @@ void LoadAndSolve(const std::string& file_name) { const int horizon = problem.deadline() == -1 ? (problem.horizon() == -1 ? ComputeNaiveHorizon(problem) - : problem.horizon()) + : problem.horizon()) : problem.deadline(); LOG(INFO) << "Horizon = " << horizon; @@ -189,8 +191,7 @@ void LoadAndSolve(const std::string& file_name) { model.Add(LowerOrEqualWithOffset(s1, makespan, delay)); } else { for (int m2 = 0; m2 < num_other_modes; ++m2) { - const int delay = - delay_matrix.recipe_delays(m1).min_delays(m2); + const int delay = delay_matrix.recipe_delays(m1).min_delays(m2); const IntegerVariable s2 = model.Get(StartVar(alternatives_per_task[n][m2])); model.Add(LowerOrEqualWithOffset(s1, s2, delay)); diff --git a/examples/cpp/sat_cnf_reader.h b/examples/cpp/sat_cnf_reader.h index 69f8c558da..dcd0ef1343 100644 --- a/examples/cpp/sat_cnf_reader.h +++ b/examples/cpp/sat_cnf_reader.h @@ -15,12 +15,15 @@ #define OR_TOOLS_SAT_SAT_CNF_READER_H_ #include +#include #include +#include #include #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" +#include "ortools/base/macros.h" #include "ortools/base/strtoint.h" #include "ortools/base/split.h" #include "ortools/base/string_view.h" @@ -103,7 +106,7 @@ class SatCnfReader { return problem_name; } - int64 StringPieceAtoi(string_view input) { + int64 StringPieceAtoi(absl::string_view input) { // Hack: data() is not null terminated, but we do know that it points // inside a std::string where numbers are separated by " " and since atoi64 will // stop at the first invalid char, this works. @@ -112,8 +115,7 @@ class SatCnfReader { void ProcessNewLine(const std::string& line, LinearBooleanProblem* problem) { static const char kWordDelimiters[] = " "; - words_ = strings::Split( - line, kWordDelimiters, + words_ = strings::Split(line, kWordDelimiters, static_cast(strings::SkipEmpty())); if (words_.empty() || words_[0] == "c" || end_marker_seen_) return; if (words_[0] == "%") { @@ -225,7 +227,7 @@ class SatCnfReader { int num_variables_; // Temporary storage for ProcessNewLine(). - std::vector words_; + std::vector words_; // We stores the objective in a map because we want the variables to appear // only once in the LinearObjective proto. diff --git a/examples/cpp/sat_runner.cc b/examples/cpp/sat_runner.cc index 2f2ddd72e8..a4d67acc12 100644 --- a/examples/cpp/sat_runner.cc +++ b/examples/cpp/sat_runner.cc @@ -11,8 +11,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include +#include +#include +#include #include +#include #include #include "ortools/base/commandlineflags.h" @@ -22,27 +25,29 @@ #include "ortools/base/strtoint.h" #include "ortools/base/timer.h" #include "ortools/base/file.h" -#include "google/protobuf/descriptor.h" -#include "google/protobuf/message.h" #include "google/protobuf/text_format.h" -#include "ortools/base/stringpiece_utils.h" -#include "ortools/base/strutil.h" -#include "ortools/base/threadpool.h" +#include "ortools/base/join.h" +#include "ortools/base/string_view_utils.h" #include "ortools/algorithms/sparse_permutation.h" #include "ortools/sat/boolean_problem.h" +#include "ortools/sat/boolean_problem.pb.h" #include "ortools/sat/cp_model.pb.h" #include "ortools/sat/cp_model_solver.h" #include "ortools/sat/drat.h" +#include "ortools/sat/lp_utils.h" +#include "ortools/sat/model.h" #include "examples/cpp/opb_reader.h" #include "ortools/sat/optimization.h" +#include "ortools/sat/pb_constraint.h" +#include "ortools/sat/sat_base.h" #include "examples/cpp/sat_cnf_reader.h" -#include "ortools/base/join.h" +#include "ortools/sat/sat_parameters.pb.h" #include "ortools/sat/sat_solver.h" #include "ortools/sat/simplification.h" #include "ortools/sat/symmetry.h" #include "ortools/util/file_util.h" +#include "ortools/util/sigint.h" #include "ortools/util/time_limit.h" -#include "ortools/base/random.h" #include "ortools/base/status.h" DEFINE_string( @@ -208,13 +213,6 @@ int Run() { solver->SetDratWriter(drat_writer.get()); } - // The global time limit. - std::unique_ptr time_limit(TimeLimit::FromParameters(parameters)); - - // Catch ^C. - bool interrupt_solve = false; - time_limit->RegisterExternalBooleanAsLimit(&interrupt_solve); - // Read the problem. LinearBooleanProblem problem; CpModelProto cp_model; @@ -227,9 +225,13 @@ int Run() { // TODO(user): clean this hack. Ideally LinearBooleanProblem should be // completely replaced by the more general CpModelProto. if (cp_model.variables_size() != 0) { + problem.Clear(); // We no longer need it, release memory. + bool stopped = false; Model model; - model.GetOrCreate()->SetParameters(parameters); - model.SetSingleton(std::move(time_limit)); + model.Add(NewSatParameters(parameters)); + model.GetOrCreate()->RegisterExternalBooleanAsLimit(&stopped); + model.GetOrCreate()->Register( + [&stopped]() { stopped = true; }); LOG(INFO) << CpModelStats(cp_model); const CpSolverResponse response = SolveCpModel(cp_model, &model); LOG(INFO) << CpSolverResponseStats(response); @@ -428,7 +430,12 @@ static const char kUsage[] = "This program solves a given Boolean linear problem."; int main(int argc, char** argv) { + // By default, we want to show how the solver progress. Note that this needs + // to be set before InitGoogle() which has the nice side-effect of allowing + // the user to override it. + base::SetFlag(&FLAGS_vmodule, "*cp_model*=1"); gflags::SetUsageMessage(kUsage); gflags::ParseCommandLineFlags(&argc, &argv, true); + base::SetFlag(&FLAGS_alsologtostderr, true); return operations_research::sat::Run(); } diff --git a/examples/cpp/shift_minimization_sat.cc b/examples/cpp/shift_minimization_sat.cc index 368ddc41ba..6a86211676 100644 --- a/examples/cpp/shift_minimization_sat.cc +++ b/examples/cpp/shift_minimization_sat.cc @@ -36,7 +36,7 @@ #include "ortools/base/commandlineflags.h" #include "ortools/base/logging.h" #include "ortools/base/strtoint.h" -#include "ortools/util/filelineiter.h" +#include "ortools/base/filelineiter.h" #include "ortools/base/split.h" #include "ortools/sat/cp_constraints.h" #include "ortools/sat/cp_model_solver.h" @@ -90,16 +90,10 @@ class ShiftMinimizationParser { return false; } - File* file = nullptr; - if (!file::Open(file_name, "r", &file, file::Defaults()).ok()) { - LOG(WARNING) << "Can't open " << file_name; - return false; - } - load_status_ = STARTED; for (const std::string& line : - FileLines(file_name, FileLineIterator::REMOVE_INLINE_CR)) { + FileLines(file_name, FileLineIterator::REMOVE_INLINE_CR)) { ProcessLine(line); } @@ -119,7 +113,7 @@ class ShiftMinimizationParser { } const std::vector words = strings::Split( - line, strings::delimiter::AnyOf(" :\t"), strings::SkipEmpty()); + line, strings::delimiter::AnyOf(" :\t"), absl::SkipEmpty()); switch (load_status_) { case NOT_STARTED: { diff --git a/examples/cpp/tsp.cc b/examples/cpp/tsp.cc index 3360b97d0d..74459daaee 100644 --- a/examples/cpp/tsp.cc +++ b/examples/cpp/tsp.cc @@ -31,6 +31,7 @@ #include "ortools/base/commandlineflags.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" +#include "google/protobuf/text_format.h" #include "ortools/base/join.h" #include "ortools/base/join.h" #include "ortools/constraint_solver/routing.h" diff --git a/examples/cpp/weighted_tardiness_sat.cc b/examples/cpp/weighted_tardiness_sat.cc index 05d9183eeb..6385b558e6 100644 --- a/examples/cpp/weighted_tardiness_sat.cc +++ b/examples/cpp/weighted_tardiness_sat.cc @@ -22,12 +22,11 @@ #include "google/protobuf/text_format.h" #include "ortools/base/join.h" #include "ortools/base/split.h" -#include "ortools/base/strtoint.h" #include "ortools/base/strutil.h" #include "ortools/sat/cp_model.pb.h" #include "ortools/sat/cp_model_solver.h" #include "ortools/sat/model.h" -#include "ortools/util/filelineiter.h" +#include "ortools/base/filelineiter.h" DEFINE_string(input, "examples/data/weighted_tardiness/wt40.txt", "wt data file name."); @@ -262,10 +261,10 @@ int main(int argc, char** argv) { std::vector numbers; std::vector entries; - for (const std::string& line : operations_research::FileLines(FLAGS_input)) { - entries = strings::Split(line, ' ', strings::SkipEmpty()); + for (const std::string& line : FileLines(FLAGS_input)) { + entries = absl::StrSplit(line, ' ', absl::SkipEmpty()); for (const std::string& entry : entries) { - numbers.push_back(operations_research::atoi32(entry)); + numbers.push_back(atoi(entry.c_str())); } } diff --git a/makefiles/Makefile.gen.mk b/makefiles/Makefile.gen.mk index eada3fe36d..5b8ab04183 100644 --- a/makefiles/Makefile.gen.mk +++ b/makefiles/Makefile.gen.mk @@ -1,7 +1,6 @@ BASE_DEPS = \ $(SRC_DIR)/ortools/base/adjustable_priority_queue.h \ $(SRC_DIR)/ortools/base/basictypes.h \ - $(SRC_DIR)/ortools/base/callback.h \ $(SRC_DIR)/ortools/base/casts.h \ $(SRC_DIR)/ortools/base/file.h \ $(SRC_DIR)/ortools/base/inlined_vector.h \ @@ -11,7 +10,9 @@ BASE_DEPS = \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/macros.h \ $(SRC_DIR)/ortools/base/status.h \ + $(SRC_DIR)/ortools/base/stringpiece_utils.h \ $(SRC_DIR)/ortools/base/string_view.h \ + $(SRC_DIR)/ortools/base/strutil.h \ $(SRC_DIR)/ortools/base/thorough_hash.h \ $(SRC_DIR)/ortools/base/time_support.h @@ -19,7 +20,6 @@ BASE_LIB_OBJS = \ $(OBJ_DIR)/base/bitmap.$O \ $(OBJ_DIR)/base/callback.$O \ $(OBJ_DIR)/base/file.$O \ - $(OBJ_DIR)/base/filelinereader.$O \ $(OBJ_DIR)/base/join.$O \ $(OBJ_DIR)/base/mutex.$O \ $(OBJ_DIR)/base/numbers.$O \ @@ -57,10 +57,11 @@ $(SRC_DIR)/ortools/base/file.h: \ $(SRC_DIR)/ortools/base/status.h \ $(SRC_DIR)/ortools/base/string_view.h -$(SRC_DIR)/ortools/base/filelinereader.h: \ - $(SRC_DIR)/ortools/base/callback.h \ +$(SRC_DIR)/ortools/base/filelineiter.h: \ $(SRC_DIR)/ortools/base/file.h \ - $(SRC_DIR)/ortools/base/integral_types.h + $(SRC_DIR)/ortools/base/logging.h \ + $(SRC_DIR)/ortools/base/stringpiece_utils.h \ + $(SRC_DIR)/ortools/base/strutil.h $(SRC_DIR)/ortools/base/hash.h: \ $(SRC_DIR)/ortools/base/basictypes.h @@ -176,13 +177,6 @@ $(OBJ_DIR)/base/file.$O: \ $(SRC_DIR)/ortools/base/logging.h $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sbase$Sfile.cc $(OBJ_OUT)$(OBJ_DIR)$Sbase$Sfile.$O -$(OBJ_DIR)/base/filelinereader.$O: \ - $(SRC_DIR)/ortools/base/filelinereader.cc \ - $(SRC_DIR)/ortools/base/file.h \ - $(SRC_DIR)/ortools/base/filelinereader.h \ - $(SRC_DIR)/ortools/base/logging.h - $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sbase$Sfilelinereader.cc $(OBJ_OUT)$(OBJ_DIR)$Sbase$Sfilelinereader.$O - $(OBJ_DIR)/base/join.$O: \ $(SRC_DIR)/ortools/base/join.cc \ $(SRC_DIR)/ortools/base/basictypes.h \ @@ -252,7 +246,6 @@ $(OBJ_DIR)/base/time_support.$O: \ PORT_DEPS = \ $(SRC_DIR)/ortools/base/adjustable_priority_queue.h \ $(SRC_DIR)/ortools/base/basictypes.h \ - $(SRC_DIR)/ortools/base/callback.h \ $(SRC_DIR)/ortools/base/casts.h \ $(SRC_DIR)/ortools/base/file.h \ $(SRC_DIR)/ortools/base/inlined_vector.h \ @@ -262,13 +255,20 @@ PORT_DEPS = \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/macros.h \ $(SRC_DIR)/ortools/base/status.h \ + $(SRC_DIR)/ortools/base/stringpiece_utils.h \ $(SRC_DIR)/ortools/base/string_view.h \ + $(SRC_DIR)/ortools/base/strutil.h \ $(SRC_DIR)/ortools/base/thorough_hash.h \ $(SRC_DIR)/ortools/base/time_support.h PORT_LIB_OBJS = \ + $(OBJ_DIR)/port/file_nonport.$O \ $(OBJ_DIR)/port/sysinfo_nonport.$O +$(SRC_DIR)/ortools/port/file.h: \ + $(SRC_DIR)/ortools/base/status.h \ + $(SRC_DIR)/ortools/base/string_view.h + $(SRC_DIR)/ortools/port/proto_utils.h: \ $(SRC_DIR)/ortools/base/join.h @@ -278,6 +278,11 @@ $(SRC_DIR)/ortools/port/sysinfo.h: \ $(SRC_DIR)/ortools/port/utf8.h: \ $(SRC_DIR)/ortools/base/encodingutils.h +$(OBJ_DIR)/port/file_nonport.$O: \ + $(SRC_DIR)/ortools/port/file_nonport.cc \ + $(SRC_DIR)/ortools/port/file.h + $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sport$Sfile_nonport.cc $(OBJ_OUT)$(OBJ_DIR)$Sport$Sfile_nonport.$O + $(OBJ_DIR)/port/sysinfo_nonport.$O: \ $(SRC_DIR)/ortools/port/sysinfo_nonport.cc \ $(SRC_DIR)/ortools/port/sysinfo.h \ @@ -288,9 +293,9 @@ UTIL_DEPS = \ $(SRC_DIR)/ortools/util/bitset.h \ $(SRC_DIR)/ortools/util/running_stat.h \ $(SRC_DIR)/ortools/util/saturated_arithmetic.h \ + $(SRC_DIR)/ortools/util/time_limit.h \ $(SRC_DIR)/ortools/base/adjustable_priority_queue.h \ $(SRC_DIR)/ortools/base/basictypes.h \ - $(SRC_DIR)/ortools/base/callback.h \ $(SRC_DIR)/ortools/base/casts.h \ $(SRC_DIR)/ortools/base/file.h \ $(SRC_DIR)/ortools/base/inlined_vector.h \ @@ -300,7 +305,9 @@ UTIL_DEPS = \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/macros.h \ $(SRC_DIR)/ortools/base/status.h \ + $(SRC_DIR)/ortools/base/stringpiece_utils.h \ $(SRC_DIR)/ortools/base/string_view.h \ + $(SRC_DIR)/ortools/base/strutil.h \ $(SRC_DIR)/ortools/base/thorough_hash.h \ $(SRC_DIR)/ortools/base/time_support.h @@ -318,7 +325,8 @@ UTIL_LIB_OBJS = \ $(OBJ_DIR)/util/sorted_interval_list.$O \ $(OBJ_DIR)/util/stats.$O \ $(OBJ_DIR)/util/time_limit.$O \ - $(OBJ_DIR)/util/xml_helper.$O + $(OBJ_DIR)/util/xml_helper.$O \ + $(OBJ_DIR)/util/optional_boolean.pb.$O $(SRC_DIR)/ortools/util/affine_relation.h: \ $(SRC_DIR)/ortools/base/iterator_adaptors.h \ @@ -361,6 +369,10 @@ $(SRC_DIR)/ortools/util/graph_export.h: \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/macros.h +$(SRC_DIR)/ortools/util/integer_pq.h: \ + $(SRC_DIR)/ortools/base/logging.h \ + $(SRC_DIR)/ortools/base/macros.h + $(SRC_DIR)/ortools/util/monoid_operation_tree.h: \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/macros.h \ @@ -403,6 +415,7 @@ $(SRC_DIR)/ortools/util/sorted_interval_list.h: \ $(SRC_DIR)/ortools/base/span.h $(SRC_DIR)/ortools/util/stats.h: \ + $(SRC_DIR)/ortools/util/time_limit.h \ $(SRC_DIR)/ortools/base/timer.h $(SRC_DIR)/ortools/util/time_limit.h: \ @@ -537,11 +550,18 @@ $(OBJ_DIR)/util/xml_helper.$O: \ $(SRC_DIR)/ortools/base/strutil.h $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sutil$Sxml_helper.cc $(OBJ_OUT)$(OBJ_DIR)$Sutil$Sxml_helper.$O +$(GEN_DIR)/ortools/util/optional_boolean.pb.cc: $(SRC_DIR)/ortools/util/optional_boolean.proto + $(PROTOBUF_DIR)/bin/protoc --proto_path=$(INC_DIR) --cpp_out=$(GEN_DIR) $(SRC_DIR)/ortools/util/optional_boolean.proto + +$(GEN_DIR)/ortools/util/optional_boolean.pb.h: $(GEN_DIR)/ortools/util/optional_boolean.pb.cc + +$(OBJ_DIR)/util/optional_boolean.pb.$O: $(GEN_DIR)/ortools/util/optional_boolean.pb.cc + $(CCC) $(CFLAGS) -c $(GEN_DIR)/ortools/util/optional_boolean.pb.cc $(OBJ_OUT)$(OBJ_DIR)$Sutil$Soptional_boolean.pb.$O + DATA_DEPS = \ $(GEN_DIR)/ortools/data/rcpsp.pb.h \ $(SRC_DIR)/ortools/base/adjustable_priority_queue.h \ $(SRC_DIR)/ortools/base/basictypes.h \ - $(SRC_DIR)/ortools/base/callback.h \ $(SRC_DIR)/ortools/base/casts.h \ $(SRC_DIR)/ortools/base/file.h \ $(SRC_DIR)/ortools/base/inlined_vector.h \ @@ -551,12 +571,15 @@ DATA_DEPS = \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/macros.h \ $(SRC_DIR)/ortools/base/status.h \ + $(SRC_DIR)/ortools/base/stringpiece_utils.h \ $(SRC_DIR)/ortools/base/string_view.h \ + $(SRC_DIR)/ortools/base/strutil.h \ $(SRC_DIR)/ortools/base/thorough_hash.h \ $(SRC_DIR)/ortools/base/time_support.h \ $(SRC_DIR)/ortools/util/bitset.h \ $(SRC_DIR)/ortools/util/running_stat.h \ - $(SRC_DIR)/ortools/util/saturated_arithmetic.h + $(SRC_DIR)/ortools/util/saturated_arithmetic.h \ + $(SRC_DIR)/ortools/util/time_limit.h DATA_LIB_OBJS = \ $(OBJ_DIR)/data/rcpsp_parser.$O \ @@ -596,9 +619,9 @@ LP_DATA_DEPS = \ $(SRC_DIR)/ortools/util/bitset.h \ $(SRC_DIR)/ortools/util/running_stat.h \ $(SRC_DIR)/ortools/util/saturated_arithmetic.h \ + $(SRC_DIR)/ortools/util/time_limit.h \ $(SRC_DIR)/ortools/base/adjustable_priority_queue.h \ $(SRC_DIR)/ortools/base/basictypes.h \ - $(SRC_DIR)/ortools/base/callback.h \ $(SRC_DIR)/ortools/base/casts.h \ $(SRC_DIR)/ortools/base/file.h \ $(SRC_DIR)/ortools/base/inlined_vector.h \ @@ -608,7 +631,9 @@ LP_DATA_DEPS = \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/macros.h \ $(SRC_DIR)/ortools/base/status.h \ + $(SRC_DIR)/ortools/base/stringpiece_utils.h \ $(SRC_DIR)/ortools/base/string_view.h \ + $(SRC_DIR)/ortools/base/strutil.h \ $(SRC_DIR)/ortools/base/thorough_hash.h \ $(SRC_DIR)/ortools/base/time_support.h \ $(SRC_DIR)/ortools/algorithms/dynamic_partition.h \ @@ -784,10 +809,10 @@ $(OBJ_DIR)/lp_data/mps_reader.$O: \ $(SRC_DIR)/ortools/lp_data/mps_reader.cc \ $(SRC_DIR)/ortools/lp_data/lp_print_utils.h \ $(SRC_DIR)/ortools/lp_data/mps_reader.h \ + $(SRC_DIR)/ortools/util/filelineiter.h \ $(SRC_DIR)/ortools/base/callback.h \ $(SRC_DIR)/ortools/base/commandlineflags.h \ $(SRC_DIR)/ortools/base/file.h \ - $(SRC_DIR)/ortools/base/filelinereader.h \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/map_util.h \ $(SRC_DIR)/ortools/base/numbers.h \ @@ -835,9 +860,9 @@ GLOP_DEPS = \ $(SRC_DIR)/ortools/util/bitset.h \ $(SRC_DIR)/ortools/util/running_stat.h \ $(SRC_DIR)/ortools/util/saturated_arithmetic.h \ + $(SRC_DIR)/ortools/util/time_limit.h \ $(SRC_DIR)/ortools/base/adjustable_priority_queue.h \ $(SRC_DIR)/ortools/base/basictypes.h \ - $(SRC_DIR)/ortools/base/callback.h \ $(SRC_DIR)/ortools/base/casts.h \ $(SRC_DIR)/ortools/base/file.h \ $(SRC_DIR)/ortools/base/inlined_vector.h \ @@ -847,7 +872,9 @@ GLOP_DEPS = \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/macros.h \ $(SRC_DIR)/ortools/base/status.h \ + $(SRC_DIR)/ortools/base/stringpiece_utils.h \ $(SRC_DIR)/ortools/base/string_view.h \ + $(SRC_DIR)/ortools/base/strutil.h \ $(SRC_DIR)/ortools/base/thorough_hash.h \ $(SRC_DIR)/ortools/base/time_support.h \ $(SRC_DIR)/ortools/lp_data/lp_data.h \ @@ -1144,6 +1171,7 @@ $(OBJ_DIR)/glop/parameters.pb.$O: $(GEN_DIR)/ortools/glop/parameters.pb.cc GRAPH_DEPS = \ $(SRC_DIR)/ortools/graph/christofides.h \ + $(SRC_DIR)/ortools/graph/connected_components.h \ $(SRC_DIR)/ortools/graph/connectivity.h \ $(SRC_DIR)/ortools/graph/ebert_graph.h \ $(SRC_DIR)/ortools/graph/eulerian_path.h \ @@ -1153,7 +1181,6 @@ GRAPH_DEPS = \ $(SRC_DIR)/ortools/graph/minimum_spanning_tree.h \ $(SRC_DIR)/ortools/base/adjustable_priority_queue.h \ $(SRC_DIR)/ortools/base/basictypes.h \ - $(SRC_DIR)/ortools/base/callback.h \ $(SRC_DIR)/ortools/base/casts.h \ $(SRC_DIR)/ortools/base/file.h \ $(SRC_DIR)/ortools/base/inlined_vector.h \ @@ -1163,18 +1190,22 @@ GRAPH_DEPS = \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/macros.h \ $(SRC_DIR)/ortools/base/status.h \ + $(SRC_DIR)/ortools/base/stringpiece_utils.h \ $(SRC_DIR)/ortools/base/string_view.h \ + $(SRC_DIR)/ortools/base/strutil.h \ $(SRC_DIR)/ortools/base/thorough_hash.h \ $(SRC_DIR)/ortools/base/time_support.h \ $(SRC_DIR)/ortools/util/bitset.h \ $(SRC_DIR)/ortools/util/running_stat.h \ - $(SRC_DIR)/ortools/util/saturated_arithmetic.h + $(SRC_DIR)/ortools/util/saturated_arithmetic.h \ + $(SRC_DIR)/ortools/util/time_limit.h GRAPH_LIB_OBJS = \ $(OBJ_DIR)/graph/assignment.$O \ $(OBJ_DIR)/graph/astar.$O \ $(OBJ_DIR)/graph/bellman_ford.$O \ $(OBJ_DIR)/graph/cliques.$O \ + $(OBJ_DIR)/graph/connected_components.$O \ $(OBJ_DIR)/graph/dijkstra.$O \ $(OBJ_DIR)/graph/linear_assignment.$O \ $(OBJ_DIR)/graph/max_flow.$O \ @@ -1200,6 +1231,11 @@ $(SRC_DIR)/ortools/graph/cliques.h: \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/util/time_limit.h +$(SRC_DIR)/ortools/graph/connected_components.h: \ + $(SRC_DIR)/ortools/base/logging.h \ + $(SRC_DIR)/ortools/base/map_util.h \ + $(SRC_DIR)/ortools/base/ptr_util.h + $(SRC_DIR)/ortools/graph/connectivity.h: \ $(SRC_DIR)/ortools/base/integral_types.h \ $(SRC_DIR)/ortools/base/logging.h \ @@ -1236,12 +1272,12 @@ $(SRC_DIR)/ortools/graph/hamiltonian_path.h: \ $(SRC_DIR)/ortools/graph/io.h: \ $(SRC_DIR)/ortools/graph/graph.h \ + $(SRC_DIR)/ortools/base/filelineiter.h \ $(SRC_DIR)/ortools/base/join.h \ $(SRC_DIR)/ortools/base/numbers.h \ $(SRC_DIR)/ortools/base/split.h \ $(SRC_DIR)/ortools/base/status.h \ - $(SRC_DIR)/ortools/base/statusor.h \ - $(SRC_DIR)/ortools/util/filelineiter.h + $(SRC_DIR)/ortools/base/statusor.h $(SRC_DIR)/ortools/graph/linear_assignment.h: \ $(SRC_DIR)/ortools/graph/ebert_graph.h \ @@ -1293,6 +1329,7 @@ $(SRC_DIR)/ortools/graph/strongly_connected_components.h: \ $(SRC_DIR)/ortools/base/macros.h $(SRC_DIR)/ortools/graph/util.h: \ + $(SRC_DIR)/ortools/graph/connected_components.h \ $(SRC_DIR)/ortools/graph/graph.h \ $(SRC_DIR)/ortools/base/hash.h \ $(SRC_DIR)/ortools/base/map_util.h @@ -1323,6 +1360,11 @@ $(OBJ_DIR)/graph/cliques.$O: \ $(SRC_DIR)/ortools/base/hash.h $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sgraph$Scliques.cc $(OBJ_OUT)$(OBJ_DIR)$Sgraph$Scliques.$O +$(OBJ_DIR)/graph/connected_components.$O: \ + $(SRC_DIR)/ortools/graph/connected_components.cc \ + $(SRC_DIR)/ortools/graph/connected_components.h + $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sgraph$Sconnected_components.cc $(OBJ_OUT)$(OBJ_DIR)$Sgraph$Sconnected_components.$O + $(OBJ_DIR)/graph/dijkstra.$O: \ $(SRC_DIR)/ortools/graph/dijkstra.cc \ $(SRC_DIR)/ortools/base/adjustable_priority_queue.h \ @@ -1382,7 +1424,6 @@ ALGORITHMS_DEPS = \ $(SRC_DIR)/ortools/algorithms/dynamic_permutation.h \ $(SRC_DIR)/ortools/base/adjustable_priority_queue.h \ $(SRC_DIR)/ortools/base/basictypes.h \ - $(SRC_DIR)/ortools/base/callback.h \ $(SRC_DIR)/ortools/base/casts.h \ $(SRC_DIR)/ortools/base/file.h \ $(SRC_DIR)/ortools/base/inlined_vector.h \ @@ -1392,13 +1433,17 @@ ALGORITHMS_DEPS = \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/macros.h \ $(SRC_DIR)/ortools/base/status.h \ + $(SRC_DIR)/ortools/base/stringpiece_utils.h \ $(SRC_DIR)/ortools/base/string_view.h \ + $(SRC_DIR)/ortools/base/strutil.h \ $(SRC_DIR)/ortools/base/thorough_hash.h \ $(SRC_DIR)/ortools/base/time_support.h \ $(SRC_DIR)/ortools/util/bitset.h \ $(SRC_DIR)/ortools/util/running_stat.h \ $(SRC_DIR)/ortools/util/saturated_arithmetic.h \ + $(SRC_DIR)/ortools/util/time_limit.h \ $(SRC_DIR)/ortools/graph/christofides.h \ + $(SRC_DIR)/ortools/graph/connected_components.h \ $(SRC_DIR)/ortools/graph/connectivity.h \ $(SRC_DIR)/ortools/graph/ebert_graph.h \ $(SRC_DIR)/ortools/graph/eulerian_path.h \ @@ -1467,9 +1512,11 @@ $(OBJ_DIR)/algorithms/find_graph_symmetries.$O: \ $(SRC_DIR)/ortools/algorithms/dynamic_permutation.h \ $(SRC_DIR)/ortools/algorithms/find_graph_symmetries.h \ $(SRC_DIR)/ortools/algorithms/sparse_permutation.h \ + $(SRC_DIR)/ortools/base/canonical_errors.h \ $(SRC_DIR)/ortools/base/commandlineflags.h \ $(SRC_DIR)/ortools/base/join.h \ $(SRC_DIR)/ortools/base/stringprintf.h \ + $(SRC_DIR)/ortools/base/time_support.h \ $(SRC_DIR)/ortools/graph/iterators.h \ $(SRC_DIR)/ortools/graph/util.h $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Salgorithms$Sfind_graph_symmetries.cc $(OBJ_OUT)$(OBJ_DIR)$Salgorithms$Sfind_graph_symmetries.$O @@ -1504,6 +1551,7 @@ SAT_DEPS = \ $(SRC_DIR)/ortools/sat/drat.h \ $(SRC_DIR)/ortools/sat/integer_expr.h \ $(SRC_DIR)/ortools/sat/integer.h \ + $(SRC_DIR)/ortools/sat/integer_search.h \ $(SRC_DIR)/ortools/sat/intervals.h \ $(SRC_DIR)/ortools/sat/model.h \ $(SRC_DIR)/ortools/sat/pb_constraint.h \ @@ -1517,7 +1565,6 @@ SAT_DEPS = \ $(SRC_DIR)/ortools/sat/theta_tree.h \ $(SRC_DIR)/ortools/base/adjustable_priority_queue.h \ $(SRC_DIR)/ortools/base/basictypes.h \ - $(SRC_DIR)/ortools/base/callback.h \ $(SRC_DIR)/ortools/base/casts.h \ $(SRC_DIR)/ortools/base/file.h \ $(SRC_DIR)/ortools/base/inlined_vector.h \ @@ -1527,15 +1574,19 @@ SAT_DEPS = \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/macros.h \ $(SRC_DIR)/ortools/base/status.h \ + $(SRC_DIR)/ortools/base/stringpiece_utils.h \ $(SRC_DIR)/ortools/base/string_view.h \ + $(SRC_DIR)/ortools/base/strutil.h \ $(SRC_DIR)/ortools/base/thorough_hash.h \ $(SRC_DIR)/ortools/base/time_support.h \ $(SRC_DIR)/ortools/util/bitset.h \ $(SRC_DIR)/ortools/util/running_stat.h \ $(SRC_DIR)/ortools/util/saturated_arithmetic.h \ + $(SRC_DIR)/ortools/util/time_limit.h \ $(SRC_DIR)/ortools/algorithms/dynamic_partition.h \ $(SRC_DIR)/ortools/algorithms/dynamic_permutation.h \ $(SRC_DIR)/ortools/graph/christofides.h \ + $(SRC_DIR)/ortools/graph/connected_components.h \ $(SRC_DIR)/ortools/graph/connectivity.h \ $(SRC_DIR)/ortools/graph/ebert_graph.h \ $(SRC_DIR)/ortools/graph/eulerian_path.h \ @@ -1589,6 +1640,7 @@ SAT_LIB_OBJS = \ $(OBJ_DIR)/sat/flow_costs.$O \ $(OBJ_DIR)/sat/integer.$O \ $(OBJ_DIR)/sat/integer_expr.$O \ + $(OBJ_DIR)/sat/integer_search.$O \ $(OBJ_DIR)/sat/intervals.$O \ $(OBJ_DIR)/sat/linear_programming_constraint.$O \ $(OBJ_DIR)/sat/lp_utils.$O \ @@ -1679,6 +1731,7 @@ $(SRC_DIR)/ortools/sat/cp_model_presolve.h: \ $(SRC_DIR)/ortools/sat/cp_model_search.h: \ $(GEN_DIR)/ortools/sat/cp_model.pb.h \ $(SRC_DIR)/ortools/sat/integer.h \ + $(SRC_DIR)/ortools/sat/integer_search.h \ $(SRC_DIR)/ortools/sat/model.h \ $(SRC_DIR)/ortools/base/integral_types.h @@ -1754,6 +1807,7 @@ $(SRC_DIR)/ortools/sat/integer.h: \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/macros.h \ $(SRC_DIR)/ortools/base/map_util.h \ + $(SRC_DIR)/ortools/base/port.h \ $(SRC_DIR)/ortools/base/span.h \ $(SRC_DIR)/ortools/util/bitset.h \ $(SRC_DIR)/ortools/util/rev.h \ @@ -1761,6 +1815,10 @@ $(SRC_DIR)/ortools/sat/integer.h: \ $(SRC_DIR)/ortools/util/sorted_interval_list.h \ $(SRC_DIR)/ortools/graph/iterators.h +$(SRC_DIR)/ortools/sat/integer_search.h: \ + $(SRC_DIR)/ortools/sat/integer.h \ + $(SRC_DIR)/ortools/sat/sat_solver.h + $(SRC_DIR)/ortools/sat/intervals.h: \ $(SRC_DIR)/ortools/sat/cp_constraints.h \ $(SRC_DIR)/ortools/sat/integer_expr.h \ @@ -1778,6 +1836,7 @@ $(SRC_DIR)/ortools/sat/intervals.h: \ $(SRC_DIR)/ortools/sat/linear_programming_constraint.h: \ $(SRC_DIR)/ortools/sat/integer.h \ + $(SRC_DIR)/ortools/sat/integer_search.h \ $(SRC_DIR)/ortools/sat/model.h \ $(SRC_DIR)/ortools/base/int_type.h \ $(SRC_DIR)/ortools/util/time_limit.h \ @@ -1802,6 +1861,7 @@ $(SRC_DIR)/ortools/sat/model.h: \ $(SRC_DIR)/ortools/sat/optimization.h: \ $(GEN_DIR)/ortools/sat/boolean_problem.pb.h \ $(SRC_DIR)/ortools/sat/integer.h \ + $(SRC_DIR)/ortools/sat/integer_search.h \ $(SRC_DIR)/ortools/sat/model.h \ $(SRC_DIR)/ortools/sat/sat_base.h \ $(SRC_DIR)/ortools/sat/sat_solver.h @@ -1860,9 +1920,9 @@ $(SRC_DIR)/ortools/sat/sat_decision.h: \ $(SRC_DIR)/ortools/sat/pb_constraint.h \ $(SRC_DIR)/ortools/sat/sat_base.h \ $(GEN_DIR)/ortools/sat/sat_parameters.pb.h \ - $(SRC_DIR)/ortools/base/adjustable_priority_queue.h \ $(SRC_DIR)/ortools/base/integral_types.h \ $(SRC_DIR)/ortools/util/bitset.h \ + $(SRC_DIR)/ortools/util/integer_pq.h \ $(SRC_DIR)/ortools/util/random_engine.h $(SRC_DIR)/ortools/sat/sat_solver.h: \ @@ -2030,6 +2090,7 @@ $(OBJ_DIR)/sat/cp_model_presolve.$O: \ $(SRC_DIR)/ortools/base/join.h \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/map_util.h \ + $(SRC_DIR)/ortools/base/port.h \ $(SRC_DIR)/ortools/base/stl_util.h \ $(SRC_DIR)/ortools/util/affine_relation.h \ $(SRC_DIR)/ortools/util/bitset.h \ @@ -2149,6 +2210,12 @@ $(OBJ_DIR)/sat/integer_expr.$O: \ $(SRC_DIR)/ortools/util/sorted_interval_list.h $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Ssat$Sinteger_expr.cc $(OBJ_OUT)$(OBJ_DIR)$Ssat$Sinteger_expr.$O +$(OBJ_DIR)/sat/integer_search.$O: \ + $(SRC_DIR)/ortools/sat/integer_search.cc \ + $(SRC_DIR)/ortools/sat/integer_search.h \ + $(SRC_DIR)/ortools/sat/sat_decision.h + $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Ssat$Sinteger_search.cc $(OBJ_OUT)$(OBJ_DIR)$Ssat$Sinteger_search.$O + $(OBJ_DIR)/sat/intervals.$O: \ $(SRC_DIR)/ortools/sat/intervals.cc \ $(SRC_DIR)/ortools/sat/intervals.h \ @@ -2239,8 +2306,7 @@ $(OBJ_DIR)/sat/restart.$O: \ $(OBJ_DIR)/sat/sat_decision.$O: \ $(SRC_DIR)/ortools/sat/sat_decision.cc \ - $(SRC_DIR)/ortools/sat/sat_decision.h \ - $(SRC_DIR)/ortools/base/adjustable_priority_queue-inl.h + $(SRC_DIR)/ortools/sat/sat_decision.h $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Ssat$Ssat_decision.cc $(OBJ_OUT)$(OBJ_DIR)$Ssat$Ssat_decision.$O $(OBJ_DIR)/sat/sat_solver.$O: \ @@ -2348,7 +2414,6 @@ BOP_DEPS = \ $(SRC_DIR)/ortools/bop/bop_util.h \ $(SRC_DIR)/ortools/base/adjustable_priority_queue.h \ $(SRC_DIR)/ortools/base/basictypes.h \ - $(SRC_DIR)/ortools/base/callback.h \ $(SRC_DIR)/ortools/base/casts.h \ $(SRC_DIR)/ortools/base/file.h \ $(SRC_DIR)/ortools/base/inlined_vector.h \ @@ -2358,12 +2423,15 @@ BOP_DEPS = \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/macros.h \ $(SRC_DIR)/ortools/base/status.h \ + $(SRC_DIR)/ortools/base/stringpiece_utils.h \ $(SRC_DIR)/ortools/base/string_view.h \ + $(SRC_DIR)/ortools/base/strutil.h \ $(SRC_DIR)/ortools/base/thorough_hash.h \ $(SRC_DIR)/ortools/base/time_support.h \ $(SRC_DIR)/ortools/util/bitset.h \ $(SRC_DIR)/ortools/util/running_stat.h \ $(SRC_DIR)/ortools/util/saturated_arithmetic.h \ + $(SRC_DIR)/ortools/util/time_limit.h \ $(SRC_DIR)/ortools/lp_data/lp_data.h \ $(SRC_DIR)/ortools/lp_data/lp_types.h \ $(SRC_DIR)/ortools/lp_data/matrix_scaler.h \ @@ -2394,6 +2462,7 @@ BOP_DEPS = \ $(SRC_DIR)/ortools/sat/drat.h \ $(SRC_DIR)/ortools/sat/integer_expr.h \ $(SRC_DIR)/ortools/sat/integer.h \ + $(SRC_DIR)/ortools/sat/integer_search.h \ $(SRC_DIR)/ortools/sat/intervals.h \ $(SRC_DIR)/ortools/sat/model.h \ $(SRC_DIR)/ortools/sat/pb_constraint.h \ @@ -2661,7 +2730,6 @@ LP_DEPS = \ $(GEN_DIR)/ortools/linear_solver/linear_solver.pb.h \ $(SRC_DIR)/ortools/base/adjustable_priority_queue.h \ $(SRC_DIR)/ortools/base/basictypes.h \ - $(SRC_DIR)/ortools/base/callback.h \ $(SRC_DIR)/ortools/base/casts.h \ $(SRC_DIR)/ortools/base/file.h \ $(SRC_DIR)/ortools/base/inlined_vector.h \ @@ -2671,12 +2739,15 @@ LP_DEPS = \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/macros.h \ $(SRC_DIR)/ortools/base/status.h \ + $(SRC_DIR)/ortools/base/stringpiece_utils.h \ $(SRC_DIR)/ortools/base/string_view.h \ + $(SRC_DIR)/ortools/base/strutil.h \ $(SRC_DIR)/ortools/base/thorough_hash.h \ $(SRC_DIR)/ortools/base/time_support.h \ $(SRC_DIR)/ortools/util/bitset.h \ $(SRC_DIR)/ortools/util/running_stat.h \ $(SRC_DIR)/ortools/util/saturated_arithmetic.h \ + $(SRC_DIR)/ortools/util/time_limit.h \ $(SRC_DIR)/ortools/lp_data/lp_data.h \ $(SRC_DIR)/ortools/lp_data/lp_types.h \ $(SRC_DIR)/ortools/lp_data/matrix_scaler.h \ @@ -2791,7 +2862,6 @@ $(OBJ_DIR)/linear_solver/glop_interface.$O: \ $(SRC_DIR)/ortools/linear_solver/glop_utils.h \ $(SRC_DIR)/ortools/linear_solver/linear_solver.h \ $(SRC_DIR)/ortools/base/commandlineflags.h \ - $(SRC_DIR)/ortools/base/file.h \ $(SRC_DIR)/ortools/base/hash.h \ $(SRC_DIR)/ortools/base/integral_types.h \ $(SRC_DIR)/ortools/base/logging.h \ @@ -2845,17 +2915,14 @@ $(OBJ_DIR)/linear_solver/linear_solver.$O: \ $(SRC_DIR)/ortools/linear_solver/model_validator.h \ $(SRC_DIR)/ortools/base/accurate_sum.h \ $(SRC_DIR)/ortools/base/commandlineflags.h \ - $(SRC_DIR)/ortools/base/file.h \ $(SRC_DIR)/ortools/base/hash.h \ $(SRC_DIR)/ortools/base/integral_types.h \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/map_util.h \ - $(SRC_DIR)/ortools/base/numbers.h \ $(SRC_DIR)/ortools/base/stl_util.h \ $(SRC_DIR)/ortools/base/stringprintf.h \ $(SRC_DIR)/ortools/base/timer.h \ - $(SRC_DIR)/ortools/util/fp_utils.h \ - $(SRC_DIR)/ortools/util/proto_tools.h + $(SRC_DIR)/ortools/util/fp_utils.h $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Slinear_solver$Slinear_solver.cc $(OBJ_OUT)$(OBJ_DIR)$Slinear_solver$Slinear_solver.$O $(OBJ_DIR)/linear_solver/model_exporter.$O: \ @@ -2894,7 +2961,8 @@ $(OBJ_DIR)/linear_solver/scip_interface.$O: \ $(GEN_DIR)/ortools/linear_solver/linear_solver.pb.cc: $(SRC_DIR)/ortools/linear_solver/linear_solver.proto $(PROTOBUF_DIR)/bin/protoc --proto_path=$(INC_DIR) --cpp_out=$(GEN_DIR) $(SRC_DIR)/ortools/linear_solver/linear_solver.proto -$(GEN_DIR)/ortools/linear_solver/linear_solver.pb.h: $(GEN_DIR)/ortools/linear_solver/linear_solver.pb.cc +$(GEN_DIR)/ortools/linear_solver/linear_solver.pb.h: $(GEN_DIR)/ortools/linear_solver/linear_solver.pb.cc \ + $(GEN_DIR)/ortools/util/optional_boolean.pb.h $(OBJ_DIR)/linear_solver/linear_solver.pb.$O: $(GEN_DIR)/ortools/linear_solver/linear_solver.pb.cc $(CCC) $(CFLAGS) -c $(GEN_DIR)/ortools/linear_solver/linear_solver.pb.cc $(OBJ_OUT)$(OBJ_DIR)$Slinear_solver$Slinear_solver.pb.$O @@ -2908,7 +2976,6 @@ CP_DEPS = \ $(GEN_DIR)/ortools/constraint_solver/solver_parameters.pb.h \ $(SRC_DIR)/ortools/base/adjustable_priority_queue.h \ $(SRC_DIR)/ortools/base/basictypes.h \ - $(SRC_DIR)/ortools/base/callback.h \ $(SRC_DIR)/ortools/base/casts.h \ $(SRC_DIR)/ortools/base/file.h \ $(SRC_DIR)/ortools/base/inlined_vector.h \ @@ -2918,13 +2985,17 @@ CP_DEPS = \ $(SRC_DIR)/ortools/base/logging.h \ $(SRC_DIR)/ortools/base/macros.h \ $(SRC_DIR)/ortools/base/status.h \ + $(SRC_DIR)/ortools/base/stringpiece_utils.h \ $(SRC_DIR)/ortools/base/string_view.h \ + $(SRC_DIR)/ortools/base/strutil.h \ $(SRC_DIR)/ortools/base/thorough_hash.h \ $(SRC_DIR)/ortools/base/time_support.h \ $(SRC_DIR)/ortools/util/bitset.h \ $(SRC_DIR)/ortools/util/running_stat.h \ $(SRC_DIR)/ortools/util/saturated_arithmetic.h \ + $(SRC_DIR)/ortools/util/time_limit.h \ $(SRC_DIR)/ortools/graph/christofides.h \ + $(SRC_DIR)/ortools/graph/connected_components.h \ $(SRC_DIR)/ortools/graph/connectivity.h \ $(SRC_DIR)/ortools/graph/ebert_graph.h \ $(SRC_DIR)/ortools/graph/eulerian_path.h \ @@ -2943,6 +3014,7 @@ CP_DEPS = \ $(SRC_DIR)/ortools/sat/drat.h \ $(SRC_DIR)/ortools/sat/integer_expr.h \ $(SRC_DIR)/ortools/sat/integer.h \ + $(SRC_DIR)/ortools/sat/integer_search.h \ $(SRC_DIR)/ortools/sat/intervals.h \ $(SRC_DIR)/ortools/sat/model.h \ $(SRC_DIR)/ortools/sat/pb_constraint.h \ diff --git a/makefiles/Makefile.python.mk b/makefiles/Makefile.python.mk index 17ab712bd5..64e1b81edf 100755 --- a/makefiles/Makefile.python.mk +++ b/makefiles/Makefile.python.mk @@ -205,7 +205,10 @@ endif pylp: $(LIB_DIR)/_pywraplp.$(SWIG_LIB_SUFFIX) $(GEN_DIR)/ortools/linear_solver/pywraplp.py -$(GEN_DIR)/ortools/linear_solver/linear_solver_pb2.py: $(SRC_DIR)/ortools/linear_solver/linear_solver.proto +$(GEN_DIR)/ortools/util/optional_boolean_pb2.py: $(SRC_DIR)/ortools/util/optional_boolean.proto + $(PROTOBUF_DIR)/bin/protoc --proto_path=$(INC_DIR) --python_out=$(GEN_DIR) $(SRC_DIR)/ortools/util/optional_boolean.proto + +$(GEN_DIR)/ortools/linear_solver/linear_solver_pb2.py: $(SRC_DIR)/ortools/linear_solver/linear_solver.proto $(GEN_DIR)/ortools/util/optional_boolean_pb2.py $(PROTOBUF_DIR)/bin/protoc --proto_path=$(INC_DIR) --python_out=$(GEN_DIR) $(SRC_DIR)/ortools/linear_solver/linear_solver.proto $(GEN_DIR)/ortools/linear_solver/pywraplp.py: \ diff --git a/ortools/algorithms/dynamic_partition.cc b/ortools/algorithms/dynamic_partition.cc index 3ddf87259a..6506c40a0e 100644 --- a/ortools/algorithms/dynamic_partition.cc +++ b/ortools/algorithms/dynamic_partition.cc @@ -198,7 +198,7 @@ std::string DynamicPartition::DebugString(DebugStringSorting sorting) const { std::string out; for (const std::vector& part : parts) { if (!out.empty()) out += " | "; - out += strings::Join(part, " "); + out += absl::StrJoin(part, " "); } return out; } @@ -287,7 +287,7 @@ std::string MergingPartition::DebugString() { std::string out; for (const std::vector& part : sorted_parts) { if (!out.empty()) out += " | "; - out += strings::Join(part, " "); + out += absl::StrJoin(part, " "); } return out; } diff --git a/ortools/algorithms/find_graph_symmetries.cc b/ortools/algorithms/find_graph_symmetries.cc index 5331f0ca56..503e6020e8 100644 --- a/ortools/algorithms/find_graph_symmetries.cc +++ b/ortools/algorithms/find_graph_symmetries.cc @@ -20,13 +20,15 @@ #include "ortools/base/commandlineflags.h" #include "ortools/base/stringprintf.h" #include "ortools/base/join.h" +#include "ortools/base/join.h" +#include "ortools/base/time_support.h" #include "ortools/graph/iterators.h" #include "ortools/graph/util.h" #include "ortools/algorithms/dense_doubly_linked_list.h" #include "ortools/algorithms/dynamic_partition.h" #include "ortools/algorithms/dynamic_permutation.h" #include "ortools/algorithms/sparse_permutation.h" -#include "ortools/graph/util.h" +#include "ortools/base/canonical_errors.h" DEFINE_bool(minimize_permutation_support_size, false, "Tweak the algorithm to try and minimize the support size" @@ -36,6 +38,8 @@ DEFINE_bool(minimize_permutation_support_size, false, namespace operations_research { +using util::GraphIsSymmetric; + namespace { // Some routines used below. @@ -341,8 +345,8 @@ void GetAllOtherRepresentativesInSamePartAs( std::sort(expected_output.begin(), expected_output.end()); std::vector sorted_output = *pruned_other_nodes; std::sort(sorted_output.begin(), sorted_output.end()); - DCHECK_EQ(strings::Join(expected_output, " "), - strings::Join(sorted_output, " ")); + DCHECK_EQ(absl::StrJoin(expected_output, " "), + absl::StrJoin(sorted_output, " ")); } } } // namespace @@ -482,7 +486,7 @@ util::Status GraphSymmetryFinder::FindSymmetries( while (!potential_root_image_nodes.empty()) { if (time_limit_->LimitReached()) break; VLOG(4) << "Potential (pruned) images of root node " << root_node - << " left: [" << strings::Join(potential_root_image_nodes, " ") + << " left: [" << absl::StrJoin(potential_root_image_nodes, " ") << "]."; const int root_image_node = potential_root_image_nodes.back(); VLOG(4) << "Trying image of root node: " << root_image_node; @@ -531,6 +535,7 @@ util::Status GraphSymmetryFinder::FindSymmetries( } node_equivalence_classes.FillEquivalenceClasses(node_equivalence_classes_io); IF_STATS_ENABLED(stats_.main_search_time.StopTimerAndAddElapsedTime()); + IF_STATS_ENABLED(stats_.SetPrintOrder(StatsGroup::SORT_BY_NAME)); IF_STATS_ENABLED(LOG(INFO) << "Statistics: " << stats_.StatString()); if (time_limit_->LimitReached()) { return util::Status(util::error::DEADLINE_EXCEEDED, @@ -851,7 +856,7 @@ void GraphSymmetryFinder::PruneOrbitsUnderPermutationsCompatibleWithPartition( const DynamicPartition& partition, const std::vector>& permutations, const std::vector& permutation_indices, std::vector* nodes) { - VLOG(4) << " Pruning [" << strings::Join(*nodes, ", ") << "]"; + VLOG(4) << " Pruning [" << absl::StrJoin(*nodes, ", ") << "]"; // TODO(user): apply a smarter test to decide whether to do the pruning // or not: we can accurately estimate the cost of pruning (iterate through // all generators found so far) and its estimated benefit (the cost of @@ -919,7 +924,7 @@ void GraphSymmetryFinder::PruneOrbitsUnderPermutationsCompatibleWithPartition( tmp_partition_.ResetNode(node); } tmp_nodes_on_support.clear(); - VLOG(4) << " Pruned: [" << strings::Join(*nodes, ", ") << "]"; + VLOG(4) << " Pruned: [" << absl::StrJoin(*nodes, ", ") << "]"; } bool GraphSymmetryFinder::ConfirmFullMatchOrFindNextMappingDecision( @@ -1011,7 +1016,7 @@ std::string GraphSymmetryFinder::SearchState::DebugString() const { " remaining_pruned_image_nodes=[%s]," " num_parts_before_trying_to_map_base_node=%d }", base_node, first_image_node, - strings::Join(remaining_pruned_image_nodes, " ").c_str(), + absl::StrJoin(remaining_pruned_image_nodes, " ").c_str(), num_parts_before_trying_to_map_base_node); } diff --git a/ortools/algorithms/find_graph_symmetries.h b/ortools/algorithms/find_graph_symmetries.h index 951f6d8f9f..0635c945bd 100644 --- a/ortools/algorithms/find_graph_symmetries.h +++ b/ortools/algorithms/find_graph_symmetries.h @@ -27,10 +27,10 @@ #include #include +#include "ortools/graph/graph.h" #include "ortools/graph/iterators.h" #include "ortools/algorithms/dynamic_partition.h" #include "ortools/algorithms/dynamic_permutation.h" -#include "ortools/graph/graph.h" #include "ortools/util/stats.h" #include "ortools/util/time_limit.h" #include "ortools/base/status.h" @@ -39,6 +39,7 @@ namespace operations_research { class SparsePermutation; + class GraphSymmetryFinder { public: typedef ::util::StaticGraph<> Graph; @@ -101,7 +102,7 @@ class GraphSymmetryFinder { // elements are valid factors of the automorphism group size. util::Status FindSymmetries( double time_limit_seconds, std::vector* node_equivalence_classes_io, - std::vector>* generators, + std::vector >* generators, std::vector* factorized_automorphism_group_size); // Fully refine the partition of nodes, using the graph as symmetry breaker. @@ -163,9 +164,9 @@ class GraphSymmetryFinder { std::unique_ptr FindOneSuitablePermutation( int root_node, int root_image_node, DynamicPartition* base_partition, DynamicPartition* image_partition, - const std::vector>& + const std::vector >& generators_found_so_far, - const std::vector>& permutations_displacing_node); + const std::vector >& permutations_displacing_node); // Data structure used by FindOneSuitablePermutation(). See the .cc struct SearchState { @@ -223,7 +224,7 @@ class GraphSymmetryFinder { // For each orbit, keep the first node that appears in "nodes". void PruneOrbitsUnderPermutationsCompatibleWithPartition( const DynamicPartition& partition, - const std::vector>& all_permutations, + const std::vector >& all_permutations, const std::vector& permutation_indices, std::vector* nodes); // Temporary objects used by some of the class methods, and owned by the @@ -233,7 +234,7 @@ class GraphSymmetryFinder { mutable std::vector tmp_node_mask_; // [0..N-1] = false std::vector tmp_degree_; // [0..N-1] = 0. std::vector tmp_stack_; // Empty. - std::vector> tmp_nodes_with_degree_; // [0..N-1] = []. + std::vector > tmp_nodes_with_degree_; // [0..N-1] = []. MergingPartition tmp_partition_; // Reset(N). std::vector tmp_compatible_permutations_; // Empty. diff --git a/ortools/algorithms/sparse_permutation.cc b/ortools/algorithms/sparse_permutation.cc index b7da04cda8..b85897c50c 100644 --- a/ortools/algorithms/sparse_permutation.cc +++ b/ortools/algorithms/sparse_permutation.cc @@ -16,6 +16,7 @@ #include #include "ortools/base/logging.h" #include "ortools/base/join.h" +#include "ortools/base/join.h" namespace operations_research { @@ -70,7 +71,7 @@ std::string SparsePermutation::DebugString() const { for (const std::vector& cycle : cycles) { if (!out.empty()) out += " "; out += "("; - out += strings::Join(cycle, " "); + out += absl::StrJoin(cycle, " "); out += ")"; } return out; diff --git a/ortools/base/canonical_errors.h b/ortools/base/canonical_errors.h new file mode 100644 index 0000000000..927dbb0e61 --- /dev/null +++ b/ortools/base/canonical_errors.h @@ -0,0 +1,17 @@ +// Copyright 2010-2017 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef OR_TOOLS_BASE_CANONICAL_ERRORS_H_ +#define OR_TOOLS_BASE_CANONICAL_ERRORS_H_ + +#endif // OR_TOOLS_BASE_CANONICAL_ERRORS_H_ diff --git a/ortools/base/casts.h b/ortools/base/casts.h index c953b7158a..016c3b9b5e 100644 --- a/ortools/base/casts.h +++ b/ortools/base/casts.h @@ -20,9 +20,6 @@ template inline Dest bit_cast(const Source& source) { - COMPILE_ASSERT(sizeof(Dest) == sizeof(Source), - bit_cast_on_object_with_different_sizes); - Dest dest; memcpy(&dest, &source, sizeof(dest)); return dest; diff --git a/ortools/base/file.cc b/ortools/base/file.cc index 626b4f0479..b4ab6163d0 100644 --- a/ortools/base/file.cc +++ b/ortools/base/file.cc @@ -29,7 +29,7 @@ #include "ortools/base/file.h" #include "ortools/base/logging.h" -File::File(FILE* const f_des, const operations_research::string_view& name) +File::File(FILE* const f_des, const absl::string_view& name) : f_(f_des), name_(name) {} bool File::Delete(const char* const name) { return remove(name) == 0; } @@ -55,9 +55,10 @@ bool File::Close() { util::Status File::Close(int flags) { if (flags != file::Defaults()) return false; - return Close() ? util::Status::OK - : util::Status(util::error::INVALID_ARGUMENT, - StrCat("Could not close file '", name_, "'")); + return Close() + ? util::Status::OK + : util::Status(util::error::INVALID_ARGUMENT, + absl::StrCat("Could not close file '", name_, "'")); } void File::ReadOrDie(void* const buf, size_t size) { @@ -129,16 +130,15 @@ bool File::WriteLine(const std::string& line) { return Write("\n", 1) == 1; } -operations_research::string_view File::filename() const { return name_; } +absl::string_view File::filename() const { return name_; } bool File::Open() const { return f_ != NULL; } void File::Init() {} namespace file { -util::Status Open(const operations_research::string_view& filename, - const operations_research::string_view& mode, File** f, - int flags) { +util::Status Open(const absl::string_view& filename, + const absl::string_view& mode, File** f, int flags) { if (flags == Defaults()) { *f = File::Open(filename, mode.data()); if (*f != nullptr) { @@ -146,20 +146,20 @@ util::Status Open(const operations_research::string_view& filename, } } return util::Status(util::error::INVALID_ARGUMENT, - StrCat("Could not open '", filename, "'")); + absl::StrCat("Could not open '", filename, "'")); } -File* OpenOrDie(const operations_research::string_view& filename, - const operations_research::string_view& mode, int flags) { +File* OpenOrDie(const absl::string_view& filename, + const absl::string_view& mode, int flags) { File* f; CHECK_EQ(flags, Defaults()); f = File::Open(filename, mode.data()); - CHECK(f != nullptr) << StrCat("Could not open '", filename, "'"); + CHECK(f != nullptr) << absl::StrCat("Could not open '", filename, "'"); return f; } -util::Status GetContents(const operations_research::string_view& filename, - std::string* output, int flags) { +util::Status GetContents(const absl::string_view& filename, std::string* output, + int flags) { if (flags == Defaults()) { File* file = File::Open(filename, "r"); if (file != NULL) { @@ -168,31 +168,31 @@ util::Status GetContents(const operations_research::string_view& filename, } } return util::Status(util::error::INVALID_ARGUMENT, - StrCat("Could not read '", filename, "'")); + absl::StrCat("Could not read '", filename, "'")); } -util::Status WriteString(File* file, const std::string& contents, int flags) { +util::Status WriteString(File* file, const absl::string_view& contents, + int flags) { if (flags == Defaults() && file != NULL && - file->Write(contents.c_str(), contents.size()) == contents.size() && + file->Write(contents.data(), contents.size()) == contents.size() && file->Close()) { return util::Status::OK; } - return util::Status(util::error::INVALID_ARGUMENT, - StrCat("Could not write ", contents.size(), " bytes")); + return util::Status( + util::error::INVALID_ARGUMENT, + absl::StrCat("Could not write ", contents.size(), " bytes")); } -util::Status SetContents(const operations_research::string_view& filename, - const std::string& contents, int flags) { +util::Status SetContents(const absl::string_view& filename, + const absl::string_view& contents, int flags) { return WriteString(File::Open(filename, "w"), contents, flags); } -bool ReadFileToString(const operations_research::string_view& file_name, - std::string* output) { +bool ReadFileToString(const absl::string_view& file_name, std::string* output) { return GetContents(file_name, output, file::Defaults()).ok(); } -bool WriteStringToFile(const std::string& data, - const operations_research::string_view& file_name) { +bool WriteStringToFile(const std::string& data, const absl::string_view& file_name) { return SetContents(file_name, data, file::Defaults()).ok(); } @@ -203,7 +203,7 @@ class NoOpErrorCollector : public google::protobuf::io::ErrorCollector { }; } // namespace -bool ReadFileToProto(const operations_research::string_view& file_name, +bool ReadFileToProto(const absl::string_view& file_name, google::protobuf::Message* proto) { std::string str; if (!ReadFileToString(file_name, &str)) { @@ -232,59 +232,68 @@ bool ReadFileToProto(const operations_research::string_view& file_name, return false; } -void ReadFileToProtoOrDie(const operations_research::string_view& file_name, +void ReadFileToProtoOrDie(const absl::string_view& file_name, google::protobuf::Message* proto) { CHECK(ReadFileToProto(file_name, proto)) << "file_name: " << file_name; } bool WriteProtoToASCIIFile(const google::protobuf::Message& proto, - const operations_research::string_view& file_name) { + const absl::string_view& file_name) { std::string proto_string; return google::protobuf::TextFormat::PrintToString(proto, &proto_string) && WriteStringToFile(proto_string, file_name); } -void WriteProtoToASCIIFileOrDie( - const google::protobuf::Message& proto, - const operations_research::string_view& file_name) { +void WriteProtoToASCIIFileOrDie(const google::protobuf::Message& proto, + const absl::string_view& file_name) { CHECK(WriteProtoToASCIIFile(proto, file_name)) << "file_name: " << file_name; } bool WriteProtoToFile(const google::protobuf::Message& proto, - const operations_research::string_view& file_name) { + const absl::string_view& file_name) { std::string proto_string; return proto.AppendToString(&proto_string) && WriteStringToFile(proto_string, file_name); } void WriteProtoToFileOrDie(const google::protobuf::Message& proto, - const operations_research::string_view& file_name) { + const absl::string_view& file_name) { CHECK(WriteProtoToFile(proto, file_name)) << "file_name: " << file_name; } -util::Status SetTextProto(const operations_research::string_view& filename, +util::Status SetTextProto(const absl::string_view& filename, const google::protobuf::Message& proto, int flags) { if (flags == Defaults()) { if (WriteProtoToASCIIFile(proto, filename)) return util::Status::OK; } - return util::Status(util::error::INVALID_ARGUMENT, - StrCat("Could not write proto to '", filename, "'.")); + return util::Status( + util::error::INVALID_ARGUMENT, + absl::StrCat("Could not write proto to '", filename, "'.")); } -util::Status SetBinaryProto(const operations_research::string_view& filename, +util::Status SetBinaryProto(const absl::string_view& filename, const google::protobuf::Message& proto, int flags) { if (flags == Defaults()) { if (WriteProtoToFile(proto, filename)) return util::Status::OK; } - return util::Status(util::error::INVALID_ARGUMENT, - StrCat("Could not write proto to '", filename, "'.")); + return util::Status( + util::error::INVALID_ARGUMENT, + absl::StrCat("Could not write proto to '", filename, "'.")); } -util::Status Delete(const operations_research::string_view& path, int flags) { +util::Status Delete(const absl::string_view& path, int flags) { if (flags == Defaults()) { if (remove(path.data())) return util::Status::OK; } return util::Status(util::error::INVALID_ARGUMENT, - StrCat("Could not delete '", path, "'.")); + absl::StrCat("Could not delete '", path, "'.")); +} + +util::Status Exists(const absl::string_view& path, int flags) { + if (flags == Defaults()) { + if (access(path.data(), F_OK) == 0) return util::Status::OK; + } + return util::Status(util::error::INVALID_ARGUMENT, + absl::StrCat("File '", path, "' does not exist.")); } } // namespace file diff --git a/ortools/base/file.h b/ortools/base/file.h index 6800098614..5b09af7ba5 100644 --- a/ortools/base/file.h +++ b/ortools/base/file.h @@ -36,7 +36,7 @@ class File { static File* Open(const char* const name, const char* const flag); #ifndef SWIG // no overloading - inline static File* Open(const operations_research::string_view& name, + inline static File* Open(const absl::string_view& name, const char* const mode) { return Open(name.data(), mode); } @@ -47,7 +47,7 @@ class File { static File* OpenOrDie(const char* const name, const char* const flag); #ifndef SWIG // no overloading - inline static File* OpenOrDie(const operations_research::string_view& name, + inline static File* OpenOrDie(const absl::string_view& name, const char* const flag) { return OpenOrDie(name.data(), flag); } @@ -95,11 +95,11 @@ class File { static void Init(); // Returns the file name. - operations_research::string_view filename() const; + absl::string_view filename() const; // Deletes a file. static bool Delete(const char* const name); - static bool Delete(const operations_research::string_view& name) { + static bool Delete(const absl::string_view& name) { return Delete(name.data()); } @@ -109,50 +109,48 @@ class File { bool Open() const; private: - File(FILE* const descriptor, const operations_research::string_view& name); + File(FILE* const descriptor, const absl::string_view& name); FILE* f_; - const operations_research::string_view name_; + const absl::string_view name_; }; namespace file { inline int Defaults() { return 0xBABA; } // As of 2016-01, these methods can only be used with flags = file::Defaults(). -util::Status Open(const operations_research::string_view& filename, - const operations_research::string_view& mode, File** f, - int flags); -File* OpenOrDie(const operations_research::string_view& filename, - const operations_research::string_view& mode, int flags); -util::Status SetTextProto(const operations_research::string_view& filename, +util::Status Open(const absl::string_view& filename, + const absl::string_view& mode, File** f, int flags); +File* OpenOrDie(const absl::string_view& filename, + const absl::string_view& mode, int flags); +util::Status SetTextProto(const absl::string_view& filename, const google::protobuf::Message& proto, int flags); -util::Status SetBinaryProto(const operations_research::string_view& filename, +util::Status SetBinaryProto(const absl::string_view& filename, const google::protobuf::Message& proto, int flags); -util::Status SetContents(const operations_research::string_view& filename, - const std::string& contents, int flags); -util::Status GetContents(const operations_research::string_view& filename, - std::string* output, int flags); -util::Status WriteString(File* file, const std::string& contents, int flags); +util::Status SetContents(const absl::string_view& filename, + const absl::string_view& contents, int flags); +util::Status GetContents(const absl::string_view& filename, std::string* output, + int flags); +util::Status WriteString(File* file, const absl::string_view& contents, + int flags); -bool ReadFileToString(const operations_research::string_view& file_name, - std::string* output); -bool WriteStringToFile(const std::string& data, - const operations_research::string_view& file_name); -bool ReadFileToProto(const operations_research::string_view& file_name, +bool ReadFileToString(const absl::string_view& file_name, std::string* output); +bool WriteStringToFile(const std::string& data, const absl::string_view& file_name); +bool ReadFileToProto(const absl::string_view& file_name, google::protobuf::Message* proto); -void ReadFileToProtoOrDie(const operations_research::string_view& file_name, +void ReadFileToProtoOrDie(const absl::string_view& file_name, google::protobuf::Message* proto); bool WriteProtoToASCIIFile(const google::protobuf::Message& proto, - const operations_research::string_view& file_name); -void WriteProtoToASCIIFileOrDie( - const google::protobuf::Message& proto, - const operations_research::string_view& file_name); + const absl::string_view& file_name); +void WriteProtoToASCIIFileOrDie(const google::protobuf::Message& proto, + const absl::string_view& file_name); bool WriteProtoToFile(const google::protobuf::Message& proto, - const operations_research::string_view& file_name); + const absl::string_view& file_name); void WriteProtoToFileOrDie(const google::protobuf::Message& proto, - const operations_research::string_view& file_name); + const absl::string_view& file_name); -util::Status Delete(const operations_research::string_view& path, int flags); +util::Status Delete(const absl::string_view& path, int flags); +util::Status Exists(const absl::string_view& path, int flags); } // namespace file diff --git a/ortools/base/filelineiter.h b/ortools/base/filelineiter.h new file mode 100644 index 0000000000..8d0a53a343 --- /dev/null +++ b/ortools/base/filelineiter.h @@ -0,0 +1,138 @@ +// Copyright 2010-2017 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Allows to read a text file line by line with: +// for (const std::string& line : FileLines("myfile.txt")) { ... } +// +// More details: +// * The lines are separated by '\n' (which is removed by default) and have no +// size limits. +// * Consecutive '\n' result in empty lines being produced. +// * If not empty, the std::string after the last '\n' is produced as the last line. +// * Options are available to keep the trailing '\n' for each line, to remove +// carriage-return chararters ('\r'), and to remove blank lines. + +#ifndef OR_TOOLS_BASE_FILELINEITER_H_ +#define OR_TOOLS_BASE_FILELINEITER_H_ + +#include "ortools/base/logging.h" +#include "ortools/base/file.h" +#include "ortools/base/stringpiece_utils.h" +#include "ortools/base/strutil.h" + +// Implements the minimum interface for a range-based for loop iterator. +class FileLineIterator { + public: + enum { + DEFAULT = 0x0000, + REMOVE_LINEFEED = DEFAULT, + KEEP_LINEFEED = 0x0001, // Terminating \n in result. + REMOVE_INLINE_CR = 0x0002, // Remove \r characters. + REMOVE_BLANK_LINES = 0x0004, // Remove empty or \n-only lines. + }; + + FileLineIterator(File* file, int options) + : next_position_after_eol_(0), + buffer_size_(0), + file_(file), + options_(options) { + ReadNextLine(); + } + const std::string& operator*() const { return line_; } + bool operator!=(const FileLineIterator& other) const { + return file_ != other.file_; + } + void operator++() { ReadNextLine(); } + + private: + bool HasOption(int option) const { return options_ & option; } + + void ReadNextLine() { + line_.clear(); + if (file_ == nullptr) return; + do { + while (true) { + int i = next_position_after_eol_; + for (; i < buffer_size_; ++i) { + if (buffer_[i] == '\n') break; + } + if (i == buffer_size_) { + line_.append(&buffer_[next_position_after_eol_], + i - next_position_after_eol_); + buffer_size_ = file_->Read(&buffer_, kBufferSize); + if (buffer_size_ < 0) { + LOG(WARNING) << "Error while reading file."; + file_ = nullptr; + break; + } + next_position_after_eol_ = 0; + if (buffer_size_ == 0) { + if (line_.empty()) { + file_ = nullptr; + } + break; + } + } else { + line_.append(&buffer_[next_position_after_eol_], + i - next_position_after_eol_ + 1); + next_position_after_eol_ = i + 1; + break; + } + } + PostProcessLine(); + } while (file_ != nullptr && HasOption(REMOVE_BLANK_LINES) && + (line_.empty() || line_ == "\n")); + } + + void PostProcessLine() { + if (HasOption(REMOVE_INLINE_CR)) { + line_.erase(std::remove(line_.begin(), line_.end(), '\r'), line_.end()); + } + const auto eol = std::find(line_.begin(), line_.end(), '\n'); + if (!HasOption(KEEP_LINEFEED) && eol != line_.end()) { + line_.erase(eol); + } + } + + static const int kBufferSize = 5 * 1024; + char buffer_[kBufferSize]; + int next_position_after_eol_; + int64 buffer_size_; + File* file_; + std::string line_; + const int options_; +}; + +class FileLines { + public: + FileLines(const std::string& filename, int options) : options_(options) { + if (!file::Open(filename, "r", &file_, file::Defaults()).ok()) return; + } + + explicit FileLines(const std::string& filename) + : FileLines(filename, FileLineIterator::DEFAULT) {} + + ~FileLines() { + if (file_ != nullptr) file_->Close(file::Defaults()).IgnoreError(); + } + + FileLineIterator begin() { return FileLineIterator(file_, options_); } + + FileLineIterator end() const { return FileLineIterator(nullptr, options_); } + + private: + File* file_; + const int options_; +}; + +#endif // OR_TOOLS_BASE_FILELINEITER_H_ diff --git a/ortools/base/filelinereader.cc b/ortools/base/filelinereader.cc deleted file mode 100644 index e68dc529b6..0000000000 --- a/ortools/base/filelinereader.cc +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2010-2017 Google -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ortools/base/filelinereader.h" - -#include -#include -#include - -#include "ortools/base/file.h" -#include "ortools/base/logging.h" - -namespace operations_research { -FileLineReader::FileLineReader(const char* const filename) - : filename_(filename), - line_callback_(nullptr), - loaded_successfully_(false) {} - -FileLineReader::~FileLineReader() {} - -void FileLineReader::set_line_callback(Callback1* const callback) { - line_callback_.reset(callback); -} - -void FileLineReader::Reload() { - const int kMaxLineLength = 60 * 1024; - File* const data_file = File::Open(filename_, "r"); - if (data_file == NULL) { - loaded_successfully_ = false; - return; - } - - std::unique_ptr line(new char[kMaxLineLength]); - for (;;) { - char* const result = data_file->ReadLine(line.get(), kMaxLineLength); - if (result == NULL) { - data_file->Close(); - loaded_successfully_ = true; - return; - } - // Chop the last linefeed if present. - int len = strlen(result); - if (len > 0 && result[len - 1] == '\n') { // Linefeed. - result[--len] = '\0'; - } - if (len > 0 && result[len - 1] == '\r') { // Carriage return. - result[--len] = '\0'; - } - if (line_callback_.get() != NULL) { - line_callback_->Run(result); - } - } - data_file->Close(); -} - -bool FileLineReader::loaded_successfully() const { - return loaded_successfully_; -} -} // namespace operations_research diff --git a/ortools/base/filelinereader.h b/ortools/base/filelinereader.h deleted file mode 100644 index f79aaae0f3..0000000000 --- a/ortools/base/filelinereader.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2010-2017 Google -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_FILELINEREADER_H_ -#define OR_TOOLS_BASE_FILELINEREADER_H_ - -#include -#include -#include -#include - -#include "ortools/base/callback.h" -#include "ortools/base/integral_types.h" -#include "ortools/base/file.h" - -namespace operations_research { -// The FileLineReader class will read a text file specified by -// 'filename' line by line. Each line will be cleaned with respect to -// termination ('\n' and '\r'). The line callback will be called in -// sequence on each line. -class FileLineReader { - public: - // Creates a file line reader object that will read the file 'filename' - // line by line. - explicit FileLineReader(const char* const filename); - - ~FileLineReader(); - - // Sets the line callback and takes ownership. - void set_line_callback(Callback1* const callback); - // Reloads the file line by line. - void Reload(); - // Indicates if the file was loaded successfully. - bool loaded_successfully() const; - - private: - const char* filename_; - std::unique_ptr > line_callback_; - bool loaded_successfully_; -}; -} // namespace operations_research -#endif // OR_TOOLS_BASE_FILELINEREADER_H_ diff --git a/ortools/base/inlined_vector.h b/ortools/base/inlined_vector.h index 09d266c884..a805fe0f53 100644 --- a/ortools/base/inlined_vector.h +++ b/ortools/base/inlined_vector.h @@ -38,7 +38,7 @@ #include "ortools/base/logging.h" -namespace gtl { +namespace absl { template class InlinedVector { @@ -665,6 +665,6 @@ inline void InlinedVector::AppendRange(Iter first, Iter last) { AppendRange(first, last, IterTag()); } -} // namespace gtl +} // namespace absl #endif // OR_TOOLS_BASE_INLINED_VECTOR_H_ diff --git a/ortools/base/join.h b/ortools/base/join.h index d705d28a11..15686bd35c 100644 --- a/ortools/base/join.h +++ b/ortools/base/join.h @@ -37,7 +37,7 @@ char* NumToBuffer(T i, char* buffer) { } struct AlphaNum { - operations_research::string_view piece; + absl::string_view piece; char digits[kFastToBufferSize]; // No bool ctor -- bools convert to an integral type. @@ -62,13 +62,11 @@ struct AlphaNum { piece.set(digits); } AlphaNum(const char* c_str) : piece(c_str) {} // NOLINT(runtime/explicit) - AlphaNum(const operations_research::string_view& pc) + AlphaNum(const absl::string_view& pc) : piece(pc) {} // NOLINT(runtime/explicit) AlphaNum(const std::string& s) : piece(s) {} // NOLINT(runtime/explicit) - operations_research::string_view::size_type size() const { - return piece.size(); - } + absl::string_view::size_type size() const { return piece.size(); } const char* data() const { return piece.data(); } private: @@ -78,11 +76,6 @@ struct AlphaNum { extern AlphaNum gEmptyAlphaNum; -template -const T& LegacyPrecision(const T& t) { - return t; -} - std::string StrCat(const AlphaNum& a); std::string StrCat(const AlphaNum& a, const AlphaNum& b); std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c); @@ -169,12 +162,143 @@ std::string Join(const Iterable& elements, const std::string& separator) { } return out; } - } // namespace strings +namespace absl { + +template +const T& LegacyPrecision(const T& t) { + return t; +} + +inline std::string StrCat(const AlphaNum& a) { return ::StrCat(a); } + +inline std::string StrCat(const AlphaNum& a, const AlphaNum& b) { return ::StrCat(a, b); } +inline std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) { + return ::StrCat(a, b, c); +} +inline std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, + const AlphaNum& d) { + return ::StrCat(a, b, c, d); +} +inline std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, + const AlphaNum& d, const AlphaNum& e) { + return ::StrCat(a, b, c, d, e); +} +inline std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, + const AlphaNum& d, const AlphaNum& e, const AlphaNum& f) { + return ::StrCat(a, b, c, d, e, f); +} +inline std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, + const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, + const AlphaNum& g) { + return ::StrCat(a, b, c, d, e, f, g); +} +inline std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, + const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, + const AlphaNum& g, const AlphaNum& h) { + return ::StrCat(a, b, c, d, e, f, g, h); +} +inline std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, + const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, + const AlphaNum& g, const AlphaNum& h, const AlphaNum& i) { + return ::StrCat(a, b, c, d, e, f, g, h, i); +} +inline std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, + const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, + const AlphaNum& g, const AlphaNum& h, const AlphaNum& i, + const AlphaNum& j) { + return ::StrCat(a, b, c, d, e, f, g, h, i, j); +} +inline std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, + const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, + const AlphaNum& g, const AlphaNum& h, const AlphaNum& i, + const AlphaNum& j, const AlphaNum& k) { + return ::StrCat(a, b, c, d, e, f, g, h, i, j, k); +} +inline std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, + const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, + const AlphaNum& g, const AlphaNum& h, const AlphaNum& i, + const AlphaNum& j, const AlphaNum& k, const AlphaNum& l) { + return ::StrCat(a, b, c, d, e, f, g, h, i, j, k, l); +} +inline std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, + const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, + const AlphaNum& g, const AlphaNum& h, const AlphaNum& i, + const AlphaNum& j, const AlphaNum& k, const AlphaNum& l, + const AlphaNum& m) { + return ::StrCat(a, b, c, d, e, f, g, h, i, j, k, l, m); +} + +inline void StrAppend(std::string* s, const AlphaNum& a) { ::StrAppend(s, a); } +inline void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b) { + ::StrAppend(s, a, b); +} +inline void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, + const AlphaNum& c) { + ::StrAppend(s, a, b, c); +} +inline void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, + const AlphaNum& c, const AlphaNum& d) { + ::StrAppend(s, a, b, c, d); +} +inline void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, + const AlphaNum& c, const AlphaNum& d, const AlphaNum& e) { + ::StrAppend(s, a, b, c, d, e); +} +inline void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, + const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, + const AlphaNum& f) { + ::StrAppend(s, a, b, c, d, e, f); +} +inline void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, + const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, + const AlphaNum& f, const AlphaNum& g) { + ::StrAppend(s, a, b, c, d, e, f, g); +} +inline void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, + const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, + const AlphaNum& f, const AlphaNum& g, const AlphaNum& h) { + ::StrAppend(s, a, b, c, d, e, f, g, h); +} +inline void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, + const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, + const AlphaNum& f, const AlphaNum& g, const AlphaNum& h, + const AlphaNum& i) { + ::StrAppend(s, a, b, c, d, e, f, g, h, i); +} +inline void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, + const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, + const AlphaNum& f, const AlphaNum& g, const AlphaNum& h, + const AlphaNum& i, const AlphaNum& j) { + ::StrAppend(s, a, b, c, d, e, f, g, h, i, j); +} +inline void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, + const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, + const AlphaNum& f, const AlphaNum& g, const AlphaNum& h, + const AlphaNum& i, const AlphaNum& j, const AlphaNum& k) { + ::StrAppend(s, a, b, c, d, e, f, g, h, i, j, k); +} +inline void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, + const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, + const AlphaNum& f, const AlphaNum& g, const AlphaNum& h, + const AlphaNum& i, const AlphaNum& j, const AlphaNum& k, + const AlphaNum& l) { + ::StrAppend(s, a, b, c, d, e, f, g, h, i, j, k, l); +} +inline void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, + const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, + const AlphaNum& f, const AlphaNum& g, const AlphaNum& h, + const AlphaNum& i, const AlphaNum& j, const AlphaNum& k, + const AlphaNum& l, const AlphaNum& m) { + ::StrAppend(s, a, b, c, d, e, f, g, h, i, j, k, l, m); +} + template std::string StrJoin(const Iterable& elements, const std::string& separator) { - return strings::Join(elements, separator); + return strings::Join(elements, separator); } +} // namespace absl + #endif // OR_TOOLS_BASE_JOIN_H_ diff --git a/ortools/base/numbers.cc b/ortools/base/numbers.cc index 6789fde890..e3b75de08f 100644 --- a/ortools/base/numbers.cc +++ b/ortools/base/numbers.cc @@ -16,8 +16,6 @@ #include #include -namespace operations_research { - #ifdef _MSC_VER #define strtof strtod #define strtoll _strtoi64 @@ -65,5 +63,3 @@ bool safe_strto64(const std::string& str, int64* value) { #undef strtof #undef strtoll - -} // namespace operations_research diff --git a/ortools/base/numbers.h b/ortools/base/numbers.h index 2d5f279447..4f5f588af6 100644 --- a/ortools/base/numbers.h +++ b/ortools/base/numbers.h @@ -18,7 +18,6 @@ #include "ortools/base/integral_types.h" #include "ortools/base/join.h" -namespace operations_research { // Convert strings to numerical values. // Leading and trailing spaces are allowed. // Values may be rounded on over- and underflow. @@ -29,6 +28,5 @@ bool safe_strtod(const std::string& str, double* value); bool safe_strto64(const std::string& str, int64* value); // Converting int to std::string. inline std::string SimpleItoa(int i) { return StrCat(i); } -} // namespace operations_research #endif // OR_TOOLS_BASE_NUMBERS_H_ diff --git a/ortools/base/ptr_util.h b/ortools/base/ptr_util.h new file mode 100644 index 0000000000..d09119ac39 --- /dev/null +++ b/ortools/base/ptr_util.h @@ -0,0 +1,19 @@ +// Copyright 2010-2017 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef OR_TOOLS_BASE_PTR_UTIL_H_ +#define OR_TOOLS_BASE_PTR_UTIL_H_ + +#include + +#endif // OR_TOOLS_BASE_PTR_UTIL_H_ diff --git a/ortools/base/span.h b/ortools/base/span.h index 523d008f55..72686784b1 100644 --- a/ortools/base/span.h +++ b/ortools/base/span.h @@ -26,13 +26,13 @@ // array elements of type T. // // Implicit conversion operations are provided from types such as -// std::vector and util::gtl::InlinedVector. Note that Span +// std::vector and absl::InlinedVector. Note that Span // objects constructed from types in this way may be invalidated by // any operations that mutate the underlying vector. // // One common use for Span is when passing arguments to a // routine where you want to be able to accept a variety of array -// types (e.g. a vector, a util::gtl::InlinedVector, a C-style array, +// types (e.g. a vector, a absl::InlinedVector, a C-style array, // etc.). The usual approach here is to have the client explicitly // pass in a pointer and a length, as in: // @@ -45,7 +45,7 @@ // std::vector my_vector; // MyRoutine(vector_as_array(&my_vector), my_vector.size()); // -// util::gtl::InlinedVector my_inline_vector; +// absl::InlinedVector my_inline_vector; // MyRoutine(my_inline_vector.array(), my_inline_vector.size()); // // int my_array[10]; @@ -62,36 +62,14 @@ // std::vector my_vector; // MyRoutine(my_vector); // -// util::gtl::InlinedVector my_inline_vector; +// absl::InlinedVector my_inline_vector; // MyRoutine(my_inline_vector); // // int my_array[10]; // MyRoutine(my_array); // // int* my_array = new int[10]; -// MyRoutine(gtl::Span(my_array, 10)); -// -// MutableSpan represents a mutable array of elements, and, like -// Span, does not own the backing store. The implicit constructors it -// provides allow functions not to worry about whether their mutable arguments -// refer to vectors, arrays, google::protobuf::RepeatedFields, etc.: -// -// void MyMutatingRoutine(MutableSpan a) { -// for (int i = 0; i < a.size(); i++) { .. mutate a[i] .. } -// } -// -// std::vector my_vector; -// MyMutatingRoutine(&my_vector); -// -// int my_array[10]; -// MyMutatingRoutine(my_array); -// -// int* my_array = new int[10]; -// MyMutatingRoutine(gtl::MutableSpan(my_array, 10)); -// -// MyProto my_proto; -// for (int i = 0; i < 10; ++i) { my_proto.add_value(i); } -// MyMutatingRoutine(my_proto.mutable_value()); +// MyRoutine(absl::Span(my_array, 10)); #include #include @@ -99,7 +77,7 @@ #include "ortools/base/inlined_vector.h" -namespace gtl { +namespace absl { namespace internal { // Template logic for generic constructors. @@ -114,13 +92,6 @@ struct Data { } }; -struct MutableData { - template - static decltype(std::declval().mutable_data()) Get(C* v) { - return v->mutable_data(); - } -}; - struct Size { template static decltype(std::declval().size()) Get(C* v) { @@ -128,11 +99,6 @@ struct Size { } }; -struct MutableStringData { - // Defined only for std::string. - static char* Get(std::string* v) { return v->empty() ? nullptr : &*v->begin(); } -}; - // Checks whether M::Get(C*) is defined and has a return type R such that // Checker::valid()==true. template @@ -196,23 +162,11 @@ using FirstWithGet = typename std::conditional::type; template using ContainerData = Wrapper, const C>; -// Wraps a method returning a pointer to mutable data. Prefers data() over -// mutable_data(), and handles strings when T==char. If data() returns a pointer -// to mutable data, it is most likely overloaded, but may also be a single -// method 'T* C::data() const' in a non-STL-compliant container. -template -using ContainerMutableData = - FirstWithGet, C>, - FirstWithGet, C>, - Wrapper, C>>>; - // Wraps C::size() const. template using ContainerSize = Wrapper; -// Implementation class for Span and MutableSpan. In the case of -// Span, T will be a const type; for MutableSpan, T will be a -// mutable type. +// Implementation class for Span. T will be a const type. template class SpanImplBase { public: @@ -239,8 +193,8 @@ class SpanImplBase { : ptr_(x.ptr_ + pos), length_(std::min(x.length_ - pos, len)) {} // Some of the const methods below return pointers and references to mutable - // data. This is only the case in this internal class; Span and - // MutableSpan provide deep-constness. + // data. This is only the case in this internal class; Span provides + // deep-constness. pointer data() const { return ptr_; } size_type size() const { return length_; } @@ -310,22 +264,6 @@ class SpanImpl : public SpanImplBase { : SpanImplBase(ContainerData::Get(std::addressof(v)), ContainerSize::Get(std::addressof(v))) {} }; - -template -class MutableSpanImpl : public SpanImplBase { - public: - using SpanImplBase::SpanImplBase; - - template - using EnableIfConvertibleFrom = - typename std::enable_if::HasGet() && - ContainerSize::HasGet()>::type; - - template - explicit MutableSpanImpl(C* v) - : SpanImplBase(ContainerMutableData::Get(v), - ContainerSize::Get(v)) {} -}; } // namespace internal template @@ -385,6 +323,8 @@ class Span { Span(std::initializer_list v) // NOLINT(runtime/explicit) : impl_(v.begin(), v.size()) {} + Span(const Span& x) : impl_(x.impl_) {} + // Substring of another Span. // pos must be non-negative and <= x.length(). // len must be non-negative and will be pinned to at most x.length() - pos. @@ -425,108 +365,9 @@ class Span { Impl impl_; }; -// Mutable version of Span, which allows the clients to mutate the -// underlying data. It is implicitly convertible to Span since it provides -// the data() and size() methods with correct signatures. When a -// MutableSpan is created from a pointer to a container (as opposed to raw -// memory pointer), the pointer must not be null. -// -// A note on const-ness: "mutable" here refers to the mutability of the -// underlying data, not of the slice itself. It is perfectly reasonable to have -// a variable of type "const MutableSpan"; this means that the bounds -// of the view on the array cannot be changed, but the underlying data in the -// array still may be modified. This is akin to a "T* const" pointer, as opposed -// to a "const T*" pointer (corresponding to a non-const Span). -template -class MutableSpan { - private: - typedef internal::MutableSpanImpl Impl; - - public: - typedef T value_type; - typedef typename Impl::pointer pointer; - typedef typename Impl::const_pointer const_pointer; - typedef typename Impl::reference reference; - typedef typename Impl::const_reference const_reference; - typedef typename Impl::iterator iterator; - typedef typename Impl::const_iterator const_iterator; - typedef typename Impl::reverse_iterator reverse_iterator; - typedef typename Impl::const_reverse_iterator const_reverse_iterator; - typedef typename Impl::size_type size_type; - typedef typename Impl::difference_type difference_type; - - static const size_type npos = Impl::npos; - - MutableSpan() : impl_(nullptr, 0) {} - MutableSpan(pointer array, size_type length) : impl_(array, length) {} - - // Implicit conversion constructors - MutableSpan(std::vector* v) // NOLINT(runtime/explicit) - : impl_(v->data(), v->size()) {} - - template - MutableSpan(value_type (&a)[N]) // NOLINT(runtime/explicit) - : impl_(a, N) {} - - template - MutableSpan(InlinedVector* v) // NOLINT(runtime/explicit) - : impl_(v->data(), v->size()) {} - - // The constructor for any class supplying 'T* data()' or 'T* mutable_data()' - // (the former is called if both exist), and 'some_integral_type size() - // const'. google::protobuf::RepeatedField is an example of this. Also supports std::string - // arguments, when T==char. The appropriate ctor is selected using SFINAE. See - // span_internal.h for details. - template > - MutableSpan(V* v) // NOLINT(runtime/explicit) - : impl_(v) {} - - // Substring of another MutableSpan. - // pos must be non-negative and <= x.length(). - // len must be non-negative and will be pinned to at most x.length() - pos. - // If len==npos, the substring continues till the end of x. - MutableSpan(const MutableSpan& x, size_type pos, size_type len) - : impl_(x.impl_, pos, len) {} - - // Accessors. - pointer data() const { return impl_.data(); } - size_type size() const { return impl_.size(); } - size_type length() const { return size(); } - bool empty() const { return size() == 0; } - - void clear() { impl_.clear(); } - - reference operator[](size_type i) const { return impl_[i]; } - reference at(size_type i) const { return impl_.at(i); } - reference front() const { return impl_.front(); } - reference back() const { return impl_.back(); } - - iterator begin() const { return impl_.begin(); } - iterator end() const { return impl_.end(); } - reverse_iterator rbegin() const { return impl_.rbegin(); } - reverse_iterator rend() const { return impl_.rend(); } - - void remove_prefix(size_type n) { impl_.remove_prefix(n); } - void remove_suffix(size_type n) { impl_.remove_suffix(n); } - void pop_back() { remove_suffix(1); } - void pop_front() { remove_prefix(1); } - - bool operator==(Span other) const { return Span(*this) == other; } - bool operator!=(Span other) const { return Span(*this) != other; } - - // DEPRECATED(jacobsa): Please use data() instead. - pointer mutable_data() const { return impl_.data(); } - - private: - Impl impl_; -}; - template const typename Span::size_type Span::npos; -template -const typename MutableSpan::size_type MutableSpan::npos; -} // namespace gtl +} // namespace absl #endif // OR_TOOLS_BASE_SPAN_H_ diff --git a/ortools/base/split.cc b/ortools/base/split.cc index d3e824d86b..925608fee2 100644 --- a/ortools/base/split.cc +++ b/ortools/base/split.cc @@ -74,24 +74,23 @@ static inline void InternalSplitStringUsing(const std::string& full, } // namespace std::vector Split(const std::string& full, char delim, int flags) { - CHECK_EQ(SkipEmpty(), flags); + CHECK_EQ(absl::SkipEmpty(), flags); std::vector out; InternalSplitStringUsingChar(full, delim, &out); return out; } std::vector Split(const std::string& full, const char* delim, int flags) { - CHECK_EQ(SkipEmpty(), flags); + CHECK_EQ(absl::SkipEmpty(), flags); std::vector out; InternalSplitStringUsing(full, delim, &out); return out; } -std::vector Split(const std::string& full, - const char* delim, - int64 flags) { - CHECK_EQ(SkipEmpty(), flags); - std::vector out; +std::vector Split(const std::string& full, const char* delim, + int64 flags) { + CHECK_EQ(absl::SkipEmpty(), flags); + std::vector out; InternalSplitStringUsing(full, delim, &out); return out; } diff --git a/ortools/base/split.h b/ortools/base/split.h index c6dbedcaff..af80197376 100644 --- a/ortools/base/split.h +++ b/ortools/base/split.h @@ -34,17 +34,28 @@ std::vector Split(const std::string& full, char delim, int flags); // // Hack: the int64 allow the C++ compiler to distinguish the two functions. It // is possible to implement this more cleanly at the cost of more complexity. -std::vector Split(const std::string& full, - const char* delim, - int64 flags); +std::vector Split(const std::string& full, const char* delim, + int64 flags); namespace delimiter { inline const char* AnyOf(const char* x) { return x; } } // namespace delimiter +} // namespace strings +namespace absl { inline int SkipEmpty() { return 0xDEADBEEF; } -} // namespace strings +inline std::vector StrSplit(const std::string& full, + const char* delim, int flags) { + return strings::Split(full, delim, flags); +} + +inline std::vector StrSplit(const std::string& full, + char delim, int flags) { + return strings::Split(full, delim, flags); +} + +} // namespace absl // Split a std::string using a nul-terminated list of character // delimiters. For each component, parse using the provided @@ -54,8 +65,7 @@ inline int SkipEmpty() { return 0xDEADBEEF; } // all of them. This function will correctly handle parsing // strings that have embedded \0s. template -bool SplitStringAndParse(operations_research::string_view source, - const std::string& delim, +bool SplitStringAndParse(absl::string_view source, const std::string& delim, bool (*parse)(const std::string& str, T* value), std::vector* result); @@ -63,7 +73,7 @@ bool SplitStringAndParse(operations_research::string_view source, // function. As of 2013-04, it can only be used like this: // const char* separators = ...; // std::vector result = strings::Split( -// full, strings::delimiter::AnyOf(separators), strings::SkipEmpty()); +// full, strings::delimiter::AnyOf(separators), absl::SkipEmpty()); // // TODO(user): The current interface has a really bug prone side effect because // it can also be used without the AnyOf(). If separators contains only one @@ -78,11 +88,11 @@ bool SplitStringAndParse(const std::string& source, const std::string& delim, CHECK(nullptr != parse); CHECK(nullptr != result); CHECK_GT(delim.size(), 0); - const std::vector pieces = + const std::vector pieces = ::strings::Split(source, strings::delimiter::AnyOf(delim.c_str()), - static_cast(strings::SkipEmpty())); + static_cast(absl::SkipEmpty())); T t; - for (operations_research::string_view piece : pieces) { + for (absl::string_view piece : pieces) { if (!parse(piece.as_string(), &t)) return false; result->push_back(t); } diff --git a/ortools/base/string_view.cc b/ortools/base/string_view.cc index 9c96333c4b..51746ae0d3 100644 --- a/ortools/base/string_view.cc +++ b/ortools/base/string_view.cc @@ -15,7 +15,7 @@ #include // NOLINT #include -namespace operations_research { +namespace absl { std::ostream& operator<<(std::ostream& o, const string_view& piece) { o.write(piece.data(), piece.size()); @@ -109,4 +109,4 @@ string_view string_view::substr(size_type pos, size_type n) const { const string_view::size_type string_view::npos = size_type(-1); -} // namespace operations_research +} // namespace absl diff --git a/ortools/base/string_view.h b/ortools/base/string_view.h index 1c3b52ec43..3b32ec341d 100644 --- a/ortools/base/string_view.h +++ b/ortools/base/string_view.h @@ -32,7 +32,7 @@ #include #include -namespace operations_research { +namespace absl { class string_view { private: @@ -167,6 +167,6 @@ inline bool operator>=(const string_view& x, const string_view& y) { // Allow string_view to be logged. extern std::ostream& operator<<(std::ostream& o, const string_view& piece); -} // namespace operations_research +} // namespace absl #endif // OR_TOOLS_BASE_STRING_VIEW_H_ diff --git a/ortools/base/stringpiece_utils.h b/ortools/base/stringpiece_utils.h index decc59ee03..16f0ad808d 100644 --- a/ortools/base/stringpiece_utils.h +++ b/ortools/base/stringpiece_utils.h @@ -20,14 +20,12 @@ namespace strings { // Returns whether s begins with x. -inline bool StartsWith(operations_research::string_view s, - operations_research::string_view x) { +inline bool StartsWith(absl::string_view s, absl::string_view x) { return s.size() >= x.size() && memcmp(s.data(), x.data(), x.size()) == 0; } // Returns whether s ends with x. -inline bool EndsWith(operations_research::string_view s, - operations_research::string_view x) { +inline bool EndsWith(absl::string_view s, absl::string_view x) { return s.size() >= x.size() && memcmp(s.data() + (s.size() - x.size()), x.data(), x.size()) == 0; } diff --git a/ortools/base/strutil.h b/ortools/base/strutil.h index e9aba75aa2..ca4e4a4ac4 100644 --- a/ortools/base/strutil.h +++ b/ortools/base/strutil.h @@ -21,11 +21,13 @@ using std::string; namespace operations_research { -inline bool HasSuffixString(const string_view& str, const string_view& suffix) { +inline bool HasSuffixString(const absl::string_view& str, + const absl::string_view& suffix) { return str.ends_with(suffix); } -inline bool HasPrefixString(const string_view& str, const string_view& prefix) { +inline bool HasPrefixString(const absl::string_view& str, + const absl::string_view& prefix) { return str.starts_with(prefix); } diff --git a/ortools/base/time_support.cc b/ortools/base/time_support.cc index 5388c53452..b1ab0ea27b 100644 --- a/ortools/base/time_support.cc +++ b/ortools/base/time_support.cc @@ -23,7 +23,7 @@ #endif #include -namespace base { +namespace absl { int64 GetCurrentTimeNanos() { #if defined(_MSC_VER) @@ -49,4 +49,4 @@ int64 GetCurrentTimeNanos() { #endif } -} // namespace base +} // namespace absl diff --git a/ortools/base/time_support.h b/ortools/base/time_support.h index a1d8918ab9..d26ee68b43 100644 --- a/ortools/base/time_support.h +++ b/ortools/base/time_support.h @@ -16,7 +16,7 @@ #include "ortools/base/integral_types.h" -namespace base { +namespace absl { // Ideally, this should be a super-fast implementation that isn't too // unreliable: @@ -36,9 +36,9 @@ int64 GetCurrentTimeNanos(); inline int64 Now() { return GetCurrentTimeNanos(); } -inline double WallTime_Now() { return base::GetCurrentTimeNanos() * 1e-9; } +inline double WallTime_Now() { return GetCurrentTimeNanos() * 1e-9; } -} // namespace base +} // namespace absl inline double ToWallTime(int64 nanos) { return 1e-9 * nanos; } diff --git a/ortools/base/timer.h b/ortools/base/timer.h index 4f7302a1a4..f0d53dd569 100644 --- a/ortools/base/timer.h +++ b/ortools/base/timer.h @@ -29,7 +29,7 @@ class WallTimer { // When Start() is called multiple times, only the most recent is used. void Start() { running_ = true; - start_ = base::GetCurrentTimeNanos(); + start_ = absl::GetCurrentTimeNanos(); } void Restart() { sum_ = 0; @@ -37,7 +37,7 @@ class WallTimer { } void Stop() { if (running_) { - sum_ += base::GetCurrentTimeNanos() - start_; + sum_ += absl::GetCurrentTimeNanos() - start_; running_ = false; } } @@ -47,7 +47,7 @@ class WallTimer { protected: int64 GetNanos() const { - return running_ ? base::GetCurrentTimeNanos() - start_ + sum_ : sum_; + return running_ ? absl::GetCurrentTimeNanos() - start_ + sum_ : sum_; } private: @@ -62,7 +62,7 @@ typedef WallTimer UserTimer; // This is meant to be a ultra-fast interface to the hardware cycle counter, // without periodic recalibration, to be even faster than -// base::GetCurrentTimeNanos(). +// absl::GetCurrentTimeNanos(). // But this current implementation just uses GetCurrentTimeNanos(). // TODO(user): implement it. class CycleTimer : public WallTimer { diff --git a/ortools/bop/bop_lns.h b/ortools/bop/bop_lns.h index f78b83a423..3080781af7 100644 --- a/ortools/bop/bop_lns.h +++ b/ortools/bop/bop_lns.h @@ -64,7 +64,7 @@ class BopCompleteLNSOptimizer : public BopOptimizerBase { // // NOTE(user): Using a sat_propagator as the output of the algorithm allows for // a really simple and efficient interface for the generator that relies on it. -// However, if a generator don't rely on it at all, it may slow down a bit the +// However, if a generator doesn't rely on it at all, it may slow down a bit the // code (to investigate). If this happens, we will probably need another // function here and a way to select between which one to call. class NeighborhoodGenerator { diff --git a/ortools/bop/complete_optimizer.cc b/ortools/bop/complete_optimizer.cc index 4b886f83d1..b63409542d 100644 --- a/ortools/bop/complete_optimizer.cc +++ b/ortools/bop/complete_optimizer.cc @@ -70,13 +70,6 @@ sat::SatSolver::Status SatCoreBasedOptimizer::SolveWithAssumptions() { sat::ReduceNodesAndExtractAssumptions(upper_bound_, stratified_lower_bound_, &lower_bound_, &nodes_, &solver_); - - // The lower bound is proved to equal the upper bound, the upper bound - // corresponding to the current solution value from the problem_state. As the - // optimizer is looking for a better solution (see - // LoadStateProblemToSatSolver), that means the current model is UNSAT and so - // the synchronized solution is optimal. - if (assumptions.empty()) return sat::SatSolver::MODEL_UNSAT; return solver_.ResetAndSolveWithGivenAssumptions(assumptions); } diff --git a/ortools/bop/complete_optimizer.h b/ortools/bop/complete_optimizer.h index ebbd1813c7..be7707b4c7 100644 --- a/ortools/bop/complete_optimizer.h +++ b/ortools/bop/complete_optimizer.h @@ -70,4 +70,5 @@ class SatCoreBasedOptimizer : public BopOptimizerBase { } // namespace bop } // namespace operations_research + #endif // OR_TOOLS_BOP_COMPLETE_OPTIMIZER_H_ diff --git a/ortools/bop/integral_solver.cc b/ortools/bop/integral_solver.cc index e0dfd41a2d..3cd84745c6 100644 --- a/ortools/bop/integral_solver.cc +++ b/ortools/bop/integral_solver.cc @@ -1072,6 +1072,8 @@ BopSolveStatus IntegralSolver::SolveWithTimeLimit( LPDecomposer decomposer; decomposer.Decompose(lp); const int num_sub_problems = decomposer.GetNumberOfProblems(); + LOG(INFO) << "Problem is decomposable into " << num_sub_problems + << " components!"; if (num_sub_problems > 1) { // The problem can be decomposed. Solve each sub-problem and aggregate the // result. diff --git a/ortools/constraint_solver/assignment.cc b/ortools/constraint_solver/assignment.cc index c2b2523c68..7d14a84918 100644 --- a/ortools/constraint_solver/assignment.cc +++ b/ortools/constraint_solver/assignment.cc @@ -23,6 +23,7 @@ #include "ortools/base/file.h" #include "ortools/base/recordio.h" #include "ortools/base/join.h" +#include "ortools/base/join.h" #include "ortools/base/map_util.h" #include "ortools/base/hash.h" #include "ortools/constraint_solver/assignment.pb.h" @@ -319,9 +320,9 @@ void SequenceVarElement::WriteToProto( std::string SequenceVarElement::DebugString() const { if (Activated()) { return StringPrintf("[forward %s, backward %s, unperformed [%s]]", - strings::Join(forward_sequence_, " -> ").c_str(), - strings::Join(backward_sequence_, " -> ").c_str(), - strings::Join(unperformed_, ", ").c_str()); + absl::StrJoin(forward_sequence_, " -> ").c_str(), + absl::StrJoin(backward_sequence_, " -> ").c_str(), + absl::StrJoin(unperformed_, ", ").c_str()); } else { return "(...)"; } @@ -930,40 +931,40 @@ void Assignment::SetObjectiveValue(int64 value) { } } -void Assignment::Activate(const IntVar* const b) { - int_var_container_.MutableElement(b)->Activate(); +void Assignment::Activate(const IntVar* const var) { + int_var_container_.MutableElement(var)->Activate(); } -void Assignment::Deactivate(const IntVar* const b) { - int_var_container_.MutableElement(b)->Deactivate(); +void Assignment::Deactivate(const IntVar* const var) { + int_var_container_.MutableElement(var)->Deactivate(); } -bool Assignment::Activated(const IntVar* const b) const { - return int_var_container_.Element(b).Activated(); +bool Assignment::Activated(const IntVar* const var) const { + return int_var_container_.Element(var).Activated(); } -void Assignment::Activate(const IntervalVar* const b) { - interval_var_container_.MutableElement(b)->Activate(); +void Assignment::Activate(const IntervalVar* const var) { + interval_var_container_.MutableElement(var)->Activate(); } -void Assignment::Deactivate(const IntervalVar* const b) { - interval_var_container_.MutableElement(b)->Deactivate(); +void Assignment::Deactivate(const IntervalVar* const var) { + interval_var_container_.MutableElement(var)->Deactivate(); } -bool Assignment::Activated(const IntervalVar* const b) const { - return interval_var_container_.Element(b).Activated(); +bool Assignment::Activated(const IntervalVar* const var) const { + return interval_var_container_.Element(var).Activated(); } -void Assignment::Activate(const SequenceVar* const b) { - sequence_var_container_.MutableElement(b)->Activate(); +void Assignment::Activate(const SequenceVar* const var) { + sequence_var_container_.MutableElement(var)->Activate(); } -void Assignment::Deactivate(const SequenceVar* const b) { - sequence_var_container_.MutableElement(b)->Deactivate(); +void Assignment::Deactivate(const SequenceVar* const var) { + sequence_var_container_.MutableElement(var)->Deactivate(); } -bool Assignment::Activated(const SequenceVar* const b) const { - return sequence_var_container_.Element(b).Activated(); +bool Assignment::Activated(const SequenceVar* const var) const { + return sequence_var_container_.Element(var).Activated(); } void Assignment::ActivateObjective() { @@ -997,13 +998,42 @@ bool Assignment::Contains(const SequenceVar* const var) const { return sequence_var_container_.Contains(var); } +void Assignment::CopyIntersection(const Assignment* assignment) { + int_var_container_.CopyIntersection(assignment->int_var_container_); + interval_var_container_.CopyIntersection(assignment->interval_var_container_); + sequence_var_container_.CopyIntersection(assignment->sequence_var_container_); + objective_element_ = assignment->objective_element_; +} + void Assignment::Copy(const Assignment* assignment) { + Clear(); int_var_container_.Copy(assignment->int_var_container_); interval_var_container_.Copy(assignment->interval_var_container_); sequence_var_container_.Copy(assignment->sequence_var_container_); objective_element_ = assignment->objective_element_; } +void SetAssignmentFromAssignment(Assignment* target_assignment, + const std::vector& target_vars, + const Assignment* source_assignment, + const std::vector& source_vars) { + const int vars_size = target_vars.size(); + CHECK_EQ(source_vars.size(), vars_size); + CHECK(target_assignment != nullptr); + + target_assignment->Clear(); + const Solver* const target_solver = target_assignment->solver(); + const Solver* const source_solver = source_assignment->solver(); + for (int index = 0; index < vars_size; index++) { + IntVar* target_var = target_vars[index]; + CHECK_EQ(target_var->solver(), target_solver); + IntVar* source_var = source_vars[index]; + CHECK_EQ(source_var->solver(), source_solver); + target_assignment->Add(target_var) + ->SetValue(source_assignment->Value(source_var)); + } +} + Assignment* Solver::MakeAssignment() { return RevAlloc(new Assignment(this)); } Assignment* Solver::MakeAssignment(const Assignment* const a) { diff --git a/ortools/constraint_solver/constraint_solver.cc b/ortools/constraint_solver/constraint_solver.cc index 0fc4f46b7c..fb0cea9530 100644 --- a/ortools/constraint_solver/constraint_solver.cc +++ b/ortools/constraint_solver/constraint_solver.cc @@ -30,7 +30,6 @@ #include "ortools/base/logging.h" #include "ortools/base/macros.h" #include "ortools/base/stringprintf.h" -#include "ortools/base/string_view.h" #include "ortools/base/file.h" #include "ortools/base/recordio.h" #include "zlib.h" @@ -536,9 +535,9 @@ class NoCompressionTrailPacker : public TrailPacker { void Pack(const addrval* block, std::string* packed_block) override { DCHECK(block != nullptr); DCHECK(packed_block != nullptr); - string_view block_str; - block_str.set(block, this->input_size()); - block_str.CopyToString(packed_block); + absl::string_view block_str(reinterpret_cast(block), + this->input_size()); + packed_block->assign(block_str.data(), block_str.size()); } void Unpack(const std::string& packed_block, addrval* block) override { DCHECK(block != nullptr); @@ -567,9 +566,9 @@ class ZlibTrailPacker : public TrailPacker { compress(reinterpret_cast(tmp_block_.get()), &size, reinterpret_cast(block), this->input_size()); CHECK_EQ(Z_OK, result); - string_view block_str; - block_str.set(tmp_block_.get(), size); - block_str.CopyToString(packed_block); + absl::string_view block_str; + block_str = absl::string_view(tmp_block_.get(), size); + packed_block->assign(block_str.data(), block_str.size()); } void Unpack(const std::string& packed_block, addrval* block) override { @@ -989,8 +988,8 @@ class Search { void EnterSearch(); void RestartSearch(); void ExitSearch(); - void BeginNextDecision(DecisionBuilder* const b); - void EndNextDecision(DecisionBuilder* const b, Decision* const d); + void BeginNextDecision(DecisionBuilder* const db); + void EndNextDecision(DecisionBuilder* const db, Decision* const d); void ApplyDecision(Decision* const d); void AfterDecision(Decision* const d, bool apply); void RefuteDecision(Decision* const d); @@ -1018,7 +1017,7 @@ class Search { void set_created_by_solve(bool c) { created_by_solve_ = c; } bool created_by_solve() const { return created_by_solve_; } Solver::DecisionModification ModifyDecision(); - void SetBranchSelector(Solver::BranchSelector s); + void SetBranchSelector(Solver::BranchSelector bs); void LeftMove() { search_depth_++; left_search_depth_++; @@ -2868,7 +2867,8 @@ void SearchMonitor::Install() { } // ---------- Propagation Monitor ----------- -PropagationMonitor::PropagationMonitor(Solver* const s) : SearchMonitor(s) {} +PropagationMonitor::PropagationMonitor(Solver* const solver) + : SearchMonitor(solver) {} PropagationMonitor::~PropagationMonitor() {} @@ -2879,7 +2879,8 @@ void PropagationMonitor::Install() { } // ---------- Local Search Monitor ----------- -LocalSearchMonitor::LocalSearchMonitor(Solver* const s) : SearchMonitor(s) {} +LocalSearchMonitor::LocalSearchMonitor(Solver* const solver) + : SearchMonitor(solver) {} LocalSearchMonitor::~LocalSearchMonitor() {} diff --git a/ortools/constraint_solver/constraint_solver.h b/ortools/constraint_solver/constraint_solver.h index 90be0526d2..b7546c83b4 100644 --- a/ortools/constraint_solver/constraint_solver.h +++ b/ortools/constraint_solver/constraint_solver.h @@ -122,6 +122,7 @@ class IntVarElement; class IntervalVar; class IntervalVarAssignment; class IntervalVarElement; +class IntVarLocalSearchFilter; class LocalSearchFilter; class LocalSearchOperator; class LocalSearchPhaseParameters; @@ -783,20 +784,21 @@ class Solver { #endif // SWIG // Solver API - explicit Solver(const std::string& modelname); - Solver(const std::string& modelname, const ConstraintSolverParameters& parameters); + explicit Solver(const std::string& name); + Solver(const std::string& name, const ConstraintSolverParameters& parameters); ~Solver(); // Stored Parameters. ConstraintSolverParameters parameters() const { return parameters_; } // Create a ConstraintSolverParameters proto with all the default values. + // TODO(user): Move to constraint_solver_parameters.h. static ConstraintSolverParameters DefaultSolverParameters(); // reversibility // SaveValue() will save the value of the corresponding object. It must be - // called before modifying the object. The value will be restored upon - // backtrack. + // called before modifying the object. The value will be restored upon + // backtrack. template void SaveValue(T* o) { InternalSaveValue(o); @@ -866,11 +868,11 @@ class Solver { // constraints should do: solver.AddConstraint(solver.RevAlloc(new // MyConstraint(...)); void AddConstraint(Constraint* const c); - // Adds the constraint 'c' to the solver and marks it as a cast - // constraint, that is, a constraint created calling Var() on an - // expression. This is used internally. - void AddCastConstraint(CastConstraint* const c, IntVar* const target_var, - IntExpr* const casted_expression); + // Adds 'constraint' to the solver and marks it as a cast constraint, that is, + // a constraint created calling Var() on an expression. This is used + // internally. + void AddCastConstraint(CastConstraint* const constraint, + IntVar* const target_var, IntExpr* const expr); // @{ // Solves the problem using the given DecisionBuilder and returns true if a @@ -968,12 +970,12 @@ class Solver { SearchMonitor* const m2, SearchMonitor* const m3); // Checks whether the given assignment satisfies all the relevant constraints. - bool CheckAssignment(Assignment* const assignment); + bool CheckAssignment(Assignment* const solution); // Checks whether adding this constraint will lead to an immediate - // failure. It will return false if the model is already - // inconsistent, or if adding the constraint makes it inconsistent. - bool CheckConstraint(Constraint* const constraint); + // failure. It will return false if the model is already inconsistent, or if + // adding the constraint makes it inconsistent. + bool CheckConstraint(Constraint* const ct); // State of the solver. SolverState state() const { return state_; } @@ -994,10 +996,10 @@ class Solver { const std::vector& monitors, DecisionBuilder* const db) const; // Loads the model into the solver, and returns true upon success. - bool LoadModel(const CpModel& proto); + bool LoadModel(const CpModel& model_proto); // Loads the model into the solver, appends search monitors to monitors, // and returns true upon success. - bool LoadModelWithSearchMonitors(const CpModel& proto, + bool LoadModelWithSearchMonitors(const CpModel& model_proto, std::vector* monitors); // Upgrades the model to the latest version. static bool UpgradeModel(CpModel* const proto); @@ -1089,7 +1091,7 @@ class Solver { // ----- Int Variables and Constants ----- // MakeIntVar will create the best range based int var for the bounds given. - IntVar* MakeIntVar(int64 vmin, int64 vmax, const std::string& name); + IntVar* MakeIntVar(int64 min, int64 max, const std::string& name); // MakeIntVar will create a variable with the given sparse domain. IntVar* MakeIntVar(const std::vector& values, const std::string& name); @@ -1098,7 +1100,7 @@ class Solver { IntVar* MakeIntVar(const std::vector& values, const std::string& name); // MakeIntVar will create the best range based int var for the bounds given. - IntVar* MakeIntVar(int64 vmin, int64 vmax); + IntVar* MakeIntVar(int64 min, int64 max); // MakeIntVar will create a variable with the given sparse domain. IntVar* MakeIntVar(const std::vector& values); @@ -1182,10 +1184,10 @@ class Solver { // expr ^ n (n > 0) IntExpr* MakePower(IntExpr* const expr, int64 n); - // vals[index] - IntExpr* MakeElement(const std::vector& vals, IntVar* const index); - // vals[index] - IntExpr* MakeElement(const std::vector& vals, IntVar* const index); + // values[index] + IntExpr* MakeElement(const std::vector& values, IntVar* const index); + // values[index] + IntExpr* MakeElement(const std::vector& values, IntVar* const index); // Function-based element. The constraint takes ownership of // callback The callback must be able to cope with any possible @@ -1199,11 +1201,11 @@ class Solver { // incorrect increasing parameter will result in undefined behavior. IntExpr* MakeMonotonicElement(IndexEvaluator1 values, bool increasing, IntVar* const index); - // 2D version of function-based element expression, values(index1, index2). + // 2D version of function-based element expression, values(expr1, expr2). IntExpr* MakeElement(IndexEvaluator2 values, IntVar* const index1, IntVar* const index2); - // vars[index] + // vars[expr] IntExpr* MakeElement(const std::vector& vars, IntVar* const index); #if !defined(SWIG) @@ -1226,28 +1228,28 @@ class Solver { IntExpr* MakeMin(const std::vector& vars); // std::min (left, right) IntExpr* MakeMin(IntExpr* const left, IntExpr* const right); - // std::min(expr, val) - IntExpr* MakeMin(IntExpr* const expr, int64 val); - // std::min(expr, val) - IntExpr* MakeMin(IntExpr* const expr, int val); + // std::min(expr, value) + IntExpr* MakeMin(IntExpr* const expr, int64 value); + // std::min(expr, value) + IntExpr* MakeMin(IntExpr* const expr, int value); // std::max(vars) IntExpr* MakeMax(const std::vector& vars); // std::max(left, right) IntExpr* MakeMax(IntExpr* const left, IntExpr* const right); - // std::max(expr, val) - IntExpr* MakeMax(IntExpr* const expr, int64 val); - // std::max(expr, val) - IntExpr* MakeMax(IntExpr* const expr, int val); + // std::max(expr, value) + IntExpr* MakeMax(IntExpr* const expr, int64 value); + // std::max(expr, value) + IntExpr* MakeMax(IntExpr* const expr, int value); - // convex piecewise function. - IntExpr* MakeConvexPiecewiseExpr(IntExpr* e, int64 early_cost, + // Convex piecewise function. + IntExpr* MakeConvexPiecewiseExpr(IntExpr* expr, int64 early_cost, int64 early_date, int64 late_date, int64 late_cost); // Semi continuous Expression (x <= 0 -> f(x) = 0; x > 0 -> f(x) = ax + b) // a >= 0 and b >= 0 - IntExpr* MakeSemiContinuousExpr(IntExpr* const e, int64 fixed_charge, + IntExpr* MakeSemiContinuousExpr(IntExpr* const expr, int64 fixed_charge, int64 step); // General piecewise-linear function expression, built from f(x) where f is @@ -1265,9 +1267,10 @@ class Solver { // Modulo expression x % mod (with the python convention for modulo). IntExpr* MakeModulo(IntExpr* const x, IntExpr* const mod); - // Conditional Expr condition ? expression : value + // Conditional Expr condition ? expr : unperformed_value IntExpr* MakeConditionalExpression(IntVar* const condition, - IntExpr* const expression, int64 value); + IntExpr* const expr, + int64 unperformed_value); // ----- Constraints ----- // This constraint always succeeds. @@ -1276,14 +1279,15 @@ class Solver { Constraint* MakeFalseConstraint(); Constraint* MakeFalseConstraint(const std::string& explanation); - // b == (v == c) - Constraint* MakeIsEqualCstCt(IntExpr* const v, int64 c, IntVar* const b); - // status var of (v == c) + // boolvar == (var == value) + Constraint* MakeIsEqualCstCt(IntExpr* const var, int64 value, + IntVar* const boolvar); + // status var of (var == value) IntVar* MakeIsEqualCstVar(IntExpr* const var, int64 value); // b == (v1 == v2) Constraint* MakeIsEqualCt(IntExpr* const v1, IntExpr* v2, IntVar* const b); // status var of (v1 == v2) - IntVar* MakeIsEqualVar(IntExpr* const var, IntExpr* v2); + IntVar* MakeIsEqualVar(IntExpr* const v1, IntExpr* v2); // left == right Constraint* MakeEquality(IntExpr* const left, IntExpr* const right); // expr == value @@ -1291,10 +1295,11 @@ class Solver { // expr == value Constraint* MakeEquality(IntExpr* const expr, int value); - // b == (v != c) - Constraint* MakeIsDifferentCstCt(IntExpr* const v, int64 c, IntVar* const b); - // status var of (v != c) - IntVar* MakeIsDifferentCstVar(IntExpr* const v, int64 c); + // boolvar == (var != value) + Constraint* MakeIsDifferentCstCt(IntExpr* const var, int64 value, + IntVar* const boolvar); + // status var of (var != value) + IntVar* MakeIsDifferentCstVar(IntExpr* const var, int64 value); // status var of (v1 != v2) IntVar* MakeIsDifferentVar(IntExpr* const v1, IntExpr* const v2); // b == (v1 != v2) @@ -1307,11 +1312,11 @@ class Solver { // expr != value Constraint* MakeNonEquality(IntExpr* const expr, int value); - // b == (v <= c) - Constraint* MakeIsLessOrEqualCstCt(IntExpr* const v, int64 c, - IntVar* const b); - // status var of (v <= c) - IntVar* MakeIsLessOrEqualCstVar(IntExpr* const v, int64 c); + // boolvar == (var <= value) + Constraint* MakeIsLessOrEqualCstCt(IntExpr* const var, int64 value, + IntVar* const boolvar); + // status var of (var <= value) + IntVar* MakeIsLessOrEqualCstVar(IntExpr* const var, int64 value); // status var of (left <= right) IntVar* MakeIsLessOrEqualVar(IntExpr* const left, IntExpr* const right); // b == (left <= right) @@ -1324,11 +1329,11 @@ class Solver { // expr <= value Constraint* MakeLessOrEqual(IntExpr* const expr, int value); - // b == (v >= c) - Constraint* MakeIsGreaterOrEqualCstCt(IntExpr* const v, int64 c, - IntVar* const b); - // status var of (v >= c) - IntVar* MakeIsGreaterOrEqualCstVar(IntExpr* const v, int64 c); + // boolvar == (var >= value) + Constraint* MakeIsGreaterOrEqualCstCt(IntExpr* const var, int64 value, + IntVar* const boolvar); + // status var of (var >= value) + IntVar* MakeIsGreaterOrEqualCstVar(IntExpr* const var, int64 value); // status var of (left >= right) IntVar* MakeIsGreaterOrEqualVar(IntExpr* const left, IntExpr* const right); // b == (left >= right) @@ -1343,8 +1348,8 @@ class Solver { // b == (v > c) Constraint* MakeIsGreaterCstCt(IntExpr* const v, int64 c, IntVar* const b); - // status var of (v > c) - IntVar* MakeIsGreaterCstVar(IntExpr* const v, int64 c); + // status var of (var > value) + IntVar* MakeIsGreaterCstVar(IntExpr* const var, int64 value); // status var of (left > right) IntVar* MakeIsGreaterVar(IntExpr* const left, IntExpr* const right); // b == (left > right) @@ -1359,8 +1364,8 @@ class Solver { // b == (v < c) Constraint* MakeIsLessCstCt(IntExpr* const v, int64 c, IntVar* const b); - // status var of (v < c) - IntVar* MakeIsLessCstVar(IntExpr* const v, int64 c); + // status var of (var < value) + IntVar* MakeIsLessCstVar(IntExpr* const var, int64 value); // status var of (left < right) IntVar* MakeIsLessVar(IntExpr* const left, IntExpr* const right); // b == (left < right) @@ -1393,10 +1398,10 @@ class Solver { const std::vector& coefficients, IntVar* const target); Constraint* MakeScalProdGreaterOrEqual(const std::vector& vars, - const std::vector& coefficients, + const std::vector& coeffs, int64 cst); Constraint* MakeScalProdGreaterOrEqual(const std::vector& vars, - const std::vector& coefficients, + const std::vector& coeffs, int64 cst); Constraint* MakeScalProdLessOrEqual(const std::vector& vars, const std::vector& coefficients, @@ -1406,9 +1411,9 @@ class Solver { int64 cst); Constraint* MakeMinEquality(const std::vector& vars, - IntVar* const var); + IntVar* const min_var); Constraint* MakeMaxEquality(const std::vector& vars, - IntVar* const var); + IntVar* const max_var); Constraint* MakeElementEquality(const std::vector& vals, IntVar* const index, IntVar* const target); @@ -1443,59 +1448,66 @@ class Solver { // ----- Between and related constraints ----- - // (l <= v <= u) - Constraint* MakeBetweenCt(IntExpr* const v, int64 l, int64 u); + // (l <= expr <= u) + Constraint* MakeBetweenCt(IntExpr* const expr, int64 l, int64 u); - // (v < l || v > u) + // (expr < l || expr > u) // This constraint is lazy as it will not make holes in the domain of // variables. It will propagate only when expr->Min() >= l // or expr->Max() <= u. - Constraint* MakeNotBetweenCt(IntExpr* const v, int64 l, int64 u); + Constraint* MakeNotBetweenCt(IntExpr* const expr, int64 l, int64 u); - // b == (l <= v <= u) - Constraint* MakeIsBetweenCt(IntExpr* const v, int64 l, int64 u, + // b == (l <= expr <= u) + Constraint* MakeIsBetweenCt(IntExpr* const expr, int64 l, int64 u, IntVar* const b); IntVar* MakeIsBetweenVar(IntExpr* const v, int64 l, int64 u); // ----- Member and related constraints ----- - // v in set. Propagation is lazy, i.e. this constraint does not + // expr in set. Propagation is lazy, i.e. this constraint does not // creates holes in the domain of the variable. - Constraint* MakeMemberCt(IntExpr* const v, const std::vector& values); - Constraint* MakeMemberCt(IntExpr* const v, const std::vector& values); + Constraint* MakeMemberCt(IntExpr* const expr, + const std::vector& values); + Constraint* MakeMemberCt(IntExpr* const expr, const std::vector& values); - // v not in set. - Constraint* MakeNotMemberCt(IntExpr* const v, + // expr not in set. + Constraint* MakeNotMemberCt(IntExpr* const expr, const std::vector& values); - Constraint* MakeNotMemberCt(IntExpr* const v, const std::vector& values); + Constraint* MakeNotMemberCt(IntExpr* const expr, + const std::vector& values); - // v should not be in the list of forbidden intervals [start[i]..end[i]]. - Constraint* MakeNotMemberCt(IntExpr* const v, std::vector starts, + // expr should not be in the list of forbidden intervals [start[i]..end[i]]. + Constraint* MakeNotMemberCt(IntExpr* const expr, std::vector starts, std::vector ends); - // v should not be in the list of forbidden intervals [start[i]..end[i]]. - Constraint* MakeNotMemberCt(IntExpr* const v, std::vector starts, + // expr should not be in the list of forbidden intervals [start[i]..end[i]]. + Constraint* MakeNotMemberCt(IntExpr* const expr, std::vector starts, std::vector ends); #if !defined(SWIG) - // v should not be in the list of forbidden intervals. - Constraint* MakeNotMemberCt(IntExpr* v, SortedDisjointIntervalList intervals); + // expr should not be in the list of forbidden intervals. + Constraint* MakeNotMemberCt(IntExpr* expr, + SortedDisjointIntervalList intervals); #endif // !defined(SWIG) - // b == (v in set) - Constraint* MakeIsMemberCt(IntExpr* const v, const std::vector& values, - IntVar* const b); - Constraint* MakeIsMemberCt(IntExpr* const v, const std::vector& values, - IntVar* const b); - IntVar* MakeIsMemberVar(IntExpr* const v, const std::vector& values); - IntVar* MakeIsMemberVar(IntExpr* const v, const std::vector& values); + // boolvar == (expr in set) + Constraint* MakeIsMemberCt(IntExpr* const expr, + const std::vector& values, + IntVar* const boolvar); + Constraint* MakeIsMemberCt(IntExpr* const expr, + const std::vector& values, + IntVar* const boolvar); + IntVar* MakeIsMemberVar(IntExpr* const expr, + const std::vector& values); + IntVar* MakeIsMemberVar(IntExpr* const expr, const std::vector& values); - // |{i | v[i] == value}| <= count - Constraint* MakeAtMost(std::vector v, int64 value, int64 count); - // |{i | v[i] == value}| == count - Constraint* MakeCount(const std::vector& v, int64 value, - int64 count); - // |{i | v[i] == value}| == count - Constraint* MakeCount(const std::vector& v, int64 value, - IntVar* const count); + // |{i | vars[i] == value}| <= max_count + Constraint* MakeAtMost(std::vector vars, int64 value, + int64 max_count); + // |{i | vars[i] == value}| == max_count + Constraint* MakeCount(const std::vector& vars, int64 value, + int64 max_count); + // |{i | vars[i] == value}| == max_count + Constraint* MakeCount(const std::vector& vars, int64 value, + IntVar* const max_count); // Aggregated version of count: |{i | v[i] == values[j]}| == cards[j] Constraint* MakeDistribute(const std::vector& vars, @@ -1701,8 +1713,8 @@ class Solver { // Contraint enforcing, for each pair (i,j) in precedences, i to be before j // in paths defined by next variables. // TODO(user): This constraint does not make holes in variable domains; - // the implementation can easily be modified to do that; evaluate the the - // impact on models solved with local search. + // the implementation can easily be modified to do that; evaluate the impact + // on models solved with local search. Constraint* MakePathPrecedenceConstraint( std::vector nexts, const std::vector>& precedences); @@ -1713,10 +1725,10 @@ class Solver { const std::vector>& precedences); #endif // This constraint maps the domain of 'var' onto the array of - // variables 'vars'. That is - // for all i in [0 .. size - 1]: vars[i] == 1 <=> var->Contains(i); + // variables 'actives'. That is + // for all i in [0 .. size - 1]: actives[i] == 1 <=> var->Contains(i); Constraint* MakeMapDomain(IntVar* const var, - const std::vector& vars); + const std::vector& actives); // This method creates a constraint where the graph of the relation // between the variables is given in extension. There are 'arity' @@ -1727,25 +1739,25 @@ class Solver { // This constraint create a finite automaton that will check the // sequence of variables vars. It uses a transition table called - // 'transitions'. Each transition is a triple + // 'transition_table'. Each transition is a triple // (current_state, variable_value, new_state). // The initial state is given, and the set of accepted states is decribed // by 'final_states'. These states are hidden inside the constraint. // Only the transitions (i.e. the variables) are visible. Constraint* MakeTransitionConstraint(const std::vector& vars, - const IntTupleSet& transitions, + const IntTupleSet& transition_table, int64 initial_state, const std::vector& final_states); // This constraint create a finite automaton that will check the // sequence of variables vars. It uses a transition table called - // 'transitions'. Each transition is a triple + // 'transition_table'. Each transition is a triple // (current_state, variable_value, new_state). // The initial state is given, and the set of accepted states is decribed // by 'final_states'. These states are hidden inside the constraint. // Only the transitions (i.e. the variables) are visible. Constraint* MakeTransitionConstraint(const std::vector& vars, - const IntTupleSet& transitions, + const IntTupleSet& transition_table, int64 initial_state, const std::vector& final_states); @@ -1753,7 +1765,7 @@ class Solver { // Compatibility layer for python API. Constraint* MakeAllowedAssignments( const std::vector& vars, - const std::vector>& raw_tuples) { + const std::vector >& raw_tuples) { IntTupleSet tuples(vars.size()); tuples.InsertAll(raw_tuples); return MakeAllowedAssignments(vars, tuples); @@ -1761,7 +1773,7 @@ class Solver { Constraint* MakeTransitionConstraint( const std::vector& vars, - const std::vector>& raw_transitions, + const std::vector >& raw_transitions, int64 initial_state, const std::vector& final_states) { IntTupleSet transitions(3); transitions.InsertAll(raw_transitions); @@ -1841,7 +1853,7 @@ class Solver { // The duration must be greater than 0. IntervalVar* MakeFixedDurationIntervalVar(IntVar* const start_variable, int64 duration, - IntVar* const performed_var, + IntVar* const performed_variable, const std::string& name); // This method fills the vector with 'count' interval var built with @@ -2089,11 +2101,11 @@ class Solver { // then the target var is unperformed too. Also, if the target // variable is unperformed, then all the intervals variables are // unperformed too. - Constraint* MakeCover(const std::vector& intervals, + Constraint* MakeCover(const std::vector& vars, IntervalVar* const target_var); // This constraints states that the two interval variables are equal. - Constraint* MakeEquality(IntervalVar* const left, IntervalVar* const right); + Constraint* MakeEquality(IntervalVar* const var1, IntervalVar* const var2); // ----- Assignments ----- @@ -2120,18 +2132,26 @@ class Solver { SolutionCollector* MakeLastSolutionCollector(); // Collect the solution corresponding to the optimal value of the objective - // of 'a'; if 'a' does not have an objective no solution is collected. This - // collector only collects one solution corresponding to the best objective - // value (the first one found). + // of 'assignment'; if 'assignment' does not have an objective no solution is + // collected. This collector only collects one solution corresponding to the + // best objective value (the first one found). SolutionCollector* MakeBestValueSolutionCollector( const Assignment* const assignment, bool maximize); // Collect the solution corresponding to the optimal value of the - // objective of 'a'; if 'a' does not have an objective no solution - // is collected. This collector only collects one solution + // objective of 'assignment'; if 'assignment' does not have an objective no + // solution is collected. This collector only collects one solution // corresponding to the best objective value (the first one // found). The variables will need to be added later. SolutionCollector* MakeBestValueSolutionCollector(bool maximize); + // Same as MakeBestValueSolutionCollector but collects the best + // solution_count solutions. Collected solutions are sorted in increasing + // optimality order (the best solution is the last one). + SolutionCollector* MakeNBestValueSolutionCollector( + const Assignment* const assignment, int solution_count, bool maximize); + SolutionCollector* MakeNBestValueSolutionCollector(int solution_count, + bool maximize); + // Collect all solutions of the search. SolutionCollector* MakeAllSolutionCollector( const Assignment* const assignment); @@ -2151,36 +2171,36 @@ class Solver { OptimizeVar* MakeOptimize(bool maximize, IntVar* const v, int64 step); // Creates a minimization weighted objective. The actual objective is - // scalar_prod(vars, weights). - OptimizeVar* MakeWeightedMinimize(const std::vector& vars, + // scalar_prod(sub_objectives, weights). + OptimizeVar* MakeWeightedMinimize(const std::vector& sub_objectives, const std::vector& weights, int64 step); // Creates a minimization weighted objective. The actual objective is - // scalar_prod(vars, weights). - OptimizeVar* MakeWeightedMinimize(const std::vector& vars, + // scalar_prod(sub_objectives, weights). + OptimizeVar* MakeWeightedMinimize(const std::vector& sub_objectives, const std::vector& weights, int64 step); // Creates a maximization weigthed objective. - OptimizeVar* MakeWeightedMaximize(const std::vector& vars, + OptimizeVar* MakeWeightedMaximize(const std::vector& sub_objectives, const std::vector& weights, int64 step); // Creates a maximization weigthed objective. - OptimizeVar* MakeWeightedMaximize(const std::vector& vars, + OptimizeVar* MakeWeightedMaximize(const std::vector& sub_objectives, const std::vector& weights, int64 step); // Creates a weighted objective with a given sense (true = maximization). OptimizeVar* MakeWeightedOptimize(bool maximize, - const std::vector& vars, + const std::vector& sub_objectives, const std::vector& weights, int64 step); // Creates a weighted objective with a given sense (true = maximization). OptimizeVar* MakeWeightedOptimize(bool maximize, - const std::vector& vars, + const std::vector& sub_objectives, const std::vector& weights, int64 step); @@ -2208,16 +2228,12 @@ class Solver { int64 keep_tenure, int64 forbid_tenure, double tabu_factor); - // Creates a Tabu Search monitor based on the solution value of the objective. - // In the context of local search the behavior is similar to MakeOptimize(), - // creating an objective in a given sense. The behavior differs once a local - // optimum is reached: thereafter solutions which degrade the value of the - // objective are allowed if they are not "tabu". A solution is "tabu" if it - // doesn't respect the following rules: - // - improving the best solution found so far - // - objective value must be different from the last considered solutions. - SearchMonitor* MakeObjectiveTabuSearch(bool maximize, IntVar* const v, - int64 step, int64 forbid_tenure); + // Creates a Tabu Search based on the vars |vars|. + // A solution is "tabu" if all the vars in |vars| keep their value. + SearchMonitor* MakeGenericTabuSearch(bool maximize, IntVar* const v, + int64 step, + const std::vector& tabu_vars, + int64 forbid_tenure); // Creates a Simulated Annealing monitor. // TODO(user): document behavior @@ -2349,31 +2365,31 @@ class Solver { // ----- Search Log ----- // The SearchMonitors below will display a periodic search log - // on LOG(INFO) every branch_count branches explored. + // on LOG(INFO) every branch_period branches explored. - SearchMonitor* MakeSearchLog(int branch_count); + SearchMonitor* MakeSearchLog(int branch_period); - // At each solution, this monitor also display the objective value. - SearchMonitor* MakeSearchLog(int branch_count, IntVar* const objective); + // At each solution, this monitor also display the var value. + SearchMonitor* MakeSearchLog(int branch_period, IntVar* const var); // At each solution, this monitor will also display result of @p // display_callback. - SearchMonitor* MakeSearchLog(int branch_count, + SearchMonitor* MakeSearchLog(int branch_period, std::function display_callback); - // At each solution, this monitor will display the objective value and the + // At each solution, this monitor will display the 'var' value and the // result of @p display_callback. - SearchMonitor* MakeSearchLog(int branch_count, IntVar* objective, + SearchMonitor* MakeSearchLog(int branch_period, IntVar* var, std::function display_callback); // OptimizeVar Search Logs - // At each solution, this monitor will also display the objective->Print(). + // At each solution, this monitor will also display the 'opt_var' value. - SearchMonitor* MakeSearchLog(int branch_count, OptimizeVar* const objective); + SearchMonitor* MakeSearchLog(int branch_period, OptimizeVar* const opt_var); // Creates a search monitor that will also print the result of the // display callback. - SearchMonitor* MakeSearchLog(int branch_count, OptimizeVar* const objective, + SearchMonitor* MakeSearchLog(int branch_period, OptimizeVar* const opt_var, std::function display_callback); @@ -2418,10 +2434,10 @@ class Solver { // ----- Search Decicions and Decision Builders ----- // ----- Decisions ----- - Decision* MakeAssignVariableValue(IntVar* const var, int64 value); + Decision* MakeAssignVariableValue(IntVar* const var, int64 val); Decision* MakeVariableLessOrEqualValue(IntVar* const var, int64 value); Decision* MakeVariableGreaterOrEqualValue(IntVar* const var, int64 value); - Decision* MakeSplitVariableDomain(IntVar* const var, int64 value, + Decision* MakeSplitVariableDomain(IntVar* const var, int64 val, bool start_with_lower_half); Decision* MakeAssignVariableValueOrFail(IntVar* const var, int64 value); Decision* MakeAssignVariablesValues(const std::vector& vars, @@ -2483,7 +2499,8 @@ class Solver { IntValueStrategy val_str); DecisionBuilder* MakePhase(const std::vector& vars, - IntVarStrategy var_str, IndexEvaluator2 val_eval); + IntVarStrategy var_str, + IndexEvaluator2 value_evaluator); DecisionBuilder* MakePhase( const std::vector& vars, IntVarStrategy var_str, @@ -2493,15 +2510,16 @@ class Solver { DecisionBuilder* MakePhase(const std::vector& vars, IndexEvaluator1 var_evaluator, - IndexEvaluator2 val_eval); + IndexEvaluator2 value_evaluator); DecisionBuilder* MakePhase(const std::vector& vars, - IntVarStrategy var_str, IndexEvaluator2 val_eval, + IntVarStrategy var_str, + IndexEvaluator2 value_evaluator, IndexEvaluator1 tie_breaker); DecisionBuilder* MakePhase(const std::vector& vars, IndexEvaluator1 var_evaluator, - IndexEvaluator2 val_eval, + IndexEvaluator2 value_evaluator, IndexEvaluator1 tie_breaker); DecisionBuilder* MakeDefaultPhase(const std::vector& vars); @@ -2552,7 +2570,7 @@ class Solver { // of these variables. Ownership of the callback is passed to the decision // builder. DecisionBuilder* MakePhase(const std::vector& vars, - IndexEvaluator2 evaluator, EvaluatorStrategy str); + IndexEvaluator2 eval, EvaluatorStrategy str); // Returns a decision builder which assigns values to variables // which minimize the values returned by the evaluator. In case of @@ -2562,8 +2580,7 @@ class Solver { // variables in vars and the values of these variables. Ownership of // the callback is passed to the decision builder. DecisionBuilder* MakePhase(const std::vector& vars, - IndexEvaluator2 evaluator, - IndexEvaluator1 tie_breaker, + IndexEvaluator2 eval, IndexEvaluator1 tie_breaker, EvaluatorStrategy str); // Scheduling phases. @@ -2815,22 +2832,22 @@ class Solver { // Local Search Filters LocalSearchFilter* MakeVariableDomainFilter(); - LocalSearchFilter* MakeLocalSearchObjectiveFilter( + IntVarLocalSearchFilter* MakeLocalSearchObjectiveFilter( const std::vector& vars, IndexEvaluator2 values, IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, Solver::LocalSearchOperation op_enum); - LocalSearchFilter* MakeLocalSearchObjectiveFilter( + IntVarLocalSearchFilter* MakeLocalSearchObjectiveFilter( const std::vector& vars, IndexEvaluator2 values, ObjectiveWatcher delta_objective_callback, IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, Solver::LocalSearchOperation op_enum); - LocalSearchFilter* MakeLocalSearchObjectiveFilter( + IntVarLocalSearchFilter* MakeLocalSearchObjectiveFilter( const std::vector& vars, const std::vector& secondary_vars, Solver::IndexEvaluator3 values, IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, Solver::LocalSearchOperation op_enum); - LocalSearchFilter* MakeLocalSearchObjectiveFilter( + IntVarLocalSearchFilter* MakeLocalSearchObjectiveFilter( const std::vector& vars, const std::vector& secondary_vars, Solver::IndexEvaluator3 values, ObjectiveWatcher delta_objective_callback, @@ -2938,7 +2955,7 @@ class Solver { // Returns whether the object has been named or not. bool HasName(const PropagationBaseObject* object) const; // Adds a new demon and wraps it inside a DemonProfiler if necessary. - Demon* RegisterDemon(Demon* const d); + Demon* RegisterDemon(Demon* const demon); // Registers a new IntExpr and wraps it inside a TraceIntExpr if necessary. IntExpr* RegisterIntExpr(IntExpr* const expr); // Registers a new IntVar and wraps it inside a TraceIntVar if necessary. @@ -3000,17 +3017,17 @@ class Solver { friend class RevImmutableMultiMap; // Returns true if expr represents either boolean_var or 1 - - // boolean_var. In that case, it fills sub_var and is_negated to be + // boolean_var. In that case, it fills inner_var and is_negated to be // true if the expression is 1 - boolean_var -- equivalent to // not(boolean_var). - bool IsBooleanVar(IntExpr* const expr, IntVar** sub_var, + bool IsBooleanVar(IntExpr* const expr, IntVar** inner_var, bool* is_negated) const; // Returns true if expr represents a product of a expr and a - // constant. In that case, it fills sub_expr and coefficient with - // these, and returns true. In the other case, it fills sub_expr + // constant. In that case, it fills inner_expr and coefficient with + // these, and returns true. In the other case, it fills inner_expr // with expr, coefficient with 1, and returns false. - bool IsProduct(IntExpr* const expr, IntExpr** sub_expr, int64* coefficient); + bool IsProduct(IntExpr* const expr, IntExpr** inner_expr, int64* coefficient); #endif // !defined(SWIG) // Internal. If the variables is the result of expr->Var(), this @@ -3037,7 +3054,7 @@ class Solver { void PushSentinel(int magic_code); void BacktrackToSentinel(int magic_code); void ProcessConstraints(); - bool BacktrackOneLevel(Decision** fd); + bool BacktrackOneLevel(Decision** fail_decision); void JumpToSentinelWhenNested(); void JumpToSentinel(); void check_alloc_state(); @@ -3584,7 +3601,7 @@ class ModelVisitor : public BaseObject { virtual void VisitIntervalVariable(const IntervalVar* const variable, const std::string& operation, int64 value, IntervalVar* const delegate); - virtual void VisitSequenceVariable(const SequenceVar* const sequence); + virtual void VisitSequenceVariable(const SequenceVar* const variable); // Visit integer arguments. virtual void VisitIntegerArgument(const std::string& arg_name, int64 value); @@ -3605,13 +3622,13 @@ class ModelVisitor : public BaseObject { IntervalVar* const argument); virtual void VisitIntervalArrayArgument( - const std::string& arg_name, const std::vector& argument); + const std::string& arg_name, const std::vector& arguments); // Visit sequence argument. virtual void VisitSequenceArgument(const std::string& arg_name, SequenceVar* const argument); virtual void VisitSequenceArrayArgument( - const std::string& arg_name, const std::vector& argument); + const std::string& arg_name, const std::vector& arguments); // Helpers. #if !defined(SWIG) virtual void VisitIntegerVariableEvaluatorArgument( @@ -3619,12 +3636,12 @@ class ModelVisitor : public BaseObject { // Using SWIG on callbacks is troublesome, so we hide these methods during // the wrapping. - void VisitInt64ToBoolExtension(Solver::IndexFilter1 callback, int64 index_min, + void VisitInt64ToBoolExtension(Solver::IndexFilter1 filter, int64 index_min, int64 index_max); - void VisitInt64ToInt64Extension(const Solver::IndexEvaluator1& callback, + void VisitInt64ToInt64Extension(const Solver::IndexEvaluator1& eval, int64 index_min, int64 index_max); // Expands function as array when index min is 0. - void VisitInt64ToInt64AsArray(const Solver::IndexEvaluator1& callback, + void VisitInt64ToInt64AsArray(const Solver::IndexEvaluator1& eval, const std::string& arg_name, int64 index_max); #endif // #if !defined(SWIG) }; @@ -4148,8 +4165,8 @@ class IntVar : public IntExpr { // from the collector used. class SolutionCollector : public SearchMonitor { public: - SolutionCollector(Solver* const s, const Assignment* assignment); - explicit SolutionCollector(Solver* const s); + SolutionCollector(Solver* const solver, const Assignment* assignment); + explicit SolutionCollector(Solver* const solver); ~SolutionCollector() override; // Add API. @@ -4211,19 +4228,29 @@ class SolutionCollector : public SearchMonitor { const std::vector& Unperformed(int n, SequenceVar* const var) const; protected: + struct SolutionData { + Assignment* solution; + int64 time; + int64 branches; + int64 failures; + int64 objective_value; + bool operator<(const SolutionData& data) const { + return solution < data.solution; + } + }; + // Push the current state as a new solution. void PushSolution(); + void Push(const SolutionData& data) { solution_data_.push_back(data); } // Remove and delete the last popped solution. void PopSolution(); - + SolutionData BuildSolutionDataForCurrentState(); + void FreeSolution(Assignment* solution); void check_index(int n) const; + std::unique_ptr prototype_; - std::vector solutions_; + std::vector solution_data_; std::vector recycle_solutions_; - std::vector times_; - std::vector branches_; - std::vector failures_; - std::vector objective_values_; private: DISALLOW_COPY_AND_ASSIGN(SolutionCollector); @@ -4362,7 +4389,7 @@ class NoGoodManager : public SearchMonitor { virtual void Clear() = 0; // NoGood factory. Create an empty nogood. NoGood* MakeNoGood(); - // Add one nogood to the recorder. Ownership is transfered to the recorder. + // Add one nogood to the recorder. Ownership is transferred to the recorder. virtual void AddNoGood(NoGood* const nogood) = 0; // Returns the number of nogoods added to the recorder. virtual int NoGoodCount() const = 0; @@ -4606,8 +4633,8 @@ class SequenceVar : public PropagationBaseObject { // rank_first[0] corresponds to the first interval of the sequence. // rank_last[0] corresponds to the last interval of the sequence. // All intervals in the unperformed vector will be marked as such. - void RankSequence(const std::vector& rank_firsts, - const std::vector& rank_lasts, + void RankSequence(const std::vector& rank_first, + const std::vector& rank_last, const std::vector& unperformed); // Clears 'rank_first' and 'rank_last', and fills them with the @@ -4619,7 +4646,7 @@ class SequenceVar : public PropagationBaseObject { // rank_first[0] corresponds to the first interval of the sequence. // rank_last[0] corresponds to the last interval of the sequence. void FillSequence(std::vector* const rank_first, - std::vector* const rank_lasts, + std::vector* const rank_last, std::vector* const unperformed) const; // Returns the index_th interval of the sequence. @@ -4905,8 +4932,9 @@ class AssignmentContainer { // null variables. void Resize(size_t size) { elements_.resize(size); } bool Empty() const { return elements_.empty(); } - // Copies intersection of containers. - void Copy(const AssignmentContainer& container) { + // Copies the elements of 'container' which are already in the calling + // container. + void CopyIntersection(const AssignmentContainer& container) { for (int i = 0; i < container.elements_.size(); ++i) { const E& element = container.elements_[i]; const V* const var = element.Var(); @@ -4926,6 +4954,15 @@ class AssignmentContainer { } } } + // Copies all the elements of 'container' to this container, clearing its + // previous content. + void Copy(const AssignmentContainer& container) { + Clear(); + for (int i = 0; i < container.elements_.size(); ++i) { + const E& element = container.elements_[i]; + FastAdd(element.Var())->Copy(element); + } + } bool Contains(const V* const var) const { int index; return Find(var, &index); @@ -5066,13 +5103,13 @@ class Assignment : public PropagationBaseObject { #if !defined(SWIG) bool Load(File* file); #endif // #if !defined(SWIG) - void Load(const AssignmentProto& proto); + void Load(const AssignmentProto& assignment_proto); // Saves the assignment to a file. bool Save(const std::string& filename) const; #if !defined(SWIG) bool Save(File* file) const; #endif // #if !defined(SWIG) - void Save(AssignmentProto* const proto) const; + void Save(AssignmentProto* const assignment_proto) const; void AddObjective(IntVar* const v); IntVar* Objective() const; @@ -5086,81 +5123,81 @@ class Assignment : public PropagationBaseObject { void SetObjectiveValue(int64 value); void SetObjectiveRange(int64 l, int64 u); - IntVarElement* Add(IntVar* const v); - void Add(const std::vector& v); + IntVarElement* Add(IntVar* const var); + void Add(const std::vector& vars); // Adds without checking if variable has been previously added. - IntVarElement* FastAdd(IntVar* const v); - int64 Min(const IntVar* const v) const; - int64 Max(const IntVar* const v) const; - int64 Value(const IntVar* const v) const; - bool Bound(const IntVar* const v) const; - void SetMin(const IntVar* const v, int64 m); - void SetMax(const IntVar* const v, int64 m); - void SetRange(const IntVar* const v, int64 l, int64 u); - void SetValue(const IntVar* const v, int64 value); + IntVarElement* FastAdd(IntVar* const var); + int64 Min(const IntVar* const var) const; + int64 Max(const IntVar* const var) const; + int64 Value(const IntVar* const var) const; + bool Bound(const IntVar* const var) const; + void SetMin(const IntVar* const var, int64 m); + void SetMax(const IntVar* const var, int64 m); + void SetRange(const IntVar* const var, int64 l, int64 u); + void SetValue(const IntVar* const var, int64 value); - IntervalVarElement* Add(IntervalVar* const v); + IntervalVarElement* Add(IntervalVar* const var); void Add(const std::vector& vars); // Adds without checking if variable has been previously added. - IntervalVarElement* FastAdd(IntervalVar* const v); - int64 StartMin(const IntervalVar* const v) const; - int64 StartMax(const IntervalVar* const v) const; - int64 StartValue(const IntervalVar* const v) const; - int64 DurationMin(const IntervalVar* const v) const; - int64 DurationMax(const IntervalVar* const v) const; - int64 DurationValue(const IntervalVar* const c) const; - int64 EndMin(const IntervalVar* const v) const; - int64 EndMax(const IntervalVar* const v) const; - int64 EndValue(const IntervalVar* const v) const; - int64 PerformedMin(const IntervalVar* const v) const; - int64 PerformedMax(const IntervalVar* const v) const; - int64 PerformedValue(const IntervalVar* const v) const; - void SetStartMin(const IntervalVar* const v, int64 m); - void SetStartMax(const IntervalVar* const v, int64 m); - void SetStartRange(const IntervalVar* const v, int64 mi, int64 ma); - void SetStartValue(const IntervalVar* const v, int64 value); - void SetDurationMin(const IntervalVar* const v, int64 m); - void SetDurationMax(const IntervalVar* const v, int64 m); - void SetDurationRange(const IntervalVar* const v, int64 mi, int64 ma); - void SetDurationValue(const IntervalVar* const v, int64 value); - void SetEndMin(const IntervalVar* const v, int64 m); - void SetEndMax(const IntervalVar* const v, int64 m); - void SetEndRange(const IntervalVar* const v, int64 mi, int64 ma); - void SetEndValue(const IntervalVar* const v, int64 value); - void SetPerformedMin(const IntervalVar* const v, int64 m); - void SetPerformedMax(const IntervalVar* const v, int64 m); - void SetPerformedRange(const IntervalVar* const v, int64 mi, int64 ma); - void SetPerformedValue(const IntervalVar* const v, int64 value); + IntervalVarElement* FastAdd(IntervalVar* const var); + int64 StartMin(const IntervalVar* const var) const; + int64 StartMax(const IntervalVar* const var) const; + int64 StartValue(const IntervalVar* const var) const; + int64 DurationMin(const IntervalVar* const var) const; + int64 DurationMax(const IntervalVar* const var) const; + int64 DurationValue(const IntervalVar* const var) const; + int64 EndMin(const IntervalVar* const var) const; + int64 EndMax(const IntervalVar* const var) const; + int64 EndValue(const IntervalVar* const var) const; + int64 PerformedMin(const IntervalVar* const var) const; + int64 PerformedMax(const IntervalVar* const var) const; + int64 PerformedValue(const IntervalVar* const var) const; + void SetStartMin(const IntervalVar* const var, int64 m); + void SetStartMax(const IntervalVar* const var, int64 m); + void SetStartRange(const IntervalVar* const var, int64 mi, int64 ma); + void SetStartValue(const IntervalVar* const var, int64 value); + void SetDurationMin(const IntervalVar* const var, int64 m); + void SetDurationMax(const IntervalVar* const var, int64 m); + void SetDurationRange(const IntervalVar* const var, int64 mi, int64 ma); + void SetDurationValue(const IntervalVar* const var, int64 value); + void SetEndMin(const IntervalVar* const var, int64 m); + void SetEndMax(const IntervalVar* const var, int64 m); + void SetEndRange(const IntervalVar* const var, int64 mi, int64 ma); + void SetEndValue(const IntervalVar* const var, int64 value); + void SetPerformedMin(const IntervalVar* const var, int64 m); + void SetPerformedMax(const IntervalVar* const var, int64 m); + void SetPerformedRange(const IntervalVar* const var, int64 mi, int64 ma); + void SetPerformedValue(const IntervalVar* const var, int64 value); - SequenceVarElement* Add(SequenceVar* const v); + SequenceVarElement* Add(SequenceVar* const var); void Add(const std::vector& vars); // Adds without checking if variable has been previously added. - SequenceVarElement* FastAdd(SequenceVar* const v); - const std::vector& ForwardSequence(const SequenceVar* const v) const; - const std::vector& BackwardSequence(const SequenceVar* const v) const; - const std::vector& Unperformed(const SequenceVar* const v) const; - void SetSequence(const SequenceVar* const v, + SequenceVarElement* FastAdd(SequenceVar* const var); + const std::vector& ForwardSequence(const SequenceVar* const var) const; + const std::vector& BackwardSequence(const SequenceVar* const var) const; + const std::vector& Unperformed(const SequenceVar* const var) const; + void SetSequence(const SequenceVar* const var, const std::vector& forward_sequence, const std::vector& backward_sequence, const std::vector& unperformed); - void SetForwardSequence(const SequenceVar* const v, + void SetForwardSequence(const SequenceVar* const var, const std::vector& forward_sequence); - void SetBackwardSequence(const SequenceVar* const v, + void SetBackwardSequence(const SequenceVar* const var, const std::vector& backward_sequence); - void SetUnperformed(const SequenceVar* const v, + void SetUnperformed(const SequenceVar* const var, const std::vector& unperformed); - void Activate(const IntVar* const v); - void Deactivate(const IntVar* const v); - bool Activated(const IntVar* const v) const; + void Activate(const IntVar* const var); + void Deactivate(const IntVar* const var); + bool Activated(const IntVar* const var) const; - void Activate(const IntervalVar* const v); - void Deactivate(const IntervalVar* const v); - bool Activated(const IntervalVar* const v) const; + void Activate(const IntervalVar* const var); + void Deactivate(const IntervalVar* const var); + bool Activated(const IntervalVar* const var) const; - void Activate(const SequenceVar* const v); - void Deactivate(const SequenceVar* const v); - bool Activated(const SequenceVar* const v) const; + void Activate(const SequenceVar* const var); + void Deactivate(const SequenceVar* const var); + bool Activated(const SequenceVar* const var) const; void ActivateObjective(); void DeactivateObjective(); @@ -5172,6 +5209,9 @@ class Assignment : public PropagationBaseObject { bool Contains(const IntervalVar* const var) const; bool Contains(const SequenceVar* const var) const; // Copies the intersection of the two assignments to the current assignment. + void CopyIntersection(const Assignment* assignment); + // Copies 'assignment' to the current assignment, clearing its previous + // content. void Copy(const Assignment* assignment); // TODO(user): Add iterators on elements to avoid exposing container class. @@ -5210,6 +5250,16 @@ class Assignment : public PropagationBaseObject { std::ostream& operator<<(std::ostream& out, const Assignment& assignment); // NOLINT +// Given a "source_assignment", clears the "target_assignment" and adds +// all IntVars in "target_vars", with the values of the variables set according +// to the corresponding values of "source_vars" in "source_assignment". +// source_vars and target_vars must have the same number of elements. +// The source and target assignments can belong to different Solvers. +void SetAssignmentFromAssignment(Assignment* target_assignment, + const std::vector& target_vars, + const Assignment* source_assignment, + const std::vector& source_vars); + // ---------- Pack Constraint ---------- class Pack : public Constraint { @@ -5233,14 +5283,14 @@ class Pack : public Constraint { // This dimension imposes that for all bins b, the weighted sum // (weights->Run(i)) of all objects i assigned to 'b' is less or - // equal to 'bounds[b]'. Ownership of the callback is transfered to + // equal to 'bounds[b]'. Ownership of the callback is transferred to // the pack constraint. void AddWeightedSumLessOrEqualConstantDimension( Solver::IndexEvaluator1 weights, const std::vector& bounds); // This dimension imposes that for all bins b, the weighted sum // (weights->Run(i, b) of all objects i assigned to 'b' is less or - // equal to 'bounds[b]'. Ownership of the callback is transfered to + // equal to 'bounds[b]'. Ownership of the callback is transferred to // the pack constraint. void AddWeightedSumLessOrEqualConstantDimension( Solver::IndexEvaluator2 weights, const std::vector& bounds); @@ -5257,7 +5307,7 @@ class Pack : public Constraint { // This dimension imposes: // forall b in bins, - // sum (i in items: weight[i] * is_assigned(i, b)) <= capacities[b] + // sum (i in items: usage[i] * is_assigned(i, b)) <= capacity[b] // where is_assigned(i, b) is true if and only if item i is assigned // to the bin b. // @@ -5265,8 +5315,7 @@ class Pack : public Constraint { // the same item on parallel dimensions with an allowed assignment // constraint. void AddSumVariableWeightsLessOrEqualConstantDimension( - const std::vector& weights, - const std::vector& capacities); + const std::vector& usage, const std::vector& capacity); // This dimension enforces that cost_var == sum of weights[i] for // all objects 'i' assigned to a bin. @@ -5311,13 +5360,13 @@ class Pack : public Constraint { const int bins_; std::vector dims_; std::unique_ptr unprocessed_; - std::vector> forced_; - std::vector> removed_; + std::vector > forced_; + std::vector > removed_; std::vector holes_; uint64 stamp_; Demon* demon_; - std::vector> to_set_; - std::vector> to_unset_; + std::vector > to_set_; + std::vector > to_unset_; bool in_process_; }; @@ -5335,9 +5384,9 @@ class DisjunctiveConstraint : public Constraint { // Add a transition time between intervals. It forces the distance between // the end of interval a and start of interval b that follows it to be at - // least transit_evaluator(a, b). This function must always return + // least transition_time(a, b). This function must always return // a positive or null value. - void SetTransitionTime(Solver::IndexEvaluator2 transit_evaluator); + void SetTransitionTime(Solver::IndexEvaluator2 transition_time); int64 TransitionTime(int before_index, int after_index) { DCHECK(transition_time_); diff --git a/ortools/constraint_solver/constraint_solveri.h b/ortools/constraint_solver/constraint_solveri.h index 5808ec2700..6acd6f3375 100644 --- a/ortools/constraint_solver/constraint_solveri.h +++ b/ortools/constraint_solver/constraint_solveri.h @@ -443,12 +443,12 @@ class RevBitSet { explicit RevBitSet(int64 size); ~RevBitSet(); - // Sets the 'pos' bit. - void SetToOne(Solver* const solver, int64 pos); - // Erases the 'pos' bit. - void SetToZero(Solver* const solver, int64 pos); - // Returns whether the 'pos' bit is set. - bool IsSet(int64 pos) const; + // Sets the 'index' bit. + void SetToOne(Solver* const solver, int64 index); + // Erases the 'index' bit. + void SetToZero(Solver* const solver, int64 index); + // Returns whether the 'index' bit is set. + bool IsSet(int64 index) const; // Returns the number of bits set to one. int64 Cardinality() const; // Is bitset null? @@ -542,7 +542,7 @@ Demon* MakeConstraintDemon0(Solver* const s, T* const ct, void (T::*method)(), template std::string ParameterDebugString(P param) { - return StrCat(param); + return absl::StrCat(param); } // Support limited to pointers to classes which define DebugString(). @@ -563,7 +563,7 @@ class CallMethod1 : public Demon { void Run(Solver* const s) override { (constraint_->*method_)(param1_); } std::string DebugString() const override { - return StrCat("CallMethod_", name_, "(", constraint_->DebugString(), + return absl::StrCat("CallMethod_", name_, "(", constraint_->DebugString(), ", ", ParameterDebugString(param1_), ")"); } @@ -719,7 +719,7 @@ class DelayedCallMethod1 : public Demon { } std::string DebugString() const override { - return StrCat("DelayedCallMethod_", name_, "(", + return absl::StrCat("DelayedCallMethod_", name_, "(", constraint_->DebugString(), ", ", ParameterDebugString(param1_), ")"); } @@ -1293,6 +1293,11 @@ class PathOperator : public IntVarLocalSearchOperator { virtual int64 GetBaseNodeRestartPosition(int base_index) { return StartNode(base_index); } + // Set the next base to increment on next iteration. All base > base_index + // will be reset to their start value. + virtual void SetNextBaseToIncrement(int64 base_index) { + next_base_to_increment_ = base_index; + } int64 OldNext(int64 node_index) const { DCHECK(!IsPathEnd(node_index)); @@ -1341,6 +1346,7 @@ class PathOperator : public IntVarLocalSearchOperator { const int number_of_nexts_; const bool ignore_path_vars_; + int next_base_to_increment_; private: void OnStart() override; @@ -1364,7 +1370,7 @@ class PathOperator : public IntVarLocalSearchOperator { void InitializePathStarts(); void InitializeInactives(); void InitializeBaseNodes(); - bool CheckChainValidity(int64 chain_start, int64 chain_end, + bool CheckChainValidity(int64 before_chain, int64 chain_end, int64 exclude) const; void Synchronize(); @@ -1457,6 +1463,8 @@ class LocalSearchFilter : public BaseObject { class IntVarLocalSearchFilter : public LocalSearchFilter { public: + IntVarLocalSearchFilter(const std::vector& vars, + Solver::ObjectiveWatcher objective_callback); explicit IntVarLocalSearchFilter(const std::vector& vars); ~IntVarLocalSearchFilter() override; // This method should not be overridden. Override OnSynchronize() instead @@ -1473,6 +1481,10 @@ class IntVarLocalSearchFilter : public LocalSearchFilter { return *index != kUnassigned; } + virtual void InjectObjectiveValue(int64 objective_value) { + injected_objective_value_ = objective_value; + } + // Add variables to "track" to the filter. void AddVars(const std::vector& vars); int Size() const { return vars_.size(); } @@ -1487,12 +1499,23 @@ class IntVarLocalSearchFilter : public LocalSearchFilter { virtual void OnSynchronize(const Assignment* delta) {} void SynchronizeOnAssignment(const Assignment* assignment); + bool CanPropagateObjectiveValue() const { + return objective_callback_ != nullptr; + } + void PropagateObjectiveValue(int64 objective_value) { + if (objective_callback_ != nullptr) { + objective_callback_(objective_value); + } + } + int64 injected_objective_value_; + private: std::vector vars_; std::vector values_; std::vector var_synced_; std::vector var_index_to_index_; static const int kUnassigned; + Solver::ObjectiveWatcher objective_callback_; }; // ---------- PropagationMonitor ---------- @@ -1603,7 +1626,7 @@ class BooleanVar : public IntVar { void SetMin(int64 m) override; int64 Max() const override { return (value_ != 0); } void SetMax(int64 m) override; - void SetRange(int64 l, int64 u) override; + void SetRange(int64 mi, int64 ma) override; bool Bound() const override { return (value_ != kUnboundBooleanVarValue); } int64 Value() const override { CHECK_NE(value_, kUnboundBooleanVarValue) << "variable is not bound"; @@ -2764,7 +2787,7 @@ inline int64 PosIntDivUp(int64 e, int64 v) { if (e >= 0) { return e % v == 0 ? e / v : e / v + 1; } else { - return -(-e / v); + return e / v; } } diff --git a/ortools/constraint_solver/constraints.cc b/ortools/constraint_solver/constraints.cc index bc8099f2c3..15e0c6355d 100644 --- a/ortools/constraint_solver/constraints.cc +++ b/ortools/constraint_solver/constraints.cc @@ -98,7 +98,7 @@ class FalseConstraint : public Constraint { void Post() override {} void InitialPropagate() override { solver()->Fail(); } std::string DebugString() const override { - return StrCat("FalseConstraint(", explanation_, ")"); + return absl::StrCat("FalseConstraint(", explanation_, ")"); } void Accept(ModelVisitor* const visitor) const override { diff --git a/ortools/constraint_solver/count_cst.cc b/ortools/constraint_solver/count_cst.cc index 3f42f9eea5..9e4d7321c6 100644 --- a/ortools/constraint_solver/count_cst.cc +++ b/ortools/constraint_solver/count_cst.cc @@ -22,43 +22,45 @@ #include "ortools/base/logging.h" #include "ortools/base/stringprintf.h" #include "ortools/base/join.h" +#include "ortools/base/join.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/string_array.h" namespace operations_research { -Constraint* Solver::MakeCount(const std::vector& vars, int64 v, - int64 c) { +Constraint* Solver::MakeCount(const std::vector& vars, int64 value, + int64 max_count) { std::vector tmp_sum; for (int i = 0; i < vars.size(); ++i) { - if (vars[i]->Contains(v)) { + if (vars[i]->Contains(value)) { if (vars[i]->Bound()) { - c--; + max_count--; } else { - tmp_sum.push_back(MakeIsEqualCstVar(vars[i], v)); + tmp_sum.push_back(MakeIsEqualCstVar(vars[i], value)); } } } - return MakeSumEquality(tmp_sum, c); + return MakeSumEquality(tmp_sum, max_count); } -Constraint* Solver::MakeCount(const std::vector& vars, int64 v, - IntVar* c) { - if (c->Bound()) { - return MakeCount(vars, v, c->Min()); +Constraint* Solver::MakeCount(const std::vector& vars, int64 value, + IntVar* max_count) { + if (max_count->Bound()) { + return MakeCount(vars, value, max_count->Min()); } else { std::vector tmp_sum; int64 num_vars_bound_to_v = 0; for (int i = 0; i < vars.size(); ++i) { - if (vars[i]->Contains(v)) { + if (vars[i]->Contains(value)) { if (vars[i]->Bound()) { ++num_vars_bound_to_v; } else { - tmp_sum.push_back(MakeIsEqualCstVar(vars[i], v)); + tmp_sum.push_back(MakeIsEqualCstVar(vars[i], value)); } } } - return MakeSumEquality(tmp_sum, MakeSum(c, -num_vars_bound_to_v)->Var()); + return MakeSumEquality(tmp_sum, + MakeSum(max_count, -num_vars_bound_to_v)->Var()); } } @@ -163,15 +165,15 @@ class Distribute : public Constraint { void Post() override; void InitialPropagate() override; - void OneBound(int vindex); - void OneDomain(int vindex); + void OneBound(int index); + void OneDomain(int index); void CountVar(int cindex); void CardMin(int cindex); void CardMax(int cindex); std::string DebugString() const override { return StringPrintf("Distribute(vars = [%s], values = [%s], cards = [%s])", JoinDebugStringPtr(vars_, ", ").c_str(), - strings::Join(values_, ", ").c_str(), + absl::StrJoin(values_, ", ").c_str(), JoinDebugStringPtr(cards_, ", ").c_str()); } @@ -327,8 +329,8 @@ class FastDistribute : public Constraint { void Post() override; void InitialPropagate() override; - void OneBound(int vindex); - void OneDomain(int vindex); + void OneBound(int index); + void OneDomain(int index); void CountVar(int card_index); void CardMin(int card_index); void CardMax(int card_index); @@ -515,8 +517,8 @@ class BoundedDistribute : public Constraint { void Post() override; void InitialPropagate() override; - void OneBound(int vindex); - void OneDomain(int vindex); + void OneBound(int index); + void OneDomain(int index); void CountVar(int card_index); void CardMin(int card_index); void CardMax(int card_index); @@ -592,9 +594,9 @@ std::string BoundedDistribute::DebugString() const { "BoundedDistribute([%s], values = [%s], card_min = [%s], card_max = " "[%s]", JoinDebugStringPtr(vars_, ", ").c_str(), - strings::Join(values_, ", ").c_str(), - strings::Join(card_min_, ", ").c_str(), - strings::Join(card_max_, ", ").c_str()); + absl::StrJoin(values_, ", ").c_str(), + absl::StrJoin(card_min_, ", ").c_str(), + absl::StrJoin(card_max_, ", ").c_str()); } void BoundedDistribute::Post() { @@ -719,8 +721,8 @@ class BoundedFastDistribute : public Constraint { void Post() override; void InitialPropagate() override; - void OneBound(int vindex); - void OneDomain(int vindex); + void OneBound(int index); + void OneDomain(int index); void CountVar(int card_index); void CardMin(int card_index); void CardMax(int card_index); @@ -791,8 +793,8 @@ std::string BoundedFastDistribute::DebugString() const { return StringPrintf( "BoundedFastDistribute([%s], card_min = [%s], card_max = [%s]", JoinDebugStringPtr(vars_, ", ").c_str(), - strings::Join(card_min_, ", ").c_str(), - strings::Join(card_max_, ", ").c_str()); + absl::StrJoin(card_min_, ", ").c_str(), + absl::StrJoin(card_max_, ", ").c_str()); } void BoundedFastDistribute::Post() { diff --git a/ortools/constraint_solver/csharp/constraint_solver.i b/ortools/constraint_solver/csharp/constraint_solver.i index b1c8c3c1b2..b7935d679d 100644 --- a/ortools/constraint_solver/csharp/constraint_solver.i +++ b/ortools/constraint_solver/csharp/constraint_solver.i @@ -882,7 +882,7 @@ PROTO2_RETURN(operations_research::SearchLimitParameters, PROTO_INPUT(operations_research::CpModel, Google.OrTools.ConstraintSolver.CpModel, - proto) + model_proto) PROTO2_RETURN(operations_research::CpModel, Google.OrTools.ConstraintSolver.CpModel) diff --git a/ortools/constraint_solver/demon_profiler.cc b/ortools/constraint_solver/demon_profiler.cc index 8f7d5a59f5..e0bde1fcde 100644 --- a/ortools/constraint_solver/demon_profiler.cc +++ b/ortools/constraint_solver/demon_profiler.cc @@ -24,6 +24,7 @@ #include "ortools/base/logging.h" #include "ortools/base/stringprintf.h" #include "ortools/base/file.h" +#include "ortools/base/time_support.h" #include "ortools/base/stl_util.h" #include "ortools/base/hash.h" #include "ortools/constraint_solver/constraint_solver.h" @@ -51,7 +52,7 @@ class DemonProfiler : public PropagationMonitor { : PropagationMonitor(solver), active_constraint_(nullptr), active_demon_(nullptr), - start_time_ns_(base::GetCurrentTimeNanos()) {} + start_time_ns_(absl::GetCurrentTimeNanos()) {} ~DemonProfiler() override { STLDeleteContainerPairSecondPointers(constraint_map_.begin(), @@ -61,7 +62,7 @@ class DemonProfiler : public PropagationMonitor { // In microseconds. // TODO(user): rename and return nanoseconds. int64 CurrentTime() const { - return (base::GetCurrentTimeNanos() - start_time_ns_) / 1000; + return (absl::GetCurrentTimeNanos() - start_time_ns_) / 1000; } void BeginConstraintInitialPropagation( diff --git a/ortools/constraint_solver/element.cc b/ortools/constraint_solver/element.cc index a4b537d762..e9b18d5224 100644 --- a/ortools/constraint_solver/element.cc +++ b/ortools/constraint_solver/element.cc @@ -23,9 +23,9 @@ #include "ortools/base/logging.h" #include "ortools/base/stringprintf.h" #include "ortools/base/join.h" +#include "ortools/base/join.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" -#include "ortools/graph/iterators.h" #include "ortools/util/range_minimum_query.h" #include "ortools/util/string_array.h" @@ -67,7 +67,7 @@ class VectorGreater { class BaseIntExprElement : public BaseIntExpr { public: - BaseIntExprElement(Solver* const s, IntVar* const expr); + BaseIntExprElement(Solver* const s, IntVar* const e); ~BaseIntExprElement() override {} int64 Min() const override; int64 Max() const override; @@ -261,7 +261,7 @@ class IntElementConstraint : public CastConstraint { std::string DebugString() const override { return StringPrintf("IntElementConstraint(%s, %s, %s)", - strings::Join(values_, ", ").c_str(), + absl::StrJoin(values_, ", ").c_str(), index_->DebugString().c_str(), target_var_->DebugString().c_str()); } @@ -302,7 +302,7 @@ class IntExprElement : public BaseIntExprElement { expr_->name().c_str()); } else { return StringPrintf("IntElement(%s, %s)", - strings::Join(values_, ", ").c_str(), + absl::StrJoin(values_, ", ").c_str(), expr_->name().c_str()); } } @@ -314,7 +314,7 @@ class IntExprElement : public BaseIntExprElement { expr_->DebugString().c_str()); } else { return StringPrintf("IntElement(%s, %s)", - strings::Join(values_, ", ").c_str(), + absl::StrJoin(values_, ", ").c_str(), expr_->DebugString().c_str()); } } @@ -472,12 +472,12 @@ class IncreasingIntExprElement : public BaseIntExpr { // TODO(user) : improve me, the previous test is not always true std::string name() const override { return StringPrintf("IntElement(%s, %s)", - strings::Join(values_, ", ").c_str(), + absl::StrJoin(values_, ", ").c_str(), index_->name().c_str()); } std::string DebugString() const override { return StringPrintf("IntElement(%s, %s)", - strings::Join(values_, ", ").c_str(), + absl::StrJoin(values_, ", ").c_str(), index_->DebugString().c_str()); } @@ -679,7 +679,7 @@ namespace { class IntExprFunctionElement : public BaseIntExprElement { public: IntExprFunctionElement(Solver* const s, Solver::IndexEvaluator1 values, - IntVar* const expr); + IntVar* const e); ~IntExprFunctionElement() override; std::string name() const override { @@ -902,10 +902,10 @@ class IntIntExprFunctionElement : public BaseIntExpr { } int64 Min() const override; int64 Max() const override; - void Range(int64* mi, int64* ma) override; - void SetMin(int64 m) override; - void SetMax(int64 m) override; - void SetRange(int64 mi, int64 ma) override; + void Range(int64* lower_bound, int64* upper_bound) override; + void SetMin(int64 lower_bound) override; + void SetMax(int64 upper_bound) override; + void SetRange(int64 lower_bound, int64 upper_bound) override; bool Bound() const override { return expr1_->Bound() && expr2_->Bound(); } // TODO(user) : improve me, the previous test is not always true void WhenRange(Demon* d) override { @@ -1206,7 +1206,7 @@ class IntExprEvaluatorElementCt : public CastConstraint { public: IntExprEvaluatorElementCt(Solver* const s, Solver::Int64ToIntVar evaluator, int64 range_start, int64 range_end, - IntVar* const index, IntVar* const target); + IntVar* const index, IntVar* const target_var); ~IntExprEvaluatorElementCt() override {} void Post() override; diff --git a/ortools/constraint_solver/expr_array.cc b/ortools/constraint_solver/expr_array.cc index efc06195ae..be694417b9 100644 --- a/ortools/constraint_solver/expr_array.cc +++ b/ortools/constraint_solver/expr_array.cc @@ -27,6 +27,7 @@ #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" +#include "ortools/base/join.h" #include "ortools/util/saturated_arithmetic.h" #include "ortools/util/string_array.h" @@ -1981,7 +1982,7 @@ class BooleanScalProdLessConstant : public Constraint { std::string DebugString() const override { return StringPrintf("BooleanScalProd([%s], [%s]) <= %" GG_LL_FORMAT "d)", JoinDebugStringPtr(vars_, ", ").c_str(), - strings::Join(coefs_, ", ").c_str(), upper_bound_); + absl::StrJoin(coefs_, ", ").c_str(), upper_bound_); } void Accept(ModelVisitor* const visitor) const override { @@ -2102,7 +2103,7 @@ class PositiveBooleanScalProdEqVar : public CastConstraint { std::string DebugString() const override { return StringPrintf("PositiveBooleanScal([%s], [%s]) == %s", JoinDebugStringPtr(vars_, ", ").c_str(), - strings::Join(coefs_, ", ").c_str(), + absl::StrJoin(coefs_, ", ").c_str(), target_var_->DebugString().c_str()); } @@ -2217,7 +2218,7 @@ class PositiveBooleanScalProd : public BaseIntExpr { std::string DebugString() const override { return StringPrintf("PositiveBooleanScalProd([%s], [%s])", JoinDebugStringPtr(vars_, ", ").c_str(), - strings::Join(coefs_, ", ").c_str()); + absl::StrJoin(coefs_, ", ").c_str()); } void WhenRange(Demon* d) override { @@ -2348,7 +2349,7 @@ class PositiveBooleanScalProdEqCst : public Constraint { return StringPrintf("PositiveBooleanScalProd([%s], [%s]) == %" GG_LL_FORMAT "d", JoinDebugStringPtr(vars_, ", ").c_str(), - strings::Join(coefs_, ", ").c_str(), constant_); + absl::StrJoin(coefs_, ", ").c_str(), constant_); } void Accept(ModelVisitor* const visitor) const override { diff --git a/ortools/constraint_solver/expr_cst.cc b/ortools/constraint_solver/expr_cst.cc index 103480b9b8..cea71ffdb7 100644 --- a/ortools/constraint_solver/expr_cst.cc +++ b/ortools/constraint_solver/expr_cst.cc @@ -24,6 +24,7 @@ #include "ortools/base/logging.h" #include "ortools/base/stringprintf.h" #include "ortools/base/join.h" +#include "ortools/base/join.h" #include "ortools/base/stl_util.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" @@ -462,7 +463,7 @@ IntVar* Solver::MakeIsEqualCstVar(IntExpr* const var, int64 value) { if (IsADifference(var, &left, &right)) { return MakeIsEqualVar(left, MakeSum(right, value)); } - if (var->Max() - var->Min() == 1) { + if (CapSub(var->Max(), var->Min()) == 1) { if (value == var->Min()) { return MakeDifference(value + 1, var)->Var(); } else if (value == var->Max()) { @@ -486,13 +487,13 @@ Constraint* Solver::MakeIsEqualCstCt(IntExpr* const var, int64 value, CHECK_EQ(this, var->solver()); CHECK_EQ(this, boolvar->solver()); if (value == var->Min()) { - if (var->Max() - var->Min() == 1) { + if (CapSub(var->Max(), var->Min()) == 1) { return MakeEquality(MakeDifference(value + 1, var), boolvar); } return MakeIsLessOrEqualCstCt(var, value, boolvar); } if (value == var->Max()) { - if (var->Max() - var->Min() == 1) { + if (CapSub(var->Max(), var->Min()) == 1) { return MakeEquality(MakeSum(var, -value + 1), boolvar); } return MakeIsGreaterOrEqualCstCt(var, value, boolvar); @@ -919,24 +920,24 @@ int64 ExtractExprProductCoeff(IntExpr** expr) { } } // namespace -Constraint* Solver::MakeBetweenCt(IntExpr* e, int64 l, int64 u) { - DCHECK_EQ(this, e->solver()); +Constraint* Solver::MakeBetweenCt(IntExpr* expr, int64 l, int64 u) { + DCHECK_EQ(this, expr->solver()); // Catch empty and singleton intervals. if (l >= u) { if (l > u) return MakeFalseConstraint(); - return MakeEquality(e, l); + return MakeEquality(expr, l); } int64 emin = 0; int64 emax = 0; - e->Range(&emin, &emax); + expr->Range(&emin, &emax); // Catch the trivial cases first. if (emax < l || emin > u) return MakeFalseConstraint(); if (emin >= l && emax <= u) return MakeTrueConstraint(); // Catch one-sided constraints. - if (emax <= u) return MakeGreaterOrEqual(e, l); - if (emin >= l) return MakeLessOrEqual(e, u); + if (emax <= u) return MakeGreaterOrEqual(expr, l); + if (emin >= l) return MakeLessOrEqual(expr, u); // Simplify the common factor, if any. - int64 coeff = ExtractExprProductCoeff(&e); + int64 coeff = ExtractExprProductCoeff(&expr); if (coeff != 1) { CHECK_NE(coeff, 0); // Would have been caught by the trivial cases already. if (coeff < 0) { @@ -945,15 +946,15 @@ Constraint* Solver::MakeBetweenCt(IntExpr* e, int64 l, int64 u) { l = -l; coeff = -coeff; } - return MakeBetweenCt(e, PosIntDivUp(l, coeff), PosIntDivDown(u, coeff)); + return MakeBetweenCt(expr, PosIntDivUp(l, coeff), PosIntDivDown(u, coeff)); } else { // No further reduction is possible. - return RevAlloc(new BetweenCt(this, e, l, u)); + return RevAlloc(new BetweenCt(this, expr, l, u)); } } -Constraint* Solver::MakeNotBetweenCt(IntExpr* e, int64 l, int64 u) { - DCHECK_EQ(this, e->solver()); +Constraint* Solver::MakeNotBetweenCt(IntExpr* expr, int64 l, int64 u) { + DCHECK_EQ(this, expr->solver()); // Catch empty interval. if (l > u) { return MakeTrueConstraint(); @@ -961,15 +962,16 @@ Constraint* Solver::MakeNotBetweenCt(IntExpr* e, int64 l, int64 u) { int64 emin = 0; int64 emax = 0; - e->Range(&emin, &emax); + expr->Range(&emin, &emax); // Catch the trivial cases first. if (emax < l || emin > u) return MakeTrueConstraint(); if (emin >= l && emax <= u) return MakeFalseConstraint(); // Catch one-sided constraints. - if (emin >= l) return MakeGreater(e, u); - if (emax <= u) return MakeLess(e, l); - // TODO(user): Add back simplification code if e is constant * other_expr. - return RevAlloc(new NotBetweenCt(this, e, l, u)); + if (emin >= l) return MakeGreater(expr, u); + if (emax <= u) return MakeLess(expr, l); + // TODO(user): Add back simplification code if expr is constant * + // other_expr. + return RevAlloc(new NotBetweenCt(this, expr, l, u)); } // ----- is_between_cst Constraint ----- @@ -1048,26 +1050,26 @@ class IsBetweenCt : public Constraint { }; } // namespace -Constraint* Solver::MakeIsBetweenCt(IntExpr* e, int64 l, int64 u, +Constraint* Solver::MakeIsBetweenCt(IntExpr* expr, int64 l, int64 u, IntVar* const b) { - CHECK_EQ(this, e->solver()); + CHECK_EQ(this, expr->solver()); CHECK_EQ(this, b->solver()); // Catch empty and singleton intervals. if (l >= u) { if (l > u) return MakeEquality(b, Zero()); - return MakeIsEqualCstCt(e, l, b); + return MakeIsEqualCstCt(expr, l, b); } int64 emin = 0; int64 emax = 0; - e->Range(&emin, &emax); + expr->Range(&emin, &emax); // Catch the trivial cases first. if (emax < l || emin > u) return MakeEquality(b, Zero()); if (emin >= l && emax <= u) return MakeEquality(b, 1); // Catch one-sided constraints. - if (emax <= u) return MakeIsGreaterOrEqualCstCt(e, l, b); - if (emin >= l) return MakeIsLessOrEqualCstCt(e, u, b); + if (emax <= u) return MakeIsGreaterOrEqualCstCt(expr, l, b); + if (emin >= l) return MakeIsLessOrEqualCstCt(expr, u, b); // Simplify the common factor, if any. - int64 coeff = ExtractExprProductCoeff(&e); + int64 coeff = ExtractExprProductCoeff(&expr); if (coeff != 1) { CHECK_NE(coeff, 0); // Would have been caught by the trivial cases already. if (coeff < 0) { @@ -1076,11 +1078,11 @@ Constraint* Solver::MakeIsBetweenCt(IntExpr* e, int64 l, int64 u, l = -l; coeff = -coeff; } - return MakeIsBetweenCt(e, PosIntDivUp(l, coeff), PosIntDivDown(u, coeff), + return MakeIsBetweenCt(expr, PosIntDivUp(l, coeff), PosIntDivDown(u, coeff), b); } else { // No further reduction is possible. - return RevAlloc(new IsBetweenCt(this, e, l, u, b)); + return RevAlloc(new IsBetweenCt(this, expr, l, u, b)); } } @@ -1112,7 +1114,7 @@ class MemberCt : public Constraint { std::string DebugString() const override { return StringPrintf("Member(%s, %s)", var_->DebugString().c_str(), - strings::Join(values_, ", ").c_str()); + absl::StrJoin(values_, ", ").c_str()); } void Accept(ModelVisitor* const visitor) const override { @@ -1143,7 +1145,7 @@ class NotMemberCt : public Constraint { std::string DebugString() const override { return StringPrintf("NotMember(%s, %s)", var_->DebugString().c_str(), - strings::Join(values_, ", ").c_str()); + absl::StrJoin(values_, ", ").c_str()); } void Accept(ModelVisitor* const visitor) const override { @@ -1345,7 +1347,7 @@ class IsMemberCt : public Constraint { std::string DebugString() const override { return StringPrintf("IsMemberCt(%s, %s, %s)", var_->DebugString().c_str(), - strings::Join(values_, ", ").c_str(), + absl::StrJoin(values_, ", ").c_str(), boolvar_->DebugString().c_str()); } diff --git a/ortools/constraint_solver/expressions.cc b/ortools/constraint_solver/expressions.cc index 7ab8edd62e..c080ec876d 100644 --- a/ortools/constraint_solver/expressions.cc +++ b/ortools/constraint_solver/expressions.cc @@ -1331,7 +1331,7 @@ class DomainIntVar : public IntVar { void SetMin(int64 m) override; int64 Max() const override { return max_.Value(); } void SetMax(int64 m) override; - void SetRange(int64 l, int64 u) override; + void SetRange(int64 mi, int64 ma) override; void SetValue(int64 v) override; bool Bound() const override { return (min_.Value() == max_.Value()); } int64 Value() const override { @@ -1393,7 +1393,7 @@ class DomainIntVar : public IntVar { return cache->Var(); } else { if (value_watcher_ == nullptr) { - if (Max() - Min() <= 256) { + if (CapSub(Max(), Min()) <= 256) { solver()->SaveAndSetValue( reinterpret_cast(&value_watcher_), reinterpret_cast( @@ -1466,7 +1466,7 @@ class DomainIntVar : public IntVar { return cache->Var(); } else { if (bound_watcher_ == nullptr) { - if (Max() - Min() <= 256) { + if (CapSub(Max(), Min()) <= 256) { solver()->SaveAndSetValue( reinterpret_cast(&bound_watcher_), reinterpret_cast(solver()->RevAlloc( @@ -1492,7 +1492,7 @@ class DomainIntVar : public IntVar { Constraint* SetIsGreaterOrEqual(const std::vector& values, const std::vector& vars) { if (bound_watcher_ == nullptr) { - if (Max() - Min() <= 256) { + if (CapSub(Max(), Min()) <= 256) { solver()->SaveAndSetValue( reinterpret_cast(&bound_watcher_), reinterpret_cast(solver()->RevAlloc( @@ -1531,7 +1531,8 @@ class DomainIntVar : public IntVar { void CleanInProcess(); uint64 Size() const override { if (bits_ != nullptr) return bits_->Size(); - return (max_.Value() - min_.Value() + 1); + return (static_cast(max_.Value()) - + static_cast(min_.Value()) + 1); } bool Contains(int64 v) const override { if (v < min_.Value() || v > max_.Value()) return false; @@ -6567,65 +6568,66 @@ void Solver::InitCachedIntConstants() { } } -IntExpr* Solver::MakeSum(IntExpr* const l, IntExpr* const r) { - CHECK_EQ(this, l->solver()); - CHECK_EQ(this, r->solver()); - if (r->Bound()) { - return MakeSum(l, r->Min()); +IntExpr* Solver::MakeSum(IntExpr* const left, IntExpr* const right) { + CHECK_EQ(this, left->solver()); + CHECK_EQ(this, right->solver()); + if (right->Bound()) { + return MakeSum(left, right->Min()); } - if (l->Bound()) { - return MakeSum(r, l->Min()); + if (left->Bound()) { + return MakeSum(right, left->Min()); } - if (l == r) { - return MakeProd(l, 2); + if (left == right) { + return MakeProd(left, 2); } - IntExpr* cache = - model_cache_->FindExprExprExpression(l, r, ModelCache::EXPR_EXPR_SUM); + IntExpr* cache = model_cache_->FindExprExprExpression( + left, right, ModelCache::EXPR_EXPR_SUM); if (cache == nullptr) { - cache = - model_cache_->FindExprExprExpression(r, l, ModelCache::EXPR_EXPR_SUM); + cache = model_cache_->FindExprExprExpression(right, left, + ModelCache::EXPR_EXPR_SUM); } if (cache != nullptr) { return cache; } else { IntExpr* const result = - AddOverflows(l->Max(), r->Max()) || AddOverflows(l->Min(), r->Min()) - ? RegisterIntExpr(RevAlloc(new SafePlusIntExpr(this, l, r))) - : RegisterIntExpr(RevAlloc(new PlusIntExpr(this, l, r))); - model_cache_->InsertExprExprExpression(result, l, r, + AddOverflows(left->Max(), right->Max()) || + AddOverflows(left->Min(), right->Min()) + ? RegisterIntExpr(RevAlloc(new SafePlusIntExpr(this, left, right))) + : RegisterIntExpr(RevAlloc(new PlusIntExpr(this, left, right))); + model_cache_->InsertExprExprExpression(result, left, right, ModelCache::EXPR_EXPR_SUM); return result; } } -IntExpr* Solver::MakeSum(IntExpr* const e, int64 v) { - CHECK_EQ(this, e->solver()); - if (e->Bound()) { - return MakeIntConst(e->Min() + v); +IntExpr* Solver::MakeSum(IntExpr* const expr, int64 value) { + CHECK_EQ(this, expr->solver()); + if (expr->Bound()) { + return MakeIntConst(expr->Min() + value); } - if (v == 0) { - return e; + if (value == 0) { + return expr; } - IntExpr* result = - Cache()->FindExprConstantExpression(e, v, ModelCache::EXPR_CONSTANT_SUM); + IntExpr* result = Cache()->FindExprConstantExpression( + expr, value, ModelCache::EXPR_CONSTANT_SUM); if (result == nullptr) { - if (e->IsVar() && !AddOverflows(v, e->Max()) && - !AddOverflows(v, e->Min())) { - IntVar* const var = e->Var(); + if (expr->IsVar() && !AddOverflows(value, expr->Max()) && + !AddOverflows(value, expr->Min())) { + IntVar* const var = expr->Var(); switch (var->VarType()) { case DOMAIN_INT_VAR: { result = RegisterIntExpr(RevAlloc(new PlusCstDomainIntVar( - this, reinterpret_cast(var), v))); + this, reinterpret_cast(var), value))); break; } case CONST_VAR: { - result = RegisterIntExpr(MakeIntConst(var->Min() + v)); + result = RegisterIntExpr(MakeIntConst(var->Min() + value)); break; } case VAR_ADD_CST: { PlusCstVar* const add_var = reinterpret_cast(var); IntVar* const sub_var = add_var->SubVar(); - const int64 new_constant = v + add_var->Constant(); + const int64 new_constant = value + add_var->Constant(); if (new_constant == 0) { result = sub_var; } else { @@ -6644,7 +6646,7 @@ IntExpr* Solver::MakeSum(IntExpr* const e, int64 v) { case CST_SUB_VAR: { SubCstIntVar* const add_var = reinterpret_cast(var); IntVar* const sub_var = add_var->SubVar(); - const int64 new_constant = v + add_var->Constant(); + const int64 new_constant = value + add_var->Constant(); result = RegisterIntExpr( RevAlloc(new SubCstIntVar(this, sub_var, new_constant))); break; @@ -6653,36 +6655,37 @@ IntExpr* Solver::MakeSum(IntExpr* const e, int64 v) { OppIntVar* const add_var = reinterpret_cast(var); IntVar* const sub_var = add_var->SubVar(); result = - RegisterIntExpr(RevAlloc(new SubCstIntVar(this, sub_var, v))); + RegisterIntExpr(RevAlloc(new SubCstIntVar(this, sub_var, value))); break; } default: - result = RegisterIntExpr(RevAlloc(new PlusCstIntVar(this, var, v))); + result = + RegisterIntExpr(RevAlloc(new PlusCstIntVar(this, var, value))); } } else { - result = RegisterIntExpr(RevAlloc(new PlusIntCstExpr(this, e, v))); + result = RegisterIntExpr(RevAlloc(new PlusIntCstExpr(this, expr, value))); } - Cache()->InsertExprConstantExpression(result, e, v, + Cache()->InsertExprConstantExpression(result, expr, value, ModelCache::EXPR_CONSTANT_SUM); } return result; } -IntExpr* Solver::MakeDifference(IntExpr* const l, IntExpr* const r) { - CHECK_EQ(this, l->solver()); - CHECK_EQ(this, r->solver()); - if (l->Bound()) { - return MakeDifference(l->Min(), r); +IntExpr* Solver::MakeDifference(IntExpr* const left, IntExpr* const right) { + CHECK_EQ(this, left->solver()); + CHECK_EQ(this, right->solver()); + if (left->Bound()) { + return MakeDifference(left->Min(), right); } - if (r->Bound()) { - return MakeSum(l, -r->Min()); + if (right->Bound()) { + return MakeSum(left, -right->Min()); } IntExpr* sub_left = nullptr; IntExpr* sub_right = nullptr; int64 left_coef = 1; int64 right_coef = 1; - if (IsProduct(l, &sub_left, &left_coef) && - IsProduct(r, &sub_right, &right_coef)) { + if (IsProduct(left, &sub_left, &left_coef) && + IsProduct(right, &sub_right, &right_coef)) { const int64 abs_gcd = MathUtil::GCD64(std::abs(left_coef), std::abs(right_coef)); if (abs_gcd != 0 && abs_gcd != 1) { @@ -6692,41 +6695,42 @@ IntExpr* Solver::MakeDifference(IntExpr* const l, IntExpr* const r) { } } - IntExpr* result = - Cache()->FindExprExprExpression(l, r, ModelCache::EXPR_EXPR_DIFFERENCE); + IntExpr* result = Cache()->FindExprExprExpression( + left, right, ModelCache::EXPR_EXPR_DIFFERENCE); if (result == nullptr) { - if (!SubOverflows(l->Min(), r->Max()) && - !SubOverflows(l->Max(), r->Min())) { - result = RegisterIntExpr(RevAlloc(new SubIntExpr(this, l, r))); + if (!SubOverflows(left->Min(), right->Max()) && + !SubOverflows(left->Max(), right->Min())) { + result = RegisterIntExpr(RevAlloc(new SubIntExpr(this, left, right))); } else { - result = RegisterIntExpr(RevAlloc(new SafeSubIntExpr(this, l, r))); + result = RegisterIntExpr(RevAlloc(new SafeSubIntExpr(this, left, right))); } - Cache()->InsertExprExprExpression(result, l, r, + Cache()->InsertExprExprExpression(result, left, right, ModelCache::EXPR_EXPR_DIFFERENCE); } return result; } -// warning: this is 'v - e'. -IntExpr* Solver::MakeDifference(int64 v, IntExpr* const e) { - CHECK_EQ(this, e->solver()); - if (e->Bound()) { - return MakeIntConst(v - e->Min()); +// warning: this is 'value - expr'. +IntExpr* Solver::MakeDifference(int64 value, IntExpr* const expr) { + CHECK_EQ(this, expr->solver()); + if (expr->Bound()) { + return MakeIntConst(value - expr->Min()); } - if (v == 0) { - return MakeOpposite(e); + if (value == 0) { + return MakeOpposite(expr); } IntExpr* result = Cache()->FindExprConstantExpression( - e, v, ModelCache::EXPR_CONSTANT_DIFFERENCE); + expr, value, ModelCache::EXPR_CONSTANT_DIFFERENCE); if (result == nullptr) { - if (e->IsVar() && e->Min() != kint64min && !SubOverflows(v, e->Min()) && - !SubOverflows(v, e->Max())) { - IntVar* const var = e->Var(); + if (expr->IsVar() && expr->Min() != kint64min && + !SubOverflows(value, expr->Min()) && + !SubOverflows(value, expr->Max())) { + IntVar* const var = expr->Var(); switch (var->VarType()) { case VAR_ADD_CST: { PlusCstVar* const add_var = reinterpret_cast(var); IntVar* const sub_var = add_var->SubVar(); - const int64 new_constant = v - add_var->Constant(); + const int64 new_constant = value - add_var->Constant(); if (new_constant == 0) { result = sub_var; } else { @@ -6738,85 +6742,87 @@ IntExpr* Solver::MakeDifference(int64 v, IntExpr* const e) { case CST_SUB_VAR: { SubCstIntVar* const add_var = reinterpret_cast(var); IntVar* const sub_var = add_var->SubVar(); - const int64 new_constant = v - add_var->Constant(); + const int64 new_constant = value - add_var->Constant(); result = MakeSum(sub_var, new_constant); break; } case OPP_VAR: { OppIntVar* const add_var = reinterpret_cast(var); IntVar* const sub_var = add_var->SubVar(); - result = MakeSum(sub_var, v); + result = MakeSum(sub_var, value); break; } default: - result = RegisterIntExpr(RevAlloc(new SubCstIntVar(this, var, v))); + result = + RegisterIntExpr(RevAlloc(new SubCstIntVar(this, var, value))); } } else { - result = RegisterIntExpr(RevAlloc(new SubIntCstExpr(this, e, v))); + result = RegisterIntExpr(RevAlloc(new SubIntCstExpr(this, expr, value))); } - Cache()->InsertExprConstantExpression(result, e, v, + Cache()->InsertExprConstantExpression(result, expr, value, ModelCache::EXPR_CONSTANT_DIFFERENCE); } return result; } -IntExpr* Solver::MakeOpposite(IntExpr* const e) { - CHECK_EQ(this, e->solver()); - if (e->Bound()) { - return MakeIntConst(-e->Min()); +IntExpr* Solver::MakeOpposite(IntExpr* const expr) { + CHECK_EQ(this, expr->solver()); + if (expr->Bound()) { + return MakeIntConst(-expr->Min()); } - IntExpr* result = Cache()->FindExprExpression(e, ModelCache::EXPR_OPPOSITE); + IntExpr* result = + Cache()->FindExprExpression(expr, ModelCache::EXPR_OPPOSITE); if (result == nullptr) { - if (e->IsVar()) { - result = RegisterIntVar(RevAlloc(new OppIntExpr(this, e))->Var()); + if (expr->IsVar()) { + result = RegisterIntVar(RevAlloc(new OppIntExpr(this, expr))->Var()); } else { - result = RegisterIntExpr(RevAlloc(new OppIntExpr(this, e))); + result = RegisterIntExpr(RevAlloc(new OppIntExpr(this, expr))); } - Cache()->InsertExprExpression(result, e, ModelCache::EXPR_OPPOSITE); + Cache()->InsertExprExpression(result, expr, ModelCache::EXPR_OPPOSITE); } return result; } -IntExpr* Solver::MakeProd(IntExpr* const e, int64 v) { - CHECK_EQ(this, e->solver()); - IntExpr* result = - Cache()->FindExprConstantExpression(e, v, ModelCache::EXPR_CONSTANT_PROD); +IntExpr* Solver::MakeProd(IntExpr* const expr, int64 value) { + CHECK_EQ(this, expr->solver()); + IntExpr* result = Cache()->FindExprConstantExpression( + expr, value, ModelCache::EXPR_CONSTANT_PROD); if (result != nullptr) { return result; } else { - IntExpr* expr = nullptr; + IntExpr* m_expr = nullptr; int64 coefficient = 1; - if (IsProduct(e, &expr, &coefficient)) { - coefficient *= v; + if (IsProduct(expr, &m_expr, &coefficient)) { + coefficient *= value; } else { - expr = e; - coefficient = v; + m_expr = expr; + coefficient = value; } - if (expr->Bound()) { - return MakeIntConst(coefficient * expr->Min()); + if (m_expr->Bound()) { + return MakeIntConst(coefficient * m_expr->Min()); } else if (coefficient == 1) { - return expr; + return m_expr; } else if (coefficient == -1) { - return MakeOpposite(expr); + return MakeOpposite(m_expr); } else if (coefficient > 0) { - if (expr->Max() > kint64max / coefficient || - expr->Min() < kint64min / coefficient) { + if (m_expr->Max() > kint64max / coefficient || + m_expr->Min() < kint64min / coefficient) { result = RegisterIntExpr( - RevAlloc(new SafeTimesPosIntCstExpr(this, expr, coefficient))); + RevAlloc(new SafeTimesPosIntCstExpr(this, m_expr, coefficient))); } else { result = RegisterIntExpr( - RevAlloc(new TimesPosIntCstExpr(this, expr, coefficient))); + RevAlloc(new TimesPosIntCstExpr(this, m_expr, coefficient))); } } else if (coefficient == 0) { result = MakeIntConst(0); } else { // coefficient < 0. result = RegisterIntExpr( - RevAlloc(new TimesIntNegCstExpr(this, expr, coefficient))); + RevAlloc(new TimesIntNegCstExpr(this, m_expr, coefficient))); } - if (expr->IsVar() && !FLAGS_cp_disable_expression_optimization) { + if (m_expr->IsVar() && !FLAGS_cp_disable_expression_optimization) { result = result->Var(); } - Cache()->InsertExprConstantExpression(result, e, v, + Cache()->InsertExprConstantExpression(result, expr, value, ModelCache::EXPR_CONSTANT_PROD); return result; } @@ -6866,81 +6872,84 @@ void ExtractProduct(IntExpr** const expr, int64* const coefficient, } } // namespace -IntExpr* Solver::MakeProd(IntExpr* const l, IntExpr* const r) { - if (l->Bound()) { - return MakeProd(r, l->Min()); +IntExpr* Solver::MakeProd(IntExpr* const left, IntExpr* const right) { + if (left->Bound()) { + return MakeProd(right, left->Min()); } - if (r->Bound()) { - return MakeProd(l, r->Min()); + if (right->Bound()) { + return MakeProd(left, right->Min()); } // ----- Discover squares and powers ----- - IntExpr* left = l; - IntExpr* right = r; + IntExpr* m_left = left; + IntExpr* m_right = right; int64 left_exponant = 1; int64 right_exponant = 1; - ExtractPower(&left, &left_exponant); - ExtractPower(&right, &right_exponant); + ExtractPower(&m_left, &left_exponant); + ExtractPower(&m_right, &right_exponant); - if (left == right) { - return MakePower(left, left_exponant + right_exponant); + if (m_left == m_right) { + return MakePower(m_left, left_exponant + right_exponant); } // ----- Discover nested products ----- - left = l; - right = r; + m_left = left; + m_right = right; int64 coefficient = 1; bool modified = false; - ExtractProduct(&left, &coefficient, &modified); - ExtractProduct(&right, &coefficient, &modified); + ExtractProduct(&m_left, &coefficient, &modified); + ExtractProduct(&m_right, &coefficient, &modified); if (modified) { - return MakeProd(MakeProd(left, right), coefficient); + return MakeProd(MakeProd(m_left, m_right), coefficient); } // ----- Standard build ----- - CHECK_EQ(this, l->solver()); - CHECK_EQ(this, r->solver()); - IntExpr* result = - model_cache_->FindExprExprExpression(l, r, ModelCache::EXPR_EXPR_PROD); + CHECK_EQ(this, left->solver()); + CHECK_EQ(this, right->solver()); + IntExpr* result = model_cache_->FindExprExprExpression( + left, right, ModelCache::EXPR_EXPR_PROD); if (result == nullptr) { - result = - model_cache_->FindExprExprExpression(r, l, ModelCache::EXPR_EXPR_PROD); + result = model_cache_->FindExprExprExpression(right, left, + ModelCache::EXPR_EXPR_PROD); } if (result != nullptr) { return result; } - if (l->IsVar() && l->Var()->VarType() == BOOLEAN_VAR) { - if (r->Min() >= 0) { + if (left->IsVar() && left->Var()->VarType() == BOOLEAN_VAR) { + if (right->Min() >= 0) { result = RegisterIntExpr(RevAlloc(new TimesBooleanPosIntExpr( - this, reinterpret_cast(l), r))); + this, reinterpret_cast(left), right))); } else { - result = RegisterIntExpr(RevAlloc( - new TimesBooleanIntExpr(this, reinterpret_cast(l), r))); + result = RegisterIntExpr(RevAlloc(new TimesBooleanIntExpr( + this, reinterpret_cast(left), right))); } - } else if (r->IsVar() && - reinterpret_cast(r)->VarType() == BOOLEAN_VAR) { - if (l->Min() >= 0) { + } else if (right->IsVar() && + reinterpret_cast(right)->VarType() == BOOLEAN_VAR) { + if (left->Min() >= 0) { result = RegisterIntExpr(RevAlloc(new TimesBooleanPosIntExpr( - this, reinterpret_cast(r), l))); + this, reinterpret_cast(right), left))); } else { - result = RegisterIntExpr(RevAlloc( - new TimesBooleanIntExpr(this, reinterpret_cast(r), l))); + result = RegisterIntExpr(RevAlloc(new TimesBooleanIntExpr( + this, reinterpret_cast(right), left))); } - } else if (l->Min() >= 0 && r->Min() >= 0) { - if (CapProd(l->Max(), r->Max()) == kint64max) { // Potential overflow. - result = RegisterIntExpr(RevAlloc(new SafeTimesPosIntExpr(this, l, r))); + } else if (left->Min() >= 0 && right->Min() >= 0) { + if (CapProd(left->Max(), right->Max()) == + kint64max) { // Potential overflow. + result = + RegisterIntExpr(RevAlloc(new SafeTimesPosIntExpr(this, left, right))); } else { - result = RegisterIntExpr(RevAlloc(new TimesPosIntExpr(this, l, r))); + result = + RegisterIntExpr(RevAlloc(new TimesPosIntExpr(this, left, right))); } } else { - result = RegisterIntExpr(RevAlloc(new TimesIntExpr(this, l, r))); + result = RegisterIntExpr(RevAlloc(new TimesIntExpr(this, left, right))); } - model_cache_->InsertExprExprExpression(result, l, r, + model_cache_->InsertExprExprExpression(result, left, right, ModelCache::EXPR_EXPR_PROD); return result; } @@ -6983,32 +6992,32 @@ IntExpr* Solver::MakeDiv(IntExpr* const numerator, IntExpr* const denominator) { return result; } -IntExpr* Solver::MakeDiv(IntExpr* const e, int64 v) { - CHECK(e != nullptr); - CHECK_EQ(this, e->solver()); - if (e->Bound()) { - return MakeIntConst(e->Min() / v); - } else if (v == 1) { - return e; - } else if (v == -1) { - return MakeOpposite(e); - } else if (v > 0) { - return RegisterIntExpr(RevAlloc(new DivPosIntCstExpr(this, e, v))); - } else if (v == 0) { +IntExpr* Solver::MakeDiv(IntExpr* const expr, int64 value) { + CHECK(expr != nullptr); + CHECK_EQ(this, expr->solver()); + if (expr->Bound()) { + return MakeIntConst(expr->Min() / value); + } else if (value == 1) { + return expr; + } else if (value == -1) { + return MakeOpposite(expr); + } else if (value > 0) { + return RegisterIntExpr(RevAlloc(new DivPosIntCstExpr(this, expr, value))); + } else if (value == 0) { LOG(FATAL) << "Cannot divide by 0"; return nullptr; } else { return RegisterIntExpr( - MakeOpposite(RevAlloc(new DivPosIntCstExpr(this, e, -v)))); + MakeOpposite(RevAlloc(new DivPosIntCstExpr(this, expr, -value)))); // TODO(user) : implement special case. } } -Constraint* Solver::MakeAbsEquality(IntVar* const sub, IntVar* const abs_var) { - if (Cache()->FindExprExpression(sub, ModelCache::EXPR_ABS) == nullptr) { - Cache()->InsertExprExpression(abs_var, sub, ModelCache::EXPR_ABS); +Constraint* Solver::MakeAbsEquality(IntVar* const var, IntVar* const abs_var) { + if (Cache()->FindExprExpression(var, ModelCache::EXPR_ABS) == nullptr) { + Cache()->InsertExprExpression(abs_var, var, ModelCache::EXPR_ABS); } - return RevAlloc(new IntAbsConstraint(this, sub, abs_var)); + return RevAlloc(new IntAbsConstraint(this, var, abs_var)); } IntExpr* Solver::MakeAbs(IntExpr* const e) { @@ -7032,29 +7041,29 @@ IntExpr* Solver::MakeAbs(IntExpr* const e) { return result; } -IntExpr* Solver::MakeSquare(IntExpr* const e) { - CHECK_EQ(this, e->solver()); - if (e->Bound()) { - const int64 v = e->Min(); +IntExpr* Solver::MakeSquare(IntExpr* const expr) { + CHECK_EQ(this, expr->solver()); + if (expr->Bound()) { + const int64 v = expr->Min(); return MakeIntConst(v * v); } - IntExpr* result = Cache()->FindExprExpression(e, ModelCache::EXPR_SQUARE); + IntExpr* result = Cache()->FindExprExpression(expr, ModelCache::EXPR_SQUARE); if (result == nullptr) { - if (e->Min() >= 0) { - result = RegisterIntExpr(RevAlloc(new PosIntSquare(this, e))); + if (expr->Min() >= 0) { + result = RegisterIntExpr(RevAlloc(new PosIntSquare(this, expr))); } else { - result = RegisterIntExpr(RevAlloc(new IntSquare(this, e))); + result = RegisterIntExpr(RevAlloc(new IntSquare(this, expr))); } - Cache()->InsertExprExpression(result, e, ModelCache::EXPR_SQUARE); + Cache()->InsertExprExpression(result, expr, ModelCache::EXPR_SQUARE); } return result; } -IntExpr* Solver::MakePower(IntExpr* const e, int64 n) { - CHECK_EQ(this, e->solver()); +IntExpr* Solver::MakePower(IntExpr* const expr, int64 n) { + CHECK_EQ(this, expr->solver()); CHECK_GE(n, 0); - if (e->Bound()) { - const int64 v = e->Min(); + if (expr->Bound()) { + const int64 v = expr->Min(); if (v >= OverflowLimit(n)) { // Overflow. return MakeIntConst(kint64max); } @@ -7064,119 +7073,120 @@ IntExpr* Solver::MakePower(IntExpr* const e, int64 n) { case 0: return MakeIntConst(1); case 1: - return e; + return expr; case 2: - return MakeSquare(e); + return MakeSquare(expr); default: { IntExpr* result = nullptr; if (n % 2 == 0) { // even. - if (e->Min() >= 0) { - result = RegisterIntExpr(RevAlloc(new PosIntEvenPower(this, e, n))); + if (expr->Min() >= 0) { + result = + RegisterIntExpr(RevAlloc(new PosIntEvenPower(this, expr, n))); } else { - result = RegisterIntExpr(RevAlloc(new IntEvenPower(this, e, n))); + result = RegisterIntExpr(RevAlloc(new IntEvenPower(this, expr, n))); } } else { - result = RegisterIntExpr(RevAlloc(new IntOddPower(this, e, n))); + result = RegisterIntExpr(RevAlloc(new IntOddPower(this, expr, n))); } return result; } } } -IntExpr* Solver::MakeMin(IntExpr* const l, IntExpr* const r) { - CHECK_EQ(this, l->solver()); - CHECK_EQ(this, r->solver()); - if (l->Bound()) { - return MakeMin(r, l->Min()); +IntExpr* Solver::MakeMin(IntExpr* const left, IntExpr* const right) { + CHECK_EQ(this, left->solver()); + CHECK_EQ(this, right->solver()); + if (left->Bound()) { + return MakeMin(right, left->Min()); } - if (r->Bound()) { - return MakeMin(l, r->Min()); + if (right->Bound()) { + return MakeMin(left, right->Min()); } - if (l->Min() >= r->Max()) { - return r; + if (left->Min() >= right->Max()) { + return right; } - if (r->Min() >= l->Max()) { - return l; + if (right->Min() >= left->Max()) { + return left; } - return RegisterIntExpr(RevAlloc(new MinIntExpr(this, l, r))); + return RegisterIntExpr(RevAlloc(new MinIntExpr(this, left, right))); } -IntExpr* Solver::MakeMin(IntExpr* const e, int64 v) { - CHECK_EQ(this, e->solver()); - if (v <= e->Min()) { - return MakeIntConst(v); +IntExpr* Solver::MakeMin(IntExpr* const expr, int64 value) { + CHECK_EQ(this, expr->solver()); + if (value <= expr->Min()) { + return MakeIntConst(value); } - if (e->Bound()) { - return MakeIntConst(std::min(e->Min(), v)); + if (expr->Bound()) { + return MakeIntConst(std::min(expr->Min(), value)); } - if (e->Max() <= v) { - return e; + if (expr->Max() <= value) { + return expr; } - return RegisterIntExpr(RevAlloc(new MinCstIntExpr(this, e, v))); + return RegisterIntExpr(RevAlloc(new MinCstIntExpr(this, expr, value))); } -IntExpr* Solver::MakeMin(IntExpr* const e, int v) { - return MakeMin(e, static_cast(v)); +IntExpr* Solver::MakeMin(IntExpr* const expr, int value) { + return MakeMin(expr, static_cast(value)); } -IntExpr* Solver::MakeMax(IntExpr* const l, IntExpr* const r) { - CHECK_EQ(this, l->solver()); - CHECK_EQ(this, r->solver()); - if (l->Bound()) { - return MakeMax(r, l->Min()); +IntExpr* Solver::MakeMax(IntExpr* const left, IntExpr* const right) { + CHECK_EQ(this, left->solver()); + CHECK_EQ(this, right->solver()); + if (left->Bound()) { + return MakeMax(right, left->Min()); } - if (r->Bound()) { - return MakeMax(l, r->Min()); + if (right->Bound()) { + return MakeMax(left, right->Min()); } - if (l->Min() >= r->Max()) { - return l; + if (left->Min() >= right->Max()) { + return left; } - if (r->Min() >= l->Max()) { - return r; + if (right->Min() >= left->Max()) { + return right; } - return RegisterIntExpr(RevAlloc(new MaxIntExpr(this, l, r))); + return RegisterIntExpr(RevAlloc(new MaxIntExpr(this, left, right))); } -IntExpr* Solver::MakeMax(IntExpr* const e, int64 v) { - CHECK_EQ(this, e->solver()); - if (e->Bound()) { - return MakeIntConst(std::max(e->Min(), v)); +IntExpr* Solver::MakeMax(IntExpr* const expr, int64 value) { + CHECK_EQ(this, expr->solver()); + if (expr->Bound()) { + return MakeIntConst(std::max(expr->Min(), value)); } - if (v <= e->Min()) { - return e; + if (value <= expr->Min()) { + return expr; } - if (e->Max() <= v) { - return MakeIntConst(v); + if (expr->Max() <= value) { + return MakeIntConst(value); } - return RegisterIntExpr(RevAlloc(new MaxCstIntExpr(this, e, v))); + return RegisterIntExpr(RevAlloc(new MaxCstIntExpr(this, expr, value))); } -IntExpr* Solver::MakeMax(IntExpr* const e, int v) { - return MakeMax(e, static_cast(v)); +IntExpr* Solver::MakeMax(IntExpr* const expr, int value) { + return MakeMax(expr, static_cast(value)); } -IntExpr* Solver::MakeConvexPiecewiseExpr(IntExpr* e, int64 early_cost, +IntExpr* Solver::MakeConvexPiecewiseExpr(IntExpr* expr, int64 early_cost, int64 early_date, int64 late_date, int64 late_cost) { return RegisterIntExpr(RevAlloc(new SimpleConvexPiecewiseExpr( - this, e, early_cost, early_date, late_date, late_cost))); + this, expr, early_cost, early_date, late_date, late_cost))); } -IntExpr* Solver::MakeSemiContinuousExpr(IntExpr* const e, int64 fixed_charge, +IntExpr* Solver::MakeSemiContinuousExpr(IntExpr* const expr, int64 fixed_charge, int64 step) { if (step == 0) { if (fixed_charge == 0) { return MakeIntConst(0LL); } else { return RegisterIntExpr( - RevAlloc(new SemiContinuousStepZeroExpr(this, e, fixed_charge))); + RevAlloc(new SemiContinuousStepZeroExpr(this, expr, fixed_charge))); } } else if (step == 1) { return RegisterIntExpr( - RevAlloc(new SemiContinuousStepOneExpr(this, e, fixed_charge))); + RevAlloc(new SemiContinuousStepOneExpr(this, expr, fixed_charge))); } else { return RegisterIntExpr( - RevAlloc(new SemiContinuousExpr(this, e, fixed_charge, step))); + RevAlloc(new SemiContinuousExpr(this, expr, fixed_charge, step))); } // TODO(user) : benchmark with virtualization of // PosIntDivDown and PosIntDivUp - or function pointers. diff --git a/ortools/constraint_solver/graph_constraints.cc b/ortools/constraint_solver/graph_constraints.cc index bd7ef6b69c..8fb9fe9762 100644 --- a/ortools/constraint_solver/graph_constraints.cc +++ b/ortools/constraint_solver/graph_constraints.cc @@ -23,6 +23,7 @@ #include "ortools/base/stringprintf.h" #include "ortools/base/join.h" #include "ortools/base/join.h" +#include "ortools/base/join.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/saturated_arithmetic.h" @@ -1373,15 +1374,15 @@ class PathConnectedConstraint : public Constraint { elements.push_back(next->DebugString()); } for (int i = 0; i < sources_.size(); ++i) { - elements.push_back(StrCat(sources_[i])); + elements.push_back(absl::StrCat(sources_[i])); } for (int64 sink : sinks_) { - elements.push_back(StrCat(sink)); + elements.push_back(absl::StrCat(sink)); } for (IntVar* const status : status_) { elements.push_back(status->DebugString()); } - output += strings::Join(elements, ",") + ")"; + output += absl::StrJoin(elements, ",") + ")"; return output; } @@ -1486,10 +1487,10 @@ class PathTransitPrecedenceConstraint : public Constraint { } for (int i = 0; i < predecessors_.size(); ++i) { for (const int predecessor : predecessors_[i]) { - elements.push_back(StrCat("(", predecessor, ", ", i, ")")); + elements.push_back(absl::StrCat("(", predecessor, ", ", i, ")")); } } - output += strings::Join(elements, ",") + ")"; + output += absl::StrJoin(elements, ",") + ")"; return output; } void Accept(ModelVisitor* const visitor) const override { diff --git a/ortools/constraint_solver/hybrid.h b/ortools/constraint_solver/hybrid.h index 4304571225..d3f115e4f2 100644 --- a/ortools/constraint_solver/hybrid.h +++ b/ortools/constraint_solver/hybrid.h @@ -24,7 +24,7 @@ SearchMonitor* MakeSimplexConnection(Solver* const solver, std::function builder, std::function modifier, std::function runner, - int simplex_frequency); + int frequency); // ----- Linear Relaxation Constraint ----- diff --git a/ortools/constraint_solver/interval.cc b/ortools/constraint_solver/interval.cc index 63d8d35b24..8d7c458161 100644 --- a/ortools/constraint_solver/interval.cc +++ b/ortools/constraint_solver/interval.cc @@ -1166,7 +1166,7 @@ std::string FixedDurationPerformedIntervalVar::DebugString() const { class StartVarPerformedIntervalVar : public IntervalVar { public: - StartVarPerformedIntervalVar(Solver* const s, IntVar* const start_var, + StartVarPerformedIntervalVar(Solver* const s, IntVar* const var, int64 duration, const std::string& name); ~StartVarPerformedIntervalVar() override {} @@ -2208,8 +2208,9 @@ class FixedDurationIntervalVarStartSyncedOnEnd // ----- API ----- -IntervalVar* Solver::MakeMirrorInterval(IntervalVar* const t) { - return RegisterIntervalVar(RevAlloc(new MirrorIntervalVar(this, t))); +IntervalVar* Solver::MakeMirrorInterval(IntervalVar* const interval_var) { + return RegisterIntervalVar( + RevAlloc(new MirrorIntervalVar(this, interval_var))); } IntervalVar* Solver::MakeIntervalRelaxedMax(IntervalVar* const interval_var) { diff --git a/ortools/constraint_solver/io.cc b/ortools/constraint_solver/io.cc index 000da05b5d..93b9b4d343 100644 --- a/ortools/constraint_solver/io.cc +++ b/ortools/constraint_solver/io.cc @@ -592,7 +592,8 @@ class SecondPassVisitor : public ModelVisitor { if (variable->HasName()) { var_proto->set_name(variable->name()); } - if (variable->Size() == variable->Max() - variable->Min() + 1) { + if (variable->Size() == static_cast(variable->Max()) - + static_cast(variable->Min()) + 1) { // Contiguous CpArgument* const min_proto = var_proto->add_arguments(); min_proto->set_argument_index(TagIndex(ModelVisitor::kMinArgument)); diff --git a/ortools/constraint_solver/local_search.cc b/ortools/constraint_solver/local_search.cc index ab3293f465..ffba8bb2ef 100644 --- a/ortools/constraint_solver/local_search.cc +++ b/ortools/constraint_solver/local_search.cc @@ -341,6 +341,7 @@ PathOperator::PathOperator(const std::vector& next_vars, : IntVarLocalSearchOperator(next_vars), number_of_nexts_(next_vars.size()), ignore_path_vars_(path_vars.empty()), + next_base_to_increment_(number_of_base_nodes), base_nodes_(number_of_base_nodes), end_nodes_(number_of_base_nodes), base_paths_(number_of_base_nodes), @@ -459,6 +460,7 @@ bool PathOperator::MakeChainInactive(int64 before_chain, int64 chain_end) { bool PathOperator::IncrementPosition() { const int base_node_size = base_nodes_.size(); + if (!just_started_) { const int number_of_paths = path_starts_.size(); // Finding next base node positions. @@ -468,13 +470,14 @@ bool PathOperator::IncrementPosition() { // action is called a restart). int last_restarted = base_node_size; for (int i = base_node_size - 1; i >= 0; --i) { - if (base_nodes_[i] < number_of_nexts_) { + if (base_nodes_[i] < number_of_nexts_ && i <= next_base_to_increment_) { base_nodes_[i] = OldNext(base_nodes_[i]); break; } base_nodes_[i] = StartNode(i); last_restarted = i; } + next_base_to_increment_ = base_node_size; // At the end of the loop, base nodes with indexes in // [last_restarted, base_node_size[ have been restarted. // Restarted base nodes are then repositioned by the virtual @@ -802,7 +805,7 @@ class Relocate : public PathOperator { std::function start_empty_path_class, int64 chain_length = 1LL, bool single_path = false) : Relocate(vars, secondary_vars, - StrCat("Relocate<", chain_length, ">"), + absl::StrCat("Relocate<", chain_length, ">"), std::move(start_empty_path_class), chain_length, single_path) { } ~Relocate() override {} @@ -1674,7 +1677,7 @@ class PathLns : public PathOperator { private: inline bool ChainsAreFullPaths() const { return chunk_size_ == 0; } - void DeactivateChain(int64 node0); + void DeactivateChain(int64 node); void DeactivateUnactives(); const int number_of_chunks_; @@ -1924,8 +1927,8 @@ bool RandomCompoundOperator::MakeNextNeighbor(Assignment* delta, std::vector indices(size); std::iota(indices.begin(), indices.end(), 0); std::random_shuffle(indices.begin(), indices.end(), rand_); - for (int i = 0; i < size; ++i) { - if (operators_[indices[i]]->MakeNextNeighbor(delta, deltadelta)) { + for (int index : indices) { + if (operators_[index]->MakeNextNeighbor(delta, deltadelta)) { return true; } } @@ -2227,10 +2230,17 @@ LocalSearchFilter* Solver::MakeVariableDomainFilter() { const int IntVarLocalSearchFilter::kUnassigned = -1; IntVarLocalSearchFilter::IntVarLocalSearchFilter( - const std::vector& vars) { + const std::vector& vars, + Solver::ObjectiveWatcher objective_callback) + : injected_objective_value_(0), + objective_callback_(std::move(objective_callback)) { AddVars(vars); } +IntVarLocalSearchFilter::IntVarLocalSearchFilter( + const std::vector& vars) + : IntVarLocalSearchFilter(vars, nullptr) {} + void IntVarLocalSearchFilter::AddVars(const std::vector& vars) { if (!vars.empty()) { for (int i = 0; i < vars.size(); ++i) { @@ -2298,11 +2308,10 @@ class ObjectiveFilter : public IntVarLocalSearchFilter { Solver::ObjectiveWatcher delta_objective_callback, const IntVar* const objective, Solver::LocalSearchFilterBound filter_enum) - : IntVarLocalSearchFilter(vars), + : IntVarLocalSearchFilter(vars, std::move(delta_objective_callback)), primary_vars_size_(vars.size()), cache_(new int64[vars.size()]), delta_cache_(new int64[vars.size()]), - delta_objective_callback_(std::move(delta_objective_callback)), objective_(objective), filter_enum_(filter_enum), op_(), @@ -2349,9 +2358,8 @@ class ObjectiveFilter : public IntVarLocalSearchFilter { var_min = std::max(var_min, delta->ObjectiveMin()); var_max = std::min(var_max, delta->ObjectiveMax()); } - if (delta_objective_callback_ != nullptr) { - delta_objective_callback_(value); - } + value = CapAdd(value, injected_objective_value_); + PropagateObjectiveValue(value); switch (filter_enum_) { case Solver::LE: { return value <= var_max; @@ -2380,7 +2388,6 @@ class ObjectiveFilter : public IntVarLocalSearchFilter { const int primary_vars_size_; int64* const cache_; int64* const delta_cache_; - Solver::ObjectiveWatcher delta_objective_callback_; const IntVar* const objective_; Solver::LocalSearchFilterBound filter_enum_; Operator op_; @@ -2400,9 +2407,7 @@ class ObjectiveFilter : public IntVarLocalSearchFilter { old_value_ = op_.value(); old_delta_value_ = old_value_; incremental_ = false; - if (delta_objective_callback_ != nullptr) { - delta_objective_callback_(op_.value()); - } + PropagateObjectiveValue(CapAdd(op_.value(), injected_objective_value_)); } int64 Evaluate(const Assignment* delta, int64 current_value, const int64* const out_values, bool cache_delta_values) { @@ -2575,7 +2580,7 @@ class TernaryObjectiveFilter : public ObjectiveFilter { } \ return nullptr; -LocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( +IntVarLocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( const std::vector& vars, Solver::IndexEvaluator2 values, IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, Solver::LocalSearchOperation op_enum) { @@ -2583,7 +2588,7 @@ LocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( objective, filter_enum); } -LocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( +IntVarLocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( const std::vector& vars, Solver::IndexEvaluator2 values, ObjectiveWatcher delta_objective_callback, IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, @@ -2592,7 +2597,7 @@ LocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( delta_objective_callback, objective, filter_enum); } -LocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( +IntVarLocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( const std::vector& vars, const std::vector& secondary_vars, Solver::IndexEvaluator3 values, IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, @@ -2601,7 +2606,7 @@ LocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( values, nullptr, objective, filter_enum); } -LocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( +IntVarLocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( const std::vector& vars, const std::vector& secondary_vars, Solver::IndexEvaluator3 values, ObjectiveWatcher delta_objective_callback, IntVar* const objective, @@ -2636,14 +2641,14 @@ class LocalSearchProfiler : public LocalSearchMonitor { } std::string overview = "Local search operator statistics:\n"; StringAppendF(&overview, - StrCat("%", op_name_size, + absl::StrCat("%", op_name_size, "s | Neighbors | Filtered | " "Accepted | Time (s)\n") .c_str(), ""); OperatorStats total_stats; const std::string row_format = - StrCat("%", op_name_size, "s | %9d | %8d | %8d | %7.2g\n"); + absl::StrCat("%", op_name_size, "s | %9d | %8d | %8d | %7.2g\n"); for (const auto& stat : operator_stats_) { StringAppendF(&overview, row_format.c_str(), stat.first.c_str(), stat.second.neighbors, stat.second.filtered_neighbors, @@ -2662,14 +2667,14 @@ class LocalSearchProfiler : public LocalSearchMonitor { } StringAppendF( &overview, - StrCat("Local search filter statistics:\n%", op_name_size, + absl::StrCat("Local search filter statistics:\n%", op_name_size, "s | Calls | Rejects | Time (s) " "| Rejects/s\n") .c_str(), ""); FilterStats total_filter_stats; const std::string filter_row_format = - StrCat("%", op_name_size, "s | %9d | %9d | %7.2g | %7.2g\n"); + absl::StrCat("%", op_name_size, "s | %9d | %9d | %7.2g | %7.2g\n"); for (const auto& stat : filter_stats_) { StringAppendF(&overview, filter_row_format.c_str(), stat.first.c_str(), stat.second.calls, stat.second.rejects, stat.second.seconds, @@ -2849,7 +2854,7 @@ Decision* FindOneNeighbor::Next(Solver* const solver) { // Keeping the code in case a performance problem forces us to // use the old code with a zero test on pool_. - // reference_assignment_->Copy(assignment_); + // reference_assignment_->CopyIntersection(assignment_); pool_->Initialize(assignment_); SynchronizeAll(solver); } @@ -2899,8 +2904,8 @@ Decision* FindOneNeighbor::Next(Solver* const solver) { ls_operator_, mh_filter && move_filter); if (mh_filter && move_filter) { solver->filtered_neighbors_ += 1; - assignment_copy->Copy(reference_assignment_.get()); - assignment_copy->Copy(delta); + assignment_copy->CopyIntersection(reference_assignment_.get()); + assignment_copy->CopyIntersection(delta); solver->GetLocalSearchMonitor()->BeginAcceptNeighbor(ls_operator_); const bool accept = solver->SolveAndCommit(restore); solver->GetLocalSearchMonitor()->EndAcceptNeighbor(ls_operator_, @@ -2917,7 +2922,7 @@ Decision* FindOneNeighbor::Next(Solver* const solver) { AcceptNeighbor(solver->ParentSearch()); // Keeping the code in case a performance problem forces us to // use the old code with a zero test on pool_. - // reference_assignment_->Copy(assignment_); + // reference_assignment_->CopyIntersection(assignment_); pool_->RegisterNewSolution(assignment_); SynchronizeAll(solver); } else { @@ -2934,11 +2939,11 @@ bool FindOneNeighbor::FilterAccept(Solver* solver, const Assignment* delta, const Assignment* deltadelta) { bool ok = true; LocalSearchMonitor* const monitor = solver->GetLocalSearchMonitor(); - for (int i = 0; i < filters_.size(); ++i) { - if (ok || filters_[i]->IsIncremental()) { - monitor->BeginFiltering(filters_[i]); - const bool accept = filters_[i]->Accept(delta, deltadelta); - monitor->EndFiltering(filters_[i], !accept); + for (LocalSearchFilter* filter : filters_) { + if (ok || filter->IsIncremental()) { + monitor->BeginFiltering(filter); + const bool accept = filter->Accept(delta, deltadelta); + monitor->EndFiltering(filter, !accept); ok = accept && ok; } } @@ -2956,8 +2961,8 @@ void FindOneNeighbor::SynchronizeAll(Solver* solver) { } void FindOneNeighbor::SynchronizeFilters(const Assignment* assignment) { - for (int i = 0; i < filters_.size(); ++i) { - filters_[i]->Synchronize(assignment, nullptr); + for (LocalSearchFilter* filter : filters_) { + filter->Synchronize(assignment, nullptr); } } @@ -3343,11 +3348,11 @@ class DefaultSolutionPool : public SolutionPool { } void RegisterNewSolution(Assignment* const assignment) override { - reference_assignment_->Copy(assignment); + reference_assignment_->CopyIntersection(assignment); } void GetNextSolution(Assignment* const assignment) override { - assignment->Copy(reference_assignment_.get()); + assignment->CopyIntersection(reference_assignment_.get()); } bool SyncNeeded(Assignment* const local_assignment) override { return false; } diff --git a/ortools/constraint_solver/model_cache.cc b/ortools/constraint_solver/model_cache.cc index dc1335fc06..6202a75cc3 100644 --- a/ortools/constraint_solver/model_cache.cc +++ b/ortools/constraint_solver/model_cache.cc @@ -28,7 +28,7 @@ DEFINE_bool(cp_disable_cache, false, "Disable caching of model objects"); namespace operations_research { // ----- ModelCache ----- -ModelCache::ModelCache(Solver* const s) : solver_(s) {} +ModelCache::ModelCache(Solver* const solver) : solver_(solver) {} ModelCache::~ModelCache() {} diff --git a/ortools/constraint_solver/pack.cc b/ortools/constraint_solver/pack.cc index e38bee3cf7..e8f94bbfbc 100644 --- a/ortools/constraint_solver/pack.cc +++ b/ortools/constraint_solver/pack.cc @@ -25,6 +25,7 @@ #include "ortools/base/logging.h" #include "ortools/base/stringprintf.h" #include "ortools/base/join.h" +#include "ortools/base/join.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" @@ -227,8 +228,8 @@ void Pack::InitialPropagate() { if (need_context) { solver()->GetPropagationMonitor()->PushContext(StringPrintf( "Pack(bin %d, forced = [%s], undecided = [%s])", bin_index, - strings::Join(forced_[bin_index], ", ").c_str(), - strings::Join(data->undecided(bin_index), ", ").c_str())); + absl::StrJoin(forced_[bin_index], ", ").c_str(), + absl::StrJoin(data->undecided(bin_index), ", ").c_str())); } for (int dim_index = 0; dim_index < dims_.size(); ++dim_index) { @@ -250,8 +251,8 @@ void Pack::InitialPropagate() { if (need_context) { solver()->GetPropagationMonitor()->PushContext( StringPrintf("Pack(assigned = [%s], unassigned = [%s])", - strings::Join(data->assigned(), ", ").c_str(), - strings::Join(data->unassigned(), ", ").c_str())); + absl::StrJoin(data->assigned(), ", ").c_str(), + absl::StrJoin(data->unassigned(), ", ").c_str())); } for (int dim_index = 0; dim_index < dims_.size(); ++dim_index) { if (need_context) { @@ -283,8 +284,8 @@ void Pack::Propagate() { if (need_context) { solver()->GetPropagationMonitor()->PushContext(StringPrintf( "Pack(bin %d, forced = [%s], removed = [%s])", bin_index, - strings::Join(forced_[bin_index], ", ").c_str(), - strings::Join(removed_[bin_index], ", ").c_str())); + absl::StrJoin(forced_[bin_index], ", ").c_str(), + absl::StrJoin(removed_[bin_index], ", ").c_str())); } for (int dim_index = 0; dim_index < dims_.size(); ++dim_index) { @@ -307,8 +308,8 @@ void Pack::Propagate() { if (need_context) { solver()->GetPropagationMonitor()->PushContext( StringPrintf("Pack(removed = [%s], forced = [%s])", - strings::Join(removed_[bins_], ", ").c_str(), - strings::Join(forced_[bins_], ", ").c_str())); + absl::StrJoin(removed_[bins_], ", ").c_str(), + absl::StrJoin(forced_[bins_], ", ").c_str())); } for (int dim_index = 0; dim_index < dims_.size(); ++dim_index) { diff --git a/ortools/constraint_solver/resource.cc b/ortools/constraint_solver/resource.cc index 0b6367bab5..de6a07f5b3 100644 --- a/ortools/constraint_solver/resource.cc +++ b/ortools/constraint_solver/resource.cc @@ -177,11 +177,6 @@ struct ThetaNode { // our case, we use StartMin() + DurationMin() for the earliest completion // time of a task, which should not break any assumptions, but may give // bounds that are too loose. - // LOG_IF_FIRST_N(WARNING, - // (interval->DurationMin() != interval->DurationMax()), 1) - // << "You are using the Theta-tree on tasks having variable durations. " - // "This may lead to unexpected results, such as discarding valid " - // "solutions or allowing invalid ones."; } void Compute(const ThetaNode& left, const ThetaNode& right) { @@ -195,7 +190,7 @@ struct ThetaNode { } std::string DebugString() const { - return StrCat("ThetaNode{ p = ", total_processing, + return absl::StrCat("ThetaNode{ p = ", total_processing, ", e = ", total_ect < 0LL ? -1LL : total_ect, " }"); } @@ -2278,7 +2273,7 @@ class CumulativeConstraint : public Constraint { if (high_demand_intervals.size() >= 2) { // If there are less than 2 such intervals, the constraint would do // nothing - std::string seq_name = StrCat(name(), "-HighDemandSequence"); + std::string seq_name = absl::StrCat(name(), "-HighDemandSequence"); constraint = solver()->MakeDisjunctiveConstraint(high_demand_intervals, seq_name); } @@ -2467,7 +2462,7 @@ class VariableDemandCumulativeConstraint : public Constraint { if (high_demand_intervals.size() >= 2) { // If there are less than 2 such intervals, the constraint would do // nothing - const std::string seq_name = StrCat(name(), "-HighDemandSequence"); + const std::string seq_name = absl::StrCat(name(), "-HighDemandSequence"); constraint = solver()->MakeStrictDisjunctiveConstraint( high_demand_intervals, seq_name); } diff --git a/ortools/constraint_solver/routing.cc b/ortools/constraint_solver/routing.cc index e430566333..3432f39c40 100644 --- a/ortools/constraint_solver/routing.cc +++ b/ortools/constraint_solver/routing.cc @@ -4357,10 +4357,6 @@ void RoutingModel::SetupMetaheuristics( search_parameters.optimization_step(), nexts_, 10, 10, .8); break; - case LocalSearchMetaheuristic::OBJECTIVE_TABU_SEARCH: - optimize = solver_->MakeObjectiveTabuSearch( - false, cost_, search_parameters.optimization_step(), 100); - break; default: optimize = solver_->MakeMinimize(cost_, search_parameters.optimization_step()); diff --git a/ortools/constraint_solver/search.cc b/ortools/constraint_solver/search.cc index 337829b2d1..f804b7831a 100644 --- a/ortools/constraint_solver/search.cc +++ b/ortools/constraint_solver/search.cc @@ -17,11 +17,12 @@ #include #include #include +#include #include #include #include -//#include "ortools/base/casts.h" +#include "ortools/base/casts.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" @@ -175,7 +176,7 @@ void SearchLog::NoMoreSolutions() { OutputLine(buffer); } -void SearchLog::ApplyDecision(Decision* const d) { +void SearchLog::ApplyDecision(Decision* const decision) { Maintain(); const int64 b = solver()->branches(); if (b % period_ == 0 && b > 0) { @@ -183,9 +184,9 @@ void SearchLog::ApplyDecision(Decision* const d) { } } -void SearchLog::RefuteDecision(Decision* const d) { +void SearchLog::RefuteDecision(Decision* const decision) { min_right_depth_ = std::min(min_right_depth_, solver()->SearchDepth()); - ApplyDecision(d); + ApplyDecision(decision); } void SearchLog::OutputDecision() { @@ -261,34 +262,38 @@ std::string SearchLog::MemoryUsage() { } } -SearchMonitor* Solver::MakeSearchLog(int period) { - return RevAlloc(new SearchLog(this, nullptr, nullptr, nullptr, period)); +SearchMonitor* Solver::MakeSearchLog(int branch_period) { + return RevAlloc( + new SearchLog(this, nullptr, nullptr, nullptr, branch_period)); } -SearchMonitor* Solver::MakeSearchLog(int period, IntVar* const var) { - return RevAlloc(new SearchLog(this, nullptr, var, nullptr, period)); +SearchMonitor* Solver::MakeSearchLog(int branch_period, IntVar* const var) { + return RevAlloc(new SearchLog(this, nullptr, var, nullptr, branch_period)); } -SearchMonitor* Solver::MakeSearchLog(int period, +SearchMonitor* Solver::MakeSearchLog(int branch_period, std::function display_callback) { return RevAlloc(new SearchLog(this, nullptr, nullptr, - std::move(display_callback), period)); + std::move(display_callback), branch_period)); } -SearchMonitor* Solver::MakeSearchLog(int period, IntVar* const var, +SearchMonitor* Solver::MakeSearchLog(int branch_period, IntVar* const var, std::function display_callback) { + return RevAlloc(new SearchLog(this, nullptr, var, std::move(display_callback), + branch_period)); +} + +SearchMonitor* Solver::MakeSearchLog(int branch_period, + OptimizeVar* const opt_var) { return RevAlloc( - new SearchLog(this, nullptr, var, std::move(display_callback), period)); + new SearchLog(this, opt_var, nullptr, nullptr, branch_period)); } -SearchMonitor* Solver::MakeSearchLog(int period, OptimizeVar* const obj) { - return RevAlloc(new SearchLog(this, obj, nullptr, nullptr, period)); -} - -SearchMonitor* Solver::MakeSearchLog(int period, OptimizeVar* const obj, +SearchMonitor* Solver::MakeSearchLog(int branch_period, + OptimizeVar* const opt_var, std::function display_callback) { - return RevAlloc( - new SearchLog(this, obj, nullptr, std::move(display_callback), period)); + return RevAlloc(new SearchLog(this, opt_var, nullptr, + std::move(display_callback), branch_period)); } @@ -602,7 +607,7 @@ class TryDecisionBuilder : public CompositeDecisionBuilder { TryDecisionBuilder(); explicit TryDecisionBuilder(const std::vector& dbs); ~TryDecisionBuilder() override; - Decision* Next(Solver* const s) override; + Decision* Next(Solver* const solver) override; std::string DebugString() const override; void AdvanceToNextBuilder(Solver* const solver); @@ -1525,8 +1530,8 @@ void AssignOneVariableValue::Refute(Solver* const s) { } } // namespace -Decision* Solver::MakeAssignVariableValue(IntVar* const v, int64 val) { - return RevAlloc(new AssignOneVariableValue(v, val)); +Decision* Solver::MakeAssignVariableValue(IntVar* const var, int64 val) { + return RevAlloc(new AssignOneVariableValue(var, val)); } // ----- AssignOneVariableValueOrFail decision ----- @@ -1564,8 +1569,9 @@ void AssignOneVariableValueOrFail::Apply(Solver* const s) { void AssignOneVariableValueOrFail::Refute(Solver* const s) { s->Fail(); } } // namespace -Decision* Solver::MakeAssignVariableValueOrFail(IntVar* const v, int64 value) { - return RevAlloc(new AssignOneVariableValueOrFail(v, value)); +Decision* Solver::MakeAssignVariableValueOrFail(IntVar* const var, + int64 value) { + return RevAlloc(new AssignOneVariableValueOrFail(var, value)); } // ----- AssignOneVariableValue decision ----- @@ -1619,9 +1625,9 @@ void SplitOneVariable::Refute(Solver* const s) { } } // namespace -Decision* Solver::MakeSplitVariableDomain(IntVar* const v, int64 val, +Decision* Solver::MakeSplitVariableDomain(IntVar* const var, int64 val, bool start_with_lower_half) { - return RevAlloc(new SplitOneVariable(v, val, start_with_lower_half)); + return RevAlloc(new SplitOneVariable(var, val, start_with_lower_half)); } Decision* Solver::MakeVariableLessOrEqualValue(IntVar* const var, int64 value) { @@ -2135,15 +2141,19 @@ DecisionBuilder* Solver::MakeDecisionBuilderFromAssignment( // ----- Base Class ----- -SolutionCollector::SolutionCollector(Solver* const s, const Assignment* const a) - : SearchMonitor(s), - prototype_(a == nullptr ? nullptr : new Assignment(a)) {} +SolutionCollector::SolutionCollector(Solver* const solver, + const Assignment* const assignment) + : SearchMonitor(solver), + prototype_(assignment == nullptr ? nullptr : new Assignment(assignment)) { +} -SolutionCollector::SolutionCollector(Solver* const s) - : SearchMonitor(s), prototype_(new Assignment(s)) {} +SolutionCollector::SolutionCollector(Solver* const solver) + : SearchMonitor(solver), prototype_(new Assignment(solver)) {} SolutionCollector::~SolutionCollector() { - STLDeleteElements(&solutions_); + for (auto& data : solution_data_) { + delete data.solution; + } STLDeleteElements(&recycle_solutions_); } @@ -2190,124 +2200,122 @@ void SolutionCollector::AddObjective(IntVar* const objective) { } void SolutionCollector::EnterSearch() { - STLDeleteElements(&solutions_); + for (auto& data : solution_data_) { + delete data.solution; + } STLDeleteElements(&recycle_solutions_); - solutions_.clear(); + solution_data_.clear(); recycle_solutions_.clear(); - times_.clear(); - branches_.clear(); - failures_.clear(); - objective_values_.clear(); } void SolutionCollector::PushSolution() { - Assignment* new_sol = nullptr; - if (prototype_ != nullptr) { - if (!recycle_solutions_.empty()) { - new_sol = recycle_solutions_.back(); - DCHECK(new_sol != nullptr); - recycle_solutions_.pop_back(); - } else { - new_sol = new Assignment(prototype_.get()); - } - new_sol->Store(); - } - Solver* const s = solver(); - solutions_.push_back(new_sol); - times_.push_back(s->wall_time()); - branches_.push_back(s->branches()); - failures_.push_back(s->failures()); - if (new_sol != nullptr) { - objective_values_.push_back(new_sol->ObjectiveValue()); - } else { - objective_values_.push_back(0); - } + Push(BuildSolutionDataForCurrentState()); } void SolutionCollector::PopSolution() { - if (!solutions_.empty()) { - Assignment* popped = solutions_.back(); - solutions_.pop_back(); - if (popped != nullptr) { - recycle_solutions_.push_back(popped); + if (!solution_data_.empty()) { + FreeSolution(solution_data_.back().solution); + solution_data_.pop_back(); + } +} + +SolutionCollector::SolutionData +SolutionCollector::BuildSolutionDataForCurrentState() { + Assignment* solution = nullptr; + if (prototype_ != nullptr) { + if (!recycle_solutions_.empty()) { + solution = recycle_solutions_.back(); + DCHECK(solution != nullptr); + recycle_solutions_.pop_back(); + } else { + solution = new Assignment(prototype_.get()); } - times_.pop_back(); - branches_.pop_back(); - failures_.pop_back(); - objective_values_.pop_back(); + solution->Store(); + } + SolutionData data; + data.solution = solution; + data.time = solver()->wall_time(); + data.branches = solver()->branches(); + data.failures = solver()->failures(); + if (solution != nullptr) { + data.objective_value = solution->ObjectiveValue(); + } else { + data.objective_value = 0; + } + return data; +} + +void SolutionCollector::FreeSolution(Assignment* solution) { + if (solution != nullptr) { + recycle_solutions_.push_back(solution); } } void SolutionCollector::check_index(int n) const { CHECK_GE(n, 0) << "wrong index in solution getter"; - CHECK_LT(n, solutions_.size()) << "wrong index in solution getter"; + CHECK_LT(n, solution_data_.size()) << "wrong index in solution getter"; } Assignment* SolutionCollector::solution(int n) const { check_index(n); - return solutions_[n]; + return solution_data_[n].solution; } -int SolutionCollector::solution_count() const { return solutions_.size(); } +int SolutionCollector::solution_count() const { return solution_data_.size(); } int64 SolutionCollector::wall_time(int n) const { check_index(n); - return times_[n]; + return solution_data_[n].time; } int64 SolutionCollector::branches(int n) const { check_index(n); - return branches_[n]; + return solution_data_[n].branches; } int64 SolutionCollector::failures(int n) const { check_index(n); - return failures_[n]; + return solution_data_[n].failures; } int64 SolutionCollector::objective_value(int n) const { check_index(n); - return objective_values_[n]; + return solution_data_[n].objective_value; } int64 SolutionCollector::Value(int n, IntVar* const var) const { - check_index(n); - return solutions_[n]->Value(var); + return solution(n)->Value(var); } int64 SolutionCollector::StartValue(int n, IntervalVar* const var) const { - check_index(n); - return solutions_[n]->StartValue(var); + return solution(n)->StartValue(var); } int64 SolutionCollector::DurationValue(int n, IntervalVar* const var) const { - check_index(n); - return solutions_[n]->DurationValue(var); + return solution(n)->DurationValue(var); } int64 SolutionCollector::EndValue(int n, IntervalVar* const var) const { - check_index(n); - return solutions_[n]->EndValue(var); + return solution(n)->EndValue(var); } int64 SolutionCollector::PerformedValue(int n, IntervalVar* const var) const { - check_index(n); - return solutions_[n]->PerformedValue(var); + return solution(n)->PerformedValue(var); } const std::vector& SolutionCollector::ForwardSequence( - int n, SequenceVar* const v) const { - return solutions_[n]->ForwardSequence(v); + int n, SequenceVar* const var) const { + return solution(n)->ForwardSequence(var); } const std::vector& SolutionCollector::BackwardSequence( - int n, SequenceVar* const v) const { - return solutions_[n]->BackwardSequence(v); + int n, SequenceVar* const var) const { + return solution(n)->BackwardSequence(var); } const std::vector& SolutionCollector::Unperformed( - int n, SequenceVar* const v) const { - return solutions_[n]->Unperformed(v); + int n, SequenceVar* const var) const { + return solution(n)->Unperformed(var); } namespace { @@ -2359,8 +2367,8 @@ std::string FirstSolutionCollector::DebugString() const { } // namespace SolutionCollector* Solver::MakeFirstSolutionCollector( - const Assignment* const a) { - return RevAlloc(new FirstSolutionCollector(this, a)); + const Assignment* const assignment) { + return RevAlloc(new FirstSolutionCollector(this, assignment)); } SolutionCollector* Solver::MakeFirstSolutionCollector() { @@ -2405,8 +2413,8 @@ std::string LastSolutionCollector::DebugString() const { } // namespace SolutionCollector* Solver::MakeLastSolutionCollector( - const Assignment* const a) { - return RevAlloc(new LastSolutionCollector(this, a)); + const Assignment* const assignment) { + return RevAlloc(new LastSolutionCollector(this, assignment)); } SolutionCollector* Solver::MakeLastSolutionCollector() { @@ -2476,14 +2484,122 @@ std::string BestValueSolutionCollector::DebugString() const { } // namespace SolutionCollector* Solver::MakeBestValueSolutionCollector( - const Assignment* const a, bool maximize) { - return RevAlloc(new BestValueSolutionCollector(this, a, maximize)); + const Assignment* const assignment, bool maximize) { + return RevAlloc(new BestValueSolutionCollector(this, assignment, maximize)); } SolutionCollector* Solver::MakeBestValueSolutionCollector(bool maximize) { return RevAlloc(new BestValueSolutionCollector(this, maximize)); } +// ----- N Best Solution Collector ----- + +namespace { +class NBestValueSolutionCollector : public SolutionCollector { + public: + NBestValueSolutionCollector(Solver* const solver, + const Assignment* const assignment, + int solution_count, bool maximize); + NBestValueSolutionCollector(Solver* const solver, int solution_count, + bool maximize); + ~NBestValueSolutionCollector() override { Clear(); } + void EnterSearch() override; + void ExitSearch() override; + bool AtSolution() override; + std::string DebugString() const override; + + public: + void Clear(); + + const bool maximize_; + std::priority_queue> solutions_pq_; + const int solution_count_; +}; + +NBestValueSolutionCollector::NBestValueSolutionCollector( + Solver* const solver, const Assignment* const assignment, + int solution_count, bool maximize) + : SolutionCollector(solver, assignment), + maximize_(maximize), + solution_count_(solution_count) {} + +NBestValueSolutionCollector::NBestValueSolutionCollector(Solver* const solver, + int solution_count, + bool maximize) + : SolutionCollector(solver), + maximize_(maximize), + solution_count_(solution_count) {} + +void NBestValueSolutionCollector::EnterSearch() { + SolutionCollector::EnterSearch(); + Clear(); +} + +void NBestValueSolutionCollector::ExitSearch() { + while (!solutions_pq_.empty()) { + Push(solutions_pq_.top().second); + solutions_pq_.pop(); + } +} + +bool NBestValueSolutionCollector::AtSolution() { + if (prototype_ != nullptr) { + const IntVar* objective = prototype_->Objective(); + if (objective != nullptr) { + const int64 objective_value = + maximize_ ? -objective->Max() : objective->Min(); + if (solutions_pq_.size() < solution_count_) { + solutions_pq_.push( + {objective_value, BuildSolutionDataForCurrentState()}); + } else if (!solutions_pq_.empty()) { + const auto& top = solutions_pq_.top(); + if (top.first > objective_value) { + FreeSolution(solutions_pq_.top().second.solution); + solutions_pq_.pop(); + solutions_pq_.push( + {objective_value, BuildSolutionDataForCurrentState()}); + } + } + } + } + return true; +} + +std::string NBestValueSolutionCollector::DebugString() const { + if (prototype_ == nullptr) { + return "NBestValueSolutionCollector()"; + } else { + return "NBestValueSolutionCollector(" + prototype_->DebugString() + ")"; + } +} + +void NBestValueSolutionCollector::Clear() { + while (!solutions_pq_.empty()) { + delete solutions_pq_.top().second.solution; + solutions_pq_.pop(); + } +} + +} // namespace + +SolutionCollector* Solver::MakeNBestValueSolutionCollector( + const Assignment* const assignment, int solution_count, bool maximize) { + if (solution_count == 1) { + return MakeBestValueSolutionCollector(assignment, maximize); + } + return RevAlloc(new NBestValueSolutionCollector(this, assignment, + solution_count, maximize)); +} + +SolutionCollector* Solver::MakeNBestValueSolutionCollector(int solution_count, + bool maximize) { + if (solution_count == 1) { + return MakeBestValueSolutionCollector(maximize); + } + return RevAlloc( + new NBestValueSolutionCollector(this, solution_count, maximize)); +} + // ----- All Solution Collector ----- // collect all solutions @@ -2520,8 +2636,9 @@ std::string AllSolutionCollector::DebugString() const { } } // namespace -SolutionCollector* Solver::MakeAllSolutionCollector(const Assignment* const a) { - return RevAlloc(new AllSolutionCollector(this, a)); +SolutionCollector* Solver::MakeAllSolutionCollector( + const Assignment* const assignment) { + return RevAlloc(new AllSolutionCollector(this, assignment)); } SolutionCollector* Solver::MakeAllSolutionCollector() { @@ -2783,7 +2900,7 @@ class TabuSearch : public Metaheuristic { void AcceptNeighbor() override; std::string DebugString() const override { return "Tabu Search"; } - private: + protected: struct VarValue { VarValue(IntVar* const var, int64 value, int64 stamp) : var_(var), value_(value), stamp_(stamp) {} @@ -2793,6 +2910,10 @@ class TabuSearch : public Metaheuristic { }; typedef std::list TabuList; + virtual std::vector CreateTabuConstraints(); + const TabuList& forbid_tabu_list() { return forbid_tabu_list_; } + + private: void AgeList(int64 tenure, TabuList* list); void AgeLists(); @@ -2840,13 +2961,40 @@ void TabuSearch::ApplyDecision(Decision* const d) { // Accept a neighbor if it improves the best solution found so far IntVar* aspiration = s->MakeBoolVar(); if (maximize_) { - s->AddConstraint( - s->MakeIsGreaterOrEqualCstCt(objective_, best_ + step_, aspiration)); + s->AddConstraint(s->MakeIsGreaterOrEqualCstCt( + objective_, CapAdd(best_, step_), aspiration)); } else { - s->AddConstraint( - s->MakeIsLessOrEqualCstCt(objective_, best_ - step_, aspiration)); + s->AddConstraint(s->MakeIsLessOrEqualCstCt(objective_, CapSub(best_, step_), + aspiration)); } + std::vector tabu_vars = CreateTabuConstraints(); + + if (!tabu_vars.empty()) { + IntVar* const tabu = s->MakeBoolVar(); + s->AddConstraint(s->MakeIsGreaterOrEqualCstCt( + s->MakeSum(tabu_vars)->Var(), tabu_vars.size() * tabu_factor_, tabu)); + s->AddConstraint(s->MakeGreaterOrEqual(s->MakeSum(aspiration, tabu), 1LL)); + } + + // Go downhill to the next local optimum + if (maximize_) { + const int64 bound = (current_ > kint64min) ? current_ + step_ : current_; + s->AddConstraint(s->MakeGreaterOrEqual(objective_, bound)); + } else { + const int64 bound = (current_ < kint64max) ? current_ - step_ : current_; + s->AddConstraint(s->MakeLessOrEqual(objective_, bound)); + } + + // Avoid cost plateau's which lead to tabu cycles + if (found_initial_solution_) { + s->AddConstraint(s->MakeNonEquality(objective_, last_)); + } +} + +std::vector TabuSearch::CreateTabuConstraints() { + Solver* const s = solver(); + // Tabu criterion // A variable in the "keep" list must keep its value, a variable in the // "forbid" list must not take its value in the list. The tabu criterion is @@ -2868,27 +3016,7 @@ void TabuSearch::ApplyDecision(Decision* const d) { s->AddConstraint(forbid_cst); tabu_vars.push_back(tabu_var); } - - if (!tabu_vars.empty()) { - IntVar* const tabu = s->MakeBoolVar(); - s->AddConstraint(s->MakeIsGreaterOrEqualCstCt( - s->MakeSum(tabu_vars)->Var(), tabu_vars.size() * tabu_factor_, tabu)); - s->AddConstraint(s->MakeGreaterOrEqual(s->MakeSum(aspiration, tabu), 1LL)); - } - - // Go downhill to the next local optimum - if (maximize_) { - const int64 bound = (current_ > kint64min) ? current_ + step_ : current_; - s->AddConstraint(s->MakeGreaterOrEqual(objective_, bound)); - } else { - const int64 bound = (current_ < kint64max) ? current_ - step_ : current_; - s->AddConstraint(s->MakeLessOrEqual(objective_, bound)); - } - - // Avoid cost plateau's which lead to tabu cycles - if (found_initial_solution_) { - s->AddConstraint(s->MakeNonEquality(objective_, last_)); - } + return tabu_vars; } bool TabuSearch::AtSolution() { @@ -2949,6 +3077,39 @@ void TabuSearch::AgeLists() { AgeList(forbid_tenure_, &forbid_tabu_list_); ++stamp_; } + +class GenericTabuSearch : public TabuSearch { + public: + GenericTabuSearch(Solver* const s, bool maximize, IntVar* objective, + int64 step, const std::vector& vars, + int64 forbid_tenure) + : TabuSearch(s, maximize, objective, step, vars, 0, forbid_tenure, 1) {} + std::string DebugString() const override { return "Generic Tabu Search"; } + + protected: + std::vector CreateTabuConstraints() override; +}; + +std::vector GenericTabuSearch::CreateTabuConstraints() { + Solver* const s = solver(); + + std::vector tabu_vars; + + // Tabu criterion + // At least one element of the forbid_tabu_list must change value. + std::vector forbid_values; + int i = 0; + for (const VarValue& vv : forbid_tabu_list()) { + forbid_values.push_back(s->MakeIsDifferentCstVar(vv.var_, vv.value_)); + i++; + } + + if (!forbid_values.empty()) { + tabu_vars.push_back(s->MakeIsGreaterCstVar(s->MakeSum(forbid_values), 0)); + } + return tabu_vars; +} + } // namespace SearchMonitor* Solver::MakeTabuSearch(bool maximize, IntVar* const v, @@ -2960,12 +3121,11 @@ SearchMonitor* Solver::MakeTabuSearch(bool maximize, IntVar* const v, forbid_tenure, tabu_factor)); } -SearchMonitor* Solver::MakeObjectiveTabuSearch(bool maximize, IntVar* const v, - int64 step, - int64 forbid_tenure) { - const std::vector& vars = {v}; +SearchMonitor* Solver::MakeGenericTabuSearch( + bool maximize, IntVar* const v, int64 step, + const std::vector& tabu_vars, int64 forbid_tenure) { return RevAlloc( - new TabuSearch(this, maximize, v, step, vars, 0, forbid_tenure, 1)); + new GenericTabuSearch(this, maximize, v, step, tabu_vars, forbid_tenure)); } // ---------- Simulated Annealing ---------- @@ -3398,7 +3558,7 @@ bool GuidedLocalSearch::LocalOptimum() { utility[i] = std::pair(arc, value / (penalty + 1.0)); } Comparator comparator; - std::stable_sort(utility.begin(), utility.end(), comparator); + std::sort(utility.begin(), utility.end(), comparator); int64 utility_value = utility[0].second; penalties_->Increment(utility[0].first); for (int i = 1; i < utility.size() && utility_value == utility[i].second; @@ -3827,8 +3987,8 @@ int64 RegularLimit::TimeDelta() { } } // namespace -SearchLimit* Solver::MakeTimeLimit(int64 time) { - return MakeLimit(time, kint64max, kint64max, kint64max); +SearchLimit* Solver::MakeTimeLimit(int64 time_in_ms) { + return MakeLimit(time_in_ms, kint64max, kint64max, kint64max); } SearchLimit* Solver::MakeBranchesLimit(int64 branches) { @@ -3931,7 +4091,7 @@ class ORLimit : public SearchLimit { limit_2_->RefuteDecision(d); } std::string DebugString() const override { - return StrCat("OR limit (", limit_1_->DebugString(), " OR ", + return absl::StrCat("OR limit (", limit_1_->DebugString(), " OR ", limit_2_->DebugString(), ")"); } diff --git a/ortools/constraint_solver/table.cc b/ortools/constraint_solver/table.cc index 569c33ad4e..c0a1809a62 100644 --- a/ortools/constraint_solver/table.cc +++ b/ortools/constraint_solver/table.cc @@ -25,6 +25,7 @@ #include "ortools/base/logging.h" #include "ortools/base/stringprintf.h" #include "ortools/base/join.h" +#include "ortools/base/join.h" #include "ortools/base/map_util.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" @@ -1249,7 +1250,7 @@ class TransitionConstraint : public Constraint { "TransitionConstraint([%s], %d transitions, initial = %" GG_LL_FORMAT "d, final = [%s])", JoinDebugStringPtr(vars_, ", ").c_str(), transition_table_.NumTuples(), - initial_state_, strings::Join(final_states_, ", ").c_str()); + initial_state_, absl::StrJoin(final_states_, ", ").c_str()); } private: diff --git a/ortools/constraint_solver/trace.cc b/ortools/constraint_solver/trace.cc index 657dcbb8ca..0fe92cce73 100644 --- a/ortools/constraint_solver/trace.cc +++ b/ortools/constraint_solver/trace.cc @@ -24,6 +24,7 @@ #include "ortools/base/logging.h" #include "ortools/base/stringprintf.h" #include "ortools/base/join.h" +#include "ortools/base/join.h" #include "ortools/base/map_util.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" @@ -638,14 +639,14 @@ class PrintTrace : public PropagationMonitor { void SetValues(IntVar* const var, const std::vector& values) override { DisplayModification(StringPrintf("SetValues(%s, %s)", var->DebugString().c_str(), - strings::Join(values, ", ").c_str())); + absl::StrJoin(values, ", ").c_str())); } void RemoveValues(IntVar* const var, const std::vector& values) override { DisplayModification(StringPrintf("RemoveValues(%s, %s)", var->DebugString().c_str(), - strings::Join(values, ", ").c_str())); + absl::StrJoin(values, ", ").c_str())); } // ----- IntervalVar modifiers ----- @@ -731,9 +732,9 @@ class PrintTrace : public PropagationMonitor { const std::vector& unperformed) override { DisplayModification(StringPrintf( "RankSequence(%s, forward [%s], backward[%s], unperformed[%s])", - var->DebugString().c_str(), strings::Join(rank_first, ", ").c_str(), - strings::Join(rank_last, ", ").c_str(), - strings::Join(unperformed, ", ").c_str())); + var->DebugString().c_str(), absl::StrJoin(rank_first, ", ").c_str(), + absl::StrJoin(rank_last, ", ").c_str(), + absl::StrJoin(unperformed, ", ").c_str())); } void Install() override { diff --git a/ortools/constraint_solver/utilities.cc b/ortools/constraint_solver/utilities.cc index cf4e1eef05..2a09db44d5 100644 --- a/ortools/constraint_solver/utilities.cc +++ b/ortools/constraint_solver/utilities.cc @@ -20,6 +20,7 @@ #include "ortools/base/logging.h" #include "ortools/base/stringprintf.h" #include "ortools/base/join.h" +#include "ortools/base/join.h" #include "ortools/base/map_util.h" #include "ortools/base/hash.h" #include "ortools/constraint_solver/constraint_solver.h" @@ -402,7 +403,7 @@ class PrintModelVisitor : public ModelVisitor { void VisitIntegerArrayArgument(const std::string& arg_name, const std::vector& values) override { - LOG(INFO) << Spaces() << arg_name << ": [" << strings::Join(values, ", ") + LOG(INFO) << Spaces() << arg_name << ": [" << absl::StrJoin(values, ", ") << "]"; } diff --git a/ortools/data/rcpsp_parser.cc b/ortools/data/rcpsp_parser.cc index 14a0aa8835..8966bf1ee4 100644 --- a/ortools/data/rcpsp_parser.cc +++ b/ortools/data/rcpsp_parser.cc @@ -79,7 +79,7 @@ void RcpspParser::ProcessRcpspLine(const std::string& line) { if (strings::StartsWith(line, "---")) return; const std::vector words = - strings::Split(line, AnyOf(" :\t\r"), strings::SkipEmpty()); + strings::Split(line, AnyOf(" :\t\r"), absl::SkipEmpty()); if (words.empty()) return; @@ -252,7 +252,7 @@ void RcpspParser::ProcessRcpspLine(const std::string& line) { void RcpspParser::ProcessRcpspMaxLine(const std::string& line) { const std::vector words = - strings::Split(line, AnyOf(" :\t[]\r"), strings::SkipEmpty()); + strings::Split(line, AnyOf(" :\t[]\r"), absl::SkipEmpty()); switch (load_status_) { case NOT_STARTED: { @@ -481,7 +481,7 @@ void RcpspParser::ProcessRcpspMaxLine(const std::string& line) { void RcpspParser::ProcessPattersonLine(const std::string& line) { const std::vector words = - strings::Split(line, AnyOf(" :\t[]\r"), strings::SkipEmpty()); + strings::Split(line, AnyOf(" :\t[]\r"), absl::SkipEmpty()); if (words.empty()) return; diff --git a/ortools/flatzinc/cp_model_fz_solver.cc b/ortools/flatzinc/cp_model_fz_solver.cc index 9d0446b395..6e06fb2018 100644 --- a/ortools/flatzinc/cp_model_fz_solver.cc +++ b/ortools/flatzinc/cp_model_fz_solver.cc @@ -701,17 +701,17 @@ std::string SolutionString( if (output.variable != nullptr) { const int64 value = value_func(output.variable); if (output.display_as_boolean) { - return StrCat(output.name, " = ", value == 1 ? "true" : "false", + return absl::StrCat(output.name, " = ", value == 1 ? "true" : "false", ";"); } else { - return StrCat(output.name, " = ", value, ";"); + return absl::StrCat(output.name, " = ", value, ";"); } } else { const int bound_size = output.bounds.size(); std::string result = StrCat(output.name, " = array", bound_size, "d("); for (int i = 0; i < bound_size; ++i) { if (output.bounds[i].max_value != 0) { - StrAppend(&result, output.bounds[i].min_value, "..", + absl::StrAppend(&result, output.bounds[i].min_value, "..", output.bounds[i].max_value, ", "); } else { result.append("{},"); @@ -723,7 +723,7 @@ std::string SolutionString( if (output.display_as_boolean) { result.append(value ? "true" : "false"); } else { - StrAppend(&result, value); + absl::StrAppend(&result, value); } if (i != output.flat_variables.size() - 1) { result.append(", "); @@ -749,7 +749,7 @@ std::string SolutionString( void LogInFlatzincFormat(const std::string& multi_line_input) { std::vector lines = - strings::Split(multi_line_input, '\n', strings::SkipEmpty()); + strings::Split(multi_line_input, '\n', absl::SkipEmpty()); for (const std::string& line : lines) { FZLOG << line << FZENDL; } diff --git a/ortools/flatzinc/model.cc b/ortools/flatzinc/model.cc index 7821a6fbc1..17a634f860 100644 --- a/ortools/flatzinc/model.cc +++ b/ortools/flatzinc/model.cc @@ -379,7 +379,7 @@ std::string Domain::DebugString() const { } else if (values.size() == 1) { return StrCat(values.back()); } else { - return StringPrintf("[%s]", strings::Join(values, ", ").c_str()); + return StringPrintf("[%s]", absl::StrJoin(values, ", ").c_str()); } } @@ -454,7 +454,7 @@ std::string Argument::DebugString() const { return StringPrintf("[%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d]", values[0], values[1]); case INT_LIST: - return StringPrintf("[%s]", strings::Join(values, ", ").c_str()); + return StringPrintf("[%s]", absl::StrJoin(values, ", ").c_str()); case DOMAIN_LIST: return StringPrintf("[%s]", JoinDebugString(domains, ", ").c_str()); case INT_VAR_REF: diff --git a/ortools/flatzinc/parser_util.h b/ortools/flatzinc/parser_util.h index ffc1e64fb7..f9f9c0e70f 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::HasPrefixString; -using operations_research::HasSuffixString; using operations_research::StringPrintf; namespace operations_research { diff --git a/ortools/glop/preprocessor.cc b/ortools/glop/preprocessor.cc index 94beb879fa..5d93789f2a 100644 --- a/ortools/glop/preprocessor.cc +++ b/ortools/glop/preprocessor.cc @@ -169,7 +169,7 @@ void MainLpPreprocessor::RunAndPushIfRelevant( } void MainLpPreprocessor::RecoverSolution(ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); while (!preprocessors_.empty()) { preprocessors_.back()->RecoverSolution(solution); preprocessors_.pop_back(); @@ -339,7 +339,7 @@ Fractional ComputeMaxVariableBoundsMagnitude(const LinearProgram& lp) { } // namespace bool EmptyColumnPreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); column_deletion_helper_.Clear(); const ColIndex num_cols = lp->num_variables(); @@ -383,7 +383,7 @@ bool EmptyColumnPreprocessor::Run(LinearProgram* lp) { } void EmptyColumnPreprocessor::RecoverSolution(ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_IF_NULL(solution); column_deletion_helper_.RestoreDeletedColumns(solution); } @@ -437,7 +437,7 @@ struct ColumnWithRepresentativeAndScaledCost { } // namespace bool ProportionalColumnPreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); ColMapping mapping = FindProportionalColumns( lp->GetSparseMatrix(), parameters_.preprocessor_zero_tolerance()); @@ -667,7 +667,7 @@ bool ProportionalColumnPreprocessor::Run(LinearProgram* lp) { void ProportionalColumnPreprocessor::RecoverSolution( ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_IF_NULL(solution); column_deletion_helper_.RestoreDeletedColumns(solution); @@ -781,7 +781,7 @@ void ProportionalColumnPreprocessor::RecoverSolution( // -------------------------------------------------------- bool ProportionalRowPreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); const RowIndex num_rows = lp->num_constraints(); const SparseMatrix& transpose = lp->GetTransposeSparseMatrix(); @@ -957,7 +957,7 @@ bool ProportionalRowPreprocessor::Run(LinearProgram* lp) { void ProportionalRowPreprocessor::RecoverSolution( ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_IF_NULL(solution); row_deletion_helper_.RestoreDeletedRows(solution); @@ -1030,7 +1030,7 @@ void ProportionalRowPreprocessor::RecoverSolution( // -------------------------------------------------------- bool FixedVariablePreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); const ColIndex num_cols = lp->num_variables(); for (ColIndex col(0); col < num_cols; ++col) { @@ -1053,7 +1053,7 @@ bool FixedVariablePreprocessor::Run(LinearProgram* lp) { void FixedVariablePreprocessor::RecoverSolution( ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_IF_NULL(solution); column_deletion_helper_.RestoreDeletedColumns(solution); } @@ -1063,7 +1063,7 @@ void FixedVariablePreprocessor::RecoverSolution( // -------------------------------------------------------- bool ForcingAndImpliedFreeConstraintPreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); const RowIndex num_rows = lp->num_constraints(); @@ -1212,7 +1212,7 @@ bool ForcingAndImpliedFreeConstraintPreprocessor::Run(LinearProgram* lp) { void ForcingAndImpliedFreeConstraintPreprocessor::RecoverSolution( ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_IF_NULL(solution); column_deletion_helper_.RestoreDeletedColumns(solution); row_deletion_helper_.RestoreDeletedRows(solution); @@ -1296,7 +1296,7 @@ struct ColWithDegree { } // namespace bool ImpliedFreePreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); const RowIndex num_rows = lp->num_constraints(); const ColIndex num_cols = lp->num_variables(); @@ -1483,7 +1483,7 @@ bool ImpliedFreePreprocessor::Run(LinearProgram* lp) { } void ImpliedFreePreprocessor::RecoverSolution(ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_IF_NULL(solution); const ColIndex num_cols = solution->variable_statuses.size(); for (ColIndex col(0); col < num_cols; ++col) { @@ -1507,7 +1507,7 @@ void ImpliedFreePreprocessor::RecoverSolution(ProblemSolution* solution) const { // -------------------------------------------------------- bool DoubletonFreeColumnPreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); // We will modify the matrix transpose and then push the change to the linear // program by calling lp->UseTransposeMatrixAsReference(). Note @@ -1617,7 +1617,7 @@ bool DoubletonFreeColumnPreprocessor::Run(LinearProgram* lp) { void DoubletonFreeColumnPreprocessor::RecoverSolution( ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); row_deletion_helper_.RestoreDeletedRows(solution); for (const RestoreInfo& r : Reverse(restore_stack_)) { // Correct the constraint status. @@ -1741,7 +1741,7 @@ void UnconstrainedVariablePreprocessor::RemoveZeroCostUnconstrainedVariable( } bool UnconstrainedVariablePreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); const Fractional tolerance = parameters_.preprocessor_zero_tolerance(); @@ -1961,7 +1961,7 @@ bool UnconstrainedVariablePreprocessor::Run(LinearProgram* lp) { void UnconstrainedVariablePreprocessor::RecoverSolution( ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_IF_NULL(solution); column_deletion_helper_.RestoreDeletedColumns(solution); row_deletion_helper_.RestoreDeletedRows(solution); @@ -2028,7 +2028,7 @@ void UnconstrainedVariablePreprocessor::RecoverSolution( // -------------------------------------------------------- bool FreeConstraintPreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); const RowIndex num_rows = lp->num_constraints(); for (RowIndex row(0); row < num_rows; ++row) { @@ -2044,7 +2044,7 @@ bool FreeConstraintPreprocessor::Run(LinearProgram* lp) { void FreeConstraintPreprocessor::RecoverSolution( ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_IF_NULL(solution); row_deletion_helper_.RestoreDeletedRows(solution); } @@ -2054,7 +2054,7 @@ void FreeConstraintPreprocessor::RecoverSolution( // -------------------------------------------------------- bool EmptyConstraintPreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); const RowIndex num_rows(lp->num_constraints()); const ColIndex num_cols(lp->num_variables()); @@ -2092,7 +2092,7 @@ bool EmptyConstraintPreprocessor::Run(LinearProgram* lp) { void EmptyConstraintPreprocessor::RecoverSolution( ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_IF_NULL(solution); row_deletion_helper_.RestoreDeletedRows(solution); } @@ -2614,7 +2614,7 @@ bool SingletonPreprocessor::MakeConstraintAnEqualityIfPossible( } bool SingletonPreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); const SparseMatrix& matrix = lp->GetSparseMatrix(); const SparseMatrix& transpose = lp->GetTransposeSparseMatrix(); @@ -2687,7 +2687,7 @@ bool SingletonPreprocessor::Run(LinearProgram* lp) { } void SingletonPreprocessor::RecoverSolution(ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_IF_NULL(solution); // Note that the two deletion helpers must restore 0.0 values in the positions @@ -2740,7 +2740,7 @@ MatrixEntry SingletonPreprocessor::GetSingletonRowMatrixEntry( // -------------------------------------------------------- bool RemoveNearZeroEntriesPreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); const ColIndex num_cols = lp->num_variables(); if (num_cols == 0) return false; @@ -2814,7 +2814,7 @@ void RemoveNearZeroEntriesPreprocessor::RecoverSolution( // -------------------------------------------------------- bool SingletonColumnSignPreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); const ColIndex num_cols = lp->num_variables(); if (num_cols == 0) return false; @@ -2843,7 +2843,7 @@ bool SingletonColumnSignPreprocessor::Run(LinearProgram* lp) { void SingletonColumnSignPreprocessor::RecoverSolution( ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_IF_NULL(solution); for (int i = 0; i < changed_columns_.size(); ++i) { const ColIndex col = changed_columns_[i]; @@ -2862,7 +2862,7 @@ void SingletonColumnSignPreprocessor::RecoverSolution( // -------------------------------------------------------- bool DoubletonEqualityRowPreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); // Note that we don't update the transpose during this preprocessor run. const SparseMatrix& original_transpose = lp->GetTransposeSparseMatrix(); @@ -3028,7 +3028,7 @@ bool DoubletonEqualityRowPreprocessor::Run(LinearProgram* lp) { void DoubletonEqualityRowPreprocessor::RecoverSolution( ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_IF_NULL(solution); column_deletion_helper_.RestoreDeletedColumns(solution); row_deletion_helper_.RestoreDeletedRows(solution); @@ -3118,7 +3118,7 @@ void DoubletonEqualityRowPreprocessor:: // -------------------------------------------------------- bool DualizerPreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); if (parameters_.solve_dual_problem() == GlopParameters::NEVER_DO) { return false; @@ -3234,7 +3234,7 @@ bool DualizerPreprocessor::Run(LinearProgram* lp) { // the first ColIndex and RowIndex for the rows and columns of the given // problem. void DualizerPreprocessor::RecoverSolution(ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_IF_NULL(solution); DenseRow new_primal_values(primal_num_cols_, 0.0); @@ -3368,7 +3368,7 @@ ProblemStatus DualizerPreprocessor::ChangeStatusToDualStatus( // -------------------------------------------------------- bool ShiftVariableBoundsPreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); // Save the linear program bounds before shifting them. @@ -3437,7 +3437,7 @@ bool ShiftVariableBoundsPreprocessor::Run(LinearProgram* lp) { void ShiftVariableBoundsPreprocessor::RecoverSolution( ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_IF_NULL(solution); const ColIndex num_cols = solution->variable_statuses.size(); for (ColIndex col(0); col < num_cols; ++col) { @@ -3468,7 +3468,7 @@ void ShiftVariableBoundsPreprocessor::RecoverSolution( // -------------------------------------------------------- bool ScalingPreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); if (!parameters_.use_scaling()) return false; @@ -3492,7 +3492,7 @@ bool ScalingPreprocessor::Run(LinearProgram* lp) { } void ScalingPreprocessor::RecoverSolution(ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_IF_NULL(solution); scaler_.ScaleRowVector(false, &(solution->primal_values)); @@ -3532,7 +3532,7 @@ void ScalingPreprocessor::RecoverSolution(ProblemSolution* solution) const { // -------------------------------------------------------- bool ToMinimizationPreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); if (lp->IsMaximizationProblem()) { for (ColIndex col(0); col < lp->num_variables(); ++col) { @@ -3556,7 +3556,7 @@ void ToMinimizationPreprocessor::RecoverSolution( // -------------------------------------------------------- bool AddSlackVariablesPreprocessor::Run(LinearProgram* lp) { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_VALUE_IF_NULL(lp, false); lp->AddSlackVariablesWhereNecessary( /*detect_integer_constraints=*/true); @@ -3566,7 +3566,7 @@ bool AddSlackVariablesPreprocessor::Run(LinearProgram* lp) { void AddSlackVariablesPreprocessor::RecoverSolution( ProblemSolution* solution) const { - SCOPED_INSTRUCTION_COUNT; + SCOPED_INSTRUCTION_COUNT(time_limit_); RETURN_IF_NULL(solution); // Compute constraint statuses from statuses of slack variables. diff --git a/ortools/glop/revised_simplex.cc b/ortools/glop/revised_simplex.cc index ed8ca088ae..5668925d27 100644 --- a/ortools/glop/revised_simplex.cc +++ b/ortools/glop/revised_simplex.cc @@ -2947,7 +2947,7 @@ void RevisedSimplex::DisplayRevisedSimplexDebugInfo() { std::string output = "z = " + StringifyWithFlags(ComputeObjectiveValue()); const DenseRow& reduced_costs = reduced_costs_.GetReducedCosts(); for (const ColIndex col : variables_info_.GetNotBasicBitRow()) { - StrAppend(&output, StringifyMonomialWithFlags(reduced_costs[col], + absl::StrAppend(&output, StringifyMonomialWithFlags(reduced_costs[col], variable_name_[col])); } VLOG(3) << output << ";"; @@ -2957,11 +2957,11 @@ void RevisedSimplex::DisplayRevisedSimplexDebugInfo() { for (const SparseRow& row : dictionary) { output.clear(); ColIndex basic_col = basis_[r]; - StrAppend(&output, variable_name_[basic_col], " = ", + absl::StrAppend(&output, variable_name_[basic_col], " = ", StringifyWithFlags(variable_values_.Get(basic_col))); for (const SparseRowEntry e : row) { if (e.col() != basic_col) { - StrAppend(&output, + absl::StrAppend(&output, StringifyMonomialWithFlags(e.coefficient(), variable_name_[e.col()])); } @@ -2984,17 +2984,17 @@ void RevisedSimplex::DisplayProblem() const { for (ColIndex col(0); col < num_cols_; ++col) { const Fractional coeff = objective_[col]; has_objective |= (coeff != 0.0); - StrAppend(&output, + absl::StrAppend(&output, StringifyMonomialWithFlags(coeff, variable_name_[col])); } if (!has_objective) { - StrAppend(&output, " 0"); + absl::StrAppend(&output, " 0"); } VLOG(3) << output << ";"; for (RowIndex row(0); row < num_rows_; ++row) { output = ""; for (ColIndex col(0); col < num_cols_; ++col) { - StrAppend( + absl::StrAppend( &output, StringifyMonomialWithFlags( matrix_with_slack_.column(col).LookUpCoefficient(row), variable_name_[col])); diff --git a/ortools/graph/connected_components.cc b/ortools/graph/connected_components.cc new file mode 100644 index 0000000000..03d2a12728 --- /dev/null +++ b/ortools/graph/connected_components.cc @@ -0,0 +1,136 @@ +// Copyright 2010-2017 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// The following uses disjoint-sets algorithms, see: +// https://en.wikipedia.org/wiki/Disjoint-set_data_structure#Disjoint-set_forests + +#include + +#include "ortools/graph/connected_components.h" + +void DenseConnectedComponentsFinder::SetNumberOfNodes(int num_nodes) { + const int old_num_nodes = GetNumberOfNodes(); + if (num_nodes == old_num_nodes) { + return; + } + CHECK_GT(num_nodes, old_num_nodes); + // Each new node starts as an isolated component: + // It has itself as root. + parent_.resize(num_nodes); + std::iota(parent_.begin() + old_num_nodes, parent_.end(), old_num_nodes); + // It's in an isolated component of size 1. + component_size_.resize(num_nodes, 1); + // Its rank is 0. + rank_.resize(num_nodes); + // This introduces one extra component per added node. + num_components_ += num_nodes - old_num_nodes; +} + +int DenseConnectedComponentsFinder::FindRoot(int node) { + DCHECK_GE(node, 0); + DCHECK_LT(node, GetNumberOfNodes()); + + // Search the root. + int root = parent_[node]; + while (parent_[root] != root) { + root = parent_[root]; + } + + // Apply path compression. + while (node != root) { + const int prev_parent = parent_[node]; + parent_[node] = root; + node = prev_parent; + } + return root; +} + +void DenseConnectedComponentsFinder::AddEdge(int node1, int node2) { + // Grow if needed. + const int min_num_nodes = std::max(node1, node2) + 1; + if (min_num_nodes > GetNumberOfNodes()) { + SetNumberOfNodes(min_num_nodes); + } + + // Just union the sets for node1 and node2. + int root1 = FindRoot(node1); + int root2 = FindRoot(node2); + + // Already the same set. + if (root1 == root2) { + return; + } + + DCHECK_GE(num_components_, 2); + --num_components_; + + const int component_size = component_size_[root1] + component_size_[root2]; + + // Attach the shallowest tree to root of the deepest one. Note that this + // operation grows the rank of the new common root by at most one (if the two + // trees origginally have the same rank). + if (rank_[root1] > rank_[root2]) { + parent_[root2] = root1; + component_size_[root1] = component_size; + } else { + parent_[root1] = root2; + component_size_[root2] = component_size; + // If the ranks were the same then attaching just grew the rank by one. + if (rank_[root1] == rank_[root2]) { + ++rank_[root2]; + } + } +} + +bool DenseConnectedComponentsFinder::Connected(int node1, int node2) { + if (node1 < 0 || node1 >= GetNumberOfNodes() || node2 < 0 || + node2 >= GetNumberOfNodes()) { + return false; + } + return FindRoot(node1) == FindRoot(node2); +} + +int DenseConnectedComponentsFinder::GetSize(int node) { + if (node < 0 || node >= GetNumberOfNodes()) { + return 0; + } + return component_size_[FindRoot(node)]; +} + +std::vector DenseConnectedComponentsFinder::GetComponentIds() { + std::vector component_ids(GetNumberOfNodes(), -1); + int current_component = 0; + for (int node = 0; node < GetNumberOfNodes(); ++node) { + int& root_component = component_ids[FindRoot(node)]; + if (root_component < 0) { + // This is the first node in a yet unseen component. + root_component = current_component; + ++current_component; + } + component_ids[node] = root_component; + } + return component_ids; +} diff --git a/ortools/graph/connected_components.h b/ortools/graph/connected_components.h new file mode 100644 index 0000000000..22cf863480 --- /dev/null +++ b/ortools/graph/connected_components.h @@ -0,0 +1,255 @@ +// Copyright 2010-2017 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Utility for finding connected components in an undirected graph. +// A set of nodes and edges between the nodes form an undirected graph. +// If two nodes can be reached from one another, then they are in the +// same connected component. +// +// To use this: +// ConnectedComponentsFinder cc; +// cc.AddNode(node1); +// cc.AddNode(node2); +// cc.AddEdge(node1, node2); +// ... repeating, adding nodes and edges as needed. Adding an edge +// will automatically also add the two nodes at its ends, if they +// haven't already been added. +// std::vector > components; +// cc.FindConnectedComponents(&components); +// Each entry in components now contains all the nodes in a single +// connected component. +// +// If you want to, you can continue adding nodes and edges after calling +// FindConnectedComponents, then call it again later. +// +// If your node type isn't STL-friendly, then you can use pointers to +// it instead: +// ConnectedComponentsFinder cc; +// cc.AddNode(&node1); +// ... and so on... +// Of course, in this usage, the connected components finder retains +// these pointers through its lifetime (though it doesn't dereference them). + +#ifndef UTIL_GRAPH_CONNECTED_COMPONENTS_H_ +#define UTIL_GRAPH_CONNECTED_COMPONENTS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "ortools/base/logging.h" +#include "ortools/base/map_util.h" +#include "ortools/base/ptr_util.h" + +// A connected components finder that only works on dense ints. +class DenseConnectedComponentsFinder { + public: + DenseConnectedComponentsFinder() {} + + DenseConnectedComponentsFinder(const DenseConnectedComponentsFinder&) = + delete; + DenseConnectedComponentsFinder& operator=( + const DenseConnectedComponentsFinder&) = delete; + + // The main API is the same as ConnectedComponentsFinder (below): see the + // homonymous functions there. + void AddEdge(int node1, int node2); + bool Connected(int node1, int node2); + int GetSize(int node); + int GetNumberOfComponents() const { return num_components_; } + int GetNumberOfNodes() const { return parent_.size(); } + + // Sets the number of nodes in the graph. The graph can only grow: this + // dies if "num_nodes" is lower or equal to any of the values ever given + // to AddEdge(), or lower than a previous value given to SetNumberOfNodes(). + // You need this if there are nodes that don't have any edges. + void SetNumberOfNodes(int num_nodes); + + // Returns the root of the set for the given node. node must be in + // [0;GetNumberOfNodes()-1]. + // Non-const because it does path compression internally. + int FindRoot(int node); + + // Returns a vector of size GetNumberOfNodes() with the "component id" each + // node. Two nodes are in the same component iff their component id is equal, + // and components are numbered 0 to (GetNumberOfComponents() - 1). + // The order is deterministic: for two nodes b and c, b < c => + // component_id(b) <= component_id(c) + // or + // there exists a < b, component_id(a) = component_id(c) + // Non-const because it does path compression internally. + std::vector GetComponentIds(); + + private: + // parent[i] is the id of an ancestor for node i. A node is a root iff + // parent[i] == i. + std::vector parent_; + // If i is a root, component_size_[i] is the number of elements in the + // component. If i is not a root, component_size_[i] is meaningless. + std::vector component_size_; + // rank[i] is the depth of the tree. + std::vector rank_; + // Number of connected components. + int num_components_ = 0; +}; + +namespace internal { +// A helper to deduce the type of map to use depending on whether CompareOrHashT +// is a comparator or a hasher (prefer the latter). +template +struct ConnectedComponentsTypeHelper { + // SFINAE helpers to detect a hash functor. + template + struct hash_by_ref {}; + template + struct hash_by_value {}; + + // SFINAE dispatchers that return the right kind of set depending on the + // functor. + template + static std::unordered_set ReturnSet( + hash_by_ref*); + template + static std::unordered_set ReturnSet( + hash_by_value*); + template + static std::set ReturnSet(...); + using Set = decltype(ReturnSet(nullptr)); + + // SFINAE dispatchers that return the right kind of map depending on the + // functor. + template + static std::unordered_map ReturnMap( + hash_by_ref*); + template + static std::unordered_map ReturnMap( + hash_by_value*); + template + static std::map ReturnMap(...); + using Map = decltype(ReturnMap(nullptr)); +}; + +} // namespace internal + +template > +class ConnectedComponentsFinder { + public: + // Constructs a connected components finder. + ConnectedComponentsFinder() {} + + ConnectedComponentsFinder(const ConnectedComponentsFinder&) = delete; + ConnectedComponentsFinder& operator=(const ConnectedComponentsFinder&) = + delete; + + // Adds a node in the graph. It is OK to add the same node more than + // once; additions after the first have no effect. + void AddNode(T node) { LookupOrInsertNode(node); } + + // Adds an edge in the graph. Also adds both endpoint nodes as necessary. + // It is not an error to add the same edge twice. Self-edges are OK too. + void AddEdge(T node1, T node2) { + delegate_.AddEdge(LookupOrInsertNode(node1), + LookupOrInsertNode(node2)); + } + + // Returns true iff both nodes are in the same connected component. + // Returns false if either node has not been already added with AddNode. + bool Connected(T node1, T node2) { + return delegate_.Connected(FindWithDefault(index_, node1, -1), + FindWithDefault(index_, node2, -1)); + } + + // Finds the connected component containing a node, and returns the + // total number of nodes in that component. Returns zero iff the + // node has not been already added with AddNode. + int GetSize(T node) { + return delegate_.GetSize(FindWithDefault(index_, node, -1)); + } + + // Finds all the connected components and assigns them to components. + // Components are ordered in the same way nodes were added, i.e. if node 'b' + // was added before node 'c', then either: + // - 'c' belongs to the same component as a node 'a' added before 'b', or + // - the component for 'c' comes after the one for 'b'. + // There are two versions: + // - The first one returns the result, and stores each component in a vector. + // This is the preferred version. + // - The second one populates the result, and stores each component in a set. + std::vector> FindConnectedComponents() { + const auto component_ids = delegate_.GetComponentIds(); + std::vector> components(delegate_.GetNumberOfComponents()); + for (const auto& elem_id : index_) { + components[component_ids[elem_id.second]].push_back(elem_id.first); + } + return components; + } + void FindConnectedComponents( + std::vector::Set>* components) { + const auto component_ids = delegate_.GetComponentIds(); + components->clear(); + components->resize(delegate_.GetNumberOfComponents()); + for (const auto& elem_id : index_) { + components->at(component_ids[elem_id.second]).insert(elem_id.first); + } + } + + // Returns the current number of connected components. + // This number can change as the new nodes or edges are added. + int GetNumberOfComponents() const { + return delegate_.GetNumberOfComponents(); + } + + // Returns the current number of added distinct nodes. + // This includes nodes added explicitly via the calls to AddNode() method + // and implicitly via the calls to AddEdge() method. + // Nodes that were added several times only count once. + int GetNumberOfNodes() const { return delegate_.GetNumberOfNodes(); } + + private: + // Returns the index for the given node. If the node does not exist and + // update_delegate is true, explicitly add the node to the delegate. + template + int LookupOrInsertNode(T node) { + const auto result = index_.emplace(node, index_.size()); + const int node_id = result.first->second; + if (update_delegate && result.second) { + // A new index was created. + delegate_.SetNumberOfNodes(node_id + 1); + } + return node_id; + } + + DenseConnectedComponentsFinder delegate_; + typename internal::ConnectedComponentsTypeHelper::Map + index_; +}; + +#endif // UTIL_GRAPH_CONNECTED_COMPONENTS_H_ diff --git a/ortools/graph/io.h b/ortools/graph/io.h index ec4079fbea..efe582e617 100644 --- a/ortools/graph/io.h +++ b/ortools/graph/io.h @@ -22,11 +22,12 @@ #include #include -#include "ortools/util/filelineiter.h" +#include "ortools/base/filelineiter.h" #include "ortools/base/join.h" #include "ortools/base/numbers.h" #include "ortools/base/split.h" #include "ortools/base/join.h" +#include "ortools/base/join.h" #include "ortools/graph/graph.h" #include "ortools/base/status.h" #include "ortools/base/statusor.h" @@ -110,7 +111,7 @@ std::string GraphToString(const Graph& graph, GraphToStringFormat format) { if (format == PRINT_GRAPH_ARCS) { for (const typename Graph::ArcIndex arc : graph.OutgoingArcs(node)) { if (!out.empty()) out += '\n'; - StrAppend(&out, node, "->", graph.Head(arc)); + absl::StrAppend(&out, node, "->", graph.Head(arc)); } } else { // PRINT_GRAPH_ADJACENCY_LISTS[_SORTED] adj.clear(); @@ -121,7 +122,7 @@ std::string GraphToString(const Graph& graph, GraphToStringFormat format) { std::sort(adj.begin(), adj.end()); } if (node != 0) out += '\n'; - StrAppend(&out, node, ": ", strings::Join(adj, " ")); + absl::StrAppend(&out, node, ": ", absl::StrJoin(adj, " ")); } } return out; @@ -135,16 +136,15 @@ util::StatusOr ReadGraphFile( int64 num_nodes = -1; int64 num_expected_lines = -1; int64 num_lines_read = 0; - for (const std::string& line : operations_research::FileLines(filename)) { + for (const std::string& line : FileLines(filename)) { ++num_lines_read; if (num_lines_read == 1) { std::vector header_ints; - if (!SplitStringAndParse(line, " ", &operations_research::safe_strto64, - &header_ints) || + if (!SplitStringAndParse(line, " ", &safe_strto64, &header_ints) || header_ints.size() < 2 || header_ints[0] < 0 || header_ints[1] < 0) { return util::Status( util::error::INVALID_ARGUMENT, - StrCat("First line of '", filename, + absl::StrCat("First line of '", filename, "' should be at least two nonnegative integers.")); } num_nodes = header_ints[0]; @@ -159,7 +159,7 @@ util::StatusOr ReadGraphFile( if (header_ints.size() != num_colors + 2) { return util::Status( util::error::INVALID_ARGUMENT, - StrCat( + absl::StrCat( "There should be num_colors-1 color cardinalities in the" " header of '", filename, "' (where num_colors=", num_colors, @@ -173,7 +173,7 @@ util::StatusOr ReadGraphFile( if (header_ints[i] <= 0 || num_nodes_left <= 0) { return util::Status( util::error::INVALID_ARGUMENT, - StrCat( + absl::StrCat( "The color cardinalities in the header of '", filename, " should always be >0 and add up to less than the" " total number of nodes.")); @@ -192,7 +192,7 @@ util::StatusOr ReadGraphFile( node2 < 0 || node1 >= num_nodes || node2 >= num_nodes) { return util::Status( util::error::INVALID_ARGUMENT, - StrCat("In '", filename, "', line ", num_lines_read, + absl::StrCat("In '", filename, "', line ", num_lines_read, ": Expected two", " integers in the range [0, ", num_nodes, ").")); } @@ -209,7 +209,7 @@ util::StatusOr ReadGraphFile( } if (num_lines_read != num_expected_lines + 1) { return util::Status(util::error::INVALID_ARGUMENT, - StrCat("The number of arcs/edges in '", filename, + absl::StrCat("The number of arcs/edges in '", filename, "' (", num_lines_read - 1, " does not match the value announced in", " the header (", num_expected_lines, ")")); diff --git a/ortools/graph/max_flow.cc b/ortools/graph/max_flow.cc index 763b0b07b1..92451d102c 100644 --- a/ortools/graph/max_flow.cc +++ b/ortools/graph/max_flow.cc @@ -982,8 +982,8 @@ FlowModel GenericMaxFlow::CreateFlowModel() { // TODO(user): moves this code out of a .cc file and include it at the end of // the header so it can work with any graph implementation ? template class GenericMaxFlow; -template class GenericMaxFlow<::util::ReverseArcListGraph<> >; -template class GenericMaxFlow<::util::ReverseArcStaticGraph<> >; -template class GenericMaxFlow<::util::ReverseArcMixedGraph<> >; +template class GenericMaxFlow<::util::ReverseArcListGraph<>>; +template class GenericMaxFlow<::util::ReverseArcStaticGraph<>>; +template class GenericMaxFlow<::util::ReverseArcMixedGraph<>>; } // namespace operations_research diff --git a/ortools/graph/min_cost_flow.cc b/ortools/graph/min_cost_flow.cc index 79e3eafbb1..4c9cd802ad 100644 --- a/ortools/graph/min_cost_flow.cc +++ b/ortools/graph/min_cost_flow.cc @@ -978,10 +978,10 @@ bool GenericMinCostFlow::IsArcDirect( // TODO(user): Move this code out of a .cc file and include it at the end of // the header so it can work with any graph implementation? template class GenericMinCostFlow; -template class GenericMinCostFlow<::util::ReverseArcListGraph<> >; -template class GenericMinCostFlow<::util::ReverseArcStaticGraph<> >; -template class GenericMinCostFlow<::util::ReverseArcMixedGraph<> >; -template class GenericMinCostFlow<::util::ReverseArcStaticGraph >; +template class GenericMinCostFlow<::util::ReverseArcListGraph<>>; +template class GenericMinCostFlow<::util::ReverseArcStaticGraph<>>; +template class GenericMinCostFlow<::util::ReverseArcMixedGraph<>>; +template class GenericMinCostFlow<::util::ReverseArcStaticGraph>; // A more memory-efficient version for large graphs. template class GenericMinCostFlow<::util::ReverseArcStaticGraph, diff --git a/ortools/graph/util.h b/ortools/graph/util.h index ca0b315718..666dda6820 100644 --- a/ortools/graph/util.h +++ b/ortools/graph/util.h @@ -25,7 +25,7 @@ #include #include -//#include "ortools/graph/connected_components.h" +#include "ortools/graph/connected_components.h" #include "ortools/graph/graph.h" #include "ortools/base/map_util.h" #include "ortools/base/hash.h" @@ -187,6 +187,22 @@ bool GraphIsSymmetric(const Graph& graph) { return true; } +template +bool GraphIsWeaklyConnected(const Graph& graph) { + typedef typename Graph::NodeIndex NodeIndex; + static_assert(std::numeric_limits::max() <= INT_MAX, + "GraphIsWeaklyConnected() isn't yet implemented for graphs" + " that support more than INT_MAX nodes. Reach out to" + " or-core-team@ if you need this."); + if (graph.num_nodes() == 0) return true; + DenseConnectedComponentsFinder union_find; + union_find.SetNumberOfNodes(graph.num_nodes()); + for (typename Graph::ArcIndex arc = 0; arc < graph.num_arcs(); ++arc) { + union_find.AddEdge(graph.Tail(arc), graph.Head(arc)); + } + return union_find.GetNumberOfComponents() == 1; +} + template std::unique_ptr CopyGraph(const Graph& graph) { std::unique_ptr new_graph( diff --git a/ortools/linear_solver/glop_interface.cc b/ortools/linear_solver/glop_interface.cc index 1e6c97e456..8e029d6de7 100644 --- a/ortools/linear_solver/glop_interface.cc +++ b/ortools/linear_solver/glop_interface.cc @@ -19,19 +19,12 @@ #include "ortools/base/commandlineflags.h" -#ifndef ANDROID_JNI #include "ortools/base/commandlineflags.h" -#endif #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/stringprintf.h" -#ifndef ANDROID_JNI -#include "ortools/base/file.h" -#include "google/protobuf/text_format.h" -#endif - #include "ortools/base/hash.h" #include "ortools/glop/lp_solver.h" #include "ortools/glop/parameters.pb.h" @@ -41,6 +34,10 @@ #include "ortools/lp_data/lp_types.h" #include "ortools/util/time_limit.h" +#ifndef __PORTABLE_PLATFORM__ +#include "google/protobuf/text_format.h" +#endif + namespace operations_research { namespace { @@ -431,7 +428,7 @@ void GLOPInterface::SetLpAlgorithm(int value) { bool GLOPInterface::SetSolverSpecificParametersAsString( const std::string& parameters) { -#ifdef ANDROID_JNI +#ifdef __PORTABLE_PLATFORM__ // NOTE(user): Android build uses protocol buffers in lite mode, and // parsing data from text format is not supported there. To allow solver // specific parameters from std::string on Android, we first need to switch to diff --git a/ortools/linear_solver/linear_solver.cc b/ortools/linear_solver/linear_solver.cc index 7c8c930400..959a42e831 100644 --- a/ortools/linear_solver/linear_solver.cc +++ b/ortools/linear_solver/linear_solver.cc @@ -30,15 +30,9 @@ #include "ortools/base/stringprintf.h" #include "ortools/base/timer.h" -#ifndef ANDROID_JNI -#include "ortools/base/file.h" -#endif +#include "ortools/port/file.h" -#ifdef ANDROID_JNI -#include "ortools/base/numbers.h" -#endif /// ANDROID_JNI - #include "ortools/base/map_util.h" #include "ortools/base/stl_util.h" #include "ortools/base/hash.h" @@ -47,9 +41,6 @@ #include "ortools/linear_solver/model_exporter.h" #include "ortools/linear_solver/model_validator.h" #include "ortools/util/fp_utils.h" -#ifndef ANDROID_JNI -#include "ortools/util/proto_tools.h" -#endif // TODO(user): Clean up includes. E.g., parameters.pb.h seems not used. @@ -76,17 +67,6 @@ DEFINE_bool(mpsolver_bypass_model_validation, false, // operations_research namespace in open_source/base). namespace operations_research { -#if defined(ANDROID_JNI) && (defined(__ANDROID__) || defined(__APPLE__)) -// Enum -> std::string conversions are not present in MessageLite that is being used -// on Android. -std::string MPSolverResponseStatus_Name(int status) { - return SimpleItoa(status); -} - -std::string MPModelRequest_SolverType_Name(int type) { - return SimpleItoa(type); -} -#endif // defined(ANDROID_JNI) && (defined(__ANDROID__) || defined(__APPLE__)) double MPConstraint::GetCoefficient(const MPVariable* const var) const { DLOG_IF(DFATAL, !interface_->solver_->OwnsVariable(var)) << var; @@ -357,9 +337,12 @@ extern MPSolverInterface* BuildGurobiInterface(bool mip, #endif #if defined(USE_CPLEX) extern MPSolverInterface* BuildCplexInterface(bool mip, MPSolver* const solver); + +extern MPSolverInterface* BuildGLOPInterface(MPSolver* const solver); #endif -#ifdef ANDROID_JNI +// TODO(user): use portable static initialization method instead. +#ifdef __PORTABLE_PLATFORM__ extern MPSolverInterface* BuildGLOPInterface(MPSolver* const solver); #endif @@ -409,7 +392,7 @@ MPSolverInterface* BuildSolverInterface(MPSolver* const solver) { // TODO(user): Revert to the best *available* interface. LOG(FATAL) << "Linear solver not recognized."; } - return NULL; + return nullptr; } } // namespace @@ -537,8 +520,12 @@ void MPSolver::SetIndexConstraints(bool enabled) { MPConstraint* MPSolver::LookupConstraintOrNull(const std::string& constraint_name) const { - std::unordered_map::const_iterator it = - constraint_name_to_index_->find(constraint_name); + if (!constraint_name_to_index_) { + LOG_EVERY_N(WARNING, 100) << "Trying to lookup constraint by name, but " + "constraints are not indexed"; + return nullptr; + } + const auto it = constraint_name_to_index_->find(constraint_name); if (it == constraint_name_to_index_->end()) return nullptr; return constraints_[it->second]; } @@ -644,7 +631,7 @@ MPSolverResponseStatus ResultStatusToMPSolverResponseStatus( } // namespace void MPSolver::FillSolutionResponseProto(MPSolutionResponse* response) const { - CHECK_NOTNULL(response); + CHECK(response != nullptr); response->Clear(); response->set_status( ResultStatusToMPSolverResponseStatus(interface_->result_status_)); @@ -669,7 +656,7 @@ void MPSolver::FillSolutionResponseProto(MPSolutionResponse* response) const { // static void MPSolver::SolveWithProto(const MPModelRequest& model_request, MPSolutionResponse* response) { - CHECK_NOTNULL(response); + CHECK(response != nullptr); const MPModelProto& model = model_request.model(); MPSolver solver(model.name(), static_cast( model_request.solver_type())); @@ -681,7 +668,7 @@ void MPSolver::SolveWithProto(const MPModelRequest& model_request, if (response->status() != MPSOLVER_MODEL_IS_VALID) { LOG(WARNING) << "Loading model from protocol buffer failed, load status = " - << MPSolverResponseStatus_Name(response->status()) << " (" + << ProtoEnumToString(response->status()) << " (" << response->status() << "); Error: " << error_message; return; @@ -1249,7 +1236,6 @@ bool MPSolver::OwnsVariable(const MPVariable* var) const { return variables_[var_index] == var; } -#ifndef ANDROID_JNI bool MPSolver::ExportModelAsLpFormat(bool obfuscate, std::string* output) { MPModelProto proto; ExportModelToProto(&proto); @@ -1264,7 +1250,6 @@ bool MPSolver::ExportModelAsMpsFormat(bool fixed_format, bool obfuscate, MPModelProtoExporter exporter(proto); return exporter.ExportModelAsMpsFormat(fixed_format, obfuscate, output); } -#endif // ---------- MPSolverInterface ---------- @@ -1375,7 +1360,7 @@ void MPSolverInterface::InvalidateSolutionSynchronization() { double MPSolverInterface::ComputeExactConditionNumber() const { // Override this method in interfaces that actually support it. LOG(DFATAL) << "ComputeExactConditionNumber not implemented for " - << MPModelRequest_SolverType_Name( + << ProtoEnumToString( static_cast( solver_->ProblemType())); return 0.0; @@ -1438,19 +1423,17 @@ void MPSolverInterface::SetIntegerParamToUnsupportedValue( bool MPSolverInterface::SetSolverSpecificParametersAsString( const std::string& parameters) { -#ifdef ANDROID_JNI - // This is not implemented on Android because there is no default /tmp and a - // pointer to the Java environment is require to query for the application - // folder or the location of external storage (if any). - return false; -#else - if (parameters.empty()) return true; - // Note(user): this method needs to return a success/failure boolean // immediately, so we also perform the actual parameter parsing right away. // Some implementations will keep them forever and won't need to re-parse // them; some (eg. SCIP, Gurobi) need to re-parse the parameters every time // they do Solve(). We just store the parameters std::string anyway. + // + // Note(user): This is not implemented on Android because there is no + // temporary directory to write files to without a pointer to the Java + // environment. + if (parameters.empty()) return true; + std::string extension = ValidFileExtensionForParameterFile(); #if defined(__linux) int32 tid = static_cast(pthread_self()); @@ -1462,31 +1445,29 @@ bool MPSolverInterface::SetSolverSpecificParametersAsString( #else // _MSC_VER int32 pid = 456; #endif // _MSC_VER - int64 now = base::GetCurrentTimeNanos(); + int64 now = absl::GetCurrentTimeNanos(); std::string filename = StringPrintf("/tmp/parameters-tempfile-%x-%d-%llx%s", tid, pid, now, extension.c_str()); bool no_error_so_far = true; if (no_error_so_far) { - no_error_so_far = - file::SetContents(filename, parameters, file::Defaults()).ok(); + no_error_so_far = FileSetContents(filename, parameters).ok(); } if (no_error_so_far) { no_error_so_far = ReadParameterFile(filename); // We need to clean up the file even if ReadParameterFile() returned // false. In production we can continue even if the deletion failed. - if (!file::Delete(filename, file::Defaults()).ok()) { + if (!DeleteFile(filename).ok()) { LOG(DFATAL) << "Couldn't delete temporary parameters file: " << filename; } } if (!no_error_so_far) { LOG(WARNING) << "Error in SetSolverSpecificParametersAsString() " << "for solver type: " - << MPModelRequest::SolverType_Name( + << ProtoEnumToString( static_cast( solver_->ProblemType())); } return no_error_so_far; -#endif } bool MPSolverInterface::ReadParameterFile(const std::string& filename) { diff --git a/ortools/linear_solver/linear_solver.h b/ortools/linear_solver/linear_solver.h index 5b013cd9d0..4cc1c9a1ed 100644 --- a/ortools/linear_solver/linear_solver.h +++ b/ortools/linear_solver/linear_solver.h @@ -152,6 +152,7 @@ #include "ortools/glop/parameters.pb.h" #include "ortools/linear_solver/linear_expr.h" #include "ortools/linear_solver/linear_solver.pb.h" +#include "ortools/port/proto_utils.h" namespace operations_research { @@ -441,13 +442,13 @@ class MPSolver { bool LoadSolutionFromProto(const MPSolutionResponse& response); // ----- Export model to files or strings ----- -#ifndef ANDROID_JNI // Shortcuts to the homonymous MPModelProtoExporter methods, via // exporting to a MPModelProto with ExportModelToProto() (see above). + // + // Produces empty std::string on portable platforms (e.g. android, ios). bool ExportModelAsLpFormat(bool obfuscated, std::string* model_str); bool ExportModelAsMpsFormat(bool fixed_format, bool obfuscated, std::string* model_str); -#endif // ----- Misc ----- // Advanced usage: pass solver specific parameters in text format. The format @@ -637,13 +638,11 @@ class MPSolver { DISALLOW_COPY_AND_ASSIGN(MPSolver); }; -#ifndef ANDROID_JNI inline std::ostream& operator<<(std::ostream& os, MPSolver::ResultStatus status) { - return os << MPSolverResponseStatus_Name( + return os << ProtoEnumToString( static_cast(status)); } -#endif // The data structure used to store the coefficients of the contraints and of // the objective. Also define a type to facilitate iteration over them with: diff --git a/ortools/linear_solver/linear_solver.proto b/ortools/linear_solver/linear_solver.proto index 3bc4cca3c5..a6d1eedfff 100644 --- a/ortools/linear_solver/linear_solver.proto +++ b/ortools/linear_solver/linear_solver.proto @@ -32,6 +32,9 @@ syntax = "proto2"; option java_package = "com.google.ortools.linearsolver"; option java_multiple_files = true; +// import "google/protobuf/wrappers.proto"; +import "ortools/util/optional_boolean.proto"; + package operations_research; // A variable is always constrained in the form: @@ -135,15 +138,11 @@ message MPModelProto { optional PartialVariableAssignment solution_hint = 6; } -// A "three-way" boolean: unspecified, false or true. -enum OptionalBoolean { - BOOL_UNSPECIFIED = 0; - BOOL_FALSE = 1; - BOOL_TRUE = 2; -} - // To support 'unspecified' double value in proto3, the simplest is to wrap // any double value in a nested message (has_XXX works for message fields). +// We don't use google/protobuf/wrappers.proto because depending on it makes +// the following android integration test fail: +// http://sponge/c4bce1fd-41bd-4d0b-b4ca-fc04d4d64621 message OptionalDouble { optional double value = 1; } diff --git a/ortools/linear_solver/model_validator.cc b/ortools/linear_solver/model_validator.cc index 088a75a289..53eb06c5ca 100644 --- a/ortools/linear_solver/model_validator.cc +++ b/ortools/linear_solver/model_validator.cc @@ -17,20 +17,11 @@ #include #include "ortools/base/join.h" #include "ortools/base/accurate_sum.h" +#include "ortools/port/proto_utils.h" #include "ortools/util/fp_utils.h" namespace operations_research { namespace { -// This code is also used in the android export, which uses a lightweight proto -// library that doesn't have DebugString() nor ShortDebugString(). -template -std::string DebugString(const Proto& proto) { -#ifdef ANDROID_JNI - return std::string(""); -#else // ANDROID_JNI - return proto.ShortDebugString(); -#endif // ANDROID_JNI -} static const double kInfinity = std::numeric_limits::infinity(); @@ -42,19 +33,19 @@ std::string FindErrorInMPVariable(const MPVariableProto& variable) { variable.upper_bound() == -kInfinity || variable.lower_bound() > variable.upper_bound()) { return StrCat("Infeasible bounds: [", - LegacyPrecision(variable.lower_bound()), ", ", - LegacyPrecision(variable.upper_bound()), "]"); + absl::LegacyPrecision(variable.lower_bound()), ", ", + absl::LegacyPrecision(variable.upper_bound()), "]"); } if (variable.is_integer() && ceil(variable.lower_bound()) > floor(variable.upper_bound())) { return StrCat("Infeasible bounds for integer variable: [", - LegacyPrecision(variable.lower_bound()), ", ", - LegacyPrecision(variable.upper_bound()), "]", + absl::LegacyPrecision(variable.lower_bound()), ", ", + absl::LegacyPrecision(variable.upper_bound()), "]", " translate to the empty set"); } if (!std::isfinite(variable.objective_coefficient())) { return StrCat("Invalid objective_coefficient: ", - LegacyPrecision(variable.objective_coefficient())); + absl::LegacyPrecision(variable.objective_coefficient())); } return std::string(); } @@ -70,8 +61,8 @@ std::string FindErrorInMPConstraint(const MPConstraintProto& constraint, constraint.upper_bound() == -kInfinity || constraint.lower_bound() > constraint.upper_bound()) { return StrCat("Infeasible bounds: [", - LegacyPrecision(constraint.lower_bound()), ", ", - LegacyPrecision(constraint.upper_bound()), "]"); + absl::LegacyPrecision(constraint.lower_bound()), ", ", + absl::LegacyPrecision(constraint.upper_bound()), "]"); } // TODO(user): clarify explicitly, at least in a comment, whether we want @@ -91,7 +82,7 @@ std::string FindErrorInMPConstraint(const MPConstraintProto& constraint, } const double coeff = constraint.coefficient(i); if (!std::isfinite(coeff)) { - return StrCat("coefficient(", i, ")=", LegacyPrecision(coeff), + return StrCat("coefficient(", i, ")=", absl::LegacyPrecision(coeff), " is invalid"); } } @@ -134,7 +125,7 @@ std::string FindErrorInSolutionHint(const PartialVariableAssignment& solution_hi var_in_hint[var_index] = true; if (!std::isfinite(solution_hint.var_value(i))) { return StrCat("var_value(", i, - ")=", LegacyPrecision(solution_hint.var_value(i)), + ")=", absl::LegacyPrecision(solution_hint.var_value(i)), " is not a finite number"); } } @@ -152,7 +143,7 @@ std::string FindErrorInMPModelProto(const MPModelProto& model) { if (!std::isfinite(model.objective_offset())) { return StrCat("Invalid objective_offset: ", - LegacyPrecision(model.objective_offset())); + absl::LegacyPrecision(model.objective_offset())); } const int num_vars = model.variable_size(); const int num_cts = model.constraint_size(); @@ -163,7 +154,7 @@ std::string FindErrorInMPModelProto(const MPModelProto& model) { error = FindErrorInMPVariable(model.variable(i)); if (!error.empty()) { return StrCat("In variable #", i, ": ", error, ". Variable proto: ", - DebugString(model.variable(i))); + ProtobufShortDebugString(model.variable(i))); } } @@ -190,7 +181,7 @@ std::string FindErrorInMPModelProto(const MPModelProto& model) { constraint.coefficient_size(), ")."); } return StrCat("In constraint #", i, ": ", error, ". Constraint proto: ", - DebugString(constraint_light), suffix_str); + ProtobufShortDebugString(constraint_light), suffix_str); } } @@ -236,11 +227,11 @@ std::string FindFeasibilityErrorInSolutionHint(const MPModelProto& model, if (!IsSmallerWithinTolerance(value, ub, tolerance) || !IsSmallerWithinTolerance(lb, value, tolerance)) { return StrCat("Variable '", model.variable(var_index).name(), - "' is set to ", LegacyPrecision(value), + "' is set to ", absl::LegacyPrecision(value), " which is not in the variable bounds [", - LegacyPrecision(lb), ", ", LegacyPrecision(ub), + absl::LegacyPrecision(lb), ", ", absl::LegacyPrecision(ub), "] modulo a tolerance of ", - LegacyPrecision(tolerance), "."); + absl::LegacyPrecision(tolerance), "."); } } @@ -257,11 +248,11 @@ std::string FindFeasibilityErrorInSolutionHint(const MPModelProto& model, if (!IsSmallerWithinTolerance(activity.Value(), ub, tolerance) || !IsSmallerWithinTolerance(lb, activity.Value(), tolerance)) { return StrCat("Constraint '", model.constraint(cst_index).name(), - "' has activity ", LegacyPrecision(activity.Value()), + "' has activity ", absl::LegacyPrecision(activity.Value()), " which is not in the constraint bounds [", - LegacyPrecision(lb), ", ", LegacyPrecision(ub), + absl::LegacyPrecision(lb), ", ", absl::LegacyPrecision(ub), "] modulo a tolerance of ", - LegacyPrecision(tolerance), "."); + absl::LegacyPrecision(tolerance), "."); } } diff --git a/ortools/lp_data/lp_data.cc b/ortools/lp_data/lp_data.cc index 04194b3dbf..0eab11e284 100644 --- a/ortools/lp_data/lp_data.cc +++ b/ortools/lp_data/lp_data.cc @@ -589,9 +589,9 @@ std::string LinearProgram::DumpSolution(const DenseRow& variable_values) const { DCHECK_EQ(variable_values.size(), num_variables()); std::string output; for (ColIndex col(0); col < variable_values.size(); ++col) { - if (!output.empty()) StrAppend(&output, ", "); - StrAppend(&output, GetVariableName(col), " = ", - LegacyPrecision(variable_values[col])); + if (!output.empty()) absl::StrAppend(&output, ", "); + absl::StrAppend(&output, GetVariableName(col), " = ", + absl::LegacyPrecision(variable_values[col])); } return output; } @@ -678,7 +678,7 @@ void LinearProgram::AddSlackVariablesWhereNecessary( } const ColIndex slack_col = CreateNewSlackVariable( has_integer_slack_variable[row], -constraint_upper_bounds_[row], - -constraint_lower_bounds_[row], StrCat("s", row.value())); + -constraint_lower_bounds_[row], absl::StrCat("s", row.value())); SetCoefficient(row, slack_col, 1.0); SetConstraintBounds(row, 0.0, 0.0); } diff --git a/ortools/lp_data/lp_print_utils.cc b/ortools/lp_data/lp_print_utils.cc index eb50593f98..8f65d6899f 100644 --- a/ortools/lp_data/lp_print_utils.cc +++ b/ortools/lp_data/lp_print_utils.cc @@ -38,8 +38,8 @@ std::string StringifyRational(const double x, const double precision) { Fraction fraction = RationalApproximation(x, precision); const int64 numerator = fraction.first; const int64 denominator = fraction.second; - return denominator == 1 ? StrCat(numerator) - : StrCat(numerator, "/", denominator); + return denominator == 1 ? absl::StrCat(numerator) + : absl::StrCat(numerator, "/", denominator); } std::string Stringify(const Fractional x, bool fraction) { @@ -53,13 +53,13 @@ std::string Stringify(const Fractional x, bool fraction) { std::string StringifyMonomial(const Fractional a, const std::string& x, bool fraction) { if (a == 0.0) return ""; return a > 0.0 - ? StrCat( + ? absl::StrCat( " + ", - a == 1.0 ? x : StrCat(Stringify(a, fraction), " ", x)) - : StrCat( + a == 1.0 ? x : absl::StrCat(Stringify(a, fraction), " ", x)) + : absl::StrCat( " - ", a == -1.0 ? x - : StrCat(Stringify(-a, fraction), " ", x)); + : absl::StrCat(Stringify(-a, fraction), " ", x)); } } // namespace glop diff --git a/ortools/lp_data/mps_reader.cc b/ortools/lp_data/mps_reader.cc index 8035bc0490..38bf8e2c5b 100644 --- a/ortools/lp_data/mps_reader.cc +++ b/ortools/lp_data/mps_reader.cc @@ -25,12 +25,13 @@ #include "ortools/base/logging.h" #include "ortools/base/stringprintf.h" #include "ortools/base/file.h" -#include "ortools/base/filelinereader.h" +#include "ortools/util/filelineiter.h" #include "ortools/base/numbers.h" // for safe_strtod #include "ortools/base/split.h" #include "ortools/base/strutil.h" #include "ortools/base/map_util.h" // for FindOrNull, FindWithDefault #include "ortools/lp_data/lp_print_utils.h" +#include "ortools/util/filelineiter.h" #include "ortools/base/status.h" DEFINE_bool(mps_free_form, false, "Read MPS files in free form."); @@ -108,7 +109,7 @@ void MPSReader::DisplaySummary() { void MPSReader::SplitLineIntoFields() { if (free_form_) { - fields_ = strings::Split(line_, ' ', strings::SkipEmpty()); + fields_ = strings::Split(line_, ' ', absl::SkipEmpty()); CHECK_GE(kNumFields, fields_.size()); } else { int length = line_.length(); @@ -141,21 +142,13 @@ bool MPSReader::LoadFile(const std::string& file_name, LinearProgram* data) { Reset(); data_ = data; data_->Clear(); - std::ifstream tmp_file(file_name.c_str()); - const bool file_exists = tmp_file.good(); - tmp_file.close(); - if (file_exists) { - FileLineReader reader(file_name.c_str()); - reader.set_line_callback( - NewPermanentCallback(this, &MPSReader::ProcessLine)); - reader.Reload(); - data->CleanUp(); - DisplaySummary(); - return reader.loaded_successfully() && parse_success_; - } else { - LOG(DFATAL) << "File not found: " << file_name; - return false; + for (const std::string& line : + FileLines(file_name, FileLineIterator::REMOVE_INLINE_CR)) { + ProcessLine(line); } + data->CleanUp(); + DisplaySummary(); + return parse_success_; } // TODO(user): Ideally have a method to compare instances of LinearProgram @@ -199,7 +192,7 @@ bool MPSReader::IsCommentOrBlank() const { return true; } -void MPSReader::ProcessLine(char* line) { +void MPSReader::ProcessLine(const std::string& line) { ++line_num_; if (!parse_success_ && FLAGS_mps_stop_after_first_error) return; line_ = line; @@ -207,7 +200,7 @@ void MPSReader::ProcessLine(char* line) { return; // Skip blank lines and comments. } std::string section; - if (*line != '\0' && *line != ' ') { + if (line[0] != '\0' && line[0] != ' ') { section = GetFirstWord(); section_ = FindWithDefault(section_name_to_id_map_, section, UNKNOWN_SECTION); diff --git a/ortools/lp_data/mps_reader.h b/ortools/lp_data/mps_reader.h index 6a09b5cc91..dfa0d740cc 100644 --- a/ortools/lp_data/mps_reader.h +++ b/ortools/lp_data/mps_reader.h @@ -117,7 +117,7 @@ class MPSReader { int GetFieldOffset() const { return free_form_ ? fields_.size() & 1 : 0; } // Line processor. - void ProcessLine(char* line); + void ProcessLine(const std::string& line); // Process section NAME in the MPS file. void ProcessNameSection(); diff --git a/ortools/lp_data/sparse_vector.h b/ortools/lp_data/sparse_vector.h index 3af3e5f29a..ab5881b2c3 100644 --- a/ortools/lp_data/sparse_vector.h +++ b/ortools/lp_data/sparse_vector.h @@ -302,7 +302,7 @@ class SparseVector { // for (const EntryIndex i : sparse_vector.AllEntryIndices()) { ... } // TODO(user): consider removing this, in favor of the natural range // iteration. - ::util::IntegerRange AllEntryIndices() const { + ::util::IntegerRange AllEntryIndices() const { return ::util::IntegerRange(EntryIndex(0), num_entries_); } diff --git a/ortools/port/file.h b/ortools/port/file.h new file mode 100644 index 0000000000..6d31842ee7 --- /dev/null +++ b/ortools/port/file.h @@ -0,0 +1,33 @@ +// Copyright 2010-2017 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef OR_TOOLS_PORT_FILE_H_ +#define OR_TOOLS_PORT_FILE_H_ + +#include "ortools/base/string_view.h" +#include "ortools/base/status.h" + +namespace operations_research { + +// See ortools/base/file.h +::util::Status FileSetContents(absl::string_view file_name, + absl::string_view content); + +::util::Status FileGetContents(absl::string_view file_name, std::string* output); + +::util::Status DeleteFile(absl::string_view file_name); + + +} // namespace operations_research + +#endif // OR_TOOLS_PORT_FILE_H_ diff --git a/ortools/port/file_nonport.cc b/ortools/port/file_nonport.cc new file mode 100644 index 0000000000..aacacac0d4 --- /dev/null +++ b/ortools/port/file_nonport.cc @@ -0,0 +1,34 @@ +// Copyright 2010-2017 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ortools/port/file.h" + +#include "ortools/base/file.h" + +namespace operations_research { + +::util::Status FileSetContents(absl::string_view file_name, + absl::string_view content) { + return file::SetContents(file_name, content, file::Defaults()); +} + +::util::Status FileGetContents(absl::string_view file_name, std::string* output) { + return file::GetContents(file_name, output, file::Defaults()); +} + + +::util::Status DeleteFile(absl::string_view file_name) { + return file::Delete(file_name, file::Defaults()); +} + +} // namespace operations_research diff --git a/ortools/port/proto_utils.h b/ortools/port/proto_utils.h index 4273e9772c..369b97f1a6 100644 --- a/ortools/port/proto_utils.h +++ b/ortools/port/proto_utils.h @@ -36,7 +36,7 @@ std::string ProtobufShortDebugString(const P& message) { template std::string ProtoEnumToString(ProtoEnumType enum_value) { - return StrCat(enum_value); + return absl::StrCat(enum_value); } #else // __PORTABLE_PLATFORM__ @@ -53,9 +53,9 @@ std::string ProtobufShortDebugString(const P& message) { template std::string ProtoEnumToString(ProtoEnumType enum_value) { auto enum_descriptor = google::protobuf::GetEnumDescriptor(); - auto enum_value_descriptor = enum_descriptor->value(enum_value); + auto enum_value_descriptor = enum_descriptor->FindValueByNumber(enum_value); if (enum_value_descriptor == nullptr) { - return StrCat( + return absl::StrCat( "Invalid enum value of: ", enum_value, " for enum type: ", google::protobuf::GetEnumDescriptor()->name()); } diff --git a/ortools/sat/clause.cc b/ortools/sat/clause.cc index 069150eb3e..c5c8edaf9b 100644 --- a/ortools/sat/clause.cc +++ b/ortools/sat/clause.cc @@ -70,7 +70,6 @@ void LiteralWatchers::Resize(int num_variables) { watchers_on_false_.resize(num_variables << 1); reasons_.resize(num_variables); needs_cleaning_.Resize(LiteralIndex(num_variables << 1)); - statistics_.resize(num_variables); } // Note that this is the only place where we add Watcher so the DCHECK @@ -154,7 +153,7 @@ bool LiteralWatchers::PropagateOnFalse(Literal false_literal, Trail* trail) { *new_it++ = *it; } } - num_inspected_clause_literals_ += watchers.size(); + num_inspected_clause_literals_ += watchers.size(); // The blocking ones. watchers.erase(new_it, watchers.end()); return true; } @@ -168,7 +167,7 @@ bool LiteralWatchers::Propagate(Trail* trail) { return true; } -gtl::Span LiteralWatchers::Reason(const Trail& trail, +absl::Span LiteralWatchers::Reason(const Trail& trail, int trail_index) const { return reasons_[trail_index]->PropagationReason(); } @@ -180,24 +179,12 @@ SatClause* LiteralWatchers::ReasonClause(int trail_index) const { bool LiteralWatchers::AttachAndPropagate(SatClause* clause, Trail* trail) { SCOPED_TIME_STAT(&stats_); ++num_watched_clauses_; - // Updating the statistics for each learned clause take quite a lot of time - // (like 6% of the total running time). So for now, we just compute the - // statistics depending on the problem clauses. Note that this do not take - // into account the other type of constraint though. - // - // TODO(user): This is actually not used a lot with the default parameters. - // Find a better way for the "initial" polarity choice and tie breaking - // between literal choices. Note that it seems none of the modern SAT solver - // relies on this. - if (!clause->IsRedundant()) UpdateStatistics(*clause, /*added=*/true); - clause->SortLiterals(statistics_, parameters_); return clause->AttachAndEnqueuePotentialUnitPropagation(trail, this); } void LiteralWatchers::LazyDetach(SatClause* clause) { SCOPED_TIME_STAT(&stats_); --num_watched_clauses_; - if (!clause->IsRedundant()) UpdateStatistics(*clause, /*added=*/false); clause->LazyDetach(); is_clean_ = false; needs_cleaning_.Set(clause->FirstLiteral().Index()); @@ -215,22 +202,6 @@ void LiteralWatchers::CleanUpWatchers() { is_clean_ = true; } -void LiteralWatchers::UpdateStatistics(const SatClause& clause, bool added) { - SCOPED_TIME_STAT(&stats_); - for (const Literal literal : clause) { - const BooleanVariable var = literal.Variable(); - const int direction = added ? 1 : -1; - statistics_[var].num_appearances += direction; - statistics_[var].weighted_num_appearances += - 1.0 / clause.Size() * direction; - if (literal.IsPositive()) { - statistics_[var].num_positive_clauses += direction; - } else { - statistics_[var].num_negative_clauses += direction; - } - } -} - // ----- BinaryImplicationGraph ----- void BinaryImplicationGraph::Resize(int num_variables) { @@ -308,7 +279,7 @@ bool BinaryImplicationGraph::Propagate(Trail* trail) { return true; } -gtl::Span BinaryImplicationGraph::Reason( +absl::Span BinaryImplicationGraph::Reason( const Trail& trail, int trail_index) const { return {&reasons_[trail_index], 1}; } @@ -562,8 +533,7 @@ void BinaryImplicationGraph::RemoveFixedVariables( // ----- SatClause ----- // static -SatClause* SatClause::Create(const std::vector& literals, - bool is_redundant) { +SatClause* SatClause::Create(const std::vector& literals) { CHECK_GE(literals.size(), 2); SatClause* clause = reinterpret_cast( ::operator new(sizeof(SatClause) + literals.size() * sizeof(Literal))); @@ -571,8 +541,6 @@ SatClause* SatClause::Create(const std::vector& literals, for (int i = 0; i < literals.size(); ++i) { clause->literals_[i] = literals[i]; } - clause->is_redundant_ = is_redundant; - clause->is_attached_ = false; return clause; } @@ -580,7 +548,7 @@ SatClause* SatClause::Create(const std::vector& literals, // any of the watched literal is assigned, then the clause is necessarily true. bool SatClause::RemoveFixedLiteralsAndTestIfTrue( const VariablesAssignment& assignment) { - DCHECK(is_attached_); + DCHECK(IsAttached()); if (assignment.VariableIsAssigned(literals_[0].Variable()) || assignment.VariableIsAssigned(literals_[1].Variable())) { DCHECK(IsSatisfied(assignment)); @@ -602,68 +570,8 @@ bool SatClause::RemoveFixedLiteralsAndTestIfTrue( return false; } -namespace { - -// Support struct to sort literals for ordering. -struct WeightedLiteral { - WeightedLiteral(Literal l, int w) : literal(l), weight(w) {} - - Literal literal; - int weight; -}; - -// Lexical order, by smaller weight, then by smaller literal to break ties. -bool LiteralWithSmallerWeightFirst(const WeightedLiteral& wv1, - const WeightedLiteral& wv2) { - return (wv1.weight < wv2.weight) || - (wv1.weight == wv2.weight && - wv1.literal.SignedValue() < wv2.literal.SignedValue()); -} - -// Lexical order, by larger weight, then by smaller literal to break ties. -bool LiteralWithLargerWeightFirst(const WeightedLiteral& wv1, - const WeightedLiteral& wv2) { - return (wv1.weight > wv2.weight) || - (wv1.weight == wv2.weight && - wv1.literal.SignedValue() < wv2.literal.SignedValue()); -} - -} // namespace - -void SatClause::SortLiterals( - const ITIVector& statistics, - const SatParameters& parameters) { - DCHECK(!IsAttached()); - const SatParameters::LiteralOrdering literal_order = - parameters.literal_ordering(); - if (literal_order != SatParameters::LITERAL_IN_ORDER) { - std::vector order; - for (Literal literal : *this) { - int weight = literal.IsPositive() - ? statistics[literal.Variable()].num_positive_clauses - : statistics[literal.Variable()].num_negative_clauses; - order.push_back(WeightedLiteral(literal, weight)); - } - switch (literal_order) { - case SatParameters::VAR_MIN_USAGE: { - std::sort(order.begin(), order.end(), LiteralWithSmallerWeightFirst); - break; - } - case SatParameters::VAR_MAX_USAGE: { - std::sort(order.begin(), order.end(), LiteralWithLargerWeightFirst); - break; - } - default: { break; } - } - for (int i = 0; i < order.size(); ++i) { - literals_[i] = order[i].literal; - } - } -} - bool SatClause::AttachAndEnqueuePotentialUnitPropagation( Trail* trail, LiteralWatchers* demons) { - CHECK(!IsAttached()); // Select the first two literals that are not assigned to false and put them // on position 0 and 1. int num_literal_not_false = 0; @@ -702,7 +610,6 @@ bool SatClause::AttachAndEnqueuePotentialUnitPropagation( } // Attach the watchers. - is_attached_ = true; demons->AttachOnFalse(literals_[0], literals_[1], this); demons->AttachOnFalse(literals_[1], literals_[0], this); return true; diff --git a/ortools/sat/clause.h b/ortools/sat/clause.h index 940a9d08d5..6ced3a35a7 100644 --- a/ortools/sat/clause.h +++ b/ortools/sat/clause.h @@ -58,15 +58,16 @@ struct VariableInfo { }; // This is how the SatSolver stores a clause. A clause is just a disjunction of -// literals. In many places, we just use std::vector to encode one. However, -// the solver needs to keep a few extra fields attached to each clause. +// literals. In many places, we just use std::vector to encode one. But in +// the critical propagation code, we use this class to remove one memory +// indirection. class SatClause { public: // Creates a sat clause. There must be at least 2 literals. Smaller clause are - // treated separatly and never constructed. A redundant clause can be removed - // without changing the problem. - static SatClause* Create(const std::vector& literals, - bool is_redundant); + // treated separatly and never constructed. In practice, we do use + // BinaryImplicationGraph for the clause of size 2, so this is mainly used for + // size at least 3. + static SatClause* Create(const std::vector& literals); // Non-sized delete because this is a tail-padded class. void operator delete(void* p) { @@ -93,9 +94,9 @@ class SatClause { // Returns the reason for the last unit propagation of this clause. The // preconditions are the same as for PropagatedLiteral(). - gtl::Span PropagationReason() const { + absl::Span PropagationReason() const { // Note that we don't need to include the propagated literal. - return gtl::Span(&(literals_[1]), size_ - 1); + return absl::Span(&(literals_[1]), size_ - 1); } // Removes literals that are fixed. This should only be called at level 0 @@ -106,46 +107,34 @@ class SatClause { // old_size) of literals(). bool RemoveFixedLiteralsAndTestIfTrue(const VariablesAssignment& assignment); - // True if the clause can be safely removed without changing the current - // problem. Usually the clause we learn during the search are redundant since - // the original clauses are enough to define the problem. - bool IsRedundant() const { return is_redundant_; } - // Returns true if the clause is satisfied for the given assignment. Note that // the assignment may be partial, so false does not mean that the clause can't // be satisfied by completing the assignment. bool IsSatisfied(const VariablesAssignment& assignment) const; - // Sorts the literals of the clause depending on the given parameters and - // statistics. Do not call this on an attached clause. - void SortLiterals(const ITIVector& statistics, - const SatParameters& parameters); - // Sets up the 2-watchers data structure. It selects two non-false literals // and attaches the clause to the event: one of the watched literals become // false. It returns false if the clause only contains literals assigned to // false. If only one literals is not false, it propagates it to true if it // is not already assigned. + // + // This MUST be called just once just after a clause has been created. + // TODO(user): merge with Create()? bool AttachAndEnqueuePotentialUnitPropagation(Trail* trail, LiteralWatchers* demons); // Returns true if the clause is attached to a LiteralWatchers. - bool IsAttached() const { return is_attached_; } + bool IsAttached() const { return size_ > 0; } // Marks the clause so that the next call to CleanUpWatchers() can identify it - // and actually detach it. - void LazyDetach() { is_attached_ = false; } + // and actually detach it. We use size_ = 0 for this since the clause will + // never be used afterwards. + void LazyDetach() { size_ = 0; } std::string DebugString() const; private: - // The data is packed so that only 4 bytes are used for these fields. - // - // TODO(user): It should be possible to remove one or both of the Booleans. - // That may speed up the code slightly. - bool is_redundant_ : 1; - bool is_attached_ : 1; - unsigned int size_ : 30; + int32 size_; // This class store the literals inline, and literals_ mark the starts of the // variable length portion. @@ -163,7 +152,7 @@ class LiteralWatchers : public SatPropagator { ~LiteralWatchers() override; bool Propagate(Trail* trail) final; - gtl::Span Reason(const Trail& trail, + absl::Span Reason(const Trail& trail, int trail_index) const final; // Resizes the data structure. @@ -192,12 +181,6 @@ class LiteralWatchers : public SatPropagator { // Number of clauses currently watched. int64 num_watched_clauses() const { return num_watched_clauses_; } - // Returns some statistics on the number of appearance of this variable in - // all the attached clauses. - const VariableInfo& VariableStatistic(BooleanVariable var) const { - return statistics_[var]; - } - // Parameters management. void SetParameters(const SatParameters& parameters) { parameters_ = parameters; @@ -224,10 +207,6 @@ class LiteralWatchers : public SatPropagator { reasons_[trail_index] = clause; } - // Updates statistics_ for the literals in the given clause. added indicates - // if we are adding the clause or deleting it. - void UpdateStatistics(const SatClause& clause, bool added); - // Contains, for each literal, the list of clauses that need to be inspected // when the corresponding literal becomes false. struct Watcher { @@ -246,7 +225,6 @@ class LiteralWatchers : public SatPropagator { SparseBitset needs_cleaning_; bool is_clean_; - ITIVector statistics_; SatParameters parameters_; int64 num_inspected_clauses_; int64 num_inspected_clause_literals_; @@ -344,7 +322,7 @@ class BinaryImplicationGraph : public SatPropagator { } bool Propagate(Trail* trail) final; - gtl::Span Reason(const Trail& trail, + absl::Span Reason(const Trail& trail, int trail_index) const final; // Resizes the data structure. @@ -440,7 +418,7 @@ class BinaryImplicationGraph : public SatPropagator { // // TODO(user): We could be even more efficient since a size of int32 is enough // for us and we could store in common the inlined/not-inlined size. - ITIVector> implications_; + ITIVector> implications_; int64 num_implications_; // Some stats. diff --git a/ortools/sat/cp_model_checker.cc b/ortools/sat/cp_model_checker.cc index c4ca0a412c..7bad6964eb 100644 --- a/ortools/sat/cp_model_checker.cc +++ b/ortools/sat/cp_model_checker.cc @@ -70,25 +70,25 @@ bool LiteralReferenceIsValid(const CpModelProto& model, int reference) { std::string ValidateIntegerVariable(const CpModelProto& model, int v) { const IntegerVariableProto& proto = model.variables(v); if (proto.domain_size() == 0) { - return StrCat("var #", v, + return absl::StrCat("var #", v, " has no domain(): ", ProtobufShortDebugString(proto)); } if (proto.domain_size() % 2 != 0) { - return StrCat("var #", v, " has an odd domain() size: ", + return absl::StrCat("var #", v, " has an odd domain() size: ", ProtobufShortDebugString(proto)); } if (!DomainInProtoIsValid(proto)) { - return StrCat("var #", v, " has and invalid domain() format: ", + return absl::StrCat("var #", v, " has and invalid domain() format: ", ProtobufShortDebugString(proto)); } if (!proto.enforcement_literal().empty()) { if (proto.enforcement_literal_size() > 1) { - return StrCat("var #", v, + return absl::StrCat("var #", v, " has more than one enforcement_literal: ", ProtobufShortDebugString(proto)); } if (!LiteralReferenceIsValid(model, proto.enforcement_literal(0))) { - return StrCat("var #", v, " has an invalid enforcement_literal: ", + return absl::StrCat("var #", v, " has an invalid enforcement_literal: ", ProtobufShortDebugString(proto)); } } @@ -102,37 +102,37 @@ std::string ValidateArgumentReferencesInConstraint(const CpModelProto& model, AddReferencesUsedByConstraint(ct, &references); for (const int v : references.variables) { if (!VariableReferenceIsValid(model, v)) { - return StrCat("Out of bound integer variable ", v, + return absl::StrCat("Out of bound integer variable ", v, " in constraint #", c, " : ", ProtobufShortDebugString(ct)); } } if (ct.enforcement_literal_size() > 1) { - return StrCat("More than one enforcement_literal in constraint #", c, + return absl::StrCat("More than one enforcement_literal in constraint #", c, " : ", ProtobufShortDebugString(ct)); } if (ct.enforcement_literal_size() == 1) { const int lit = ct.enforcement_literal(0); if (!LiteralReferenceIsValid(model, lit)) { - return StrCat("Invalid enforcement literal ", lit, + return absl::StrCat("Invalid enforcement literal ", lit, " in constraint #", c, " : ", ProtobufShortDebugString(ct)); } } for (const int lit : references.literals) { if (!LiteralReferenceIsValid(model, lit)) { - return StrCat("Invalid literal ", lit, " in constraint #", c, " : ", + return absl::StrCat("Invalid literal ", lit, " in constraint #", c, " : ", ProtobufShortDebugString(ct)); } } for (const int i : references.intervals) { if (i < 0 || i >= model.constraints_size()) { - return StrCat("Out of bound interval ", i, " in constraint #", c, + return absl::StrCat("Out of bound interval ", i, " in constraint #", c, " : ", ProtobufShortDebugString(ct)); } if (model.constraints(i).constraint_case() != ConstraintProto::ConstraintCase::kInterval) { - return StrCat( + return absl::StrCat( "Interval ", i, " does not refer to an interval constraint. Problematic constraint #", c, " : ", ProtobufShortDebugString(ct)); @@ -176,7 +176,7 @@ std::string ValidateReservoirConstraint(const CpModelProto& model, const IntegerVariableProto& time = model.variables(t); for (const int64 bound : time.domain()) { if (bound < 0) { - return StrCat("Time variables must be >= 0 in constraint ", + return absl::StrCat("Time variables must be >= 0 in constraint ", ProtobufShortDebugString(ct)); } } @@ -196,7 +196,7 @@ std::string ValidateCircuitCoveringConstraint(const ConstraintProto& ct) { const int num_nodes = ct.circuit_covering().nexts_size(); for (const int d : ct.circuit_covering().distinguished_nodes()) { if (d < 0 || d >= num_nodes) { - return StrCat("Distinguished node ", d, " not in [0, ", num_nodes, + return absl::StrCat("Distinguished node ", d, " not in [0, ", num_nodes, ")."); } } @@ -252,11 +252,11 @@ std::string ValidateCpModel(const CpModelProto& model) { switch (type) { case ConstraintProto::ConstraintCase::kLinear: if (!DomainInProtoIsValid(ct.linear())) { - return StrCat("Invalid domain in constraint #", c, " : ", + return absl::StrCat("Invalid domain in constraint #", c, " : ", ProtobufShortDebugString(ct)); } if (ct.linear().coeffs_size() != ct.linear().vars_size()) { - return StrCat("coeffs_size() != vars_size() in constraint #", c, + return absl::StrCat("coeffs_size() != vars_size() in constraint #", c, " : ", ProtobufShortDebugString(ct)); } RETURN_IF_NOT_EMPTY(ValidateLinearConstraint(model, ct)); @@ -264,7 +264,7 @@ std::string ValidateCpModel(const CpModelProto& model) { case ConstraintProto::ConstraintCase::kCumulative: if (ct.cumulative().intervals_size() != ct.cumulative().demands_size()) { - return StrCat( + return absl::StrCat( "intervals_size() != demands_size() in constraint #", c, " : ", ProtobufShortDebugString(ct)); } diff --git a/ortools/sat/cp_model_presolve.cc b/ortools/sat/cp_model_presolve.cc index d8befdaa11..517b344b81 100644 --- a/ortools/sat/cp_model_presolve.cc +++ b/ortools/sat/cp_model_presolve.cc @@ -28,6 +28,7 @@ #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" +#include "ortools/base/port.h" #include "ortools/base/join.h" #include #include "ortools/base/map_util.h" diff --git a/ortools/sat/cp_model_search.h b/ortools/sat/cp_model_search.h index a9555c13d8..b2878feb60 100644 --- a/ortools/sat/cp_model_search.h +++ b/ortools/sat/cp_model_search.h @@ -20,6 +20,7 @@ #include "ortools/base/integral_types.h" #include "ortools/sat/cp_model.pb.h" #include "ortools/sat/integer.h" +#include "ortools/sat/integer_search.h" #include "ortools/sat/model.h" namespace operations_research { diff --git a/ortools/sat/cp_model_solver.cc b/ortools/sat/cp_model_solver.cc index 6d0ff9f2fa..9e4e84595e 100644 --- a/ortools/sat/cp_model_solver.cc +++ b/ortools/sat/cp_model_solver.cc @@ -1418,10 +1418,10 @@ std::string CpModelStats(const CpModelProto& model_proto) { } std::string result; - StrAppend(&result, "Model '", model_proto.name(), "':\n"); + absl::StrAppend(&result, "Model '", model_proto.name(), "':\n"); for (const DecisionStrategyProto& strategy : model_proto.search_strategy()) { - StrAppend( + absl::StrAppend( &result, "Search strategy: on ", strategy.variables_size(), " variables, ", ProtoEnumToString( @@ -1432,12 +1432,12 @@ std::string CpModelStats(const CpModelProto& model_proto) { "\n"); } - StrAppend(&result, "#Variables: ", model_proto.variables_size(), "\n"); + absl::StrAppend(&result, "#Variables: ", model_proto.variables_size(), "\n"); if (num_vars_per_domains.size() < 20) { for (const auto& entry : num_vars_per_domains) { - const std::string temp = StrCat(" - ", entry.second, " in ", + const std::string temp = absl::StrCat(" - ", entry.second, " in ", IntervalsAsString(entry.first), "\n"); - StrAppend(&result, Summarize(temp)); + absl::StrAppend(&result, Summarize(temp)); } } else { int64 max_complexity = 0; @@ -1449,40 +1449,40 @@ std::string CpModelStats(const CpModelProto& model_proto) { max_complexity = std::max(max_complexity, static_cast(entry.first.size())); } - StrAppend(&result, " - ", num_vars_per_domains.size(), + absl::StrAppend(&result, " - ", num_vars_per_domains.size(), " different domains in [", min, ",", max, "] with a largest complexity of ", max_complexity, ".\n"); } if (num_constants > 0) { const std::string temp = - StrCat(" - ", num_constants, " constants in {", - StrJoin(constant_values, ","), "} \n"); - StrAppend(&result, Summarize(temp)); + absl::StrCat(" - ", num_constants, " constants in {", + absl::StrJoin(constant_values, ","), "} \n"); + absl::StrAppend(&result, Summarize(temp)); } const VariableUsage usage = ComputeVariableUsage(model_proto); - StrAppend(&result, "#Booleans: ", usage.booleans.size(), "\n"); - StrAppend(&result, "#Integers: ", usage.integers.size(), "\n"); + absl::StrAppend(&result, "#Booleans: ", usage.booleans.size(), "\n"); + absl::StrAppend(&result, "#Integers: ", usage.integers.size(), "\n"); std::vector constraints; constraints.reserve(num_constraints_by_type.size()); for (const auto entry : num_constraints_by_type) { constraints.push_back( - StrCat("#", ConstraintCaseName(entry.first), ": ", entry.second, + absl::StrCat("#", ConstraintCaseName(entry.first), ": ", entry.second, " (", num_reif_constraints_by_type[entry.first], " with enforcement literal)")); } std::sort(constraints.begin(), constraints.end()); - StrAppend(&result, StrJoin(constraints, "\n")); + absl::StrAppend(&result, absl::StrJoin(constraints, "\n")); return result; } std::string CpSolverResponseStats(const CpSolverResponse& response) { std::string result; - StrAppend(&result, "CpSolverResponse:"); - StrAppend(&result, "\nstatus: ", + absl::StrAppend(&result, "CpSolverResponse:"); + absl::StrAppend(&result, "\nstatus: ", ProtoEnumToString(response.status())); // We special case the pure-decision problem for clarity. @@ -1492,32 +1492,32 @@ std::string CpSolverResponseStats(const CpSolverResponse& response) { // objective is zero... if (response.status() != CpSolverStatus::OPTIMAL && response.objective_value() == 0 && response.best_objective_bound() == 0) { - StrAppend(&result, "\nobjective: NA"); - StrAppend(&result, "\nbest_bound: NA"); + absl::StrAppend(&result, "\nobjective: NA"); + absl::StrAppend(&result, "\nbest_bound: NA"); } else { - StrAppend(&result, "\nobjective: ", - LegacyPrecision(response.objective_value())); - StrAppend(&result, "\nbest_bound: ", - LegacyPrecision(response.best_objective_bound())); + absl::StrAppend(&result, "\nobjective: ", + absl::LegacyPrecision(response.objective_value())); + absl::StrAppend(&result, "\nbest_bound: ", + absl::LegacyPrecision(response.best_objective_bound())); } - StrAppend(&result, "\nbooleans: ", response.num_booleans()); - StrAppend(&result, "\nconflicts: ", response.num_conflicts()); - StrAppend(&result, "\nbranches: ", response.num_branches()); + absl::StrAppend(&result, "\nbooleans: ", response.num_booleans()); + absl::StrAppend(&result, "\nconflicts: ", response.num_conflicts()); + absl::StrAppend(&result, "\nbranches: ", response.num_branches()); // TODO(user): This is probably better named "binary_propagation", but we just // output "propagations" to be consistent with sat/analyze.sh. - StrAppend(&result, + absl::StrAppend(&result, "\npropagations: ", response.num_binary_propagations()); - StrAppend( + absl::StrAppend( &result, "\ninteger_propagations: ", response.num_integer_propagations()); - StrAppend(&result, - "\nwalltime: ", LegacyPrecision(response.wall_time())); - StrAppend(&result, - "\nusertime: ", LegacyPrecision(response.user_time())); - StrAppend(&result, "\ndeterministic_time: ", - LegacyPrecision(response.deterministic_time())); - StrAppend(&result, "\n"); + absl::StrAppend(&result, + "\nwalltime: ", absl::LegacyPrecision(response.wall_time())); + absl::StrAppend(&result, + "\nusertime: ", absl::LegacyPrecision(response.user_time())); + absl::StrAppend(&result, "\ndeterministic_time: ", + absl::LegacyPrecision(response.deterministic_time())); + absl::StrAppend(&result, "\n"); return result; } @@ -2134,7 +2134,7 @@ IntegerVariable AddLPConstraints(const CpModelProto& model_proto, // Register LP constraints. Note that this needs to be done after all the // constraints have been added. for (auto* lp_constraint : lp_constraints) { - lp_constraint->RegisterWith(m->GetOrCreate()); + lp_constraint->RegisterWith(m->model()); VLOG(1) << "LP constraint: " << lp_constraint->DimensionString() << "."; } diff --git a/ortools/sat/drat.cc b/ortools/sat/drat.cc index 2ef0f15d4b..1360083861 100644 --- a/ortools/sat/drat.cc +++ b/ortools/sat/drat.cc @@ -64,18 +64,18 @@ void DratWriter::AddOneVariable() { reverse_mapping_.push_back(BooleanVariable(variable_index_++)); } -void DratWriter::AddClause(gtl::Span clause) { +void DratWriter::AddClause(absl::Span clause) { WriteClause(clause); } -void DratWriter::DeleteClause(gtl::Span clause, +void DratWriter::DeleteClause(absl::Span clause, bool ignore_call) { if (ignore_call) return; buffer_ += "d "; WriteClause(clause); } -void DratWriter::WriteClause(gtl::Span clause) { +void DratWriter::WriteClause(absl::Span clause) { values_.clear(); for (const Literal l : clause) { CHECK_LT(l.Variable(), reverse_mapping_.size()); diff --git a/ortools/sat/drat.h b/ortools/sat/drat.h index 5395b79262..edfd73e7c0 100644 --- a/ortools/sat/drat.h +++ b/ortools/sat/drat.h @@ -59,7 +59,7 @@ class DratWriter { // newer variables always comes first. This is needed because in the DRAT // format, the clause is checked for the RAT property with only its first // literal. - void AddClause(gtl::Span clause); + void AddClause(absl::Span clause); // Writes a "deletion" information about a clause that has been added before // to the DRAT output. Note that it is also possible to delete a clause from @@ -74,10 +74,10 @@ class DratWriter { // // TODO(user): an alternative would be to call AddClause() on all the problem // clause first. - void DeleteClause(gtl::Span clause, bool ignore_call = true); + void DeleteClause(absl::Span clause, bool ignore_call = true); private: - void WriteClause(gtl::Span clause); + void WriteClause(absl::Span clause); // We need to keep track of the variable newly created. int variable_index_; diff --git a/ortools/sat/integer.cc b/ortools/sat/integer.cc index 235615532a..fca00ca9e1 100644 --- a/ortools/sat/integer.cc +++ b/ortools/sat/integer.cc @@ -356,13 +356,13 @@ bool IntegerTrail::Propagate(Trail* trail) { const int level = trail->CurrentDecisionLevel(); for (ReversibleInterface* rev : reversible_classes_) rev->SetLevel(level); - // Make sure that our internal "integer_decision_levels_" size matches the - // sat decision levels. At the level zero, integer_decision_levels_ should + // Make sure that our internal "integer_search_levels_" size matches the + // sat decision levels. At the level zero, integer_search_levels_ should // be empty. - if (level > integer_decision_levels_.size()) { - integer_decision_levels_.push_back(integer_trail_.size()); + if (level > integer_search_levels_.size()) { + integer_search_levels_.push_back(integer_trail_.size()); reason_decision_levels_.push_back(literals_reason_starts_.size()); - CHECK_EQ(trail->CurrentDecisionLevel(), integer_decision_levels_.size()); + CHECK_EQ(trail->CurrentDecisionLevel(), integer_search_levels_.size()); } // This is used to map any integer literal out of the initial variable domain @@ -393,9 +393,9 @@ void IntegerTrail::Untrail(const Trail& trail, int literal_trail_index) { // Note that if a conflict was detected before Propagate() of this class was // even called, it is possible that there is nothing to backtrack. - if (level >= integer_decision_levels_.size()) return; - const int target = integer_decision_levels_[level]; - integer_decision_levels_.resize(level); + if (level >= integer_search_levels_.size()) return; + const int target = integer_search_levels_[level]; + integer_search_levels_.resize(level); CHECK_GE(target, vars_.size()); CHECK_LE(target, integer_trail_.size()); @@ -425,7 +425,7 @@ IntegerVariable IntegerTrail::AddIntegerVariable(IntegerValue lower_bound, CHECK_LE(lower_bound, kMaxIntegerValue); CHECK_GE(upper_bound, kMinIntegerValue); CHECK_LE(upper_bound, kMaxIntegerValue); - CHECK(integer_decision_levels_.empty()); + CHECK(integer_search_levels_.empty()); CHECK_EQ(vars_.size(), integer_trail_.size()); const IntegerVariable i(vars_.size()); @@ -608,11 +608,11 @@ int IntegerTrail::FindLowestTrailIndexThatExplainBound( bool IntegerTrail::EnqueueAssociatedLiteral( Literal literal, int trail_index_with_same_reason, - gtl::Span literal_reason, - gtl::Span integer_reason, + absl::Span literal_reason, + absl::Span integer_reason, BooleanVariable* variable_with_same_reason) { if (!trail_->Assignment().VariableIsAssigned(literal.Variable())) { - if (integer_decision_levels_.empty()) { + if (integer_search_levels_.empty()) { trail_->EnqueueWithUnitReason(literal); return true; } @@ -658,8 +658,8 @@ bool IntegerTrail::EnqueueAssociatedLiteral( namespace { -std::string ReasonDebugString(gtl::Span literal_reason, - gtl::Span integer_reason) { +std::string ReasonDebugString(absl::Span literal_reason, + absl::Span integer_reason) { std::string result = "literals:{"; for (const Literal l : literal_reason) { if (result.back() != '{') result += ","; @@ -696,14 +696,14 @@ std::string IntegerTrail::DebugString() { } bool IntegerTrail::Enqueue(IntegerLiteral i_lit, - gtl::Span literal_reason, - gtl::Span integer_reason) { + absl::Span literal_reason, + absl::Span integer_reason) { return Enqueue(i_lit, literal_reason, integer_reason, integer_trail_.size()); } bool IntegerTrail::Enqueue(IntegerLiteral i_lit, - gtl::Span literal_reason, - gtl::Span integer_reason, + absl::Span literal_reason, + absl::Span integer_reason, int trail_index_with_same_reason) { DCHECK(AllLiteralsAreFalse(literal_reason)); @@ -718,12 +718,12 @@ bool IntegerTrail::Enqueue(IntegerLiteral i_lit, // This may not indicate an incorectness, but just some propagators that // didn't reach a fixed-point at level zero. - if (DEBUG_MODE && !integer_decision_levels_.empty()) { + if (DEBUG_MODE && !integer_search_levels_.empty()) { std::vector l(literal_reason.begin(), literal_reason.end()); MergeReasonInto(integer_reason, &l); LOG_IF(WARNING, l.empty()) << "Propagating a literal with no reason at a positive level!\n" - << "level:" << integer_decision_levels_.size() << " i_lit:" << i_lit + << "level:" << integer_search_levels_.size() << " i_lit:" << i_lit << " " << ReasonDebugString(literal_reason, integer_reason) << "\n" << DebugString(); } @@ -794,7 +794,7 @@ bool IntegerTrail::Enqueue(IntegerLiteral i_lit, // literal, and we where reaching a conflict while propagating the // associated literal instead of setting is_ignored below to false. const Literal is_ignored = Literal(is_ignored_literals_[var]); - if (integer_decision_levels_.empty()) { + if (integer_search_levels_.empty()) { trail_->EnqueueWithUnitReason(is_ignored); } else { EnqueueLiteral(is_ignored, literal_reason, integer_reason); @@ -828,7 +828,7 @@ bool IntegerTrail::Enqueue(IntegerLiteral i_lit, } // Special case for level zero. - if (integer_decision_levels_.empty()) { + if (integer_search_levels_.empty()) { vars_[i_lit.var].current_bound = i_lit.bound; integer_trail_[i_lit.var.value()].bound = i_lit.bound; return true; @@ -917,7 +917,7 @@ std::vector IntegerTrail::ReasonFor(IntegerLiteral literal) const { } bool IntegerTrail::AllLiteralsAreFalse( - gtl::Span literals) const { + absl::Span literals) const { for (const Literal lit : literals) { if (!trail_->Assignment().LiteralIsFalse(lit)) return false; } @@ -926,7 +926,7 @@ bool IntegerTrail::AllLiteralsAreFalse( // TODO(user): If this is called many time on the same variables, it could be // made faster by using some caching mecanism. -void IntegerTrail::MergeReasonInto(gtl::Span literals, +void IntegerTrail::MergeReasonInto(absl::Span literals, std::vector* output) const { DCHECK(tmp_queue_.empty()); const int size = vars_.size(); @@ -1047,7 +1047,7 @@ void IntegerTrail::MergeReasonIntoInternal(std::vector* output) const { STLSortAndRemoveDuplicates(output); } -gtl::Span IntegerTrail::Reason(const Trail& trail, +absl::Span IntegerTrail::Reason(const Trail& trail, int trail_index) const { const int index = boolean_trail_index_to_integer_one_[trail_index]; std::vector* reason = trail.GetEmptyVectorToStoreReason(trail_index); @@ -1064,10 +1064,10 @@ gtl::Span IntegerTrail::Reason(const Trail& trail, } void IntegerTrail::EnqueueLiteral( - Literal literal, gtl::Span literal_reason, - gtl::Span integer_reason) { + Literal literal, absl::Span literal_reason, + absl::Span integer_reason) { DCHECK(AllLiteralsAreFalse(literal_reason)); - if (integer_decision_levels_.empty()) { + if (integer_search_levels_.empty()) { // Level zero. We don't keep any reason. trail_->EnqueueWithUnitReason(literal); return; @@ -1075,12 +1075,12 @@ void IntegerTrail::EnqueueLiteral( // This may not indicate an incorectness, but just some propagators that // didn't reach a fixed-point at level zero. - if (DEBUG_MODE && !integer_decision_levels_.empty()) { + if (DEBUG_MODE && !integer_search_levels_.empty()) { std::vector l(literal_reason.begin(), literal_reason.end()); MergeReasonInto(integer_reason, &l); LOG_IF(WARNING, l.empty()) << "Propagating a literal with no reason at a positive level!\n" - << "level:" << integer_decision_levels_.size() << " lit:" << literal + << "level:" << integer_search_levels_.size() << " lit:" << literal << " " << ReasonDebugString(literal_reason, integer_reason) << "\n" << DebugString(); } @@ -1315,126 +1315,6 @@ void GenericLiteralWatcher::RegisterReversibleInt(int id, int* rev) { id_to_reversible_ints_[id].push_back(rev); } -// TODO(user): the complexity of this heuristic and the one below is ok when -// use_fixed_search() is false because it is not executed often, however, we do -// a linear scan for each search decision when use_fixed_search() is true, which -// seems bad. Improve. -std::function FirstUnassignedVarAtItsMinHeuristic( - const std::vector& vars, Model* model) { - IntegerEncoder* const integer_encoder = model->GetOrCreate(); - IntegerTrail* const integer_trail = model->GetOrCreate(); - return [integer_encoder, integer_trail, /*copy*/ vars]() { - for (const IntegerVariable var : vars) { - // Note that there is no point trying to fix a currently ignored variable. - if (integer_trail->IsCurrentlyIgnored(var)) continue; - const IntegerValue lb = integer_trail->LowerBound(var); - if (lb < integer_trail->UpperBound(var)) { - return integer_encoder - ->GetOrCreateAssociatedLiteral( - IntegerLiteral::LowerOrEqual(var, lb)) - .Index(); - } - } - return LiteralIndex(kNoLiteralIndex); - }; -} - -std::function UnassignedVarWithLowestMinAtItsMinHeuristic( - const std::vector& vars, Model* model) { - IntegerEncoder* const integer_encoder = model->GetOrCreate(); - IntegerTrail* const integer_trail = model->GetOrCreate(); - return [integer_encoder, integer_trail, /*copy */ vars]() { - IntegerVariable candidate = kNoIntegerVariable; - IntegerValue candidate_lb; - for (const IntegerVariable var : vars) { - if (integer_trail->IsCurrentlyIgnored(var)) continue; - const IntegerValue lb = integer_trail->LowerBound(var); - if (lb < integer_trail->UpperBound(var) && - (candidate == kNoIntegerVariable || lb < candidate_lb)) { - candidate = var; - candidate_lb = lb; - } - } - // Fix compilation error on visual studio 2013. - if (candidate == kNoIntegerVariable) return LiteralIndex(kNoLiteralIndex); - return integer_encoder - ->GetOrCreateAssociatedLiteral( - IntegerLiteral::LowerOrEqual(candidate, candidate_lb)) - .Index(); - }; -} - -std::function SequentialSearch( - std::vector> heuristics) { - return [heuristics]() { - for (const auto& h : heuristics) { - const LiteralIndex li = h(); - if (li != kNoLiteralIndex) return li; - } - return kNoLiteralIndex; - }; -} - -SatSolver::Status SolveIntegerProblemWithLazyEncoding( - const std::vector& assumptions, - const std::function& next_decision, Model* model) { - SatSolver* const solver = model->GetOrCreate(); - if (solver->IsModelUnsat()) return SatSolver::MODEL_UNSAT; - solver->Backtrack(0); - solver->SetAssumptionLevel(0); - - const SatParameters& parameters = *(model->GetOrCreate()); - if (parameters.use_fixed_search()) { - CHECK(assumptions.empty()) << "Not supported yet"; - // Note that it is important to do the level-zero propagation if it wasn't - // already done because EnqueueDecisionAndBackjumpOnConflict() assumes that - // the solver is in a "propagated" state. - if (!solver->Propagate()) return SatSolver::MODEL_UNSAT; - } - TimeLimit* time_limit = model->GetOrCreate(); - while (!time_limit->LimitReached()) { - // If we are not in fixed search, let the SAT solver do a full search to - // instantiate all the already created Booleans. - if (!parameters.use_fixed_search()) { - if (assumptions.empty()) { - const SatSolver::Status status = solver->Solve(); - if (status != SatSolver::MODEL_SAT) return status; - } else { - // TODO(user): We actually don't want to reset the solver from one - // loop to the next as it is less efficient. - const SatSolver::Status status = - solver->ResetAndSolveWithGivenAssumptions(assumptions); - if (status != SatSolver::MODEL_SAT) return status; - } - } - - // Look for the "best" non-instantiated integer variable. - // If all variables are instantiated, we have a solution. - const LiteralIndex decision = - next_decision == nullptr ? kNoLiteralIndex : next_decision(); - if (decision == kNoLiteralIndex) return SatSolver::MODEL_SAT; - - // Always try to Enqueue this literal as the next decision so we bypass - // any default polarity heuristic the solver may use on this literal. - solver->EnqueueDecisionAndBackjumpOnConflict(Literal(decision)); - if (solver->IsModelUnsat()) return SatSolver::MODEL_UNSAT; - } - return SatSolver::Status::LIMIT_REACHED; -} - -// Shortcut when there is no assumption, and we consider all variables in -// order for the search decision. -SatSolver::Status SolveIntegerProblemWithLazyEncoding(Model* model) { - const IntegerVariable num_vars = - model->GetOrCreate()->NumIntegerVariables(); - std::vector all_variables; - for (IntegerVariable var(0); var < num_vars; ++var) { - all_variables.push_back(var); - } - return SolveIntegerProblemWithLazyEncoding( - {}, FirstUnassignedVarAtItsMinHeuristic(all_variables, model), model); -} - // This is really close to ExcludeCurrentSolutionAndBacktrack(). std::function ExcludeCurrentSolutionWithoutIgnoredVariableAndBacktrack() { diff --git a/ortools/sat/integer.h b/ortools/sat/integer.h index 781eed50b6..53c1b48cf7 100644 --- a/ortools/sat/integer.h +++ b/ortools/sat/integer.h @@ -27,6 +27,7 @@ #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" +#include "ortools/base/port.h" #include "ortools/base/inlined_vector.h" #include "ortools/base/join.h" #include "ortools/base/span.h" @@ -139,8 +140,8 @@ struct IntegerLiteral { std::string DebugString() const { return VariableIsPositive(var) - ? StrCat("I", var.value() / 2, ">=", bound.value()) - : StrCat("I", var.value() / 2, "<=", -bound.value()); + ? absl::StrCat("I", var.value() / 2, ">=", bound.value()) + : absl::StrCat("I", var.value() / 2, "<=", -bound.value()); } // Note that bound should be in [kMinIntegerValue, kMaxIntegerValue + 1]. @@ -153,12 +154,12 @@ inline std::ostream& operator<<(std::ostream& os, IntegerLiteral i_lit) { return os; } -using InlinedIntegerLiteralVector = gtl::InlinedVector; +using InlinedIntegerLiteralVector = absl::InlinedVector; // A singleton that holds the INITIAL integer variable domains. struct IntegerDomains : public ITIVector> { + absl::InlinedVector> { explicit IntegerDomains(Model* model) {} }; @@ -373,7 +374,7 @@ class IntegerTrail : public SatPropagator { // correct state before calling any of its functions. bool Propagate(Trail* trail) final; void Untrail(const Trail& trail, int literal_trail_index) final; - gtl::Span Reason(const Trail& trail, + absl::Span Reason(const Trail& trail, int trail_index) const final; // Returns the number of created integer variables. @@ -484,22 +485,22 @@ class IntegerTrail : public SatPropagator { // reason is better? how to decide and what to do in this case? to think about // it. Currently we simply don't do anything. MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, - gtl::Span literal_reason, - gtl::Span integer_reason); + absl::Span literal_reason, + absl::Span integer_reason); // Same as Enqueue(), but takes an extra argument which if smaller than // integer_trail_.size() is interpreted as the trail index of an old Enqueue() // that had the same reason as this one. Note that the given Span must still // be valid as they are used in case of conflict. MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, - gtl::Span literal_reason, - gtl::Span integer_reason, + absl::Span literal_reason, + absl::Span integer_reason, int trail_index_with_same_reason); // Enqueues the given literal on the trail. // See the comment of Enqueue() for the reason format. - void EnqueueLiteral(Literal literal, gtl::Span literal_reason, - gtl::Span integer_reason); + void EnqueueLiteral(Literal literal, absl::Span literal_reason, + absl::Span integer_reason); // 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 @@ -508,7 +509,7 @@ class IntegerTrail : public SatPropagator { // Appends the reason for the given integer literals to the output and call // STLSortAndRemoveDuplicates() on it. - void MergeReasonInto(gtl::Span bounds, + void MergeReasonInto(absl::Span bounds, std::vector* output) const; // Returns the number of enqueues that changed a variable bounds. We don't @@ -529,14 +530,14 @@ class IntegerTrail : public SatPropagator { // Helper functions to report a conflict. Always return false so a client can // simply do: return integer_trail_->ReportConflict(...); - bool ReportConflict(gtl::Span literal_reason, - gtl::Span integer_reason) { + bool ReportConflict(absl::Span literal_reason, + absl::Span integer_reason) { std::vector* conflict = trail_->MutableConflict(); conflict->assign(literal_reason.begin(), literal_reason.end()); MergeReasonInto(integer_reason, conflict); return false; } - bool ReportConflict(gtl::Span integer_reason) { + bool ReportConflict(absl::Span integer_reason) { std::vector* conflict = trail_->MutableConflict(); conflict->clear(); MergeReasonInto(integer_reason, conflict); @@ -559,7 +560,7 @@ class IntegerTrail : public SatPropagator { private: // Tests that all the literals in the given reason are assigned to false. // This is used to DCHECK the given reasons to the Enqueue*() functions. - bool AllLiteralsAreFalse(gtl::Span literals) const; + bool AllLiteralsAreFalse(absl::Span literals) const; // Does the work of MergeReasonInto() when queue_ is already initialized. void MergeReasonIntoInternal(std::vector* output) const; @@ -568,8 +569,8 @@ class IntegerTrail : public SatPropagator { // an integer literal and maintained by encoder_. bool EnqueueAssociatedLiteral(Literal literal, int trail_index_with_same_reason, - gtl::Span literal_reason, - gtl::Span integer_reason, + absl::Span literal_reason, + absl::Span integer_reason, BooleanVariable* variable_with_same_reason); // Returns a lower bound on the given var that will always be valid. @@ -632,7 +633,7 @@ class IntegerTrail : public SatPropagator { // Start of each decision levels in integer_trail_. // TODO(user): use more general reversible mechanism? - std::vector integer_decision_levels_; + std::vector integer_search_levels_; // Buffer to store the reason of each trail entry. // Note that bounds_reason_buffer_ is an "union". It initially contains the @@ -1117,46 +1118,6 @@ FullyEncodeVariable(IntegerVariable var) { }; } -// A wrapper around SatSolver::Solve that handles integer variable with lazy -// encoding. Repeatedly calls SatSolver::Solve() on the model until the given -// next_decision() function return kNoLiteralIndex or the model is proved to -// be UNSAT. -// -// Returns the status of the last call to SatSolver::Solve(). -// -// Note that the next_decision() function must always return an unassigned -// literal or kNoLiteralIndex to end the search. -SatSolver::Status SolveIntegerProblemWithLazyEncoding( - const std::vector& assumptions, - const std::function& next_decision, Model* model); - -// Shortcut for SolveIntegerProblemWithLazyEncoding() when there is no -// assumption and we consider all variables in their index order for the next -// search decision. -SatSolver::Status SolveIntegerProblemWithLazyEncoding(Model* model); - -// Decision heuristic for SolveIntegerProblemWithLazyEncoding(). Returns a -// function that will return the literal corresponding to the fact that the -// first currently non-fixed variable value is <= its min. The function will -// return kNoLiteralIndex if all the given variables are fixed. -// -// Note that this function will create the associated literal if needed. -std::function FirstUnassignedVarAtItsMinHeuristic( - const std::vector& vars, Model* model); - -// Decision heuristic for SolveIntegerProblemWithLazyEncoding(). Like -// FirstUnassignedVarAtItsMinHeuristic() but the function will return the -// literal corresponding to the fact that the currently non-assigned variable -// with the lowest min has a value <= this min. -std::function UnassignedVarWithLowestMinAtItsMinHeuristic( - const std::vector& vars, Model* model); - -// Combines search heuristics in order: if the i-th one returns kNoLiteralIndex, -// ask the (i+1)-th. If every heuristic returned kNoLiteralIndex, -// returns kNoLiteralIndex. -std::function SequentialSearch( - std::vector> heuristics); - // Same as ExcludeCurrentSolutionAndBacktrack() but this version works for an // integer problem with optional variables. The issue is that an optional // variable that is ignored can basically take any value, and we don't really diff --git a/ortools/sat/integer_search.cc b/ortools/sat/integer_search.cc new file mode 100644 index 0000000000..6da839f3fd --- /dev/null +++ b/ortools/sat/integer_search.cc @@ -0,0 +1,244 @@ +// Copyright 2010-2017 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ortools/sat/integer_search.h" +#include "ortools/sat/sat_decision.h" + +namespace operations_research { +namespace sat { + +// TODO(user): the complexity of this heuristic and the one below is ok when +// use_fixed_search() is false because it is not executed often, however, we do +// a linear scan for each search decision when use_fixed_search() is true, which +// seems bad. Improve. +std::function FirstUnassignedVarAtItsMinHeuristic( + const std::vector& vars, Model* model) { + IntegerEncoder* const integer_encoder = model->GetOrCreate(); + IntegerTrail* const integer_trail = model->GetOrCreate(); + return [integer_encoder, integer_trail, /*copy*/ vars]() { + for (const IntegerVariable var : vars) { + // Note that there is no point trying to fix a currently ignored variable. + if (integer_trail->IsCurrentlyIgnored(var)) continue; + const IntegerValue lb = integer_trail->LowerBound(var); + if (lb < integer_trail->UpperBound(var)) { + return integer_encoder + ->GetOrCreateAssociatedLiteral( + IntegerLiteral::LowerOrEqual(var, lb)) + .Index(); + } + } + return kNoLiteralIndex; + }; +} + +std::function UnassignedVarWithLowestMinAtItsMinHeuristic( + const std::vector& vars, Model* model) { + IntegerEncoder* const integer_encoder = model->GetOrCreate(); + IntegerTrail* const integer_trail = model->GetOrCreate(); + return [integer_encoder, integer_trail, /*copy */ vars]() { + IntegerVariable candidate = kNoIntegerVariable; + IntegerValue candidate_lb; + for (const IntegerVariable var : vars) { + if (integer_trail->IsCurrentlyIgnored(var)) continue; + const IntegerValue lb = integer_trail->LowerBound(var); + if (lb < integer_trail->UpperBound(var) && + (candidate == kNoIntegerVariable || lb < candidate_lb)) { + candidate = var; + candidate_lb = lb; + } + } + if (candidate == kNoIntegerVariable) return kNoLiteralIndex; + return integer_encoder + ->GetOrCreateAssociatedLiteral( + IntegerLiteral::LowerOrEqual(candidate, candidate_lb)) + .Index(); + }; +} + +std::function SequentialSearch( + std::vector> heuristics) { + return [heuristics]() { + for (const auto& h : heuristics) { + const LiteralIndex li = h(); + if (li != kNoLiteralIndex) return li; + } + return kNoLiteralIndex; + }; +} + +std::function NullSearch() { + return []() { return kNoLiteralIndex; }; +} + +std::function SatSolverHeuristic(Model* model) { + SatSolver* sat_solver = model->GetOrCreate(); + Trail* trail = model->GetOrCreate(); + SatDecisionPolicy* decision_policy = model->GetOrCreate(); + return [sat_solver, trail, decision_policy] { + const bool all_assigned = trail->Index() == sat_solver->NumVariables(); + return all_assigned ? kNoLiteralIndex + : decision_policy->NextBranch().Index(); + }; +} + +std::function RestartEveryKFailures(int k, SatSolver* solver) { + bool reset_at_next_call = true; + int next_num_failures = 0; + return [=]() mutable { + if (reset_at_next_call) { + next_num_failures = solver->num_failures() + k; + reset_at_next_call = false; + } else if (solver->num_failures() >= next_num_failures) { + reset_at_next_call = true; + } + return reset_at_next_call; + }; +} + +std::function SatSolverRestartPolicy(Model* model) { + auto policy = model->GetOrCreate(); + return [policy]() { return policy->ShouldRestart(); }; +} + +SatSolver::Status SolveIntegerProblemWithLazyEncoding( + const std::vector& assumptions, + const std::function& next_decision, Model* model) { + SatSolver* const solver = model->GetOrCreate(); + if (solver->IsModelUnsat()) return SatSolver::MODEL_UNSAT; + solver->Backtrack(0); + solver->SetAssumptionLevel(0); + + const SatParameters& parameters = *(model->GetOrCreate()); + if (parameters.use_fixed_search()) { + CHECK(assumptions.empty()) << "Not supported yet"; + // Note that it is important to do the level-zero propagation if it wasn't + // already done because EnqueueDecisionAndBackjumpOnConflict() assumes that + // the solver is in a "propagated" state. + if (!solver->Propagate()) return SatSolver::MODEL_UNSAT; + } + + if (parameters.use_portfolio_search() && assumptions.empty()) { + auto incomplete_portfolio = AddModelHeuristics({next_decision}, model); + auto portfolio = CompleteHeuristics( + incomplete_portfolio, + SequentialSearch({SatSolverHeuristic(model), next_decision})); + auto default_restart_policy = SatSolverRestartPolicy(model); + auto restart_policies = std::vector>( + portfolio.size(), default_restart_policy); + return SolveProblemWithPortfolioSearch(portfolio, restart_policies, model); + } + + TimeLimit* time_limit = model->GetOrCreate(); + while (!time_limit->LimitReached()) { + // If we are not in fixed search, let the SAT solver do a full search to + // instantiate all the already created Booleans. + if (!parameters.use_fixed_search()) { + if (assumptions.empty()) { + const SatSolver::Status status = solver->Solve(); + if (status != SatSolver::MODEL_SAT) return status; + } else { + // TODO(user): We actually don't want to reset the solver from one + // loop to the next as it is less efficient. + const SatSolver::Status status = + solver->ResetAndSolveWithGivenAssumptions(assumptions); + if (status != SatSolver::MODEL_SAT) return status; + } + } + + // Look for the "best" non-instantiated integer variable. + // If all variables are instantiated, we have a solution. + const LiteralIndex decision = + next_decision == nullptr ? kNoLiteralIndex : next_decision(); + if (decision == kNoLiteralIndex) return SatSolver::MODEL_SAT; + + // Always try to Enqueue this literal as the next decision so we bypass + // any default polarity heuristic the solver may use on this literal. + solver->EnqueueDecisionAndBackjumpOnConflict(Literal(decision)); + if (solver->IsModelUnsat()) return SatSolver::MODEL_UNSAT; + } + return SatSolver::Status::LIMIT_REACHED; +} + +std::vector> AddModelHeuristics( + const std::vector>& input_heuristics, + Model* model) { + std::vector> heuristics = input_heuristics; + auto* extra_heuristics = model->GetOrCreate(); + heuristics.insert(heuristics.end(), extra_heuristics->begin(), + extra_heuristics->end()); + heuristics.push_back(NullSearch()); + return heuristics; +} + +std::vector> CompleteHeuristics( + const std::vector>& incomplete_heuristics, + const std::function& completion_heuristic) { + std::vector> complete_heuristics; + complete_heuristics.reserve(incomplete_heuristics.size()); + for (const auto& incomplete : incomplete_heuristics) { + complete_heuristics.push_back( + SequentialSearch({incomplete, completion_heuristic})); + } + return complete_heuristics; +} + +SatSolver::Status SolveProblemWithPortfolioSearch( + std::vector> decision_policies, + std::vector> restart_policies, Model* model) { + const int num_policies = decision_policies.size(); + CHECK_EQ(num_policies, restart_policies.size()); + CHECK_GT(num_policies, 0); + SatSolver* const solver = model->GetOrCreate(); + if (solver->IsModelUnsat()) return SatSolver::MODEL_UNSAT; + solver->Backtrack(0); + + // Note that it is important to do the level-zero propagation if it wasn't + // already done because EnqueueDecisionAndBackjumpOnConflict() assumes that + // the solver is in a "propagated" state. + if (!solver->Propagate()) return SatSolver::MODEL_UNSAT; + + // Main search loop. + int policy_index = 0; + TimeLimit* time_limit = model->GetOrCreate(); + while (!time_limit->LimitReached()) { + // If needed, restart and switch decision_policy. + if (restart_policies[policy_index]()) { + solver->Backtrack(0); + if (!solver->Propagate()) return SatSolver::MODEL_UNSAT; + policy_index = (policy_index + 1) % num_policies; + } + + // Get next decision, try to enqueue. + const LiteralIndex decision = decision_policies[policy_index](); + if (decision == kNoLiteralIndex) return SatSolver::MODEL_SAT; + solver->EnqueueDecisionAndBackjumpOnConflict(Literal(decision)); + if (solver->IsModelUnsat()) return SatSolver::MODEL_UNSAT; + } + return SatSolver::Status::LIMIT_REACHED; +} + +// Shortcut when there is no assumption, and we consider all variables in +// order for the search decision. +SatSolver::Status SolveIntegerProblemWithLazyEncoding(Model* model) { + const IntegerVariable num_vars = + model->GetOrCreate()->NumIntegerVariables(); + std::vector all_variables; + for (IntegerVariable var(0); var < num_vars; ++var) { + all_variables.push_back(var); + } + return SolveIntegerProblemWithLazyEncoding( + {}, FirstUnassignedVarAtItsMinHeuristic(all_variables, model), model); +} + +} // namespace sat +} // namespace operations_research diff --git a/ortools/sat/integer_search.h b/ortools/sat/integer_search.h new file mode 100644 index 0000000000..c74cbde3fa --- /dev/null +++ b/ortools/sat/integer_search.h @@ -0,0 +1,102 @@ +// Copyright 2010-2017 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef OR_TOOLS_SAT_INTEGER_SEARCH_H_ +#define OR_TOOLS_SAT_INTEGER_SEARCH_H_ + +#include + +#include "ortools/sat/integer.h" +#include "ortools/sat/sat_solver.h" + +namespace operations_research { +namespace sat { + +// Some heuristics may be generated automatically, for instance by constraints. +// Those will be stored in a SearchHeuristicsVector object owned by the model. +class SearchHeuristicsVector + : public std::vector> {}; + +// Decision heuristic for SolveIntegerProblemWithLazyEncoding(). Returns a +// function that will return the literal corresponding to the fact that the +// first currently non-fixed variable value is <= its min. The function will +// return kNoLiteralIndex if all the given variables are fixed. +// +// Note that this function will create the associated literal if needed. +std::function FirstUnassignedVarAtItsMinHeuristic( + const std::vector& vars, Model* model); + +// Decision heuristic for SolveIntegerProblemWithLazyEncoding(). Like +// FirstUnassignedVarAtItsMinHeuristic() but the function will return the +// literal corresponding to the fact that the currently non-assigned variable +// with the lowest min has a value <= this min. +std::function UnassignedVarWithLowestMinAtItsMinHeuristic( + const std::vector& vars, Model* model); + +// Combines search heuristics in order: if the i-th one returns kNoLiteralIndex, +// ask the (i+1)-th. If every heuristic returned kNoLiteralIndex, +// returns kNoLiteralIndex. +std::function SequentialSearch( + std::vector> heuristics); + +// Returns the LiteralIndex advised by the underliying SAT solver. +std::function SatSolverHeuristic(Model* model); + +// Always returns kNoLiteralIndex. Useful for compositions. +std::function NullSearch(); + +// A restart policy that restarts every k failures. +std::function RestartEveryKFailures(int k, SatSolver* solver); + +// A restart policy that uses the underlying sat solver's policy. +std::function SatSolverRestartPolicy(Model* model); + +// Appends model-owned automatic heuristics to input_heuristics in a new vector. +std::vector> AddModelHeuristics( + const std::vector>& input_heuristics, + Model* model); + +// Concatenates each input_heuristic with a default heuristic that instantiate +// all the problem's Boolean variables, into a new vector. +std::vector> CompleteHeuristics( + const std::vector>& incomplete_heuristics, + const std::function& completion_heuristic); + +// A wrapper around SatSolver::Solve that handles integer variable with lazy +// encoding. Repeatedly calls SatSolver::Solve() on the model until the given +// next_decision() function return kNoLiteralIndex or the model is proved to +// be UNSAT. +// +// Returns the status of the last call to SatSolver::Solve(). +// +// Note that the next_decision() function must always return an unassigned +// literal or kNoLiteralIndex to end the search. +SatSolver::Status SolveIntegerProblemWithLazyEncoding( + const std::vector& assumptions, + const std::function& next_decision, Model* model); + +// Shortcut for SolveIntegerProblemWithLazyEncoding() when there is no +// assumption and we consider all variables in their index order for the next +// search decision. +SatSolver::Status SolveIntegerProblemWithLazyEncoding(Model* model); + +// Solves a problem with the given heuristics. +// heuristics[i] will be used with restart_policies[i] only. +SatSolver::Status SolveProblemWithPortfolioSearch( + std::vector> decision_policies, + std::vector> restart_policies, Model* model); + +} // namespace sat +} // namespace operations_research + +#endif // OR_TOOLS_SAT_INTEGER_SEARCH_H_ diff --git a/ortools/sat/linear_programming_constraint.cc b/ortools/sat/linear_programming_constraint.cc index 9f9068bc58..91a069a50c 100644 --- a/ortools/sat/linear_programming_constraint.cc +++ b/ortools/sat/linear_programming_constraint.cc @@ -38,6 +38,7 @@ LinearProgrammingConstraint::LinearProgrammingConstraint(Model* model) time_limit_(model->GetOrCreate()), integer_trail_(model->GetOrCreate()), trail_(model->GetOrCreate()), + model_heuristics_(model->GetOrCreate()), dispatcher_(model->GetOrCreate()) { // Tweak the default parameters to make the solve incremental. glop::GlopParameters parameters; @@ -87,7 +88,7 @@ void LinearProgrammingConstraint::SetObjectiveCoefficient(IntegerVariable ivar, std::make_pair(GetOrCreateMirrorVariable(pos_var), coeff)); } -void LinearProgrammingConstraint::RegisterWith(GenericLiteralWatcher* watcher) { +void LinearProgrammingConstraint::RegisterWith(Model* model) { DCHECK(!lp_constraint_is_registered_); lp_constraint_is_registered_ = true; @@ -102,6 +103,7 @@ void LinearProgrammingConstraint::RegisterWith(GenericLiteralWatcher* watcher) { lp_data_.ScaleObjective(); lp_data_.AddSlackVariablesWhereNecessary(false); + GenericLiteralWatcher* watcher = model->GetOrCreate(); const int watcher_id = watcher->Register(this); const int num_vars = integer_variables_.size(); for (int i = 0; i < num_vars; i++) { @@ -111,6 +113,12 @@ void LinearProgrammingConstraint::RegisterWith(GenericLiteralWatcher* watcher) { watcher->WatchUpperBound(objective_cp_, watcher_id); } watcher->SetPropagatorPriority(watcher_id, 2); + + if (integer_variables_.size() >= 20) { // Do not use on small subparts. + auto* container = model->GetOrCreate(); + container->push_back(HeuristicLPPseudoCostBinary(model)); + container->push_back(HeuristicLPMostInfeasibleBinary(model)); + } } void LinearProgrammingConstraint::AddCutGenerator(CutGenerator generator) { @@ -524,41 +532,35 @@ CutGenerator CreateCVRPCutGenerator(int num_nodes, return result; } -std::function HeuristicLPMostInfeasibleBinary(Model* model) { +std::function +LinearProgrammingConstraint::HeuristicLPMostInfeasibleBinary(Model* model) { + IntegerTrail* integer_trail = integer_trail_; + IntegerEncoder* integer_encoder = model->GetOrCreate(); // Gather all 0-1 variables that appear in some LP. - IntegerTrail* integer_trail = model->GetOrCreate(); - LinearProgrammingDispatcher* dispatcher = - model->GetOrCreate(); std::vector variables; - for (const auto entry : *dispatcher) { - IntegerVariable var = entry.first; - if (integer_trail->LowerBound(var) == 0 && - integer_trail->UpperBound(var) == 1) { + for (IntegerVariable var : integer_variables_) { + if (integer_trail_->LowerBound(var) == 0 && + integer_trail_->UpperBound(var) == 1) { variables.push_back(var); } } - std::sort(variables.begin(), variables.end()); - LOG(INFO) << "HeuristicLPMostInfeasibleBinary has " << variables.size() << " variables."; - IntegerEncoder* integer_encoder = model->GetOrCreate(); - SatSolver* sat_solver = model->GetOrCreate(); - return [variables, dispatcher, integer_trail, integer_encoder, sat_solver]() { + return [this, variables, integer_trail, integer_encoder]() { const double kEpsilon = 1e-6; // Find most fractional value. IntegerVariable fractional_var = kNoIntegerVariable; double fractional_distance_best = -1.0; for (const IntegerVariable var : variables) { - // Check variable is not ignored and unfixed. - if (integer_trail->IsCurrentlyIgnored(var)) continue; - const IntegerValue lb = integer_trail->LowerBound(var); - const IntegerValue ub = integer_trail->UpperBound(var); + // Skip ignored and fixed variables. + if (integer_trail_->IsCurrentlyIgnored(var)) continue; + const IntegerValue lb = integer_trail_->LowerBound(var); + const IntegerValue ub = integer_trail_->UpperBound(var); if (lb == ub) continue; // Check variable's support is fractional. - LinearProgrammingConstraint* lp = (*dispatcher)[var]; - const double lp_value = lp->GetSolutionValue(var); + const double lp_value = this->GetSolutionValue(var); const double fractional_distance = std::min(std::ceil(lp_value - kEpsilon) - lp_value, lp_value - std::floor(lp_value + kEpsilon)); @@ -581,21 +583,16 @@ std::function HeuristicLPMostInfeasibleBinary(Model* model) { }; } -std::function HeuristicLPPseudoCostBinary(Model* model) { +std::function +LinearProgrammingConstraint::HeuristicLPPseudoCostBinary(Model* model) { // Gather all 0-1 variables that appear in some LP. - IntegerTrail* integer_trail = model->GetOrCreate(); - LinearProgrammingDispatcher* dispatcher = - model->GetOrCreate(); std::vector variables; - for (const auto entry : *dispatcher) { - IntegerVariable var = entry.first; - if (integer_trail->LowerBound(var) == 0 && - integer_trail->UpperBound(var) == 1) { + for (IntegerVariable var : integer_variables_) { + if (integer_trail_->LowerBound(var) == 0 && + integer_trail_->UpperBound(var) == 1) { variables.push_back(var); } } - std::sort(variables.begin(), variables.end()); - LOG(INFO) << "HeuristicLPPseudoCostBinary has " << variables.size() << " variables."; @@ -624,15 +621,17 @@ std::function HeuristicLPPseudoCostBinary(Model* model) { // Accumulate pseudo-costs of all unassigned variables. for (int i = 0; i < num_vars; i++) { const IntegerVariable var = variables[i]; - if (integer_trail->LowerBound(var) == integer_trail->UpperBound(var)) - continue; + // Skip ignored and fixed variables. + if (integer_trail_->IsCurrentlyIgnored(var)) continue; + const IntegerValue lb = integer_trail_->LowerBound(var); + const IntegerValue ub = integer_trail_->UpperBound(var); + if (lb == ub) continue; - LinearProgrammingConstraint* lp = (*dispatcher)[var]; - const double rc = lp->GetSolutionReducedCost(var); + const double rc = this->GetSolutionReducedCost(var); // Skip reduced costs that are nonzero because of numerical issues. if (std::abs(rc) < kEpsilon) continue; - const double value = std::round(lp->GetSolutionValue(var)); + const double value = std::round(this->GetSolutionValue(var)); if (value == 1.0 && rc < 0.0) { cost_to_zero[i] -= rc; num_cost_to_zero[i]++; @@ -644,9 +643,11 @@ std::function HeuristicLPPseudoCostBinary(Model* model) { double best_cost = 0.0; for (int i = 0; i < num_vars; i++) { const IntegerVariable var = variables[i]; - if (integer_trail->LowerBound(var) == integer_trail->UpperBound(var)) { - continue; - } + // Skip ignored and fixed variables. + if (integer_trail_->IsCurrentlyIgnored(var)) continue; + const IntegerValue lb = integer_trail_->LowerBound(var); + const IntegerValue ub = integer_trail_->UpperBound(var); + if (lb == ub) continue; if (num_cost_to_zero[i] > 0 && best_cost < cost_to_zero[i] / num_cost_to_zero[i]) { diff --git a/ortools/sat/linear_programming_constraint.h b/ortools/sat/linear_programming_constraint.h index 389fed7600..af1bf6037b 100644 --- a/ortools/sat/linear_programming_constraint.h +++ b/ortools/sat/linear_programming_constraint.h @@ -24,6 +24,7 @@ #include "ortools/lp_data/lp_types.h" #include "ortools/lp_data/matrix_scaler.h" #include "ortools/sat/integer.h" +#include "ortools/sat/integer_search.h" #include "ortools/sat/model.h" #include "ortools/util/time_limit.h" @@ -114,7 +115,7 @@ class LinearProgrammingConstraint : public PropagatorInterface { // PropagatorInterface API. bool Propagate() override; bool IncrementalPropagate(const std::vector& watch_indices) override; - void RegisterWith(GenericLiteralWatcher* watcher); + void RegisterWith(Model* model); std::string DimensionString() const { return lp_data_.GetDimensionString(); } @@ -141,6 +142,34 @@ class LinearProgrammingConstraint : public PropagatorInterface { // Returns the variable value on the same scale as the CP variable value. glop::Fractional GetVariableValueAtCpScale(glop::ColIndex var); + // Returns a LiteralIndex guided by the underlying LP constraints. + // This looks at all unassigned 0-1 variables, takes the one with + // a support value closest to 0.5, and tries to assign it to 1. + // If all 0-1 variables have an integer support, returns kNoLiteralIndex. + // Tie-breaking is done using the variable natural order. + // + // TODO(user): This fixes to 1, but for some problems fixing to 0 + // or to the std::round(support value) might work better. When this is the + // case, change behaviour automatically? + std::function HeuristicLPMostInfeasibleBinary(Model* model); + + // Returns a LiteralIndex guided by the underlying LP constraints. + // This computes the mean of reduced costs over successive calls, + // and tries to fix the variable which has the highest reduced cost. + // Tie-breaking is done using the variable natural order. + // + // TODO(user): Try to get better pseudocosts than averaging every time + // the heuristic is called. MIP solvers initialize this with strong branching, + // then keep track of the pseudocosts when doing tree search. Also, this + // version only branches on var >= 1 and keeps track of reduced costs from var + // = 1 to var = 0. This works better than the conventional MIP where the + // chosen variable will be argmax_var std::min(pseudocost_var(0->1), + // pseudocost_var(1->0)), probably because we are doing DFS search where MIP + // does BFS. This might depend on the model, more trials are necessary. We + // could also do exponential smoothing instead of decaying every N calls, i.e. + // pseudo = a * pseudo + (1-a) reduced. + std::function HeuristicLPPseudoCostBinary(Model* model); + // TODO(user): use solver's precision epsilon. static const double kEpsilon; @@ -171,6 +200,7 @@ class LinearProgrammingConstraint : public PropagatorInterface { TimeLimit* time_limit_; IntegerTrail* integer_trail_; Trail* trail_; + SearchHeuristicsVector* model_heuristics_; // The dispatcher for all LP propagators of the model, allows to find which // LinearProgrammingConstraint has a given IntegerVariable. @@ -222,34 +252,6 @@ CutGenerator CreateCVRPCutGenerator(int num_nodes, const std::vector& vars, const std::vector& demands, int64 capacity); - -// Returns a LiteralIndex guided by the underlying LP constraints. -// This looks at all unassigned 0-1 variables, takes the one with -// a support value closest to 0.5, and tries to assign it to 1. -// If all 0-1 variables have an integer support, returns kNoLiteralIndex. -// Tie-breaking is done using the variable natural order. -// -// TODO(user): This fixes to 1, but for some problems fixing to 0 -// or to the std::round(support value) might work better. When this is the -// case, change behaviour automatically? -std::function HeuristicLPMostInfeasibleBinary(Model* model); - -// Returns a LiteralIndex guided by the underlying LP constraints. -// This computes the mean of reduced costs over successive calls, -// and tries to fix the variable which has the highest reduced cost. -// Tie-breaking is done using the variable natural order. -// -// TODO(user): Try to get better pseudocosts than averaging every time the -// heuristic is called. MIP solvers initialize this with strong branching, then -// keep track of the pseudocosts when doing tree search. Also, this version only -// branches on var >= 1 and keeps track of reduced costs from var = 1 to var = -// 0. This works better than the conventional MIP where the chosen variable will -// be argmax_var std::min(pseudocost_var(0->1), pseudocost_var(1->0)), probably -// because we are doing DFS search where MIP does BFS. This might depend on the -// model, more trials are necessary. We could also do exponential smoothing -// instead of decaying every N calls, i.e. pseudo = a * pseudo + (1-a) reduced. -std::function HeuristicLPPseudoCostBinary(Model* model); - } // namespace sat } // namespace operations_research diff --git a/ortools/sat/optimization.cc b/ortools/sat/optimization.cc index 9634a678c5..2ccc9aaf31 100644 --- a/ortools/sat/optimization.cc +++ b/ortools/sat/optimization.cc @@ -864,7 +864,7 @@ SatSolver::Status SolveWithRandomParameters(LogBehavior log, min_seen = std::min(min_seen, objective); max_seen = std::max(max_seen, objective); - logger.Log(StrCat( + logger.Log(absl::StrCat( "c ", objective.value(), " [", min_seen.value(), ", ", max_seen.value(), "] objective_preference: ", use_obj ? "true" : "false", " ", ProtobufShortDebugString(parameters))); @@ -1406,7 +1406,7 @@ SatSolver::Status MinimizeWithCoreAndLazyEncoding( ? 0 : static_cast(std::ceil( 100.0 * (ub - lb) / std::max(std::abs(ub), std::abs(lb)))); - LOG(INFO) << StrCat("unscaled_objective:[", lb, ",", ub, + LOG(INFO) << absl::StrCat("unscaled_objective:[", lb, ",", ub, "]" " gap:", gap, "%", " assumptions:", term_indices.size(), diff --git a/ortools/sat/optimization.h b/ortools/sat/optimization.h index ac032b4085..db5621e425 100644 --- a/ortools/sat/optimization.h +++ b/ortools/sat/optimization.h @@ -24,6 +24,7 @@ #include "ortools/sat/boolean_problem.pb.h" #include "ortools/sat/integer.h" +#include "ortools/sat/integer_search.h" #include "ortools/sat/model.h" #include "ortools/sat/sat_base.h" #include "ortools/sat/sat_solver.h" diff --git a/ortools/sat/pb_constraint.cc b/ortools/sat/pb_constraint.cc index eb88d24994..9145a2d20b 100644 --- a/ortools/sat/pb_constraint.cc +++ b/ortools/sat/pb_constraint.cc @@ -961,7 +961,7 @@ void PbConstraints::Untrail(const Trail& trail, int trail_index) { } } -gtl::Span PbConstraints::Reason(const Trail& trail, +absl::Span PbConstraints::Reason(const Trail& trail, int trail_index) const { SCOPED_TIME_STAT(&stats_); const PbConstraintsEnqueueHelper::ReasonInfo& reason_info = diff --git a/ortools/sat/pb_constraint.h b/ortools/sat/pb_constraint.h index dfd0b6143d..854ab735c8 100644 --- a/ortools/sat/pb_constraint.h +++ b/ortools/sat/pb_constraint.h @@ -532,7 +532,7 @@ class PbConstraints : public SatPropagator { bool Propagate(Trail* trail) final; void Untrail(const Trail& trail, int trail_index) final; - gtl::Span Reason(const Trail& trail, + absl::Span Reason(const Trail& trail, int trail_index) const final; // Changes the number of variables. diff --git a/ortools/sat/restart.cc b/ortools/sat/restart.cc index 6ea4ae9985..38a93634e1 100644 --- a/ortools/sat/restart.cc +++ b/ortools/sat/restart.cc @@ -38,7 +38,7 @@ void RestartPolicy::Reset() { parameters_.restart_algorithms().end()); if (strategies_.empty()) { const std::vector string_values = strings::Split( - parameters_.default_restart_algorithms(), ',', strings::SkipEmpty()); + parameters_.default_restart_algorithms(), ',', absl::SkipEmpty()); for (const std::string& string_value : string_values) { SatParameters::RestartAlgorithm tmp; #if defined(__PORTABLE_PLATFORM__) diff --git a/ortools/sat/sat_base.h b/ortools/sat/sat_base.h index 1931b89be1..ef603e0533 100644 --- a/ortools/sat/sat_base.h +++ b/ortools/sat/sat_base.h @@ -26,10 +26,10 @@ #include "ortools/base/logging.h" #include "ortools/base/macros.h" #include "ortools/base/stringprintf.h" +#include "ortools/base/port.h" #include "ortools/base/span.h" #include "ortools/base/int_type.h" #include "ortools/base/int_type_indexed_vector.h" -#include "ortools/base/port.h" #include "ortools/sat/model.h" #include "ortools/util/bitset.h" @@ -292,7 +292,7 @@ class Trail { } // Returns the reason why this variable was assigned. - gtl::Span Reason(BooleanVariable var) const; + absl::Span Reason(BooleanVariable var) const; // Returns the "type" of an assignment (see AssignmentType). Note that this // function never returns kSameReasonAs or kCachedReason, it instead returns @@ -348,7 +348,7 @@ class Trail { } // Returns the last conflict. - gtl::Span FailingClause() const { return conflict_; } + absl::Span FailingClause() const { return conflict_; } // Specific SatClause interface so we can update the conflict clause activity. // Note that MutableConflict() automatically sets this to nullptr, so we can @@ -419,7 +419,7 @@ class Trail { // variables, the memory address of the vectors (kept in reasons_) are still // valid. mutable std::deque> reasons_repository_; - mutable ITIVector> reasons_; + mutable ITIVector> reasons_; mutable ITIVector old_type_; // This is used by RegisterPropagator() and Reason(). @@ -473,7 +473,7 @@ class SatPropagator { // The returned Span has to be valid until the literal is untrailed. A client // can use trail_.GetEmptyVectorToStoreReason() if it doesn't have a memory // location that already contains the reason. - virtual gtl::Span Reason(const Trail& trail, + virtual absl::Span Reason(const Trail& trail, int trail_index) const { LOG(FATAL) << "Not implemented."; return {}; @@ -565,7 +565,7 @@ inline int Trail::AssignmentType(BooleanVariable var) const { return type != AssignmentType::kCachedReason ? type : old_type_[var]; } -inline gtl::Span Trail::Reason(BooleanVariable var) const { +inline absl::Span Trail::Reason(BooleanVariable var) const { // Special case for AssignmentType::kSameReasonAs to avoid a recursive call. var = ReferenceVarWithSameReason(var); diff --git a/ortools/sat/sat_decision.cc b/ortools/sat/sat_decision.cc index 70619161f8..7827e29f61 100644 --- a/ortools/sat/sat_decision.cc +++ b/ortools/sat/sat_decision.cc @@ -13,8 +13,6 @@ #include "ortools/sat/sat_decision.h" -#include "ortools/base/adjustable_priority_queue-inl.h" - namespace operations_research { namespace sat { @@ -29,23 +27,23 @@ void SatDecisionPolicy::IncreaseNumVariables(int num_variables) { DCHECK_GE(num_variables, activities_.size()); activities_.resize(num_variables, parameters_.initial_variables_activity()); + tie_breakers_.resize(num_variables, 0.0); num_bumps_.resize(num_variables, 0); pq_need_update_for_var_at_trail_index_.IncreaseSize(num_variables); - queue_elements_.resize(num_variables); weighted_sign_.resize(num_variables, 0.0); var_polarity_.resize(num_variables); var_use_phase_saving_.resize(num_variables, parameters_.use_phase_saving()); ResetInitialPolarity(/*from=*/old_num_variables); - // Important: Because there is new variables, we need to recompute the - // priority queue. Note that this will not reset the activity, it will however - // change the order of the element with the same priority. - // - // TODO(user): Not even do that and just push the new ones at the end? It - // would be even better to simply deall with zero-activity variables - // differently, but that would change the current search order. - var_ordering_is_initialized_ = false; + // Update the priority queue. Note that each addition is in O(1) because + // the priority is 0.0. + var_ordering_.Reserve(num_variables); + if (var_ordering_is_initialized_) { + for (BooleanVariable var(old_num_variables); var < num_variables; ++var) { + var_ordering_.Add({var, 0.0, activities_[var]}); + } + } } void SatDecisionPolicy::ResetDecisionHeuristic() { @@ -54,8 +52,9 @@ void SatDecisionPolicy::ResetDecisionHeuristic() { const int num_variables = activities_.size(); variable_activity_increment_ = 1.0; activities_.assign(num_variables, parameters_.initial_variables_activity()); + tie_breakers_.assign(num_variables, 0.0); num_bumps_.assign(num_variables, 0); - queue_elements_.assign(num_variables, WeightedVarQueueElement()); + var_ordering_.Clear(); ResetInitialPolarity(/*from=*/0); var_use_phase_saving_.assign(num_variables, parameters_.use_phase_saving()); @@ -109,9 +108,9 @@ void SatDecisionPolicy::InitializeVariableOrdering() { std::vector variables; for (BooleanVariable var(0); var < num_variables; ++var) { if (!trail_->Assignment().VariableIsAssigned(var)) { - if (activities_[var] > 0) { - queue_elements_[var].weight = activities_[var]; - var_ordering_.Add(&queue_elements_[var]); + if (activities_[var] > 0.0) { + var_ordering_.Add( + {var, static_cast(tie_breakers_[var]), activities_[var]}); } else { variables.push_back(var); } @@ -136,9 +135,8 @@ void SatDecisionPolicy::InitializeVariableOrdering() { } // Add the variables without activity to the queue (in the default order) - for (BooleanVariable var : variables) { - queue_elements_[var].weight = 0.0; - var_ordering_.Add(&queue_elements_[var]); + for (const BooleanVariable var : variables) { + var_ordering_.Add({var, static_cast(tie_breakers_[var]), 0.0}); } // Finish the queue initialization. @@ -158,7 +156,7 @@ void SatDecisionPolicy::SetAssignmentPreference(Literal literal, // The tie_breaker is changed, so we need to reinitialize the priority queue. // Note that this doesn't change the activity though. - queue_elements_[literal.Variable()].tie_breaker = weight; + tie_breakers_[literal.Variable()] = weight; var_ordering_is_initialized_ = false; } @@ -168,9 +166,9 @@ std::vector> SatDecisionPolicy::AllPreferences() for (BooleanVariable var(0); var < var_polarity_.size(); ++var) { // TODO(user): we currently assume that if the tie_breaker is zero then // no preference was set (which is not 100% correct). Fix that. - if (queue_elements_[var].tie_breaker > 0.0) { - prefs.push_back(std::make_pair(Literal(var, var_polarity_[var]), - queue_elements_[var].tie_breaker)); + const double value = var_ordering_.GetElement(var.value()).tie_breaker; + if (value > 0.0) { + prefs.push_back(std::make_pair(Literal(var, var_polarity_[var]), value)); } } return prefs; @@ -251,23 +249,22 @@ Literal SatDecisionPolicy::NextBranch() { while (true) { // TODO(user): This may not be super efficient if almost all the // variables are assigned. - std::uniform_int_distribution index_dist( - 0, var_ordering_.Raw()->size() - 1); - var = BooleanVariable((*var_ordering_.Raw())[index_dist(random_)] - - &queue_elements_.front()); + std::uniform_int_distribution index_dist(0, + var_ordering_.Size() - 1); + var = var_ordering_.QueueElement(index_dist(random_)).var; if (!trail_->Assignment().VariableIsAssigned(var)) break; pq_need_update_for_var_at_trail_index_.Set(trail_->Info(var).trail_index); - var_ordering_.Remove(&queue_elements_[var]); + var_ordering_.Remove(var.value()); } } else { // The loop is done this way in order to leave the final choice in the heap. DCHECK(!var_ordering_.IsEmpty()); - var = BooleanVariable(var_ordering_.Top() - &queue_elements_.front()); + var = var_ordering_.Top().var; while (trail_->Assignment().VariableIsAssigned(var)) { var_ordering_.Pop(); pq_need_update_for_var_at_trail_index_.Set(trail_->Info(var).trail_index); DCHECK(!var_ordering_.IsEmpty()); - var = BooleanVariable(var_ordering_.Top() - &queue_elements_.front()); + var = var_ordering_.Top().var; } } @@ -282,15 +279,12 @@ Literal SatDecisionPolicy::NextBranch() { } void SatDecisionPolicy::PqInsertOrUpdate(BooleanVariable var) { - WeightedVarQueueElement* element = &(queue_elements_[var]); - if (var_ordering_.Contains(element)) { - // Note that because of the pq_need_update_for_var_at_trail_index_ - // optimization the new weight should always be higher than the old one. - DCHECK_GT(activities_[var], element->weight); - element->weight = activities_[var]; - var_ordering_.NoteChangedPriority(element); + const WeightedVarQueueElement element{ + var, static_cast(tie_breakers_[var]), activities_[var]}; + if (var_ordering_.Contains(var.value())) { + // Note that the new weight should always be higher than the old one. + var_ordering_.IncreasePriority(element); } else { - element->weight = activities_[var]; var_ordering_.Add(element); } } @@ -359,8 +353,8 @@ void SatDecisionPolicy::Untrail(int target_trail_index) { for (int trail_index = trail_->Index() - 1; trail_index > target_trail_index; --trail_index) { const BooleanVariable var = (*trail_)[trail_index].Variable(); - CHECK(var_ordering_.Contains(&(queue_elements_[var]))); - CHECK_EQ(activities_[var], queue_elements_[var].weight); + CHECK(var_ordering_.Contains(var.value())); + CHECK_EQ(activities_[var], var_ordering_.GetElement(var.value()).weight); } } } diff --git a/ortools/sat/sat_decision.h b/ortools/sat/sat_decision.h index a8b81abe99..836c2cb309 100644 --- a/ortools/sat/sat_decision.h +++ b/ortools/sat/sat_decision.h @@ -21,8 +21,8 @@ #include "ortools/sat/sat_base.h" #include "ortools/sat/sat_parameters.pb.h" #include "ortools/util/bitset.h" +#include "ortools/util/integer_pq.h" #include "ortools/util/random_engine.h" -#include "ortools/base/adjustable_priority_queue.h" namespace operations_research { namespace sat { @@ -120,13 +120,10 @@ class SatDecisionPolicy { // by its position in the queue_elements_ vector, and we can recover the later // using (pointer - &queue_elements_[0]). struct WeightedVarQueueElement { - WeightedVarQueueElement() : heap_index(-1), tie_breaker(0.0), weight(0.0) {} + // Interface for the IntegerPriorityQueue. + int Index() const { return var.value(); } - // Interface for the AdjustablePriorityQueue. - void SetHeapIndex(int h) { heap_index = h; } - int GetHeapIndex() const { return heap_index; } - - // Priority order. The AdjustablePriorityQueue returns the largest element + // Priority order. The IntegerPriorityQueue returns the largest element // first. // // Note(user): We used to also break ties using the variable index, however @@ -146,7 +143,7 @@ class SatDecisionPolicy { (weight == other.weight && (tie_breaker < other.tie_breaker)); } - int32 heap_index; + BooleanVariable var; float tie_breaker; // TODO(user): Experiment with float. In the rest of the code, we use @@ -158,8 +155,7 @@ class SatDecisionPolicy { ERROR_WeightedVarQueueElement_is_not_well_compacted); bool var_ordering_is_initialized_ = false; - AdjustablePriorityQueue var_ordering_; - ITIVector queue_elements_; + IntegerPriorityQueue var_ordering_; // This is used for the branching heuristic described in "Learning Rate Based // Branching Heuristic for SAT solvers", J.H.Liang, V. Ganesh, P. Poupart, @@ -189,6 +185,7 @@ class SatDecisionPolicy { // Stores variable activity and the number of time each variable was "bumped". // The later is only used with the ERWA heuristic. ITIVector activities_; + ITIVector tie_breakers_; ITIVector num_bumps_; // Used by NextBranch() to choose the polarity of the next decision. For the diff --git a/ortools/sat/sat_parameters.proto b/ortools/sat/sat_parameters.proto index 64d9e0f73f..570077797c 100644 --- a/ortools/sat/sat_parameters.proto +++ b/ortools/sat/sat_parameters.proto @@ -18,7 +18,7 @@ package operations_research.sat; // Contains the definitions for all the sat algorithm parameters and their // default values. // -// NEXT TAG: 94 +// NEXT TAG: 95 message SatParameters { // ========================================================================== // Branching and polarity @@ -65,7 +65,7 @@ message SatParameters { // Satisfiability Testing, 2007. optional bool use_phase_saving = 44 [default = true]; - // The proportion of polarity choosen at random. Note that this take + // The proportion of polarity chosen at random. Note that this take // precedence over the phase saving heuristic. This is different from // initial_polarity:POLARITY_RANDOM because it will select a new random // polarity each time the variable is branched upon instead of selecting one @@ -77,19 +77,6 @@ message SatParameters { // from the given variable_ordering strategy. optional double random_branches_ratio = 32 [default = 0]; - // Specifies how literals should be initially sorted in a clause. - enum LiteralOrdering { - // Do nothing and keep literals in their current order. - LITERAL_IN_ORDER = 0; - // Sort the literals by increasing order of their variable appearance in the - // problem. - VAR_MIN_USAGE = 1; - // Sort the literals by decreasing order of their variable appearance in the - // problem. - VAR_MAX_USAGE = 2; - } - optional LiteralOrdering literal_ordering = 3 [default = LITERAL_IN_ORDER]; - // Whether we use the ERWA (Exponential Recency Weighted Average) heuristic as // described in "Learning Rate Based Branching Heuristic for SAT solvers", // J.H.Liang, V. Ganesh, P. Poupart, K.Czarnecki, SAT 2016. @@ -501,6 +488,10 @@ message SatParameters { // decisions variables must be integers. optional bool use_fixed_search = 82 [default = false]; + // If true, the solver will use generic heuristics to find solutions and + // prove optimality. Incompatible with use_fixed_search. + optional bool use_portfolio_search = 94 [default = false]; + // The default optimization method is a simple "linear scan", each time trying // to find a better solution than the previous one. If this is true, then we // use a core-based approach (like in max-SAT) when we try to increase the diff --git a/ortools/sat/sat_solver.cc b/ortools/sat/sat_solver.cc index 182a180e7d..ef6f4fc98c 100644 --- a/ortools/sat/sat_solver.cc +++ b/ortools/sat/sat_solver.cc @@ -223,8 +223,7 @@ bool SatSolver::AddProblemClauseInternal(const std::vector& literals) { if (parameters_->treat_binary_clauses_separately() && literals.size() == 2) { AddBinaryClauseInternal(literals[0], literals[1]); } else { - std::unique_ptr clause( - SatClause::Create(literals, /*is_redundant=*/false)); + std::unique_ptr clause(SatClause::Create(literals)); if (!clauses_propagator_.AttachAndPropagate(clause.get(), trail_)) { return SetModelUnsat(); } @@ -352,7 +351,7 @@ int SatSolver::AddLearnedClauseAndEnqueueUnitPropagation( } CleanClauseDatabaseIfNeeded(); - SatClause* clause = SatClause::Create(literals, is_redundant); + SatClause* clause = SatClause::Create(literals); clauses_.emplace_back(clause); // Important: Even though the only literal at the last decision level has @@ -714,7 +713,9 @@ bool SatSolver::PropagateAndStopAfterOneConflictResolution() { for (SatClause* clause : subsumed_clauses_) { DCHECK(ClauseSubsumption(learned_conflict_, clause)); clauses_propagator_.LazyDetach(clause); - if (!clause->IsRedundant()) is_redundant = false; + if (!ContainsKey(clauses_info_, clause)) { + is_redundant = false; + } } clauses_propagator_.CleanUpWatchers(); counters_.num_subsumed_clauses += subsumed_clauses_.size(); @@ -1092,8 +1093,6 @@ void SatSolver::BumpReasonActivities(const std::vector& literals) { } void SatSolver::BumpClauseActivity(SatClause* clause) { - if (!clause->IsRedundant()) return; - // We only bump the activity of the clauses that have some info. So if we know // that we will keep a clause forever, we don't need to create its Info. More // than the speed, this allows to limit as much as possible the activity @@ -1301,10 +1300,9 @@ void SatSolver::ProcessNewlyFixedVariables() { const size_t new_size = clause->Size(); if (new_size != old_size && drat_writer_ != nullptr) { - // TODO(user): Instead delete the original clause in - // DeleteDetachedClause(). The problem is that we currently don't have - // the initial size anywhere. - drat_writer_->AddClause({clause->begin(), new_size}); + if (new_size > 0) { + drat_writer_->AddClause({clause->begin(), new_size}); + } drat_writer_->DeleteClause( {clause->begin(), old_size}, /*ignore_call=*/clauses_info_.find(clause) == clauses_info_.end()); @@ -1491,7 +1489,7 @@ std::string SatSolver::DebugString(const SatClause& clause) const { return result; } -int SatSolver::ComputeMaxTrailIndex(gtl::Span clause) const { +int SatSolver::ComputeMaxTrailIndex(absl::Span clause) const { SCOPED_TIME_STAT(&stats_); int trail_index = -1; for (const Literal literal : clause) { @@ -1542,7 +1540,7 @@ void SatSolver::ComputeFirstUIPConflict( // // This last literal will be the first UIP because by definition all the // propagation done at the current level will pass though it at some point. - gtl::Span clause_to_expand = trail_->FailingClause(); + absl::Span clause_to_expand = trail_->FailingClause(); SatClause* sat_clause = trail_->FailingSatClause(); DCHECK(!clause_to_expand.empty()); int num_literal_at_highest_level_that_needs_to_be_processed = 0; @@ -1849,7 +1847,7 @@ void SatSolver::MinimizeConflictSimple(std::vector* conflict) { bool can_be_removed = false; if (DecisionLevel(var) != current_level) { // It is important not to call Reason(var) when it can be avoided. - const gtl::Span reason = trail_->Reason(var); + const absl::Span reason = trail_->Reason(var); if (!reason.empty()) { can_be_removed = true; for (Literal literal : reason) { @@ -2108,7 +2106,7 @@ void SatSolver::MinimizeConflictExperimental(std::vector* conflict) { // A nullptr reason means that this was a decision variable from the // previous levels. - const gtl::Span reason = trail_->Reason(var); + const absl::Span reason = trail_->Reason(var); if (reason.empty()) continue; // Compute how many and which literals from the current reason do not appear diff --git a/ortools/sat/sat_solver.h b/ortools/sat/sat_solver.h index 31dd6aba24..bae5aa011c 100644 --- a/ortools/sat/sat_solver.h +++ b/ortools/sat/sat_solver.h @@ -283,7 +283,7 @@ class SatSolver { // Extract the current problem clauses. The Output type must support the two // functions: // - void AddBinaryClause(Literal a, Literal b); - // - void AddClause(gtl::Span clause); + // - void AddClause(absl::Span clause); // // TODO(user): also copy the learned clauses? template @@ -303,9 +303,10 @@ class SatSolver { // currently process the clauses in order. binary_implication_graph_.ExtractAllBinaryClauses(out); for (SatClause* clause : clauses_) { - if (!clause->IsRedundant()) { + // We skip redundant clauses. + if (!ContainsKey(clauses_info_, clause)) { out->AddClause( - gtl::Span(clause->begin(), clause->Size())); + absl::Span(clause->begin(), clause->Size())); } } } @@ -515,7 +516,7 @@ class SatSolver { // Returns the maximum trail_index of the literals in the given clause. // All the literals must be assigned. Returns -1 if the clause is empty. - int ComputeMaxTrailIndex(gtl::Span clause) const; + int ComputeMaxTrailIndex(absl::Span clause) const; // Computes what is known as the first UIP (Unique implication point) conflict // clause starting from the failing clause. For a definition of UIP and a @@ -632,8 +633,9 @@ class SatSolver { // here either. std::vector clauses_; - // Clause information used for the clause database management. - // Note that only the clauses that can be removed need to appear here. + // Clause information used for the clause database management. Note that only + // the clauses that can be removed appear here. The problem clauses and + // the learned one that we wants to keep forever do not appear. struct ClauseInfo { double activity = 0.0; int32 lbd = 0; diff --git a/ortools/sat/simplification.cc b/ortools/sat/simplification.cc index ed4e44b480..219a670932 100644 --- a/ortools/sat/simplification.cc +++ b/ortools/sat/simplification.cc @@ -40,7 +40,7 @@ SatPostsolver::SatPostsolver(int num_variables) assignment_.Resize(num_variables); } -void SatPostsolver::Add(Literal x, gtl::Span clause) { +void SatPostsolver::Add(Literal x, absl::Span clause) { CHECK(!clause.empty()); DCHECK(std::find(clause.begin(), clause.end(), x) != clause.end()); associated_literal_.push_back(ApplyReverseMapping(x)); @@ -156,7 +156,7 @@ std::vector SatPostsolver::PostsolveSolution( void SatPresolver::AddBinaryClause(Literal a, Literal b) { AddClause({a, b}); } -void SatPresolver::AddClause(gtl::Span clause) { +void SatPresolver::AddClause(absl::Span clause) { CHECK_GT(clause.size(), 0) << "Added an empty clause to the presolver"; const ClauseIndex ci(clauses_.size()); clauses_.push_back(std::vector(clause.begin(), clause.end())); diff --git a/ortools/sat/simplification.h b/ortools/sat/simplification.h index 47ec337265..afc9045b46 100644 --- a/ortools/sat/simplification.h +++ b/ortools/sat/simplification.h @@ -51,7 +51,7 @@ class SatPostsolver { // The postsolver will process the Add() calls in reverse order. If the given // clause has all its literals at false, it simply sets the literal x to true. // Note that x must be a literal of the given clause. - void Add(Literal x, const gtl::Span clause); + void Add(Literal x, const absl::Span clause); // Tells the postsolver that the given literal must be true in any solution. // We currently check that the variable is not already fixed. @@ -82,7 +82,7 @@ class SatPostsolver { std::vector Clause(int i) const { // TODO(user): we could avoid the copy here, but because clauses_literals_ // is a deque, we do need a special return class and cannot juste use - // gtl::Span for instance. + // absl::Span for instance. const int begin = clauses_start_[i]; const int end = i + 1 < clauses_start_.size() ? clauses_start_[i + 1] : clauses_literals_.size(); @@ -153,7 +153,7 @@ class SatPresolver { // Adds new clause to the SatPresolver. void SetNumVariables(int num_variables); void AddBinaryClause(Literal a, Literal b); - void AddClause(gtl::Span clause); + void AddClause(absl::Span clause); // Presolves the problem currently loaded. Returns false if the model is // proven to be UNSAT during the presolving. diff --git a/ortools/sat/symmetry.cc b/ortools/sat/symmetry.cc index dbdf74c18c..ac168233b3 100644 --- a/ortools/sat/symmetry.cc +++ b/ortools/sat/symmetry.cc @@ -95,7 +95,7 @@ bool SymmetryPropagator::PropagateNext(Trail* trail) { // Set the conflict on the trail. // Note that we need to fetch a reason for this. std::vector* conflict = trail->MutableConflict(); - const gtl::Span initial_reason = + const absl::Span initial_reason = trail->Reason(non_symmetric.literal.Variable()); Permute(p_index, initial_reason, conflict); conflict->push_back(non_symmetric.image); @@ -144,7 +144,7 @@ void SymmetryPropagator::Untrail(const Trail& trail, int trail_index) { } } -gtl::Span SymmetryPropagator::Reason(const Trail& trail, +absl::Span SymmetryPropagator::Reason(const Trail& trail, int trail_index) const { SCOPED_TIME_STAT(&stats_); const ReasonInfo& reason_info = reasons_[trail_index]; @@ -189,7 +189,7 @@ bool SymmetryPropagator::Enqueue(const Trail& trail, Literal literal, return *index == p_trail->size(); } -void SymmetryPropagator::Permute(int index, gtl::Span input, +void SymmetryPropagator::Permute(int index, absl::Span input, std::vector* output) const { SCOPED_TIME_STAT(&stats_); diff --git a/ortools/sat/symmetry.h b/ortools/sat/symmetry.h index 2f0cf28426..5fafe1bcaf 100644 --- a/ortools/sat/symmetry.h +++ b/ortools/sat/symmetry.h @@ -65,7 +65,7 @@ class SymmetryPropagator : public SatPropagator { bool Propagate(Trail* trail) final; void Untrail(const Trail& trail, int trail_index) final; - gtl::Span Reason(const Trail& trail, + absl::Span Reason(const Trail& trail, int trail_index) const final; // Adds a new permutation to this symmetry propagator. The ownership is @@ -92,7 +92,7 @@ class SymmetryPropagator : public SatPropagator { // Permutes a list of literals from input into output using the permutation // with given index. This uses tmp_literal_mapping_ and has a complexity in // O(permutation_support + input_size). - void Permute(int index, gtl::Span input, + void Permute(int index, absl::Span input, std::vector* output) const; private: diff --git a/ortools/util/file_util.cc b/ortools/util/file_util.cc index 23d771cff9..c1697ec063 100644 --- a/ortools/util/file_util.cc +++ b/ortools/util/file_util.cc @@ -24,7 +24,7 @@ namespace operations_research { using ::google::protobuf::TextFormat; -bool ReadFileToProto(string_view filename, google::protobuf::Message* proto) { +bool ReadFileToProto(absl::string_view filename, google::protobuf::Message* proto) { std::string data; CHECK_OK(file::GetContents(filename, &data, file::Defaults())); // Note that gzipped files are currently not supported. @@ -35,7 +35,7 @@ bool ReadFileToProto(string_view filename, google::protobuf::Message* proto) { return false; } -bool WriteProtoToFile(string_view filename, const google::protobuf::Message& proto, +bool WriteProtoToFile(absl::string_view filename, const google::protobuf::Message& proto, ProtoWriteFormat proto_write_format, bool gzipped) { // Note that gzipped files are currently not supported. gzipped = false; @@ -58,7 +58,7 @@ bool WriteProtoToFile(string_view filename, const google::protobuf::Message& pro } break; } - const std::string output_filename = StrCat(filename, file_type_suffix); + const std::string output_filename = absl::StrCat(filename, file_type_suffix); VLOG(1) << "Writing " << output_string.size() << " bytes to " << output_filename; if (!file::SetContents(output_filename, output_string, file::Defaults()) diff --git a/ortools/util/file_util.h b/ortools/util/file_util.h index 0bbc13716f..a09874e7b7 100644 --- a/ortools/util/file_util.h +++ b/ortools/util/file_util.h @@ -26,10 +26,10 @@ namespace operations_research { // Reads a proto from a file. Supports the following formats: binary, text, // JSON, all of those optionally gzipped. Returns false on failure. -bool ReadFileToProto(string_view filename, google::protobuf::Message* proto); +bool ReadFileToProto(absl::string_view filename, google::protobuf::Message* proto); template -Proto ReadFileToProtoOrDie(string_view filename) { +Proto ReadFileToProtoOrDie(absl::string_view filename) { Proto proto; CHECK(ReadFileToProto(filename, &proto)); return proto; @@ -42,7 +42,7 @@ enum class ProtoWriteFormat { kProtoText, kProtoBinary, kJson }; // If 'proto_write_format' is kProtoBinary, ".bin" is appended to file_name. If // 'proto_write_format' is kJson, ".json" is appended to file_name. If 'gzipped' // is true, ".gz" is appended to file_name. -bool WriteProtoToFile(string_view filename, const google::protobuf::Message& proto, +bool WriteProtoToFile(absl::string_view filename, const google::protobuf::Message& proto, ProtoWriteFormat proto_write_format, bool gzipped); namespace internal { @@ -73,7 +73,7 @@ std::vector ReadNumRecords(File* file, int expected_num_records) { // Ditto, taking a filename as argument. template -std::vector ReadNumRecords(string_view filename, +std::vector ReadNumRecords(absl::string_view filename, int expected_num_records) { return ReadNumRecords(file::OpenOrDie(filename, "r", file::Defaults()), expected_num_records); @@ -84,7 +84,7 @@ std::vector ReadNumRecords(string_view filename, // file is empty. Dies if the file doesn't exist or contains something else than // protos encoded in RecordIO format. template -std::vector ReadAllRecordsOrDie(string_view filename) { +std::vector ReadAllRecordsOrDie(absl::string_view filename) { return internal::ReadNumRecords(filename, -1); } template @@ -96,7 +96,7 @@ std::vector ReadAllRecordsOrDie(File* file) { // doesn't contain exactly one record, or contains something else than protos // encoded in RecordIO format. template -Proto ReadOneRecordOrDie(string_view filename) { +Proto ReadOneRecordOrDie(absl::string_view filename) { Proto p; p.Swap(&internal::ReadNumRecords(filename, 1)[0]); return p; @@ -105,7 +105,7 @@ Proto ReadOneRecordOrDie(string_view filename) { // Writes all records in Proto format to 'file'. Dies if it is unable to open // the file or write to it. template -void WriteRecordsOrDie(string_view filename, +void WriteRecordsOrDie(absl::string_view filename, const std::vector& protos) { recordio::RecordWriter writer( file::OpenOrDie(filename, "w", file::Defaults())); diff --git a/ortools/util/filelineiter.h b/ortools/util/filelineiter.h index f26ceff629..fd8711b9a0 100644 --- a/ortools/util/filelineiter.h +++ b/ortools/util/filelineiter.h @@ -131,7 +131,6 @@ class FileLines { private: File* file_; const int options_; - DISALLOW_COPY_AND_ASSIGN(FileLines); }; } // namespace operations_research diff --git a/ortools/util/integer_pq.h b/ortools/util/integer_pq.h new file mode 100644 index 0000000000..165b2483b6 --- /dev/null +++ b/ortools/util/integer_pq.h @@ -0,0 +1,190 @@ +// Copyright 2010-2017 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains and adjustable priority queue templated by an Element +// class that must: +// - Be efficiently copiable and storable in a std::vector. +// - Be comparable via the templated Compare class. Top() will returns +// the element with the largest priority (like std::priority_queue). +// - Implements the "int Index() const" function that must returns an integer +// that uniquely identify this particular element. Ideally this index must +// be dense in [0, max_num_elements). +// +#ifndef OR_TOOLS_UTIL_INTEGER_PQ_H_ +#define OR_TOOLS_UTIL_INTEGER_PQ_H_ + +#include + +#include "ortools/base/logging.h" +#include "ortools/base/macros.h" + +namespace operations_research { + +// Classic asjustable priority queue implementation. It behaves exactly the same +// as AdjustablePriorityQueue regarding identical elements, but it uses less +// memory and is in general slightly faster. +template > +class IntegerPriorityQueue { + public: + // Starts with an empty queue and reserve space for n elements. + explicit IntegerPriorityQueue(int n = 0, Compare comp = Compare()) + : size_(0), less_(comp) { + Reserve(n); + } + + // Increases the space reservation to n elements indices in [0, n). All + // elements passed to the other functions must have an Index() smaller than + // this n. + void Reserve(int n) { + // The heap_ starts at 1 for faster left/right child indices computation. + // This also allow us to use position 0 for element not in the queue. + heap_.resize(n + 1); + position_.resize(n, 0); + } + + // Returns the number of elements currently present. + int Size() const { return size_; } + bool IsEmpty() const { return size_ == 0; } + + // Removes all elements from the queue. + // TODO(user): we could make this sparse if it is needed. + void Clear() { + size_ = 0; + position_.assign(position_.size(), 0); + } + + // Returns true if the element with given index is currently in the queue. + bool Contains(int index) const { return position_[index] != 0; } + + // Adds the given element to the queue and set its priority. + // Preconditions: Contains(element) must be false. + void Add(Element element) { + DCHECK(!Contains(element.Index())); + SetAndIncreasePriority(++size_, element); + } + + // Top() returns the top element and Pop() remove it from the queue. + // Preconditions: IsEmpty() must be false. + Element Top() const { return heap_[1]; } + void Pop() { + DCHECK(!IsEmpty()); + position_[Top().Index()] = 0; + const int old_size = size_--; + if (old_size > 1) SetAndDecreasePriority(1, heap_[old_size]); + } + + // Removes the element with given index from the queue. + // Preconditions: Contains(index) must be true. + void Remove(int index) { + DCHECK(Contains(index)); + const int to_replace = position_[index]; + position_[index] = 0; + const int old_size = size_--; + if (to_replace == old_size) return; + const Element element = heap_[old_size]; + if (less_(element, heap_[to_replace])) { + SetAndDecreasePriority(to_replace, element); + } else { + SetAndIncreasePriority(to_replace, element); + } + } + + // Change the priority of the given element and adjust the queue. + // + // Preconditions: Contains(element) must be true. + void ChangePriority(Element element) { + DCHECK(Contains(element.Index())); + const int i = position_[element.Index()]; + if (i > 1 && less_(heap_[i >> 1], element)) { + SetAndIncreasePriority(i, element); + } else { + SetAndDecreasePriority(i, element); + } + } + + // Optimized version of ChangePriority() when we know the direction. + void IncreasePriority(Element element) { + SetAndIncreasePriority(position_[element.Index()], element); + } + void DecreasePriority(Element element) { + SetAndDecreasePriority(position_[element.Index()], element); + } + + // Returns the element with given index. + Element GetElement(int index) const { return heap_[position_[index]]; } + + // For i in [0, Size()) returns an element currently in the queue. + // This can be used to get a random element from the queue for instance. + Element QueueElement(int i) const { return heap_[1 + i]; } + + private: + // Puts the given element at heap index i. + void Set(int i, Element element) { + heap_[i] = element; + position_[element.Index()] = i; + } + + // Puts the given element at heap index i and update the heap knowing that the + // element has a priority <= than the priority of the element currently at + // this position. + void SetAndDecreasePriority(int i, const Element element) { + const int size = size_; + while (true) { + const int left = i * 2; + const int right = left + 1; + if (right > size) { + if (left > size) break; + const Element left_element = heap_[left]; + if (!less_(element, left_element)) break; + Set(i, left_element); + i = left; + break; + } + const Element left_element = heap_[left]; + const Element right_element = heap_[right]; + if (less_(left_element, right_element)) { + if (!less_(element, right_element)) break; + Set(i, right_element); + i = right; + } else { + if (!less_(element, left_element)) break; + Set(i, left_element); + i = left; + } + } + Set(i, element); + } + + // Puts the given element at heap index i and update the heap knowing that the + // element has a priority >= than the priority of the element currently at + // this position. + void SetAndIncreasePriority(int i, const Element element) { + while (i > 1) { + const int parent = i >> 1; + const Element parent_element = heap_[parent]; + if (!less_(parent_element, element)) break; + Set(i, parent_element); + i = parent; + } + Set(i, element); + } + + int size_; + Compare less_; + std::vector heap_; + std::vector position_; +}; + +} // namespace operations_research + +#endif // OR_TOOLS_UTIL_INTEGER_PQ_H_ diff --git a/ortools/util/optional_boolean.proto b/ortools/util/optional_boolean.proto new file mode 100644 index 0000000000..99b2075281 --- /dev/null +++ b/ortools/util/optional_boolean.proto @@ -0,0 +1,30 @@ +// Copyright 2010-2017 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package operations_research; + +// A "three-way" boolean: unspecified, false or true. +// +// We don't use the value of 1 to increase the chance to catch bugs: eg. in +// python, a user may set a proto field of this type enum to a boolean value +// without type checks, if they set it to True, the proto validity code will +// catch it (because it'll be cast to 1, which is an invalid enum value). +// Note that if the user sets if to False (i.e. 0), it will be caught by the +// routing library's parameter validity check too. +enum OptionalBoolean { + BOOL_UNSPECIFIED = 0; + BOOL_FALSE = 2; + BOOL_TRUE = 3; +} diff --git a/ortools/util/proto_tools.cc b/ortools/util/proto_tools.cc index 60ab4cda4d..d99f4cb973 100644 --- a/ortools/util/proto_tools.cc +++ b/ortools/util/proto_tools.cc @@ -38,17 +38,17 @@ void WriteFullProtocolMessage(const google::protobuf::Message& message, int inde const int start = repeated ? 0 : -1; const int limit = repeated ? refl->FieldSize(message, fd) : 0; for (int j = start; j < limit; ++j) { - StrAppend(out, indent, fd->name()); + absl::StrAppend(out, indent, fd->name()); if (fd->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - StrAppend(out, " {\n"); + absl::StrAppend(out, " {\n"); const google::protobuf::Message& nested_message = repeated ? refl->GetRepeatedMessage(message, fd, j) : refl->GetMessage(message, fd); WriteFullProtocolMessage(nested_message, indent_level + 1, out); - StrAppend(out, indent, "}\n"); + absl::StrAppend(out, indent, "}\n"); } else { TextFormat::PrintFieldValueToString(message, fd, j, &temp_string); - StrAppend(out, ": ", temp_string, "\n"); + absl::StrAppend(out, ": ", temp_string, "\n"); } } } diff --git a/ortools/util/sort.h b/ortools/util/sort.h index 200c06d8a3..2cedc571a2 100644 --- a/ortools/util/sort.h +++ b/ortools/util/sort.h @@ -24,7 +24,8 @@ template using value_type_t = typename std::iterator_traits::value_type; // Sorts the elements in the range [begin, end) in ascending order using the -// comp predicate. +// comp predicate. The order of equal elements is guaranteed to be preserved +// only if is_stable is true. // // This function performs well if the elements in the range [begin, end) are // almost sorted. @@ -37,13 +38,13 @@ using value_type_t = typename std::iterator_traits::value_type; // may actually use the comp predicate more than max_comparisons in order // to complete its current insertion. // 3) If insertion sort exceeds the maximum number of comparisons, the range is -// sorted using std::sort. +// sorted using std::stable_sort if is_stable is true or std::sort otherwise. // // The first two steps of this algorithm are inspired by the ones recommended // in Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. template >> void IncrementalSort(int max_comparisons, Iterator begin, Iterator end, - Compare comp = Compare{}) { + Compare comp = Compare{}, bool is_stable = false) { // Ranges of at most one element are already sorted. if (std::distance(begin, end) <= 1) return; @@ -57,17 +58,15 @@ void IncrementalSort(int max_comparisons, Iterator begin, Iterator end, } } - // Perform insertion sort with a limited number of comparisons. - int num_comps = max_comparisons; // We know that the elements in the range [begin, last_sorted) are the // smallest elements of [begin, end) and are sorted. Iterator it = std::next(last_sorted); - for (; it != end && num_comps > 0; ++it) { + for (; it != end && max_comparisons > 0; ++it) { const auto inserted = *it; Iterator j = it; - num_comps--; + max_comparisons--; while (comp(inserted, *std::prev(j))) { - num_comps--; + max_comparisons--; *j = *std::prev(j); j--; } @@ -77,7 +76,11 @@ void IncrementalSort(int max_comparisons, Iterator begin, Iterator end, // Stop if insertion sort was able to sort the range. if (it == end) return; - std::sort(last_sorted, end, comp); + if (is_stable) { + std::stable_sort(last_sorted, end, comp); + } else { + std::sort(last_sorted, end, comp); + } } // Sorts the elements in the range [begin, end) in ascending order using the @@ -117,17 +120,19 @@ void InsertionSort(Iterator begin, Iterator end, Compare comp = Compare{}) { } // Sorts the elements in the range [begin, end) in ascending order using the -// comp predicate. The order of equal elements is guaranteed to be preserved. +// comp predicate. The order of equal elements is guaranteed to be preserved +// only if is_stable is true. // // This function performs well if the elements in the range [begin, end) are // almost sorted. template >> -void IncrementalSort(Iterator begin, Iterator end, Compare comp = Compare{}) { +void IncrementalSort(Iterator begin, Iterator end, Compare comp = Compare{}, + bool is_stable = false) { const int size = std::distance(begin, end); if (size <= 32) { InsertionSort(begin, end, comp); } else { - IncrementalSort(size * 8, begin, end, comp); + IncrementalSort(size * 8, begin, end, comp, is_stable); } } diff --git a/ortools/util/sorted_interval_list.cc b/ortools/util/sorted_interval_list.cc index b7e175f801..aa28148359 100644 --- a/ortools/util/sorted_interval_list.cc +++ b/ortools/util/sorted_interval_list.cc @@ -75,7 +75,7 @@ bool IntervalsAreSortedAndDisjoint( return true; } -bool SortedDisjointIntervalsContain(gtl::Span intervals, +bool SortedDisjointIntervalsContain(absl::Span intervals, int64 value) { for (const ClosedInterval& interval : intervals) { if (interval.start <= value && interval.end >= value) return true; diff --git a/ortools/util/sorted_interval_list.h b/ortools/util/sorted_interval_list.h index 60ed82b736..4a2833b385 100644 --- a/ortools/util/sorted_interval_list.h +++ b/ortools/util/sorted_interval_list.h @@ -71,7 +71,7 @@ bool IntervalsAreSortedAndDisjoint( // // TODO(user): This works in O(n), but could be made to work in O(log n) for // long list of intervals. -bool SortedDisjointIntervalsContain(gtl::Span intervals, +bool SortedDisjointIntervalsContain(absl::Span intervals, int64 value); // Returns the intersection of two lists of sorted disjoint intervals in a diff --git a/ortools/util/stats.cc b/ortools/util/stats.cc index a2fc81b817..47c1c3e761 100644 --- a/ortools/util/stats.cc +++ b/ortools/util/stats.cc @@ -24,6 +24,9 @@ namespace operations_research { +const char inst_retired_event[] = "inst_retired:any_p:u"; +const char cycles_event[] = "cycles:u"; + std::string MemoryUsage() { const int64 mem = operations_research::sysinfo::MemoryUsageProcess(); static const int64 kDisplayThreshold = 2; @@ -64,7 +67,7 @@ void StatsGroup::Reset() { namespace { -bool CompareStatPointers(Stat* s1, Stat* s2) { +bool CompareStatPointers(const Stat* s1, const Stat* s2) { if (s1->Priority() == s2->Priority()) { if (s1->Sum() == s2->Sum()) return s1->Name() < s2->Name(); return (s1->Sum() > s2->Sum()); @@ -87,7 +90,19 @@ std::string StatsGroup::StatString() const { longest_name_size = std::max(longest_name_size, size); sorted_stats.push_back(stats_[i]); } - std::sort(sorted_stats.begin(), sorted_stats.end(), CompareStatPointers); + switch (print_order_) { + case SORT_BY_PRIORITY_THEN_VALUE: + std::sort(sorted_stats.begin(), sorted_stats.end(), CompareStatPointers); + break; + case SORT_BY_NAME: + std::sort(sorted_stats.begin(), sorted_stats.end(), + [](const Stat* s1, const Stat* s2) -> bool { + return s1->Name() < s2->Name(); + }); + break; + default: + LOG(FATAL) << "Unknown print order: " << print_order_; + } // Do not display groups without print-worthy stats. if (sorted_stats.empty()) return ""; @@ -231,4 +246,19 @@ std::string IntegerDistribution::ValueAsString() const { max_, Average(), StdDeviation(), sum_); } +#ifdef HAS_PERF_SUBSYSTEM +EnabledScopedInstructionCounter::EnabledScopedInstructionCounter( + const std::string& name, TimeLimit* time_limit) + : time_limit_(time_limit), name_(name) { + starting_count_ = + time_limit_ != nullptr ? time_limit_->ReadInstructionCounter() : 0; +} + +EnabledScopedInstructionCounter::~EnabledScopedInstructionCounter() { + ending_count_ = + time_limit_ != nullptr ? time_limit_->ReadInstructionCounter() : 0; + LOG(INFO) << name_ << ", Instructions: " << ending_count_ - starting_count_; +} +#endif // HAS_PERF_SUBSYSTEM + } // namespace operations_research diff --git a/ortools/util/stats.h b/ortools/util/stats.h index ac003029d7..e396ef619e 100644 --- a/ortools/util/stats.h +++ b/ortools/util/stats.h @@ -73,6 +73,7 @@ #ifdef HAS_PERF_SUBSYSTEM #include "absl/ortools/base/str_replace.h" #include "exegesis/exegesis/itineraries/perf_subsystem.h" +#include "ortools/util/time_limit.h" #endif // HAS_PERF_SUBSYSTEM #include "ortools/base/timer.h" @@ -126,6 +127,11 @@ class Stat { // Base class to print a nice summary of a group of statistics. class StatsGroup { public: + enum PrintOrder { + SORT_BY_PRIORITY_THEN_VALUE = 0, + SORT_BY_NAME = 1, + }; + explicit StatsGroup(const std::string& name) : name_(name), stats_(), time_distributions_() {} ~StatsGroup(); @@ -139,6 +145,10 @@ class StatsGroup { // Note that only the stats WorthPrinting() are printed. std::string StatString() const; + // Changes the print ordering (will affect the order in which the stats + // registered with this group are printed via StatString()). + void SetPrintOrder(PrintOrder print_order) { print_order_ = print_order; } + // Returns and if needed creates and registers a TimeDistribution with the // given name. Note that this involve a map lookup and his thus slower than // directly accessing a TimeDistribution variable. @@ -149,6 +159,7 @@ class StatsGroup { private: std::string name_; + PrintOrder print_order_ = SORT_BY_PRIORITY_THEN_VALUE; std::vector stats_; std::map time_distributions_; @@ -330,33 +341,24 @@ class DisabledScopedTimeDistributionUpdater { #ifdef HAS_PERF_SUBSYSTEM // Helper classes to count instructions during execution of a block of code and // add print the results to logs. -// Creates new perf subsystem and start collecting 'inst_retired:any_p' event on -// creation and stops collecting on destruction. class EnabledScopedInstructionCounter { public: - explicit EnabledScopedInstructionCounter(const std::string& name) : name_(name) { - perf_subsystem_.CleanUp(); - perf_subsystem_.AddEvent("inst_retired:any_p:u,p"); - perf_subsystem_.AddEvent("cycles:u,p"); - perf_subsystem_.StartCollecting(); - } + explicit EnabledScopedInstructionCounter(const std::string& name, + TimeLimit* time_limit); EnabledScopedInstructionCounter(const EnabledScopedInstructionCounter&) = delete; EnabledScopedInstructionCounter& operator=( const EnabledScopedInstructionCounter&) = delete; - ~EnabledScopedInstructionCounter() { - exegesis::PerfResult perf_result = perf_subsystem_.StopAndReadCounters(); - LOG(INFO) << name_ << ": " << perf_result.ToString(); - } + ~EnabledScopedInstructionCounter(); // Used only for testing. - exegesis::PerfResult GetPerfResult() { - return perf_subsystem_.ReadCounters(); - } + double ReadInstructionCount() { return ending_count_ - starting_count_; } private: - exegesis::PerfSubsystem perf_subsystem_; + TimeLimit* time_limit_; std::string name_; + double starting_count_; + double ending_count_; }; #endif // HAS_PERF_SUBSYSTEM @@ -400,9 +402,10 @@ inline std::string RemoveOperationsResearchAndGlop(const std::string& pretty_fun pretty_function, {{"operations_research::", ""}, {"glop::", ""}}); } -#define SCOPED_INSTRUCTION_COUNT \ - operations_research::ScopedInstructionCounter scoped_instruction_count( \ - RemoveOperationsResearchAndGlop(__PRETTY_FUNCTION__)) +#define SCOPED_INSTRUCTION_COUNT(time_limit) \ + operations_research::ScopedInstructionCounterTemp scoped_instruction_count( \ + RemoveOperationsResearchAndGlop(__PRETTY_FUNCTION__), time_limit) + #endif // HAS_PERF_SUBSYSTEM #else // OR_STATS @@ -414,7 +417,7 @@ using ScopedInstructionCounter = DisabledScopedInstructionCounter; #define IF_STATS_ENABLED(instructions) #define SCOPED_TIME_STAT(stats) -#define SCOPED_INSTRUCTION_COUNT +#define SCOPED_INSTRUCTION_COUNT(time_limit) #endif // OR_STATS diff --git a/ortools/util/time_limit.cc b/ortools/util/time_limit.cc index 01df8a210d..e48b7c68ac 100644 --- a/ortools/util/time_limit.cc +++ b/ortools/util/time_limit.cc @@ -30,18 +30,18 @@ const int TimeLimit::kHistorySize = 100; std::string TimeLimit::DebugString() const { std::string buffer = - StrCat("Time left: ", LegacyPrecision(GetTimeLeft()), + absl::StrCat("Time left: ", absl::LegacyPrecision(GetTimeLeft()), "\nDeterministic time left: ", - LegacyPrecision(GetDeterministicTimeLeft()), - "\nElapsed time: ", LegacyPrecision(GetElapsedTime()), + absl::LegacyPrecision(GetDeterministicTimeLeft()), + "\nElapsed time: ", absl::LegacyPrecision(GetElapsedTime()), "\nElapsed deterministic time: ", - LegacyPrecision(GetElapsedDeterministicTime())); + absl::LegacyPrecision(GetElapsedDeterministicTime())); #ifndef NDEBUG for (const auto& counter : deterministic_counters_) { const std::string& counter_name = counter.first; const double counter_value = counter.second; - StrAppend(&buffer, "\n", counter_name, ": ", - LegacyPrecision(counter_value)); + absl::StrAppend(&buffer, "\n", counter_name, ": ", + absl::LegacyPrecision(counter_value)); } #endif return buffer; diff --git a/ortools/util/time_limit.h b/ortools/util/time_limit.h index 612b8cf163..6dab2a19e6 100644 --- a/ortools/util/time_limit.h +++ b/ortools/util/time_limit.h @@ -217,7 +217,7 @@ class TimeLimit { // Returns the time elapsed in seconds since the construction of this object. double GetElapsedTime() const { - return 1e-9 * (base::GetCurrentTimeNanos() - start_ns_); + return 1e-9 * (absl::GetCurrentTimeNanos() - start_ns_); } // Returns the elapsed deterministic time since the construction of this @@ -384,7 +384,7 @@ inline void TimeLimit::ResetTimers(double limit_in_seconds, perf_subsystem_.StartCollecting(); } #endif // HAS_PERF_SUBSYSTEM - start_ns_ = base::GetCurrentTimeNanos(); + start_ns_ = absl::GetCurrentTimeNanos(); last_ns_ = start_ns_; limit_ns_ = limit_in_seconds >= 1e-9 * (kint64max - start_ns_) ? kint64max @@ -423,7 +423,7 @@ inline bool TimeLimit::LimitReached() { } #endif // HAS_PERF_SUBSYSTEM - const int64 current_ns = base::GetCurrentTimeNanos(); + const int64 current_ns = absl::GetCurrentTimeNanos(); running_max_.Add(std::max(safety_buffer_ns_, current_ns - last_ns_)); last_ns_ = current_ns; if (current_ns + running_max_.GetCurrentMax() >= limit_ns_) { @@ -447,7 +447,7 @@ inline bool TimeLimit::LimitReached() { inline double TimeLimit::GetTimeLeft() const { if (limit_ns_ == kint64max) return std::numeric_limits::infinity(); - const int64 delta_ns = limit_ns_ - base::GetCurrentTimeNanos(); + const int64 delta_ns = limit_ns_ - absl::GetCurrentTimeNanos(); if (delta_ns < 0) return 0.0; if (FLAGS_time_limit_use_usertime) { return std::max(limit_in_seconds_ - user_timer_.Get(), 0.0);