OR-Tools  9.6
util/permutation.h
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 //
15 // Classes for permuting indexable, ordered containers of data without
16 // depending on that data to be accessible in any particular way. The
17 // client needs to give us two things:
18 // 1. a permutation to apply to some container(s) of data, and
19 // 2. a description of how to move data around in the container(s).
20 //
21 // The permutation (1) comes to us in the form of an array argument to
22 // PermutationApplier::Apply(), along with index values that tell us
23 // where in that array the permutation of interest lies. Typically
24 // those index values will span the entire array that describes the
25 // permutation.
26 //
27 // Applying a permutation involves decomposing the permutation into
28 // disjoint cycles and walking each element of the underlying data one
29 // step around the unique cycle in which it participates. The
30 // decomposition into disjoint cycles is done implicitly on the fly as
31 // the code in PermutationApplier::Apply() advances through the array
32 // describing the permutation. As an important piece of bookkeeping to
33 // support the decomposition into cycles, the elements of the
34 // permutation array typically get modified somehow to indicate which
35 // ones have already been used.
36 //
37 // At first glance, it would seem that if the containers are
38 // indexable, we don't need anything more complicated than just the
39 // permutation and the container of data we want to permute; it would
40 // seem we can just use the container's operator[] to retrieve and
41 // assign elements within the container. Unfortunately it's not so
42 // simple because the containers of interest can be indexable without
43 // providing any consistent way of accessing their contents that
44 // applies to all the containers of interest. For instance, if we
45 // could insist that every indexable container must define a
46 // `value_type& operator[]` we could simply use that for the assignments we need
47 // to do while walking around cycles of the permutation. This is not guaranteed
48 // though (common examples are `std::vector<bool>` or containers of bit-sized
49 // integers for which no c++ reference exists).
50 // This is the main reason we need a codified description (2) of
51 // how to move data around in the indexable container. That
52 // description comes to us via the PermutationApplier constructor's
53 // argument which is a PermutationCycleHandler instance. Such an
54 // object has three important methods defined: SetTempFromIndex(),
55 // SetIndexFromIndex(), and SetIndexFromTemp(). Those methods embody
56 // all we need to know about how to move data in the indexable
57 // container(s) underlying the PermutationCycleHandler.
58 //
59 // Another reason we need the description (2) of how to move elements
60 // around in the container(s) is that it is often important to permute
61 // side-by-side containers of elements according to the same
62 // permutation. This situation, too, is covered by defining a
63 // PermutationCycleHandler that knows about multiple underlying
64 // indexable containers.
65 //
66 // The above-mentioned PermutationCycleHandler methods embody
67 // knowledge of how to assign elements. It happens that
68 // PermutationCycleHandler is also a convenient place to embody the
69 // knowledge of how to keep track of which permutation elements have
70 // been consumed by the process of walking data around cycles. We
71 // depend on the PermutationCycleHandler instance we're given to
72 // define SetSeen() and Unseen() methods for that purpose.
73 //
74 // For the common case in which elements can be accessed using
75 // operator[](), we provide the class template
76 // ArrayIndexCycleHandler.
77 
78 #ifndef OR_TOOLS_UTIL_PERMUTATION_H_
79 #define OR_TOOLS_UTIL_PERMUTATION_H_
80 
81 #include "ortools/base/logging.h"
82 #include "ortools/base/macros.h"
83 
84 namespace operations_research {
85 
86 // Abstract base class template defining the interface needed by
87 // PermutationApplier to handle a single cycle of a permutation.
88 template <typename IndexType>
90  public:
91  // Sets the internal temporary storage from the given index in the
92  // underlying container(s).
93  virtual void SetTempFromIndex(IndexType source) = 0;
94 
95  // Moves a data element one step along its cycle.
96  virtual void SetIndexFromIndex(IndexType source,
97  IndexType destination) const = 0;
98 
99  // Sets a data element from the temporary.
100  virtual void SetIndexFromTemp(IndexType destination) const = 0;
101 
102  // Marks an element of the permutation as handled by
103  // PermutationHandler::Apply(), meaning that we have read the
104  // corresponding value from the data to be permuted, and put that
105  // value somewhere (either in the temp or in its ultimate
106  // destination in the data.
107  //
108  // This method must be overridden in implementations where it is
109  // called. If an implementation doesn't call it, no need to
110  // override.
111  virtual void SetSeen(IndexType* unused_permutation_element) const {
112  LOG(FATAL) << "Base implementation of SetSeen() must not be called.";
113  }
114 
115  // Returns true iff the given element of the permutation is unseen,
116  // meaning that it has not yet been handled by
117  // PermutationApplier::Apply().
118  //
119  // This method must be overridden in implementations where it is
120  // called. If an implementation doesn't call it, no need to
121  // override.
122  virtual bool Unseen(IndexType unused_permutation_element) const {
123  LOG(FATAL) << "Base implementation of Unseen() must not be called.";
124  return false;
125  }
126 
128 
129  protected:
131 
132  private:
133  DISALLOW_COPY_AND_ASSIGN(PermutationCycleHandler);
134 };
135 
136 // A generic cycle handler class for the common case in which the
137 // object to be permuted is indexable with T& operator[](int), and the
138 // permutation is represented by a mutable array of nonnegative
139 // int-typed index values. To mark a permutation element as seen, we
140 // replace it by its ones-complement value.
141 template <typename DataType, typename IndexType>
143  public:
144  explicit ArrayIndexCycleHandler(DataType* data) : data_(data) {}
145 
146  void SetTempFromIndex(IndexType source) override { temp_ = data_[source]; }
147  void SetIndexFromIndex(IndexType source,
148  IndexType destination) const override {
149  data_[destination] = data_[source];
150  }
151  void SetIndexFromTemp(IndexType destination) const override {
152  data_[destination] = temp_;
153  }
154  void SetSeen(IndexType* permutation_element) const override {
155  *permutation_element = -*permutation_element - 1;
156  }
157  bool Unseen(IndexType permutation_element) const override {
158  return permutation_element >= 0;
159  }
160 
161  private:
162  // Pointer to the base of the array of data to be permuted.
163  DataType* data_;
164 
165  // Temporary storage for the one extra element we need.
166  DataType temp_;
167 
168  DISALLOW_COPY_AND_ASSIGN(ArrayIndexCycleHandler);
169 };
170 
171 // Note that this template is not implemented in an especially
172 // performance-sensitive way. In particular, it makes multiple virtual
173 // method calls for each element of the permutation.
174 template <typename IndexType>
176  public:
178  : cycle_handler_(cycle_handler) {}
179 
180  void Apply(IndexType permutation[], int permutation_start,
181  int permutation_end) {
182  for (IndexType current = permutation_start; current < permutation_end;
183  ++current) {
184  IndexType next = permutation[current];
185  // cycle_start is only for debugging.
186  const IndexType cycle_start = current;
187  if (cycle_handler_->Unseen(next)) {
188  cycle_handler_->SetSeen(&permutation[current]);
189  DCHECK(!cycle_handler_->Unseen(permutation[current]));
190  cycle_handler_->SetTempFromIndex(current);
191  while (cycle_handler_->Unseen(permutation[next])) {
192  cycle_handler_->SetIndexFromIndex(next, current);
193  current = next;
194  next = permutation[next];
195  cycle_handler_->SetSeen(&permutation[current]);
196  DCHECK(!cycle_handler_->Unseen(permutation[current]));
197  }
198  cycle_handler_->SetIndexFromTemp(current);
199  // Set current back to the start of this cycle.
200  current = next;
201  }
202  DCHECK_EQ(cycle_start, current);
203  }
204  }
205 
206  private:
207  PermutationCycleHandler<IndexType>* cycle_handler_;
208 
209  DISALLOW_COPY_AND_ASSIGN(PermutationApplier);
210 };
211 } // namespace operations_research
212 #endif // OR_TOOLS_UTIL_PERMUTATION_H_
void SetIndexFromIndex(IndexType source, IndexType destination) const override
void SetIndexFromTemp(IndexType destination) const override
bool Unseen(IndexType permutation_element) const override
void SetTempFromIndex(IndexType source) override
void SetSeen(IndexType *permutation_element) const override
PermutationApplier(PermutationCycleHandler< IndexType > *cycle_handler)
void Apply(IndexType permutation[], int permutation_start, int permutation_end)
virtual void SetIndexFromTemp(IndexType destination) const =0
virtual bool Unseen(IndexType unused_permutation_element) const
virtual void SetTempFromIndex(IndexType source)=0
virtual void SetSeen(IndexType *unused_permutation_element) const
virtual void SetIndexFromIndex(IndexType source, IndexType destination) const =0
Block * next
Collection of objects used to extend the Constraint Solver library.