Files
ortools-clone/ortools/lp_data/permutation.h
2025-02-05 18:11:16 +01:00

257 lines
8.4 KiB
C++

// Copyright 2010-2025 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_LP_DATA_PERMUTATION_H_
#define OR_TOOLS_LP_DATA_PERMUTATION_H_
#include <cstddef>
#include "absl/log/check.h"
#include "absl/random/random.h"
#include "ortools/base/strong_vector.h"
#include "ortools/lp_data/lp_types.h"
#include "ortools/util/return_macros.h"
namespace operations_research {
namespace glop {
// Permutation<IndexType> is a template class for storing and using
// row- and column- permutations, when instantiated with RowIndex and ColIndex
// respectively.
//
// By a row permutation we mean a permutation that maps the row 'i' of a matrix
// (or column vector) to the row 'permutation[i]' and in a similar fashion by a
// column permutation we mean a permutation that maps the column 'j' of a matrix
// (or row vector) to the column 'permutation[j]'.
//
// A permutation can be represented as a matrix P, but it gets a bit tricky
// here: P.x permutes the rows of x according to the permutation P but x^T.P
// permutes the columns of x^T (a row vector) using the INVERSE permutation.
// That is, to permute the columns of x^T using P, one has to compute
// x^T.P^{-1} but P^{-1} = P^T so the notation is consistent: If P.x permutes x,
// then (P.x)^T = x^T.P^T permutes x^T with the same permutation.
//
// So to be clear, if P and Q are permutation matrices, the matrix P.A.Q^{-1}
// is the image of A through the row permutation P and column permutation Q.
template <typename IndexType>
class Permutation {
public:
Permutation() : perm_() {}
explicit Permutation(IndexType size) : perm_(size, IndexType(0)) {}
// This type is neither copyable nor movable.
Permutation(const Permutation&) = delete;
Permutation& operator=(const Permutation&) = delete;
IndexType size() const { return perm_.size(); }
bool empty() const { return perm_.empty(); }
void clear() { perm_.clear(); }
void resize(IndexType size, IndexType value) {
perm_.resize(size.value(), value);
}
void assign(IndexType size, IndexType value) { perm_.assign(size, value); }
IndexType& operator[](IndexType i) { return perm_[i]; }
IndexType operator[](IndexType i) const { return perm_[i]; }
// Populates the calling object with the inverse permutation of the parameter
// inverse.
void PopulateFromInverse(const Permutation& inverse);
// Populates the calling object with the identity permutation.
void PopulateFromIdentity();
// Populates the calling object with a random permutation.
void PopulateRandomly();
// Returns true if the calling object contains a permutation, false otherwise.
bool Check() const;
// Returns the signature of a permutation in O(n), where n is the permutation
// size.
// The signature of a permutation is the product of the signature of
// the cycles defining the permutation.
// The signature of an odd cycle is 1, while the signature of an even cycle
// is -1. (Remembering hint: the signature of a swap (a 2-cycle) is -1.)
int ComputeSignature() const;
// For hot-loops it might be slighlty faster to cache the pointer and avoid
// bound checking on each [] access.
const IndexType* data() const { return perm_.data(); }
StrictITISpan<IndexType, const IndexType> const_view() const {
return perm_.view();
}
private:
StrictITIVector<IndexType, IndexType> perm_;
};
typedef Permutation<RowIndex> RowPermutation;
typedef Permutation<ColIndex> ColumnPermutation;
// Applies the permutation perm to the vector b. Overwrites result to store
// the result.
// TODO(user): Try to restrict this method to using the same integer type in
// the permutation and for the vector indices, i.e.
// IndexType == ITIVectorType::IndexType. Some client code will need to be
// refactored.
template <typename IndexType, typename ITIVectorType>
void ApplyPermutation(const Permutation<IndexType>& perm,
const ITIVectorType& b, ITIVectorType* result);
// Applies the inverse of perm to the vector b. Overwrites result to store
// the result.
template <typename IndexType, typename ITIVectorType>
void ApplyInversePermutation(const Permutation<IndexType>& perm,
const ITIVectorType& b, ITIVectorType* result);
// Specialization of ApplyPermutation(): apply a column permutation to a
// row-indexed vector v.
template <typename RowIndexedVector>
void ApplyColumnPermutationToRowIndexedVector(
StrictITISpan<ColIndex, const ColIndex> col_perm, RowIndexedVector* v,
RowIndexedVector* tmp) {
// Empty size means identity.
const int size = col_perm.size().value();
if (size == 0) return;
// Permute into tmp and swap.
DCHECK_EQ(size, v->size());
tmp->resize(RowIndex(size));
const auto* from = v->data();
auto* to = tmp->data();
for (int i = 0; i < size; ++i) {
to[col_perm[ColIndex(i)].value()] = from[i];
}
std::swap(*tmp, *v);
}
// --------------------------------------------------------
// Implementation
// --------------------------------------------------------
template <typename IndexType>
void Permutation<IndexType>::PopulateFromInverse(const Permutation& inverse) {
const IndexType size = inverse.perm_.size();
perm_.resize(size);
for (IndexType i(0); i < size; ++i) {
perm_[inverse[i]] = i;
}
}
template <typename IndexType>
void Permutation<IndexType>::PopulateFromIdentity() {
const IndexType size = perm_.size();
perm_.resize(size, IndexType(0));
for (IndexType i(0); i < size; ++i) {
perm_[i] = i;
}
}
template <typename IndexType>
void Permutation<IndexType>::PopulateRandomly() {
PopulateFromIdentity();
std::shuffle(perm_.begin(), perm_.end(), absl::BitGen());
}
template <typename IndexType>
bool Permutation<IndexType>::Check() const {
const size_t size = perm_.size().value();
util_intops::StrongVector<IndexType, bool> visited(size, false);
for (IndexType i(0); i < size; ++i) {
if (perm_[i] < 0 || perm_[i] >= size) {
return false;
}
visited[perm_[i]] = true;
}
for (IndexType i(0); i < size; ++i) {
if (!visited[i]) {
return false;
}
}
return true;
}
template <typename IndexType>
int Permutation<IndexType>::ComputeSignature() const {
const size_t size = perm_.size().value();
util_intops::StrongVector<IndexType, bool> visited(size);
DCHECK(Check());
int signature = 1;
for (IndexType i(0); i < size; ++i) {
if (!visited[i]) {
int cycle_size = 0;
IndexType j = i;
do {
j = perm_[j];
visited[j] = true;
++cycle_size;
} while (j != i);
if ((cycle_size & 1) == 0) {
signature = -signature;
}
}
}
return signature;
}
template <typename IndexType, typename ITIVectorType>
void ApplyPermutation(const Permutation<IndexType>& perm,
const ITIVectorType& b, ITIVectorType* result) {
RETURN_IF_NULL(result);
const IndexType size(perm.size());
if (size == 0) {
// Empty size means identity.
*result = b;
return;
}
DCHECK_EQ(size.value(), b.size().value());
result->resize(b.size(), /*whatever junk value*/ b.back());
for (IndexType i(0); i < size; ++i) {
const typename ITIVectorType::IndexType ith_index(i.value());
const typename ITIVectorType::IndexType permuted(perm[i].value());
(*result)[permuted] = b[ith_index];
}
}
template <typename IndexType, typename ITIVectorType>
void ApplyInversePermutation(const Permutation<IndexType>& perm,
const ITIVectorType& b, ITIVectorType* result) {
RETURN_IF_NULL(result);
const IndexType size(perm.size());
if (size == 0) {
// Empty size means identity.
*result = b;
return;
}
DCHECK_EQ(size.value(), b.size().value());
result->resize(b.size(), /*whatever junk value*/ b.back());
for (IndexType i(0); i < size; ++i) {
const typename ITIVectorType::IndexType ith_index(i.value());
const typename ITIVectorType::IndexType permuted(perm[i].value());
(*result)[ith_index] = b[permuted];
}
}
} // namespace glop
} // namespace operations_research
#endif // OR_TOOLS_LP_DATA_PERMUTATION_H_