blob: 61716cc25aa01deb09ecfdccac444b137277c69d [file]
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
#define EIGEN_RUNTIME_NO_MALLOC
#include "main.h"
#define EIGEN_TEST_ANNOYING_SCALAR_DONT_THROW
#include "AnnoyingScalar.h"
template <typename Lhs, typename Rhs>
void check_degenerate_gemv(const Lhs& lhs, const Rhs& rhs) {
using Scalar = typename ScalarBinaryOpTraits<typename Lhs::Scalar, typename Rhs::Scalar>::ReturnType;
using Real = typename NumTraits<Scalar>::Real;
using Wide = std::conditional_t<NumTraits<Scalar>::IsComplex, std::complex<long double>, long double>;
using Vector = Matrix<Scalar, Dynamic, 1>;
const Scalar alpha = internal::random<Scalar>();
const Vector initial = Vector::Random(lhs.rows());
Vector contiguous(initial);
Vector storage = Vector::Constant(2 * lhs.rows() + 1, Scalar(7));
Map<Vector, Unaligned, InnerStride<2>> result(storage.data(), lhs.rows());
Vector dynamicStorage(storage);
Map<Vector, Unaligned, InnerStride<Dynamic>> dynamicResult(dynamicStorage.data(), lhs.rows(),
InnerStride<Dynamic>(2));
for (int operation = 0; operation < 3; ++operation) {
result = initial;
contiguous = initial;
dynamicResult = initial;
auto evaluate = [&](auto& target) {
if (operation == 0) target.noalias() = alpha * lhs * rhs;
if (operation == 1) target.noalias() += alpha * lhs * rhs;
if (operation == 2) target.noalias() -= alpha * lhs * rhs;
};
evaluate(result);
evaluate(contiguous);
evaluate(dynamicResult);
for (Index i = 0; i < lhs.rows(); ++i) {
Wide sum(0);
long double magnitude = 0;
for (Index k = 0; k < lhs.cols(); ++k) {
const Wide term = Wide(alpha) * Wide(lhs.coeff(i, k)) * Wide(rhs.coeff(k, 0));
sum += term;
magnitude += numext::abs(term);
}
const Wide expected = operation == 0 ? sum : Wide(initial[i]) + (operation == 1 ? sum : -sum);
const long double bound =
32 * static_cast<long double>(NumTraits<Real>::epsilon()) * (magnitude + numext::abs(Wide(initial[i])));
VERIFY(numext::abs(Wide(result[i]) - expected) <= bound);
VERIFY(numext::abs(Wide(contiguous[i]) - expected) <= bound);
VERIFY(numext::abs(Wide(dynamicResult[i]) - expected) <= bound);
}
for (Index i = 1; i < storage.size(); i += 2) VERIFY_IS_EQUAL(storage[i], Scalar(7));
VERIFY_IS_EQUAL(storage[storage.size() - 1], Scalar(7));
for (Index i = 1; i < dynamicStorage.size(); i += 2) VERIFY_IS_EQUAL(dynamicStorage[i], Scalar(7));
VERIFY_IS_EQUAL(dynamicStorage[dynamicStorage.size() - 1], Scalar(7));
}
}
template <typename LhsScalar, typename RhsScalar = LhsScalar>
void degenerate_gemv() {
using MatrixType = Matrix<LhsScalar, Dynamic, Dynamic>;
using RhsVector = Matrix<RhsScalar, Dynamic, 1>;
for (Index n : {0, 1, 3, 8, 17, 257}) {
for (Index depth : {0, 1, 2, 4}) {
MatrixType a = MatrixType::Random(depth, n);
RhsVector rhsStorage = RhsVector::Random(3 * depth + 1);
Map<RhsVector, Unaligned, InnerStride<3>> rhs(rhsStorage.data(), depth);
check_degenerate_gemv(a.transpose(), rhs);
check_degenerate_gemv(a.adjoint(), rhs.conjugate());
check_degenerate_gemv(LhsScalar(0.75) * a.adjoint(), RhsScalar(0.5) * rhs);
MatrixType columnMajor = a.transpose();
check_degenerate_gemv(columnMajor, rhs);
check_degenerate_gemv(columnMajor.conjugate(), rhs.conjugate());
check_degenerate_gemv(LhsScalar(0.75) * columnMajor.conjugate(), RhsScalar(0.5) * rhs);
}
Matrix<LhsScalar, Dynamic, 1> lhsStorage = Matrix<LhsScalar, Dynamic, 1>::Random(3 * n + 1);
using RowMatrix = Matrix<LhsScalar, Dynamic, Dynamic, RowMajor>;
Map<RowMatrix, Unaligned, OuterStride<3>> lhs(lhsStorage.data(), n, 1);
RhsVector rhs = RhsVector::Random(1);
check_degenerate_gemv(lhs, rhs);
Map<RowMatrix, Unaligned, OuterStride<>> runtimeLhs(lhsStorage.data(), n, 1, OuterStride<>(3));
check_degenerate_gemv(runtimeLhs, rhs);
Matrix<LhsScalar, 1, Dynamic> row = Matrix<LhsScalar, 1, Dynamic>::Random(n);
check_degenerate_gemv(row.transpose(), rhs);
Matrix<LhsScalar, 2, Dynamic> fixedDepth = Matrix<LhsScalar, 2, Dynamic>::Random(2, n);
RhsVector fixedDepthRhs = RhsVector::Random(2);
check_degenerate_gemv(fixedDepth.transpose(), fixedDepthRhs);
}
}
template <typename Scalar, int StorageOrder>
void degenerate_gemv_zero_alpha() {
using MatrixType = Matrix<Scalar, Dynamic, Dynamic, StorageOrder>;
using Vector = Matrix<Scalar, Dynamic, 1>;
using Real = typename NumTraits<Scalar>::Real;
MatrixType lhs(17, 1);
Vector rhs(1);
Vector backing(2 * lhs.rows() + 1);
Map<Vector, Unaligned, InnerStride<2>> strided(backing.data(), lhs.rows());
Map<Vector, Unaligned, InnerStride<Dynamic>> dynamicStrided(backing.data(), lhs.rows(), InnerStride<Dynamic>(2));
Vector contiguous(lhs.rows());
for (const Real exceptional : {NumTraits<Real>::quiet_NaN(), NumTraits<Real>::infinity()}) {
for (int operand = 0; operand < 2; ++operand) {
lhs.setConstant(operand == 0 ? Scalar(exceptional) : Scalar(2));
rhs.setConstant(operand == 1 ? Scalar(exceptional) : Scalar(3));
for (const Real zero : {Real(0), -Real(0)}) {
for (int factor = 0; factor < 3; ++factor) {
for (int operation = 0; operation < 2; ++operation) {
auto check = [&](auto& destination) {
backing.setConstant(Scalar(7));
contiguous.setConstant(Scalar(7));
auto update = [&](const auto& product) {
if (operation == 0)
destination.noalias() += product;
else
destination.noalias() -= product;
};
if (factor == 0) update(Scalar(zero) * (lhs * rhs));
if (factor == 1) update((Scalar(zero) * lhs) * rhs);
if (factor == 2) update(lhs * (Scalar(zero) * rhs));
for (Index i = 0; i < destination.size(); ++i) VERIFY_IS_EQUAL(destination[i], Scalar(7));
for (Index i = 1; i < backing.size(); i += 2) VERIFY_IS_EQUAL(backing[i], Scalar(7));
VERIFY_IS_EQUAL(backing[backing.size() - 1], Scalar(7));
};
check(contiguous);
check(strided);
check(dynamicStrided);
}
}
}
}
}
}
template <typename Scalar, int StorageOrder>
void degenerate_gemv_array_destinations() {
using MatrixType = Matrix<Scalar, Dynamic, Dynamic, StorageOrder>;
using Vector = Matrix<Scalar, Dynamic, 1>;
using ArrayVector = Array<Scalar, Dynamic, 1>;
const Index size = 17;
MatrixType lhs(size, 1);
ArrayVector expected(size);
for (Index i = 0; i < size; ++i) {
lhs(i, 0) = Scalar(2 * (i + 1));
expected[i] = Scalar(3 * (i + 1));
}
const Vector rhs = Vector::Constant(1, Scalar(3));
const Scalar alpha(0.5);
const ArrayVector contiguous = alpha * lhs * rhs;
VERIFY((contiguous == expected).all());
ArrayVector backing = ArrayVector::Constant(2 * size + 1, Scalar(7));
Map<ArrayVector, Unaligned, InnerStride<2>> strided(backing.data(), size);
Map<ArrayVector, Unaligned, InnerStride<Dynamic>> dynamicStrided(backing.data(), size, InnerStride<Dynamic>(2));
auto check = [&](auto& destination) {
backing.setConstant(Scalar(7));
destination = alpha * lhs * rhs;
VERIFY((destination == expected).all());
for (Index i = 1; i < backing.size(); i += 2) VERIFY_IS_EQUAL(backing[i], Scalar(7));
VERIFY_IS_EQUAL(backing[backing.size() - 1], Scalar(7));
};
check(strided);
check(dynamicStrided);
const Array<Scalar, 1, Dynamic> row = alpha * rhs.transpose() * lhs.transpose();
VERIFY((row.transpose() == expected).all());
Array<Scalar, Dynamic, Dynamic> rows = Array<Scalar, Dynamic, Dynamic>::Constant(2, size + 2, Scalar(7));
rows.row(1).segment(1, size) = alpha * rhs.transpose() * lhs.transpose();
VERIFY((rows.row(1).segment(1, size).transpose() == expected).all());
VERIFY((rows.row(0) == Scalar(7)).all());
VERIFY_IS_EQUAL(rows(1, 0), Scalar(7));
VERIFY_IS_EQUAL(rows(1, size + 1), Scalar(7));
}
EIGEN_DECLARE_TEST(gemv_degenerate) {
// The disabled C++14 dispatch must not require coefficient access or instantiate the update.
int placeholder = 0;
VERIFY((!internal::gemv_single_column<false, false, false>::run(placeholder, placeholder, placeholder, placeholder,
true)));
degenerate_gemv_array_destinations<float, RowMajor>();
degenerate_gemv_array_destinations<float, ColMajor>();
degenerate_gemv_array_destinations<double, RowMajor>();
degenerate_gemv_array_destinations<double, ColMajor>();
degenerate_gemv_array_destinations<std::complex<float>, RowMajor>();
degenerate_gemv_array_destinations<std::complex<float>, ColMajor>();
degenerate_gemv_array_destinations<std::complex<double>, RowMajor>();
degenerate_gemv_array_destinations<std::complex<double>, ColMajor>();
degenerate_gemv_zero_alpha<float, RowMajor>();
degenerate_gemv_zero_alpha<double, RowMajor>();
degenerate_gemv_zero_alpha<std::complex<float>, RowMajor>();
degenerate_gemv_zero_alpha<std::complex<double>, RowMajor>();
degenerate_gemv_zero_alpha<float, ColMajor>();
degenerate_gemv_zero_alpha<double, ColMajor>();
STATIC_CHECK(!std::is_trivially_copyable<AnnoyingScalar>::value);
for (int repeat = 0; repeat < g_repeat; ++repeat) {
degenerate_gemv<float>();
degenerate_gemv<double>();
degenerate_gemv<half>();
degenerate_gemv<bfloat16>();
degenerate_gemv<std::complex<float>>();
degenerate_gemv<std::complex<double>>();
degenerate_gemv<double, std::complex<double>>();
degenerate_gemv<std::complex<double>, double>();
}
Matrix<double, Dynamic, Dynamic, RowMajor> lhs = MatrixXd::Constant(2, 1, 1e-200);
VectorXd rhs = VectorXd::Constant(1, 1e308);
VectorXd result = VectorXd::Zero(2);
result.noalias() += 2.0 * lhs * rhs;
for (Index i = 0; i < result.size(); ++i) {
VERIFY((numext::isfinite)(result[i]));
VERIFY(numext::abs(result[i] / 2e108 - 1.0) <= 8 * NumTraits<double>::epsilon());
}
Matrix<AnnoyingScalar, Dynamic, Dynamic, RowMajor> custom =
Matrix<AnnoyingScalar, Dynamic, Dynamic, RowMajor>::Constant(5, 1, AnnoyingScalar(2));
Matrix<AnnoyingScalar, Dynamic, 1> customRhs = Matrix<AnnoyingScalar, Dynamic, 1>::Constant(1, AnnoyingScalar(3));
Matrix<AnnoyingScalar, Dynamic, 1> customResult(5);
customResult.noalias() = custom * customRhs;
for (Index i = 0; i < customResult.size(); ++i) VERIFY_IS_EQUAL(customResult[i], AnnoyingScalar(6));
MatrixXd column = MatrixXd::Constant(20000, 1, 2.0);
VectorXd factor = VectorXd::Constant(1, 3.0);
VectorXd backing = VectorXd::Zero(2 * column.rows());
Map<VectorXd, Unaligned, InnerStride<2>> strided(backing.data(), column.rows());
internal::set_is_malloc_allowed(false);
strided.noalias() += 0.75 * column * factor;
internal::set_is_malloc_allowed(true);
for (Index i = 0; i < column.rows(); ++i) {
VERIFY_IS_EQUAL(strided[i], 4.5);
VERIFY_IS_EQUAL(backing[2 * i + 1], 0.0);
}
}