blob: bfaf885e2566b308b33afa8fea97a12c94d2ddd1 [file]
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
#define TEST_ENABLE_TEMPORARY_TRACKING
#define EIGEN_RUNTIME_NO_MALLOC
#include "main.h"
#include <Eigen/Core>
#include <algorithm>
#include <cstdint>
template <unsigned int UpLo, typename MatrixType, typename Transformation>
void check_selfadjoint_permutation(MatrixType& matrix, const Transformation& transformation) {
using IndicesType = typename Transformation::IndicesType;
using Permutation = PermutationMatrix<IndicesType::SizeAtCompileTime, IndicesType::MaxSizeAtCompileTime,
typename IndicesType::Scalar>;
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization): Transpositions inputs need this conversion.
const Permutation permutation(transformation);
using PlainObject = typename MatrixType::PlainObject;
const PlainObject original = matrix;
const Index size = matrix.rows();
PlainObject full(size, size);
for (Index j = 0; j < size; ++j) {
for (Index i = 0; i < size; ++i) {
full(Index(permutation.indices()[i]), Index(permutation.indices()[j])) =
(UpLo == Lower ? i >= j : i <= j) ? original(i, j) : numext::conj(original(j, i));
}
}
PlainObject expected = original;
expected.template triangularView<UpLo>() = full;
auto view = matrix.template selfadjointView<UpLo>();
VERIFY(&view.applyPermutation(transformation) == &view);
VERIFY_IS_EQUAL(matrix, expected);
VERIFY_IS_EQUAL(view.toDenseMatrix(), full);
view.applyPermutation(transformation.inverse());
VERIFY_IS_EQUAL(matrix, original);
view.applyPermutation(transformation.transpose()).applyPermutation(transformation);
VERIFY_IS_EQUAL(matrix, original);
}
template <unsigned int UpLo, typename MatrixType>
void check_selfadjoint_transposition(MatrixType& matrix, Index i, Index j) {
using PlainObject = typename MatrixType::PlainObject;
const PlainObject original = matrix;
PlainObject full = original.template selfadjointView<UpLo>();
full.row(i).swap(full.row(j));
full.col(i).swap(full.col(j));
PlainObject expected = original;
expected.template triangularView<UpLo>() = full;
auto view = matrix.template selfadjointView<UpLo>();
internal::set_is_malloc_allowed(false);
VERIFY(&view.applyTransposition(i, j) == &view);
internal::set_is_malloc_allowed(true);
VERIFY_IS_EQUAL(matrix, expected);
view.applyTransposition(j, i);
VERIFY_IS_EQUAL(matrix, original);
}
template <typename Scalar, int Order>
void selfadjoint_permutation_cycles() {
using MatrixType = Matrix<Scalar, Dynamic, Dynamic, Order>;
for (Index size = 0; size <= 5; ++size) {
MatrixType matrix = MatrixType::Random(size, size);
matrix.diagonal() = matrix.diagonal().real().template cast<Scalar>();
PermutationMatrix<Dynamic> permutation(size);
permutation.setIdentity();
do {
check_selfadjoint_permutation<Lower>(matrix, permutation);
check_selfadjoint_permutation<Upper>(matrix, permutation);
} while (size > 1 && std::next_permutation(permutation.indices().data(), permutation.indices().data() + size));
for (Index i = 0; i < size; ++i) {
for (Index j = 0; j < size; ++j) {
check_selfadjoint_transposition<Lower>(matrix, i, j);
check_selfadjoint_transposition<Upper>(matrix, i, j);
}
}
}
}
template <typename Scalar, int Order>
void selfadjoint_permutation_views() {
using MatrixType = Matrix<Scalar, Dynamic, Dynamic, Order>;
const Index size = internal::random<Index>(2, 30);
MatrixType storage = MatrixType::Random(2 * size + 3, 2 * size + 3);
const MatrixType original = storage;
auto block = storage.block(1, 2, size, size);
PermutationMatrix<Dynamic, Dynamic, std::int64_t> permutation(size);
randomPermutationVector(permutation.indices(), size);
const Map<PermutationMatrix<Dynamic, Dynamic, std::int64_t>> mapped_permutation(permutation.indices().data(), size);
check_selfadjoint_permutation<Lower>(block, mapped_permutation);
check_selfadjoint_permutation<Upper>(block, permutation.indices().asPermutation());
VERIFY_IS_EQUAL(storage, original);
Map<MatrixType, Unaligned, Stride<Dynamic, Dynamic>> strided(storage.data(), size, size,
Stride<Dynamic, Dynamic>(2 * storage.outerStride(), 2));
check_selfadjoint_permutation<Lower>(strided, permutation);
check_selfadjoint_permutation<Upper>(strided, permutation);
for (Index i = 0; i < size; ++i) {
check_selfadjoint_transposition<Lower>(block, i, size - 1 - i);
check_selfadjoint_transposition<Upper>(strided, i, size - 1 - i);
}
VERIFY_IS_EQUAL(storage, original);
}
template <typename Scalar, int Size>
void selfadjoint_permutation_fixed() {
Matrix<Scalar, Size, Size> matrix = Matrix<Scalar, Size, Size>::Random();
PermutationMatrix<Size> permutation;
randomPermutationVector(permutation.indices(), Size);
internal::set_is_malloc_allowed(false);
check_selfadjoint_permutation<Lower>(matrix, permutation);
check_selfadjoint_permutation<Upper>(matrix, permutation);
Transpositions<Size> transpositions;
for (Index i = 0; i < Size; ++i) transpositions.indices()[i] = int(Size - 1 - i);
check_selfadjoint_permutation<Lower>(matrix, transpositions);
check_selfadjoint_permutation<Upper>(matrix, transpositions);
internal::set_is_malloc_allowed(true);
}
void selfadjoint_permutation_preconditions() {
MatrixXd matrix = MatrixXd::Random(3, 3);
PermutationMatrix<Dynamic> permutation(2);
permutation.setIdentity();
VERIFY_RAISES_ASSERT(matrix.selfadjointView<Lower>().applyPermutation(permutation));
VERIFY_RAISES_ASSERT(matrix.selfadjointView<Upper>().applyPermutation(permutation.inverse()));
VERIFY_RAISES_ASSERT(matrix.selfadjointView<Lower>().applyTransposition(-1, 0));
VERIFY_RAISES_ASSERT(matrix.selfadjointView<Upper>().applyTransposition(0, 3));
Transpositions<Dynamic> transpositions(2);
transpositions.setIdentity();
VERIFY_RAISES_ASSERT(matrix.selfadjointView<Lower>().applyPermutation(transpositions));
VERIFY_RAISES_ASSERT(matrix.selfadjointView<Upper>().applyPermutation(transpositions.inverse()));
// Indices that do not form a permutation: a repeat would otherwise loop forever.
PermutationMatrix<Dynamic> invalid(3);
invalid.indices() << 1, 1, 2;
VERIFY_RAISES_ASSERT(matrix.selfadjointView<Lower>().applyPermutation(invalid));
VERIFY_RAISES_ASSERT(matrix.selfadjointView<Upper>().applyPermutation(invalid));
invalid.indices() << 2, 0, 0;
VERIFY_RAISES_ASSERT(matrix.selfadjointView<Lower>().applyPermutation(invalid));
invalid.indices() << 3, 0, 1;
VERIFY_RAISES_ASSERT(matrix.selfadjointView<Upper>().applyPermutation(invalid));
matrix.resize(2, 3);
VERIFY_RAISES_ASSERT(matrix.selfadjointView<Lower>().applyPermutation(permutation));
VERIFY_RAISES_ASSERT(matrix.selfadjointView<Upper>().applyPermutation(permutation));
VERIFY_RAISES_ASSERT(matrix.selfadjointView<Lower>().applyTransposition(0, 1));
VERIFY_RAISES_ASSERT(matrix.selfadjointView<Upper>().applyPermutation(transpositions));
VERIFY_RAISES_ASSERT(matrix.selfadjointView<Lower>().applyPermutation(transpositions.transpose()));
}
void selfadjoint_permutation_aliased_indices() {
Matrix3i matrix;
matrix << 1, 2, 0, 4, 5, 6, 7, 8, 9;
const PermutationMatrix<3> permutation(matrix.row(0).transpose());
Matrix3i expected = matrix;
expected.selfadjointView<Upper>().applyPermutation(permutation);
matrix.selfadjointView<Upper>().applyPermutation(matrix.row(0).transpose().asPermutation());
VERIFY_IS_EQUAL(matrix, expected);
matrix << 1, 2, 0, 4, 5, 6, 7, 8, 9;
expected = matrix;
expected.selfadjointView<Upper>().applyPermutation(permutation.inverse());
matrix.selfadjointView<Upper>().applyPermutation(matrix.row(0).transpose().asPermutation().inverse());
VERIFY_IS_EQUAL(matrix, expected);
for (bool inverse : {false, true}) {
matrix << 1, 2, 0, 4, 5, 6, 7, 8, 9;
auto indices = matrix.row(0).transpose();
TranspositionsWrapper<decltype(indices)> wrapped(indices);
const Transpositions<3> transpositions(wrapped);
expected = matrix;
if (inverse) {
expected.selfadjointView<Upper>().applyPermutation(transpositions.inverse());
matrix.selfadjointView<Upper>().applyPermutation(wrapped.inverse());
} else {
expected.selfadjointView<Upper>().applyPermutation(transpositions);
matrix.selfadjointView<Upper>().applyPermutation(wrapped);
}
VERIFY_IS_EQUAL(matrix, expected);
}
}
template <typename Scalar, int Order>
void selfadjoint_transpositions() {
using MatrixType = Matrix<Scalar, Dynamic, Dynamic, Order>;
for (Index size = 0; size <= 4; ++size) {
MatrixType matrix = MatrixType::Random(size, size);
matrix.diagonal() = matrix.diagonal().real().template cast<Scalar>();
Transpositions<Dynamic, Dynamic, std::int64_t> transpositions(size);
transpositions.indices().setZero();
// All n^n sequences, including swaps with earlier indices and repeated targets.
bool finished = false;
do {
check_selfadjoint_permutation<Lower>(matrix, transpositions);
check_selfadjoint_permutation<Upper>(matrix, transpositions);
Index i = 0;
while (i < size && ++transpositions.indices()[i] == size) transpositions.indices()[i++] = 0;
finished = i == size;
} while (!finished);
transpositions.setIdentity();
const Map<Transpositions<Dynamic, Dynamic, std::int64_t>> mapped(transpositions.indices().data(), size);
check_selfadjoint_permutation<Lower>(matrix, mapped);
check_selfadjoint_permutation<Upper>(matrix, mapped);
}
}
void selfadjoint_permutation_temporary_count() {
MatrixXd matrix = MatrixXd::Random(8, 8);
PermutationMatrix<Dynamic> permutation(8);
randomPermutationVector(permutation.indices(), 8);
VERIFY_EVALUATION_COUNT(matrix.selfadjointView<Lower>().applyPermutation(permutation), 1);
VERIFY_EVALUATION_COUNT(matrix.selfadjointView<Upper>().applyPermutation(permutation.inverse()), 1);
VERIFY_EVALUATION_COUNT(matrix.selfadjointView<Lower>().applyPermutation(permutation.transpose()), 1);
Transpositions<Dynamic> transpositions(8);
transpositions.setIdentity();
VERIFY_EVALUATION_COUNT(matrix.selfadjointView<Lower>().applyPermutation(transpositions), 1);
VERIFY_EVALUATION_COUNT(matrix.selfadjointView<Upper>().applyPermutation(transpositions.transpose()), 1);
VERIFY_EVALUATION_COUNT(matrix.selfadjointView<Lower>().applyTransposition(1, 7), 0);
}
EIGEN_DECLARE_TEST(selfadjoint_permutation) {
CALL_SUBTEST_1((selfadjoint_permutation_cycles<float, ColMajor>()));
CALL_SUBTEST_1((selfadjoint_permutation_cycles<float, RowMajor>()));
CALL_SUBTEST_2((selfadjoint_permutation_cycles<double, ColMajor>()));
CALL_SUBTEST_2((selfadjoint_permutation_cycles<double, RowMajor>()));
CALL_SUBTEST_3((selfadjoint_permutation_cycles<std::complex<float>, ColMajor>()));
CALL_SUBTEST_3((selfadjoint_permutation_cycles<std::complex<float>, RowMajor>()));
CALL_SUBTEST_4((selfadjoint_permutation_cycles<std::complex<double>, ColMajor>()));
CALL_SUBTEST_4((selfadjoint_permutation_cycles<std::complex<double>, RowMajor>()));
CALL_SUBTEST_5((selfadjoint_permutation_cycles<int, ColMajor>()));
CALL_SUBTEST_5((selfadjoint_permutation_cycles<short, RowMajor>()));
CALL_SUBTEST_5((selfadjoint_permutation_cycles<half, ColMajor>()));
CALL_SUBTEST_5((selfadjoint_permutation_cycles<bfloat16, RowMajor>()));
for (int i = 0; i < g_repeat; ++i) {
CALL_SUBTEST_1((selfadjoint_permutation_views<float, ColMajor>()));
CALL_SUBTEST_2((selfadjoint_permutation_views<double, RowMajor>()));
CALL_SUBTEST_3((selfadjoint_permutation_views<std::complex<float>, RowMajor>()));
CALL_SUBTEST_4((selfadjoint_permutation_views<std::complex<double>, ColMajor>()));
CALL_SUBTEST_1((selfadjoint_permutation_fixed<float, 0>()));
CALL_SUBTEST_1((selfadjoint_permutation_fixed<float, 1>()));
CALL_SUBTEST_2((selfadjoint_permutation_fixed<double, 4>()));
CALL_SUBTEST_3((selfadjoint_permutation_fixed<std::complex<float>, 5>()));
}
CALL_SUBTEST_6((selfadjoint_transpositions<double, ColMajor>()));
CALL_SUBTEST_6((selfadjoint_transpositions<double, RowMajor>()));
CALL_SUBTEST_6((selfadjoint_transpositions<std::complex<double>, ColMajor>()));
CALL_SUBTEST_6((selfadjoint_transpositions<std::complex<double>, RowMajor>()));
CALL_SUBTEST_6((selfadjoint_transpositions<int, RowMajor>()));
CALL_SUBTEST_6(selfadjoint_permutation_temporary_count());
CALL_SUBTEST_1(selfadjoint_permutation_preconditions());
CALL_SUBTEST_5(selfadjoint_permutation_aliased_indices());
}