// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2006-2008 Benoit Jacob // // 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/. #include "product.h" #include // regression test for bug 447 template void product1x1() { Matrix matAstatic; Matrix matBstatic; matAstatic.setRandom(); matBstatic.setRandom(); VERIFY_IS_APPROX((matAstatic * matBstatic).coeff(0, 0), matAstatic.cwiseProduct(matBstatic.transpose()).sum()); MatrixXf matAdynamic(1, 3); MatrixXf matBdynamic(3, 1); matAdynamic.setRandom(); matBdynamic.setRandom(); VERIFY_IS_APPROX((matAdynamic * matBdynamic).coeff(0, 0), matAdynamic.cwiseProduct(matBdynamic.transpose()).sum()); } template const TC &ref_prod(TC &C, const TA &A, const TB &B) { for (Index i = 0; i < C.rows(); ++i) for (Index j = 0; j < C.cols(); ++j) for (Index k = 0; k < A.cols(); ++k) C.coeffRef(i, j) += A.coeff(i, k) * B.coeff(k, j); return C; } template std::enable_if_t test_lazy_single(int rows, int cols, int depth) { Matrix A(rows, depth); A.setRandom(); Matrix B(depth, cols); B.setRandom(); Matrix C(rows, cols); C.setRandom(); Matrix D(C); VERIFY_IS_APPROX(C += A.lazyProduct(B), ref_prod(D, A, B)); } void test_dynamic_bool() { int rows = internal::random(1, 64); int cols = internal::random(1, 64); int depth = internal::random(1, 65); typedef Matrix MatrixX; MatrixX A(rows, depth); A.setRandom(); MatrixX B(depth, cols); B.setRandom(); MatrixX C(rows, cols); C.setRandom(); MatrixX D(C); for (Index i = 0; i < C.rows(); ++i) for (Index j = 0; j < C.cols(); ++j) for (Index k = 0; k < A.cols(); ++k) D.coeffRef(i, j) |= (A.coeff(i, k) && B.coeff(k, j)); C += A * B; VERIFY_IS_EQUAL(C, D); MatrixX E = B.transpose(); for (Index i = 0; i < B.rows(); ++i) for (Index j = 0; j < B.cols(); ++j) VERIFY_IS_EQUAL(B(i, j), E(j, i)); } template std::enable_if_t<((Rows == 1 && Depth != 1 && OA == ColMajor) || (Depth == 1 && Rows != 1 && OA == RowMajor) || (Cols == 1 && Depth != 1 && OB == RowMajor) || (Depth == 1 && Cols != 1 && OB == ColMajor) || (Rows == 1 && Cols != 1 && OC == ColMajor) || (Cols == 1 && Rows != 1 && OC == RowMajor)), void> test_lazy_single(int, int, int) {} template void test_lazy_all_layout(int rows = Rows, int cols = Cols, int depth = Depth) { CALL_SUBTEST((test_lazy_single(rows, cols, depth))); CALL_SUBTEST((test_lazy_single(rows, cols, depth))); CALL_SUBTEST((test_lazy_single(rows, cols, depth))); CALL_SUBTEST((test_lazy_single(rows, cols, depth))); CALL_SUBTEST((test_lazy_single(rows, cols, depth))); CALL_SUBTEST((test_lazy_single(rows, cols, depth))); CALL_SUBTEST((test_lazy_single(rows, cols, depth))); CALL_SUBTEST((test_lazy_single(rows, cols, depth))); } template void test_lazy_l1() { int rows = internal::random(1, 12); int cols = internal::random(1, 12); int depth = internal::random(1, 12); // Inner CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout(1, 1, depth))); // Outer CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout(4, cols))); CALL_SUBTEST((test_lazy_all_layout(7, cols))); CALL_SUBTEST((test_lazy_all_layout(rows))); CALL_SUBTEST((test_lazy_all_layout(rows))); CALL_SUBTEST((test_lazy_all_layout(rows, cols))); } template void test_lazy_l2() { int rows = internal::random(1, 12); int cols = internal::random(1, 12); int depth = internal::random(1, 12); // mat-vec CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout(rows))); CALL_SUBTEST((test_lazy_all_layout(4, 1, depth))); CALL_SUBTEST((test_lazy_all_layout(rows, 1, depth))); // vec-mat CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout(1, cols))); CALL_SUBTEST((test_lazy_all_layout(1, 4, depth))); CALL_SUBTEST((test_lazy_all_layout(1, cols, depth))); } template void test_lazy_l3() { int rows = internal::random(1, 12); int cols = internal::random(1, 12); int depth = internal::random(1, 12); // mat-mat CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout(rows))); CALL_SUBTEST((test_lazy_all_layout(4, 3, depth))); CALL_SUBTEST((test_lazy_all_layout(rows, 6, depth))); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout())); CALL_SUBTEST((test_lazy_all_layout(8, cols))); CALL_SUBTEST((test_lazy_all_layout(3, 4, depth))); CALL_SUBTEST((test_lazy_all_layout(4, cols, depth))); } template void test_linear_but_not_vectorizable() { // Check tricky cases for which the result of the product is a vector and thus must exhibit the LinearBit flag, // but is not vectorizable along the linear dimension. Index n = N == Dynamic ? internal::random(1, 32) : N; Index m = M == Dynamic ? internal::random(1, 32) : M; Index k = K == Dynamic ? internal::random(1, 32) : K; { Matrix A; A.setRandom(n, m + 1); Matrix B; B.setRandom(m * 2, k); Matrix C; Matrix R; C.noalias() = A.template topLeftCorner<1, M>() * (B.template topRows() + B.template bottomRows()); R.noalias() = A.template topLeftCorner<1, M>() * (B.template topRows() + B.template bottomRows()).eval(); VERIFY_IS_APPROX(C, R); } { Matrix A; A.setRandom(m + 1, n); Matrix B; B.setRandom(k, m * 2); Matrix C; Matrix R; C.noalias() = (B.template leftCols() + B.template rightCols()) * A.template topLeftCorner(); R.noalias() = (B.template leftCols() + B.template rightCols()).eval() * A.template topLeftCorner(); VERIFY_IS_APPROX(C, R); } } template void bug_1311() { Matrix A; A.setRandom(); Vector2d b = Vector2d::Random(); Matrix res; res.noalias() = 1. * (A * b); VERIFY_IS_APPROX(res, A * b); res.noalias() = 1. * A * b; VERIFY_IS_APPROX(res, A * b); res.noalias() = (1. * A).lazyProduct(b); VERIFY_IS_APPROX(res, A * b); res.noalias() = (1. * A).lazyProduct(1. * b); VERIFY_IS_APPROX(res, A * b); res.noalias() = (A).lazyProduct(1. * b); VERIFY_IS_APPROX(res, A * b); } template void product_small_regressions() { { // test compilation of (outer_product) * vector Vector3f v = Vector3f::Random(); VERIFY_IS_APPROX((v * v.transpose()) * v, (v * v.transpose()).eval() * v); } { // regression test for pull-request #93 Eigen::Matrix A; A.setRandom(); Eigen::Matrix B; B.setRandom(); Eigen::Matrix C; C.setRandom(); VERIFY_IS_APPROX(B * A.inverse(), B * A.inverse()[0]); VERIFY_IS_APPROX(A.inverse() * C, A.inverse()[0] * C); } { Eigen::Matrix A, B, C; A.setRandom(); C = A; for (int k = 0; k < 79; ++k) C = C * A; B.noalias() = (((A * A) * (A * A)) * ((A * A) * (A * A)) * ((A * A) * (A * A)) * ((A * A) * (A * A)) * ((A * A) * (A * A)) * ((A * A) * (A * A)) * ((A * A) * (A * A)) * ((A * A) * (A * A)) * ((A * A) * (A * A)) * ((A * A) * (A * A))) * (((A * A) * (A * A)) * ((A * A) * (A * A)) * ((A * A) * (A * A)) * ((A * A) * (A * A)) * ((A * A) * (A * A)) * ((A * A) * (A * A)) * ((A * A) * (A * A)) * ((A * A) * (A * A)) * ((A * A) * (A * A)) * ((A * A) * (A * A))); VERIFY_IS_APPROX(B, C); } } template void product_sweep(int max_m, int max_k, int max_n) { using Matrix = Eigen::Matrix; for (int m = 1; m < max_m; ++m) { for (int n = 1; n < max_n; ++n) { Matrix C = Matrix::Zero(m, n); Matrix Cref = Matrix::Zero(m, n); for (int k = 1; k < max_k; ++k) { Matrix A = Matrix::Random(m, k); Matrix B = Matrix::Random(k, n); C = A * B; Cref.setZero(); ref_prod(Cref, A, B); VERIFY_IS_APPROX(C, Cref); } } } } EIGEN_DECLARE_TEST(product_small) { for (int i = 0; i < g_repeat; i++) { CALL_SUBTEST_1(product(Matrix())); CALL_SUBTEST_2(product(Matrix())); CALL_SUBTEST_8(product(Matrix())); CALL_SUBTEST_3(product(Matrix3d())); CALL_SUBTEST_4(product(Matrix4d())); CALL_SUBTEST_5(product(Matrix4f())); CALL_SUBTEST_10(product(Matrix())); CALL_SUBTEST_6(product1x1<0>()); CALL_SUBTEST_11(test_lazy_l1()); CALL_SUBTEST_12(test_lazy_l2()); CALL_SUBTEST_13(test_lazy_l3()); CALL_SUBTEST_21(test_lazy_l1()); CALL_SUBTEST_22(test_lazy_l2()); CALL_SUBTEST_23(test_lazy_l3()); CALL_SUBTEST_31(test_lazy_l1 >()); CALL_SUBTEST_32(test_lazy_l2 >()); CALL_SUBTEST_33(test_lazy_l3 >()); CALL_SUBTEST_41(test_lazy_l1 >()); CALL_SUBTEST_42(test_lazy_l2 >()); CALL_SUBTEST_43(test_lazy_l3 >()); CALL_SUBTEST_7((test_linear_but_not_vectorizable())); CALL_SUBTEST_7((test_linear_but_not_vectorizable())); CALL_SUBTEST_7((test_linear_but_not_vectorizable())); CALL_SUBTEST_6(bug_1311<3>()); CALL_SUBTEST_6(bug_1311<5>()); CALL_SUBTEST_9(test_dynamic_bool()); // Commonly specialized vectorized types. CALL_SUBTEST_50(product_sweep(10, 10, 10)); CALL_SUBTEST_51(product_sweep(10, 10, 10)); CALL_SUBTEST_52(product_sweep(10, 10, 10)); CALL_SUBTEST_53(product_sweep(10, 10, 10)); } CALL_SUBTEST_6(product_small_regressions<0>()); }