// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2006-2010 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 "main.h" template void diagonal(const MatrixType& m) { typedef typename MatrixType::Scalar Scalar; Index rows = m.rows(); Index cols = m.cols(); MatrixType m1 = MatrixType::Random(rows, cols), m2 = MatrixType::Random(rows, cols); Scalar s1 = internal::random(); // check diagonal() VERIFY_IS_APPROX(m1.diagonal(), m1.transpose().diagonal()); m2.diagonal() = 2 * m1.diagonal(); m2.diagonal()[0] *= 3; if (rows > 2) { enum { N1 = MatrixType::RowsAtCompileTime > 2 ? 2 : 0, N2 = MatrixType::RowsAtCompileTime > 1 ? -1 : 0 }; // check sub/super diagonal if (MatrixType::SizeAtCompileTime != Dynamic) { VERIFY(m1.template diagonal().RowsAtCompileTime == m1.diagonal(N1).size()); VERIFY(m1.template diagonal().RowsAtCompileTime == m1.diagonal(N2).size()); } m2.template diagonal() = 2 * m1.template diagonal(); VERIFY_IS_APPROX(m2.template diagonal(), static_cast(2) * m1.diagonal(N1)); m2.template diagonal()[0] *= 3; VERIFY_IS_APPROX(m2.template diagonal()[0], static_cast(6) * m1.template diagonal()[0]); m2.template diagonal() = 2 * m1.template diagonal(); m2.template diagonal()[0] *= 3; VERIFY_IS_APPROX(m2.template diagonal()[0], static_cast(6) * m1.template diagonal()[0]); m2.diagonal(N1) = 2 * m1.diagonal(N1); VERIFY_IS_APPROX(m2.template diagonal(), static_cast(2) * m1.diagonal(N1)); m2.diagonal(N1)[0] *= 3; VERIFY_IS_APPROX(m2.diagonal(N1)[0], static_cast(6) * m1.diagonal(N1)[0]); m2.diagonal(N2) = 2 * m1.diagonal(N2); VERIFY_IS_APPROX(m2.template diagonal(), static_cast(2) * m1.diagonal(N2)); m2.diagonal(N2)[0] *= 3; VERIFY_IS_APPROX(m2.diagonal(N2)[0], static_cast(6) * m1.diagonal(N2)[0]); m2.diagonal(N2).x() = s1; VERIFY_IS_APPROX(m2.diagonal(N2).x(), s1); m2.diagonal(N2).coeffRef(0) = Scalar(2) * s1; VERIFY_IS_APPROX(m2.diagonal(N2).coeff(0), Scalar(2) * s1); } VERIFY(m1.diagonal(cols).size() == 0); VERIFY(m1.diagonal(-rows).size() == 0); } template void diagonal_assert(const MatrixType& m) { Index rows = m.rows(); Index cols = m.cols(); MatrixType m1 = MatrixType::Random(rows, cols); if (rows >= 2 && cols >= 2) { VERIFY_RAISES_ASSERT(m1 += m1.diagonal()); VERIFY_RAISES_ASSERT(m1 -= m1.diagonal()); VERIFY_RAISES_ASSERT(m1.array() *= m1.diagonal().array()); VERIFY_RAISES_ASSERT(m1.array() /= m1.diagonal().array()); } VERIFY_RAISES_ASSERT(m1.diagonal(cols + 1)); VERIFY_RAISES_ASSERT(m1.diagonal(-(rows + 1))); } // Test that (A * B).diagonal() gives the same result as (A * B).eval().diagonal(). // The diagonal-of-product path uses LazyProduct evaluation (see ProductEvaluators.h), // which avoids computing the full product. Verify this optimization is correct. template void diagonal_of_product() { const Index PS = internal::packet_traits::size; const Index sizes[] = {1, 2, 3, PS - 1, PS, PS + 1, 2 * PS - 1, 2 * PS, 2 * PS + 1, 4 * PS, 4 * PS + 1}; typedef Matrix Mat; typedef Matrix Vec; for (int si = 0; si < 11; ++si) { Index n = sizes[si]; if (n <= 0) continue; Mat A = Mat::Random(n, n); Mat B = Mat::Random(n, n); // Lazy diagonal vs explicit product diagonal Vec diag_lazy = (A * B).diagonal(); Vec diag_explicit = (A * B).eval().diagonal(); VERIFY_IS_APPROX(diag_lazy, diag_explicit); // Also test non-square: A is m×k, B is k×n for (int k : {1, 3, (int)n}) { if (k <= 0) continue; Mat C = Mat::Random(n, k); Mat D = Mat::Random(k, n); Vec diag_lazy2 = (C * D).diagonal(); Vec diag_explicit2 = (C * D).eval().diagonal(); VERIFY_IS_APPROX(diag_lazy2, diag_explicit2); } } } // Test .select() at vectorization boundary sizes. // select() uses CwiseTernaryOp which has packet-level evaluation with remainder handling. template void select_boundary() { const Index PS = internal::packet_traits::size; const Index sizes[] = {1, 2, 3, PS - 1, PS, PS + 1, 2 * PS - 1, 2 * PS, 2 * PS + 1, 4 * PS, 4 * PS + 1}; typedef Array Arr; for (int si = 0; si < 11; ++si) { Index n = sizes[si]; if (n <= 0) continue; Arr a = Arr::Random(n); Arr b = Arr::Random(n); auto cond = (a > Scalar(0)); // select with two arrays Arr result = cond.select(a, b); for (Index k = 0; k < n; ++k) { Scalar expected = (a(k) > Scalar(0)) ? a(k) : b(k); VERIFY_IS_APPROX(result(k), expected); } // select with scalar else Arr result2 = cond.select(a, Scalar(0)); for (Index k = 0; k < n; ++k) { Scalar expected = (a(k) > Scalar(0)) ? a(k) : Scalar(0); VERIFY_IS_APPROX(result2(k), expected); } // select with scalar then Arr result3 = cond.select(Scalar(42), b); for (Index k = 0; k < n; ++k) { Scalar expected = (a(k) > Scalar(0)) ? Scalar(42) : b(k); VERIFY_IS_APPROX(result3(k), expected); } } } EIGEN_DECLARE_TEST(diagonal) { for (int i = 0; i < g_repeat; i++) { CALL_SUBTEST_1(diagonal(Matrix())); CALL_SUBTEST_1(diagonal(Matrix())); CALL_SUBTEST_1(diagonal(Matrix())); CALL_SUBTEST_2(diagonal(Matrix4d())); CALL_SUBTEST_2(diagonal( MatrixXcf(internal::random(1, EIGEN_TEST_MAX_SIZE), internal::random(1, EIGEN_TEST_MAX_SIZE)))); CALL_SUBTEST_2(diagonal( MatrixXi(internal::random(1, EIGEN_TEST_MAX_SIZE), internal::random(1, EIGEN_TEST_MAX_SIZE)))); CALL_SUBTEST_2(diagonal( MatrixXcd(internal::random(1, EIGEN_TEST_MAX_SIZE), internal::random(1, EIGEN_TEST_MAX_SIZE)))); CALL_SUBTEST_1(diagonal( MatrixXf(internal::random(1, EIGEN_TEST_MAX_SIZE), internal::random(1, EIGEN_TEST_MAX_SIZE)))); CALL_SUBTEST_1(diagonal(Matrix(3, 4))); CALL_SUBTEST_1(diagonal_assert( MatrixXf(internal::random(1, EIGEN_TEST_MAX_SIZE), internal::random(1, EIGEN_TEST_MAX_SIZE)))); } // Diagonal-of-product optimization (deterministic, outside g_repeat). CALL_SUBTEST_3(diagonal_of_product()); CALL_SUBTEST_3(diagonal_of_product()); CALL_SUBTEST_3(diagonal_of_product>()); // Select at vectorization boundaries (deterministic, outside g_repeat). CALL_SUBTEST_4(select_boundary()); CALL_SUBTEST_4(select_boundary()); CALL_SUBTEST_4(select_boundary()); }