// 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_LP_DATA_PERMUTATION_H_ #define OR_TOOLS_LP_DATA_PERMUTATION_H_ #include "absl/random/random.h" #include "ortools/lp_data/lp_types.h" #include "ortools/util/return_macros.h" namespace operations_research { namespace glop { // Permutation 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 class Permutation { public: Permutation() : perm_() {} explicit Permutation(IndexType size) : perm_(size.value(), IndexType(0)) {} IndexType size() const { return IndexType(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(), value); } IndexType& operator[](IndexType i) { return perm_[i]; } const 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; private: gtl::ITIVector perm_; DISALLOW_COPY_AND_ASSIGN(Permutation); }; typedef Permutation RowPermutation; typedef Permutation 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 void ApplyPermutation(const Permutation& perm, const ITIVectorType& b, ITIVectorType* result); // Applies the inverse of perm to the vector b. Overwrites result to store // the result. template void ApplyInversePermutation(const Permutation& perm, const ITIVectorType& b, ITIVectorType* result); // Specialization of ApplyPermutation(): apply a column permutation to a // row-indexed vector v. template void ApplyColumnPermutationToRowIndexedVector( const Permutation& col_perm, RowIndexedVector* v) { RowIndexedVector temp_v = *v; ApplyPermutation(col_perm, temp_v, v); } // -------------------------------------------------------- // Implementation // -------------------------------------------------------- template void Permutation::PopulateFromInverse(const Permutation& inverse) { const size_t size = inverse.perm_.size(); perm_.resize(size); for (IndexType i(0); i < size; ++i) { perm_[inverse[i]] = i; } } template void Permutation::PopulateFromIdentity() { const size_t size = perm_.size(); perm_.resize(size, IndexType(0)); for (IndexType i(0); i < size; ++i) { perm_[i] = i; } } template void Permutation::PopulateRandomly() { PopulateFromIdentity(); std::shuffle(perm_.begin(), perm_.end()); } template bool Permutation::Check() const { const size_t size = perm_.size(); gtl::ITIVector 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 int Permutation::ComputeSignature() const { const size_t size = perm_.size(); gtl::ITIVector 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 void ApplyPermutation(const Permutation& perm, const ITIVectorType& b, ITIVectorType* result) { RETURN_IF_NULL(result); const IndexType size(perm.size()); if (size == 0) 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 void ApplyInversePermutation(const Permutation& perm, const ITIVectorType& b, ITIVectorType* result) { RETURN_IF_NULL(result); const IndexType size(perm.size().value()); if (size == 0) 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_