Files
eigen/test/product_symm.cpp

338 lines
16 KiB
C++

// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// SPDX-License-Identifier: MPL-2.0
#include "main.h"
template <typename Scalar, int Size, int OtherSize>
void symm(int size = Size, int othersize = OtherSize) {
typedef Matrix<Scalar, Size, Size> MatrixType;
typedef Matrix<Scalar, Size, OtherSize> Rhs1;
typedef Matrix<Scalar, OtherSize, Size> Rhs2;
enum { order = OtherSize == 1 ? 0 : RowMajor };
typedef Matrix<Scalar, Size, OtherSize, order> Rhs3;
Index rows = size;
Index cols = size;
MatrixType m1 = MatrixType::Random(rows, cols), m2 = MatrixType::Random(rows, cols), m3;
m1 = (m1 + m1.adjoint()).eval();
Rhs1 rhs1 = Rhs1::Random(cols, othersize), rhs12(cols, othersize), rhs13(cols, othersize);
Rhs2 rhs2 = Rhs2::Random(othersize, rows), rhs22(othersize, rows), rhs23(othersize, rows);
Rhs3 rhs3 = Rhs3::Random(cols, othersize), rhs32(cols, othersize), rhs33(cols, othersize);
Scalar s1 = internal::random<Scalar>(), s2 = internal::random<Scalar>();
m2 = m1.template triangularView<Lower>();
m3 = m2.template selfadjointView<Lower>();
VERIFY_IS_EQUAL(m1, m3);
VERIFY_IS_APPROX(rhs12 = (s1 * m2).template selfadjointView<Lower>() * (s2 * rhs1), rhs13 = (s1 * m1) * (s2 * rhs1));
VERIFY_IS_APPROX(rhs12 = (s1 * m2).transpose().template selfadjointView<Upper>() * (s2 * rhs1),
rhs13 = (s1 * m1.transpose()) * (s2 * rhs1));
VERIFY_IS_APPROX(rhs12 = (s1 * m2).template selfadjointView<Lower>().transpose() * (s2 * rhs1),
rhs13 = (s1 * m1.transpose()) * (s2 * rhs1));
VERIFY_IS_APPROX(rhs12 = (s1 * m2).conjugate().template selfadjointView<Lower>() * (s2 * rhs1),
rhs13 = (s1 * m1).conjugate() * (s2 * rhs1));
VERIFY_IS_APPROX(rhs12 = (s1 * m2).template selfadjointView<Lower>().conjugate() * (s2 * rhs1),
rhs13 = (s1 * m1).conjugate() * (s2 * rhs1));
VERIFY_IS_APPROX(rhs12 = (s1 * m2).adjoint().template selfadjointView<Upper>() * (s2 * rhs1),
rhs13 = (s1 * m1).adjoint() * (s2 * rhs1));
VERIFY_IS_APPROX(rhs12 = (s1 * m2).template selfadjointView<Lower>().adjoint() * (s2 * rhs1),
rhs13 = (s1 * m1).adjoint() * (s2 * rhs1));
m2 = m1.template triangularView<Upper>();
rhs12.setRandom();
rhs13 = rhs12;
m3 = m2.template selfadjointView<Upper>();
VERIFY_IS_EQUAL(m1, m3);
VERIFY_IS_APPROX(rhs12 += (s1 * m2).template selfadjointView<Upper>() * (s2 * rhs1),
rhs13 += (s1 * m1) * (s2 * rhs1));
m2 = m1.template triangularView<Lower>();
VERIFY_IS_APPROX(rhs12 = (s1 * m2).template selfadjointView<Lower>() * (s2 * rhs2.adjoint()),
rhs13 = (s1 * m1) * (s2 * rhs2.adjoint()));
m2 = m1.template triangularView<Upper>();
VERIFY_IS_APPROX(rhs12 = (s1 * m2).template selfadjointView<Upper>() * (s2 * rhs2.adjoint()),
rhs13 = (s1 * m1) * (s2 * rhs2.adjoint()));
m2 = m1.template triangularView<Upper>();
VERIFY_IS_APPROX(rhs12 = (s1 * m2.adjoint()).template selfadjointView<Lower>() * (s2 * rhs2.adjoint()),
rhs13 = (s1 * m1.adjoint()) * (s2 * rhs2.adjoint()));
// test row major = <...>
m2 = m1.template triangularView<Lower>();
rhs32.setRandom();
rhs13 = rhs32;
VERIFY_IS_APPROX(rhs32.noalias() -= (s1 * m2).template selfadjointView<Lower>() * (s2 * rhs3),
rhs13 -= (s1 * m1) * (s2 * rhs3));
m2 = m1.template triangularView<Upper>();
VERIFY_IS_APPROX(rhs32.noalias() = (s1 * m2.adjoint()).template selfadjointView<Lower>() * (s2 * rhs3).conjugate(),
rhs13 = (s1 * m1.adjoint()) * (s2 * rhs3).conjugate());
m2 = m1.template triangularView<Upper>();
rhs13 = rhs12;
VERIFY_IS_APPROX(rhs12.noalias() += s1 * ((m2.adjoint()).template selfadjointView<Lower>() * (s2 * rhs3).conjugate()),
rhs13 += (s1 * m1.adjoint()) * (s2 * rhs3).conjugate());
m2 = m1.template triangularView<Lower>();
VERIFY_IS_APPROX(rhs22 = (rhs2) * (m2).template selfadjointView<Lower>(), rhs23 = (rhs2) * (m1));
VERIFY_IS_APPROX(rhs22 = (s2 * rhs2) * (s1 * m2).template selfadjointView<Lower>(), rhs23 = (s2 * rhs2) * (s1 * m1));
// destination with a non-default inner-stride
// see bug 1741
{
typedef Matrix<Scalar, Dynamic, Dynamic> MatrixX;
MatrixX buffer(2 * cols, 2 * othersize);
// As for map2 below, the outer stride spans Rhs1's outer dimension, which is not rows() when
// Rhs1 is row major.
Map<Rhs1, 0, Stride<Dynamic, 2> > map1(buffer.data(), cols, othersize,
Stride<Dynamic, 2>(2 * rhs13.outerStride(), 2));
buffer.setZero();
VERIFY_IS_APPROX(map1.noalias() = (s1 * m2).template selfadjointView<Lower>() * (s2 * rhs1),
rhs13 = (s1 * m1) * (s2 * rhs1));
Map<Rhs2, 0, Stride<Dynamic, 2> > map2(buffer.data(), rhs22.rows(), rhs22.cols(),
Stride<Dynamic, 2>(2 * rhs22.outerStride(), 2));
buffer.setZero();
VERIFY_IS_APPROX(map2 = (rhs2) * (m2).template selfadjointView<Lower>(), rhs23 = (rhs2) * (m1));
}
}
// Physical RowMajor selfadjoint operand. symm<> above always builds a ColMajor
// operand, so the RowMajor packers -- symm_pack_lhs/symm_pack_rhs specialized on
// RowMajor, including the SME versions whose transposed regions carry the
// two-pass trailing transpose -- are otherwise never reached through the public
// API. Both operand positions (selfadjoint on the LHS and on the RHS) and both
// stored triangles are checked against a dense reference.
template <typename Scalar>
void symm_rowmajor_selfadjoint(Index size, Index othersize) {
typedef Matrix<Scalar, Dynamic, Dynamic, RowMajor> RowMat;
typedef Matrix<Scalar, Dynamic, Dynamic> ColMat;
RowMat m1 = RowMat::Random(size, size);
m1 = (m1 + m1.adjoint()).eval(); // exactly self-adjoint
RowMat lo = m1.template triangularView<Lower>();
RowMat up = m1.template triangularView<Upper>();
// Selfadjoint on the LHS: packs the RowMajor operand via symm_pack_lhs.
ColMat rhs = ColMat::Random(size, othersize);
ColMat ref = m1 * rhs;
VERIFY_IS_APPROX((lo.template selfadjointView<Lower>() * rhs).eval(), ref);
VERIFY_IS_APPROX((up.template selfadjointView<Upper>() * rhs).eval(), ref);
// Selfadjoint on the RHS: packs the RowMajor operand via symm_pack_rhs.
ColMat lhs = ColMat::Random(othersize, size);
ColMat ref2 = lhs * m1;
VERIFY_IS_APPROX((lhs * lo.template selfadjointView<Lower>()).eval(), ref2);
VERIFY_IS_APPROX((lhs * up.template selfadjointView<Upper>()).eval(), ref2);
}
// Test symmetric products at blocking boundary sizes.
// The existing test uses random sizes; these deterministic sizes exercise
// transitions in GEBP blocking (early-return at 48, block size changes).
template <int>
void product_symm_boundary() {
const int sizes[] = {1, 2, 3, 4, 8, 16, 47, 48, 49, 64, 96, 128};
for (int si = 0; si < 12; ++si) {
int n = sizes[si];
// double, matrix RHS
symm<double, Dynamic, Dynamic>(n, 5);
// double, vector RHS
symm<double, Dynamic, 1>(n);
// float, matrix RHS
symm<float, Dynamic, Dynamic>(n, 7);
// complex float, matrix RHS
symm<std::complex<float>, Dynamic, Dynamic>(n, 3);
}
// RowMajor selfadjoint operand. The partial last-panel widths in this list
// drive the RowMajor packers' transposed regions through the two-pass trailing
// transpose for streaming vector lengths from SVL=128 (svlw=4) up to SVL=2048
// (svlw=64): a partial width w in (svlw, 2*svlw) needs two predicated passes.
const int sa_sizes[] = {1, 5, 7, 17, 32, 33, 39, 45, 48, 49, 55, 57, 63, 64, 65, 96};
for (int n : sa_sizes) {
symm_rowmajor_selfadjoint<float>(n, 7);
symm_rowmajor_selfadjoint<float>(n, 1);
symm_rowmajor_selfadjoint<double>(n, 4);
}
}
// Packed-buffer contract: symm_pack_lhs/rhs applied to a stored triangle must
// be bit-identical to gemm_pack_lhs/rhs applied to the reconstructed dense
// matrix -- gebp_kernel cannot tell the two apart. Sentinels in the unused
// triangle catch reads of the wrong half; a marker past the packed range (in
// both buffers) catches writing too much or too little.
template <typename Scalar>
Scalar symm_pack_sentinel() {
return Scalar(typename NumTraits<Scalar>::Real(98765));
}
// Build an n x n Hermitian `full` and its triangle-only image `stored`
// (row >= col valid, sentinel elsewhere).
template <typename MatrixType>
void make_stored_triangle(Index n, MatrixType& stored, MatrixType& full) {
full = MatrixType::Random(n, n);
full = (full + full.adjoint()).eval();
stored = MatrixType::Constant(n, n, symm_pack_sentinel<typename MatrixType::Scalar>());
stored.template triangularView<Lower>() = full;
}
// A buffer holding `packed_size` packed entries followed by overrun markers.
template <typename Scalar>
Matrix<Scalar, Dynamic, 1> make_marked_buffer(Index packed_size) {
return Matrix<Scalar, Dynamic, 1>::Constant(packed_size + 32, Scalar(typename NumTraits<Scalar>::Real(-31415)));
}
template <typename Scalar, int StorageOrder>
void check_symm_pack_lhs(Index kc) {
using Traits = internal::gebp_traits<Scalar, Scalar>;
using Mat = Matrix<Scalar, Dynamic, Dynamic, StorageOrder>;
using Mapper = internal::const_blas_data_mapper<Scalar, Index, StorageOrder>;
Mat stored, full;
make_stored_triangle<Mat>(kc, stored, full);
Matrix<Scalar, Dynamic, 1> packed = make_marked_buffer<Scalar>(kc * kc);
Matrix<Scalar, Dynamic, 1> ref = packed;
internal::symm_pack_lhs<Scalar, Index, Traits::mr, Traits::LhsProgress, StorageOrder>()(packed.data(), stored.data(),
stored.outerStride(), kc, kc);
internal::gemm_pack_lhs<Scalar, Index, Mapper, Traits::mr, Traits::LhsProgress, typename Traits::LhsPacket4Packing,
StorageOrder, false, false>()(ref.data(), Mapper(full.data(), full.outerStride()), kc, kc);
VERIFY_IS_EQUAL(packed, ref);
}
template <typename Scalar, int StorageOrder>
void check_symm_pack_rhs(Index n, Index rows, Index k2) {
using Traits = internal::gebp_traits<Scalar, Scalar>;
using Mat = Matrix<Scalar, Dynamic, Dynamic, StorageOrder>;
using Mapper = internal::const_blas_data_mapper<Scalar, Index, StorageOrder>;
Mat stored, full;
make_stored_triangle<Mat>(n, stored, full);
Matrix<Scalar, Dynamic, 1> packed = make_marked_buffer<Scalar>(rows * n);
Matrix<Scalar, Dynamic, 1> ref = packed;
internal::symm_pack_rhs<Scalar, Index, Traits::nr, StorageOrder>()(packed.data(), stored.data(), stored.outerStride(),
rows, n, k2);
internal::gemm_pack_rhs<Scalar, Index, Mapper, Traits::nr, StorageOrder, false, false>()(
ref.data(), Mapper(full.data(), full.outerStride()).getSubMapper(k2, 0), rows, n);
VERIFY_IS_EQUAL(packed, ref);
}
template <typename Scalar>
void symm_pack_buffers() {
using Traits = internal::gebp_traits<Scalar, Scalar>;
constexpr Index mr = Traits::mr;
constexpr Index nr = Traits::nr;
constexpr Index ps = internal::packet_traits<Scalar>::size;
// LHS: every panel width up to a full panel plus a packet, then multi-panel
// borders around the 2*mr/3*mr transitions and the half-packet tail.
for (Index kc = 1; kc <= mr + ps + 2; ++kc) {
check_symm_pack_lhs<Scalar, ColMajor>(kc);
check_symm_pack_lhs<Scalar, RowMajor>(kc);
}
const Index lhs_borders[] = {2 * mr - 1, 2 * mr, 2 * mr + 1, 3 * mr, 3 * mr + ps / 2, 3 * mr + ps / 2 + 1, 97};
for (Index kc : lhs_borders) {
check_symm_pack_lhs<Scalar, ColMajor>(kc);
check_symm_pack_lhs<Scalar, RowMajor>(kc);
}
// RHS: sizes on and off the 8/4-column panel grid, with every depth block
// the driver's blocking can produce (k2 a multiple of 8; a block ends
// 8-aligned or at the matrix edge).
const Index rhs_sizes[] = {1, 2, 3, nr - 1, nr, nr + 1, 2 * nr + 1, 15, 16, 17,
23, 24, 25, 31, 32, 33, 47, 48, 49, 97};
for (Index n : rhs_sizes) {
if (n < 1) continue;
for (Index k2 = 0; k2 < n; k2 += 8) {
check_symm_pack_rhs<Scalar, ColMajor>(n, n - k2, k2);
check_symm_pack_rhs<Scalar, RowMajor>(n, n - k2, k2);
for (Index rows = 8; k2 + rows <= (n / 8) * 8; rows += 8) {
check_symm_pack_rhs<Scalar, ColMajor>(n, rows, k2);
check_symm_pack_rhs<Scalar, RowMajor>(n, rows, k2);
}
}
}
}
// A physically RowMajor selfadjoint operand. The symm<> tests above always
// build ColMajor operands, so without this the RowMajor symm_pack_lhs/rhs
// instantiations are never reached through the public API.
template <typename Scalar>
void symm_rowmajor_operand(Index n, Index m) {
using RowMat = Matrix<Scalar, Dynamic, Dynamic, RowMajor>;
using ColMat = Matrix<Scalar, Dynamic, Dynamic, ColMajor>;
RowMat s = RowMat::Random(n, n);
s = (s + s.adjoint()).eval();
RowMat lo = s.template triangularView<Lower>();
RowMat up = s.template triangularView<Upper>();
ColMat b = ColMat::Random(n, m), c = ColMat::Random(m, n);
ColMat ref = ColMat(s) * b;
VERIFY_IS_APPROX(ColMat(lo.template selfadjointView<Lower>() * b), ref);
VERIFY_IS_APPROX(ColMat(up.template selfadjointView<Upper>() * b), ref);
ColMat ref2 = c * ColMat(s);
VERIFY_IS_APPROX(ColMat(c * lo.template selfadjointView<Lower>()), ref2);
VERIFY_IS_APPROX(ColMat(c * up.template selfadjointView<Upper>()), ref2);
}
template <int>
void symm_packers_and_rowmajor_operands() {
symm_pack_buffers<float>();
symm_pack_buffers<double>();
symm_pack_buffers<std::complex<float> >();
symm_pack_buffers<std::complex<double> >();
const Index sizes[] = {1, 2, 7, 8, 9, 24, 25, 31, 32, 33, 47, 48, 49, 65};
for (Index n : sizes) {
for (Index m : {1, 3, 17}) {
symm_rowmajor_operand<float>(n, m);
symm_rowmajor_operand<double>(n, m);
symm_rowmajor_operand<std::complex<float> >(n, m);
}
}
}
EIGEN_DECLARE_TEST(product_symm) {
for (int i = 0; i < g_repeat; i++) {
CALL_SUBTEST_1((symm<float, Dynamic, Dynamic>(internal::random<int>(1, EIGEN_TEST_MAX_SIZE),
internal::random<int>(1, EIGEN_TEST_MAX_SIZE))));
CALL_SUBTEST_2((symm<double, Dynamic, Dynamic>(internal::random<int>(1, EIGEN_TEST_MAX_SIZE),
internal::random<int>(1, EIGEN_TEST_MAX_SIZE))));
CALL_SUBTEST_3((symm<std::complex<float>, Dynamic, Dynamic>(internal::random<int>(1, EIGEN_TEST_MAX_SIZE / 2),
internal::random<int>(1, EIGEN_TEST_MAX_SIZE / 2))));
CALL_SUBTEST_4((symm<std::complex<double>, Dynamic, Dynamic>(internal::random<int>(1, EIGEN_TEST_MAX_SIZE / 2),
internal::random<int>(1, EIGEN_TEST_MAX_SIZE / 2))));
CALL_SUBTEST_5((symm<float, Dynamic, 1>(internal::random<int>(1, EIGEN_TEST_MAX_SIZE))));
CALL_SUBTEST_6((symm<double, Dynamic, 1>(internal::random<int>(1, EIGEN_TEST_MAX_SIZE))));
CALL_SUBTEST_7((symm<std::complex<float>, Dynamic, 1>(internal::random<int>(1, EIGEN_TEST_MAX_SIZE))));
CALL_SUBTEST_8((symm<std::complex<double>, Dynamic, 1>(internal::random<int>(1, EIGEN_TEST_MAX_SIZE))));
}
// Deterministic blocking boundary tests (outside g_repeat).
CALL_SUBTEST_9(product_symm_boundary<0>());
// Packed-buffer contract checks and RowMajor selfadjoint operands.
CALL_SUBTEST_10(symm_packers_and_rowmajor_operands<0>());
}