From 2ff000ab9a7ba37c55205c44822a8cbb2ad4e786 Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Wed, 20 Nov 2019 14:28:11 -0800 Subject: [PATCH] bump abseil to the latest version, adapt the code all around --- makefiles/Makefile.third_party.unix.mk | 76 ++- ortools/algorithms/dynamic_partition.cc | 2 +- ortools/algorithms/dynamic_partition.h | 12 +- ortools/algorithms/samples/knapsack.py | 1 - .../samples/simple_knapsack_program.py | 1 - ortools/base/adjustable_priority_queue.h | 28 +- ortools/base/encodingutils.h | 2 +- ortools/base/file.h | 10 +- ortools/base/filelineiter.h | 3 +- ortools/base/int_type.h | 1 - ortools/base/jniutil.h | 4 +- ortools/base/protoutil.h | 1 + ortools/base/random.cc | 3 +- ortools/base/random.h | 15 +- ortools/base/status.h | 5 +- ortools/base/stl_util.h | 52 +- ortools/base/thorough_hash.h | 4 +- ortools/bop/bop_base.h | 2 +- ortools/bop/bop_ls.cc | 3 +- ortools/bop/bop_ls.h | 3 +- ortools/bop/bop_solver.cc | 2 +- ortools/constraint_solver/assignment.cc | 2 +- .../constraint_solver/constraint_solver.cc | 13 +- ortools/constraint_solver/constraint_solver.h | 36 +- .../constraint_solver/constraint_solveri.h | 215 +++++-- .../csharp/constraint_solver.i | 1 - ortools/constraint_solver/default_search.cc | 14 +- .../java/constraint_solver.i | 2 +- ortools/constraint_solver/local_search.cc | 277 +++++---- .../python/constraint_solver.i | 4 +- .../constraint_solver/python/pywrapcp_util.h | 1 + ortools/constraint_solver/routing.cc | 105 ++-- ortools/constraint_solver/routing.h | 93 ++- ortools/constraint_solver/routing_flags.cc | 16 +- ortools/constraint_solver/routing_flags.h | 2 +- .../routing_lp_scheduling.cc | 89 ++- .../constraint_solver/routing_lp_scheduling.h | 2 +- .../routing_neighborhoods.cc | 575 +++++++++++++----- .../constraint_solver/routing_neighborhoods.h | 134 +++- .../constraint_solver/routing_parameters.cc | 31 +- .../routing_parameters.proto | 29 +- ortools/constraint_solver/routing_search.cc | 205 +++++-- .../samples/simple_routing_program.cc | 1 + ortools/constraint_solver/samples/tsp.cc | 1 + .../samples/tsp_circuit_board.cc | 1 + .../constraint_solver/samples/tsp_cities.cc | 1 + .../samples/tsp_distance_matrix.cc | 1 + ortools/constraint_solver/samples/vrp.cc | 1 + .../constraint_solver/samples/vrp_capacity.cc | 1 + .../samples/vrp_drop_nodes.cc | 1 + .../samples/vrp_global_span.cc | 1 + .../samples/vrp_initial_routes.cc | 1 + .../samples/vrp_pickup_delivery.cc | 1 + .../samples/vrp_pickup_delivery_fifo.cc | 1 + .../samples/vrp_pickup_delivery_lifo.cc | 1 + .../samples/vrp_resources.cc | 1 + .../samples/vrp_starts_ends.cc | 1 + .../samples/vrp_time_windows.cc | 1 + ortools/constraint_solver/search.cc | 11 +- ortools/flatzinc/fz.cc | 2 +- ortools/flatzinc/model.h | 2 +- ortools/flatzinc/parser.lex | 2 +- ortools/flatzinc/parser.tab.cc | 336 +++++----- ortools/flatzinc/parser.tab.hh | 6 +- ortools/flatzinc/parser.yy | 2 +- ortools/flatzinc/parser.yy.cc | 2 +- ortools/flatzinc/parser_util.h | 2 +- ortools/glop/lu_factorization.h | 2 +- ortools/glop/markowitz.h | 2 +- ortools/glop/preprocessor.cc | 26 +- ortools/glop/primal_edge_norms.h | 2 +- ortools/glop/revised_simplex.cc | 7 +- ortools/glop/revised_simplex.h | 11 +- ortools/glop/status.h | 2 +- ortools/glop/update_row.h | 2 +- ortools/graph/christofides.h | 7 +- ortools/graph/cliques.h | 5 +- ortools/graph/connected_components.h | 6 +- ortools/graph/connectivity.h | 2 +- ortools/graph/ebert_graph.h | 6 +- ortools/graph/graph.h | 10 +- ortools/graph/iterators.h | 2 +- ortools/graph/max_flow.cc | 2 +- ortools/graph/perfect_matching.cc | 6 +- ortools/graph/python/graph.i | 2 + ortools/graph/strongly_connected_components.h | 18 +- ortools/linear_solver/clp_interface.cc | 4 +- ortools/linear_solver/glop_interface.cc | 2 +- ortools/linear_solver/linear_solver.cc | 7 +- ortools/linear_solver/model_exporter.cc | 8 +- ortools/linear_solver/model_exporter.h | 10 +- ortools/linear_solver/model_validator.cc | 2 +- ortools/linear_solver/model_validator.h | 12 +- ortools/linear_solver/python/linear_solver.i | 9 + .../samples/integer_programming_example.py | 1 - .../samples/simple_lp_program.py | 1 - .../samples/simple_mip_program.py | 1 - ortools/linear_solver/sat_interface.cc | 6 +- ortools/linear_solver/sat_proto_solver.cc | 3 +- ortools/lp_data/lp_data.h | 20 +- ortools/lp_data/lp_print_utils.cc | 4 +- ortools/lp_data/lp_print_utils.h | 6 +- ortools/lp_data/lp_types.h | 8 +- ortools/lp_data/matrix_scaler.h | 2 +- ortools/lp_data/mps_reader.cc | 14 +- ortools/lp_data/permutation.h | 2 +- ortools/port/utf8.h | 2 +- ortools/sat/boolean_problem.cc | 2 +- ortools/sat/boolean_problem.h | 2 +- ortools/sat/clause.h | 4 +- ortools/sat/cp_model.h | 12 +- ortools/sat/cp_model_checker.cc | 2 +- ortools/sat/cp_model_checker.h | 5 +- ortools/sat/cp_model_loader.cc | 90 ++- ortools/sat/cp_model_presolve.cc | 23 +- ortools/sat/cp_model_solver.cc | 3 +- ortools/sat/cp_model_solver.h | 8 +- ortools/sat/integer.cc | 34 +- ortools/sat/integer.h | 4 + ortools/sat/integer_expr.h | 4 +- ortools/sat/linear_constraint_manager.cc | 36 +- ortools/sat/linear_constraint_manager.h | 10 +- ortools/sat/linear_programming_constraint.cc | 26 +- ortools/sat/linear_programming_constraint.h | 5 +- ortools/sat/model.h | 20 +- ortools/sat/optimization.cc | 2 +- ortools/sat/pb_constraint.h | 4 +- ortools/sat/precedences.cc | 2 +- ortools/sat/presolve_context.cc | 23 +- ortools/sat/presolve_context.h | 1 + ortools/sat/restart.h | 2 +- ortools/sat/sat_base.h | 2 +- ortools/sat/sat_solver.cc | 2 +- ortools/sat/sat_solver.h | 2 +- ortools/sat/swig_helper.h | 7 +- ortools/sat/util.h | 2 +- ortools/util/bitset.h | 2 +- ortools/util/file_util.cc | 2 +- ortools/util/file_util.h | 2 +- ortools/util/java/vector.i | 6 +- ortools/util/monoid_operation_tree.h | 2 +- ortools/util/proto_tools.h | 2 +- ortools/util/sorted_interval_list.h | 3 +- ortools/util/stats.cc | 4 +- ortools/util/stats.h | 7 +- ortools/util/string_array.h | 4 +- ortools/util/time_limit.h | 15 +- 147 files changed, 1982 insertions(+), 1107 deletions(-) diff --git a/makefiles/Makefile.third_party.unix.mk b/makefiles/Makefile.third_party.unix.mk index c12cd14720..538adf7c76 100644 --- a/makefiles/Makefile.third_party.unix.mk +++ b/makefiles/Makefile.third_party.unix.mk @@ -22,7 +22,7 @@ PROTOC_BINARY := $(shell $(WHICH) ${UNIX_PROTOC_BINARY}) GFLAGS_TAG = 2.2.2 GLOG_TAG = 0.4.0 PROTOBUF_TAG = 3.10.0 -ABSL_TAG = bf29470 +ABSL_TAG = 2103fd9 CBC_TAG = 2.10.3 CGL_TAG = 0.60.2 CLP_TAG = 1.17.3 @@ -338,7 +338,8 @@ dependencies/install/lib/libabsl.$L: dependencies/sources/abseil-cpp-$(ABSL_TAG) cd dependencies/sources/abseil-cpp-$(ABSL_TAG) && \ $(SET_COMPILER) $(CMAKE) -H. -Bbuild_cmake \ -DCMAKE_PREFIX_PATH="$(OR_TOOLS_TOP)/dependencies/install" \ - -DBUILD_SHARED_LIBS=ON \ + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_STATIC_LIBS=ON \ -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ -DCMAKE_CXX_FLAGS="$(MAC_VERSION)" \ -DBUILD_TESTING=OFF \ @@ -350,7 +351,7 @@ dependencies/sources/abseil-cpp-$(ABSL_TAG): | dependencies/sources -$(DELREC) dependencies/sources/abseil-cpp-$(ABSL_TAG) git clone --quiet https://github.com/abseil/abseil-cpp.git dependencies/sources/abseil-cpp-$(ABSL_TAG) cd dependencies/sources/abseil-cpp-$(ABSL_TAG) && git reset --hard $(ABSL_TAG) - cd dependencies/sources/abseil-cpp-$(ABSL_TAG) && git apply "$(OR_TOOLS_TOP)/patches/abseil-cpp-$(ABSL_TAG).patch" +# cd dependencies/sources/abseil-cpp-$(ABSL_TAG) && git apply "$(OR_TOOLS_TOP)/patches/abseil-cpp-$(ABSL_TAG).patch" ABSL_INC = -I$(UNIX_ABSL_DIR)/include ABSL_SWIG = $(ABSL_INC) @@ -366,16 +367,43 @@ $(_ABSL_STATIC_LIB_DIR)libabsl_bad_variant_access.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_city.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_civil_time.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_debugging_internal.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_demangle_internal.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_dynamic_annotations.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_examine_stack.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_exponential_biased.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_failure_signal_handler.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_flags.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_flags_config.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_flags_handle.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_flags_internal.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_flags_marshalling.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_flags_parse.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_flags_program_name.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_flags_registry.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_flags_usage.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_flags_usage_internal.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_graphcycles_internal.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_hash.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_hashtablez_sampler.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_int128.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_leak_check.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_leak_check_disable.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_log_severity.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_malloc_internal.a \ -$(_ABSL_STATIC_LIB_DIR)libabsl_optional.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_periodic_sampler.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_random_distributions.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_random_internal_distribution_test_util.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_random_internal_pool_urbg.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_random_internal_randen.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_random_internal_randen_hwaes.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_random_internal_randen_hwaes_impl.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_random_internal_randen_slow.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_random_internal_seed_material.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_random_seed_gen_exception.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_random_seed_sequences.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_raw_hash_set.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_raw_logging_internal.a \ +$(_ABSL_STATIC_LIB_DIR)libabsl_scoped_set_env.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_spinlock_wait.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_stacktrace.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_str_format_internal.a \ @@ -385,12 +413,40 @@ $(_ABSL_STATIC_LIB_DIR)libabsl_symbolize.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_throw_delegate.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_time.a \ $(_ABSL_STATIC_LIB_DIR)libabsl_time_zone.a \ -$(_ABSL_STATIC_LIB_DIR)libabsl_base.a \ -$(_ABSL_STATIC_LIB_DIR)libabsl_int128.a \ -$(_ABSL_STATIC_LIB_DIR)libabsl_synchronization.a \ -$(_ABSL_STATIC_LIB_DIR)libabsl_debugging_internal.a \ -$(_ABSL_STATIC_LIB_DIR)libabsl_demangle_internal.a \ -$(_ABSL_STATIC_LIB_DIR)libabsl_dynamic_annotations.a \ + +#$(_ABSL_STATIC_LIB_DIR)libabsl_base.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_synchronization.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_bad_any_cast_impl.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_bad_optional_access.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_bad_variant_access.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_city.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_civil_time.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_debugging_internal.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_examine_stack.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_failure_signal_handler.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_graphcycles_internal.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_hash.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_hashtablez_sampler.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_int128.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_leak_check.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_malloc_internal.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_optional.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_raw_hash_set.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_spinlock_wait.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_stacktrace.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_str_format_internal.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_strings.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_strings_internal.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_symbolize.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_throw_delegate.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_time.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_time_zone.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_base.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_int128.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_synchronization.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_debugging_internal.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_demangle_internal.a \ +#$(_ABSL_STATIC_LIB_DIR)libabsl_dynamic_annotations.a \ _ABSL_LIB_DIR = $(dir $(wildcard \ $(UNIX_ABSL_DIR)/lib*/libabsl_base.$L \ diff --git a/ortools/algorithms/dynamic_partition.cc b/ortools/algorithms/dynamic_partition.cc index a14735e8af..2886ca8b54 100644 --- a/ortools/algorithms/dynamic_partition.cc +++ b/ortools/algorithms/dynamic_partition.cc @@ -282,7 +282,7 @@ std::string MergingPartition::DebugString() { std::sort(part.begin(), part.end()); std::sort(sorted_parts.begin(), sorted_parts.end()); // Note: typically, a lot of elements of "sorted_parts" will be empty, - // but these won't be visible in the std::string that we construct below. + // but these won't be visible in the string that we construct below. std::string out; for (const std::vector& part : sorted_parts) { if (!out.empty()) out += " | "; diff --git a/ortools/algorithms/dynamic_partition.h b/ortools/algorithms/dynamic_partition.h index 851ebaa075..8b9c6d89ad 100644 --- a/ortools/algorithms/dynamic_partition.h +++ b/ortools/algorithms/dynamic_partition.h @@ -22,10 +22,10 @@ // (aka "elements") into disjoint equivalence classes (aka "parts"). // // SAFETY: -// Like std::vector crashes when used improperly, these classes are not -// "safe": most of their methods may crash if called with invalid arguments. The -// client code is responsible for using this class properly. A few DCHECKs() -// will help catch bugs, though. +// Like vector crashes when used improperly, these classes are not "safe": +// most of their methods may crash if called with invalid arguments. The client +// code is responsible for using this class properly. A few DCHECKs() will help +// catch bugs, though. #ifndef OR_TOOLS_ALGORITHMS_DYNAMIC_PARTITION_H_ #define OR_TOOLS_ALGORITHMS_DYNAMIC_PARTITION_H_ @@ -112,8 +112,8 @@ class DynamicPartition { // Prerequisite: NumParts() >= original_num_parts. void UndoRefineUntilNumPartsEqual(int original_num_parts); - // Dump the partition to a std::string. There might be different conventions - // for sorting the parts and the elements inside them. + // Dump the partition to a string. There might be different conventions for + // sorting the parts and the elements inside them. enum DebugStringSorting { // Elements are sorted within parts, and parts are then sorted // lexicographically. diff --git a/ortools/algorithms/samples/knapsack.py b/ortools/algorithms/samples/knapsack.py index a4f3fa368f..6f2bca90d8 100644 --- a/ortools/algorithms/samples/knapsack.py +++ b/ortools/algorithms/samples/knapsack.py @@ -15,7 +15,6 @@ # [START import] from __future__ import print_function from ortools.algorithms import pywrapknapsack_solver - # [END import] diff --git a/ortools/algorithms/samples/simple_knapsack_program.py b/ortools/algorithms/samples/simple_knapsack_program.py index 15b00f862f..cd062bc6c3 100644 --- a/ortools/algorithms/samples/simple_knapsack_program.py +++ b/ortools/algorithms/samples/simple_knapsack_program.py @@ -15,7 +15,6 @@ # [START import] from __future__ import print_function from ortools.algorithms import pywrapknapsack_solver - # [END import] diff --git a/ortools/base/adjustable_priority_queue.h b/ortools/base/adjustable_priority_queue.h index d6204cfcfb..925ad7b1dd 100644 --- a/ortools/base/adjustable_priority_queue.h +++ b/ortools/base/adjustable_priority_queue.h @@ -15,6 +15,7 @@ #define OR_TOOLS_BASE_ADJUSTABLE_PRIORITY_QUEUE_H_ #include + #include #include #include @@ -28,11 +29,12 @@ class LowerPriorityThan { public: explicit LowerPriorityThan(Comparator* compare) : compare_(compare) {} bool operator()(T* a, T* b) const { return (*compare_)(*a, *b); } + private: Comparator* compare_; }; -template > +template > class AdjustablePriorityQueue { public: // The objects references 'c' and 'm' are not required to be alive for the @@ -99,14 +101,14 @@ class AdjustablePriorityQueue { topvec->push_back(elems_[ind]); int leftchild = 1 + 2 * ind; if (leftchild < Size()) { - if (!LowerPriorityThan(&c_)(elems_[leftchild], elems_[ind])) { - need_to_check_children.push_back(leftchild); - } - int rightchild = leftchild + 1; - if (rightchild < Size() && - !LowerPriorityThan(&c_)(elems_[rightchild], elems_[ind])) { - need_to_check_children.push_back(rightchild); - } + if (!LowerPriorityThan(&c_)(elems_[leftchild], elems_[ind])) { + need_to_check_children.push_back(leftchild); + } + int rightchild = leftchild + 1; + if (rightchild < Size() && + !LowerPriorityThan(&c_)(elems_[rightchild], elems_[ind])) { + need_to_check_children.push_back(rightchild); + } } } } @@ -130,13 +132,15 @@ class AdjustablePriorityQueue { // priority than its child). void CheckValid() { for (int i = 0; i < elems_.size(); ++i) { - int left_child = 1 + 2*i; + int left_child = 1 + 2 * i; if (left_child < elems_.size()) { - CHECK(!(LowerPriorityThan(&c_))(elems_[i], elems_[left_child])); + CHECK( + !(LowerPriorityThan(&c_))(elems_[i], elems_[left_child])); } int right_child = left_child + 1; if (right_child < elems_.size()) { - CHECK(!(LowerPriorityThan(&c_))(elems_[i], elems_[right_child])); + CHECK( + !(LowerPriorityThan(&c_))(elems_[i], elems_[right_child])); } } } diff --git a/ortools/base/encodingutils.h b/ortools/base/encodingutils.h index c7e27c3de4..bf28632ae0 100644 --- a/ortools/base/encodingutils.h +++ b/ortools/base/encodingutils.h @@ -18,7 +18,7 @@ namespace EncodingUtils { -// Returns the number of characters of a UTF8-encoded std::string. +// Returns the number of characters of a UTF8-encoded string. inline int UTF8StrLen(const std::string& utf8_str) { if (utf8_str.empty()) return 0; const char* c = utf8_str.c_str(); diff --git a/ortools/base/file.h b/ortools/base/file.h index f18b80d5d5..651f11fefc 100644 --- a/ortools/base/file.h +++ b/ortools/base/file.h @@ -60,12 +60,12 @@ class File { // If read failed, program will exit. void ReadOrDie(void* const buff, size_t size); - // Reads a line from file to a std::string. + // Reads a line from file to a string. // Each line must be no more than max_length bytes. char* ReadLine(char* const output, uint64 max_length); - // Reads the whole file to a std::string, with a maximum length of - // 'max_length'. Returns the number of bytes read. + // Reads the whole file to a string, with a maximum length of 'max_length'. + // Returns the number of bytes read. int64 ReadToString(std::string* const line, uint64 max_length); // Writes "size" bytes of buff to file, buff should be pre-allocated. @@ -75,10 +75,10 @@ class File { // If write failed, program will exit. void WriteOrDie(const void* const buff, size_t size); - // Writes a std::string to file. + // Writes a string to file. size_t WriteString(const std::string& line); - // Writes a std::string to file and append a "\n". + // Writes a string to file and append a "\n". bool WriteLine(const std::string& line); // Closes the file. diff --git a/ortools/base/filelineiter.h b/ortools/base/filelineiter.h index 03906eac1b..905a96d566 100644 --- a/ortools/base/filelineiter.h +++ b/ortools/base/filelineiter.h @@ -18,8 +18,7 @@ // * 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. +// * If not empty, the 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. // diff --git a/ortools/base/int_type.h b/ortools/base/int_type.h index 3a8e81da40..e333c95ce2 100644 --- a/ortools/base/int_type.h +++ b/ortools/base/int_type.h @@ -151,7 +151,6 @@ #include #include // NOLINT #include -#include #include "absl/strings/string_view.h" #include "ortools/base/macros.h" diff --git a/ortools/base/jniutil.h b/ortools/base/jniutil.h index a3cf3c9d67..e7f7408e3a 100644 --- a/ortools/base/jniutil.h +++ b/ortools/base/jniutil.h @@ -29,8 +29,8 @@ class JNIUtil { return env->NewStringUTF(cstr); } - // Creates a null-terminated UTF-8 encoded C std::string from a jstring. - // The returned std::string should be "delete[]"-ed when no longer needed. + // Creates a null-terminated UTF-8 encoded C string from a jstring. + // The returned string should be "delete[]"-ed when no longer needed. static char* MakeCString(JNIEnv* env, jstring str) { if (str == NULL) return NULL; jsize length = env->GetStringUTFLength(str); diff --git a/ortools/base/protoutil.h b/ortools/base/protoutil.h index 773848121f..4546b593cc 100644 --- a/ortools/base/protoutil.h +++ b/ortools/base/protoutil.h @@ -14,6 +14,7 @@ #ifndef OR_TOOLS_BASE_PROTOUTIL_H_ #define OR_TOOLS_BASE_PROTOUTIL_H_ +#include "absl/time/clock.h" #include "absl/time/time.h" #include "google/protobuf/duration.pb.h" #include "ortools/base/status.h" diff --git a/ortools/base/random.cc b/ortools/base/random.cc index b71c7ab78b..04079a3d68 100644 --- a/ortools/base/random.cc +++ b/ortools/base/random.cc @@ -79,8 +79,7 @@ int32 ACMRandom::HostnamePidTimeSeed() { } const int namelen = strlen(name); for (size_t i = 0; i < sizeof(uint32) * 3; ++i) { - name[namelen + i] = - '\0'; // so we mix 0's once we get to end-of-std::string + name[namelen + i] = '\0'; // so we mix 0's once we get to end-of-string } #if defined(__GNUC__) uint32 a = getpid(); diff --git a/ortools/base/random.h b/ortools/base/random.h index 94958902c4..08607e2fe2 100644 --- a/ortools/base/random.h +++ b/ortools/base/random.h @@ -17,6 +17,7 @@ #include #include +#include "absl/random/random.h" #include "ortools/base/basictypes.h" namespace operations_research { @@ -119,18 +120,4 @@ typedef ACMRandom RandomBase; } // namespace operations_research -namespace absl { -template -bool Bernoulli(URBG&& urbg, // NOLINT(runtime/references) - double p) { - return std::bernoulli_distribution(p)(urbg); -} - -template -int Uniform(URGB&& urgb, // NOLINT(runtime/references) - int low, int high_excluded) { - return std::uniform_int_distribution<>(low, high_excluded - 1)(urgb); -} -} // namespace absl - #endif // OR_TOOLS_BASE_RANDOM_H_ diff --git a/ortools/base/status.h b/ortools/base/status.h index 6f636d2cec..9fae8f73fb 100644 --- a/ortools/base/status.h +++ b/ortools/base/status.h @@ -47,12 +47,13 @@ struct Status { return absl::StrCat("ERROR #", error_code_, ": '", error_message_, "'"); } - util::error::Error code() const { return static_cast(error_code_); } + util::error::Error code() const { + return static_cast(error_code_); + } std::string error_message() const { return error_message_; } std::string message() const { return error_message(); } - void IgnoreError() const {} private: diff --git a/ortools/base/stl_util.h b/ortools/base/stl_util.h index 788b7cbf95..969c6d6a7b 100644 --- a/ortools/base/stl_util.h +++ b/ortools/base/stl_util.h @@ -186,11 +186,11 @@ inline void STLClearHashIfBig(T* obj, size_t limit) { } } -// Reserves space in the given std::string only if the existing capacity is not -// already enough. This is useful for strings because std::string::reserve() may +// Reserves space in the given string only if the existing capacity is not +// already enough. This is useful for strings because string::reserve() may // *shrink* the capacity in some cases, which is usually not what users want. // The behavior of this function is similar to that of vector::reserve() but for -// std::string. +// string. inline void STLStringReserveIfNeeded(std::string* s, size_t min_capacity) { if (min_capacity > s->capacity()) s->reserve(min_capacity); } @@ -198,15 +198,15 @@ inline void STLStringReserveIfNeeded(std::string* s, size_t min_capacity) { // Like str->resize(new_size), except any new characters added to "*str" as a // result of resizing may be left uninitialized, rather than being filled with // '0' bytes. Typically used when code is then going to overwrite the backing -// store of the std::string with known data. +// store of the string with known data. template inline void STLStringResizeUninitialized(std::basic_string* s, size_t new_size) { absl::strings_internal::STLStringResizeUninitialized(s, new_size); } -// Returns true if the std::string implementation supports a resize where -// the new characters added to the std::string are left untouched. +// Returns true if the string implementation supports a resize where +// the new characters added to the string are left untouched. // // (A better name might be "STLStringSupportsUninitializedResize", alluding to // the previous function.) @@ -216,26 +216,26 @@ inline bool STLStringSupportsNontrashingResize( return absl::strings_internal::STLStringSupportsNontrashingResize(&s); } -// Assigns the n bytes starting at ptr to the given std::string. This is -// intended to be faster than std::string::assign() in SOME cases, however, it's -// actually slower in some cases as well. +// Assigns the n bytes starting at ptr to the given string. This is intended to +// be faster than string::assign() in SOME cases, however, it's actually slower +// in some cases as well. // -// Just use std::string::assign directly unless you have benchmarks showing that -// this function makes your code faster. (Even then, a future version of -// std::string::assign() may be faster than this.) +// Just use string::assign directly unless you have benchmarks showing that this +// function makes your code faster. (Even then, a future version of +// string::assign() may be faster than this.) inline void STLAssignToString(std::string* str, const char* ptr, size_t n) { STLStringResizeUninitialized(str, n); if (n == 0) return; memcpy(&*str->begin(), ptr, n); } -// Appends the n bytes starting at ptr to the given std::string. This is -// intended to be faster than std::string::append() in SOME cases, however, it's -// actually slower in some cases as well. +// Appends the n bytes starting at ptr to the given string. This is intended to +// be faster than string::append() in SOME cases, however, it's actually slower +// in some cases as well. // -// Just use std::string::append directly unless you have benchmarks showing that -// this function makes your code faster. (Even then, a future version of -// std::string::append() may be faster than this.) +// Just use string::append directly unless you have benchmarks showing that this +// function makes your code faster. (Even then, a future version of +// string::append() may be faster than this.) inline void STLAppendToString(std::string* str, const char* ptr, size_t n) { if (n == 0) return; size_t old_size = str->size(); @@ -243,20 +243,20 @@ inline void STLAppendToString(std::string* str, const char* ptr, size_t n) { memcpy(&*str->begin() + old_size, ptr, n); } -// Returns a mutable char* pointing to a std::string's internal buffer, which -// may not be null-terminated. Returns nullptr for an empty std::string. If not -// non-null, writing through this pointer will modify the std::string. +// Returns a mutable char* pointing to a string's internal buffer, which may not +// be null-terminated. Returns nullptr for an empty string. If not non-null, +// writing through this pointer will modify the string. // // string_as_array(&str)[i] is valid for 0 <= i < str.size() until the -// next call to a std::string method that invalidates iterators. +// next call to a string method that invalidates iterators. // // In C++11 you may simply use &str[0] to get a mutable char*. // // Prior to C++11, there was no standard-blessed way of getting a mutable -// reference to a std::string's internal buffer. The requirement that -// std::string be contiguous is officially part of the C++11 standard -// [std::string.require]/5. According to Matt Austern, this should already work -// on all current C++98 implementations. +// reference to a string's internal buffer. The requirement that string be +// contiguous is officially part of the C++11 standard [string.require]/5. +// According to Matt Austern, this should already work on all current C++98 +// implementations. inline char* string_as_array(std::string* str) { // DO NOT USE const_cast(str->data())! See the unittest for why. return str->empty() ? nullptr : &*str->begin(); diff --git a/ortools/base/thorough_hash.h b/ortools/base/thorough_hash.h index aac508fe9d..50fca0cd95 100644 --- a/ortools/base/thorough_hash.h +++ b/ortools/base/thorough_hash.h @@ -28,8 +28,8 @@ inline uint64 MixTwoUInt64(uint64 fp1, uint64 fp2) { return a + (~a >> 47); } -// This should be better (collision-wise) than the default hash, -// without being much slower. It never returns 0 or 1. +// This should be better (collision-wise) than the default hash, without +// being much slower. It never returns 0 or 1. inline uint64 ThoroughHash(const char* bytes, size_t len) { // Some big prime numer. uint64 fp = 0xa5b85c5e198ed849ULL; diff --git a/ortools/bop/bop_base.h b/ortools/bop/bop_base.h index 1cc79609c4..be7f43988d 100644 --- a/ortools/bop/bop_base.h +++ b/ortools/bop/bop_base.h @@ -91,7 +91,7 @@ class BopOptimizerBase { const ProblemState& problem_state, LearnedInfo* learned_info, TimeLimit* time_limit) = 0; - // Returns a std::string describing the status. + // Returns a string describing the status. static std::string GetStatusString(Status status); protected: diff --git a/ortools/bop/bop_ls.cc b/ortools/bop/bop_ls.cc index 5e12c35d2f..f5f98ac584 100644 --- a/ortools/bop/bop_ls.cc +++ b/ortools/bop/bop_ls.cc @@ -88,8 +88,7 @@ BopOptimizerBase::Status LocalSearchOptimizer::Optimize( // the ls? note that this is minor. sat_wrapper_.ExtractLearnedInfo(learned_info); if (assignment_iterator_->BetterSolutionHasBeenFound()) { - // TODO(user): simply use std::vector instead of a BopSolution - // internally. + // TODO(user): simply use vector instead of a BopSolution internally. learned_info->solution = assignment_iterator_->LastReferenceAssignment(); return BopOptimizerBase::SOLUTION_FOUND; } diff --git a/ortools/bop/bop_ls.h b/ortools/bop/bop_ls.h index 712cfbdf61..c9b4915bdc 100644 --- a/ortools/bop/bop_ls.h +++ b/ortools/bop/bop_ls.h @@ -31,6 +31,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" +#include "absl/random/random.h" #include "ortools/base/hash.h" #include "ortools/base/random.h" #include "ortools/bop/bop_base.h" @@ -222,7 +223,7 @@ class NonOrderedSetHasher { } // The hash of a set is simply the XOR of all its elements. This allows - // to compute an hash incrementally or without the need of a std::vector<>. + // to compute an hash incrementally or without the need of a vector<>. uint64 Hash(IntType e) const { return hashes_[e]; } // Returns true if Initialize() has been called with a non-zero size. diff --git a/ortools/bop/bop_solver.cc b/ortools/bop/bop_solver.cc index 097036b084..9bdcebe58a 100644 --- a/ortools/bop/bop_solver.cc +++ b/ortools/bop/bop_solver.cc @@ -179,7 +179,7 @@ double BopSolver::GetScaledGap() const { void BopSolver::UpdateParameters() { if (parameters_.solver_optimizer_sets_size() == 0) { - // No user defined optimizers, use the default std::string to define the + // No user defined optimizers, use the default string to define the // behavior. CHECK(::google::protobuf::TextFormat::ParseFromString( parameters_.default_solver_optimizer_sets(), diff --git a/ortools/constraint_solver/assignment.cc b/ortools/constraint_solver/assignment.cc index b7959abd03..69f89cfab0 100644 --- a/ortools/constraint_solver/assignment.cc +++ b/ortools/constraint_solver/assignment.cc @@ -541,7 +541,7 @@ void Assignment::Load(const AssignmentProto& assignment_proto) { const IntVarAssignment& objective = assignment_proto.objective(); const std::string objective_id = objective.var_id(); CHECK(!objective_id.empty()); - if (HasObjective() && objective_id.compare(Objective()->name()) == 0) { + if (HasObjective() && objective_id == Objective()->name()) { const int64 obj_min = objective.min(); const int64 obj_max = objective.max(); SetObjectiveRange(obj_min, obj_max); diff --git a/ortools/constraint_solver/constraint_solver.cc b/ortools/constraint_solver/constraint_solver.cc index 8b9bcf55a3..b8a1c3403b 100644 --- a/ortools/constraint_solver/constraint_solver.cc +++ b/ortools/constraint_solver/constraint_solver.cc @@ -25,13 +25,14 @@ #include #include "absl/memory/memory.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/file.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" #include "ortools/base/map_util.h" -#include "ortools/base/random.h" #include "ortools/base/recordio.h" #include "ortools/base/stl_util.h" #include "ortools/constraint_solver/constraint_solveri.h" @@ -85,6 +86,10 @@ DEFINE_bool(cp_use_element_rmq, true, DEFINE_int32(cp_check_solution_period, 1, "Number of solutions explored between two solution checks during " "local search."); +DEFINE_int64(cp_random_seed, 12345, + "Random seed used in several (but not all) random number " + "generators used by the CP solver. Use -1 to auto-generate an" + "undeterministic random seed."); void ConstraintSolverFailsHere() { VLOG(3) << "Fail"; } @@ -1395,7 +1400,7 @@ Solver::Solver(const std::string& name, const ConstraintSolverParameters& parameters) : name_(name), parameters_(parameters), - random_(ACMRandom::DeterministicSeed()), + random_(CpRandomSeed()), demon_profiler_(BuildDemonProfiler(this)), use_fast_local_search_(true), local_search_profiler_(BuildLocalSearchProfiler(this)) { @@ -1405,7 +1410,7 @@ Solver::Solver(const std::string& name, Solver::Solver(const std::string& name) : name_(name), parameters_(DefaultSolverParameters()), - random_(ACMRandom::DeterministicSeed()), + random_(CpRandomSeed()), demon_profiler_(BuildDemonProfiler(this)), use_fast_local_search_(true), local_search_profiler_(BuildLocalSearchProfiler(this)) { @@ -2455,7 +2460,7 @@ std::string Solver::GetName(const PropagationBaseObject* object) { void Solver::SetName(const PropagationBaseObject* object, const std::string& name) { if (parameters_.store_names() && - GetName(object).compare(name) != 0) { // in particular if name.empty() + GetName(object) != name) { // in particular if name.empty() propagation_object_names_[object] = name; } } diff --git a/ortools/constraint_solver/constraint_solver.h b/ortools/constraint_solver/constraint_solver.h index 9807b00b4e..47c623c7fe 100644 --- a/ortools/constraint_solver/constraint_solver.h +++ b/ortools/constraint_solver/constraint_solver.h @@ -66,12 +66,15 @@ #include #include #include +#include #include #include #include #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" +#include "absl/random/distributions.h" +#include "absl/random/random.h" #include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/hash.h" @@ -79,7 +82,6 @@ #include "ortools/base/logging.h" #include "ortools/base/macros.h" #include "ortools/base/map_util.h" -#include "ortools/base/random.h" #include "ortools/base/sysinfo.h" #include "ortools/base/timer.h" #include "ortools/constraint_solver/solver_parameters.pb.h" @@ -87,6 +89,10 @@ #include "ortools/util/sorted_interval_list.h" #include "ortools/util/tuple_set.h" +#if !defined(SWIG) +DECLARE_int64(cp_random_seed); +#endif // !defined(SWIG) + class File; namespace operations_research { @@ -149,6 +155,12 @@ struct Trail; template class SimpleRevFIFO; +inline int64 CpRandomSeed() { + return FLAGS_cp_random_seed == -1 + ? absl::Uniform(absl::BitGen(), 0, kint64max) + : FLAGS_cp_random_seed; +} + /// This struct holds all parameters for the default search. /// DefaultPhaseParameters is only used by Solver::MakeDefaultPhase methods. /// Note this is for advanced users only. @@ -1508,9 +1520,9 @@ class Solver { /// https://mpi-inf.mpg.de/~mehlhorn/ftp/Mehlhorn-Thiel.pdf Constraint* MakeSortingConstraint(const std::vector& vars, const std::vector& sorted); - // TODO(user): Add void MakeSortedArray(const std::vector& vars, - /// std::vector* const - /// sorted); + // TODO(user): Add void MakeSortedArray( + // const std::vector& vars, + // std::vector* const sorted); /// Creates a constraint that enforces that left is lexicographically less /// than right. @@ -2217,7 +2229,7 @@ class Solver { RegularLimitParameters MakeDefaultRegularLimitParameters() const; /// Creates a search limit that is reached when either of the underlying limit - /// is reached. That is, the returned limit is more stringent than both + /// is reached. That is, the returned limit is more std::stringent than both /// argument limits. SearchLimit* MakeLimit(SearchLimit* const limit_1, SearchLimit* const limit_2); @@ -2771,13 +2783,19 @@ class Solver { } /// Returns a random value between 0 and 'size' - 1; - int64 Rand64(int64 size) { return random_.Next64() % size; } + int64 Rand64(int64 size) { + DCHECK_GT(size, 0); + return absl::Uniform(random_, 0, size); + } /// Returns a random value between 0 and 'size' - 1; - int32 Rand32(int32 size) { return random_.Next() % size; } + int32 Rand32(int32 size) { + DCHECK_GT(size, 0); + return absl::Uniform(random_, 0, size); + } /// Reseed the solver random generator. - void ReSeed(int32 seed) { random_.Reset(seed); } + void ReSeed(int32 seed) { random_.seed(seed); } /// Exports the profiling information in a human readable overview. /// The parameter profile_level used to create the solver must be @@ -3033,7 +3051,7 @@ class Solver { OptimizationDirection optimization_direction_; std::unique_ptr timer_; std::vector searches_; - ACMRandom random_; + std::mt19937 random_; uint64 fail_stamp_; std::unique_ptr balancing_decision_; /// intercept failures diff --git a/ortools/constraint_solver/constraint_solveri.h b/ortools/constraint_solver/constraint_solveri.h index 34d3fbe3c7..534dad38b2 100644 --- a/ortools/constraint_solver/constraint_solveri.h +++ b/ortools/constraint_solver/constraint_solveri.h @@ -821,7 +821,7 @@ template class VarLocalSearchOperator : public LocalSearchOperator { public: VarLocalSearchOperator() : activated_(), was_activated_(), cleared_(true) {} - VarLocalSearchOperator(std::vector vars, Handler var_handler) + explicit VarLocalSearchOperator(Handler var_handler) : activated_(), was_activated_(), cleared_(true), @@ -835,8 +835,8 @@ class VarLocalSearchOperator : public LocalSearchOperator { CHECK_LE(size, assignment->Size()) << "Assignment contains fewer variables than operator"; for (int i = 0; i < size; ++i) { - activated_.Set(i, var_handler_.ValueFromAssignent(*assignment, vars_[i], - i, &values_[i])); + activated_.Set(i, var_handler_.ValueFromAssignment(*assignment, vars_[i], + i, &values_[i])); } prev_values_ = old_values_; old_values_ = values_; @@ -899,7 +899,7 @@ class VarLocalSearchOperator : public LocalSearchOperator { cleared_ = true; for (const int64 index : changes_.PositionsSetAtLeastOnce()) { values_[index] = old_values_[index]; - var_handler_.OnRevertChanges(index); + var_handler_.OnRevertChanges(index, values_[index]); activated_.CopyBucket(was_activated_, index); assignment_indices_[index] = -1; } @@ -948,8 +948,14 @@ class VarLocalSearchOperator : public LocalSearchOperator { }; /// Base operator class for operators manipulating IntVars. +class IntVarLocalSearchOperator; + class IntVarLocalSearchHandler { public: + IntVarLocalSearchHandler() : op_(nullptr) {} + IntVarLocalSearchHandler(const IntVarLocalSearchHandler& other) + : op_(other.op_) {} + explicit IntVarLocalSearchHandler(IntVarLocalSearchOperator* op) : op_(op) {} void AddToAssignment(IntVar* var, int64 value, bool active, std::vector* assignment_indices, int64 index, Assignment* assignment) const { @@ -973,20 +979,13 @@ class IntVarLocalSearchHandler { element->Deactivate(); } } - bool ValueFromAssignent(const Assignment& assignment, IntVar* var, - int64 index, int64* value) { - const Assignment::IntContainer& container = assignment.IntVarContainer(); - const IntVarElement* element = &(container.Element(index)); - if (element->Var() != var) { - CHECK(container.Contains(var)) - << "Assignment does not contain operator variable " << var; - element = &(container.Element(var)); - } - *value = element->Value(); - return element->Activated(); - } - void OnRevertChanges(int64 index) {} + bool ValueFromAssignment(const Assignment& assignment, IntVar* var, + int64 index, int64* value); + void OnRevertChanges(int64 index, int64 value); void OnAddVars() {} + + private: + IntVarLocalSearchOperator* const op_; }; /// Specialization of LocalSearchOperator built from an array of IntVars @@ -1033,11 +1032,24 @@ class IntVarLocalSearchHandler { class IntVarLocalSearchOperator : public VarLocalSearchOperator { public: - IntVarLocalSearchOperator() {} - explicit IntVarLocalSearchOperator(const std::vector& vars) + IntVarLocalSearchOperator() : max_inverse_value_(-1) {} + // If keep_inverse_values is true, assumes that vars models an injective + // function f with domain [0, vars.size()) in which case the operator will + // maintain the inverse function. + explicit IntVarLocalSearchOperator(const std::vector& vars, + bool keep_inverse_values = false) : VarLocalSearchOperator( - vars, IntVarLocalSearchHandler()) { + IntVarLocalSearchHandler(this)), + max_inverse_value_(keep_inverse_values ? vars.size() - 1 : -1) { AddVars(vars); + if (keep_inverse_values) { + int64 max_value = -1; + for (const IntVar* const var : vars) { + max_value = std::max(max_value, var->Max()); + } + inverse_values_.resize(max_value + 1, -1); + old_inverse_values_.resize(max_value + 1, -1); + } } ~IntVarLocalSearchOperator() override {} /// Redefines MakeNextNeighbor to export a simpler interface. The calls to @@ -1049,13 +1061,63 @@ class IntVarLocalSearchOperator bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override; protected: + friend class IntVarLocalSearchHandler; + /// Creates a new neighbor. It returns false when the neighborhood is /// completely explored. // TODO(user): make it pure virtual, implies porting all apps overriding /// MakeNextNeighbor() in a subclass of IntVarLocalSearchOperator. virtual bool MakeOneNeighbor(); + + bool IsInverseValue(int64 index) const { + DCHECK_GE(index, 0); + return index <= max_inverse_value_; + } + + int64 InverseValue(int64 index) const { return inverse_values_[index]; } + + int64 OldInverseValue(int64 index) const { + return old_inverse_values_[index]; + } + + void SetInverseValue(int64 index, int64 value) { + inverse_values_[index] = value; + } + + void SetOldInverseValue(int64 index, int64 value) { + old_inverse_values_[index] = value; + } + + private: + const int64 max_inverse_value_; + std::vector old_inverse_values_; + std::vector inverse_values_; }; +inline bool IntVarLocalSearchHandler::ValueFromAssignment( + const Assignment& assignment, IntVar* var, int64 index, int64* value) { + const Assignment::IntContainer& container = assignment.IntVarContainer(); + const IntVarElement* element = &(container.Element(index)); + if (element->Var() != var) { + CHECK(container.Contains(var)) + << "Assignment does not contain operator variable " << var; + element = &(container.Element(var)); + } + *value = element->Value(); + if (op_->IsInverseValue(index)) { + op_->SetInverseValue(*value, index); + op_->SetOldInverseValue(*value, index); + } + return element->Activated(); +} + +inline void IntVarLocalSearchHandler::OnRevertChanges(int64 index, + int64 value) { + if (op_->IsInverseValue(index)) { + op_->SetInverseValue(value, index); + } +} + /// SequenceVarLocalSearchOperator class SequenceVarLocalSearchOperator; @@ -1069,9 +1131,9 @@ class SequenceVarLocalSearchHandler { void AddToAssignment(SequenceVar* var, const std::vector& value, bool active, std::vector* assignment_indices, int64 index, Assignment* assignment) const; - bool ValueFromAssignent(const Assignment& assignment, SequenceVar* var, - int64 index, std::vector* value); - void OnRevertChanges(int64 index); + bool ValueFromAssignment(const Assignment& assignment, SequenceVar* var, + int64 index, std::vector* value); + void OnRevertChanges(int64 index, const std::vector& value); void OnAddVars(); private: @@ -1102,7 +1164,7 @@ class SequenceVarLocalSearchOperator SequenceVarLocalSearchOperator() {} explicit SequenceVarLocalSearchOperator(const std::vector& vars) : SequenceVarLocalSearchOperatorTemplate( - vars, SequenceVarLocalSearchHandler(this)) { + SequenceVarLocalSearchHandler(this)) { AddVars(vars); } ~SequenceVarLocalSearchOperator() override {} @@ -1152,7 +1214,7 @@ inline void SequenceVarLocalSearchHandler::AddToAssignment( } } -inline bool SequenceVarLocalSearchHandler::ValueFromAssignent( +inline bool SequenceVarLocalSearchHandler::ValueFromAssignment( const Assignment& assignment, SequenceVar* var, int64 index, std::vector* value) { const Assignment::SequenceContainer& container = @@ -1170,7 +1232,8 @@ inline bool SequenceVarLocalSearchHandler::ValueFromAssignent( return element->Activated(); } -inline void SequenceVarLocalSearchHandler::OnRevertChanges(int64 index) { +inline void SequenceVarLocalSearchHandler::OnRevertChanges( + int64 index, const std::vector& value) { op_->backward_values_[index].clear(); } @@ -1252,7 +1315,7 @@ class ChangeValue : public IntVarLocalSearchOperator { /// a path). /// Several services are provided: /// - arc manipulators (SetNext(), ReverseChain(), MoveChain()) -/// - path inspectors (Next(), IsPathEnd()) +/// - path inspectors (Next(), Prev(), IsPathEnd()) /// - path iterators: operators need a given number of nodes to define a /// neighbor; this class provides the iteration on a given number of (base) /// nodes which can be used to define a neighbor (through the BaseNode method) @@ -1275,7 +1338,7 @@ class PathOperator : public IntVarLocalSearchOperator { /// be removed. PathOperator(const std::vector& next_vars, const std::vector& path_vars, int number_of_base_nodes, - bool skip_locally_optimal_paths, + bool skip_locally_optimal_paths, bool accept_path_end_base, std::function start_empty_path_class); ~PathOperator() override {} virtual bool MakeNeighbor() = 0; @@ -1290,6 +1353,13 @@ class PathOperator : public IntVarLocalSearchOperator { return Value(node); } + /// Returns the node before node in the current delta. + int64 Prev(int64 node) const { + DCHECK(!IsPathStart(node)); + DCHECK_EQ(Next(InverseValue(node)), node); + return InverseValue(node); + } + /// Returns the index of the path to which node belongs in the current delta. /// Only returns a valid value if path variables are taken into account. int64 Path(int64 node) const { @@ -1302,9 +1372,37 @@ class PathOperator : public IntVarLocalSearchOperator { protected: /// This method should not be overridden. Override MakeNeighbor() instead. bool MakeOneNeighbor() override; + /// Called by OnStart() after initializing node information. Should be + /// overridden instead of OnStart() to avoid calling PathOperator::OnStart + /// explicitly. + virtual void OnNodeInitialization() {} /// Returns the ith base node of the operator. int64 BaseNode(int i) const { return base_nodes_[i]; } + /// Returns the alternative for the ith base node. + int BaseAlternative(int i) const { return base_alternatives_[i]; } + /// Returns the alternative node for the ith base node. + int64 BaseAlternativeNode(int i) const { + if (!ConsiderAlternatives(i)) return BaseNode(i); + const int alternative_index = alternative_index_[BaseNode(i)]; + return alternative_index >= 0 + ? alternative_sets_[alternative_index][base_alternatives_[i]] + : BaseNode(i); + } + /// Returns the alternative for the sibling of the ith base node. + int BaseSiblingAlternative(int i) const { + return base_sibling_alternatives_[i]; + } + /// Returns the alternative node for the sibling of the ith base node. + int64 BaseSiblingAlternativeNode(int i) const { + if (!ConsiderAlternatives(i)) return BaseNode(i); + const int sibling_alternative_index = + GetSiblingAlternativeIndex(BaseNode(i)); + return sibling_alternative_index >= 0 + ? alternative_sets_[sibling_alternative_index] + [base_sibling_alternatives_[i]] + : BaseNode(i); + } /// Returns the start node of the ith base node. int64 StartNode(int i) const { return path_starts_[base_paths_[i]]; } /// Returns the vector of path start nodes. @@ -1343,12 +1441,20 @@ class PathOperator : public IntVarLocalSearchOperator { virtual void SetNextBaseToIncrement(int64 base_index) { next_base_to_increment_ = base_index; } + /// Indicates if alternatives should be considered when iterating over base + /// nodes. + virtual bool ConsiderAlternatives(int64 base_index) const { return false; } int64 OldNext(int64 node) const { DCHECK(!IsPathEnd(node)); return OldValue(node); } + int64 OldPrev(int64 node) const { + DCHECK(!IsPathStart(node)); + return OldInverseValue(node); + } + int64 OldPath(int64 node) const { return ignore_path_vars_ ? 0LL : OldValue(node + number_of_nexts_); } @@ -1366,11 +1472,14 @@ class PathOperator : public IntVarLocalSearchOperator { /// Makes the nodes on the chain starting after before_chain and ending at /// chain_end inactive. bool MakeChainInactive(int64 before_chain, int64 chain_end); + /// Replaces active by inactive in the current path, making active inactive. + bool SwapActiveAndInactive(int64 active, int64 inactive); /// Sets 'to' to be the node after 'from' on the given path. void SetNext(int64 from, int64 to, int64 path) { DCHECK_LT(from, number_of_nexts_); SetValue(from, to); + SetInverseValue(to, from); if (!ignore_path_vars_) { DCHECK_LT(from + number_of_nexts_, Size()); SetValue(from + number_of_nexts_, path); @@ -1381,6 +1490,9 @@ class PathOperator : public IntVarLocalSearchOperator { /// that node is outside the range of the variable array. bool IsPathEnd(int64 node) const { return node >= number_of_nexts_; } + /// Returns true if node is the first node on the path. + bool IsPathStart(int64 node) const { return OldInverseValue(node) == -1; } + /// Returns true if node is inactive. bool IsInactive(int64 node) const { return !IsPathEnd(node) && inactives_[node]; @@ -1430,6 +1542,12 @@ class PathOperator : public IntVarLocalSearchOperator { int64 GetActiveAlternativeNode(int node) const { return GetActiveInAlternativeSet(alternative_index_[node]); } + /// Returns the index of the alternative set of the sibling of node. + int GetSiblingAlternativeIndex(int node) const { + if (node >= alternative_index_.size()) return -1; + const int alternative = alternative_index_[node]; + return alternative >= 0 ? sibling_alternative_[alternative] : -1; + } /// Returns the active node in the alternative set of the sibling of the given /// node. int64 GetActiveAlternativeSibling(int node) const { @@ -1439,6 +1557,10 @@ class PathOperator : public IntVarLocalSearchOperator { alternative >= 0 ? sibling_alternative_[alternative] : -1; return GetActiveInAlternativeSet(sibling_alternative); } + /// Returns true if the chain is a valid path without cycles from before_chain + /// to chain_end and does not contain exclude. + bool CheckChainValidity(int64 before_chain, int64 chain_end, + int64 exclude) const; const int number_of_nexts_; const bool ignore_path_vars_; @@ -1448,10 +1570,6 @@ class PathOperator : public IntVarLocalSearchOperator { private: void OnStart() override; - /// Called by OnStart() after initializing node information. Should be - /// overridden instead of OnStart() to avoid calling PathOperator::OnStart - /// explicitly. - virtual void OnNodeInitialization() {} /// Returns true if two nodes are on the same path in the current assignment. bool OnSamePath(int64 node1, int64 node2) const; @@ -1469,17 +1587,18 @@ class PathOperator : public IntVarLocalSearchOperator { void InitializeInactives(); void InitializeBaseNodes(); void InitializeAlternatives(); - bool CheckChainValidity(int64 before_chain, int64 chain_end, - int64 exclude) const; void Synchronize(); std::vector base_nodes_; + std::vector base_alternatives_; + std::vector base_sibling_alternatives_; std::vector end_nodes_; std::vector base_paths_; std::vector path_starts_; std::vector inactives_; bool just_started_; bool first_start_; + const bool accept_path_end_base_; std::function start_empty_path_class_; bool skip_locally_optimal_paths_; bool optimal_paths_enabled_; @@ -1494,34 +1613,6 @@ class PathOperator : public IntVarLocalSearchOperator { std::vector sibling_alternative_; }; -/// Simple PathOperator wrapper that also stores the current previous nodes, -/// and is thus able to provide the "Prev" and "IsPathStart" functions. -class PathWithPreviousNodesOperator : public PathOperator { - public: - PathWithPreviousNodesOperator( - const std::vector& vars, - const std::vector& secondary_vars, int number_of_base_nodes, - std::function start_empty_path_class); - ~PathWithPreviousNodesOperator() override {} - - bool IsPathStart(int64 node) const { return prevs_[node] == -1; } - - int64 Prev(int64 node) const { - DCHECK(!IsPathStart(node)); - return prevs_[node]; - } - - std::string DebugString() const override { - return "PathWithPreviousNodesOperator"; - } - - protected: - void OnNodeInitialization() override; /// Initializes the "prevs_" array. - - private: - std::vector prevs_; -}; - /// Operator Factories. template LocalSearchOperator* MakeLocalSearchOperator( diff --git a/ortools/constraint_solver/csharp/constraint_solver.i b/ortools/constraint_solver/csharp/constraint_solver.i index d9f6db0e9d..1d8e47c7d4 100644 --- a/ortools/constraint_solver/csharp/constraint_solver.i +++ b/ortools/constraint_solver/csharp/constraint_solver.i @@ -714,7 +714,6 @@ namespace operations_research { %unignore SearchLog::Maintain; %unignore SearchLog::OutputDecision; - // IntVarLocalSearchHandler %ignore IntVarLocalSearchHandler; diff --git a/ortools/constraint_solver/default_search.cc b/ortools/constraint_solver/default_search.cc index 235231b2df..5faa810292 100644 --- a/ortools/constraint_solver/default_search.cc +++ b/ortools/constraint_solver/default_search.cc @@ -25,7 +25,6 @@ #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/random.h" #include "ortools/base/stl_util.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" @@ -41,7 +40,6 @@ namespace { const int kDefaultNumberOfSplits = 100; const int kDefaultHeuristicPeriod = 100; const int kDefaultHeuristicNumFailuresLimit = 30; -const int kDefaultSeed = 0; const bool kDefaultUseLastConflict = true; } // namespace @@ -53,7 +51,7 @@ DefaultPhaseParameters::DefaultPhaseParameters() heuristic_period(kDefaultHeuristicPeriod), heuristic_num_failures_limit(kDefaultHeuristicNumFailuresLimit), persistent_impact(true), - random_seed(kDefaultSeed), + random_seed(CpRandomSeed()), display_level(DefaultPhaseParameters::NORMAL), use_last_conflict(kDefaultUseLastConflict), decision_builder(nullptr) {} @@ -740,12 +738,16 @@ class RunHeuristicsAsDives : public Decision { } return false; } else { - const int index = random_.Uniform(heuristics_.size()); + DCHECK_GT(heuristics_.size(), 0); + const int index = absl::Uniform(random_, 0, heuristics_.size()); return RunOneHeuristic(solver, index); } } - int Rand32(int size) { return random_.Next() % size; } + int Rand32(int size) { + DCHECK_GT(size, 0); + return absl::Uniform(random_, 0, size); + } void Init(Solver* const solver, const std::vector& vars, int heuristic_num_failures_limit) { @@ -817,7 +819,7 @@ class RunHeuristicsAsDives : public Decision { SearchMonitor* heuristic_limit_; DefaultPhaseParameters::DisplayLevel display_level_; bool run_all_heuristics_; - ACMRandom random_; + std::mt19937 random_; const int heuristic_period_; int heuristic_branch_count_; int heuristic_runs_; diff --git a/ortools/constraint_solver/java/constraint_solver.i b/ortools/constraint_solver/java/constraint_solver.i index 0e4b7c4a10..92ba416397 100644 --- a/ortools/constraint_solver/java/constraint_solver.i +++ b/ortools/constraint_solver/java/constraint_solver.i @@ -189,7 +189,7 @@ PROTECT_FROM_FAILURE(Solver::Fail(), arg1); %typemap(javain) TYPE "$javainput" // passing the Callback to JNI java class. %enddef -// Method taking no parameters and returning a std::string +// Method taking no parameters and returning a string %define DEFINE_VOID_TO_STRING_CALLBACK( TYPE, JAVA_TYPE, JAVA_METHOD, JAVA_SIGN) diff --git a/ortools/constraint_solver/local_search.cc b/ortools/constraint_solver/local_search.cc index 4c90ccdb1c..626d58b5f8 100644 --- a/ortools/constraint_solver/local_search.cc +++ b/ortools/constraint_solver/local_search.cc @@ -24,6 +24,8 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/memory/memory.h" +#include "absl/random/distributions.h" +#include "absl/random/random.h" #include "absl/strings/str_cat.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/hash.h" @@ -32,7 +34,6 @@ #include "ortools/base/logging.h" #include "ortools/base/macros.h" #include "ortools/base/map_util.h" -#include "ortools/base/random.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/graph/hamiltonian_path.h" @@ -176,14 +177,14 @@ class RandomLns : public BaseLns { std::string DebugString() const override { return "RandomLns"; } private: - ACMRandom rand_; + std::mt19937 rand_; const int number_of_variables_; }; bool RandomLns::NextFragment() { DCHECK_GT(Size(), 0); for (int i = 0; i < number_of_variables_; ++i) { - AppendToFragment(rand_.Uniform(Size())); + AppendToFragment(absl::Uniform(rand_, 0, Size())); } return true; } @@ -191,8 +192,7 @@ bool RandomLns::NextFragment() { LocalSearchOperator* Solver::MakeRandomLnsOperator( const std::vector& vars, int number_of_variables) { - return MakeRandomLnsOperator(vars, number_of_variables, - ACMRandom::HostnamePidTimeSeed()); + return MakeRandomLnsOperator(vars, number_of_variables, CpRandomSeed()); } LocalSearchOperator* Solver::MakeRandomLnsOperator( @@ -342,16 +342,20 @@ PathOperator::PathOperator(const std::vector& next_vars, const std::vector& path_vars, int number_of_base_nodes, bool skip_locally_optimal_paths, + bool accept_path_end_base, std::function start_empty_path_class) - : IntVarLocalSearchOperator(next_vars), + : IntVarLocalSearchOperator(next_vars, true), 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), + base_alternatives_(number_of_base_nodes), + base_sibling_alternatives_(number_of_base_nodes), end_nodes_(number_of_base_nodes), base_paths_(number_of_base_nodes), just_started_(false), first_start_(true), + accept_path_end_base_(accept_path_end_base), start_empty_path_class_(std::move(start_empty_path_class)), skip_locally_optimal_paths_(skip_locally_optimal_paths), optimal_paths_enabled_(false), @@ -409,26 +413,25 @@ bool PathOperator::SkipUnchanged(int index) const { bool PathOperator::MoveChain(int64 before_chain, int64 chain_end, int64 destination) { - if (CheckChainValidity(before_chain, chain_end, destination) && - !IsPathEnd(chain_end) && !IsPathEnd(destination)) { - const int64 destination_path = Path(destination); - const int64 after_chain = Next(chain_end); - SetNext(chain_end, Next(destination), destination_path); - if (!ignore_path_vars_) { - int current = destination; - int next = Next(before_chain); - while (current != chain_end) { - SetNext(current, next, destination_path); - current = next; - next = Next(next); - } - } else { - SetNext(destination, Next(before_chain), destination_path); + if (destination == before_chain || destination == chain_end) return false; + DCHECK(CheckChainValidity(before_chain, chain_end, destination) && + !IsPathEnd(chain_end) && !IsPathEnd(destination)); + const int64 destination_path = Path(destination); + const int64 after_chain = Next(chain_end); + SetNext(chain_end, Next(destination), destination_path); + if (!ignore_path_vars_) { + int current = destination; + int next = Next(before_chain); + while (current != chain_end) { + SetNext(current, next, destination_path); + current = next; + next = Next(next); } - SetNext(before_chain, after_chain, Path(before_chain)); - return true; + } else { + SetNext(destination, Next(before_chain), destination_path); } - return false; + SetNext(before_chain, after_chain, Path(before_chain)); + return true; } bool PathOperator::ReverseChain(int64 before_chain, int64 after_chain, @@ -482,6 +485,12 @@ bool PathOperator::MakeChainInactive(int64 before_chain, int64 chain_end) { return false; } +bool PathOperator::SwapActiveAndInactive(int64 active, int64 inactive) { + if (active == inactive) return false; + const int64 prev = Prev(active); + return MakeChainInactive(prev, active) && MakeActive(inactive, prev); +} + bool PathOperator::IncrementPosition() { const int base_node_size = base_nodes_.size(); @@ -495,9 +504,39 @@ bool PathOperator::IncrementPosition() { int last_restarted = base_node_size; for (int i = base_node_size - 1; i >= 0; --i) { if (base_nodes_[i] < number_of_nexts_ && i <= next_base_to_increment_) { + if (ConsiderAlternatives(i)) { + // Iterate on sibling alternatives. + const int sibling_alternative_index = + GetSiblingAlternativeIndex(base_nodes_[i]); + if (sibling_alternative_index >= 0) { + if (base_sibling_alternatives_[i] < + alternative_sets_[sibling_alternative_index].size() - 1) { + ++base_sibling_alternatives_[i]; + break; + } else { + base_sibling_alternatives_[i] = 0; + } + } + // Iterate on base alternatives. + const int alternative_index = alternative_index_[base_nodes_[i]]; + if (alternative_index >= 0) { + if (base_alternatives_[i] < + alternative_sets_[alternative_index].size() - 1) { + ++base_alternatives_[i]; + break; + } else { + base_alternatives_[i] = 0; + base_sibling_alternatives_[i] = 0; + } + } + } + base_alternatives_[i] = 0; + base_sibling_alternatives_[i] = 0; base_nodes_[i] = OldNext(base_nodes_[i]); - break; + if (accept_path_end_base_ || !IsPathEnd(base_nodes_[i])) break; } + base_alternatives_[i] = 0; + base_sibling_alternatives_[i] = 0; base_nodes_[i] = StartNode(i); last_restarted = i; } @@ -512,6 +551,8 @@ bool PathOperator::IncrementPosition() { // base nodes "below" the node being repositioned have their final // position. for (int i = last_restarted; i < base_node_size; ++i) { + base_alternatives_[i] = 0; + base_sibling_alternatives_[i] = 0; base_nodes_[i] = GetBaseNodeRestartPosition(i); } if (last_restarted > 0) { @@ -544,12 +585,16 @@ bool PathOperator::IncrementPosition() { const int next_path_index = base_paths_[i] + 1; if (next_path_index < number_of_paths) { base_paths_[i] = next_path_index; + base_alternatives_[i] = 0; + base_sibling_alternatives_[i] = 0; base_nodes_[i] = path_starts_[next_path_index]; if (i == 0 || !OnSamePathAsPreviousBase(i)) { break; } } else { base_paths_[i] = 0; + base_alternatives_[i] = 0; + base_sibling_alternatives_[i] = 0; base_nodes_[i] = path_starts_[0]; } } @@ -667,6 +712,9 @@ void PathOperator::InitializePathStarts() { node_paths[node] = i; } for (int j = 0; j < base_nodes_.size(); ++j) { + // Always restart from first alternative. + base_alternatives_[j] = 0; + base_sibling_alternatives_[j] = 0; if (IsInactive(base_nodes_[j]) || node_paths[base_nodes_[j]] == -1) { // Base node was made inactive or was moved to a new path, reposition // the base node to the start of the path on which it was. @@ -748,6 +796,10 @@ void PathOperator::InitializeBaseNodes() { base_paths_[i] = base_paths_[i - 1]; } } + for (int i = 0; i < base_nodes_.size(); ++i) { + base_alternatives_[i] = 0; + base_sibling_alternatives_[i] = 0; + } just_started_ = true; } @@ -808,25 +860,6 @@ bool PathOperator::CheckChainValidity(int64 before_chain, int64 chain_end, return true; } -PathWithPreviousNodesOperator::PathWithPreviousNodesOperator( - const std::vector& vars, - const std::vector& secondary_vars, int number_of_base_nodes, - std::function start_empty_path_class) - : PathOperator(vars, secondary_vars, number_of_base_nodes, true, - std::move(start_empty_path_class)) { - int64 max_next = -1; - for (const IntVar* const var : vars) { - max_next = std::max(max_next, var->Max()); - } - prevs_.resize(max_next + 1, -1); -} - -void PathWithPreviousNodesOperator::OnNodeInitialization() { - for (int node_index = 0; node_index < number_of_nexts(); ++node_index) { - prevs_[Next(node_index)] = node_index; - } -} - // ----- 2Opt ----- // Reverses a sub-chain of a path. It is called 2Opt because it breaks @@ -842,7 +875,7 @@ class TwoOpt : public PathOperator { TwoOpt(const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class) - : PathOperator(vars, secondary_vars, 2, true, + : PathOperator(vars, secondary_vars, 2, true, true, std::move(start_empty_path_class)), last_base_(-1), last_(-1) {} @@ -916,7 +949,7 @@ class Relocate : public PathOperator { const std::vector& secondary_vars, const std::string& name, std::function start_empty_path_class, int64 chain_length = 1LL, bool single_path = false) - : PathOperator(vars, secondary_vars, 2, true, + : PathOperator(vars, secondary_vars, 2, true, false, std::move(start_empty_path_class)), chain_length_(chain_length), single_path_(single_path), @@ -951,16 +984,18 @@ class Relocate : public PathOperator { bool Relocate::MakeNeighbor() { DCHECK(!single_path_ || StartNode(0) == StartNode(1)); + const int64 destination = BaseNode(1); + DCHECK(!IsPathEnd(destination)); const int64 before_chain = BaseNode(0); int64 chain_end = before_chain; for (int i = 0; i < chain_length_; ++i) { - if (IsPathEnd(chain_end)) { + if (IsPathEnd(chain_end) || chain_end == destination) { return false; } chain_end = Next(chain_end); } - const int64 destination = BaseNode(1); - return MoveChain(before_chain, chain_end, destination); + return !IsPathEnd(chain_end) && + MoveChain(before_chain, chain_end, destination); } // ----- Exchange ----- @@ -978,7 +1013,7 @@ class Exchange : public PathOperator { Exchange(const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class) - : PathOperator(vars, secondary_vars, 2, true, + : PathOperator(vars, secondary_vars, 2, true, false, std::move(start_empty_path_class)) {} ~Exchange() override {} bool MakeNeighbor() override; @@ -988,20 +1023,13 @@ class Exchange : public PathOperator { bool Exchange::MakeNeighbor() { const int64 prev_node0 = BaseNode(0); - if (IsPathEnd(prev_node0)) return false; const int64 node0 = Next(prev_node0); + if (IsPathEnd(node0)) return false; const int64 prev_node1 = BaseNode(1); - if (IsPathEnd(prev_node1)) return false; const int64 node1 = Next(prev_node1); - if (node0 == prev_node1) { - return MoveChain(prev_node1, node1, prev_node0); - } else if (node1 == prev_node0) { - return MoveChain(prev_node0, node0, prev_node1); - } else { - return MoveChain(prev_node0, node0, prev_node1) && - MoveChain(node0, Next(node0), prev_node0); - } - return false; + if (IsPathEnd(node1)) return false; + const bool ok = MoveChain(prev_node0, node0, prev_node1); + return MoveChain(Prev(node1), node1, prev_node0) || ok; } // ----- Cross ----- @@ -1021,7 +1049,7 @@ class Cross : public PathOperator { Cross(const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class) - : PathOperator(vars, secondary_vars, 2, true, + : PathOperator(vars, secondary_vars, 2, true, true, std::move(start_empty_path_class)) {} ~Cross() override {} bool MakeNeighbor() override; @@ -1030,13 +1058,13 @@ class Cross : public PathOperator { }; bool Cross::MakeNeighbor() { - const int64 node0 = BaseNode(0); const int64 start0 = StartNode(0); - const int64 node1 = BaseNode(1); const int64 start1 = StartNode(1); - if (start1 == start0) { - return false; - } + if (start1 == start0) return false; + const int64 node0 = BaseNode(0); + if (node0 == start0) return false; + const int64 node1 = BaseNode(1); + if (node1 == start1) return false; if (!IsPathEnd(node0) && !IsPathEnd(node1)) { // If two paths are equivalent don't exchange them. if (PathClass(0) == PathClass(1) && IsPathEnd(Next(node0)) && @@ -1061,7 +1089,7 @@ class BaseInactiveNodeToPathOperator : public PathOperator { const std::vector& vars, const std::vector& secondary_vars, int number_of_base_nodes, std::function start_empty_path_class) - : PathOperator(vars, secondary_vars, number_of_base_nodes, false, + : PathOperator(vars, secondary_vars, number_of_base_nodes, false, false, std::move(start_empty_path_class)), inactive_node_(0) { // TODO(user): Activate skipping optimal paths. @@ -1146,11 +1174,9 @@ class RelocateAndMakeActiveOperator : public BaseInactiveNodeToPathOperator { ~RelocateAndMakeActiveOperator() override {} bool MakeNeighbor() override { const int64 before_node_to_move = BaseNode(1); - if (IsPathEnd(before_node_to_move)) { - return false; - } - return MoveChain(before_node_to_move, Next(before_node_to_move), - BaseNode(0)) && + const int64 node = Next(before_node_to_move); + return !IsPathEnd(node) && + MoveChain(before_node_to_move, node, BaseNode(0)) && MakeActive(GetInactiveNode(), before_node_to_move); } @@ -1182,12 +1208,10 @@ class MakeActiveAndRelocate : public BaseInactiveNodeToPathOperator { bool MakeActiveAndRelocate::MakeNeighbor() { const int64 before_chain = BaseNode(1); - if (IsPathEnd(before_chain)) { - return false; - } const int64 chain_end = Next(before_chain); const int64 destination = BaseNode(0); - return MoveChain(before_chain, chain_end, destination) && + return !IsPathEnd(chain_end) && + MoveChain(before_chain, chain_end, destination) && MakeActive(GetInactiveNode(), destination); } @@ -1204,14 +1228,11 @@ class MakeInactiveOperator : public PathOperator { MakeInactiveOperator(const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class) - : PathOperator(vars, secondary_vars, 1, true, + : PathOperator(vars, secondary_vars, 1, true, false, std::move(start_empty_path_class)) {} ~MakeInactiveOperator() override {} bool MakeNeighbor() override { const int64 base = BaseNode(0); - if (IsPathEnd(base)) { - return false; - } return MakeChainInactive(base, Next(base)); } @@ -1232,17 +1253,19 @@ class RelocateAndMakeInactiveOperator : public PathOperator { const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class) - : PathOperator(vars, secondary_vars, 2, true, + : PathOperator(vars, secondary_vars, 2, true, false, std::move(start_empty_path_class)) {} ~RelocateAndMakeInactiveOperator() override {} bool MakeNeighbor() override { const int64 destination = BaseNode(1); const int64 before_to_move = BaseNode(0); - if (IsPathEnd(destination) || IsPathEnd(before_to_move)) { + const int64 node_to_inactivate = Next(destination); + if (node_to_inactivate == before_to_move || IsPathEnd(node_to_inactivate) || + !MakeChainInactive(destination, node_to_inactivate)) { return false; } - return MakeChainInactive(destination, Next(destination)) && - MoveChain(before_to_move, Next(before_to_move), destination); + const int64 node = Next(before_to_move); + return !IsPathEnd(node) && MoveChain(before_to_move, node, destination); } std::string DebugString() const override { @@ -1264,7 +1287,7 @@ class MakeChainInactiveOperator : public PathOperator { MakeChainInactiveOperator(const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class) - : PathOperator(vars, secondary_vars, 2, true, + : PathOperator(vars, secondary_vars, 2, true, false, std::move(start_empty_path_class)) {} ~MakeChainInactiveOperator() override {} bool MakeNeighbor() override { @@ -1315,9 +1338,6 @@ class SwapActiveOperator : public BaseInactiveNodeToPathOperator { bool SwapActiveOperator::MakeNeighbor() { const int64 base = BaseNode(0); - if (IsPathEnd(base)) { - return false; - } return MakeChainInactive(base, Next(base)) && MakeActive(GetInactiveNode(), base); } @@ -1352,13 +1372,7 @@ class ExtendedSwapActiveOperator : public BaseInactiveNodeToPathOperator { bool ExtendedSwapActiveOperator::MakeNeighbor() { const int64 base0 = BaseNode(0); - if (IsPathEnd(base0)) { - return false; - } const int64 base1 = BaseNode(1); - if (IsPathEnd(base1)) { - return false; - } if (Next(base0) == base1) { return false; } @@ -1396,7 +1410,7 @@ class TSPOpt : public PathOperator { TSPOpt::TSPOpt(const std::vector& vars, const std::vector& secondary_vars, Solver::IndexEvaluator3 evaluator, int chain_length) - : PathOperator(vars, secondary_vars, 1, true, nullptr), + : PathOperator(vars, secondary_vars, 1, true, false, nullptr), hamiltonian_path_solver_(cost_), evaluator_(std::move(evaluator)), chain_length_(chain_length) {} @@ -1467,18 +1481,18 @@ class TSPLns : public PathOperator { hamiltonian_path_solver_; Solver::IndexEvaluator3 evaluator_; const int tsp_size_; - ACMRandom rand_; + std::mt19937 rand_; bool has_long_enough_paths_; }; TSPLns::TSPLns(const std::vector& vars, const std::vector& secondary_vars, Solver::IndexEvaluator3 evaluator, int tsp_size) - : PathOperator(vars, secondary_vars, 1, true, nullptr), + : PathOperator(vars, secondary_vars, 1, true, false, nullptr), hamiltonian_path_solver_(cost_), evaluator_(std::move(evaluator)), tsp_size_(tsp_size), - rand_(ACMRandom::HostnamePidTimeSeed()), + rand_(CpRandomSeed()), has_long_enough_paths_(true) { CHECK_GE(tsp_size_, 0); cost_.resize(tsp_size_); @@ -1500,9 +1514,6 @@ bool TSPLns::MakeOneNeighbor() { bool TSPLns::MakeNeighbor() { const int64 base_node = BaseNode(0); - if (IsPathEnd(base_node)) { - return false; - } std::vector nodes; for (int64 node = StartNode(0); !IsPathEnd(node); node = Next(node)) { nodes.push_back(node); @@ -1518,7 +1529,7 @@ bool TSPLns::MakeNeighbor() { breaks_set.insert(base_node); CHECK(!nodes.empty()); // Should have been caught earlier. while (breaks_set.size() < tsp_size_) { - const int64 one_break = nodes[rand_.Uniform(nodes.size())]; + const int64 one_break = nodes[absl::Uniform(rand_, 0, nodes.size())]; if (!gtl::ContainsKey(breaks_set, one_break)) { breaks_set.insert(one_break); } @@ -1688,7 +1699,7 @@ class LinKernighan : public PathOperator { LinKernighan::LinKernighan(const std::vector& vars, const std::vector& secondary_vars, const Solver::IndexEvaluator3& evaluator, bool topt) - : PathOperator(vars, secondary_vars, 1, true, nullptr), + : PathOperator(vars, secondary_vars, 1, true, false, nullptr), evaluator_(evaluator), neighbors_(evaluator, *this, kNeighbors), topt_(topt) {} @@ -1700,7 +1711,6 @@ void LinKernighan::OnNodeInitialization() { neighbors_.Initialize(); } bool LinKernighan::MakeNeighbor() { marked_.clear(); int64 node = BaseNode(0); - if (IsPathEnd(node)) return false; int64 path = Path(node); int64 base = node; int64 next = Next(node); @@ -1709,38 +1719,27 @@ bool LinKernighan::MakeNeighbor() { int64 gain = 0; marked_.insert(node); if (topt_) { // Try a 3opt first - if (InFromOut(node, next, &out, &gain)) { - marked_.insert(next); - marked_.insert(out); - const int64 node1 = out; - if (IsPathEnd(node1)) return false; - const int64 next1 = Next(node1); - if (IsPathEnd(next1)) return false; - if (InFromOut(node1, next1, &out, &gain)) { - marked_.insert(next1); - marked_.insert(out); - if (MoveChain(out, node1, node)) { - const int64 next_out = Next(out); - int64 in_cost = evaluator_(node, next_out, path); - int64 out_cost = evaluator_(out, next_out, path); - if (CapAdd(CapSub(gain, in_cost), out_cost) > 0) return true; - node = out; - if (IsPathEnd(node)) { - return false; - } - next = next_out; - if (IsPathEnd(next)) { - return false; - } - } else { - return false; - } - } else { - return false; - } - } else { + if (!InFromOut(node, next, &out, &gain)) return false; + marked_.insert(next); + marked_.insert(out); + const int64 node1 = out; + if (IsPathEnd(node1)) return false; + const int64 next1 = Next(node1); + if (IsPathEnd(next1)) return false; + if (!InFromOut(node1, next1, &out, &gain)) return false; + marked_.insert(next1); + marked_.insert(out); + if (!CheckChainValidity(out, node1, node) || !MoveChain(out, node1, node)) { return false; } + const int64 next_out = Next(out); + const int64 in_cost = evaluator_(node, next_out, path); + const int64 out_cost = evaluator_(out, next_out, path); + if (CapAdd(CapSub(gain, in_cost), out_cost) > 0) return true; + node = out; + if (IsPathEnd(node)) return false; + next = next_out; + if (IsPathEnd(next)) return false; } // Try 2opts while (InFromOut(node, next, &out, &gain)) { @@ -1804,7 +1803,8 @@ class PathLns : public PathOperator { PathLns(const std::vector& vars, const std::vector& secondary_vars, int number_of_chunks, int chunk_size, bool unactive_fragments) - : PathOperator(vars, secondary_vars, number_of_chunks, true, nullptr), + : PathOperator(vars, secondary_vars, number_of_chunks, true, true, + nullptr), number_of_chunks_(number_of_chunks), chunk_size_(chunk_size), unactive_fragments_(unactive_fragments) { @@ -2072,7 +2072,7 @@ class RandomCompoundOperator : public LocalSearchOperator { // TODO(user): define Self method. private: - ACMRandom rand_; + std::mt19937 rand_; const std::vector operators_; bool has_fragments_; }; @@ -2085,8 +2085,7 @@ void RandomCompoundOperator::Start(const Assignment* assignment) { RandomCompoundOperator::RandomCompoundOperator( std::vector operators) - : RandomCompoundOperator(std::move(operators), - ACMRandom::HostnamePidTimeSeed()) {} + : RandomCompoundOperator(std::move(operators), CpRandomSeed()) {} RandomCompoundOperator::RandomCompoundOperator( std::vector operators, int32 seed) diff --git a/ortools/constraint_solver/python/constraint_solver.i b/ortools/constraint_solver/python/constraint_solver.i index 4879b31842..e354a6fd2e 100644 --- a/ortools/constraint_solver/python/constraint_solver.i +++ b/ortools/constraint_solver/python/constraint_solver.i @@ -224,7 +224,7 @@ Constraint* PythonMethodName(int64 date) { #undef PRECEDENCE_CONSTRAINT #undef SCHEDULING_CONSTRAINT -// Use DebugString() for the native std::string conversion in python, for objects +// Use DebugString() for the native string conversion in python, for objects // that support it. %define PY_STRINGIFY_DEBUGSTRING(Class) %extend operations_research::Class { @@ -667,7 +667,7 @@ PY_STRINGIFY_DEBUGSTRING(Decision); solver->clear_fail_intercept(); // IMPORTANT: the type and message of the exception raised matter, // because they are caught by the python overrides of some CP classes. - // See the occurrences of the "PyExc_Exception" std::string below. + // See the occurrences of the "PyExc_Exception" string below. PyErr_SetString(PyExc_Exception, "CP Solver fail"); SWIG_fail; } diff --git a/ortools/constraint_solver/python/pywrapcp_util.h b/ortools/constraint_solver/python/pywrapcp_util.h index 840f10c38b..783020bcfb 100644 --- a/ortools/constraint_solver/python/pywrapcp_util.h +++ b/ortools/constraint_solver/python/pywrapcp_util.h @@ -22,6 +22,7 @@ #define OR_TOOLS_CONSTRAINT_SOLVER_PYTHON_PYWRAPCP_UTIL_H_ #include + #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" diff --git a/ortools/constraint_solver/routing.cc b/ortools/constraint_solver/routing.cc index 372da9418a..b4f4e4a5d8 100644 --- a/ortools/constraint_solver/routing.cc +++ b/ortools/constraint_solver/routing.cc @@ -4135,6 +4135,52 @@ void RoutingModel::CreateNeighborhoodOperators( ? arc_cost : CapSub(arc_cost, GetFixedCostOfVehicle(vehicle)); }; + GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters + ls_gci_parameters = { + /* is_sequential */ false, + /* farthest_seeds_ratio */ 0.0, + parameters.cheapest_insertion_ls_operator_neighbors_ratio(), + /* use_neighbors_ratio_for_initialization */ true}; + local_search_operators_[GLOBAL_CHEAPEST_INSERTION_PATH_LNS] = + solver_->RevAlloc(new FilteredHeuristicPathLNSOperator( + absl::make_unique( + this, + [this](int64 i, int64 j, int64 vehicle) { + return GetArcCostForVehicle(i, j, vehicle); + }, + [this](int64 i) { return UnperformedPenaltyOrValue(0, i); }, + GetOrCreateFeasibilityFilters(), ls_gci_parameters))); + + local_search_operators_[LOCAL_CHEAPEST_INSERTION_PATH_LNS] = + solver_->RevAlloc(new FilteredHeuristicPathLNSOperator( + absl::make_unique( + this, + [this](int64 i, int64 j, int64 vehicle) { + return GetArcCostForVehicle(i, j, vehicle); + }, + GetOrCreateFeasibilityFilters()))); + local_search_operators_[GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] = + solver_->RevAlloc(new FilteredHeuristicExpensiveChainLNSOperator( + absl::make_unique( + this, + [this](int64 i, int64 j, int64 vehicle) { + return GetArcCostForVehicle(i, j, vehicle); + }, + [this](int64 i) { return UnperformedPenaltyOrValue(0, i); }, + GetOrCreateFeasibilityFilters(), ls_gci_parameters), + parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(), + arc_cost_for_path_start)); + + local_search_operators_[LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] = + solver_->RevAlloc(new FilteredHeuristicExpensiveChainLNSOperator( + absl::make_unique( + this, + [this](int64 i, int64 j, int64 vehicle) { + return GetArcCostForVehicle(i, j, vehicle); + }, + GetOrCreateFeasibilityFilters()), + parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(), + arc_cost_for_path_start)); local_search_operators_[RELOCATE_EXPENSIVE_CHAIN] = solver_->RevAlloc(new RelocateExpensiveChain( nexts_, CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, @@ -4246,7 +4292,26 @@ LocalSearchOperator* RoutingModel::GetNeighborhoodOperators( } operator_groups.push_back(solver_->ConcatenateOperators(operators)); - // Second local search loop: Expensive LNS operators. + // Second local search loop: LNS-like operators. + operators.clear(); + if (vehicles() > 1) { + // NOTE: The following heuristic path LNS with a single vehicle are + // equivalent to using the heuristic as first solution strategy, so we only + // add these moves if we have at least 2 vehicles in the model. + CP_ROUTING_PUSH_OPERATOR(GLOBAL_CHEAPEST_INSERTION_PATH_LNS, + global_cheapest_insertion_path_lns, operators); + CP_ROUTING_PUSH_OPERATOR(LOCAL_CHEAPEST_INSERTION_PATH_LNS, + local_cheapest_insertion_path_lns, operators); + } + CP_ROUTING_PUSH_OPERATOR(GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS, + global_cheapest_insertion_expensive_chain_lns, + operators); + CP_ROUTING_PUSH_OPERATOR(LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS, + local_cheapest_insertion_expensive_chain_lns, + operators); + operator_groups.push_back(solver_->ConcatenateOperators(operators)); + + // Third local search loop: Expensive LNS operators. operators.clear(); if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH && local_search_metaheuristic != @@ -4324,22 +4389,8 @@ RoutingModel::GetOrCreateLocalSearchFilters() { filters_.push_back(MakeVehicleVarFilter(*this)); - // NOTE: We first sort the dimensions by increasing complexity of filtering: - // Dimensions with a global span cost coefficient and/or precedences will - // have a global LP created to filter feasibility and costs. - std::vector sorted_dimensions = dimensions_.get(); - std::sort(sorted_dimensions.begin(), sorted_dimensions.end(), - [](const RoutingDimension* d1, const RoutingDimension* d2) { - return (d1->global_span_cost_coefficient() <= - d2->global_span_cost_coefficient()) && - (d1->GetNodePrecedences().size() <= - d2->GetNodePrecedences().size()); - }); - for (const RoutingDimension* dimension : sorted_dimensions) { - const std::vector cumul_filters = - MakeCumulFilters(*dimension, /*filtering_objective*/ true); - filters_.insert(filters_.end(), cumul_filters.begin(), cumul_filters.end()); - } + AppendDimensionCumulFilters(GetDimensions(), /*filter_objective_cost*/ true, + &filters_); for (const RoutingDimension* dimension : dimensions_) { if (!dimension->HasBreakConstraints()) continue; @@ -4366,22 +4417,8 @@ RoutingModel::GetOrCreateFeasibilityFilters() { } feasibility_filters_.push_back(MakeVehicleVarFilter(*this)); - // NOTE: We sort the dimensions by decreasing complexity of filtering: - // Dimensions with precedences will have a global LP created to filter - // feasibility, so we want these filters to be called last. - std::vector sorted_dimensions = dimensions_.get(); - std::sort(sorted_dimensions.begin(), sorted_dimensions.end(), - [](const RoutingDimension* d1, const RoutingDimension* d2) { - return (d1->GetNodePrecedences().size() < - d2->GetNodePrecedences().size()); - }); - for (const RoutingDimension* const dimension : sorted_dimensions) { - const std::vector dimension_cumul_filters = - MakeCumulFilters(*dimension, /*filtering_objective*/ false); - feasibility_filters_.insert(feasibility_filters_.end(), - dimension_cumul_filters.begin(), - dimension_cumul_filters.end()); - } + AppendDimensionCumulFilters(GetDimensions(), /*filter_objective_cost*/ false, + &feasibility_filters_); for (const RoutingDimension* dimension : dimensions_) { if (dimension->HasBreakConstraints()) { @@ -4667,7 +4704,7 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders( gci_parameters = { /* is_sequential */ false, search_parameters.cheapest_insertion_farthest_seeds_ratio(), - search_parameters.cheapest_insertion_neighbors_ratio(), + search_parameters.cheapest_insertion_first_solution_neighbors_ratio(), /* use_neighbors_ratio_for_initialization */ false}; first_solution_filtered_decision_builders_ [FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION] = diff --git a/ortools/constraint_solver/routing.h b/ortools/constraint_solver/routing.h index ba639a47d3..58f6a3a417 100644 --- a/ortools/constraint_solver/routing.h +++ b/ortools/constraint_solver/routing.h @@ -194,10 +194,13 @@ namespace operations_research { class GlobalDimensionCumulOptimizer; -class IntVarFilteredDecisionBuilder; -class IntVarFilteredHeuristic; class LocalDimensionCumulOptimizer; class LocalSearchOperator; +#ifndef SWIG +class IntVarFilteredDecisionBuilder; +class IntVarFilteredHeuristic; +class IndexNeighborFinder; +#endif class RoutingDimension; #ifndef SWIG using util::ReverseArcListGraph; @@ -509,7 +512,7 @@ class RoutingModel { /// Outputs the names of all dimensions added to the routing engine. // TODO(user): rename. - std::vector<::std::string> GetAllDimensionNames() const; + std::vector GetAllDimensionNames() const; /// Returns all dimensions of the model. const std::vector& GetDimensions() const { return dimensions_.get(); @@ -553,8 +556,7 @@ class RoutingModel { DCHECK(dimension_name.empty() || HasDimension(dimension_name)); primary_constrained_dimension_ = dimension_name; } - /// Get the primary constrained dimension, or an empty std::string if it is - /// unset. + /// Get the primary constrained dimension, or an empty string if it is unset. const std::string& GetPrimaryConstrainedDimension() const { return primary_constrained_dimension_; } @@ -1291,6 +1293,10 @@ class RoutingModel { CROSS_EXCHANGE, TWO_OPT, OR_OPT, + GLOBAL_CHEAPEST_INSERTION_PATH_LNS, + LOCAL_CHEAPEST_INSERTION_PATH_LNS, + GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS, + LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS, RELOCATE_EXPENSIVE_CHAIN, LIN_KERNIGHAN, TSP_OPT, @@ -2090,6 +2096,42 @@ class RoutingDimension { const std::vector& forbidden_intervals() const { return forbidden_intervals_; } + /// Returns the smallest value outside the forbidden intervals of node 'index' + /// that is greater than or equal to a given 'min_value'. + int64 GetFirstPossibleGreaterOrEqualValueForNode(int64 index, + int64 min_value) const { + DCHECK_LT(index, forbidden_intervals_.size()); + const SortedDisjointIntervalList& forbidden_intervals = + forbidden_intervals_[index]; + const auto first_forbidden_interval_it = + forbidden_intervals.FirstIntervalGreaterOrEqual(min_value); + if (first_forbidden_interval_it != forbidden_intervals.end() && + min_value >= first_forbidden_interval_it->start) { + /// min_value is in a forbidden interval. + return CapAdd(first_forbidden_interval_it->end, 1); + } + /// min_value is not forbidden. + return min_value; + } + /// Returns the largest value outside the forbidden intervals of node 'index' + /// that is less than or equal to a given 'max_value'. + /// NOTE: If this method is called with a max_value lower than the node's + /// cumul min, it will return -1. + int64 GetLastPossibleLessOrEqualValueForNode(int64 index, + int64 max_value) const { + DCHECK_LT(index, forbidden_intervals_.size()); + const SortedDisjointIntervalList& forbidden_intervals = + forbidden_intervals_[index]; + const auto last_forbidden_interval_it = + forbidden_intervals.LastIntervalLessOrEqual(max_value); + if (last_forbidden_interval_it != forbidden_intervals.end() && + max_value <= last_forbidden_interval_it->end) { + /// max_value is in a forbidden interval. + return CapSub(last_forbidden_interval_it->start, 1); + } + /// max_value is not forbidden. + return max_value; + } /// Returns the capacities for all vehicles. const std::vector& vehicle_capacities() const { return vehicle_capacities_; @@ -2552,19 +2594,12 @@ class IntVarFilteredDecisionBuilder : public DecisionBuilder { }; /// Generic filter-based heuristic applied to IntVars. -// TODO(user): Remove one level of indirection by getting rid of -// IntVarFilteredHeuristic and putting everything in the -// RoutingFilteredHeuristic? -// In this case IntVarFilteredDecisionBuilder would also have to be renamed to -// RoutingFilteredDecisionBuilder as it would no longer be generic. -// TODO(user): Remove the inheritance from BaseObject as it's no longer -// needed. -class IntVarFilteredHeuristic : public BaseObject { +class IntVarFilteredHeuristic { public: IntVarFilteredHeuristic(Solver* solver, const std::vector& vars, const std::vector& filters); - ~IntVarFilteredHeuristic() override {} + virtual ~IntVarFilteredHeuristic() {} /// Builds a solution. Returns the resulting assignment if a solution was /// found, and nullptr otherwise. @@ -2575,9 +2610,11 @@ class IntVarFilteredHeuristic : public BaseObject { int64 number_of_decisions() const { return number_of_decisions_; } int64 number_of_rejects() const { return number_of_rejects_; } - std::string DebugString() const override { return "IntVarFilteredHeuristic"; } + virtual std::string DebugString() const { return "IntVarFilteredHeuristic"; } protected: + /// Resets the data members for a new solution. + void ResetSolution(); /// Virtual method to initialize the solution. virtual bool InitializeSolution() { return true; } /// Virtual method to redefine how to build a solution. @@ -2613,16 +2650,17 @@ class IntVarFilteredHeuristic : public BaseObject { int Size() const { return vars_.size(); } /// Returns the variable of index 'index'. IntVar* Var(int64 index) const { return vars_[index]; } - - private: /// Synchronizes filters with an assignment (the current solution). void SynchronizeFilters(); + + Assignment* const assignment_; + + private: /// Checks if filters accept a given modification to the current solution /// (represented by delta). bool FilterAccept(); const std::vector vars_; - Assignment* const assignment_; Assignment* const delta_; std::vector delta_indices_; std::vector is_in_delta_; @@ -2639,6 +2677,9 @@ class RoutingFilteredHeuristic : public IntVarFilteredHeuristic { RoutingFilteredHeuristic(RoutingModel* model, const std::vector& filters); ~RoutingFilteredHeuristic() override {} + /// Builds a solution starting from the routes formed by the next accessor. + const Assignment* BuildSolutionFromRoutes( + const std::function& next_accessor); RoutingModel* model() const { return model_; } /// Returns the end of the start chain of vehicle, int GetStartChainEnd(int vehicle) const { return start_chain_ends_[vehicle]; } @@ -3019,6 +3060,8 @@ class LocalCheapestInsertionFilteredHeuristic void ComputeEvaluatorSortedPositionsOnRouteAfter( int64 node, int64 start, int64 next_after_start, std::vector* sorted_positions); + + std::vector> start_end_distances_per_node_; }; /// Filtered-base decision builder based on the addition heuristic, extending @@ -3132,7 +3175,8 @@ class SavingsFilteredHeuristic : public RoutingFilteredHeuristic { double arc_coefficient = 1.0; }; - SavingsFilteredHeuristic(RoutingModel* model, RoutingIndexManager* manager, + SavingsFilteredHeuristic(RoutingModel* model, + const RoutingIndexManager* manager, SavingsParameters parameters, const std::vector& filters); ~SavingsFilteredHeuristic() override; @@ -3230,7 +3274,7 @@ class SavingsFilteredHeuristic : public RoutingFilteredHeuristic { /// memory usage specified by the savings_params_. int64 MaxNumNeighborsPerNode(int num_vehicle_types) const; - RoutingIndexManager* const manager_; + const RoutingIndexManager* const manager_; const SavingsParameters savings_params_; int64 size_squared_; @@ -3240,7 +3284,7 @@ class SavingsFilteredHeuristic : public RoutingFilteredHeuristic { class SequentialSavingsFilteredHeuristic : public SavingsFilteredHeuristic { public: SequentialSavingsFilteredHeuristic( - RoutingModel* model, RoutingIndexManager* manager, + RoutingModel* model, const RoutingIndexManager* manager, SavingsParameters parameters, const std::vector& filters) : SavingsFilteredHeuristic(model, manager, parameters, filters) {} @@ -3261,7 +3305,7 @@ class SequentialSavingsFilteredHeuristic : public SavingsFilteredHeuristic { class ParallelSavingsFilteredHeuristic : public SavingsFilteredHeuristic { public: ParallelSavingsFilteredHeuristic( - RoutingModel* model, RoutingIndexManager* manager, + RoutingModel* model, const RoutingIndexManager* manager, SavingsParameters parameters, const std::vector& filters) : SavingsFilteredHeuristic(model, manager, parameters, filters) {} @@ -3434,8 +3478,9 @@ IntVarLocalSearchFilter* MakeVehicleAmortizedCostFilter( const RoutingModel& routing_model); IntVarLocalSearchFilter* MakeTypeRegulationsFilter( const RoutingModel& routing_model); -std::vector MakeCumulFilters( - const RoutingDimension& dimension, bool filter_objective_cost); +void AppendDimensionCumulFilters( + const std::vector& dimensions, + bool filter_objective_cost, std::vector* filters); IntVarLocalSearchFilter* MakePathCumulFilter(const RoutingDimension& dimension, bool propagate_own_objective_value, bool filter_objective_cost); diff --git a/ortools/constraint_solver/routing_flags.cc b/ortools/constraint_solver/routing_flags.cc index 874c7d0c00..3031e7eb68 100644 --- a/ortools/constraint_solver/routing_flags.cc +++ b/ortools/constraint_solver/routing_flags.cc @@ -97,9 +97,9 @@ DEFINE_double(savings_arc_coefficient, 1.0, DEFINE_double(cheapest_insertion_farthest_seeds_ratio, 0, "Ratio of available vehicles in the model on which farthest " "nodes of the model are inserted as seeds."); -DEFINE_double(cheapest_insertion_neighbors_ratio, 1.0, +DEFINE_double(cheapest_insertion_first_solution_neighbors_ratio, 1.0, "Ratio of nodes considered as neighbors in the " - "GlobalCheapestInsertion heuristic."); + "GlobalCheapestInsertion first solution heuristic."); DEFINE_bool(routing_dfs, false, "Routing: use a complete depth-first search."); DEFINE_double(routing_optimization_step, 0.0, "Optimization step."); DEFINE_int32(routing_number_of_solutions_to_collect, 1, @@ -165,8 +165,8 @@ void SetFirstSolutionStrategyFromFlags(RoutingSearchParameters* parameters) { parameters->set_savings_arc_coefficient(FLAGS_savings_arc_coefficient); parameters->set_cheapest_insertion_farthest_seeds_ratio( FLAGS_cheapest_insertion_farthest_seeds_ratio); - parameters->set_cheapest_insertion_neighbors_ratio( - FLAGS_cheapest_insertion_neighbors_ratio); + parameters->set_cheapest_insertion_first_solution_neighbors_ratio( + FLAGS_cheapest_insertion_first_solution_neighbors_ratio); } void SetLocalSearchMetaheuristicFromFlags(RoutingSearchParameters* parameters) { @@ -195,6 +195,7 @@ OptionalBoolean ToOptionalBoolean(bool x) { return x ? BOOL_TRUE : BOOL_FALSE; } void AddLocalSearchNeighborhoodOperatorsFromFlags( RoutingSearchParameters* parameters) { CHECK(parameters != nullptr); + parameters->set_cheapest_insertion_ls_operator_neighbors_ratio(1.0); RoutingSearchParameters::LocalSearchNeighborhoodOperators* const local_search_operators = parameters->mutable_local_search_operators(); @@ -206,6 +207,12 @@ void AddLocalSearchNeighborhoodOperatorsFromFlags( local_search_operators->set_use_relocate_and_make_active(BOOL_FALSE); local_search_operators->set_use_node_pair_swap_active(BOOL_FALSE); local_search_operators->set_use_cross_exchange(BOOL_FALSE); + local_search_operators->set_use_global_cheapest_insertion_path_lns(BOOL_TRUE); + local_search_operators->set_use_local_cheapest_insertion_path_lns(BOOL_TRUE); + local_search_operators->set_use_global_cheapest_insertion_expensive_chain_lns( + BOOL_FALSE); + local_search_operators->set_use_local_cheapest_insertion_expensive_chain_lns( + BOOL_FALSE); local_search_operators->set_use_relocate( ToOptionalBoolean(!FLAGS_routing_no_relocate)); @@ -278,6 +285,7 @@ void SetMiscellaneousParametersFromFlags(RoutingSearchParameters* parameters) { parameters->set_log_cost_scaling_factor(1.0); parameters->set_relocate_expensive_chain_num_arcs_to_consider( FLAGS_routing_relocate_expensive_chain_num_arcs_to_consider); + parameters->set_heuristic_expensive_chain_lns_num_arcs_to_consider(4); } RoutingSearchParameters BuildSearchParametersFromFlags() { diff --git a/ortools/constraint_solver/routing_flags.h b/ortools/constraint_solver/routing_flags.h index 80d2575034..bf08bbdc73 100644 --- a/ortools/constraint_solver/routing_flags.h +++ b/ortools/constraint_solver/routing_flags.h @@ -57,7 +57,7 @@ DECLARE_double(savings_neighbors_ratio); DECLARE_bool(savings_add_reverse_arcs); DECLARE_double(savings_arc_coefficient); DECLARE_double(cheapest_insertion_farthest_seeds_ratio); -DECLARE_double(cheapest_insertion_neighbors_ratio); +DECLARE_double(cheapest_insertion_first_solution_neighbors_ratio); DECLARE_bool(routing_dfs); DECLARE_double(routing_optimization_step); DECLARE_int32(routing_number_of_solutions_to_collect); diff --git a/ortools/constraint_solver/routing_lp_scheduling.cc b/ortools/constraint_solver/routing_lp_scheduling.cc index a1fe7682d5..ea75b0f848 100644 --- a/ortools/constraint_solver/routing_lp_scheduling.cc +++ b/ortools/constraint_solver/routing_lp_scheduling.cc @@ -60,24 +60,54 @@ bool SetVariableBounds(glop::LinearProgram* linear_program, return false; } -bool GetCumulBoundsWithOffset(const IntVar& cumul_var, int64 cumul_offset, +bool GetCumulBoundsWithOffset(const RoutingDimension& dimension, + int64 node_index, int64 cumul_offset, int64* lower_bound, int64* upper_bound) { DCHECK(lower_bound != nullptr); DCHECK(upper_bound != nullptr); - *lower_bound = std::max(0, CapSub(cumul_var.Min(), cumul_offset)); + + const IntVar& cumul_var = *dimension.CumulVar(node_index); *upper_bound = cumul_var.Max(); - if (*upper_bound < kint64max) { - *upper_bound = CapSub(*upper_bound, cumul_offset); - if (*upper_bound < *lower_bound) { - // The cumul's upper bound is less than its lower bound. - // NOTE: As the lower bound is greater than 0 (using std::max), this - // includes cases where the upper bound is less than the offset. - return false; - } + if (*upper_bound < cumul_offset) { + return false; } + + const int64 first_after_offset = + std::max(dimension.GetFirstPossibleGreaterOrEqualValueForNode( + node_index, cumul_offset), + cumul_var.Min()); + DCHECK_LT(first_after_offset, kint64max); + *lower_bound = CapSub(first_after_offset, cumul_offset); + DCHECK_GE(*lower_bound, 0); + + if (*upper_bound == kint64max) { + return true; + } + *upper_bound = CapSub(*upper_bound, cumul_offset); + DCHECK_GE(*upper_bound, *lower_bound); return true; } +int64 GetFirstPossibleValueForCumulWithOffset(const RoutingDimension& dimension, + int64 node_index, + int64 lower_bound_without_offset, + int64 cumul_offset) { + return CapSub( + dimension.GetFirstPossibleGreaterOrEqualValueForNode( + node_index, CapAdd(lower_bound_without_offset, cumul_offset)), + cumul_offset); +} + +int64 GetLastPossibleValueForCumulWithOffset(const RoutingDimension& dimension, + int64 node_index, + int64 upper_bound_without_offset, + int64 cumul_offset) { + return CapSub( + dimension.GetLastPossibleLessOrEqualValueForNode( + node_index, CapAdd(upper_bound_without_offset, cumul_offset)), + cumul_offset); +} + // Finds the pickup/delivery pairs of nodes on a given vehicle's route. // Returns the vector of visited pair indices, and stores the corresponding // pickup/delivery indices in visited_pickup_delivery_indices_for_pair_. @@ -238,8 +268,8 @@ bool CumulBoundsPropagator::InitializeArcsAndBounds( int node = model->Start(vehicle); while (true) { int64 cumul_lb, cumul_ub; - if (!GetCumulBoundsWithOffset(*dimension_.CumulVar(node), cumul_offset, - &cumul_lb, &cumul_ub)) { + if (!GetCumulBoundsWithOffset(dimension_, node, cumul_offset, &cumul_lb, + &cumul_ub)) { return false; } lower_bounds[PositiveNode(node)] = cumul_lb; @@ -315,11 +345,23 @@ bool CumulBoundsPropagator::InitializeArcsAndBounds( } bool CumulBoundsPropagator::UpdateCurrentLowerBoundOfNode(int node, - int64 new_lb) { - propagated_bounds_[node] = new_lb; + int64 new_lb, + int64 offset) { + const int cumul_var_index = node / 2; + + if (node == PositiveNode(cumul_var_index)) { + // new_lb is a lower bound of the cumul of variable 'cumul_var_index'. + propagated_bounds_[node] = GetFirstPossibleValueForCumulWithOffset( + dimension_, cumul_var_index, new_lb, offset); + } else { + // -new_lb is an upper bound of the cumul of variable 'cumul_var_index'. + const int64 new_ub = CapSub(0, new_lb); + propagated_bounds_[node] = + CapSub(0, GetLastPossibleValueForCumulWithOffset( + dimension_, cumul_var_index, new_ub, offset)); + } // Test that the lower/upper bounds do not cross each other. - const int cumul_var_index = node / 2; const int64 cumul_lower_bound = propagated_bounds_[PositiveNode(cumul_var_index)]; @@ -385,7 +427,7 @@ bool CumulBoundsPropagator::PropagateCumulBounds( // node. continue; } - if (!UpdateCurrentLowerBoundOfNode(head_node, induced_lb) || + if (!UpdateCurrentLowerBoundOfNode(head_node, induced_lb, cumul_offset) || !DisassembleSubtree(head_node, node)) { // The new lower bound is infeasible, or a positive cycle was detected // in the precedence graph by DisassembleSubtree(). @@ -626,8 +668,8 @@ bool DimensionCumulOptimizerCore::ComputeRouteCumulBounds( // Extract cumul min/max and fixed transits from CP. for (int pos = 0; pos < route_size; ++pos) { - if (!GetCumulBoundsWithOffset(*dimension_->CumulVar(route[pos]), - cumul_offset, ¤t_route_min_cumuls_[pos], + if (!GetCumulBoundsWithOffset(*dimension_, route[pos], cumul_offset, + ¤t_route_min_cumuls_[pos], ¤t_route_max_cumuls_[pos])) { return false; } @@ -642,6 +684,11 @@ bool DimensionCumulOptimizerCore::ComputeRouteCumulBounds( CapAdd( CapAdd(current_route_min_cumuls_[pos - 1], fixed_transits[pos - 1]), slack_min)); + current_route_min_cumuls_[pos] = GetFirstPossibleValueForCumulWithOffset( + *dimension_, route[pos], current_route_min_cumuls_[pos], cumul_offset); + if (current_route_min_cumuls_[pos] > current_route_max_cumuls_[pos]) { + return false; + } } for (int pos = route_size - 2; pos >= 0; --pos) { @@ -654,6 +701,12 @@ bool DimensionCumulOptimizerCore::ComputeRouteCumulBounds( CapSub( CapSub(current_route_max_cumuls_[pos + 1], fixed_transits[pos]), slack_min)); + current_route_max_cumuls_[pos] = GetLastPossibleValueForCumulWithOffset( + *dimension_, route[pos], current_route_max_cumuls_[pos], + cumul_offset); + if (current_route_max_cumuls_[pos] < current_route_min_cumuls_[pos]) { + return false; + } } } return true; diff --git a/ortools/constraint_solver/routing_lp_scheduling.h b/ortools/constraint_solver/routing_lp_scheduling.h index b36b6c6b02..da7bda0a15 100644 --- a/ortools/constraint_solver/routing_lp_scheduling.h +++ b/ortools/constraint_solver/routing_lp_scheduling.h @@ -80,7 +80,7 @@ class CumulBoundsPropagator { bool InitializeArcsAndBounds(const std::function& next_accessor, int64 cumul_offset); - bool UpdateCurrentLowerBoundOfNode(int node, int64 new_lb); + bool UpdateCurrentLowerBoundOfNode(int node, int64 new_lb, int64 offset); bool DisassembleSubtree(int source, int target); diff --git a/ortools/constraint_solver/routing_neighborhoods.cc b/ortools/constraint_solver/routing_neighborhoods.cc index c785fc6c5b..c207c72b6e 100644 --- a/ortools/constraint_solver/routing_neighborhoods.cc +++ b/ortools/constraint_solver/routing_neighborhoods.cc @@ -14,6 +14,9 @@ #include "ortools/constraint_solver/routing_neighborhoods.h" #include +#include + +#include "absl/container/flat_hash_set.h" namespace operations_research { @@ -22,23 +25,20 @@ MakeRelocateNeighborsOperator::MakeRelocateNeighborsOperator( const std::vector& secondary_vars, std::function start_empty_path_class, RoutingTransitCallback2 arc_evaluator) - : PathWithPreviousNodesOperator(vars, secondary_vars, 2, - std::move(start_empty_path_class)), + : PathOperator(vars, secondary_vars, 2, true, false, + std::move(start_empty_path_class)), arc_evaluator_(std::move(arc_evaluator)) {} bool MakeRelocateNeighborsOperator::MakeNeighbor() { const int64 before_chain = BaseNode(0); - if (IsPathEnd(before_chain)) { - return false; - } int64 chain_end = Next(before_chain); - if (IsPathEnd(chain_end)) { - return false; - } + if (IsPathEnd(chain_end)) return false; const int64 destination = BaseNode(1); + if (chain_end == destination) return false; const int64 max_arc_value = arc_evaluator_(destination, chain_end); int64 next = Next(chain_end); while (!IsPathEnd(next) && arc_evaluator_(chain_end, next) <= max_arc_value) { + if (next == destination) return false; chain_end = next; next = Next(chain_end); } @@ -95,7 +95,7 @@ MakePairActiveOperator::MakePairActiveOperator( const std::vector& secondary_vars, std::function start_empty_path_class, const RoutingIndexPairs& pairs) - : PathOperator(vars, secondary_vars, 2, false, + : PathOperator(vars, secondary_vars, 2, false, true, std::move(start_empty_path_class)), inactive_pair_(0), inactive_pair_first_index_(0), @@ -172,24 +172,18 @@ MakePairInactiveOperator::MakePairInactiveOperator( const std::vector& secondary_vars, std::function start_empty_path_class, const RoutingIndexPairs& index_pairs) - : PathWithPreviousNodesOperator(vars, secondary_vars, 1, - std::move(start_empty_path_class)) { + : PathOperator(vars, secondary_vars, 1, true, false, + std::move(start_empty_path_class)) { AddPairAlternativeSets(index_pairs); } bool MakePairInactiveOperator::MakeNeighbor() { const int64 base = BaseNode(0); - if (IsPathEnd(base)) { - return false; - } const int64 first_index = Next(base); const int64 second_index = GetActiveAlternativeSibling(first_index); if (second_index < 0) { return false; } - if (Next(first_index) == second_index) { - return MakeChainInactive(base, second_index); - } return MakeChainInactive(base, first_index) && MakeChainInactive(Prev(second_index), second_index); } @@ -199,8 +193,8 @@ PairRelocateOperator::PairRelocateOperator( const std::vector& secondary_vars, std::function start_empty_path_class, const RoutingIndexPairs& index_pairs) - : PathWithPreviousNodesOperator(vars, secondary_vars, 3, - std::move(start_empty_path_class)) { + : PathOperator(vars, secondary_vars, 3, true, false, + std::move(start_empty_path_class)) { AddPairAlternativeSets(index_pairs); } @@ -212,22 +206,21 @@ bool PairRelocateOperator::MakeNeighbor() { } int64 first_prev = Prev(first_pair_node); const int second_pair_node = GetActiveAlternativeSibling(first_pair_node); - if (second_pair_node < 0) { - return false; - } - if (IsPathStart(second_pair_node)) { + if (second_pair_node < 0 || IsPathEnd(second_pair_node) || + IsPathStart(second_pair_node)) { return false; } const int64 second_prev = Prev(second_pair_node); - if (BaseNode(kPairFirstNodeDestination) == second_pair_node) { + const int64 first_node_destination = BaseNode(kPairFirstNodeDestination); + if (first_node_destination == second_pair_node) { // The second_pair_node -> first_pair_node link is forbidden. return false; } - if (second_prev == first_pair_node && - BaseNode(kPairFirstNodeDestination) == first_prev && - BaseNode(kPairSecondNodeDestination) == first_prev) { + const int64 second_node_destination = BaseNode(kPairSecondNodeDestination); + if (second_prev == first_pair_node && first_node_destination == first_prev && + second_node_destination == first_prev) { // If the current sequence is first_prev -> first_pair_node -> // second_pair_node, and both 1st and 2nd are moved both to prev, the result // of the move will be first_prev -> first_pair_node -> second_pair_node, @@ -235,32 +228,23 @@ bool PairRelocateOperator::MakeNeighbor() { return false; } - // Relocation is a success if at least one of the nodes moved and all the - // moves are successful. - bool moved = false; - - // Do not allow to move second_pair_node to its current prev. - if (second_prev != BaseNode(kPairSecondNodeDestination)) { - if (!MoveChain(second_prev, second_pair_node, - BaseNode(kPairSecondNodeDestination))) { - return false; - } - if (BaseNode(kPairSecondNodeDestination) == first_prev) { - first_prev = second_pair_node; - } - moved = true; + // Relocation is successful if both moves are feasible and at least one of the + // nodes moves. + if (second_pair_node == second_node_destination || + first_pair_node == first_node_destination) { + return false; } - - // Do not allow to move first_pair_node to its current prev. - if (first_prev != BaseNode(kPairFirstNodeDestination)) { - if (!MoveChain(first_prev, first_pair_node, - BaseNode(kPairFirstNodeDestination))) { - return false; - } - moved = true; - } - - return moved; + const bool moved_second_pair_node = + MoveChain(second_prev, second_pair_node, second_node_destination); + // Explictly calling Prev as second_pair_node might have been moved before + // first_pair_node. + const bool moved_first_pair_node = + MoveChain(Prev(first_pair_node), first_pair_node, first_node_destination); + // Swapping alternatives in. + SwapActiveAndInactive(second_pair_node, + BaseSiblingAlternativeNode(kPairFirstNode)); + SwapActiveAndInactive(first_pair_node, BaseAlternativeNode(kPairFirstNode)); + return moved_first_pair_node || moved_second_pair_node; } int64 PairRelocateOperator::GetBaseNodeRestartPosition(int base_index) { @@ -278,38 +262,27 @@ LightPairRelocateOperator::LightPairRelocateOperator( const std::vector& secondary_vars, std::function start_empty_path_class, const RoutingIndexPairs& index_pairs) - : PathWithPreviousNodesOperator(vars, secondary_vars, 2, - std::move(start_empty_path_class)) { + : PathOperator(vars, secondary_vars, 2, true, false, + std::move(start_empty_path_class)) { AddPairAlternativeSets(index_pairs); } bool LightPairRelocateOperator::MakeNeighbor() { const int64 prev1 = BaseNode(0); - if (IsPathEnd(prev1)) return false; const int64 node1 = Next(prev1); if (IsPathEnd(node1)) return false; const int64 sibling1 = GetActiveAlternativeSibling(node1); if (sibling1 == -1) return false; const int64 node2 = BaseNode(1); - if (node2 == sibling1 || IsPathEnd(node2)) { - return false; - } + if (node2 == sibling1) return false; const int64 sibling2 = GetActiveAlternativeSibling(node2); if (sibling2 == -1) return false; - int64 prev_sibling1 = Prev(sibling1); - bool move_node1 = true; - if (prev_sibling1 == node1) { - if (node2 != prev1) { - prev_sibling1 = prev1; - } else { - move_node1 = false; - } - } else if (prev_sibling1 == node2) { - prev_sibling1 = node1; - } - return (!move_node1 || MoveChain(prev1, node1, node2)) && - (sibling2 == prev_sibling1 || - MoveChain(prev_sibling1, sibling1, sibling2)); + // Note: MoveChain will return false if it is a no-op (moving the chain to its + // current position). However we want to accept the move if at least node1 or + // sibling1 gets moved to a new position. Therefore we want to be sure both + // MoveChains are called and at least one succeeds. + const bool ok = MoveChain(prev1, node1, node2); + return MoveChain(Prev(sibling1), sibling1, sibling2) || ok; } PairExchangeOperator::PairExchangeOperator( @@ -317,8 +290,8 @@ PairExchangeOperator::PairExchangeOperator( const std::vector& secondary_vars, std::function start_empty_path_class, const RoutingIndexPairs& index_pairs) - : PathWithPreviousNodesOperator(vars, secondary_vars, 2, - std::move(start_empty_path_class)) { + : PathOperator(vars, secondary_vars, 2, true, true, + std::move(start_empty_path_class)) { AddPairAlternativeSets(index_pairs); } @@ -366,6 +339,11 @@ bool PairExchangeOperator::MakeNeighbor() { status = MoveChain(sibling_prev1, sibling1, sibling2) && MoveChain(sibling_prev2, sibling2, sibling_prev1); } + // Swapping alternatives in. + SwapActiveAndInactive(sibling1, BaseSiblingAlternativeNode(0)); + SwapActiveAndInactive(node1, BaseAlternativeNode(0)); + SwapActiveAndInactive(sibling2, BaseSiblingAlternativeNode(1)); + SwapActiveAndInactive(node2, BaseAlternativeNode(1)); return status; } @@ -384,8 +362,8 @@ PairExchangeRelocateOperator::PairExchangeRelocateOperator( const std::vector& secondary_vars, std::function start_empty_path_class, const RoutingIndexPairs& index_pairs) - : PathWithPreviousNodesOperator(vars, secondary_vars, 6, - std::move(start_empty_path_class)) { + : PathOperator(vars, secondary_vars, 6, true, false, + std::move(start_empty_path_class)) { AddPairAlternativeSets(index_pairs); } @@ -630,8 +608,8 @@ IndexPairSwapActiveOperator::IndexPairSwapActiveOperator( const std::vector& secondary_vars, std::function start_empty_path_class, const RoutingIndexPairs& index_pairs) - : PathWithPreviousNodesOperator(vars, secondary_vars, 1, - std::move(start_empty_path_class)), + : PathOperator(vars, secondary_vars, 1, true, false, + std::move(start_empty_path_class)), inactive_node_(0) { AddPairAlternativeSets(index_pairs); } @@ -652,9 +630,6 @@ bool IndexPairSwapActiveOperator::MakeNextNeighbor(Assignment* delta, bool IndexPairSwapActiveOperator::MakeNeighbor() { const int64 base = BaseNode(0); - if (IsPathEnd(base)) { - return false; - } const int64 next = Next(base); const int64 other = GetActiveAlternativeSibling(next); if (other != -1) { @@ -665,7 +640,7 @@ bool IndexPairSwapActiveOperator::MakeNeighbor() { } void IndexPairSwapActiveOperator::OnNodeInitialization() { - PathWithPreviousNodesOperator::OnNodeInitialization(); + PathOperator::OnNodeInitialization(); for (int i = 0; i < Size(); ++i) { if (IsInactive(i)) { inactive_node_ = i; @@ -675,19 +650,369 @@ void IndexPairSwapActiveOperator::OnNodeInitialization() { inactive_node_ = Size(); } +FilteredHeuristicPathLNSOperator::FilteredHeuristicPathLNSOperator( + std::unique_ptr heuristic) + : IntVarLocalSearchOperator(heuristic->model()->Nexts()), + heuristic_(std::move(heuristic)), + model_(*heuristic_->model()), + consider_vehicle_vars_(!model_.CostsAreHomogeneousAcrossVehicles()), + current_route_(0), + last_route_(0), + just_started_(false) { + if (consider_vehicle_vars_) { + AddVars(model_.VehicleVars()); + } +} + +void FilteredHeuristicPathLNSOperator::OnStart() { + // NOTE: We set last_route_ to current_route_ here to make sure all routes + // are scanned in IncrementCurrentRouteToNextNonEmpty(). + last_route_ = current_route_; + if (CurrentRouteIsEmpty()) { + IncrementCurrentRouteToNextNonEmpty(); + } + just_started_ = true; +} + +bool FilteredHeuristicPathLNSOperator::MakeOneNeighbor() { + while (IncrementRoute()) { + // NOTE: No need to call RevertChanges() here as + // DestroyRouteAndReinsertNodes() will always return true if any change was + // made. + if (DestroyRouteAndReinsertNodes()) { + return true; + } + } + return false; +} + +bool FilteredHeuristicPathLNSOperator::IncrementRoute() { + if (just_started_) { + just_started_ = false; + return !CurrentRouteIsEmpty(); + } + IncrementCurrentRouteToNextNonEmpty(); + return current_route_ != last_route_; +} + +bool FilteredHeuristicPathLNSOperator::CurrentRouteIsEmpty() const { + return model_.IsEnd(OldValue(model_.Start(current_route_))); +} + +void FilteredHeuristicPathLNSOperator::IncrementCurrentRouteToNextNonEmpty() { + const int num_routes = model_.vehicles(); + do { + ++current_route_ %= num_routes; + if (current_route_ == last_route_) { + // All routes have been scanned. + return; + } + } while (CurrentRouteIsEmpty()); +} + +bool FilteredHeuristicPathLNSOperator::DestroyRouteAndReinsertNodes() { + const int64 start_node = model_.Start(current_route_); + const int64 end_node = model_.End(current_route_); + + const Assignment* const result_assignment = + heuristic_->BuildSolutionFromRoutes( + [this, start_node, end_node](int64 node) { + if (node == start_node) return end_node; + return Value(node); + }); + + if (result_assignment == nullptr) { + return false; + } + + bool has_change = false; + std::vector node_performed(model_.Size(), false); + const std::vector& elements = + result_assignment->IntVarContainer().elements(); + for (int vehicle = 0; vehicle < model_.vehicles(); vehicle++) { + int64 node_index = model_.Start(vehicle); + while (!model_.IsEnd(node_index)) { + // NOTE: When building the solution in the heuristic, Next vars are added + // to the assignment at the position corresponding to their index. + const IntVarElement& node_element = elements[node_index]; + DCHECK_EQ(node_element.Var(), model_.NextVar(node_index)); + + const int64 new_node_value = node_element.Value(); + DCHECK_NE(new_node_value, node_index); + node_performed[node_index] = true; + + const int64 vehicle_var_index = VehicleVarIndex(node_index); + if (OldValue(node_index) != new_node_value || + (consider_vehicle_vars_ && OldValue(vehicle_var_index) != vehicle)) { + has_change = true; + SetValue(node_index, new_node_value); + if (consider_vehicle_vars_) { + SetValue(vehicle_var_index, vehicle); + } + } + + node_index = new_node_value; + } + } + for (int64 node = 0; node < model_.Size(); node++) { + if (node_performed[node]) continue; + const IntVarElement& node_element = elements[node]; + DCHECK_EQ(node_element.Var(), model_.NextVar(node)); + DCHECK_EQ(node_element.Value(), node); + if (OldValue(node) != node) { + has_change = true; + SetValue(node, node); + if (consider_vehicle_vars_) { + const int64 vehicle_var_index = VehicleVarIndex(node); + DCHECK_NE(OldValue(vehicle_var_index), -1); + SetValue(vehicle_var_index, -1); + } + } + } + return has_change; +} + +// FilteredHeuristicExpensiveChainLNSOperator + +FilteredHeuristicExpensiveChainLNSOperator:: + FilteredHeuristicExpensiveChainLNSOperator( + std::unique_ptr heuristic, + int num_arcs_to_consider, + std::function arc_cost_for_route_start) + : IntVarLocalSearchOperator(heuristic->model()->Nexts()), + heuristic_(std::move(heuristic)), + model_(*heuristic_->model()), + consider_vehicle_vars_(!model_.CostsAreHomogeneousAcrossVehicles()), + current_route_(0), + last_route_(0), + num_arcs_to_consider_(num_arcs_to_consider), + current_expensive_arc_indices_({-1, -1}), + arc_cost_for_route_start_(std::move(arc_cost_for_route_start)), + just_started_(false) { + DCHECK_GE(num_arcs_to_consider_, 2); + if (consider_vehicle_vars_) { + AddVars(model_.VehicleVars()); + } +} + +void FilteredHeuristicExpensiveChainLNSOperator::OnStart() { + last_route_ = current_route_; + just_started_ = true; +} + +bool FilteredHeuristicExpensiveChainLNSOperator::MakeOneNeighbor() { + while (IncrementPosition()) { + // NOTE: No need to call RevertChanges() here as + // DestroyChainAndReinsertNodes() will always return true if any change was + // made. + if (DestroyChainAndReinsertNodes()) { + return true; + } + } + return false; +} + +bool FilteredHeuristicExpensiveChainLNSOperator::IncrementPosition() { + if (just_started_) { + just_started_ = false; + return FindMostExpensiveChainsOnRemainingRoutes(); + } + + if (IncrementCurrentArcIndices()) return true; + + return IncrementRoute() && FindMostExpensiveChainsOnRemainingRoutes(); +} + +bool FilteredHeuristicExpensiveChainLNSOperator:: + DestroyChainAndReinsertNodes() { + const int first_arc_index = current_expensive_arc_indices_.first; + const int second_arc_index = current_expensive_arc_indices_.second; + DCHECK_LE(0, first_arc_index); + DCHECK_LT(first_arc_index, second_arc_index); + DCHECK_LT(second_arc_index, most_expensive_arc_starts_and_ranks_.size()); + + const std::pair& first_start_and_rank = + most_expensive_arc_starts_and_ranks_[first_arc_index]; + const std::pair& second_start_and_rank = + most_expensive_arc_starts_and_ranks_[second_arc_index]; + int64 before_chain, after_chain; + if (first_start_and_rank.second < second_start_and_rank.second) { + before_chain = first_start_and_rank.first; + after_chain = OldValue(second_start_and_rank.first); + } else { + before_chain = second_start_and_rank.first; + after_chain = OldValue(first_start_and_rank.first); + } + + const Assignment* const result_assignment = + heuristic_->BuildSolutionFromRoutes( + [this, before_chain, after_chain](int64 node) { + if (node == before_chain) return after_chain; + return OldValue(node); + }); + + if (result_assignment == nullptr) { + return false; + } + + bool has_change = false; + std::vector node_performed(model_.Size(), false); + const std::vector& elements = + result_assignment->IntVarContainer().elements(); + for (int vehicle = 0; vehicle < model_.vehicles(); vehicle++) { + int64 node_index = model_.Start(vehicle); + while (!model_.IsEnd(node_index)) { + // NOTE: When building the solution in the heuristic, Next vars are added + // to the assignment at the position corresponding to their index. + const IntVarElement& node_element = elements[node_index]; + DCHECK_EQ(node_element.Var(), model_.NextVar(node_index)); + + const int64 new_node_value = node_element.Value(); + DCHECK_NE(new_node_value, node_index); + node_performed[node_index] = true; + + const int64 vehicle_var_index = VehicleVarIndex(node_index); + if (OldValue(node_index) != new_node_value || + (consider_vehicle_vars_ && OldValue(vehicle_var_index) != vehicle)) { + has_change = true; + SetValue(node_index, new_node_value); + if (consider_vehicle_vars_) { + SetValue(vehicle_var_index, vehicle); + } + } + + node_index = new_node_value; + } + } + for (int64 node = 0; node < model_.Size(); node++) { + if (node_performed[node]) continue; + const IntVarElement& node_element = elements[node]; + DCHECK_EQ(node_element.Var(), model_.NextVar(node)); + DCHECK_EQ(node_element.Value(), node); + if (OldValue(node) != node) { + has_change = true; + SetValue(node, node); + if (consider_vehicle_vars_) { + const int64 vehicle_var_index = VehicleVarIndex(node); + DCHECK_NE(OldValue(vehicle_var_index), -1); + SetValue(vehicle_var_index, -1); + } + } + } + return has_change; +} + +bool FilteredHeuristicExpensiveChainLNSOperator::IncrementRoute() { + ++current_route_ %= model_.vehicles(); + return current_route_ != last_route_; +} + +bool FilteredHeuristicExpensiveChainLNSOperator::IncrementCurrentArcIndices() { + int& second_index = current_expensive_arc_indices_.second; + if (++second_index < most_expensive_arc_starts_and_ranks_.size()) { + return true; + } + int& first_index = current_expensive_arc_indices_.first; + if (first_index + 2 < most_expensive_arc_starts_and_ranks_.size()) { + first_index++; + second_index = first_index + 1; + return true; + } + return false; +} + +namespace { + +// Returns false if the route starting with 'start' is empty. Otherwise sets +// most_expensive_arc_starts_and_ranks and first_expensive_arc_indices according +// to the most expensive chains on the route, and returns true. +bool FindMostExpensiveArcsOnRoute( + int num_arcs, int64 start, const std::function& next_accessor, + const std::function& is_end, + const std::function& arc_cost_for_route_start, + std::vector>* most_expensive_arc_starts_and_ranks, + std::pair* first_expensive_arc_indices) { + if (is_end(next_accessor(start))) { + // Empty route. + *first_expensive_arc_indices = {-1, -1}; + return false; + } + + // TODO(user): Investigate the impact of using a limited size priority + // queue instead of vectors on performance. + std::vector most_expensive_arc_costs(num_arcs, -1); + most_expensive_arc_starts_and_ranks->assign(num_arcs, {-1, -1}); + + int64 before_node = start; + int rank = 0; + while (!is_end(before_node)) { + const int64 after_node = next_accessor(before_node); + const int64 arc_cost = + arc_cost_for_route_start(before_node, after_node, start); + if (most_expensive_arc_costs.back() < arc_cost) { + // Insert this arc in most_expensive_* vectors. + most_expensive_arc_costs.back() = arc_cost; + most_expensive_arc_starts_and_ranks->back().first = before_node; + most_expensive_arc_starts_and_ranks->back().second = rank; + // Move the newly added element in the vectors to keep + // most_expensive_arc_costs sorted decreasingly. + int index_before_added_arc = num_arcs - 2; + while (index_before_added_arc >= 0 && + most_expensive_arc_costs[index_before_added_arc] < arc_cost) { + std::swap(most_expensive_arc_costs[index_before_added_arc + 1], + most_expensive_arc_costs[index_before_added_arc]); + std::swap( + (*most_expensive_arc_starts_and_ranks)[index_before_added_arc + 1], + (*most_expensive_arc_starts_and_ranks)[index_before_added_arc]); + index_before_added_arc--; + } + } + before_node = after_node; + rank++; + } + // If there are less than 'num_arcs' arcs on the path, resize the vector of + // arc starts. + DCHECK_GE(rank, 2); + if (rank < num_arcs) { + most_expensive_arc_starts_and_ranks->resize(rank); + } + *first_expensive_arc_indices = {0, 1}; + return true; +} + +} // namespace + +bool FilteredHeuristicExpensiveChainLNSOperator:: + FindMostExpensiveChainsOnRemainingRoutes() { + do { + if (FindMostExpensiveArcsOnRoute( + num_arcs_to_consider_, model_.Start(current_route_), + [this](int64 i) { return OldValue(i); }, + [this](int64 node) { return model_.IsEnd(node); }, + arc_cost_for_route_start_, &most_expensive_arc_starts_and_ranks_, + ¤t_expensive_arc_indices_)) { + return true; + } + } while (IncrementRoute()); + + return false; +} + RelocateExpensiveChain::RelocateExpensiveChain( const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, int num_arcs_to_consider, std::function arc_cost_for_path_start) - : PathOperator(vars, secondary_vars, 1, false, + : PathOperator(vars, secondary_vars, 1, false, false, std::move(start_empty_path_class)), num_arcs_to_consider_(num_arcs_to_consider), current_path_(0), current_expensive_arc_indices_({-1, -1}), arc_cost_for_path_start_(std::move(arc_cost_for_path_start)), end_path_(0), - has_non_empty_paths_to_explore_(false) {} + has_non_empty_paths_to_explore_(false) { + DCHECK_GE(num_arcs_to_consider_, 2); +} bool RelocateExpensiveChain::MakeNeighbor() { const int first_arc_index = current_expensive_arc_indices_.first; @@ -701,10 +1026,14 @@ bool RelocateExpensiveChain::MakeNeighbor() { const std::pair& second_start_and_rank = most_expensive_arc_starts_and_ranks_[second_arc_index]; if (first_start_and_rank.second < second_start_and_rank.second) { - return MoveChain(first_start_and_rank.first, second_start_and_rank.first, + return CheckChainValidity(first_start_and_rank.first, + second_start_and_rank.first, BaseNode(0)) && + MoveChain(first_start_and_rank.first, second_start_and_rank.first, BaseNode(0)); } - return MoveChain(second_start_and_rank.first, first_start_and_rank.first, + return CheckChainValidity(second_start_and_rank.first, + first_start_and_rank.first, BaseNode(0)) && + MoveChain(second_start_and_rank.first, first_start_and_rank.first, BaseNode(0)); } @@ -761,7 +1090,12 @@ bool RelocateExpensiveChain::IncrementCurrentArcIndices() { bool RelocateExpensiveChain::FindMostExpensiveChainsOnRemainingPaths() { do { - if (FindMostExpensiveChainsOnCurrentPath()) { + if (FindMostExpensiveArcsOnRoute( + num_arcs_to_consider_, path_starts()[current_path_], + [this](int64 i) { return OldNext(i); }, + [this](int64 node) { return IsPathEnd(node); }, + arc_cost_for_path_start_, &most_expensive_arc_starts_and_ranks_, + ¤t_expensive_arc_indices_)) { return true; } IncrementCurrentPath(); @@ -769,68 +1103,14 @@ bool RelocateExpensiveChain::FindMostExpensiveChainsOnRemainingPaths() { return false; } -bool RelocateExpensiveChain::FindMostExpensiveChainsOnCurrentPath() { - const int64 current_path_start = path_starts()[current_path_]; - - if (IsPathEnd(OldNext(current_path_start))) { - // Empty path. - current_expensive_arc_indices_.first = - current_expensive_arc_indices_.second = -1; - return false; - } - - // TODO(user): Investigate the impact of using a limited size priority - // queue instead of vectors on performance. - std::vector most_expensive_arc_costs(num_arcs_to_consider_, -1); - most_expensive_arc_starts_and_ranks_.assign(num_arcs_to_consider_, {-1, -1}); - - int64 before_node = current_path_start; - int rank = 0; - while (!IsPathEnd(before_node)) { - const int64 after_node = OldNext(before_node); - const int64 arc_cost = - arc_cost_for_path_start_(before_node, after_node, current_path_start); - if (most_expensive_arc_costs.back() < arc_cost) { - // Insert this arc in most_expensive_* vectors. - most_expensive_arc_costs.back() = arc_cost; - most_expensive_arc_starts_and_ranks_.back().first = before_node; - most_expensive_arc_starts_and_ranks_.back().second = rank; - // Move the newly added element in the vectors to keep - // most_expensive_arc_costs sorted decreasingly. - int index_before_added_arc = num_arcs_to_consider_ - 2; - while (index_before_added_arc >= 0 && - most_expensive_arc_costs[index_before_added_arc] < arc_cost) { - std::swap(most_expensive_arc_costs[index_before_added_arc + 1], - most_expensive_arc_costs[index_before_added_arc]); - std::swap( - most_expensive_arc_starts_and_ranks_[index_before_added_arc + 1], - most_expensive_arc_starts_and_ranks_[index_before_added_arc]); - index_before_added_arc--; - } - } - before_node = after_node; - rank++; - } - // If there are less than num_arcs_to_consider_ arcs on the path, resize the - // vector of arc starts. - if (rank < num_arcs_to_consider_) { - most_expensive_arc_starts_and_ranks_.resize(rank); - } - - current_expensive_arc_indices_.first = 0; - current_expensive_arc_indices_.second = 1; - - return true; -} - RelocateSubtrip::RelocateSubtrip( const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, const RoutingIndexPairs& pairs) - : PathWithPreviousNodesOperator(vars, secondary_vars, - /*number_of_base_nodes*/ 2, - std::move(start_empty_path_class)) { + : PathOperator(vars, secondary_vars, + /*number_of_base_nodes*/ 2, true, false, + std::move(start_empty_path_class)) { is_pickup_node_.resize(number_of_nexts_, false); is_delivery_node_.resize(number_of_nexts_, false); pair_of_node_.resize(number_of_nexts_, -1); @@ -950,8 +1230,6 @@ bool RelocateSubtrip::RelocateSubTripFromDelivery(const int64 chain_last_node, } bool RelocateSubtrip::MakeNeighbor() { - if (IsPathEnd(BaseNode(0))) return false; - if (IsPathEnd(BaseNode(1))) return false; if (is_pickup_node_[BaseNode(0)]) { return RelocateSubTripFromPickup(BaseNode(0), BaseNode(1)); } else if (is_delivery_node_[BaseNode(0)]) { @@ -966,8 +1244,8 @@ ExchangeSubtrip::ExchangeSubtrip( const std::vector& secondary_vars, std::function start_empty_path_class, const RoutingIndexPairs& pairs) - : PathWithPreviousNodesOperator(vars, secondary_vars, 2, - std::move(start_empty_path_class)) { + : PathOperator(vars, secondary_vars, 2, true, false, + std::move(start_empty_path_class)) { is_pickup_node_.resize(number_of_nexts_, false); is_delivery_node_.resize(number_of_nexts_, false); pair_of_node_.resize(number_of_nexts_, -1); @@ -997,7 +1275,6 @@ bool VectorContains(const std::vector& values, int64 target) { } // namespace bool ExchangeSubtrip::MakeNeighbor() { - if (IsPathEnd(BaseNode(0)) || IsPathEnd(BaseNode(1))) return false; if (pair_of_node_[BaseNode(0)] == -1) return false; if (pair_of_node_[BaseNode(1)] == -1) return false; // Break symmetry: a move generated from (BaseNode(0), BaseNode(1)) is the diff --git a/ortools/constraint_solver/routing_neighborhoods.h b/ortools/constraint_solver/routing_neighborhoods.h index 089d9aafb5..6f70bf78db 100644 --- a/ortools/constraint_solver/routing_neighborhoods.h +++ b/ortools/constraint_solver/routing_neighborhoods.h @@ -16,6 +16,7 @@ #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" +#include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_types.h" namespace operations_research { @@ -41,7 +42,7 @@ namespace operations_research { /// This operator is extremely useful to move chains of nodes which are located /// at the same place (for instance nodes part of a same stop). // TODO(user): Consider merging with standard Relocate in local_search.cc. -class MakeRelocateNeighborsOperator : public PathWithPreviousNodesOperator { +class MakeRelocateNeighborsOperator : public PathOperator { public: MakeRelocateNeighborsOperator( const std::vector& vars, @@ -134,7 +135,7 @@ class MakePairActiveOperator : public PathOperator { }; /// Operator which makes pairs of active nodes inactive. -class MakePairInactiveOperator : public PathWithPreviousNodesOperator { +class MakePairInactiveOperator : public PathOperator { public: MakePairInactiveOperator(const std::vector& vars, const std::vector& secondary_vars, @@ -153,7 +154,7 @@ class MakePairInactiveOperator : public PathWithPreviousNodesOperator { /// 1 -> [A] -> 2 -> [B] -> 3 /// 1 -> 2 -> [A] -> [B] -> 3 /// The pair can be moved to another path. -class PairRelocateOperator : public PathWithPreviousNodesOperator { +class PairRelocateOperator : public PathOperator { public: PairRelocateOperator(const std::vector& vars, const std::vector& secondary_vars, @@ -171,6 +172,10 @@ class PairRelocateOperator : public PathWithPreviousNodesOperator { } int64 GetBaseNodeRestartPosition(int base_index) override; + bool ConsiderAlternatives(int64 base_index) const override { + return base_index == kPairFirstNode; + } + private: bool RestartAtPathStartOnSynchronize() override { return true; } @@ -179,7 +184,7 @@ class PairRelocateOperator : public PathWithPreviousNodesOperator { static const int kPairSecondNodeDestination = 2; }; -class LightPairRelocateOperator : public PathWithPreviousNodesOperator { +class LightPairRelocateOperator : public PathOperator { public: LightPairRelocateOperator(const std::vector& vars, const std::vector& secondary_vars, @@ -199,7 +204,7 @@ class LightPairRelocateOperator : public PathWithPreviousNodesOperator { /// (where (1, 3) and (4, 5) are first and last nodes of the paths and can /// therefore not be moved, and (A, B) and (C,D) are pairs of nodes): /// 1 -> [C] -> [D] -> 2 -> 3, 4 -> [A] -> [B] -> 5 -class PairExchangeOperator : public PathWithPreviousNodesOperator { +class PairExchangeOperator : public PathOperator { public: PairExchangeOperator(const std::vector& vars, const std::vector& secondary_vars, @@ -212,6 +217,7 @@ class PairExchangeOperator : public PathWithPreviousNodesOperator { private: bool RestartAtPathStartOnSynchronize() override { return true; } + bool ConsiderAlternatives(int64 base_index) const override { return true; } bool GetPreviousAndSibling(int64 node, int64* previous, int64* sibling, int64* sibling_previous) const; }; @@ -229,7 +235,7 @@ class PairExchangeOperator : public PathWithPreviousNodesOperator { /// 1 -> C -> D -> 2 -> 3 4 -> A -> B -> 5 -> 6 /// 1 -> C -> 2 -> D -> 3 4 -> A -> 5 -> B -> 6 /// 1 -> 2 -> C -> D -> 3 4 -> 5 -> A -> B -> 6 -class PairExchangeRelocateOperator : public PathWithPreviousNodesOperator { +class PairExchangeRelocateOperator : public PathOperator { public: PairExchangeRelocateOperator(const std::vector& vars, const std::vector& secondary_vars, @@ -311,7 +317,7 @@ class SwapIndexPairOperator : public IntVarLocalSearchOperator { /// Operator which inserts inactive nodes into a path and makes a pair of /// active nodes inactive. -class IndexPairSwapActiveOperator : public PathWithPreviousNodesOperator { +class IndexPairSwapActiveOperator : public PathOperator { public: IndexPairSwapActiveOperator(const std::vector& vars, const std::vector& secondary_vars, @@ -331,6 +337,102 @@ class IndexPairSwapActiveOperator : public PathWithPreviousNodesOperator { int inactive_node_; }; +/// LNS-like operator based on a filtered first solution heuristic to rebuild +/// the solution, after the destruction phase consisting of removing one route. +class FilteredHeuristicPathLNSOperator : public IntVarLocalSearchOperator { + public: + explicit FilteredHeuristicPathLNSOperator( + std::unique_ptr heuristic); + ~FilteredHeuristicPathLNSOperator() override {} + + std::string DebugString() const override { + std::string heuristic_name = heuristic_->DebugString(); + const int erase_pos = heuristic_name.find("FilteredHeuristic"); + if (erase_pos != std::string::npos) { + heuristic_name.erase(erase_pos); + } + return absl::StrCat("HeuristicPathLNS(", heuristic_name, ")"); + } + + private: + void OnStart() override; + bool MakeOneNeighbor() override; + + bool IncrementRoute(); + bool CurrentRouteIsEmpty() const; + void IncrementCurrentRouteToNextNonEmpty(); + + bool DestroyRouteAndReinsertNodes(); + + int64 VehicleVarIndex(int64 node) const { return model_.Size() + node; } + + // TODO(user): Remove the dependency from RoutingModel by storing an + // IntVarFilteredHeuristic here instead and storing information on path + // start/ends like PathOperator does (instead of relying on the model). + const std::unique_ptr heuristic_; + const RoutingModel& model_; + const bool consider_vehicle_vars_; + int current_route_; + int last_route_; + bool just_started_; +}; + +/// Similar to the move above, but instead of removing one route entirely, the +/// destruction phase consists of removing all nodes on an "expensive" chain +/// from a route. +// TODO(user): Factor out MakeOneNeighbor() and the common parts of the +// Destroy...AndReinsert() methods in a parent class for the two heuristic LNS +// operators. +class FilteredHeuristicExpensiveChainLNSOperator + : public IntVarLocalSearchOperator { + public: + FilteredHeuristicExpensiveChainLNSOperator( + std::unique_ptr heuristic, + int num_arcs_to_consider, + std::function arc_cost_for_route_start); + ~FilteredHeuristicExpensiveChainLNSOperator() override {} + + std::string DebugString() const override { + std::string heuristic_name = heuristic_->DebugString(); + const int erase_pos = heuristic_name.find("FilteredHeuristic"); + if (erase_pos != std::string::npos) { + heuristic_name.erase(erase_pos); + } + return absl::StrCat("HeuristicExpensiveChainLNS(", heuristic_name, ")"); + } + + private: + void OnStart() override; + bool MakeOneNeighbor() override; + + bool IncrementPosition(); + bool IncrementRoute(); + bool IncrementCurrentArcIndices(); + bool FindMostExpensiveChainsOnRemainingRoutes(); + + bool DestroyChainAndReinsertNodes(); + + int64 VehicleVarIndex(int64 node) const { return model_.Size() + node; } + + const std::unique_ptr heuristic_; + const RoutingModel& model_; + const bool consider_vehicle_vars_; + int current_route_; + int last_route_; + + const int num_arcs_to_consider_; + std::vector> most_expensive_arc_starts_and_ranks_; + /// Indices in most_expensive_arc_starts_and_ranks_ corresponding to the first + /// and second arcs currently being considered for removal. + std::pair + current_expensive_arc_indices_; + std::function + arc_cost_for_route_start_; + + bool just_started_; +}; + /// RelocateExpensiveChain /// /// Operator which relocates the most expensive subchains (given a cost @@ -356,18 +458,14 @@ class RelocateExpensiveChain : public PathOperator { void OnNodeInitialization() override; void IncrementCurrentPath(); bool IncrementCurrentArcIndices(); - /// Returns false if current_path_ is empty. Otherwise sets - /// current_expensive_chain_ to the pair of {"preceding", "last"} nodes - /// corresponding to the most expensive chain on current_path_, and returns - /// true. - bool FindMostExpensiveChainsOnCurrentPath(); - /// Calls FindMostExpensiveChainOnCurrentPath() on remaining paths until one - /// of them returns true. Returns false if all remaining paths are empty. + /// Tries to find most expensive chains on remaining paths, starting with the + /// current one, until succeeding on one of them. + /// Returns false iff all remaining paths are empty. bool FindMostExpensiveChainsOnRemainingPaths(); int num_arcs_to_consider_; int current_path_; - std::vector > most_expensive_arc_starts_and_ranks_; + std::vector> most_expensive_arc_starts_and_ranks_; /// Indices in most_expensive_arc_starts_and_ranks_ corresponding to the first /// and second arcs currently being considered for removal. std::pair @@ -432,7 +530,7 @@ PairNodeSwapActiveOperator::PairNodeSwapActiveOperator( const std::vector& secondary_vars, std::function start_empty_path_class, const RoutingIndexPairs& index_pairs) - : PathOperator(vars, secondary_vars, 2, false, + : PathOperator(vars, secondary_vars, 2, false, false, std::move(start_empty_path_class)), inactive_pair_(0), pairs_(index_pairs) {} @@ -505,7 +603,7 @@ bool PairNodeSwapActiveOperator::MakeNeighbor() { /// at base_node such that rejected nodes are only deliveries. If the base_node /// is a delivery, it selects the smallest subtrip ending at base_node such that /// rejected nodes are only pickups. -class RelocateSubtrip : public PathWithPreviousNodesOperator { +class RelocateSubtrip : public PathOperator { public: RelocateSubtrip(const std::vector& vars, const std::vector& secondary_vars, @@ -532,7 +630,7 @@ class RelocateSubtrip : public PathWithPreviousNodesOperator { std::vector subtrip_nodes_; }; -class ExchangeSubtrip : public PathWithPreviousNodesOperator { +class ExchangeSubtrip : public PathOperator { public: ExchangeSubtrip(const std::vector& vars, const std::vector& secondary_vars, diff --git a/ortools/constraint_solver/routing_parameters.cc b/ortools/constraint_solver/routing_parameters.cc index b3893cee17..313febb0d4 100644 --- a/ortools/constraint_solver/routing_parameters.cc +++ b/ortools/constraint_solver/routing_parameters.cc @@ -52,7 +52,8 @@ RoutingSearchParameters DefaultRoutingSearchParameters() { "savings_arc_coefficient: 1 " "savings_parallel_routes: false " "cheapest_insertion_farthest_seeds_ratio: 0 " - "cheapest_insertion_neighbors_ratio: 1 " + "cheapest_insertion_first_solution_neighbors_ratio: 1 " + "cheapest_insertion_ls_operator_neighbors_ratio: 1 " "local_search_operators {" " use_relocate: BOOL_TRUE" " use_relocate_pair: BOOL_TRUE" @@ -80,8 +81,13 @@ RoutingSearchParameters DefaultRoutingSearchParameters() { " use_full_path_lns: BOOL_FALSE" " use_tsp_lns: BOOL_FALSE" " use_inactive_lns: BOOL_FALSE" + " use_global_cheapest_insertion_path_lns: BOOL_TRUE" + " use_local_cheapest_insertion_path_lns: BOOL_TRUE" + " use_global_cheapest_insertion_expensive_chain_lns: BOOL_FALSE" + " use_local_cheapest_insertion_expensive_chain_lns: BOOL_FALSE" "}" "relocate_expensive_chain_num_arcs_to_consider: 4 " + "heuristic_expensive_chain_lns_num_arcs_to_consider: 4 " "local_search_metaheuristic: AUTOMATIC " "guided_local_search_lambda_coefficient: 0.1 " "use_depth_first_search: false " @@ -176,9 +182,19 @@ std::string FindErrorInRoutingSearchParameters( } } { - const double ratio = search_parameters.cheapest_insertion_neighbors_ratio(); + const double ratio = + search_parameters.cheapest_insertion_first_solution_neighbors_ratio(); if (std::isnan(ratio) || ratio <= 0 || ratio > 1) { - return StrCat("Invalid cheapest_insertion_neighbors_ratio: ", ratio); + return StrCat( + "Invalid cheapest_insertion_first_solution_neighbors_ratio: ", ratio); + } + } + { + const double ratio = + search_parameters.cheapest_insertion_ls_operator_neighbors_ratio(); + if (std::isnan(ratio) || ratio <= 0 || ratio > 1) { + return StrCat("Invalid cheapest_insertion_ls_operator_neighbors_ratio: ", + ratio); } } { @@ -189,6 +205,15 @@ std::string FindErrorInRoutingSearchParameters( num_arcs, ". Must be between 2 and 10^6 (included)."); } } + { + const int32 num_arcs = + search_parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(); + if (num_arcs < 2 || num_arcs > 1e6) { + return StrCat( + "Invalid heuristic_expensive_chain_lns_num_arcs_to_consider: ", + num_arcs, ". Must be between 2 and 10^6 (included)."); + } + } { const double gls_coefficient = search_parameters.guided_local_search_lambda_coefficient(); diff --git a/ortools/constraint_solver/routing_parameters.proto b/ortools/constraint_solver/routing_parameters.proto index e0ce9a8962..c1320c1b83 100644 --- a/ortools/constraint_solver/routing_parameters.proto +++ b/ortools/constraint_solver/routing_parameters.proto @@ -34,6 +34,7 @@ package operations_research; // then the routing library will pick its preferred value for that parameter // automatically: this should be the case for most parameters. // To see those "default" parameters, call GetDefaultRoutingSearchParameters(). +// Next ID: 33 message RoutingSearchParameters { // First solution strategies, used as starting point of local search. FirstSolutionStrategy.Value first_solution_strategy = 1; @@ -75,14 +76,20 @@ message RoutingSearchParameters { // new insertions in the parallel/sequential cheapest insertion heuristic. // If not overridden, its default value is 1, meaning all neighbors will be // considered. - double cheapest_insertion_neighbors_ratio = 21; + // + // Neighbors ratio for the first solution heuristic. + double cheapest_insertion_first_solution_neighbors_ratio = 21; + // Neighbors ratio for the heuristic when used in a local search operator (see + // local_search_operators.use_global_cheapest_insertion_path_lns and + // local_search_operators.use_global_cheapest_insertion_chain_lns below). + double cheapest_insertion_ls_operator_neighbors_ratio = 31; // If true use minimum matching instead of minimal matching in the // Christofides algorithm. bool christofides_use_minimum_matching = 30; // Local search neighborhood operators used to build a solutions neighborhood. - // Next ID: 27 + // Next ID: 31 message LocalSearchNeighborhoodOperators { // --- Inter-route operators --- // Operator which moves a single node to another position. @@ -310,6 +317,20 @@ message RoutingSearchParameters { // consecutive arcs. That way the path can be improved by inserting inactive // nodes or swaping arcs. OptionalBoolean use_inactive_lns = 19; + // --- LNS-like large neighborhood search operators using heuristics --- + // Operator which makes all nodes on a route unperformed, and reinserts them + // using the GlobalCheapestInsertion heuristic. + OptionalBoolean use_global_cheapest_insertion_path_lns = 27; + // Same as above but using LocalCheapestInsertion as a heuristic. + OptionalBoolean use_local_cheapest_insertion_path_lns = 28; + // This operator finds heuristic_expensive_chain_lns_num_arcs_to_consider + // most expensive arcs on a route, makes the nodes in between pairs of these + // expensive arcs unperformed, and reinserts them using the + // GlobalCheapestInsertion heuristic. + OptionalBoolean use_global_cheapest_insertion_expensive_chain_lns = 29; + // Same as above but using LocalCheapestInsertion as a heuristic for + // insertion. + OptionalBoolean use_local_cheapest_insertion_expensive_chain_lns = 30; } LocalSearchNeighborhoodOperators local_search_operators = 3; @@ -322,6 +343,10 @@ message RoutingSearchParameters { // K*(K-1)/2 * number_of_routes * number_of_nodes. int32 relocate_expensive_chain_num_arcs_to_consider = 20; + // Number of expensive arcs to consider cutting in the + // FilteredHeuristicExpensiveChainLNSOperator operator. + int32 heuristic_expensive_chain_lns_num_arcs_to_consider = 32; + // Local search metaheuristics used to guide the search. LocalSearchMetaheuristic.Value local_search_metaheuristic = 4; // These are advanced settings which should not be modified unless you know diff --git a/ortools/constraint_solver/routing_search.cc b/ortools/constraint_solver/routing_search.cc index e8d1f4882b..1a101cbc01 100644 --- a/ortools/constraint_solver/routing_search.cc +++ b/ortools/constraint_solver/routing_search.cc @@ -823,18 +823,6 @@ IntVarLocalSearchFilter* MakeTypeRegulationsFilter( namespace { -int64 GetNextValueFromForbiddenIntervals( - int64 value, const SortedDisjointIntervalList& forbidden_intervals) { - int64 next_value = value; - const auto first_interval_it = - forbidden_intervals.FirstIntervalGreaterOrEqual(next_value); - if (first_interval_it != forbidden_intervals.end() && - next_value >= first_interval_it->start) { - next_value = CapAdd(first_interval_it->end, 1); - } - return next_value; -} - // ChainCumul filter. Version of dimension path filter which is O(delta) rather // than O(length of touched paths). Currently only supports dimensions without // costs (global and local span cost, soft bounds) and with unconstrained @@ -1145,7 +1133,6 @@ class PathCumulFilter : public BasePathFilter { const RoutingModel& routing_model_; const RoutingDimension& dimension_; const std::vector cumuls_; - const std::vector& forbidden_intervals_; const std::vector slacks_; std::vector start_to_vehicle_; std::vector evaluators_; @@ -1206,7 +1193,6 @@ PathCumulFilter::PathCumulFilter(const RoutingModel& routing_model, routing_model_(routing_model), dimension_(dimension), cumuls_(dimension.cumuls()), - forbidden_intervals_(dimension.forbidden_intervals()), slacks_(dimension.slacks()), evaluators_(routing_model.vehicles(), nullptr), vehicle_span_upper_bounds_(dimension.vehicle_span_upper_bounds()), @@ -1381,9 +1367,10 @@ void PathCumulFilter::OnBeforeSynchronizePaths() { total_current_cumul_cost_value_ = 0; cumul_cost_delta_ = 0; current_cumul_cost_values_.clear(); - if (FilterSpanCost() || FilterCumulSoftBounds() || FilterSlackCost() || - FilterCumulSoftLowerBounds() || FilterCumulPiecewiseLinearCosts() || - FilterPrecedences() || FilterSoftSpanCost()) { + if (NumPaths() > 0 && + (FilterSpanCost() || FilterCumulSoftBounds() || FilterSlackCost() || + FilterCumulSoftLowerBounds() || FilterCumulPiecewiseLinearCosts() || + FilterPrecedences() || FilterSoftSpanCost())) { InitializeSupportedPathCumul(¤t_min_start_, kint64max); InitializeSupportedPathCumul(¤t_max_end_, kint64min); current_path_transits_.Clear(); @@ -1419,8 +1406,8 @@ void PathCumulFilter::OnBeforeSynchronizePaths() { const int64 transit_slack = CapAdd(transit, slacks_[node]->Min()); current_path_transits_.PushTransit(r, node, next, transit_slack); cumul = CapAdd(cumul, transit_slack); - cumul = GetNextValueFromForbiddenIntervals(cumul, - forbidden_intervals_[next]); + cumul = + dimension_.GetFirstPossibleGreaterOrEqualValueForNode(next, cumul); cumul = std::max(cumuls_[next]->Min(), cumul); min_path_cumuls.push_back(cumul); node = next; @@ -1562,8 +1549,7 @@ bool PathCumulFilter::AcceptPath(int64 path_start, int64 chain_start, const int64 transit_slack = CapAdd(transit, slacks_[node]->Min()); delta_path_transits_.PushTransit(path, node, next, transit_slack); cumul = CapAdd(cumul, transit_slack); - cumul = - GetNextValueFromForbiddenIntervals(cumul, forbidden_intervals_[next]); + cumul = dimension_.GetFirstPossibleGreaterOrEqualValueForNode(next, cumul); if (cumul > std::min(capacity, cumuls_[next]->Max())) { return false; } @@ -1960,39 +1946,77 @@ bool DimensionHasCumulConstraint(const RoutingDimension& dimension) { } // namespace -std::vector MakeCumulFilters( - const RoutingDimension& dimension, bool filter_objective_cost) { - std::vector filters; - const bool has_cumul_cost = DimensionHasCumulCost(dimension); - const bool has_precedences = !dimension.GetNodePrecedences().empty(); - const bool can_use_cumul_bounds_propagator_filter = - !dimension.HasBreakConstraints() && - (!filter_objective_cost || !has_cumul_cost); - // NOTE: We always add the PathCumulFilter to filter each route's feasibility - // separately to try and cut bad decisions earlier in the search, but we don't - // propagate the computed cost if the LPCumulFilter is already doing it. - const bool use_global_lp_filter = - (has_precedences && !can_use_cumul_bounds_propagator_filter) || - (filter_objective_cost && dimension.global_span_cost_coefficient() > 0); +void AppendDimensionCumulFilters( + const std::vector& dimensions, + bool filter_objective_cost, std::vector* filters) { + // NOTE: We first sort the dimensions by increasing complexity of filtering: + // - Dimensions without any cumul-related costs or constraints will have a + // ChainCumulFilter. + // - Dimensions with cumul costs or constraints, but no global span cost + // and/or precedences will have a PathCumulFilter. + // - Dimensions with a global span cost coefficient and/or precedences will + // have a global LP filter. + const int num_dimensions = dimensions.size(); - const RoutingModel& model = *dimension.model(); - if (has_cumul_cost || DimensionHasCumulConstraint(dimension)) { - filters.push_back(MakePathCumulFilter(dimension, !use_global_lp_filter, - filter_objective_cost)); - } else { - filters.push_back( - model.solver()->RevAlloc(new ChainCumulFilter(model, dimension))); + std::vector use_path_cumul_filter(num_dimensions); + std::vector use_cumul_bounds_propagator_filter(num_dimensions); + std::vector use_global_lp_filter(num_dimensions); + std::vector filtering_difficulty(num_dimensions); + for (int d = 0; d < num_dimensions; d++) { + const RoutingDimension& dimension = *dimensions[d]; + const bool has_cumul_cost = DimensionHasCumulCost(dimension); + use_path_cumul_filter[d] = + has_cumul_cost || DimensionHasCumulConstraint(dimension); + + const bool can_use_cumul_bounds_propagator_filter = + !dimension.HasBreakConstraints() && + (!filter_objective_cost || !has_cumul_cost); + const bool has_precedences = !dimension.GetNodePrecedences().empty(); + use_global_lp_filter[d] = + (has_precedences && !can_use_cumul_bounds_propagator_filter) || + (filter_objective_cost && dimension.global_span_cost_coefficient() > 0); + + use_cumul_bounds_propagator_filter[d] = + has_precedences && !use_global_lp_filter[d]; + + filtering_difficulty[d] = 4 * use_global_lp_filter[d] + + 2 * use_cumul_bounds_propagator_filter[d] + + use_path_cumul_filter[d]; } - if (use_global_lp_filter) { - DCHECK(model.GetMutableGlobalCumulOptimizer(dimension) != nullptr); - filters.push_back( - MakeGlobalLPCumulFilter(model.GetMutableGlobalCumulOptimizer(dimension), - filter_objective_cost)); - } else if (has_precedences) { - DCHECK(can_use_cumul_bounds_propagator_filter); - filters.push_back(MakeCumulBoundsPropagatorFilter(dimension)); + + std::vector sorted_dimension_indices(num_dimensions); + std::iota(sorted_dimension_indices.begin(), sorted_dimension_indices.end(), + 0); + std::sort(sorted_dimension_indices.begin(), sorted_dimension_indices.end(), + [&filtering_difficulty](int d1, int d2) { + return filtering_difficulty[d1] < filtering_difficulty[d2]; + }); + + for (const int d : sorted_dimension_indices) { + const RoutingDimension& dimension = *dimensions[d]; + const RoutingModel& model = *dimension.model(); + // NOTE: We always add the [Chain|Path]CumulFilter to filter each route's + // feasibility separately to try and cut bad decisions earlier in the + // search, but we don't propagate the computed cost if the LPCumulFilter is + // already doing it. + const bool use_global_lp = use_global_lp_filter[d]; + if (use_path_cumul_filter[d]) { + filters->push_back(MakePathCumulFilter(dimension, !use_global_lp, + filter_objective_cost)); + } else { + filters->push_back( + model.solver()->RevAlloc(new ChainCumulFilter(model, dimension))); + } + + if (use_global_lp) { + DCHECK(model.GetMutableGlobalCumulOptimizer(dimension) != nullptr); + filters->push_back(MakeGlobalLPCumulFilter( + model.GetMutableGlobalCumulOptimizer(dimension), + filter_objective_cost)); + } else if (use_cumul_bounds_propagator_filter[d]) { + filters->push_back(MakeCumulBoundsPropagatorFilter(dimension)); + } } - return filters; } namespace { @@ -2508,7 +2532,7 @@ IntVarLocalSearchFilter* MakeCPFeasibilityFilter( IntVarFilteredDecisionBuilder::IntVarFilteredDecisionBuilder( std::unique_ptr heuristic) - : heuristic_(heuristic.release()) {} + : heuristic_(std::move(heuristic)) {} Decision* IntVarFilteredDecisionBuilder::Next(Solver* solver) { Assignment* const assignment = heuristic_->BuildSolution(); @@ -2543,8 +2567,8 @@ std::string IntVarFilteredDecisionBuilder::DebugString() const { IntVarFilteredHeuristic::IntVarFilteredHeuristic( Solver* solver, const std::vector& vars, const std::vector& filters) - : vars_(vars), - assignment_(solver->MakeAssignment()), + : assignment_(solver->MakeAssignment()), + vars_(vars), delta_(solver->MakeAssignment()), is_in_delta_(vars_.size(), false), empty_(solver->MakeAssignment()), @@ -2555,13 +2579,18 @@ IntVarFilteredHeuristic::IntVarFilteredHeuristic( delta_indices_.reserve(vars_.size()); } -Assignment* const IntVarFilteredHeuristic::BuildSolution() { +void IntVarFilteredHeuristic::ResetSolution() { number_of_decisions_ = 0; number_of_rejects_ = 0; // Wiping assignment when starting a new search. assignment_->MutableIntVarContainer()->Clear(); assignment_->MutableIntVarContainer()->Resize(vars_.size()); delta_->MutableIntVarContainer()->Clear(); + SynchronizeFilters(); +} + +Assignment* const IntVarFilteredHeuristic::BuildSolution() { + ResetSolution(); if (!InitializeSolution()) { return nullptr; } @@ -2572,6 +2601,38 @@ Assignment* const IntVarFilteredHeuristic::BuildSolution() { return nullptr; } +const Assignment* RoutingFilteredHeuristic::BuildSolutionFromRoutes( + const std::function& next_accessor) { + ResetSolution(); + ResetVehicleIndices(); + // NOTE: We don't need to clear or pre-set the two following vectors as the + // for loop below will set all elements. + start_chain_ends_.resize(model()->vehicles()); + end_chain_starts_.resize(model()->vehicles()); + + for (int v = 0; v < model_->vehicles(); v++) { + int64 node = model_->Start(v); + while (!model_->IsEnd(node)) { + const int64 next = next_accessor(node); + DCHECK_NE(next, node); + SetValue(node, next); + SetVehicleIndex(node, v); + node = next; + } + // All vehicles have full routes from start to end here. + start_chain_ends_[v] = model()->End(v); + end_chain_starts_[v] = model()->Start(v); + } + if (!Commit()) { + return nullptr; + } + SynchronizeFilters(); + if (BuildSolutionInternal()) { + return assignment_; + } + return nullptr; +} + bool IntVarFilteredHeuristic::Commit() { ++number_of_decisions_; const bool accept = FilterAccept(); @@ -2694,7 +2755,7 @@ bool RoutingFilteredHeuristic::InitializeSolution() { void RoutingFilteredHeuristic::MakeDisjunctionNodesUnperformed(int64 node) { model()->ForEachNodeInDisjunctionWithMaxCardinalityFromIndex( node, 1, [this, node](int alternate) { - if (node != alternate) { + if (node != alternate && !Contains(alternate)) { SetValue(alternate, alternate); } }); @@ -2757,6 +2818,7 @@ void CheapestInsertionFilteredHeuristic::InitializePriorityQueue( DCHECK_EQ(start_end_distances_per_node->size(), num_nodes); for (int node = 0; node < num_nodes; node++) { + if (Contains(node)) continue; std::vector& start_end_distances = (*start_end_distances_per_node)[node]; if (start_end_distances.empty()) { @@ -3964,7 +4026,13 @@ LocalCheapestInsertionFilteredHeuristic:: std::function evaluator, const std::vector& filters) : CheapestInsertionFilteredHeuristic(model, std::move(evaluator), nullptr, - filters) {} + filters) { + std::vector all_vehicles(model->vehicles()); + std::iota(std::begin(all_vehicles), std::end(all_vehicles), 0); + + start_end_distances_per_node_ = + ComputeStartEndDistanceForVehicles(all_vehicles); +} bool LocalCheapestInsertionFilteredHeuristic::BuildSolutionInternal() { // Marking if we've tried inserting a node. @@ -3979,10 +4047,13 @@ bool LocalCheapestInsertionFilteredHeuristic::BuildSolutionInternal() { model()->GetPickupAndDeliveryPairs(); for (const auto& index_pair : index_pairs) { for (int64 pickup : index_pair.first) { + if (Contains(pickup)) { + continue; + } for (int64 delivery : index_pair.second) { // If either is already in the solution, let it be inserted in the // standard node insertion loop. - if (Contains(pickup) || Contains(delivery)) { + if (Contains(delivery)) { continue; } if (StopSearch()) return false; @@ -4018,14 +4089,8 @@ bool LocalCheapestInsertionFilteredHeuristic::BuildSolutionInternal() { } } - std::vector all_vehicles(model()->vehicles()); - std::iota(std::begin(all_vehicles), std::end(all_vehicles), 0); - - std::vector> start_end_distances_per_node = - ComputeStartEndDistanceForVehicles(all_vehicles); - std::priority_queue node_queue; - InitializePriorityQueue(&start_end_distances_per_node, &node_queue); + InitializePriorityQueue(&start_end_distances_per_node_, &node_queue); while (!node_queue.empty()) { const int node = node_queue.top().second; @@ -4726,7 +4791,7 @@ class SavingsFilteredHeuristic::SavingsContainer { // SavingsFilteredHeuristic SavingsFilteredHeuristic::SavingsFilteredHeuristic( - RoutingModel* model, RoutingIndexManager* manager, + RoutingModel* model, const RoutingIndexManager* manager, SavingsParameters parameters, const std::vector& filters) : RoutingFilteredHeuristic(model, filters), @@ -5402,7 +5467,13 @@ bool ChristofidesFilteredHeuristic::BuildSolutionInternal() { DCHECK_LT(to, indices.size()); const int from_index = (from == 0) ? start : indices[from]; const int to_index = (to == 0) ? end : indices[to]; - return model()->GetArcCostForClass(from_index, to_index, cost_class); + const int64 cost = + model()->GetArcCostForClass(from_index, to_index, cost_class); + // To avoid overflow issues, capping costs at kint64max/2, the maximum + // value supported by MinCostPerfectMatching. + // TODO(user): Investigate if ChristofidesPathSolver should not + // return a status to bail out fast in case of problem. + return std::min(cost, kint64max / 2); }; using Cost = decltype(cost); ChristofidesPathSolver christofides_solver( diff --git a/ortools/constraint_solver/samples/simple_routing_program.cc b/ortools/constraint_solver/samples/simple_routing_program.cc index f168db55db..8b292e875d 100644 --- a/ortools/constraint_solver/samples/simple_routing_program.cc +++ b/ortools/constraint_solver/samples/simple_routing_program.cc @@ -14,6 +14,7 @@ // [START program] // [START import] #include + #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" #include "ortools/constraint_solver/routing_index_manager.h" diff --git a/ortools/constraint_solver/samples/tsp.cc b/ortools/constraint_solver/samples/tsp.cc index b878e7a465..16a2226386 100644 --- a/ortools/constraint_solver/samples/tsp.cc +++ b/ortools/constraint_solver/samples/tsp.cc @@ -15,6 +15,7 @@ // [START import] #include #include + #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" #include "ortools/constraint_solver/routing_index_manager.h" diff --git a/ortools/constraint_solver/samples/tsp_circuit_board.cc b/ortools/constraint_solver/samples/tsp_circuit_board.cc index ce1fac41c6..4946c8510a 100644 --- a/ortools/constraint_solver/samples/tsp_circuit_board.cc +++ b/ortools/constraint_solver/samples/tsp_circuit_board.cc @@ -15,6 +15,7 @@ // [START import] #include #include + #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" #include "ortools/constraint_solver/routing_index_manager.h" diff --git a/ortools/constraint_solver/samples/tsp_cities.cc b/ortools/constraint_solver/samples/tsp_cities.cc index 72a60cecca..7a218c6820 100644 --- a/ortools/constraint_solver/samples/tsp_cities.cc +++ b/ortools/constraint_solver/samples/tsp_cities.cc @@ -15,6 +15,7 @@ // [START import] #include #include + #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" #include "ortools/constraint_solver/routing_index_manager.h" diff --git a/ortools/constraint_solver/samples/tsp_distance_matrix.cc b/ortools/constraint_solver/samples/tsp_distance_matrix.cc index 91164cd506..a7d5f49e65 100644 --- a/ortools/constraint_solver/samples/tsp_distance_matrix.cc +++ b/ortools/constraint_solver/samples/tsp_distance_matrix.cc @@ -14,6 +14,7 @@ // [START program] // [START import] #include + #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" #include "ortools/constraint_solver/routing_index_manager.h" diff --git a/ortools/constraint_solver/samples/vrp.cc b/ortools/constraint_solver/samples/vrp.cc index 6a248388f5..9dbbe54bc6 100644 --- a/ortools/constraint_solver/samples/vrp.cc +++ b/ortools/constraint_solver/samples/vrp.cc @@ -14,6 +14,7 @@ // [START program] // [START import] #include + #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" #include "ortools/constraint_solver/routing_index_manager.h" diff --git a/ortools/constraint_solver/samples/vrp_capacity.cc b/ortools/constraint_solver/samples/vrp_capacity.cc index 3d4057b9f2..44d2e727d8 100644 --- a/ortools/constraint_solver/samples/vrp_capacity.cc +++ b/ortools/constraint_solver/samples/vrp_capacity.cc @@ -14,6 +14,7 @@ // [START program] // [START import] #include + #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" #include "ortools/constraint_solver/routing_index_manager.h" diff --git a/ortools/constraint_solver/samples/vrp_drop_nodes.cc b/ortools/constraint_solver/samples/vrp_drop_nodes.cc index c485e10020..3e0810e6e8 100644 --- a/ortools/constraint_solver/samples/vrp_drop_nodes.cc +++ b/ortools/constraint_solver/samples/vrp_drop_nodes.cc @@ -14,6 +14,7 @@ // [START program] // [START import] #include + #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" #include "ortools/constraint_solver/routing_index_manager.h" diff --git a/ortools/constraint_solver/samples/vrp_global_span.cc b/ortools/constraint_solver/samples/vrp_global_span.cc index 962df5783c..d8ba8e49d3 100644 --- a/ortools/constraint_solver/samples/vrp_global_span.cc +++ b/ortools/constraint_solver/samples/vrp_global_span.cc @@ -14,6 +14,7 @@ // [START program] // [START import] #include + #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" #include "ortools/constraint_solver/routing_index_manager.h" diff --git a/ortools/constraint_solver/samples/vrp_initial_routes.cc b/ortools/constraint_solver/samples/vrp_initial_routes.cc index ce7d58230e..702cc5ed71 100644 --- a/ortools/constraint_solver/samples/vrp_initial_routes.cc +++ b/ortools/constraint_solver/samples/vrp_initial_routes.cc @@ -14,6 +14,7 @@ // [START program] // [START import] #include + #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_index_manager.h" #include "ortools/constraint_solver/routing_parameters.h" diff --git a/ortools/constraint_solver/samples/vrp_pickup_delivery.cc b/ortools/constraint_solver/samples/vrp_pickup_delivery.cc index 5f26e2d25d..9da4d9f9c0 100644 --- a/ortools/constraint_solver/samples/vrp_pickup_delivery.cc +++ b/ortools/constraint_solver/samples/vrp_pickup_delivery.cc @@ -14,6 +14,7 @@ // [START program] // [START import] #include + #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" #include "ortools/constraint_solver/routing_index_manager.h" diff --git a/ortools/constraint_solver/samples/vrp_pickup_delivery_fifo.cc b/ortools/constraint_solver/samples/vrp_pickup_delivery_fifo.cc index ad0522624c..3df700d131 100644 --- a/ortools/constraint_solver/samples/vrp_pickup_delivery_fifo.cc +++ b/ortools/constraint_solver/samples/vrp_pickup_delivery_fifo.cc @@ -14,6 +14,7 @@ // [START program] // [START import] #include + #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" #include "ortools/constraint_solver/routing_index_manager.h" diff --git a/ortools/constraint_solver/samples/vrp_pickup_delivery_lifo.cc b/ortools/constraint_solver/samples/vrp_pickup_delivery_lifo.cc index 50ba4c9537..85d98e28cf 100644 --- a/ortools/constraint_solver/samples/vrp_pickup_delivery_lifo.cc +++ b/ortools/constraint_solver/samples/vrp_pickup_delivery_lifo.cc @@ -14,6 +14,7 @@ // [START program] // [START import] #include + #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" #include "ortools/constraint_solver/routing_index_manager.h" diff --git a/ortools/constraint_solver/samples/vrp_resources.cc b/ortools/constraint_solver/samples/vrp_resources.cc index a1ede7474c..85c38bc60d 100644 --- a/ortools/constraint_solver/samples/vrp_resources.cc +++ b/ortools/constraint_solver/samples/vrp_resources.cc @@ -15,6 +15,7 @@ // [START import] #include #include + #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" #include "ortools/constraint_solver/routing_index_manager.h" diff --git a/ortools/constraint_solver/samples/vrp_starts_ends.cc b/ortools/constraint_solver/samples/vrp_starts_ends.cc index 092ccb58f6..fa7782e1ef 100644 --- a/ortools/constraint_solver/samples/vrp_starts_ends.cc +++ b/ortools/constraint_solver/samples/vrp_starts_ends.cc @@ -14,6 +14,7 @@ // [START program] // [START import] #include + #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" #include "ortools/constraint_solver/routing_index_manager.h" diff --git a/ortools/constraint_solver/samples/vrp_time_windows.cc b/ortools/constraint_solver/samples/vrp_time_windows.cc index 236581441d..2789377b9e 100644 --- a/ortools/constraint_solver/samples/vrp_time_windows.cc +++ b/ortools/constraint_solver/samples/vrp_time_windows.cc @@ -15,6 +15,7 @@ // [START import] #include #include + #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" #include "ortools/constraint_solver/routing_index_manager.h" diff --git a/ortools/constraint_solver/search.cc b/ortools/constraint_solver/search.cc index c46b45d99a..0bc13a4c03 100644 --- a/ortools/constraint_solver/search.cc +++ b/ortools/constraint_solver/search.cc @@ -34,7 +34,6 @@ #include "ortools/base/macros.h" #include "ortools/base/map_util.h" #include "ortools/base/mathutil.h" -#include "ortools/base/random.h" #include "ortools/base/stl_util.h" #include "ortools/base/timer.h" #include "ortools/constraint_solver/constraint_solver.h" @@ -3272,7 +3271,7 @@ class SimulatedAnnealing : public Metaheuristic { const int64 temperature0_; int64 iteration_; - ACMRandom rand_; + std::mt19937 rand_; bool found_initial_solution_; DISALLOW_COPY_AND_ASSIGN(SimulatedAnnealing); @@ -3284,7 +3283,7 @@ SimulatedAnnealing::SimulatedAnnealing(Solver* const s, bool maximize, : Metaheuristic(s, maximize, objective, step), temperature0_(initial_temperature), iteration_(0), - rand_(654), + rand_(CpRandomSeed()), found_initial_solution_(false) {} void SimulatedAnnealing::EnterSearch() { @@ -3297,11 +3296,13 @@ void SimulatedAnnealing::ApplyDecision(Decision* const d) { if (d == s->balancing_decision()) { return; } + const double rand_double = absl::Uniform(rand_, 0.0, 1.0); #if defined(_MSC_VER) || defined(__ANDROID__) - const int64 energy_bound = Temperature() * log(rand_.RndFloat()) / log(2.0L); + const double rand_log2_double = log(rand_double) / log(2.0L); #else - const int64 energy_bound = Temperature() * log2(rand_.RndFloat()); + const double rand_log2_double = log2(rand_double); #endif + const int64 energy_bound = Temperature() * rand_log2_double; if (maximize_) { const int64 bound = (current_ > kint64min) ? current_ + step_ + energy_bound : current_; diff --git a/ortools/flatzinc/fz.cc b/ortools/flatzinc/fz.cc index 58db1239d9..a04cac2b77 100644 --- a/ortools/flatzinc/fz.cc +++ b/ortools/flatzinc/fz.cc @@ -42,7 +42,7 @@ DEFINE_bool(free_search, false, "If false, the solver must follow the defined search." "If true, other search are allowed."); DEFINE_int32(threads, 0, "Number of threads the solver will use."); -DEFINE_bool(presolve, false, "Presolve the model to simplify it."); +DEFINE_bool(presolve, true, "Presolve the model to simplify it."); DEFINE_bool(statistics, false, "Print solver statistics after search."); DEFINE_bool(read_from_stdin, false, "Read the FlatZinc from stdin, not from a file."); diff --git a/ortools/flatzinc/model.h b/ortools/flatzinc/model.h index 8703425bca..8f91b7a5a5 100644 --- a/ortools/flatzinc/model.h +++ b/ortools/flatzinc/model.h @@ -222,7 +222,7 @@ struct Constraint { void SetAsFalse(); // The flatzinc type of the constraint (i.e. "int_eq" for integer equality) - // stored as a std::string. + // stored as a string. std::string type; std::vector arguments; // Indicates if the constraint actually propagates towards a target variable diff --git a/ortools/flatzinc/parser.lex b/ortools/flatzinc/parser.lex index e0ba130a63..2a0f2015e4 100644 --- a/ortools/flatzinc/parser.lex +++ b/ortools/flatzinc/parser.lex @@ -19,7 +19,7 @@ #endif %} -/* Rules that parse the bottom-line std::string tokens of a .fz file and +/* Rules that parse the bottom-line string tokens of a .fz file and convert them into YACC tokens, which may carry a value. See the LexerInfo struct and the %token declarations in ./parser.yy. */ diff --git a/ortools/flatzinc/parser.tab.cc b/ortools/flatzinc/parser.tab.cc index 15d51fd9db..81a39c1b19 100644 --- a/ortools/flatzinc/parser.tab.cc +++ b/ortools/flatzinc/parser.tab.cc @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -/* A Bison parser, made by GNU Bison 3.3.2. */ +/* A Bison parser, made by GNU Bison 3.4.1. */ /* Bison implementation for Yacc-like parsers in C @@ -61,7 +61,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "3.3.2" +#define YYBISON_VERSION "3.4.1" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -104,8 +104,8 @@ #define YYERROR_VERBOSE 1 #endif -/* In a future release of Bison, this section will be replaced - by #include "parser.tab.hh". */ +/* Use api.header.include to #include this header + instead of duplicating it here. */ #ifndef YY_ORFZ_ORTOOLS_FLATZINC_PARSER_TAB_HH_INCLUDED #define YY_ORFZ_ORTOOLS_FLATZINC_PARSER_TAB_HH_INCLUDED /* Debug traces. */ @@ -124,7 +124,7 @@ extern int orfz_debug; #endif /* "%code requires" blocks. */ -#line 21 "./ortools/flatzinc/parser.yy" /* yacc.c:352 */ +#line 21 "./ortools/flatzinc/parser.yy" #if !defined(OR_TOOLS_FLATZINC_FLATZINC_TAB_HH_) #define OR_TOOLS_FLATZINC_FLATZINC_TAB_HH_ @@ -140,7 +140,7 @@ typedef operations_research::fz::LexerInfo YYSTYPE; #endif // OR_TOOLS_FLATZINC_FLATZINC_TAB_HH_ -#line 133 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:352 */ +#line 133 "./ortools/flatzinc/parser.tab.cc" /* Token type. */ #ifndef ORFZ_TOKENTYPE @@ -176,7 +176,7 @@ int orfz_parse(operations_research::fz::ParserContext* context, #endif /* !YY_ORFZ_ORTOOLS_FLATZINC_PARSER_TAB_HH_INCLUDED */ /* Unqualified %code blocks. */ -#line 38 "./ortools/flatzinc/parser.yy" /* yacc.c:355 */ +#line 38 "./ortools/flatzinc/parser.yy" #include "absl/strings/match.h" #include "absl/strings/str_format.h" @@ -195,7 +195,7 @@ using operations_research::fz::SolutionOutputSpecs; using operations_research::fz::VariableRefOrValue; using operations_research::fz::VariableRefOrValueArray; -#line 191 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:355 */ +#line 191 "./ortools/flatzinc/parser.tab.cc" #ifdef short #undef short @@ -295,6 +295,8 @@ typedef short yytype_int16; #define YY_INITIAL_VALUE(Value) /* Nothing. */ #endif +#define YY_ASSERT(E) ((void)(0 && (E))) + #if !defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ @@ -1161,6 +1163,8 @@ yynewstate: | yynewstate -- set current state (the top of the stack) to yystate. | `--------------------------------------------------------------------*/ yysetstate: + YYDPRINTF((stderr, "Entering state %d\n", yystate)); + YY_ASSERT(0 <= yystate && yystate < YYNSTATES); *yyssp = (yytype_int16)yystate; if (yyss + yystacksize - 1 <= yyssp) @@ -1216,8 +1220,6 @@ yysetstate: } #endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ - YYDPRINTF((stderr, "Entering state %d\n", yystate)); - if (yystate == YYFINAL) YYACCEPT; goto yybackup; @@ -1274,7 +1276,6 @@ yybackup: YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END - goto yynewstate; /*-----------------------------------------------------------. @@ -1305,15 +1306,15 @@ yyreduce: YY_REDUCE_PRINT(yyn); switch (yyn) { case 4: -#line 116 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 116 "./ortools/flatzinc/parser.yy" { yyerrok; } -#line 1438 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1439 "./ortools/flatzinc/parser.tab.cc" break; case 19: -#line 151 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 151 "./ortools/flatzinc/parser.yy" { // Declaration of a (named) constant: we simply register it in the // parser's context, and don't store it in the model. @@ -1332,11 +1333,11 @@ yyreduce: } delete annotations; } -#line 1462 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1463 "./ortools/flatzinc/parser.tab.cc" break; case 20: -#line 171 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 171 "./ortools/flatzinc/parser.yy" { std::vector* const annotations = (yyvsp[-4].annotations); // Declaration of a (named) constant array. See rule right above. @@ -1352,11 +1353,11 @@ yyreduce: delete assignments; delete annotations; } -#line 1481 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1482 "./ortools/flatzinc/parser.tab.cc" break; case 21: -#line 186 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 186 "./ortools/flatzinc/parser.yy" { std::vector* const annotations = (yyvsp[-3].annotations); // Declaration of a (named) constant array. See rule right above. @@ -1368,11 +1369,11 @@ yyreduce: context->integer_array_map[identifier] = std::vector(); delete annotations; } -#line 1496 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1497 "./ortools/flatzinc/parser.tab.cc" break; case 22: -#line 197 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 197 "./ortools/flatzinc/parser.yy" { std::vector* const annotations = (yyvsp[-4].annotations); // Declaration of a (named) constant array. See rule right above. @@ -1388,11 +1389,11 @@ yyreduce: delete assignments; delete annotations; } -#line 1515 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1516 "./ortools/flatzinc/parser.tab.cc" break; case 23: -#line 212 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 212 "./ortools/flatzinc/parser.yy" { std::vector* const annotations = (yyvsp[-3].annotations); // Declaration of a (named) constant array. See rule right above. @@ -1404,11 +1405,11 @@ yyreduce: context->float_array_map[identifier] = std::vector(); delete annotations; } -#line 1530 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1531 "./ortools/flatzinc/parser.tab.cc" break; case 24: -#line 223 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 223 "./ortools/flatzinc/parser.yy" { // Declaration of a (named) constant array: See rule above. CHECK_EQ((yyvsp[-12].integer_value), 1) @@ -1436,11 +1437,11 @@ yyreduce: delete assignments; delete annotations; } -#line 1560 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1561 "./ortools/flatzinc/parser.tab.cc" break; case 25: -#line 248 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 248 "./ortools/flatzinc/parser.yy" { // Declaration of a variable. If it's unassigned or assigned to a // constant, we'll create a new var stored in the model. If it's @@ -1473,11 +1474,11 @@ yyreduce: } delete annotations; } -#line 1598 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1599 "./ortools/flatzinc/parser.tab.cc" break; case 26: -#line 282 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 282 "./ortools/flatzinc/parser.yy" { // Declaration of a "variable array": these is exactly like N simple // variable declarations, where the identifier for declaration #i is @@ -1547,78 +1548,78 @@ yyreduce: delete annotations; } } -#line 1670 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1671 "./ortools/flatzinc/parser.tab.cc" break; case 27: -#line 351 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 351 "./ortools/flatzinc/parser.yy" { (yyval.var_or_value) = (yyvsp[0].var_or_value); } -#line 1676 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1677 "./ortools/flatzinc/parser.tab.cc" break; case 28: -#line 352 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 352 "./ortools/flatzinc/parser.yy" { (yyval.var_or_value) = VariableRefOrValue::Undefined(); } -#line 1682 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1683 "./ortools/flatzinc/parser.tab.cc" break; case 29: -#line 355 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 355 "./ortools/flatzinc/parser.yy" { (yyval.var_or_value_array) = (yyvsp[-1].var_or_value_array); } -#line 1688 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1689 "./ortools/flatzinc/parser.tab.cc" break; case 30: -#line 356 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 356 "./ortools/flatzinc/parser.yy" { (yyval.var_or_value_array) = nullptr; } -#line 1694 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1695 "./ortools/flatzinc/parser.tab.cc" break; case 31: -#line 357 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 357 "./ortools/flatzinc/parser.yy" { (yyval.var_or_value_array) = nullptr; } -#line 1700 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1701 "./ortools/flatzinc/parser.tab.cc" break; case 32: -#line 360 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 360 "./ortools/flatzinc/parser.yy" { (yyval.var_or_value_array) = (yyvsp[-2].var_or_value_array); (yyval.var_or_value_array)->PushBack((yyvsp[0].var_or_value)); } -#line 1709 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1710 "./ortools/flatzinc/parser.tab.cc" break; case 33: -#line 364 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 364 "./ortools/flatzinc/parser.yy" { (yyval.var_or_value_array) = new VariableRefOrValueArray(); (yyval.var_or_value_array)->PushBack((yyvsp[0].var_or_value)); } -#line 1718 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1719 "./ortools/flatzinc/parser.tab.cc" break; case 34: -#line 370 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 370 "./ortools/flatzinc/parser.yy" { (yyval.var_or_value) = VariableRefOrValue::Value((yyvsp[0].integer_value)); } -#line 1724 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1725 "./ortools/flatzinc/parser.tab.cc" break; case 35: -#line 371 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 371 "./ortools/flatzinc/parser.yy" { // A reference to an existing integer constant or variable. const std::string& id = (yyvsp[0].string_value); @@ -1634,11 +1635,11 @@ yyreduce: *ok = false; } } -#line 1742 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1743 "./ortools/flatzinc/parser.tab.cc" break; case 36: -#line 384 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 384 "./ortools/flatzinc/parser.yy" { // A given element of an existing constant array or variable array. const std::string& id = (yyvsp[-3].string_value); @@ -1655,296 +1656,296 @@ yyreduce: *ok = false; } } -#line 1763 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1764 "./ortools/flatzinc/parser.tab.cc" break; case 37: -#line 402 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 402 "./ortools/flatzinc/parser.yy" { (yyval.domain) = Domain::Boolean(); } -#line 1769 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1770 "./ortools/flatzinc/parser.tab.cc" break; case 38: -#line 403 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 403 "./ortools/flatzinc/parser.yy" { (yyval.domain) = Domain::AllInt64(); } -#line 1775 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1776 "./ortools/flatzinc/parser.tab.cc" break; case 39: -#line 404 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 404 "./ortools/flatzinc/parser.yy" { (yyval.domain) = Domain::Interval((yyvsp[-2].integer_value), (yyvsp[0].integer_value)); } -#line 1781 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1782 "./ortools/flatzinc/parser.tab.cc" break; case 40: -#line 405 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 405 "./ortools/flatzinc/parser.yy" { CHECK((yyvsp[-1].integers) != nullptr); (yyval.domain) = Domain::IntegerList(std::move(*(yyvsp[-1].integers))); delete (yyvsp[-1].integers); } -#line 1791 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1792 "./ortools/flatzinc/parser.tab.cc" break; case 41: -#line 412 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 412 "./ortools/flatzinc/parser.yy" { (yyval.domain) = Domain::SetOfBoolean(); } -#line 1797 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1798 "./ortools/flatzinc/parser.tab.cc" break; case 42: -#line 413 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 413 "./ortools/flatzinc/parser.yy" { (yyval.domain) = Domain::SetOfAllInt64(); } -#line 1803 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1804 "./ortools/flatzinc/parser.tab.cc" break; case 43: -#line 414 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 414 "./ortools/flatzinc/parser.yy" { (yyval.domain) = Domain::SetOfInterval((yyvsp[-2].integer_value), (yyvsp[0].integer_value)); } -#line 1809 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1810 "./ortools/flatzinc/parser.tab.cc" break; case 44: -#line 415 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 415 "./ortools/flatzinc/parser.yy" { CHECK((yyvsp[-1].integers) != nullptr); (yyval.domain) = Domain::SetOfIntegerList(std::move(*(yyvsp[-1].integers))); delete (yyvsp[-1].integers); } -#line 1819 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1820 "./ortools/flatzinc/parser.tab.cc" break; case 45: -#line 422 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 422 "./ortools/flatzinc/parser.yy" { (yyval.domain) = Domain::AllInt64(); } -#line 1825 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1826 "./ortools/flatzinc/parser.tab.cc" break; case 46: -#line 423 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 423 "./ortools/flatzinc/parser.yy" { const int64 lb = ConvertAsIntegerOrDie((yyvsp[-2].double_value)); const int64 ub = ConvertAsIntegerOrDie((yyvsp[0].double_value)); (yyval.domain) = Domain::Interval(lb, ub); } -#line 1835 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1836 "./ortools/flatzinc/parser.tab.cc" break; case 47: -#line 430 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 430 "./ortools/flatzinc/parser.yy" { (yyval.domain) = (yyvsp[0].domain); } -#line 1841 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1842 "./ortools/flatzinc/parser.tab.cc" break; case 48: -#line 431 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 431 "./ortools/flatzinc/parser.yy" { (yyval.domain) = (yyvsp[0].domain); } -#line 1847 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1848 "./ortools/flatzinc/parser.tab.cc" break; case 49: -#line 432 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 432 "./ortools/flatzinc/parser.yy" { (yyval.domain) = (yyvsp[0].domain); } -#line 1853 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1854 "./ortools/flatzinc/parser.tab.cc" break; case 50: -#line 435 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 435 "./ortools/flatzinc/parser.yy" { (yyval.integers) = (yyvsp[-2].integers); (yyval.integers)->emplace_back((yyvsp[0].integer_value)); } -#line 1859 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1860 "./ortools/flatzinc/parser.tab.cc" break; case 51: -#line 436 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 436 "./ortools/flatzinc/parser.yy" { (yyval.integers) = new std::vector(); (yyval.integers)->emplace_back((yyvsp[0].integer_value)); } -#line 1865 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1866 "./ortools/flatzinc/parser.tab.cc" break; case 52: -#line 439 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 439 "./ortools/flatzinc/parser.yy" { (yyval.integer_value) = (yyvsp[0].integer_value); } -#line 1871 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1872 "./ortools/flatzinc/parser.tab.cc" break; case 53: -#line 440 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 440 "./ortools/flatzinc/parser.yy" { (yyval.integer_value) = gtl::FindOrDie(context->integer_map, (yyvsp[0].string_value)); } -#line 1877 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1878 "./ortools/flatzinc/parser.tab.cc" break; case 54: -#line 441 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 441 "./ortools/flatzinc/parser.yy" { (yyval.integer_value) = Lookup( gtl::FindOrDie(context->integer_array_map, (yyvsp[-3].string_value)), (yyvsp[-1].integer_value)); } -#line 1885 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1886 "./ortools/flatzinc/parser.tab.cc" break; case 55: -#line 446 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 446 "./ortools/flatzinc/parser.yy" { (yyval.doubles) = (yyvsp[-2].doubles); (yyval.doubles)->emplace_back((yyvsp[0].double_value)); } -#line 1891 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1892 "./ortools/flatzinc/parser.tab.cc" break; case 56: -#line 447 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 447 "./ortools/flatzinc/parser.yy" { (yyval.doubles) = new std::vector(); (yyval.doubles)->emplace_back((yyvsp[0].double_value)); } -#line 1897 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1898 "./ortools/flatzinc/parser.tab.cc" break; case 57: -#line 450 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 450 "./ortools/flatzinc/parser.yy" { (yyval.double_value) = (yyvsp[0].double_value); } -#line 1903 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1904 "./ortools/flatzinc/parser.tab.cc" break; case 58: -#line 451 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 451 "./ortools/flatzinc/parser.yy" { (yyval.double_value) = gtl::FindOrDie(context->float_map, (yyvsp[0].string_value)); } -#line 1909 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1910 "./ortools/flatzinc/parser.tab.cc" break; case 59: -#line 452 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 452 "./ortools/flatzinc/parser.yy" { (yyval.double_value) = Lookup( gtl::FindOrDie(context->float_array_map, (yyvsp[-3].string_value)), (yyvsp[-1].integer_value)); } -#line 1917 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1918 "./ortools/flatzinc/parser.tab.cc" break; case 60: -#line 457 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 457 "./ortools/flatzinc/parser.yy" { (yyval.domain) = Domain::IntegerValue((yyvsp[0].integer_value)); } -#line 1923 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1924 "./ortools/flatzinc/parser.tab.cc" break; case 61: -#line 458 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 458 "./ortools/flatzinc/parser.yy" { (yyval.domain) = Domain::Interval((yyvsp[-2].integer_value), (yyvsp[0].integer_value)); } -#line 1929 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1930 "./ortools/flatzinc/parser.tab.cc" break; case 62: -#line 459 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 459 "./ortools/flatzinc/parser.yy" { CHECK((yyvsp[-1].integers) != nullptr); (yyval.domain) = Domain::IntegerList(std::move(*(yyvsp[-1].integers))); delete (yyvsp[-1].integers); } -#line 1939 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1940 "./ortools/flatzinc/parser.tab.cc" break; case 63: -#line 464 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 464 "./ortools/flatzinc/parser.yy" { (yyval.domain) = Domain::EmptyDomain(); } -#line 1945 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1946 "./ortools/flatzinc/parser.tab.cc" break; case 64: -#line 465 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 465 "./ortools/flatzinc/parser.yy" { CHECK_EQ(std::round((yyvsp[0].double_value)), (yyvsp[0].double_value)); (yyval.domain) = Domain::IntegerValue(static_cast((yyvsp[0].double_value))); } -#line 1954 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1955 "./ortools/flatzinc/parser.tab.cc" break; case 65: -#line 469 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 469 "./ortools/flatzinc/parser.yy" { (yyval.domain) = Domain::IntegerValue( gtl::FindOrDie(context->integer_map, (yyvsp[0].string_value))); } -#line 1960 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1961 "./ortools/flatzinc/parser.tab.cc" break; case 66: -#line 470 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 470 "./ortools/flatzinc/parser.yy" { (yyval.domain) = Domain::IntegerValue(Lookup( gtl::FindOrDie(context->integer_array_map, (yyvsp[-3].string_value)), (yyvsp[-1].integer_value))); } -#line 1969 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1970 "./ortools/flatzinc/parser.tab.cc" break; case 67: -#line 476 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 476 "./ortools/flatzinc/parser.yy" { (yyval.domains) = (yyvsp[-2].domains); (yyval.domains)->emplace_back((yyvsp[0].domain)); } -#line 1978 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1979 "./ortools/flatzinc/parser.tab.cc" break; case 68: -#line 480 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 480 "./ortools/flatzinc/parser.yy" { (yyval.domains) = new std::vector(); (yyval.domains)->emplace_back((yyvsp[0].domain)); } -#line 1984 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 1985 "./ortools/flatzinc/parser.tab.cc" break; case 71: -#line 490 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 490 "./ortools/flatzinc/parser.yy" { const std::string& identifier = (yyvsp[-4].string_value); CHECK((yyvsp[-2].args) != nullptr) << "Missing argument in constraint"; @@ -1970,73 +1971,73 @@ yyreduce: delete annotations; delete (yyvsp[-2].args); } -#line 2014 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2015 "./ortools/flatzinc/parser.tab.cc" break; case 72: -#line 517 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 517 "./ortools/flatzinc/parser.yy" { (yyval.args) = (yyvsp[-2].args); (yyval.args)->emplace_back((yyvsp[0].arg)); } -#line 2020 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2021 "./ortools/flatzinc/parser.tab.cc" break; case 73: -#line 518 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 518 "./ortools/flatzinc/parser.yy" { (yyval.args) = new std::vector(); (yyval.args)->emplace_back((yyvsp[0].arg)); } -#line 2026 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2027 "./ortools/flatzinc/parser.tab.cc" break; case 74: -#line 521 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 521 "./ortools/flatzinc/parser.yy" { (yyval.arg) = Argument::IntegerValue((yyvsp[0].integer_value)); } -#line 2032 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2033 "./ortools/flatzinc/parser.tab.cc" break; case 75: -#line 522 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 522 "./ortools/flatzinc/parser.yy" { (yyval.arg) = Argument::IntegerValue( ConvertAsIntegerOrDie((yyvsp[0].double_value))); } -#line 2038 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2039 "./ortools/flatzinc/parser.tab.cc" break; case 76: -#line 523 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 523 "./ortools/flatzinc/parser.yy" { (yyval.arg) = Argument::VoidArgument(); } -#line 2044 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2045 "./ortools/flatzinc/parser.tab.cc" break; case 77: -#line 524 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 524 "./ortools/flatzinc/parser.yy" { (yyval.arg) = Argument::Interval((yyvsp[-2].integer_value), (yyvsp[0].integer_value)); } -#line 2050 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2051 "./ortools/flatzinc/parser.tab.cc" break; case 78: -#line 525 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 525 "./ortools/flatzinc/parser.yy" { CHECK((yyvsp[-1].integers) != nullptr); (yyval.arg) = Argument::IntegerList(std::move(*(yyvsp[-1].integers))); delete (yyvsp[-1].integers); } -#line 2060 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2061 "./ortools/flatzinc/parser.tab.cc" break; case 79: -#line 530 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 530 "./ortools/flatzinc/parser.yy" { const std::string& id = (yyvsp[0].string_value); if (gtl::ContainsKey(context->integer_map, id)) { @@ -2074,11 +2075,11 @@ yyreduce: (yyval.arg) = Argument::DomainList(d); } } -#line 2096 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2097 "./ortools/flatzinc/parser.tab.cc" break; case 80: -#line 561 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 561 "./ortools/flatzinc/parser.yy" { const std::string& id = (yyvsp[-3].string_value); const int64 index = (yyvsp[-1].integer_value); @@ -2096,11 +2097,11 @@ yyreduce: (yyval.arg) = Argument::FromDomain(d); } } -#line 2118 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2119 "./ortools/flatzinc/parser.tab.cc" break; case 81: -#line 578 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 578 "./ortools/flatzinc/parser.yy" { VariableRefOrValueArray* const arguments = (yyvsp[-1].var_or_value_array); CHECK(arguments != nullptr); @@ -2127,81 +2128,81 @@ yyreduce: } delete arguments; } -#line 2148 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2149 "./ortools/flatzinc/parser.tab.cc" break; case 82: -#line 603 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 603 "./ortools/flatzinc/parser.yy" { (yyval.arg) = Argument::VoidArgument(); } -#line 2156 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2157 "./ortools/flatzinc/parser.tab.cc" break; case 83: -#line 612 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 612 "./ortools/flatzinc/parser.yy" { (yyval.annotations) = (yyvsp[-2].annotations) != nullptr ? (yyvsp[-2].annotations) : new std::vector(); (yyval.annotations)->emplace_back((yyvsp[0].annotation)); } -#line 2165 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2166 "./ortools/flatzinc/parser.tab.cc" break; case 84: -#line 616 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 616 "./ortools/flatzinc/parser.yy" { (yyval.annotations) = nullptr; } -#line 2171 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2172 "./ortools/flatzinc/parser.tab.cc" break; case 85: -#line 619 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 619 "./ortools/flatzinc/parser.yy" { (yyval.annotations) = (yyvsp[-2].annotations); (yyval.annotations)->emplace_back((yyvsp[0].annotation)); } -#line 2177 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2178 "./ortools/flatzinc/parser.tab.cc" break; case 86: -#line 620 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 620 "./ortools/flatzinc/parser.yy" { (yyval.annotations) = new std::vector(); (yyval.annotations)->emplace_back((yyvsp[0].annotation)); } -#line 2183 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2184 "./ortools/flatzinc/parser.tab.cc" break; case 87: -#line 623 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 623 "./ortools/flatzinc/parser.yy" { (yyval.annotation) = Annotation::Interval((yyvsp[-2].integer_value), (yyvsp[0].integer_value)); } -#line 2189 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2190 "./ortools/flatzinc/parser.tab.cc" break; case 88: -#line 624 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 624 "./ortools/flatzinc/parser.yy" { (yyval.annotation) = Annotation::IntegerValue((yyvsp[0].integer_value)); } -#line 2195 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2196 "./ortools/flatzinc/parser.tab.cc" break; case 89: -#line 625 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 625 "./ortools/flatzinc/parser.yy" { (yyval.annotation) = Annotation::String((yyvsp[0].string_value)); } -#line 2201 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2202 "./ortools/flatzinc/parser.tab.cc" break; case 90: -#line 626 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 626 "./ortools/flatzinc/parser.yy" { const std::string& id = (yyvsp[0].string_value); if (gtl::ContainsKey(context->variable_map, id)) { @@ -2214,11 +2215,11 @@ yyreduce: (yyval.annotation) = Annotation::Identifier(id); } } -#line 2216 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2217 "./ortools/flatzinc/parser.tab.cc" break; case 91: -#line 636 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 636 "./ortools/flatzinc/parser.yy" { std::vector* const annotations = (yyvsp[-1].annotations); if (annotations != nullptr) { @@ -2229,11 +2230,11 @@ yyreduce: (yyval.annotation) = Annotation::FunctionCall((yyvsp[-3].string_value)); } } -#line 2230 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2231 "./ortools/flatzinc/parser.tab.cc" break; case 92: -#line 645 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 645 "./ortools/flatzinc/parser.yy" { CHECK(gtl::ContainsKey(context->variable_array_map, (yyvsp[-3].string_value))) @@ -2242,11 +2243,11 @@ yyreduce: gtl::FindOrDie(context->variable_array_map, (yyvsp[-3].string_value)), (yyvsp[-1].integer_value))); } -#line 2241 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2242 "./ortools/flatzinc/parser.tab.cc" break; case 93: -#line 651 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 651 "./ortools/flatzinc/parser.yy" { std::vector* const annotations = (yyvsp[-1].annotations); if (annotations != nullptr) { @@ -2257,11 +2258,11 @@ yyreduce: (yyval.annotation) = Annotation::Empty(); } } -#line 2255 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2256 "./ortools/flatzinc/parser.tab.cc" break; case 94: -#line 666 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 666 "./ortools/flatzinc/parser.yy" { if ((yyvsp[-1].annotations) != nullptr) { model->Satisfy(std::move(*(yyvsp[-1].annotations))); @@ -2270,11 +2271,11 @@ yyreduce: model->Satisfy(std::vector()); } } -#line 2268 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2269 "./ortools/flatzinc/parser.tab.cc" break; case 95: -#line 674 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 674 "./ortools/flatzinc/parser.yy" { CHECK_EQ(Argument::INT_VAR_REF, (yyvsp[0].arg).type); if ((yyvsp[-2].annotations) != nullptr) { @@ -2285,11 +2286,11 @@ yyreduce: model->Minimize((yyvsp[0].arg).Var(), std::vector()); } } -#line 2282 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2283 "./ortools/flatzinc/parser.tab.cc" break; case 96: -#line 683 "./ortools/flatzinc/parser.yy" /* yacc.c:1652 */ +#line 683 "./ortools/flatzinc/parser.yy" { CHECK_EQ(Argument::INT_VAR_REF, (yyvsp[0].arg).type); if ((yyvsp[-2].annotations) != nullptr) { @@ -2300,10 +2301,11 @@ yyreduce: model->Maximize((yyvsp[0].arg).Var(), std::vector()); } } -#line 2296 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2297 "./ortools/flatzinc/parser.tab.cc" break; -#line 2300 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1652 */ +#line 2301 "./ortools/flatzinc/parser.tab.cc" + default: break; } @@ -2501,4 +2503,4 @@ yyreturn: #endif return yyresult; } -#line 693 "./ortools/flatzinc/parser.yy" /* yacc.c:1918 */ +#line 693 "./ortools/flatzinc/parser.yy" diff --git a/ortools/flatzinc/parser.tab.hh b/ortools/flatzinc/parser.tab.hh index 59639989b8..2aff83cfbc 100644 --- a/ortools/flatzinc/parser.tab.hh +++ b/ortools/flatzinc/parser.tab.hh @@ -1,4 +1,4 @@ -/* A Bison parser, made by GNU Bison 3.3.2. */ +/* A Bison parser, made by GNU Bison 3.4.1. */ /* Bison interface for Yacc-like parsers in C @@ -52,7 +52,7 @@ extern int orfz_debug; #endif /* "%code requires" blocks. */ -#line 21 "./ortools/flatzinc/parser.yy" /* yacc.c:1921 */ +#line 21 "./ortools/flatzinc/parser.yy" #if !defined(OR_TOOLS_FLATZINC_FLATZINC_TAB_HH_) #define OR_TOOLS_FLATZINC_FLATZINC_TAB_HH_ @@ -68,7 +68,7 @@ typedef operations_research::fz::LexerInfo YYSTYPE; #endif // OR_TOOLS_FLATZINC_FLATZINC_TAB_HH_ -#line 72 "./ortools/flatzinc/parser.tab.hh" /* yacc.c:1921 */ +#line 72 "./ortools/flatzinc/parser.tab.hh" /* Token type. */ #ifndef ORFZ_TOKENTYPE diff --git a/ortools/flatzinc/parser.yy b/ortools/flatzinc/parser.yy index 6642a5bc46..0dc92b285f 100644 --- a/ortools/flatzinc/parser.yy +++ b/ortools/flatzinc/parser.yy @@ -59,7 +59,7 @@ using operations_research::fz::VariableRefOrValueArray; // Type declarations. // The lexer, defined in the ./flazinc.lex file, does the low-level parsing -// of std::string tokens and converts each of them them into a YACC token. A YACC +// of string tokens and converts each of them them into a YACC token. A YACC // token has a type (VAR, IVALUE, const_literal) and optionally a value, // stored in a token-specific field of a LexerInfo instance dedicated to this // token. Eg. "26" is converted into an YACC token of type IVALUE, with value diff --git a/ortools/flatzinc/parser.yy.cc b/ortools/flatzinc/parser.yy.cc index c08f78c1c1..a8d3198add 100644 --- a/ortools/flatzinc/parser.yy.cc +++ b/ortools/flatzinc/parser.yy.cc @@ -720,7 +720,7 @@ static const flex_int32_t yy_rule_can_match_eol[32] = { #define isatty _isatty #endif #line 769 "./ortools/flatzinc/parser.yy.cc" -/* Rules that parse the bottom-line std::string tokens of a .fz file and +/* Rules that parse the bottom-line string tokens of a .fz file and convert them into YACC tokens, which may carry a value. See the LexerInfo struct and the %token declarations in ./parser.yy. */ #line 773 "./ortools/flatzinc/parser.yy.cc" diff --git a/ortools/flatzinc/parser_util.h b/ortools/flatzinc/parser_util.h index a293f6f3cb..baf29e23fa 100644 --- a/ortools/flatzinc/parser_util.h +++ b/ortools/flatzinc/parser_util.h @@ -83,7 +83,7 @@ struct VariableRefOrValueArray { }; // Class needed to pass information from the lexer to the parser. -// TODO(user): Use std::unique_ptr> to ease memory management. +// TODO(user): Use std::unique_ptr> to ease memory management. struct LexerInfo { int64 integer_value; double double_value; diff --git a/ortools/glop/lu_factorization.h b/ortools/glop/lu_factorization.h index b91465854f..86fed82403 100644 --- a/ortools/glop/lu_factorization.h +++ b/ortools/glop/lu_factorization.h @@ -191,7 +191,7 @@ class LuFactorization { markowitz_.SetParameters(parameters); } - // Returns a std::string containing the statistics for this class. + // Returns a string containing the statistics for this class. std::string StatString() const { return stats_.StatString() + markowitz_.StatString(); } diff --git a/ortools/glop/markowitz.h b/ortools/glop/markowitz.h index 74c72f0205..94d22ff139 100644 --- a/ortools/glop/markowitz.h +++ b/ortools/glop/markowitz.h @@ -301,7 +301,7 @@ class Markowitz { // Releases the memory used by this class. void Clear(); - // Returns a std::string containing the statistics for this class. + // Returns a string containing the statistics for this class. std::string StatString() const { return stats_.StatString(); } // Sets the current parameters. diff --git a/ortools/glop/preprocessor.cc b/ortools/glop/preprocessor.cc index 24546fd39f..ae5e1d091d 100644 --- a/ortools/glop/preprocessor.cc +++ b/ortools/glop/preprocessor.cc @@ -13,6 +13,8 @@ #include "ortools/glop/preprocessor.h" +#include + #include "absl/strings/str_format.h" #include "ortools/glop/revised_simplex.h" #include "ortools/glop/status.h" @@ -27,7 +29,7 @@ namespace glop { using ::util::Reverse; namespace { -// Returns an interval as an human readable std::string for debugging. +// Returns an interval as an human readable string for debugging. std::string IntervalString(Fractional lb, Fractional ub) { return absl::StrFormat("[%g, %g]", lb, ub); } @@ -1160,8 +1162,19 @@ bool ForcingAndImpliedFreeConstraintPreprocessor::Run(LinearProgram* lp) { if (is_forcing_down[e.row()]) { const Fractional candidate = e.coefficient() < 0.0 ? lower : upper; if (is_forced && candidate != target_bound) { + // The bounds are really close, so we fix to the bound with + // the lowest magnitude. As of 2019/11/19, this is "better" than + // fixing to the mid-point, because at postsolve, we always put + // non-basic variables to their exact bounds (so, with mid-point + // there would be a difference of epsilon/2 between the inner + // solution and the postsolved one, which might cause issues). + if (IsSmallerWithinPreprocessorZeroTolerance(upper, lower)) { + target_bound = std::abs(lower) < std::abs(upper) ? lower : upper; + continue; + } VLOG(1) << "A variable is forced in both directions! bounds: [" - << lower << ", " << upper << "]. coeff:" << e.coefficient(); + << std::fixed << std::setprecision(10) << lower << ", " + << upper << "]. coeff:" << e.coefficient(); status_ = ProblemStatus::PRIMAL_INFEASIBLE; return false; } @@ -1171,8 +1184,15 @@ bool ForcingAndImpliedFreeConstraintPreprocessor::Run(LinearProgram* lp) { if (is_forcing_up_[e.row()]) { const Fractional candidate = e.coefficient() < 0.0 ? upper : lower; if (is_forced && candidate != target_bound) { + // The bounds are really close, so we fix to the bound with + // the lowest magnitude. + if (IsSmallerWithinPreprocessorZeroTolerance(upper, lower)) { + target_bound = std::abs(lower) < std::abs(upper) ? lower : upper; + continue; + } VLOG(1) << "A variable is forced in both directions! bounds: [" - << lower << ", " << upper << "]. coeff:" << e.coefficient(); + << std::fixed << std::setprecision(10) << lower << ", " + << upper << "]. coeff:" << e.coefficient(); status_ = ProblemStatus::PRIMAL_INFEASIBLE; return false; } diff --git a/ortools/glop/primal_edge_norms.h b/ortools/glop/primal_edge_norms.h index 00a8400350..51e4cc4f8f 100644 --- a/ortools/glop/primal_edge_norms.h +++ b/ortools/glop/primal_edge_norms.h @@ -111,7 +111,7 @@ class PrimalEdgeNorms { parameters_ = parameters; } - // Returns a std::string with statistics about this class. + // Returns a string with statistics about this class. std::string StatString() const { return stats_.StatString(); } // Deterministic time used by the scalar product computation of this class. diff --git a/ortools/glop/revised_simplex.cc b/ortools/glop/revised_simplex.cc index d03189a7c3..1e1970254a 100644 --- a/ortools/glop/revised_simplex.cc +++ b/ortools/glop/revised_simplex.cc @@ -877,7 +877,7 @@ void RevisedSimplex::InitializeObjectiveLimit(const LinearProgram& lp) { const Fractional shifted_limit = limit / objective_scaling_factor_ - objective_offset_; - // The std::isfinite() test is there to avoid generating NaNs with clang in + // The isfinite() test is there to avoid generating NaNs with clang in // fast-math mode on iOS 9.3.i. if (set_dual) { dual_objective_limit_ = std::isfinite(shifted_limit) @@ -2964,9 +2964,8 @@ std::string StringifyMonomialWithFlags(const Fractional a, return StringifyMonomial(a, x, FLAGS_simplex_display_numbers_as_fractions); } -// Returns a std::string representing the rational approximation of x or a -// decimal approximation of x according to -// FLAGS_simplex_display_numbers_as_fractions. +// Returns a string representing the rational approximation of x or a decimal +// approximation of x according to FLAGS_simplex_display_numbers_as_fractions. std::string StringifyWithFlags(const Fractional x) { return Stringify(x, FLAGS_simplex_display_numbers_as_fractions); } diff --git a/ortools/glop/revised_simplex.h b/ortools/glop/revised_simplex.h index d7cfb0f197..4f3ffe4659 100644 --- a/ortools/glop/revised_simplex.h +++ b/ortools/glop/revised_simplex.h @@ -230,7 +230,7 @@ class RevisedSimplex { const BasisFactorization& GetBasisFactorization() const; - // Returns statistics about this class as a std::string. + // Returns statistics about this class as a string. std::string StatString(); // Computes the dictionary B^-1*N on-the-fly row by row. Returns the resulting @@ -252,8 +252,8 @@ class RevisedSimplex { // constructor though. void PropagateParameters(); - // Returns a std::string containing the same information as with - // GetSolverStats, but in a much more human-readable format. For example: + // Returns a string containing the same information as with GetSolverStats, + // but in a much more human-readable format. For example: // Problem status : Optimal // Solving time : 1.843 // Number of iterations : 12345 @@ -266,12 +266,11 @@ class RevisedSimplex { // Stop after first basis : 0 std::string GetPrettySolverStats() const; - // Returns a std::string containing formatted information about the variable + // Returns a string containing formatted information about the variable // corresponding to column col. std::string SimpleVariableInfo(ColIndex col) const; - // Displays a short std::string with the current iteration and objective - // value. + // Displays a short string with the current iteration and objective value. void DisplayIterationInfo() const; // Displays the error bounds of the current solution. diff --git a/ortools/glop/status.h b/ortools/glop/status.h index bdcfe2fe00..0c53eb63ac 100644 --- a/ortools/glop/status.h +++ b/ortools/glop/status.h @@ -63,7 +63,7 @@ class Status { std::string error_message_; }; -// Returns the std::string representation of the ErrorCode enum. +// Returns the string representation of the ErrorCode enum. std::string GetErrorCodeString(Status::ErrorCode error_code); // Macro to simplify error propagation between function returning Status. diff --git a/ortools/glop/update_row.h b/ortools/glop/update_row.h index b164432cc5..6a09d6409e 100644 --- a/ortools/glop/update_row.h +++ b/ortools/glop/update_row.h @@ -77,7 +77,7 @@ class UpdateRow { // Sets the algorithm parameters. void SetParameters(const GlopParameters& parameters); - // Returns statistics about this class as a std::string. + // Returns statistics about this class as a string. std::string StatString() const { return stats_.StatString(); } // Only used for testing. diff --git a/ortools/graph/christofides.h b/ortools/graph/christofides.h index a5df97882f..a5c27f98e9 100644 --- a/ortools/graph/christofides.h +++ b/ortools/graph/christofides.h @@ -57,7 +57,6 @@ class ChristofidesPathSolver { // By default, MINIMAL_WEIGHT_MATCHING is selected. // TODO(user): Change the default when minimum matching gets faster. void SetMatchingAlgorithm(MatchingAlgorithm matching) { - // TODO(user): Change the default when minimum matching gets faster. matching_ = matching; } @@ -131,12 +130,10 @@ ComputeMinimumWeightMatching(const GraphType& graph, } #if defined(USE_CBC) || defined(USE_SCIP) +// Computes a minimum weight perfect matching on an undirected graph using a +// Mixed Integer Programming model. // TODO(user): Handle infeasible cases if this algorithm is used outside of // Christofides. -/** - * Computes a minimum weight perfect matching on an undirected graph using a - * Mixed Integer Programming model. - */ template std::vector< std::pair> diff --git a/ortools/graph/cliques.h b/ortools/graph/cliques.h index 70ee6f140a..acbbf45784 100644 --- a/ortools/graph/cliques.h +++ b/ortools/graph/cliques.h @@ -81,7 +81,8 @@ enum class BronKerboschAlgorithmStatus { // // Typical usage: // auto graph = [](int node1, int node2) { return true; }; -// auto on_clique = [](const std::vector& clique) { LOG(INFO) << "Clique!"; +// auto on_clique = [](const std::vector& clique) { +// LOG(INFO) << "Clique!"; // }; // // BronKerboschAlgorithm bron_kerbosch(graph, num_nodes, on_clique); @@ -320,7 +321,7 @@ class BronKerboschAlgorithm { // pivot. CandidateIndex SelectCandidateIndexForRecursion(State* state); - // Returns a human-readable std::string representation of the clique. + // Returns a human-readable string representation of the clique. std::string CliqueDebugString(const std::vector& clique); // The callback called when the algorithm needs to determine if (node1, node2) diff --git a/ortools/graph/connected_components.h b/ortools/graph/connected_components.h index 3a6db9d67a..8c14ce2fdd 100644 --- a/ortools/graph/connected_components.h +++ b/ortools/graph/connected_components.h @@ -67,7 +67,7 @@ namespace util { // the one that has the lowest-index node that isn't in component #0, and so on. // // Example on the following 6-node graph: 5--3--0--1 2--4 -// std::vector> graph = {{1, 3}, {0}, {4}, {0, 5}, {2}, {3}}; +// vector> graph = {{1, 3}, {0}, {4}, {0, 5}, {2}, {3}}; // GetConnectedComponents(graph); // returns [0, 0, 1, 0, 1, 0]. template std::vector GetConnectedComponents(int num_nodes, @@ -161,7 +161,7 @@ struct ConnectedComponentsTypeHelper { // ... 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; +// vector > components; // cc.FindConnectedComponents(&components); // Each entry in components now contains all the nodes in a single // connected component. @@ -172,7 +172,7 @@ struct ConnectedComponentsTypeHelper { // ConnectedComponentType::hasher> // cc; // ... -// std::vector components; +// vector components; // cc.FindConnectedComponents(&components); // // If you want to, you can continue adding nodes and edges after calling diff --git a/ortools/graph/connectivity.h b/ortools/graph/connectivity.h index 5753fb5cad..7acf32cba6 100644 --- a/ortools/graph/connectivity.h +++ b/ortools/graph/connectivity.h @@ -56,7 +56,7 @@ namespace operations_research { // } // // Group the nodes in the same connected component together. // // group[class_number][i] contains the i-th node in group class_number. -// hash_map > group(num_connected_components); +// hash_map > group(num_connected_components); // for (int node = 0; node < num_nodes; ++node) { // group[components.GetClassRepresentative(node)].push_back(node); // } diff --git a/ortools/graph/ebert_graph.h b/ortools/graph/ebert_graph.h index a23565c73d..3b5eee3116 100644 --- a/ortools/graph/ebert_graph.h +++ b/ortools/graph/ebert_graph.h @@ -797,7 +797,7 @@ class ForwardStaticGraph } } - // Returns a debug std::string containing all the information contained in the + // Returns a debug string containing all the information contained in the // data structure in raw form. std::string DebugString() const { std::string result = "Arcs:(node) :\n"; @@ -1453,7 +1453,7 @@ class EbertGraph representation_clean_ = true; } - // Returns a debug std::string containing all the information contained in the + // Returns a debug string containing all the information contained in the // data structure in raw form. std::string DebugString() const { DCHECK(representation_clean_); @@ -1693,7 +1693,7 @@ class ForwardEbertGraph return true; } - // Returns a debug std::string containing all the information contained in the + // Returns a debug string containing all the information contained in the // data structure in raw form. std::string DebugString() const { DCHECK(representation_clean_); diff --git a/ortools/graph/graph.h b/ortools/graph/graph.h index f6ad620c41..cca98451f3 100644 --- a/ortools/graph/graph.h +++ b/ortools/graph/graph.h @@ -86,14 +86,14 @@ // Graph graph(num_nodes, arc_capacity); // // Storing and using node annotations: -// std::vector is_visited(graph.num_nodes(), false); +// vector is_visited(graph.num_nodes(), false); // ... // for (int node = 0; node < graph.num_nodes(); ++node) { // if (!is_visited[node]) ... // } // // Storing and using arc annotations: -// std::vector weights; +// vector weights; // for (...) { // graph.AddArc(tail, head); // weights.push_back(arc_weight); @@ -106,14 +106,14 @@ // More efficient version: // typedef StaticGraph<> Graph; // Graph graph(num_nodes, arc_capacity); // Optional, but help memory usage. -// std::vector weights; +// vector weights; // weights.reserve(arc_capacity); // Optional, but help memory usage. // for (...) { // graph.AddArc(tail, head); // weights.push_back(arc_weight); // } // ... -// std::vector permutation; +// vector permutation; // graph.Build(&permutation); // A static graph must be Build() before usage. // Permute(permutation, &weights); // Build() may permute the arc index. // ... @@ -742,7 +742,7 @@ void Permute(const IntVector& permutation, Array* array_to_permute) { (*array_to_permute)[0]); } -// We need a specialization for std::vector, because the default code uses +// We need a specialization for vector, because the default code uses // (*array_to_permute)[0] as ElementType, which isn't 'bool' in that case. template void Permute(const IntVector& permutation, diff --git a/ortools/graph/iterators.h b/ortools/graph/iterators.h index 4c74d60f8c..1997bcd42a 100644 --- a/ortools/graph/iterators.h +++ b/ortools/graph/iterators.h @@ -151,7 +151,7 @@ class IntegerRange : public BeginEndWrapper> { IntegerRangeIterator(end)) {} }; -// Allow iterating over a std::vector as a mutable std::vector. +// Allow iterating over a vector as a mutable vector. template struct MutableVectorIteration { explicit MutableVectorIteration(std::vector* v) : v_(v) {} diff --git a/ortools/graph/max_flow.cc b/ortools/graph/max_flow.cc index 9761a0ee9d..3f96edd207 100644 --- a/ortools/graph/max_flow.cc +++ b/ortools/graph/max_flow.cc @@ -826,7 +826,7 @@ void GenericMaxFlow::RefineWithGlobalUpdate() { // worse. The behavior of this heuristic is related to the Gap // heuristic. // - // Note that the global update will fix all such cases efficently. So + // Note that the global update will fix all such cases efficiently. So // the idea is to discharge the active node as much as possible, and // then do a global update. // diff --git a/ortools/graph/perfect_matching.cc b/ortools/graph/perfect_matching.cc index 0f99900cc3..f163810929 100644 --- a/ortools/graph/perfect_matching.cc +++ b/ortools/graph/perfect_matching.cc @@ -107,11 +107,11 @@ using NodeIndex = BlossomGraph::NodeIndex; using CostValue = BlossomGraph::CostValue; const BlossomGraph::NodeIndex BlossomGraph::kNoNodeIndex = - BlossomGraph::NodeIndex(-1); + BlossomGraph::NodeIndex(-1); const BlossomGraph::EdgeIndex BlossomGraph::kNoEdgeIndex = - BlossomGraph::EdgeIndex(-1); + BlossomGraph::EdgeIndex(-1); const BlossomGraph::CostValue BlossomGraph::kMaxCostValue = - BlossomGraph::CostValue(kint64max); + BlossomGraph::CostValue(kint64max); BlossomGraph::BlossomGraph(int num_nodes) { graph_.resize(num_nodes); diff --git a/ortools/graph/python/graph.i b/ortools/graph/python/graph.i index 3cc3cb495f..350587364b 100644 --- a/ortools/graph/python/graph.i +++ b/ortools/graph/python/graph.i @@ -30,6 +30,8 @@ %include "ortools/base/base.i" +%include "ortools/util/python/functions.i" + %import "ortools/util/python/vector.i" %import "ortools/graph/ebert_graph.h" diff --git a/ortools/graph/strongly_connected_components.h b/ortools/graph/strongly_connected_components.h index d9245c4f7e..85a5ccc575 100644 --- a/ortools/graph/strongly_connected_components.h +++ b/ortools/graph/strongly_connected_components.h @@ -23,11 +23,11 @@ // // SIMPLE EXAMPLE: // -// Fill a std::vector> graph; representing your graph adjacency -// lists. That is, graph[i] contains the nodes adjacent to node #i. The nodes -// must be integers in [0, num_nodes). Then just do: +// Fill a vector> graph; representing your graph adjacency lists. +// That is, graph[i] contains the nodes adjacent to node #i. The nodes must be +// integers in [0, num_nodes). Then just do: // -// std::vector> components; +// vector> components; // FindStronglyConnectedComponents( // static_cast(graph.size()), graph, &components); // @@ -75,10 +75,9 @@ void FindStronglyConnectedComponents(const NodeIndex num_nodes, // A simple custom output class that just counts the number of SCC. Not // allocating many vectors can save both space and speed if your graph is large. // -// Note: If this matters, you probably don't want to use -// std::vector> as an input either. See StaticGraph in -// ortools/graph/graph.h for an efficient graph data structure compatible with -// this algorithm. +// Note: If this matters, you probably don't want to use vector> as +// an input either. See StaticGraph in ortools/graph/graph.h +// for an efficient graph data structure compatible with this algorithm. template struct SccCounterOutput { int number_of_components = 0; @@ -86,8 +85,7 @@ struct SccCounterOutput { ++number_of_components; } // This is just here so this class can transparently replace a code that - // use std::vector> as an SccOutput, and get its size with - // size(). + // use vector> as an SccOutput, and get its size with size(). int size() const { return number_of_components; } }; diff --git a/ortools/linear_solver/clp_interface.cc b/ortools/linear_solver/clp_interface.cc index bedd9d3b0e..cb7c11b9ca 100644 --- a/ortools/linear_solver/clp_interface.cc +++ b/ortools/linear_solver/clp_interface.cc @@ -274,8 +274,8 @@ void CLPInterface::CreateDummyVariableForEmptyConstraints() { clp_->setColumnBounds(kDummyVariableIndex, 0.0, 0.0); clp_->setObjectiveCoefficient(kDummyVariableIndex, 0.0); // Workaround for peculiar signature of setColumnName. Note that we do need - // std::string here, and not 'std::string', which aren't the same as of - // 2013-12 (this will change later). + // std::string here, and not 'string', which aren't the same as of 2013-12 + // (this will change later). std::string dummy = "dummy"; // We do need to create this temporary variable. clp_->setColumnName(kDummyVariableIndex, dummy); } diff --git a/ortools/linear_solver/glop_interface.cc b/ortools/linear_solver/glop_interface.cc index 8214a023be..d9dd01edcd 100644 --- a/ortools/linear_solver/glop_interface.cc +++ b/ortools/linear_solver/glop_interface.cc @@ -415,7 +415,7 @@ bool GLOPInterface::SetSolverSpecificParametersAsString( const std::string& parameters) { // 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 + // specific parameters from string on Android, we first need to switch to // non-lite version of protocol buffers. if (ProtobufTextFormatMergeFromString(parameters, ¶meters_)) { lp_solver_.SetParameters(parameters_); diff --git a/ortools/linear_solver/linear_solver.cc b/ortools/linear_solver/linear_solver.cc index 4f73a8f3a9..41e17d8774 100644 --- a/ortools/linear_solver/linear_solver.cc +++ b/ortools/linear_solver/linear_solver.cc @@ -415,11 +415,6 @@ int NumDigits(int n) { return static_cast(std::max(1.0, log10(static_cast(n)) + 1.0)); #endif } - -MPSolver::OptimizationProblemType DetourProblemType( - MPSolver::OptimizationProblemType problem_type) { - return problem_type; -} } // namespace MPSolver::MPSolver(const std::string& name, @@ -1667,7 +1662,7 @@ bool MPSolverInterface::SetSolverSpecificParametersAsString( // 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. Gurobi) need to re-parse the parameters every time they do - // Solve(). We just store the parameters std::string anyway. + // Solve(). We just store the parameters 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 diff --git a/ortools/linear_solver/model_exporter.cc b/ortools/linear_solver/model_exporter.cc index c9162e292e..b96214fba3 100644 --- a/ortools/linear_solver/model_exporter.cc +++ b/ortools/linear_solver/model_exporter.cc @@ -99,7 +99,7 @@ class MPModelProtoExporter { std::string* output) const; // Same as AppendMpsLineHeader. Appends an extra new-line at the end the - // std::string pointed to by output. + // string pointed to by output. void AppendMpsLineHeaderWithNewLine(const std::string& id, const std::string& name, std::string* output) const; @@ -313,11 +313,11 @@ class LineBreaker { // Lines are broken in such a way that: // - Strings that are given to Append() are never split. // - Lines are split so that their length doesn't exceed the max length; - // unless a single std::string given to Append() exceeds that length (in - // which case it will be put alone on a single unsplit line). + // unless a single string given to Append() exceeds that length (in which + // case it will be put alone on a single unsplit line). void Append(const std::string& s); - // Returns true if std::string s will fit on the current line without adding + // Returns true if string s will fit on the current line without adding // a carriage return. bool WillFit(const std::string& s) { return line_size_ + s.size() < max_line_size_; diff --git a/ortools/linear_solver/model_exporter.h b/ortools/linear_solver/model_exporter.h index 9f8c482762..6a420321bb 100644 --- a/ortools/linear_solver/model_exporter.h +++ b/ortools/linear_solver/model_exporter.h @@ -48,9 +48,9 @@ struct MPModelExportOptions { }; /** - * Outputs the current model (variables, constraints, objective) as a - * std::string encoded in the so-called "CPLEX LP file format" as generated by - * SCIP. The LP file format is easily readable by a human. + * Outputs the current model (variables, constraints, objective) as a string + * encoded in the so-called "CPLEX LP file format" as generated by SCIP. + * The LP file format is easily readable by a human. * * Returns false if some error has occurred during execution. * The validity of names is automatically checked. If a variable name or a @@ -73,8 +73,8 @@ util::StatusOr ExportModelAsLpFormat( const MPModelExportOptions& options = MPModelExportOptions()); /** - * Outputs the current model (variables, constraints, objective) as a - * std::string encoded in MPS file format, using the "free" MPS format. + * Outputs the current model (variables, constraints, objective) as a string + * encoded in MPS file format, using the "free" MPS format. * * Returns false if some error has occurred during execution. Models with * maximization objectives trigger an error, because MPS can encode only diff --git a/ortools/linear_solver/model_validator.cc b/ortools/linear_solver/model_validator.cc index a50ae2b9a8..98a5f8e222 100644 --- a/ortools/linear_solver/model_validator.cc +++ b/ortools/linear_solver/model_validator.cc @@ -96,7 +96,7 @@ std::string FindDuplicateVarIndex(const Iterable& var_indices, } // Internal method to detect errors in a single constraint. -// "var_mask" is a std::vector whose size is the number of variables in +// "var_mask" is a vector whose size is the number of variables in // the model, and it will be all set to false before and after the call. std::string FindErrorInMPConstraint(const MPConstraintProto& constraint, std::vector* var_mask) { diff --git a/ortools/linear_solver/model_validator.h b/ortools/linear_solver/model_validator.h index 6f588e40d4..330ee32505 100644 --- a/ortools/linear_solver/model_validator.h +++ b/ortools/linear_solver/model_validator.h @@ -22,12 +22,12 @@ namespace operations_research { /** - * Returns an empty std::string iff the model is valid and not trivially - * infeasible. Otherwise, returns a description of the first error or trivial - * infeasibility encountered. + * Returns an empty string iff the model is valid and not trivially infeasible. + * Otherwise, returns a description of the first error or trivial infeasibility + * encountered. * * NOTE(user): the code of this method (and the client code too!) is - * considerably simplified by this std::string-based, simple API. If clients + * considerably simplified by this string-based, simple API. If clients * require it, we could add a formal error status enum. */ std::string FindErrorInMPModelProto(const MPModelProto& model); @@ -58,8 +58,8 @@ bool ExtractValidMPModelInPlaceOrPopulateResponseStatus( MPModelRequest* request, MPSolutionResponse* response); /** - * Returns an empty std::string if the solution hint given in the model is a - * feasible solution. Otherwise, returns a description of the first reason for + * Returns an empty string if the solution hint given in the model is a feasible + * solution. Otherwise, returns a description of the first reason for * infeasibility. * * This function can be useful for debugging/checking that the given solution diff --git a/ortools/linear_solver/python/linear_solver.i b/ortools/linear_solver/python/linear_solver.i index a0296a8cf2..fc15025c90 100644 --- a/ortools/linear_solver/python/linear_solver.i +++ b/ortools/linear_solver/python/linear_solver.i @@ -50,6 +50,7 @@ class MPSolutionResponse; #include "ortools/linear_solver/linear_solver.h" #include "ortools/linear_solver/model_exporter.h" #include "ortools/linear_solver/model_exporter_swig_helper.h" +#include "ortools/linear_solver/model_validator.h" %} %pythoncode %{ @@ -448,10 +449,18 @@ PY_CONVERT(MPVariable); %rename (ExportModelAsLpFormat) operations_research::ExportModelAsLpFormatReturnString; %rename (ExportModelAsMpsFormat) operations_research::ExportModelAsMpsFormatReturnString; +// Expose the model validator. +%rename (FindErrorInModelProto) operations_research::FindErrorInMPModelProto; + %include "ortools/linear_solver/linear_solver.h" %include "ortools/linear_solver/model_exporter.h" %include "ortools/linear_solver/model_exporter_swig_helper.h" +namespace operations_research { + std::string FindErrorInMPModelProto( + const operations_research::MPModelProto& input_model); +} // namespace operations_research + %unignoreall %pythoncode { diff --git a/ortools/linear_solver/samples/integer_programming_example.py b/ortools/linear_solver/samples/integer_programming_example.py index bc8d4f68c8..18354bf20f 100644 --- a/ortools/linear_solver/samples/integer_programming_example.py +++ b/ortools/linear_solver/samples/integer_programming_example.py @@ -15,7 +15,6 @@ from __future__ import print_function # [START import] from ortools.linear_solver import pywraplp - # [END import] diff --git a/ortools/linear_solver/samples/simple_lp_program.py b/ortools/linear_solver/samples/simple_lp_program.py index 68912fe76d..0f4b4561f1 100644 --- a/ortools/linear_solver/samples/simple_lp_program.py +++ b/ortools/linear_solver/samples/simple_lp_program.py @@ -15,7 +15,6 @@ # [START import] from __future__ import print_function from ortools.linear_solver import pywraplp - # [END import] diff --git a/ortools/linear_solver/samples/simple_mip_program.py b/ortools/linear_solver/samples/simple_mip_program.py index d6598ace74..167b5584db 100644 --- a/ortools/linear_solver/samples/simple_mip_program.py +++ b/ortools/linear_solver/samples/simple_mip_program.py @@ -15,7 +15,6 @@ # [START import] from __future__ import print_function from ortools.linear_solver import pywraplp - # [END import] diff --git a/ortools/linear_solver/sat_interface.cc b/ortools/linear_solver/sat_interface.cc index 851facfbba..dc4881f4fe 100644 --- a/ortools/linear_solver/sat_interface.cc +++ b/ortools/linear_solver/sat_interface.cc @@ -137,9 +137,9 @@ MPSolver::ResultStatus SatInterface::Solve(const MPSolverParameters& param) { MPModelRequest request; solver_->ExportModelToProto(request.mutable_model()); // If sat::SatParameters is compiled with proto-lite (go/mobile-cpp-protos), - // then serialize as non-human readable std::string. This is because - // proto-lite does not support reflection mechanism, which is a prerequisite - // for method like `ShortDebugString`. + // then serialize as non-human readable string. This is because proto-lite + // does not support reflection mechanism, which is a prerequisite for method + // like `ShortDebugString`. if (!std::is_base_of::value) { request.set_solver_specific_parameters(parameters_.SerializeAsString()); } else { diff --git a/ortools/linear_solver/sat_proto_solver.cc b/ortools/linear_solver/sat_proto_solver.cc index 57e6af5f8f..b8a805dffa 100644 --- a/ortools/linear_solver/sat_proto_solver.cc +++ b/ortools/linear_solver/sat_proto_solver.cc @@ -64,8 +64,7 @@ util::StatusOr SatSolveProto( params.set_log_search_progress(request.enable_internal_solver_output()); if (request.has_solver_specific_parameters()) { // If code is compiled with proto-lite runtime, `solver_specific_parameters` - // should be encoded as non-human readable std::string from - // `SerializeAsString`. + // should be encoded as non-human readable string from `SerializeAsString`. if (!std::is_base_of::value) { CHECK(params.MergeFromString(request.solver_specific_parameters())); } else { diff --git a/ortools/lp_data/lp_data.h b/ortools/lp_data/lp_data.h index 7200443ef1..6195bcf107 100644 --- a/ortools/lp_data/lp_data.h +++ b/ortools/lp_data/lp_data.h @@ -26,7 +26,7 @@ #include // for max #include -#include // for std::string +#include // for string #include // for vector #include "absl/container/flat_hash_map.h" @@ -292,7 +292,7 @@ class LinearProgram { Fractional ApplyObjectiveScalingAndOffset(Fractional value) const; Fractional RemoveObjectiveScalingAndOffset(Fractional value) const; - // A short std::string with the problem dimension. + // A short string with the problem dimension. std::string GetDimensionString() const; // A short line with some stats on the objective coefficients. @@ -302,12 +302,12 @@ class LinearProgram { // lp_solve (see http://lpsolve.sourceforge.net/5.1/index.htm). std::string Dump() const; - // Returns a std::string that contains the provided solution of the LP in the + // Returns a string that contains the provided solution of the LP in the // format var1 = X, var2 = Y, var3 = Z, ... std::string DumpSolution(const DenseRow& variable_values) const; - // Returns a comma-separated std::string of integers containing (in that - // order) num_constraints_, num_variables_in_file_, num_entries_, + // Returns a comma-separated string of integers containing (in that order) + // num_constraints_, num_variables_in_file_, num_entries_, // num_objective_non_zeros_, num_rhs_non_zeros_, num_less_than_constraints_, // num_greater_than_constraints_, num_equal_constraints_, // num_range_constraints_, num_non_negative_variables_, num_boxed_variables_, @@ -315,8 +315,8 @@ class LinearProgram { // Very useful for reporting in the way used in journal articles. std::string GetProblemStats() const; - // Returns a std::string containing the same information as with - // GetProblemStats(), but in a much more human-readable form, for example: + // Returns a string containing the same information as with GetProblemStats(), + // but in a much more human-readable form, for example: // Number of rows : 27 // Number of variables in file : 32 // Number of entries (non-zeros) : 83 @@ -333,7 +333,7 @@ class LinearProgram { // Number of other variables : 0 std::string GetPrettyProblemStats() const; - // Returns a comma-separated std::string of numbers containing (in that order) + // Returns a comma-separated string of numbers containing (in that order) // fill rate, max number of entries (length) in a row, average row length, // standard deviation of row length, max column length, average column length, // standard deviation of column length @@ -343,8 +343,8 @@ class LinearProgram { // moved to SparseMatrix. std::string GetNonZeroStats() const; - // Returns a std::string containing the same information as with - // GetNonZeroStats(), but in a much more human-readable form, for example: + // Returns a string containing the same information as with GetNonZeroStats(), + // but in a much more human-readable form, for example: // Fill rate : 9.61% // Entries in row (Max / average / std, dev.) : 9 / 3.07 / 1.94 // Entries in column (Max / average / std, dev.): 4 / 2.59 / 0.96 diff --git a/ortools/lp_data/lp_print_utils.cc b/ortools/lp_data/lp_print_utils.cc index a94936ecc0..29587249ec 100644 --- a/ortools/lp_data/lp_print_utils.cc +++ b/ortools/lp_data/lp_print_utils.cc @@ -26,7 +26,7 @@ namespace operations_research { namespace glop { -// Returns a std::string "num/den" representing the rational approximation of x. +// Returns a string "num/den" representing the rational approximation of x. // The absolute difference between the output fraction and the input "x" will // not exceed "precision". std::string StringifyRational(const double x, const double precision) { @@ -48,7 +48,7 @@ std::string Stringify(const Fractional x, bool fraction) { : Stringify(x); } -// Returns a std::string that pretty-prints a monomial ax with coefficient +// Returns a string that pretty-prints a monomial ax with coefficient // a and variable name x std::string StringifyMonomial(const Fractional a, const std::string& x, bool fraction) { diff --git a/ortools/lp_data/lp_print_utils.h b/ortools/lp_data/lp_print_utils.h index 22987898d0..2d46ef35f2 100644 --- a/ortools/lp_data/lp_print_utils.h +++ b/ortools/lp_data/lp_print_utils.h @@ -25,7 +25,7 @@ namespace operations_research { namespace glop { -// Returns a std::string representing a floating-point number in decimal, +// Returns a string representing a floating-point number in decimal, // with a precision corresponding to the type of the argument. inline std::string Stringify(const float a) { return absl::StrFormat("%.7g", a); @@ -39,12 +39,12 @@ inline std::string Stringify(const long double a) { return absl::StrFormat("%.19g", a); } -// Returns a std::string "num/den" representing the rational approximation of x. +// Returns a string "num/den" representing the rational approximation of x. // The absolute difference between the output fraction and the input "x" will // not exceed "precision". std::string StringifyRational(const double x, const double precision); -// If fraction is true, returns a std::string corresponding to the rational +// If fraction is true, returns a string corresponding to the rational // approximation or a decimal approximation otherwise. Note that the absolute // difference between the output fraction and "x" will never exceed // std::numeric_limits::epsilon(). diff --git a/ortools/lp_data/lp_types.h b/ortools/lp_data/lp_types.h index 15823dd6e4..ef6f3edc2d 100644 --- a/ortools/lp_data/lp_types.h +++ b/ortools/lp_data/lp_types.h @@ -162,7 +162,7 @@ enum class ProblemStatus : int8 { IMPRECISE, }; -// Returns the std::string representation of the ProblemStatus enum. +// Returns the string representation of the ProblemStatus enum. std::string GetProblemStatusString(ProblemStatus problem_status); inline std::ostream& operator<<(std::ostream& os, ProblemStatus status) { @@ -179,7 +179,7 @@ enum class VariableType : int8 { FIXED_VARIABLE }; -// Returns the std::string representation of the VariableType enum. +// Returns the string representation of the VariableType enum. std::string GetVariableTypeString(VariableType variable_type); inline std::ostream& operator<<(std::ostream& os, VariableType type) { @@ -210,7 +210,7 @@ enum class VariableStatus : int8 { FREE, }; -// Returns the std::string representation of the VariableStatus enum. +// Returns the string representation of the VariableStatus enum. std::string GetVariableStatusString(VariableStatus status); inline std::ostream& operator<<(std::ostream& os, VariableStatus status) { @@ -232,7 +232,7 @@ enum class ConstraintStatus : int8 { FREE, }; -// Returns the std::string representation of the ConstraintStatus enum. +// Returns the string representation of the ConstraintStatus enum. std::string GetConstraintStatusString(ConstraintStatus status); inline std::ostream& operator<<(std::ostream& os, ConstraintStatus status) { diff --git a/ortools/lp_data/matrix_scaler.h b/ortools/lp_data/matrix_scaler.h index 6ec108d3fb..cd5be114d2 100644 --- a/ortools/lp_data/matrix_scaler.h +++ b/ortools/lp_data/matrix_scaler.h @@ -167,7 +167,7 @@ class SparseMatrixScaler { // As above, but for the columns of the matrix. ColIndex GetColumnScaleIndex(ColIndex col_num); - // Returns a std::string containing information on the progress of the scaling + // Returns a string containing information on the progress of the scaling // algorithm. This is not meant to be called in an optimized mode as it takes // some time to compute the displayed quantities. std::string DebugInformationString() const; diff --git a/ortools/lp_data/mps_reader.cc b/ortools/lp_data/mps_reader.cc index b2e5615d56..809379fb86 100644 --- a/ortools/lp_data/mps_reader.cc +++ b/ortools/lp_data/mps_reader.cc @@ -106,8 +106,8 @@ class MPSReaderImpl { // Process section SOS in the MPS file. util::Status ProcessSosSection(); - // Safely converts a std::string to a numerical type. Returns an error if the - // std::string passed as parameter is ill-formed. + // Safely converts a string to a numerical type. Returns an error if the + // string passed as parameter is ill-formed. util::StatusOr GetDoubleFromString(const std::string& str); util::StatusOr GetBoolFromString(const std::string& str); @@ -330,13 +330,11 @@ class DataWrapper { } void SetConstraintCoefficient(int row_index, int col_index, double coefficient) { + // Note that we assume that there is no duplicate in the mps file format. If + // there is, we will just add more than one entry from the same variable in + // a constraint, and we let any program that ingests an MPModelProto handle + // it. MPConstraintProto* const constraint = data_->mutable_constraint(row_index); - for (int i = 0; i < constraint->var_index_size(); ++i) { - if (constraint->var_index(i) == col_index) { - constraint->set_coefficient(i, coefficient); - return; - } - } constraint->add_var_index(col_index); constraint->add_coefficient(coefficient); } diff --git a/ortools/lp_data/permutation.h b/ortools/lp_data/permutation.h index ce6ccaae43..4a027f151c 100644 --- a/ortools/lp_data/permutation.h +++ b/ortools/lp_data/permutation.h @@ -14,7 +14,7 @@ #ifndef OR_TOOLS_LP_DATA_PERMUTATION_H_ #define OR_TOOLS_LP_DATA_PERMUTATION_H_ -#include "ortools/base/random.h" +#include "absl/random/random.h" #include "ortools/lp_data/lp_types.h" #include "ortools/util/return_macros.h" diff --git a/ortools/port/utf8.h b/ortools/port/utf8.h index a801699525..044e884bc9 100644 --- a/ortools/port/utf8.h +++ b/ortools/port/utf8.h @@ -23,7 +23,7 @@ namespace operations_research { namespace utf8 { -// str_type should be std::string/StringPiece/Cord +// str_type should be string/StringPiece/Cord template int UTF8StrLen(StrType str_type) { #if defined(__PORTABLE_PLATFORM__) diff --git a/ortools/sat/boolean_problem.cc b/ortools/sat/boolean_problem.cc index d0c8fd8a32..b9e3cd4102 100644 --- a/ortools/sat/boolean_problem.cc +++ b/ortools/sat/boolean_problem.cc @@ -60,7 +60,7 @@ namespace { // Used by BooleanProblemIsValid() to test that there is no duplicate literals, // that they are all within range and that there is no zero coefficient. // -// A non-empty std::string indicates an error. +// A non-empty string indicates an error. template std::string ValidateLinearTerms(const LinearTerms& terms, std::vector* variable_seen) { diff --git a/ortools/sat/boolean_problem.h b/ortools/sat/boolean_problem.h index ba92e16693..0f62f9ab85 100644 --- a/ortools/sat/boolean_problem.h +++ b/ortools/sat/boolean_problem.h @@ -94,7 +94,7 @@ bool IsAssignmentValid(const LinearBooleanProblem& problem, // Converts a LinearBooleanProblem to the cnf file format. // Note that this only works for pure SAT problems (only clauses), max-sat or -// weighted max-sat problems. Returns an empty std::string on error. +// weighted max-sat problems. Returns an empty string on error. std::string LinearBooleanProblemToCnfString( const LinearBooleanProblem& problem); diff --git a/ortools/sat/clause.h b/ortools/sat/clause.h index 166cbcf14b..923e443c02 100644 --- a/ortools/sat/clause.h +++ b/ortools/sat/clause.h @@ -43,8 +43,8 @@ namespace operations_research { namespace sat { // 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. But -// in the critical propagation code, we use this class to remove one memory +// literals. In many places, we just use vector to encode one. But in +// the critical propagation code, we use this class to remove one memory // indirection. class SatClause { public: diff --git a/ortools/sat/cp_model.h b/ortools/sat/cp_model.h index b2d9dd3112..73e6e08f1b 100644 --- a/ortools/sat/cp_model.h +++ b/ortools/sat/cp_model.h @@ -86,7 +86,7 @@ class BoolVar { return other.cp_model_ != cp_model_ || other.index_ != index_; } - /// Debug std::string. + /// Debug string. std::string DebugString() const; /// Returns the underlying protobuf object (useful for testing). @@ -151,7 +151,7 @@ class IntVar { /// Sets the name of the variable. IntVar WithName(const std::string& name); - /// Returns the name of the variable (or the empty std::string if not set). + /// Returns the name of the variable (or the empty string if not set). const std::string& Name() const { return Proto().name(); } /// Equality test with another IntVar. @@ -164,7 +164,7 @@ class IntVar { return other.cp_model_ != cp_model_ || other.index_ != index_; } - /// Returns a debug std::string. + /// Returns a debug string. std::string DebugString() const; /// Returns the underlying protobuf object (useful for testing). @@ -320,7 +320,7 @@ class IntervalVar { /// Sets the name of the variable. IntervalVar WithName(const std::string& name); - /// Returns the name of the interval (or the empty std::string if not set). + /// Returns the name of the interval (or the empty string if not set). std::string Name() const; /// Returns the start variable. @@ -349,7 +349,7 @@ class IntervalVar { return other.cp_model_ != cp_model_ || other.index_ != index_; } - /// Returns a debug std::string. + /// Returns a debug string. std::string DebugString() const; /// Returns the underlying protobuf object (useful for testing). @@ -415,7 +415,7 @@ class Constraint { /// Sets the name of the constraint. Constraint WithName(const std::string& name); - /// Returns the name of the constraint (or the empty std::string if not set). + /// Returns the name of the constraint (or the empty string if not set). const std::string& Name() const; /// Returns the underlying protobuf object (useful for testing). diff --git a/ortools/sat/cp_model_checker.cc b/ortools/sat/cp_model_checker.cc index d4c321e5b8..daeba5be2c 100644 --- a/ortools/sat/cp_model_checker.cc +++ b/ortools/sat/cp_model_checker.cc @@ -37,7 +37,7 @@ namespace { // CpModelProto validation. // ============================================================================= -// If the std::string returned by "statement" is not empty, returns it. +// If the string returned by "statement" is not empty, returns it. #define RETURN_IF_NOT_EMPTY(statement) \ do { \ const std::string error_message = statement; \ diff --git a/ortools/sat/cp_model_checker.h b/ortools/sat/cp_model_checker.h index 524c4b407e..a1dee179c7 100644 --- a/ortools/sat/cp_model_checker.h +++ b/ortools/sat/cp_model_checker.h @@ -24,9 +24,8 @@ namespace operations_research { namespace sat { // Verifies that the given model satisfies all the properties described in the -// proto comments. Returns an empty std::string if it is the case, otherwise -// fails at the first error and returns a human-readable description of the -// issue. +// proto comments. Returns an empty string if it is the case, otherwise fails at +// the first error and returns a human-readable description of the issue. // // TODO(user): Add any needed overflow validation because we are far from // exhaustive. We could also run a small presolve that tighten variable bounds diff --git a/ortools/sat/cp_model_loader.cc b/ortools/sat/cp_model_loader.cc index 9452a7f42e..3e2e74dd72 100644 --- a/ortools/sat/cp_model_loader.cc +++ b/ortools/sat/cp_model_loader.cc @@ -200,13 +200,14 @@ void CpModelMapping::CreateVariables(const CpModelProto& model_proto, integers_.resize(num_proto_variables, kNoIntegerVariable); auto* integer_trail = m->GetOrCreate(); + integer_trail->ReserveSpaceForNumVariables( + var_to_instantiate_as_integer.size()); + reverse_integer_map_.resize(2 * var_to_instantiate_as_integer.size(), -1); for (const int i : var_to_instantiate_as_integer) { const auto& var_proto = model_proto.variables(i); integers_[i] = integer_trail->AddIntegerVariable(ReadDomainFromProto(var_proto)); - if (integers_[i] >= reverse_integer_map_.size()) { - reverse_integer_map_.resize(integers_[i].value() + 1, -1); - } + DCHECK_LT(integers_[i], reverse_integer_map_.size()); reverse_integer_map_[integers_[i]] = i; } @@ -1060,16 +1061,9 @@ void LoadLinearConstraint(const ConstraintProto& ct, Model* m) { auto* mapping = m->GetOrCreate(); auto* integer_trail = m->GetOrCreate(); - auto* encoder = m->GetOrCreate(); const std::vector vars = mapping->Integers(ct.linear().vars()); const std::vector coeffs = ValuesFromProto(ct.linear().coeffs()); - IntegerValue max_domain_size(0); - for (int i = 0; i < vars.size(); ++i) { - const IntegerValue vmin = integer_trail->LowerBound(vars[i]); - const IntegerValue vmax = integer_trail->UpperBound(vars[i]); - max_domain_size = std::max(max_domain_size, vmax - vmin + 1); - } // Compute the min/max to relax the bounds if needed. // @@ -1077,52 +1071,56 @@ void LoadLinearConstraint(const ConstraintProto& ct, Model* m) { // to detect if we only have Booleans. IntegerValue min_sum(0); IntegerValue max_sum(0); + IntegerValue max_domain_size(0); bool all_booleans = true; for (int i = 0; i < vars.size(); ++i) { if (all_booleans && !mapping->IsBoolean(ct.linear().vars(i))) { all_booleans = false; } - const IntegerValue term_a = coeffs[i] * integer_trail->LowerBound(vars[i]); - const IntegerValue term_b = coeffs[i] * integer_trail->UpperBound(vars[i]); + const IntegerValue lb = integer_trail->LowerBound(vars[i]); + const IntegerValue ub = integer_trail->UpperBound(vars[i]); + max_domain_size = std::max(max_domain_size, ub - lb + 1); + const IntegerValue term_a = coeffs[i] * lb; + const IntegerValue term_b = coeffs[i] * ub; min_sum += std::min(term_a, term_b); max_sum += std::max(term_a, term_b); } - const SatParameters& params = *m->GetOrCreate(); - if (params.boolean_encoding_level() > 0 && ct.linear().vars_size() == 2 && - ConstraintIsEq(ct.linear()) && ct.linear().domain(0) != min_sum && - ct.linear().domain(0) != max_sum && - encoder->VariableIsFullyEncoded(vars[0]) && - encoder->VariableIsFullyEncoded(vars[1]) && - !integer_trail->IsFixed(vars[0]) && !integer_trail->IsFixed(vars[1]) && - max_domain_size < 16) { - VLOG(3) << "Load AC version of " << ct.DebugString() << ", var0 domain = " - << integer_trail->InitialVariableDomain(vars[0]) - << ", var1 domain = " - << integer_trail->InitialVariableDomain(vars[1]); - return LoadEquivalenceAC(mapping->Literals(ct.enforcement_literal()), - IntegerValue(coeffs[0]), vars[0], - IntegerValue(coeffs[1]), vars[1], - IntegerValue(ct.linear().domain(0)), m); - } + if (ct.linear().vars_size() == 2 && !integer_trail->IsFixed(vars[0]) && + !integer_trail->IsFixed(vars[1]) && max_domain_size < 16) { + const SatParameters& params = *m->GetOrCreate(); + auto* encoder = m->GetOrCreate(); + if (params.boolean_encoding_level() > 0 && ConstraintIsEq(ct.linear()) && + ct.linear().domain(0) != min_sum && ct.linear().domain(0) != max_sum && + encoder->VariableIsFullyEncoded(vars[0]) && + encoder->VariableIsFullyEncoded(vars[1])) { + VLOG(3) << "Load AC version of " << ct.DebugString() << ", var0 domain = " + << integer_trail->InitialVariableDomain(vars[0]) + << ", var1 domain = " + << integer_trail->InitialVariableDomain(vars[1]); + return LoadEquivalenceAC(mapping->Literals(ct.enforcement_literal()), + IntegerValue(coeffs[0]), vars[0], + IntegerValue(coeffs[1]), vars[1], + IntegerValue(ct.linear().domain(0)), m); + } - int64 single_value = 0; - if (params.boolean_encoding_level() > 0 && ct.linear().vars_size() == 2 && - ConstraintIsNEq(ct.linear(), mapping, integer_trail, &single_value) && - single_value != min_sum && single_value != max_sum && - encoder->VariableIsFullyEncoded(vars[0]) && - encoder->VariableIsFullyEncoded(vars[1]) && - !integer_trail->IsFixed(vars[0]) && !integer_trail->IsFixed(vars[1]) && - max_domain_size < 16) { - VLOG(3) << "Load NAC version of " << ct.DebugString() << ", var0 domain = " - << integer_trail->InitialVariableDomain(vars[0]) - << ", var1 domain = " - << integer_trail->InitialVariableDomain(vars[1]) - << ", value = " << single_value; - return LoadEquivalenceNeqAC(mapping->Literals(ct.enforcement_literal()), - IntegerValue(coeffs[0]), vars[0], - IntegerValue(coeffs[1]), vars[1], - IntegerValue(single_value), m); + int64 single_value = 0; + if (params.boolean_encoding_level() > 0 && + ConstraintIsNEq(ct.linear(), mapping, integer_trail, &single_value) && + single_value != min_sum && single_value != max_sum && + encoder->VariableIsFullyEncoded(vars[0]) && + encoder->VariableIsFullyEncoded(vars[1])) { + VLOG(3) << "Load NAC version of " << ct.DebugString() + << ", var0 domain = " + << integer_trail->InitialVariableDomain(vars[0]) + << ", var1 domain = " + << integer_trail->InitialVariableDomain(vars[1]) + << ", value = " << single_value; + return LoadEquivalenceNeqAC(mapping->Literals(ct.enforcement_literal()), + IntegerValue(coeffs[0]), vars[0], + IntegerValue(coeffs[1]), vars[1], + IntegerValue(single_value), m); + } } if (ct.linear().domain_size() == 2) { diff --git a/ortools/sat/cp_model_presolve.cc b/ortools/sat/cp_model_presolve.cc index 31c38c0bea..84549e827f 100644 --- a/ortools/sat/cp_model_presolve.cc +++ b/ortools/sat/cp_model_presolve.cc @@ -846,6 +846,27 @@ bool CpModelPresolver::CanonicalizeLinear(ConstraintProto* ct) { const int64 coeff = RefIsPositive(ref) ? ct->linear().coeffs(i) : -ct->linear().coeffs(i); if (coeff == 0) continue; + + // TODO(user): Avoid the quadratic loop for the corner case of many + // enforcement literal (this should be pretty rare though). + bool removed = false; + for (const int enf : ct->enforcement_literal()) { + if (var == PositiveRef(enf)) { + if (RefIsPositive(enf)) { + // If the constraint is enforced, we can assume the variable is at 1. + sum_of_fixed_terms += coeff; + } else { + // We can assume the variable is at zero. + } + removed = true; + break; + } + } + if (removed) { + context_->UpdateRuleStats("linear: enforcement literal in constraint"); + continue; + } + if (context_->IsFixed(var)) { sum_of_fixed_terms += coeff * context_->MinOf(var); continue; @@ -1335,7 +1356,6 @@ bool CpModelPresolver::PropagateDomainsInLinear(int c, ConstraintProto* ct) { Domain(context_->MaxOf(var)))) { return false; } - context_->UpdateRuleStats("linear: dual fixing"); recanonicalize = true; continue; @@ -1350,7 +1370,6 @@ bool CpModelPresolver::PropagateDomainsInLinear(int c, ConstraintProto* ct) { Domain(context_->MinOf(var)))) { return false; } - context_->UpdateRuleStats("linear: dual fixing"); recanonicalize = true; continue; diff --git a/ortools/sat/cp_model_solver.cc b/ortools/sat/cp_model_solver.cc index 15db399f8e..629db82851 100644 --- a/ortools/sat/cp_model_solver.cc +++ b/ortools/sat/cp_model_solver.cc @@ -117,8 +117,7 @@ namespace sat { namespace { -// Makes the std::string fit in one line by cutting it in the middle if -// necessary. +// Makes the string fit in one line by cutting it in the middle if necessary. std::string Summarize(const std::string& input) { if (input.size() < 105) return input; const int half = 50; diff --git a/ortools/sat/cp_model_solver.h b/ortools/sat/cp_model_solver.h index f7e424754e..384abea3b7 100644 --- a/ortools/sat/cp_model_solver.h +++ b/ortools/sat/cp_model_solver.h @@ -33,10 +33,10 @@ CpSolverResponse Solve(const CpModelProto& model_proto); CpSolverResponse SolveWithParameters(const CpModelProto& model_proto, const SatParameters& params); -/// Returns a std::string with some statistics on the given CpModelProto. +/// Returns a string with some statistics on the given CpModelProto. std::string CpModelStats(const CpModelProto& model); -/// Returns a std::string with some statistics on the solver response. +/// Returns a string with some statistics on the solver response. std::string CpSolverResponseStats(const CpSolverResponse& response); /** @@ -54,8 +54,8 @@ CpSolverResponse SolveCpModel(const CpModelProto& model_proto, Model* model); #if !defined(__PORTABLE_PLATFORM__) /** - * Solves the given CpModelProto with the given sat parameters as std::string in - * JSon format, and returns an instance of CpSolverResponse. + * Solves the given CpModelProto with the given sat parameters as string in JSon + * format, and returns an instance of CpSolverResponse. */ CpSolverResponse SolveWithParameters(const CpModelProto& model_proto, const std::string& params); diff --git a/ortools/sat/integer.cc b/ortools/sat/integer.cc index 3ff6d084b3..09d0638a0b 100644 --- a/ortools/sat/integer.cc +++ b/ortools/sat/integer.cc @@ -217,12 +217,11 @@ Literal IntegerEncoder::GetOrCreateAssociatedLiteral(IntegerLiteral i_lit) { const auto canonicalization = Canonicalize(i_lit); const IntegerLiteral new_lit = canonicalization.first; - if (LiteralIsAssociated(new_lit)) { - return Literal(GetAssociatedLiteral(new_lit)); - } - if (LiteralIsAssociated(canonicalization.second)) { - return Literal(GetAssociatedLiteral(canonicalization.second)).Negated(); - } + + const LiteralIndex index = GetAssociatedLiteral(new_lit); + if (index != kNoLiteralIndex) return Literal(index); + const LiteralIndex n_index = GetAssociatedLiteral(canonicalization.second); + if (n_index != kNoLiteralIndex) return Literal(index).Negated(); ++num_created_variables_; const Literal literal(sat_solver_->NewBooleanVariable(), true); @@ -569,14 +568,25 @@ void IntegerTrail::Untrail(const Trail& trail, int literal_trail_index) { } } +void IntegerTrail::ReserveSpaceForNumVariables(int num_vars) { + // Because we always create both a variable and its negation. + const int size = 2 * num_vars; + vars_.reserve(size); + is_ignored_literals_.reserve(size); + integer_trail_.reserve(size); + domains_->reserve(size); + var_trail_index_cache_.reserve(size); + tmp_var_to_trail_index_in_queue_.reserve(size); +} + IntegerVariable IntegerTrail::AddIntegerVariable(IntegerValue lower_bound, IntegerValue upper_bound) { - CHECK_GE(lower_bound, kMinIntegerValue); - CHECK_LE(lower_bound, kMaxIntegerValue); - CHECK_GE(upper_bound, kMinIntegerValue); - CHECK_LE(upper_bound, kMaxIntegerValue); - CHECK(integer_search_levels_.empty()); - CHECK_EQ(vars_.size(), integer_trail_.size()); + DCHECK_GE(lower_bound, kMinIntegerValue); + DCHECK_LE(lower_bound, kMaxIntegerValue); + DCHECK_GE(upper_bound, kMinIntegerValue); + DCHECK_LE(upper_bound, kMaxIntegerValue); + DCHECK(integer_search_levels_.empty()); + DCHECK_EQ(vars_.size(), integer_trail_.size()); const IntegerVariable i(vars_.size()); is_ignored_literals_.push_back(kNoLiteralIndex); diff --git a/ortools/sat/integer.h b/ortools/sat/integer.h index 75e5e2bca4..7d9f5e740b 100644 --- a/ortools/sat/integer.h +++ b/ortools/sat/integer.h @@ -511,6 +511,10 @@ class IntegerTrail : public SatPropagator { return IntegerVariable(vars_.size()); } + // Optimization: you can call this before calling AddIntegerVariable() + // num_vars time. + void ReserveSpaceForNumVariables(int num_vars); + // Adds a new integer variable. Adding integer variable can only be done when // the decision level is zero (checked). The given bounds are INCLUSIVE. IntegerVariable AddIntegerVariable(IntegerValue lower_bound, diff --git a/ortools/sat/integer_expr.h b/ortools/sat/integer_expr.h index 305ddca498..dad78ce3f9 100644 --- a/ortools/sat/integer_expr.h +++ b/ortools/sat/integer_expr.h @@ -507,8 +507,8 @@ inline std::function WeightedSumNotEqual( // Model-based function to create an IntegerVariable that corresponds to the // given weighted sum of other IntegerVariables. // -// Note that this is templated so that it can seamlessly accept std::vector -// or std::vector. +// Note that this is templated so that it can seamlessly accept vector or +// vector. // // TODO(user): invert the coefficients/vars arguments. template diff --git a/ortools/sat/linear_constraint_manager.cc b/ortools/sat/linear_constraint_manager.cc index c709fc3daa..9adf62ddaf 100644 --- a/ortools/sat/linear_constraint_manager.cc +++ b/ortools/sat/linear_constraint_manager.cc @@ -177,9 +177,11 @@ void LinearConstraintManager::ComputeObjectiveParallelism( CHECK(objective_is_defined_); // lazy computation of objective norm. if (!objective_norm_computed_) { - DivideByGCD(&objective_); - CanonicalizeConstraint(&objective_); - objective_l2_norm_ = ComputeL2Norm(objective_); + double sum = 0.0; + for (const double coeff : dense_objective_coeffs_) { + sum += coeff * coeff; + } + objective_l2_norm_ = std::sqrt(sum); objective_norm_computed_ = true; } CHECK_GT(objective_l2_norm_, 0.0); @@ -193,9 +195,11 @@ void LinearConstraintManager::ComputeObjectiveParallelism( const LinearConstraint& lc = constraint_infos_[ct_index].constraint; double unscaled_objective_parallelism = 0.0; for (int i = 0; i < lc.vars.size(); ++i) { - if (lc.vars[i] < dense_objective_coeffs_.size()) { + const IntegerVariable var = lc.vars[i]; + DCHECK(VariableIsPositive(var)); + if (var < dense_objective_coeffs_.size()) { unscaled_objective_parallelism += - ToDouble(lc.coeffs[i]) * dense_objective_coeffs_[lc.vars[i]]; + ToDouble(lc.coeffs[i]) * dense_objective_coeffs_[var]; } } const double objective_parallelism = @@ -240,7 +244,14 @@ void LinearConstraintManager::SetObjectiveCoefficient(IntegerVariable var, IntegerValue coeff) { if (coeff == IntegerValue(0)) return; objective_is_defined_ = true; - objective_.AddTerm(var, coeff); + if (!VariableIsPositive(var)) { + var = NegationOf(var); + coeff = -coeff; + } + if (var.value() >= dense_objective_coeffs_.size()) { + dense_objective_coeffs_.resize(var.value() + 1, 0.0); + } + dense_objective_coeffs_[var] = ToDouble(coeff); } bool LinearConstraintManager::SimplifyConstraint(LinearConstraint* ct) { @@ -384,7 +395,6 @@ bool LinearConstraintManager::ChangeLp( // We keep any constraints that is already present, and otherwise, we add the // ones that are currently not satisfied by at least "tolerance". const double tolerance = 1e-6; - FillDenseObjectiveCoeffs(); for (ConstraintIndex i(0); i < constraint_infos_.size(); ++i) { if (constraint_infos_[i].permanently_removed) continue; @@ -392,6 +402,7 @@ bool LinearConstraintManager::ChangeLp( if (simplify_constraints && SimplifyConstraint(&constraint_infos_[i].constraint)) { DivideByGCD(&constraint_infos_[i].constraint); + constraint_infos_[i].objective_parallelism_computed = false; constraint_infos_[i].l2_norm = ComputeL2Norm(constraint_infos_[i].constraint); @@ -559,16 +570,5 @@ void LinearConstraintManager::AddAllConstraintsToLp() { } } -void LinearConstraintManager::FillDenseObjectiveCoeffs() { - if (objective_.vars.empty()) return; - DCHECK(std::is_sorted(objective_.vars.begin(), objective_.vars.end())); - const IntegerVariable last_var = objective_.vars.back(); - dense_objective_coeffs_.assign(last_var.value() + 1, 0.0); - for (int i = 0; i < objective_.vars.size(); ++i) { - dense_objective_coeffs_[objective_.vars[i]] = - ToDouble(objective_.coeffs[i]); - } -} - } // namespace sat } // namespace operations_research diff --git a/ortools/sat/linear_constraint_manager.h b/ortools/sat/linear_constraint_manager.h index 785581d817..3e76afdfa8 100644 --- a/ortools/sat/linear_constraint_manager.h +++ b/ortools/sat/linear_constraint_manager.h @@ -129,9 +129,6 @@ class LinearConstraintManager { // Returns true if the terms of the constraint changed. bool SimplifyConstraint(LinearConstraint* ct); - // Helper method to fill in the objective_values_ vector. - void FillDenseObjectiveCoeffs(); - // Helper method to compute objective parallelism for a given constraint. This // also lazily computes objective norm. void ComputeObjectiveParallelism(const ConstraintIndex ct_index); @@ -167,10 +164,11 @@ class LinearConstraintManager { bool objective_is_defined_ = false; bool objective_norm_computed_ = false; - LinearConstraint objective_; double objective_l2_norm_ = 0.0; - // Dense representation of the objective coeffs indexed by variables indices. - // It contains 0.0 where the variables does not appear in the objective. + + // Dense representation of the objective coeffs indexed by positive variables + // indices. It contains 0.0 where the variables does not appear in the + // objective. gtl::ITIVector dense_objective_coeffs_; TimeLimit* time_limit_; diff --git a/ortools/sat/linear_programming_constraint.cc b/ortools/sat/linear_programming_constraint.cc index 24197d9a77..015ef44cf9 100644 --- a/ortools/sat/linear_programming_constraint.cc +++ b/ortools/sat/linear_programming_constraint.cc @@ -22,8 +22,8 @@ #include #include "absl/container/flat_hash_map.h" +#include "absl/numeric/int128.h" #include "ortools/base/commandlineflags.h" -#include "ortools/base/int128.h" #include "ortools/base/int_type_indexed_vector.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" @@ -143,7 +143,7 @@ void LinearProgrammingConstraint::SetObjectiveCoefficient(IntegerVariable ivar, // add all variables to each LP solve and do some "sifting". That can be useful // for TSP for instance where the number of edges is large, but only a small // fraction will be used in the optimal solution. -void LinearProgrammingConstraint::CreateLpFromConstraintManager() { +bool LinearProgrammingConstraint::CreateLpFromConstraintManager() { // Fill integer_lp_. integer_lp_.clear(); infinity_norms_.clear(); @@ -157,6 +157,10 @@ void LinearProgrammingConstraint::CreateLpFromConstraintManager() { new_ct.ub = ct.ub; const int size = ct.vars.size(); IntegerValue infinity_norm(0); + if (ct.lb > ct.ub) { + LOG(INFO) << "Trivial infeasible bound in an LP constraint"; + return false; + } if (ct.lb > kMinIntegerValue) { infinity_norm = std::max(infinity_norm, IntTypeAbs(ct.lb)); } @@ -219,6 +223,7 @@ void LinearProgrammingConstraint::CreateLpFromConstraintManager() { VLOG(1) << "LP relaxation: " << lp_data_.GetDimensionString() << ". " << constraint_manager_.AllConstraints().size() << " Managed constraints."; + return true; } LPSolveInfo LinearProgrammingConstraint::SolveLpForBranching() { @@ -377,7 +382,10 @@ void LinearProgrammingConstraint::RegisterWith(Model* model) { if (!sat_parameters_.add_lp_constraints_lazily()) { constraint_manager_.AddAllConstraintsToLp(); } - CreateLpFromConstraintManager(); + if (!CreateLpFromConstraintManager()) { + model->GetOrCreate()->NotifyThatModelIsUnsat(); + return; + } GenericLiteralWatcher* watcher = model->GetOrCreate(); const int watcher_id = watcher->Register(this); @@ -926,7 +934,9 @@ bool LinearProgrammingConstraint::Propagate() { glop::BasisState state = simplex_.GetState(); if (constraint_manager_.ChangeLp(expanded_lp_solution_, &state)) { simplex_.LoadStateForNextSolve(state); - CreateLpFromConstraintManager(); + if (!CreateLpFromConstraintManager()) { + return integer_trail_->ReportConflict({}); + } const double old_obj = simplex_.GetObjectiveValue(); if (!SolveLp()) return true; if (simplex_.GetProblemStatus() == glop::ProblemStatus::OPTIMAL) { @@ -1311,13 +1321,19 @@ LinearProgrammingConstraint::ScaleLpMultiplier( max_sum += ToDouble(objective_infinity_norm_); } + std::vector> integer_multipliers; + if (max_sum == 0.0) { + // Empty linear combinaison. + *scaling = 1; + return integer_multipliers; + } + // We want max_sum * scaling to be <= 2 ^ max_pow and fit on an int64. *scaling = std::ldexp(1, max_pow) / max_sum; // Scale the multipliers by *scaling. // // TODO(user): Maybe use int128 to avoid overflow? - std::vector> integer_multipliers; for (const auto entry : cp_multipliers) { const IntegerValue coeff(std::round(entry.second * (*scaling))); if (coeff != 0) integer_multipliers.push_back({entry.first, coeff}); diff --git a/ortools/sat/linear_programming_constraint.h b/ortools/sat/linear_programming_constraint.h index b239b836c3..13dc97846d 100644 --- a/ortools/sat/linear_programming_constraint.h +++ b/ortools/sat/linear_programming_constraint.h @@ -178,7 +178,10 @@ class LinearProgrammingConstraint : public PropagatorInterface, // Reinitialize the LP from a potentially new set of constraints. // This fills all data structure and properly rescale the underlying LP. - void CreateLpFromConstraintManager(); + // + // Returns false if the problem is UNSAT (it can happen when presolve is off + // and some LP constraint are trivially false). + bool CreateLpFromConstraintManager(); // Solve the LP, returns false if something went wrong in the LP solver. bool SolveLp(); diff --git a/ortools/sat/model.h b/ortools/sat/model.h index 4b16f3f347..65268b4974 100644 --- a/ortools/sat/model.h +++ b/ortools/sat/model.h @@ -20,6 +20,7 @@ #include #include +#include "absl/container/flat_hash_map.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" #include "ortools/base/map_util.h" @@ -90,14 +91,17 @@ class Model { template T* GetOrCreate() { const size_t type_id = gtl::FastTypeId(); - if (!gtl::ContainsKey(singletons_, type_id)) { - // TODO(user): directly store std::unique_ptr<> in singletons_? - T* new_t = MyNew(0); - singletons_[type_id] = new_t; - TakeOwnership(new_t); - return new_t; + auto find = singletons_.find(type_id); + if (find != singletons_.end()) { + return static_cast(find->second); } - return static_cast(gtl::FindOrDie(singletons_, type_id)); + + // New element. + // TODO(user): directly store std::unique_ptr<> in singletons_? + T* new_t = MyNew(0); + singletons_[type_id] = new_t; + TakeOwnership(new_t); + return new_t; } /** @@ -170,7 +174,7 @@ class Model { } // Map of FastTypeId to a "singleton" of type T. - std::map singletons_; + absl::flat_hash_map singletons_; struct DeleteInterface { virtual ~DeleteInterface() = default; diff --git a/ortools/sat/optimization.cc b/ortools/sat/optimization.cc index 94090d3c56..77967ee0c4 100644 --- a/ortools/sat/optimization.cc +++ b/ortools/sat/optimization.cc @@ -24,6 +24,7 @@ #include #include +#include "absl/random/random.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "ortools/base/cleanup.h" @@ -32,7 +33,6 @@ #include "ortools/base/logging.h" #include "ortools/base/macros.h" #include "ortools/base/map_util.h" -#include "ortools/base/random.h" #include "ortools/base/stl_util.h" #include "ortools/base/timer.h" #if !defined(__PORTABLE_PLATFORM__) && (defined(USE_CBC) || defined(USE_SCIP)) diff --git a/ortools/sat/pb_constraint.h b/ortools/sat/pb_constraint.h index 9b0f670746..cc5c2ef278 100644 --- a/ortools/sat/pb_constraint.h +++ b/ortools/sat/pb_constraint.h @@ -257,7 +257,7 @@ class MutableUpperBoundedLinearConstraint { void ReduceSlackTo(const Trail& trail, int trail_index, Coefficient initial_slack, Coefficient target); - // Copies this constraint into a std::vector representation. + // Copies this constraint into a vector representation. void CopyIntoVector(std::vector* output); // Adds a non-negative value to this constraint Rhs(). @@ -306,7 +306,7 @@ class MutableUpperBoundedLinearConstraint { return non_zeros_.PositionsSetAtLeastOnce(); } - // Returns a std::string representation of the constraint. + // Returns a string representation of the constraint. std::string DebugString(); private: diff --git a/ortools/sat/precedences.cc b/ortools/sat/precedences.cc index 735e2cbc7b..3f031bf250 100644 --- a/ortools/sat/precedences.cc +++ b/ortools/sat/precedences.cc @@ -631,7 +631,7 @@ bool PrecedencesPropagator::BellmanFordTarjan(Trail* trail) { // TODO(user): we don't need bf_can_be_skipped_ since we can detect this // if this node has a parent arc which is not marked. Investigate if it is - // faster without the std::vector. + // faster without the vector. // // TODO(user): An alternative algorithm is to remove all these nodes from // the queue instead of simply marking them. This should also lead to a diff --git a/ortools/sat/presolve_context.cc b/ortools/sat/presolve_context.cc index 11e3149c62..3fcb27b7ad 100644 --- a/ortools/sat/presolve_context.cc +++ b/ortools/sat/presolve_context.cc @@ -349,13 +349,13 @@ AffineRelation::Relation PresolveContext::GetAffineRelation(int ref) const { // Create the internal structure for any new variables in working_model. void PresolveContext::InitializeNewDomains() { + domains.reserve(working_model->variables_size()); for (int i = domains.size(); i < working_model->variables_size(); ++i) { - Domain domain = ReadDomainFromProto(working_model->variables(i)); - if (domain.IsEmpty()) { + domains.emplace_back(ReadDomainFromProto(working_model->variables(i))); + if (domains.back().IsEmpty()) { is_unsat = true; return; } - domains.push_back(domain); if (IsFixed(i)) ExploitFixedDomain(i); } modified_domains.Resize(domains.size()); @@ -560,15 +560,15 @@ bool PresolveContext::CanonicalizeObjective() { // Note that the non-deterministic loop is fine, but because we iterate // one the map while modifying it, it is safer to do a copy rather than to // try to handle that in one pass. - std::vector> entries; + tmp_entries.clear(); for (const auto& entry : objective_map) { - entries.push_back(entry); + tmp_entries.push_back(entry); } // TODO(user): This is a bit duplicated with the presolve linear code. // We also do not propagate back any domain restriction from the objective to // the variables if any. - for (const auto& entry : entries) { + for (const auto& entry : tmp_entries) { const int var = entry.first; const auto it = objective_map.find(var); if (it == objective_map.end()) continue; @@ -626,12 +626,12 @@ bool PresolveContext::CanonicalizeObjective() { int64 gcd(0); // We need to sort the entries to be deterministic. - entries.clear(); + tmp_entries.clear(); for (const auto& entry : objective_map) { - entries.push_back(entry); + tmp_entries.push_back(entry); } - std::sort(entries.begin(), entries.end()); - for (const auto& entry : entries) { + std::sort(tmp_entries.begin(), tmp_entries.end()); + for (const auto& entry : tmp_entries) { const int var = entry.first; const int64 coeff = entry.second; gcd = MathUtil::GCD64(gcd, std::abs(coeff)); @@ -682,8 +682,7 @@ void PresolveContext::SubstituteVariableInObjective( // We can only "easily" substitute if the objective coefficient is a multiple // of the one in the constraint. - const int64 coeff_in_objective = - gtl::FindOrDie(objective_map, var_in_equality); + const int64 coeff_in_objective = gtl::FindOrDie(objective_map, var_in_equality); CHECK_NE(coeff_in_equality, 0); CHECK_EQ(coeff_in_objective % coeff_in_equality, 0); const int64 multiplier = coeff_in_objective / coeff_in_equality; diff --git a/ortools/sat/presolve_context.h b/ortools/sat/presolve_context.h index 7d220dff92..73c4be4495 100644 --- a/ortools/sat/presolve_context.h +++ b/ortools/sat/presolve_context.h @@ -305,6 +305,7 @@ struct PresolveContext { // on large problems (also because the objective is often dense). At the end // we re-convert it to its proto form. absl::flat_hash_map objective_map; + std::vector> tmp_entries; bool objective_domain_is_constraining = false; Domain objective_domain; double objective_offset; diff --git a/ortools/sat/restart.h b/ortools/sat/restart.h index ba553d8301..167c0b3cd9 100644 --- a/ortools/sat/restart.h +++ b/ortools/sat/restart.h @@ -49,7 +49,7 @@ class RestartPolicy { // Returns the number of restarts since the last Reset(). int NumRestarts() const { return num_restarts_; } - // Returns a std::string with the current restart statistics. + // Returns a string with the current restart statistics. std::string InfoString() const; private: diff --git a/ortools/sat/sat_base.h b/ortools/sat/sat_base.h index e4012a27af..b68ae4378d 100644 --- a/ortools/sat/sat_base.h +++ b/ortools/sat/sat_base.h @@ -64,7 +64,7 @@ const LiteralIndex kFalseLiteralIndex(-3); class Literal { public: // Not explicit for tests so we can write: - // std::vector literal = {+1, -3, +4, -9}; + // vector literal = {+1, -3, +4, -9}; Literal(int signed_value) // NOLINT : index_(signed_value > 0 ? ((signed_value - 1) << 1) : ((-signed_value - 1) << 1) ^ 1) { diff --git a/ortools/sat/sat_solver.cc b/ortools/sat/sat_solver.cc index 5fadd86c66..da888dd255 100644 --- a/ortools/sat/sat_solver.cc +++ b/ortools/sat/sat_solver.cc @@ -614,7 +614,7 @@ bool SatSolver::PropagateAndStopAfterOneConflictResolution() { &pb_backjump_level); if (pb_backjump_level == -1) return SetModelUnsat(); - // Convert the conflict into the std::vector form. + // Convert the conflict into the vector form. std::vector cst; pb_conflict_.CopyIntoVector(&cst); DCHECK(PBConstraintIsValidUnderDebugAssignment(cst, pb_conflict_.Rhs())); diff --git a/ortools/sat/sat_solver.h b/ortools/sat/sat_solver.h index cf8c6ac20d..69aa4a5fc6 100644 --- a/ortools/sat/sat_solver.h +++ b/ortools/sat/sat_solver.h @@ -1008,7 +1008,7 @@ inline std::function ExcludeCurrentSolutionAndBacktrack() { }; } -// Returns a std::string representation of a SatSolver::Status. +// Returns a string representation of a SatSolver::Status. std::string SatStatusString(SatSolver::Status status); inline std::ostream& operator<<(std::ostream& os, SatSolver::Status status) { os << SatStatusString(status); diff --git a/ortools/sat/swig_helper.h b/ortools/sat/swig_helper.h index 67ce0d08ce..6023eeec7a 100644 --- a/ortools/sat/swig_helper.h +++ b/ortools/sat/swig_helper.h @@ -153,20 +153,19 @@ class SatHelper { return SolveCpModel(model_proto, &model); } - // Returns a std::string with some statistics on the given CpModelProto. + // Returns a string with some statistics on the given CpModelProto. static std::string ModelStats( const operations_research::sat::CpModelProto& model_proto) { return CpModelStats(model_proto); } - // Returns a std::string with some statistics on the solver response. + // Returns a string with some statistics on the solver response. static std::string SolverResponseStats( const operations_research::sat::CpSolverResponse& response) { return CpSolverResponseStats(response); } - // Returns a non empty std::string explaining the issue if the model is not - // valid. + // Returns a non empty string explaining the issue if the model is not valid. static std::string ValidateModel( const operations_research::sat::CpModelProto& model_proto) { return ValidateCpModel(model_proto); diff --git a/ortools/sat/util.h b/ortools/sat/util.h index e0bcd5ea1c..4b97668661 100644 --- a/ortools/sat/util.h +++ b/ortools/sat/util.h @@ -16,7 +16,7 @@ #include -#include "ortools/base/random.h" +#include "absl/random/random.h" #include "ortools/sat/model.h" #include "ortools/sat/sat_base.h" #include "ortools/sat/sat_parameters.pb.h" diff --git a/ortools/util/bitset.h b/ortools/util/bitset.h index df43cd4a59..ea0aec869c 100644 --- a/ortools/util/bitset.h +++ b/ortools/util/bitset.h @@ -654,7 +654,7 @@ class Bitset64 { ((use2 << pos) & other2.data_[bucket]); } - // Returns a 0/1 std::string representing the bitset. + // Returns a 0/1 string representing the bitset. std::string DebugString() const { std::string output; for (IndexType i(0); i < size(); ++i) { diff --git a/ortools/util/file_util.cc b/ortools/util/file_util.cc index 2606cdb4cb..779c03078b 100644 --- a/ortools/util/file_util.cc +++ b/ortools/util/file_util.cc @@ -65,7 +65,7 @@ bool WriteProtoToFile(absl::string_view filename, break; case ProtoWriteFormat::kProtoText: if (!google::protobuf::TextFormat::PrintToString(proto, &output_string)) { - LOG(WARNING) << "Printing to std::string failed."; + LOG(WARNING) << "Printing to string failed."; return false; } break; diff --git a/ortools/util/file_util.h b/ortools/util/file_util.h index c456411006..635e51d92d 100644 --- a/ortools/util/file_util.h +++ b/ortools/util/file_util.h @@ -25,7 +25,7 @@ namespace operations_research { -// Reads a file, optionally gzipped, to a std::string. +// Reads a file, optionally gzipped, to a string. util::StatusOr ReadFileToString(absl::string_view filename); // Reads a proto from a file. Supports the following formats: binary, text, diff --git a/ortools/util/java/vector.i b/ortools/util/java/vector.i index 7d6f0f6d97..bee14c322d 100644 --- a/ortools/util/java/vector.i +++ b/ortools/util/java/vector.i @@ -120,9 +120,9 @@ VECTOR_AS_JAVA_ARRAY(double, double, Double); if (nullptr == object_class) return $null; jmethodID method_id = - jenv->GetStaticMethodID(object_class, - "getCPtr", - std::string("(L" + java_class_path + ";)J").c_str()); + jenv->GetStaticMethodID( + object_class, "getCPtr", + std::string("(L" + java_class_path + ";)J").c_str()); assert(method_id != nullptr); for (int i = 0; i < jenv->GetArrayLength($input); i++) { jobject elem = jenv->GetObjectArrayElement($input, i); diff --git a/ortools/util/monoid_operation_tree.h b/ortools/util/monoid_operation_tree.h index eb959172fa..99ac708329 100644 --- a/ortools/util/monoid_operation_tree.h +++ b/ortools/util/monoid_operation_tree.h @@ -45,7 +45,7 @@ namespace operations_research { // * Have a = operator method that sets its value to the given one. // * Have a Compute(const T& left, const T& right) method that sets its value // to the result of the binary operation for the two given operands. -// * Have a std::string DebugString() const method. +// * Have a string DebugString() const method. // // Possible use cases are: // * Maintain a sum or a product of doubles, with a guarantee that the queried diff --git a/ortools/util/proto_tools.h b/ortools/util/proto_tools.h index c3bd8a0927..bb0518946a 100644 --- a/ortools/util/proto_tools.h +++ b/ortools/util/proto_tools.h @@ -19,7 +19,7 @@ #include "google/protobuf/message.h" namespace operations_research { -// Prints a proto2 message as a std::string, it behaves like TextFormat::Print() +// Prints a proto2 message as a string, it behaves like TextFormat::Print() // but also prints the default values of unset fields which is useful for // printing parameters. std::string FullProtocolMessageAsString( diff --git a/ortools/util/sorted_interval_list.h b/ortools/util/sorted_interval_list.h index 3ac436e887..0f3f8b2ac3 100644 --- a/ortools/util/sorted_interval_list.h +++ b/ortools/util/sorted_interval_list.h @@ -275,8 +275,7 @@ class Domain { Domain SimplifyUsingImpliedDomain(const Domain& implied_domain) const; /** - * Returns a compact std::string of a vector of intervals like - * "[1,4][6][10,20]". + * Returns a compact string of a vector of intervals like "[1,4][6][10,20]". */ std::string ToString() const; diff --git a/ortools/util/stats.cc b/ortools/util/stats.cc index 6f4b9b026c..66ca89fb79 100644 --- a/ortools/util/stats.cc +++ b/ortools/util/stats.cc @@ -43,9 +43,7 @@ Stat::Stat(const std::string& name, StatsGroup* group) : name_(name) { group->Register(this); } -std::string Stat::StatString() const { - return std::string(name_ + ": " + ValueAsString()); -} +std::string Stat::StatString() const { return name_ + ": " + ValueAsString(); } StatsGroup::~StatsGroup() { gtl::STLDeleteValues(&time_distributions_); } diff --git a/ortools/util/stats.h b/ortools/util/stats.h index 8b3336651b..714379a15a 100644 --- a/ortools/util/stats.h +++ b/ortools/util/stats.h @@ -82,8 +82,7 @@ namespace operations_research { -// Returns the current thread's total memory usage in an human-readable -// std::string. +// Returns the current thread's total memory usage in an human-readable string. std::string MemoryUsage(); // Forward declaration. @@ -140,8 +139,8 @@ class StatsGroup { : name_(name), stats_(), time_distributions_() {} ~StatsGroup(); - // Registers a Stat, which will appear in the std::string returned by - // StatString(). The Stat object must live as long as this StatsGroup. + // Registers a Stat, which will appear in the string returned by StatString(). + // The Stat object must live as long as this StatsGroup. void Register(Stat* stat); // Returns this group name, followed by one line per Stat registered with this diff --git a/ortools/util/string_array.h b/ortools/util/string_array.h index 42f77fe84c..ec1048f981 100644 --- a/ortools/util/string_array.h +++ b/ortools/util/string_array.h @@ -29,8 +29,8 @@ namespace operations_research { } \ return out -// Converts a vector into a std::string by calling the given method (or simply -// getting the given std::string member), on all elements, and concatenating +// Converts a vector into a string by calling the given method (or simply +// getting the given string member), on all elements, and concatenating // the obtained strings with the given separator. // Join v[i].DebugString(). diff --git a/ortools/util/time_limit.h b/ortools/util/time_limit.h index 19ae2b5717..74cb9ce3a7 100644 --- a/ortools/util/time_limit.h +++ b/ortools/util/time_limit.h @@ -74,14 +74,13 @@ namespace operations_research { * small, without aborting too early. * * The deterministic time limit can be logged at a more granular level: the - * method TimeLimit::AdvanceDeterministicTime takes an optional std::string - * argument: the name of a counter. In debug mode, the time limit object - * computes also the elapsed time for each named counter separately, and these - * values can be used to determine the coefficients for computing the - * deterministic duration from the number of operations. The values of the - * counters can be printed using TimeLimit::DebugString(). There is no API to - * access the values of the counters directly, because they do not exist in - * optimized mode. + * method TimeLimit::AdvanceDeterministicTime takes an optional string argument: + * the name of a counter. In debug mode, the time limit object computes also the + * elapsed time for each named counter separately, and these values can be used + * to determine the coefficients for computing the deterministic duration from + * the number of operations. The values of the counters can be printed using + * TimeLimit::DebugString(). There is no API to access the values of the + * counters directly, because they do not exist in optimized mode. * * The basic steps for determining coefficients for the deterministic time are: * 1. Run the code in debug mode to collect the values of the deterministic time