Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
permutation.h
Go to the documentation of this file.
1// Copyright 2010-2024 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
82
83namespace operations_research {
84
85// Abstract base class template defining the interface needed by
86// PermutationApplier to handle a single cycle of a permutation.
87template <typename IndexType>
89 public:
90 // This type is neither copyable nor movable.
93
94 // Sets the internal temporary storage from the given index in the
95 // underlying container(s).
96 virtual void SetTempFromIndex(IndexType source) = 0;
97
98 // Moves a data element one step along its cycle.
99 virtual void SetIndexFromIndex(IndexType source,
100 IndexType destination) const = 0;
101
102 // Sets a data element from the temporary.
103 virtual void SetIndexFromTemp(IndexType destination) const = 0;
104
105 // Marks an element of the permutation as handled by
106 // PermutationHandler::Apply(), meaning that we have read the
107 // corresponding value from the data to be permuted, and put that
108 // value somewhere (either in the temp or in its ultimate
109 // destination in the data.
110 //
111 // This method must be overridden in implementations where it is
112 // called. If an implementation doesn't call it, no need to
113 // override.
114 virtual void SetSeen(IndexType* unused_permutation_element) const {
115 LOG(FATAL) << "Base implementation of SetSeen() must not be called.";
116 }
117
118 // Returns true iff the given element of the permutation is unseen,
119 // meaning that it has not yet been handled by
120 // PermutationApplier::Apply().
121 //
122 // This method must be overridden in implementations where it is
123 // called. If an implementation doesn't call it, no need to
124 // override.
125 virtual bool Unseen(IndexType unused_permutation_element) const {
126 LOG(FATAL) << "Base implementation of Unseen() must not be called.";
127 return false;
128 }
129
131
132 protected:
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.
141template <typename DataType, typename IndexType>
143 public:
144 explicit ArrayIndexCycleHandler(DataType* data) : data_(data) {}
145
146 // This type is neither copyable nor movable.
149
150 void SetTempFromIndex(IndexType source) override { temp_ = data_[source]; }
151 void SetIndexFromIndex(IndexType source,
152 IndexType destination) const override {
153 data_[destination] = data_[source];
154 }
155 void SetIndexFromTemp(IndexType destination) const override {
156 data_[destination] = temp_;
157 }
158 void SetSeen(IndexType* permutation_element) const override {
159 *permutation_element = -*permutation_element - 1;
160 }
161 bool Unseen(IndexType permutation_element) const override {
162 return permutation_element >= 0;
163 }
164
165 private:
166 // Pointer to the base of the array of data to be permuted.
167 DataType* data_;
168
169 // Temporary storage for the one extra element we need.
170 DataType temp_;
171};
172
173// Note that this template is not implemented in an especially
174// performance-sensitive way. In particular, it makes multiple virtual
175// method calls for each element of the permutation.
176template <typename IndexType>
178 public:
180 : cycle_handler_(cycle_handler) {}
181
182 // This type is neither copyable nor movable.
185
186 void Apply(IndexType permutation[], int permutation_start,
187 int permutation_end) {
188 for (IndexType current = permutation_start; current < permutation_end;
189 ++current) {
190 IndexType next = permutation[current];
191 // cycle_start is only for debugging.
192 const IndexType cycle_start = current;
193 if (cycle_handler_->Unseen(next)) {
194 cycle_handler_->SetSeen(&permutation[current]);
195 DCHECK(!cycle_handler_->Unseen(permutation[current]));
196 cycle_handler_->SetTempFromIndex(current);
197 while (cycle_handler_->Unseen(permutation[next])) {
198 cycle_handler_->SetIndexFromIndex(next, current);
199 current = next;
200 next = permutation[next];
201 cycle_handler_->SetSeen(&permutation[current]);
202 DCHECK(!cycle_handler_->Unseen(permutation[current]));
203 }
204 cycle_handler_->SetIndexFromTemp(current);
205 // Set current back to the start of this cycle.
206 current = next;
207 }
208 DCHECK_EQ(cycle_start, current);
209 }
210 }
211
212 private:
214};
215} // namespace operations_research
216#endif // OR_TOOLS_UTIL_PERMUTATION_H_
void SetTempFromIndex(IndexType source) override
ArrayIndexCycleHandler(const ArrayIndexCycleHandler &)=delete
This type is neither copyable nor movable.
ArrayIndexCycleHandler & operator=(const ArrayIndexCycleHandler &)=delete
bool Unseen(IndexType permutation_element) const override
void SetSeen(IndexType *permutation_element) const override
void SetIndexFromTemp(IndexType destination) const override
Sets a data element from the temporary.
void SetIndexFromIndex(IndexType source, IndexType destination) const override
Moves a data element one step along its cycle.
PermutationApplier(const PermutationApplier &)=delete
This type is neither copyable nor movable.
PermutationApplier & operator=(const PermutationApplier &)=delete
PermutationApplier(PermutationCycleHandler< IndexType > *cycle_handler)
void Apply(IndexType permutation[], int permutation_start, int permutation_end)
virtual void SetIndexFromTemp(IndexType destination) const =0
Sets a data element from the temporary.
PermutationCycleHandler(const PermutationCycleHandler &)=delete
This type is neither copyable nor movable.
PermutationCycleHandler & operator=(const PermutationCycleHandler &)=delete
virtual bool Unseen(IndexType unused_permutation_element) const
virtual void SetTempFromIndex(IndexType source)=0
virtual void SetIndexFromIndex(IndexType source, IndexType destination) const =0
Moves a data element one step along its cycle.
virtual void SetSeen(IndexType *unused_permutation_element) const
Block * next
In SWIG mode, we don't want anything besides these top-level includes.