| // 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>()); |
| } |
| } |