diff --git a/ortools/algorithms/dynamic_partition.cc b/ortools/algorithms/dynamic_partition.cc index 8c974fbd06..6e26046547 100644 --- a/ortools/algorithms/dynamic_partition.cc +++ b/ortools/algorithms/dynamic_partition.cc @@ -293,4 +293,74 @@ std::string MergingPartition::DebugString() { return out; } +void SimpleDynamicPartition::Refine( + absl::Span distinguished_subset) { + // Compute the size of the non-empty intersection of each part with the + // distinguished_subset. + temp_to_clean_.clear(); + std::vector& local_sizes = temp_data_by_part_; + local_sizes.resize(size_of_part_.size(), 0); + for (const int element : distinguished_subset) { + const int part = part_of_[element]; + if (local_sizes[part] == 0) temp_to_clean_.push_back(part); + local_sizes[part]++; + } + + // Reuse local_sizes to store new_part index or zero (no remapping). + // Also update the size of each part. + for (const int part : temp_to_clean_) { + if (local_sizes[part] == size_of_part_[part]) { + // No need to remap if the whole part is in distinguished_subset. + local_sizes[part] = 0; + continue; + } + + const int new_part_index = size_of_part_.size(); + size_of_part_[part] -= local_sizes[part]; + size_of_part_.push_back(local_sizes[part]); + local_sizes[part] = new_part_index; + } + + // For each part not completely included or excluded, split out the element + // from distinguished_subset into a new part. + for (const int element : distinguished_subset) { + const int new_part = local_sizes[part_of_[element]]; + if (new_part != 0) part_of_[element] = new_part; + } + + // Sparse clean. + for (const int part : temp_to_clean_) { + local_sizes[part] = 0; + } +} + +std::vector> SimpleDynamicPartition::GetParts( + std::vector* buffer) { + const int num_elements = part_of_.size(); + const int num_parts = size_of_part_.size(); + buffer->resize(num_elements); + + std::vector> result(num_parts); + if (result.empty()) return result; + + // Compute start of each part in buffer. + std::vector& starts = temp_data_by_part_; + starts.resize(num_parts, 0); + for (int i = 1; i < num_parts; ++i) { + starts[i] = starts[i - 1] + size_of_part_[i - 1]; + } + + // Fill result. + for (int i = 0; i < num_parts; ++i) { + result[i] = absl::MakeSpan(&(*buffer)[starts[i]], size_of_part_[i]); + } + + // Copy elements in order and at their place. + for (int element = 0; element < num_elements; ++element) { + (*buffer)[starts[part_of_[element]]++] = element; + } + starts.clear(); + return result; +} + } // namespace operations_research diff --git a/ortools/algorithms/dynamic_partition.h b/ortools/algorithms/dynamic_partition.h index a3f0c49fa3..362d63ed28 100644 --- a/ortools/algorithms/dynamic_partition.h +++ b/ortools/algorithms/dynamic_partition.h @@ -36,6 +36,8 @@ #include "ortools/base/logging.h" +#include "absl/types/span.h" + namespace operations_research { // Partition class that supports incremental splitting, with backtracking. @@ -272,6 +274,35 @@ class MergingPartition { std::vector tmp_part_bit_; }; +// A subset of the API of DynamicPartition without backtrack support. The +// Refine() here is about twice as fast, but we have limited query support until +// a batch ComputeElementsByPart() is called. +class SimpleDynamicPartition { + public: + explicit SimpleDynamicPartition(int num_elements) + : part_of_(num_elements, 0), + size_of_part_(num_elements > 0 ? 1 : 0, num_elements) {} + + int NumElements() const { return part_of_.size(); } + const int NumParts() const { return size_of_part_.size(); } + int PartOf(int element) const { return part_of_[element]; } + int SizeOfPart(int part) const { return size_of_part_[part]; } + + void Refine(absl::Span distinguished_subset); + + // This is meant to be called once after a bunch of Refine(). + // The returned Span<> points into the given buffer which is re-initialized. + std::vector> GetParts(std::vector* buffer); + + private: + std::vector part_of_; + std::vector size_of_part_; + + // Temp data. Always empty or all zero. + std::vector temp_to_clean_; + std::vector temp_data_by_part_; +}; + // *** Implementation of inline methods of the above classes. *** inline DynamicPartition::IterablePart DynamicPartition::ElementsInPart( diff --git a/ortools/algorithms/java/knapsack_solver.i b/ortools/algorithms/java/knapsack_solver.i index 618cf9377c..0df78dbd2f 100644 --- a/ortools/algorithms/java/knapsack_solver.i +++ b/ortools/algorithms/java/knapsack_solver.i @@ -44,9 +44,10 @@ %rename (setTimeLimit) operations_research::KnapsackSolver::set_time_limit; // untested %unignore operations_research::KnapsackSolver::SolverType; -%unignore operations_research::KnapsackSolver::KNAPSACK_BRUTE_FORCE_SOLVER; // untested -%unignore operations_research::KnapsackSolver::KNAPSACK_64ITEMS_SOLVER; // untested -%unignore operations_research::KnapsackSolver::KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER; // untested +%unignore operations_research::KnapsackSolver::KNAPSACK_BRUTE_FORCE_SOLVER; +%unignore operations_research::KnapsackSolver::KNAPSACK_64ITEMS_SOLVER; +%unignore operations_research::KnapsackSolver::KNAPSACK_DIVIDE_AND_CONQUER_SOLVER; +%unignore operations_research::KnapsackSolver::KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER; %unignore operations_research::KnapsackSolver::KNAPSACK_MULTIDIMENSION_CBC_MIP_SOLVER; // untested %unignore operations_research::KnapsackSolver::KNAPSACK_MULTIDIMENSION_GLPK_MIP_SOLVER; // untested %unignore operations_research::KnapsackSolver::KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER; diff --git a/ortools/algorithms/knapsack_solver.h b/ortools/algorithms/knapsack_solver.h index 8b622a1e41..e18f955000 100644 --- a/ortools/algorithms/knapsack_solver.h +++ b/ortools/algorithms/knapsack_solver.h @@ -20,8 +20,6 @@ #include #include -#include "absl/memory/memory.h" -#include "ortools/base/basictypes.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" diff --git a/ortools/algorithms/python/knapsack_solver.i b/ortools/algorithms/python/knapsack_solver.i index a673cd7666..419b014f5f 100644 --- a/ortools/algorithms/python/knapsack_solver.i +++ b/ortools/algorithms/python/knapsack_solver.i @@ -47,6 +47,8 @@ KNAPSACK_MULTIDIMENSION_CBC_MIP_SOLVER; %unignore operations_research::KnapsackSolver:: KNAPSACK_MULTIDIMENSION_SCIP_MIP_SOLVER; +%unignore operations_research::KnapsackSolver:: + KNAPSACK_DIVIDE_AND_CONQUER_SOLVER; %include "ortools/algorithms/knapsack_solver.h"