blob: 9d75350cb6a4f23befffba1515892d1bc48c97ee [file] [edit]
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
#include "main.h"
#include <Eigen/Core>
template <typename Scalar, int TriOrder, int OtherOrder, int Side, unsigned int Mode, int InnerStride>
void trsm_packet_case(Index n, Index nrhs, Index inner, bool well_conditioned = true, Scalar magnitude = Scalar(1)) {
using Triangle = Matrix<Scalar, Dynamic, Dynamic, TriOrder>;
using Operand = Matrix<Scalar, Dynamic, Dynamic, OtherOrder>;
using Vector = Matrix<Scalar, Dynamic, 1>;
using Strides = Stride<Dynamic, InnerStride>;
const Index rows = Side == OnTheLeft ? n : nrhs, cols = Side == OnTheLeft ? nrhs : n;
const Index outer = inner * (OtherOrder == ColMajor ? rows : cols) + 3;
Vector storage = Vector::Constant(outer * (OtherOrder == ColMajor ? cols : rows) + 2, Scalar(17));
const Vector before = storage;
Map<Operand, Unaligned, Strides> x(storage.data() + 1, rows, cols, Strides(outer, inner));
Triangle a = Triangle::Random(n, n);
if (well_conditioned) a /= Scalar(n + 1);
a.diagonal().setOnes();
a *= magnitude;
const Triangle saved = a;
const Operand b = Operand::Random(rows, cols);
x = b;
// Poison every coefficient that the solve must not read, including a unit diagonal.
a.template triangularView<(Mode & Lower) ? StrictlyUpper : StrictlyLower>().setConstant(
NumTraits<Scalar>::quiet_NaN());
if (Mode & UnitDiag) a.diagonal().setConstant(NumTraits<Scalar>::quiet_NaN());
a.template triangularView<Mode>().template solveInPlace<Side>(x);
// |A X - B| <= O(n*eps) * (|A|*|X| + |B|). The factor 8 covers blocked updates
// and the independent residual accumulation in wider precision.
const long double gamma = 8 * n * static_cast<long double>(NumTraits<Scalar>::epsilon());
for (Index j = 0; j < cols; ++j) {
for (Index i = 0; i < rows; ++i) {
long double sum = 0, scale = numext::abs(static_cast<long double>(b(i, j)));
for (Index k = 0; k < n; ++k) {
const Index r = Side == OnTheLeft ? i : k, c = Side == OnTheLeft ? k : j;
if ((Mode & Lower) ? r < c : r > c) continue;
const long double t = r == c && (Mode & UnitDiag) ? 1 : static_cast<long double>(saved(r, c));
const long double v = Side == OnTheLeft ? static_cast<long double>(x(k, j)) : static_cast<long double>(x(i, k));
sum += t * v;
scale += numext::abs(t * v);
}
VERIFY((numext::isfinite)(scale));
VERIFY(numext::abs(sum - static_cast<long double>(b(i, j))) <= gamma * scale);
}
}
Vector expected = before;
Map<Operand, Unaligned, Strides> mapped(expected.data() + 1, rows, cols, Strides(outer, inner));
mapped = x;
VERIFY_IS_CWISE_EQUAL(storage, expected);
}
template <typename Scalar, int TriOrder, int OtherOrder, int Side, int InnerStride = 1>
void trsm_packet_modes(Index n, Index nrhs, Index inner = 1) {
trsm_packet_case<Scalar, TriOrder, OtherOrder, Side, Lower, InnerStride>(n, nrhs, inner);
trsm_packet_case<Scalar, TriOrder, OtherOrder, Side, Upper, InnerStride>(n, nrhs, inner);
trsm_packet_case<Scalar, TriOrder, OtherOrder, Side, UnitLower, InnerStride>(n, nrhs, inner);
trsm_packet_case<Scalar, TriOrder, OtherOrder, Side, UnitUpper, InnerStride>(n, nrhs, inner);
}
template <typename Scalar>
void trsm_packet_cache_sizes() {
using Traits = internal::triangular_solve_packet_traits<Scalar>;
std::ptrdiff_t saved_l1, saved_l2, saved_l3, saved_l3_per_cpu;
internal::manage_caching_sizes(GetAction, &saved_l1, &saved_l2, &saved_l3, &saved_l3_per_cpu);
const Index packet = Traits::PacketSize;
const Index widths[] = {packet, Traits::RhsPackets * packet, Traits::RhsPackets * packet + 1};
const auto cutoff_for = [](Index nrhs) {
Index cutoff = 0;
for (Index n = Traits::RegisterRows; n <= Traits::WorkspaceRows; ++n) {
if (!Traits::use_unblocked(n, nrhs, l1CacheSize())) break;
cutoff = n;
}
return cutoff;
};
Index previous = 0;
for (std::ptrdiff_t l1 : {512, 2048, 8192, 32768}) {
setCpuCacheSizes(l1, 4 * l1, 16 * l1);
const Index wide_cutoff = cutoff_for(widths[1]);
VERIFY(wide_cutoff >= previous);
previous = wide_cutoff;
for (Index nrhs : widths) {
const Index cutoff = cutoff_for(nrhs);
VERIFY(cutoff >= 0 && cutoff <= Traits::WorkspaceRows);
if (nrhs == packet) VERIFY(cutoff >= wide_cutoff);
// Exercise both algorithms at the configured crossover, plus the allocation bound.
const Index sizes[] = {
(numext::maxi)(Index(Traits::RegisterRows), cutoff - 1), (numext::maxi)(Index(Traits::RegisterRows), cutoff),
(numext::maxi)(Index(Traits::RegisterRows), cutoff + 1), Traits::WorkspaceRows, Traits::WorkspaceRows + 1};
for (Index n : sizes) {
trsm_packet_modes<Scalar, ColMajor, ColMajor, OnTheLeft>(n, nrhs);
trsm_packet_modes<Scalar, RowMajor, ColMajor, OnTheLeft>(n, nrhs);
trsm_packet_modes<Scalar, ColMajor, RowMajor, OnTheRight>(n, nrhs);
trsm_packet_modes<Scalar, RowMajor, RowMajor, OnTheRight>(n, nrhs);
}
}
}
VERIFY(!Traits::use_unblocked(Index(Traits::RegisterRows), packet - 1, saved_l1));
VERIFY(!Traits::use_unblocked(Index(Traits::RegisterRows), packet, std::ptrdiff_t(0)));
VERIFY(!Traits::use_unblocked(Index(Traits::RegisterRows), packet, (std::numeric_limits<std::ptrdiff_t>::min)()));
VERIFY(
!Traits::use_unblocked(Index(Traits::WorkspaceRows + 1), packet, (std::numeric_limits<std::ptrdiff_t>::max)()));
VERIFY(Traits::use_unblocked(Index(Traits::WorkspaceRows), widths[1], (std::numeric_limits<std::ptrdiff_t>::max)()) ==
Traits::UseUnblocked);
VERIFY(!Traits::use_unblocked((std::numeric_limits<Index>::max)(), packet,
(std::numeric_limits<std::ptrdiff_t>::max)()));
internal::manage_caching_sizes(SetAction, &saved_l1, &saved_l2, &saved_l3, &saved_l3_per_cpu);
}
template <typename Scalar>
void trsm_packet() {
STATIC_CHECK((internal::triangular_solve_packet_traits<Scalar>::Enabled ==
bool(internal::packet_traits<Scalar>::Vectorizable && std::numeric_limits<Scalar>::is_iec559 &&
std::numeric_limits<Scalar>::radix == 2)));
STATIC_CHECK((!internal::triangular_solve_packet_traits<std::complex<Scalar>>::Enabled));
const Index packet = internal::packet_traits<Scalar>::size;
const Index widths[] = {1, 2, packet, 2 * packet - 1, 2 * packet, 2 * packet + 1, 4 * packet + 3};
const Index sizes[] = {0, 1, 3, 4, 7, 8, 11, 12, 16, 23, 24, 25, 31, 64, 127, 128, 129, 257};
for (Index n : sizes) {
for (Index nrhs : widths) {
trsm_packet_modes<Scalar, ColMajor, ColMajor, OnTheLeft>(n, nrhs);
trsm_packet_modes<Scalar, RowMajor, ColMajor, OnTheLeft>(n, nrhs);
trsm_packet_modes<Scalar, ColMajor, RowMajor, OnTheRight>(n, nrhs);
trsm_packet_modes<Scalar, RowMajor, RowMajor, OnTheRight>(n, nrhs);
}
}
trsm_packet_modes<Scalar, ColMajor, ColMajor, OnTheLeft, Dynamic>(65, 19, 1);
trsm_packet_modes<Scalar, RowMajor, ColMajor, OnTheLeft, Dynamic>(65, 19, 2);
trsm_packet_modes<Scalar, ColMajor, RowMajor, OnTheRight, 2>(65, 19, 2);
// Unscaled off-diagonals allow inverse growth; only backward error is checked.
trsm_packet_case<Scalar, ColMajor, ColMajor, OnTheLeft, Lower, 1>(33, 19, 1, false);
trsm_packet_case<Scalar, RowMajor, ColMajor, OnTheLeft, Upper, 1>(33, 19, 1, false);
trsm_packet_case<Scalar, ColMajor, RowMajor, OnTheRight, UnitLower, 1>(33, 19, 1, false);
trsm_packet_case<Scalar, RowMajor, RowMajor, OnTheRight, UnitUpper, 1>(33, 19, 1, false);
for (int exponent : {-40, 40}) {
const Scalar magnitude = numext::ldexp(Scalar(1), exponent);
trsm_packet_case<Scalar, ColMajor, ColMajor, OnTheLeft, Lower, 1>(65, 19, 1, true, magnitude);
trsm_packet_case<Scalar, RowMajor, ColMajor, OnTheLeft, Upper, 1>(65, 19, 1, true, magnitude);
}
trsm_packet_cache_sizes<Scalar>();
}
EIGEN_DECLARE_TEST(trsm_packet) {
for (int i = 0; i < g_repeat; ++i) {
CALL_SUBTEST_1(trsm_packet<float>());
CALL_SUBTEST_2(trsm_packet<double>());
}
}