// Copyright 2010-2018 Google LLC // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef OR_TOOLS_ALGORITHMS_DYNAMIC_PERMUTATION_H_ #define OR_TOOLS_ALGORITHMS_DYNAMIC_PERMUTATION_H_ #include #include // TODO(user): remove when no longer used. #include #include "ortools/base/logging.h" namespace operations_research { class SparsePermutation; // Maintains a 'partial' permutation of [0..n-1] onto itself, with a dynamic // API allowing it to be built incrementally, and allowing some backtracking. // This is tuned for a specific usage by ./find_graph_symmetries.cc. // // RAM usage: as of 2014-04, this class needs less than: // 32.125 * (n + 2 * support_size) bytes. class DynamicPermutation { public: // Upon construction, every element i in [0..n-1] maps to itself. explicit DynamicPermutation(int n); int Size() const { return image_.size(); } // Return the original "n". // Declares a set of mappings for this permutation: src[i] will map to dst[i]. // Requirements that are DCHECKed: // - "src" and "dst" must have the same size. // - For all i, src[i] must not already be mapped to something. // - For all i, dst[i] must not already be the image of something. // // Complexity: amortized O(src.size()). void AddMappings(const std::vector& src, const std::vector& dst); // Undoes the last AddMappings() operation, and fills the "undone_mapping_src" // vector with the src of that last operation. This works like an undo stack. // For example, applying the sequence (Add, Add, Add, Undo, Add, Undo, Undo) // has exactly the same effect as applying the first Add() alone. // If you call this too may times (i.e. there is nothing left to undo), it is // simply a no-op. // // Complexity: same as the AddMappings() operation being undone. void UndoLastMappings(std::vector* undone_mapping_src); // Makes the permutation back to the identity (i.e. like right after // construction). // Complexity: O(support size). void Reset(); int ImageOf(int i) const; // Complexity: one vector lookup. // Returns the union of all "src" ever given to AddMappings(). const std::vector& AllMappingsSrc() const { return mapping_src_stack_; } // While the permutation is partially being built, the orbit of elements will // either form unclosed paths, or closed cycles. In the former case, // RootOf(i) returns the start of the path where i lies. If i is on a cycle, // RootOf(i) will return some element of its cycle (meaning that if i maps to // itself, RootOf(i) = i). // // Complexity: O(log(orbit size)) in average, assuming that the mappings are // added in a random order. O(orbit size) in the worst case. int RootOf(int i) const; // The exhaustive set of the 'loose end' of the incomplete cycles // (e.g., paths) built so far. // TODO(user): use a faster underlying container like SparseBitSet, and // tweak this API accordingly. const std::set& LooseEnds() const { return loose_ends_; } // Creates a SparsePermutation representing the current permutation. // Requirements: the permutation must only have cycles. // // Complexity: O(support size). std::unique_ptr CreateSparsePermutation() const; std::string DebugString() const; private: std::vector image_; // ancestor_[i] isn't exactly RootOf(i): it might itself have an ancestor, and // so on. std::vector ancestor_; // The concatenation of all "src" ever given to AddMappings(), and their // sizes, to implement the undo stack. Note that "mapping_src_stack_" contains // exactly the support of the permutation. std::vector mapping_src_stack_; std::vector mapping_src_size_stack_; // See the homonymous accessor, above. std::set loose_ends_; // Used transiently by CreateSparsePermutation(). Its resting state is: // size=Size(), all elements are false. mutable std::vector tmp_mask_; }; // Forced-inline for the speed. inline int DynamicPermutation::ImageOf(int i) const { DCHECK_GE(i, 0); DCHECK_LT(i, Size()); return image_[i]; } // Forced-inline for the speed. inline int DynamicPermutation::RootOf(int i) const { DCHECK_GE(i, 0); DCHECK_LT(i, Size()); while (true) { const int j = ancestor_[i]; if (j == i) return i; i = j; } } } // namespace operations_research #endif // OR_TOOLS_ALGORITHMS_DYNAMIC_PERMUTATION_H_