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