diff options
author | Narayan Kamath <narayan@google.com> | 2012-11-02 10:59:05 +0000 |
---|---|---|
committer | Xiaotao Duan <xiaotao@google.com> | 2012-11-07 14:17:48 -0800 |
commit | c981c48f5bc9aefeffc0bcb0cc3934c2fae179dd (patch) | |
tree | 54d1c7d66098154c1d7c5bd414394ef4cf255810 /test | |
parent | 63f67d748682b46d58be31235a0a2d64d81b998c (diff) | |
download | eigen-c981c48f5bc9aefeffc0bcb0cc3934c2fae179dd.tar.gz |
Initial import of eigen 3.1.1android-cts-4.4_r4android-cts-4.2_r2android-4.4_r1.2.0.1android-4.4_r1.2android-4.4_r1.1.0.1android-4.4_r1.1android-4.4_r1.0.1android-4.4_r1android-4.4_r0.9android-4.4_r0.8android-4.3_r2.3android-4.3_r2.2android-4.3_r2.1android-4.3_r2android-4.3_r1.1android-4.3_r1android-4.3_r0.9.1android-4.3_r0.9android-4.2.2_r1.2android-4.2.2_r1.1android-4.2.2_r1kitkat-releasekitkat-cts-releasejb-mr2.0-releasejb-mr2-releasejb-mr1.1-releasejb-mr1.1-dev
Added a README.android and a MODULE_LICENSE_MPL2 file.
Added empty Android.mk and CleanSpec.mk to optimize Android build.
Non MPL2 license code is disabled in ./Eigen/src/Core/util/NonMPL2.h.
Trying to include such files will lead to an error.
Change-Id: I0e148b7c3e83999bcc4dfaa5809d33bfac2aac32
Diffstat (limited to 'test')
163 files changed, 20861 insertions, 0 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 000000000..6f8fc4ae3 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,243 @@ + +# generate split test header file +message(STATUS ${CMAKE_CURRENT_BINARY_DIR}) +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/split_test_helper.h "") +foreach(i RANGE 1 999) + file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/split_test_helper.h + "#ifdef EIGEN_TEST_PART_${i}\n" + "#define CALL_SUBTEST_${i}(FUNC) CALL_SUBTEST(FUNC)\n" + "#else\n" + "#define CALL_SUBTEST_${i}(FUNC)\n" + "#endif\n\n" + ) +endforeach() + +# configure blas/lapack (use Eigen's ones) +set(BLAS_FOUND TRUE) +set(LAPACK_FOUND TRUE) +set(BLAS_LIBRARIES eigen_blas) +set(LAPACK_LIBRARIES eigen_lapack) + +set(EIGEN_TEST_MATRIX_DIR "" CACHE STRING "Enable testing of realword sparse matrices contained in the specified path") +if(EIGEN_TEST_MATRIX_DIR) + if(NOT WIN32) + message(STATUS "Test realworld sparse matrices: ${EIGEN_TEST_MATRIX_DIR}") + add_definitions( -DTEST_REAL_CASES="${EIGEN_TEST_MATRIX_DIR}" ) + else(NOT WIN32) + message(STATUS "REAL CASES CAN NOT BE CURRENTLY TESTED ON WIN32") + endif(NOT WIN32) +endif(EIGEN_TEST_MATRIX_DIR) + +set(SPARSE_LIBS " ") + +find_package(Cholmod) +if(CHOLMOD_FOUND AND BLAS_FOUND AND LAPACK_FOUND) + add_definitions("-DEIGEN_CHOLMOD_SUPPORT") + include_directories(${CHOLMOD_INCLUDES}) + set(SPARSE_LIBS ${SPARSE_LIBS} ${CHOLMOD_LIBRARIES} ${BLAS_LIBRARIES} ${LAPACK_LIBRARIES}) + set(CHOLMOD_ALL_LIBS ${CHOLMOD_LIBRARIES} ${BLAS_LIBRARIES} ${LAPACK_LIBRARIES}) + ei_add_property(EIGEN_TESTED_BACKENDS "Cholmod, ") +else() + ei_add_property(EIGEN_MISSING_BACKENDS "Cholmod, ") +endif() + +find_package(Umfpack) +if(UMFPACK_FOUND AND BLAS_FOUND) + add_definitions("-DEIGEN_UMFPACK_SUPPORT") + include_directories(${UMFPACK_INCLUDES}) + set(SPARSE_LIBS ${SPARSE_LIBS} ${UMFPACK_LIBRARIES} ${BLAS_LIBRARIES}) + set(UMFPACK_ALL_LIBS ${UMFPACK_LIBRARIES} ${BLAS_LIBRARIES}) + ei_add_property(EIGEN_TESTED_BACKENDS "UmfPack, ") +else() + ei_add_property(EIGEN_MISSING_BACKENDS "UmfPack, ") +endif() + +find_package(SuperLU) +if(SUPERLU_FOUND AND BLAS_FOUND) + add_definitions("-DEIGEN_SUPERLU_SUPPORT") + include_directories(${SUPERLU_INCLUDES}) + set(SPARSE_LIBS ${SPARSE_LIBS} ${SUPERLU_LIBRARIES} ${BLAS_LIBRARIES}) + set(SUPERLU_ALL_LIBS ${SUPERLU_LIBRARIES} ${BLAS_LIBRARIES}) + ei_add_property(EIGEN_TESTED_BACKENDS "SuperLU, ") +else() + ei_add_property(EIGEN_MISSING_BACKENDS "SuperLU, ") +endif() + + +find_package(Pastix) +find_package(Scotch) +find_package(Metis) +if(PASTIX_FOUND AND BLAS_FOUND) + add_definitions("-DEIGEN_PASTIX_SUPPORT") + include_directories(${PASTIX_INCLUDES}) + if(SCOTCH_FOUND) + include_directories(${SCOTCH_INCLUDES}) + set(PASTIX_LIBRARIES ${PASTIX_LIBRARIES} ${SCOTCH_LIBRARIES}) + elseif(METIS_FOUND) + include_directories(${METIS_INCLUDES}) + set(PASTIX_LIBRARIES ${PASTIX_LIBRARIES} ${METIS_LIBRARIES}) + else(SCOTCH_FOUND) + ei_add_property(EIGEN_MISSING_BACKENDS "PaStiX, ") + endif(SCOTCH_FOUND) + set(SPARSE_LIBS ${SPARSE_LIBS} ${PASTIX_LIBRARIES} ${ORDERING_LIBRARIES} ${BLAS_LIBRARIES}) + set(PASTIX_ALL_LIBS ${PASTIX_LIBRARIES} ${BLAS_LIBRARIES}) + ei_add_property(EIGEN_TESTED_BACKENDS "PaStiX, ") +else() + ei_add_property(EIGEN_MISSING_BACKENDS "PaStiX, ") +endif() + +option(EIGEN_TEST_NOQT "Disable Qt support in unit tests" OFF) +if(NOT EIGEN_TEST_NOQT) + find_package(Qt4) + if(QT4_FOUND) + include(${QT_USE_FILE}) + ei_add_property(EIGEN_TESTED_BACKENDS "Qt4 support, ") + else() + ei_add_property(EIGEN_MISSING_BACKENDS "Qt4 support, ") + endif() +endif(NOT EIGEN_TEST_NOQT) + +if(TEST_LIB) + add_definitions("-DEIGEN_EXTERN_INSTANTIATIONS=1") +endif(TEST_LIB) + +ei_add_test(meta) +ei_add_test(sizeof) +ei_add_test(dynalloc) +ei_add_test(nomalloc) +ei_add_test(first_aligned) +ei_add_test(mixingtypes) +ei_add_test(packetmath) +ei_add_test(unalignedassert) +ei_add_test(vectorization_logic) +ei_add_test(basicstuff) +ei_add_test(linearstructure) +ei_add_test(integer_types) +ei_add_test(cwiseop) +ei_add_test(unalignedcount) +ei_add_test(exceptions) +ei_add_test(redux) +ei_add_test(visitor) +ei_add_test(block) +ei_add_test(corners) +ei_add_test(product_small) +ei_add_test(product_large) +ei_add_test(product_extra) +ei_add_test(diagonalmatrices) +ei_add_test(adjoint) +ei_add_test(diagonal) +ei_add_test(miscmatrices) +ei_add_test(commainitializer) +ei_add_test(smallvectors) +ei_add_test(map) +ei_add_test(mapstride) +ei_add_test(mapstaticmethods) +ei_add_test(array) +ei_add_test(array_for_matrix) +ei_add_test(array_replicate) +ei_add_test(array_reverse) +ei_add_test(triangular) +ei_add_test(selfadjoint) +ei_add_test(product_selfadjoint) +ei_add_test(product_symm) +ei_add_test(product_syrk) +ei_add_test(product_trmv) +ei_add_test(product_trmm) +ei_add_test(product_trsolve) +ei_add_test(product_mmtr) +ei_add_test(product_notemporary) +ei_add_test(stable_norm) +ei_add_test(bandmatrix) +ei_add_test(cholesky) +ei_add_test(lu) +ei_add_test(determinant) +ei_add_test(inverse) +ei_add_test(qr) +ei_add_test(qr_colpivoting) +ei_add_test(qr_fullpivoting) +ei_add_test(upperbidiagonalization) +ei_add_test(hessenberg) +ei_add_test(schur_real) +ei_add_test(schur_complex) +ei_add_test(eigensolver_selfadjoint) +ei_add_test(eigensolver_generic) +ei_add_test(eigensolver_complex) +ei_add_test(jacobi) +ei_add_test(jacobisvd) +ei_add_test(geo_orthomethods) +ei_add_test(geo_homogeneous) +ei_add_test(geo_quaternion) +ei_add_test(geo_transformations) +ei_add_test(geo_eulerangles) +ei_add_test(geo_hyperplane) +ei_add_test(geo_parametrizedline) +ei_add_test(geo_alignedbox) +ei_add_test(stdvector) +ei_add_test(stdvector_overload) +ei_add_test(stdlist) +ei_add_test(stddeque) +ei_add_test(resize) +if(QT4_FOUND) + ei_add_test(qtvector "" "${QT_QTCORE_LIBRARY}") +endif(QT4_FOUND) +ei_add_test(sparse_vector) +ei_add_test(sparse_basic) +ei_add_test(sparse_product) +ei_add_test(sparse_solvers) +ei_add_test(umeyama) +ei_add_test(householder) +ei_add_test(swap) +ei_add_test(conservative_resize) +ei_add_test(permutationmatrices) +ei_add_test(sparse_permutations) +ei_add_test(eigen2support) +ei_add_test(nullary) +ei_add_test(nesting_ops "${CMAKE_CXX_FLAGS_DEBUG}") +ei_add_test(zerosized) +ei_add_test(dontalign) +ei_add_test(sizeoverflow) +ei_add_test(prec_inverse_4x4) +ei_add_test(vectorwiseop) + +ei_add_test(simplicial_cholesky) +ei_add_test(conjugate_gradient) +ei_add_test(bicgstab) + + +if(UMFPACK_FOUND) + ei_add_test(umfpack_support "" "${UMFPACK_ALL_LIBS}") +endif() + +if(SUPERLU_FOUND) + ei_add_test(superlu_support "" "${SUPERLU_ALL_LIBS}") +endif() + +if(CHOLMOD_FOUND) + ei_add_test(cholmod_support "" "${CHOLMOD_ALL_LIBS}") +endif() + +if(PARDISO_FOUND) + ei_add_test(pardiso_support "" "${PARDISO_ALL_LIBS}") +endif() + +if(PASTIX_FOUND AND (SCOTCH_FOUND OR METIS_FOUND)) + ei_add_test(pastix_support "" "${PASTIX_ALL_LIBS}") +endif() + +string(TOLOWER "${CMAKE_CXX_COMPILER}" cmake_cxx_compiler_tolower) +if(cmake_cxx_compiler_tolower MATCHES "qcc") + set(CXX_IS_QCC "ON") +endif() + +ei_add_property(EIGEN_TESTING_SUMMARY "CXX: ${CMAKE_CXX_COMPILER}\n") +if(CMAKE_COMPILER_IS_GNUCXX AND NOT CXX_IS_QCC) + execute_process(COMMAND ${CMAKE_CXX_COMPILER} --version COMMAND head -n 1 OUTPUT_VARIABLE EIGEN_CXX_VERSION_STRING OUTPUT_STRIP_TRAILING_WHITESPACE) + ei_add_property(EIGEN_TESTING_SUMMARY "CXX_VERSION: ${EIGEN_CXX_VERSION_STRING}\n") +endif() +ei_add_property(EIGEN_TESTING_SUMMARY "CXX_FLAGS: ${CMAKE_CXX_FLAGS}\n") +ei_add_property(EIGEN_TESTING_SUMMARY "Sparse lib flags: ${SPARSE_LIBS}\n") + +option(EIGEN_TEST_EIGEN2 "Run whole Eigen2 test suite against EIGEN2_SUPPORT" OFF) +if(EIGEN_TEST_EIGEN2) + add_subdirectory(eigen2) +endif() diff --git a/test/adjoint.cpp b/test/adjoint.cpp new file mode 100644 index 000000000..b6cf0a68b --- /dev/null +++ b/test/adjoint.cpp @@ -0,0 +1,141 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#define EIGEN_NO_STATIC_ASSERT + +#include "main.h" + +template<typename MatrixType> void adjoint(const MatrixType& m) +{ + /* this test covers the following files: + Transpose.h Conjugate.h Dot.h + */ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> SquareMatrixType; + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols), + square = SquareMatrixType::Random(rows, rows); + VectorType v1 = VectorType::Random(rows), + v2 = VectorType::Random(rows), + v3 = VectorType::Random(rows), + vzero = VectorType::Zero(rows); + + Scalar s1 = internal::random<Scalar>(), + s2 = internal::random<Scalar>(); + + // check basic compatibility of adjoint, transpose, conjugate + VERIFY_IS_APPROX(m1.transpose().conjugate().adjoint(), m1); + VERIFY_IS_APPROX(m1.adjoint().conjugate().transpose(), m1); + + // check multiplicative behavior + VERIFY_IS_APPROX((m1.adjoint() * m2).adjoint(), m2.adjoint() * m1); + VERIFY_IS_APPROX((s1 * m1).adjoint(), internal::conj(s1) * m1.adjoint()); + + // check basic properties of dot, norm, norm2 + typedef typename NumTraits<Scalar>::Real RealScalar; + + RealScalar ref = NumTraits<Scalar>::IsInteger ? RealScalar(0) : (std::max)((s1 * v1 + s2 * v2).norm(),v3.norm()); + VERIFY(test_isApproxWithRef((s1 * v1 + s2 * v2).dot(v3), internal::conj(s1) * v1.dot(v3) + internal::conj(s2) * v2.dot(v3), ref)); + VERIFY(test_isApproxWithRef(v3.dot(s1 * v1 + s2 * v2), s1*v3.dot(v1)+s2*v3.dot(v2), ref)); + VERIFY_IS_APPROX(internal::conj(v1.dot(v2)), v2.dot(v1)); + VERIFY_IS_APPROX(internal::real(v1.dot(v1)), v1.squaredNorm()); + if(!NumTraits<Scalar>::IsInteger) { + VERIFY_IS_APPROX(v1.squaredNorm(), v1.norm() * v1.norm()); + // check normalized() and normalize() + VERIFY_IS_APPROX(v1, v1.norm() * v1.normalized()); + v3 = v1; + v3.normalize(); + VERIFY_IS_APPROX(v1, v1.norm() * v3); + VERIFY_IS_APPROX(v3, v1.normalized()); + VERIFY_IS_APPROX(v3.norm(), RealScalar(1)); + } + VERIFY_IS_MUCH_SMALLER_THAN(internal::abs(vzero.dot(v1)), static_cast<RealScalar>(1)); + + // check compatibility of dot and adjoint + + ref = NumTraits<Scalar>::IsInteger ? 0 : (std::max)((std::max)(v1.norm(),v2.norm()),(std::max)((square * v2).norm(),(square.adjoint() * v1).norm())); + VERIFY(test_isApproxWithRef(v1.dot(square * v2), (square.adjoint() * v1).dot(v2), ref)); + + // like in testBasicStuff, test operator() to check const-qualification + Index r = internal::random<Index>(0, rows-1), + c = internal::random<Index>(0, cols-1); + VERIFY_IS_APPROX(m1.conjugate()(r,c), internal::conj(m1(r,c))); + VERIFY_IS_APPROX(m1.adjoint()(c,r), internal::conj(m1(r,c))); + + if(!NumTraits<Scalar>::IsInteger) + { + // check that Random().normalized() works: tricky as the random xpr must be evaluated by + // normalized() in order to produce a consistent result. + VERIFY_IS_APPROX(VectorType::Random(rows).normalized().norm(), RealScalar(1)); + } + + // check inplace transpose + m3 = m1; + m3.transposeInPlace(); + VERIFY_IS_APPROX(m3,m1.transpose()); + m3.transposeInPlace(); + VERIFY_IS_APPROX(m3,m1); + + // check inplace adjoint + m3 = m1; + m3.adjointInPlace(); + VERIFY_IS_APPROX(m3,m1.adjoint()); + m3.transposeInPlace(); + VERIFY_IS_APPROX(m3,m1.conjugate()); + + // check mixed dot product + typedef Matrix<RealScalar, MatrixType::RowsAtCompileTime, 1> RealVectorType; + RealVectorType rv1 = RealVectorType::Random(rows); + VERIFY_IS_APPROX(v1.dot(rv1.template cast<Scalar>()), v1.dot(rv1)); + VERIFY_IS_APPROX(rv1.template cast<Scalar>().dot(v1), rv1.dot(v1)); +} + +void test_adjoint() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( adjoint(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( adjoint(Matrix3d()) ); + CALL_SUBTEST_3( adjoint(Matrix4f()) ); + CALL_SUBTEST_4( adjoint(MatrixXcf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2), internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2))) ); + CALL_SUBTEST_5( adjoint(MatrixXi(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_6( adjoint(MatrixXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + } + // test a large static matrix only once + CALL_SUBTEST_7( adjoint(Matrix<float, 100, 100>()) ); + +#ifdef EIGEN_TEST_PART_4 + { + MatrixXcf a(10,10), b(10,10); + VERIFY_RAISES_ASSERT(a = a.transpose()); + VERIFY_RAISES_ASSERT(a = a.transpose() + b); + VERIFY_RAISES_ASSERT(a = b + a.transpose()); + VERIFY_RAISES_ASSERT(a = a.conjugate().transpose()); + VERIFY_RAISES_ASSERT(a = a.adjoint()); + VERIFY_RAISES_ASSERT(a = a.adjoint() + b); + VERIFY_RAISES_ASSERT(a = b + a.adjoint()); + + // no assertion should be triggered for these cases: + a.transpose() = a.transpose(); + a.transpose() += a.transpose(); + a.transpose() += a.transpose() + b; + a.transpose() = a.adjoint(); + a.transpose() += a.adjoint(); + a.transpose() += a.adjoint() + b; + } +#endif +} + diff --git a/test/array.cpp b/test/array.cpp new file mode 100644 index 000000000..3548fa641 --- /dev/null +++ b/test/array.cpp @@ -0,0 +1,303 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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<typename ArrayType> void array(const ArrayType& m) +{ + typedef typename ArrayType::Index Index; + typedef typename ArrayType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Array<Scalar, ArrayType::RowsAtCompileTime, 1> ColVectorType; + typedef Array<Scalar, 1, ArrayType::ColsAtCompileTime> RowVectorType; + + Index rows = m.rows(); + Index cols = m.cols(); + + ArrayType m1 = ArrayType::Random(rows, cols), + m2 = ArrayType::Random(rows, cols), + m3(rows, cols); + + ColVectorType cv1 = ColVectorType::Random(rows); + RowVectorType rv1 = RowVectorType::Random(cols); + + Scalar s1 = internal::random<Scalar>(), + s2 = internal::random<Scalar>(); + + // scalar addition + VERIFY_IS_APPROX(m1 + s1, s1 + m1); + VERIFY_IS_APPROX(m1 + s1, ArrayType::Constant(rows,cols,s1) + m1); + VERIFY_IS_APPROX(s1 - m1, (-m1)+s1 ); + VERIFY_IS_APPROX(m1 - s1, m1 - ArrayType::Constant(rows,cols,s1)); + VERIFY_IS_APPROX(s1 - m1, ArrayType::Constant(rows,cols,s1) - m1); + VERIFY_IS_APPROX((m1*Scalar(2)) - s2, (m1+m1) - ArrayType::Constant(rows,cols,s2) ); + m3 = m1; + m3 += s2; + VERIFY_IS_APPROX(m3, m1 + s2); + m3 = m1; + m3 -= s1; + VERIFY_IS_APPROX(m3, m1 - s1); + + // scalar operators via Maps + m3 = m1; + ArrayType::Map(m1.data(), m1.rows(), m1.cols()) -= ArrayType::Map(m2.data(), m2.rows(), m2.cols()); + VERIFY_IS_APPROX(m1, m3 - m2); + + m3 = m1; + ArrayType::Map(m1.data(), m1.rows(), m1.cols()) += ArrayType::Map(m2.data(), m2.rows(), m2.cols()); + VERIFY_IS_APPROX(m1, m3 + m2); + + m3 = m1; + ArrayType::Map(m1.data(), m1.rows(), m1.cols()) *= ArrayType::Map(m2.data(), m2.rows(), m2.cols()); + VERIFY_IS_APPROX(m1, m3 * m2); + + m3 = m1; + m2 = ArrayType::Random(rows,cols); + m2 = (m2==0).select(1,m2); + ArrayType::Map(m1.data(), m1.rows(), m1.cols()) /= ArrayType::Map(m2.data(), m2.rows(), m2.cols()); + VERIFY_IS_APPROX(m1, m3 / m2); + + // reductions + VERIFY_IS_APPROX(m1.colwise().sum().sum(), m1.sum()); + VERIFY_IS_APPROX(m1.rowwise().sum().sum(), m1.sum()); + if (!internal::isApprox(m1.sum(), (m1+m2).sum(), test_precision<Scalar>())) + VERIFY_IS_NOT_APPROX(((m1+m2).rowwise().sum()).sum(), m1.sum()); + VERIFY_IS_APPROX(m1.colwise().sum(), m1.colwise().redux(internal::scalar_sum_op<Scalar>())); + + // vector-wise ops + m3 = m1; + VERIFY_IS_APPROX(m3.colwise() += cv1, m1.colwise() + cv1); + m3 = m1; + VERIFY_IS_APPROX(m3.colwise() -= cv1, m1.colwise() - cv1); + m3 = m1; + VERIFY_IS_APPROX(m3.rowwise() += rv1, m1.rowwise() + rv1); + m3 = m1; + VERIFY_IS_APPROX(m3.rowwise() -= rv1, m1.rowwise() - rv1); +} + +template<typename ArrayType> void comparisons(const ArrayType& m) +{ + typedef typename ArrayType::Index Index; + typedef typename ArrayType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Array<Scalar, ArrayType::RowsAtCompileTime, 1> VectorType; + + Index rows = m.rows(); + Index cols = m.cols(); + + Index r = internal::random<Index>(0, rows-1), + c = internal::random<Index>(0, cols-1); + + ArrayType m1 = ArrayType::Random(rows, cols), + m2 = ArrayType::Random(rows, cols), + m3(rows, cols); + + VERIFY(((m1 + Scalar(1)) > m1).all()); + VERIFY(((m1 - Scalar(1)) < m1).all()); + if (rows*cols>1) + { + m3 = m1; + m3(r,c) += 1; + VERIFY(! (m1 < m3).all() ); + VERIFY(! (m1 > m3).all() ); + } + + // comparisons to scalar + VERIFY( (m1 != (m1(r,c)+1) ).any() ); + VERIFY( (m1 > (m1(r,c)-1) ).any() ); + VERIFY( (m1 < (m1(r,c)+1) ).any() ); + VERIFY( (m1 == m1(r,c) ).any() ); + + // test Select + VERIFY_IS_APPROX( (m1<m2).select(m1,m2), m1.cwiseMin(m2) ); + VERIFY_IS_APPROX( (m1>m2).select(m1,m2), m1.cwiseMax(m2) ); + Scalar mid = (m1.cwiseAbs().minCoeff() + m1.cwiseAbs().maxCoeff())/Scalar(2); + for (int j=0; j<cols; ++j) + for (int i=0; i<rows; ++i) + m3(i,j) = internal::abs(m1(i,j))<mid ? 0 : m1(i,j); + VERIFY_IS_APPROX( (m1.abs()<ArrayType::Constant(rows,cols,mid)) + .select(ArrayType::Zero(rows,cols),m1), m3); + // shorter versions: + VERIFY_IS_APPROX( (m1.abs()<ArrayType::Constant(rows,cols,mid)) + .select(0,m1), m3); + VERIFY_IS_APPROX( (m1.abs()>=ArrayType::Constant(rows,cols,mid)) + .select(m1,0), m3); + // even shorter version: + VERIFY_IS_APPROX( (m1.abs()<mid).select(0,m1), m3); + + // count + VERIFY(((m1.abs()+1)>RealScalar(0.1)).count() == rows*cols); + + // and/or + VERIFY( (m1<RealScalar(0) && m1>RealScalar(0)).count() == 0); + VERIFY( (m1<RealScalar(0) || m1>=RealScalar(0)).count() == rows*cols); + RealScalar a = m1.abs().mean(); + VERIFY( (m1<-a || m1>a).count() == (m1.abs()>a).count()); + + typedef Array<typename ArrayType::Index, Dynamic, 1> ArrayOfIndices; + + // TODO allows colwise/rowwise for array + VERIFY_IS_APPROX(((m1.abs()+1)>RealScalar(0.1)).colwise().count(), ArrayOfIndices::Constant(cols,rows).transpose()); + VERIFY_IS_APPROX(((m1.abs()+1)>RealScalar(0.1)).rowwise().count(), ArrayOfIndices::Constant(rows, cols)); +} + +template<typename ArrayType> void array_real(const ArrayType& m) +{ + typedef typename ArrayType::Index Index; + typedef typename ArrayType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + + Index rows = m.rows(); + Index cols = m.cols(); + + ArrayType m1 = ArrayType::Random(rows, cols), + m2 = ArrayType::Random(rows, cols), + m3(rows, cols); + + Scalar s1 = internal::random<Scalar>(); + + // these tests are mostly to check possible compilation issues. + VERIFY_IS_APPROX(m1.sin(), std::sin(m1)); + VERIFY_IS_APPROX(m1.sin(), internal::sin(m1)); + VERIFY_IS_APPROX(m1.cos(), std::cos(m1)); + VERIFY_IS_APPROX(m1.cos(), internal::cos(m1)); + VERIFY_IS_APPROX(m1.asin(), std::asin(m1)); + VERIFY_IS_APPROX(m1.asin(), internal::asin(m1)); + VERIFY_IS_APPROX(m1.acos(), std::acos(m1)); + VERIFY_IS_APPROX(m1.acos(), internal::acos(m1)); + VERIFY_IS_APPROX(m1.tan(), std::tan(m1)); + VERIFY_IS_APPROX(m1.tan(), internal::tan(m1)); + + VERIFY_IS_APPROX(internal::cos(m1+RealScalar(3)*m2), internal::cos((m1+RealScalar(3)*m2).eval())); + VERIFY_IS_APPROX(std::cos(m1+RealScalar(3)*m2), std::cos((m1+RealScalar(3)*m2).eval())); + + VERIFY_IS_APPROX(m1.abs().sqrt(), std::sqrt(std::abs(m1))); + VERIFY_IS_APPROX(m1.abs().sqrt(), internal::sqrt(internal::abs(m1))); + VERIFY_IS_APPROX(m1.abs(), internal::sqrt(internal::abs2(m1))); + + VERIFY_IS_APPROX(internal::abs2(internal::real(m1)) + internal::abs2(internal::imag(m1)), internal::abs2(m1)); + VERIFY_IS_APPROX(internal::abs2(std::real(m1)) + internal::abs2(std::imag(m1)), internal::abs2(m1)); + if(!NumTraits<Scalar>::IsComplex) + VERIFY_IS_APPROX(internal::real(m1), m1); + + VERIFY_IS_APPROX(m1.abs().log(), std::log(std::abs(m1))); + VERIFY_IS_APPROX(m1.abs().log(), internal::log(internal::abs(m1))); + + VERIFY_IS_APPROX(m1.exp(), std::exp(m1)); + VERIFY_IS_APPROX(m1.exp() * m2.exp(), std::exp(m1+m2)); + VERIFY_IS_APPROX(m1.exp(), internal::exp(m1)); + VERIFY_IS_APPROX(m1.exp() / m2.exp(), std::exp(m1-m2)); + + VERIFY_IS_APPROX(m1.pow(2), m1.square()); + VERIFY_IS_APPROX(std::pow(m1,2), m1.square()); + + ArrayType exponents = ArrayType::Constant(rows, cols, RealScalar(2)); + VERIFY_IS_APPROX(std::pow(m1,exponents), m1.square()); + + m3 = m1.abs(); + VERIFY_IS_APPROX(m3.pow(RealScalar(0.5)), m3.sqrt()); + VERIFY_IS_APPROX(std::pow(m3,RealScalar(0.5)), m3.sqrt()); + + // scalar by array division + const RealScalar tiny = std::sqrt(std::numeric_limits<RealScalar>::epsilon()); + s1 += Scalar(tiny); + m1 += ArrayType::Constant(rows,cols,Scalar(tiny)); + VERIFY_IS_APPROX(s1/m1, s1 * m1.inverse()); +} + +template<typename ArrayType> void array_complex(const ArrayType& m) +{ + typedef typename ArrayType::Index Index; + + Index rows = m.rows(); + Index cols = m.cols(); + + ArrayType m1 = ArrayType::Random(rows, cols), + m2(rows, cols); + + for (Index i = 0; i < m.rows(); ++i) + for (Index j = 0; j < m.cols(); ++j) + m2(i,j) = std::sqrt(m1(i,j)); + + VERIFY_IS_APPROX(m1.sqrt(), m2); + VERIFY_IS_APPROX(m1.sqrt(), std::sqrt(m1)); + VERIFY_IS_APPROX(m1.sqrt(), internal::sqrt(m1)); +} + +template<typename ArrayType> void min_max(const ArrayType& m) +{ + typedef typename ArrayType::Index Index; + typedef typename ArrayType::Scalar Scalar; + + Index rows = m.rows(); + Index cols = m.cols(); + + ArrayType m1 = ArrayType::Random(rows, cols); + + // min/max with array + Scalar maxM1 = m1.maxCoeff(); + Scalar minM1 = m1.minCoeff(); + + VERIFY_IS_APPROX(ArrayType::Constant(rows,cols, minM1), (m1.min)(ArrayType::Constant(rows,cols, minM1))); + VERIFY_IS_APPROX(m1, (m1.min)(ArrayType::Constant(rows,cols, maxM1))); + + VERIFY_IS_APPROX(ArrayType::Constant(rows,cols, maxM1), (m1.max)(ArrayType::Constant(rows,cols, maxM1))); + VERIFY_IS_APPROX(m1, (m1.max)(ArrayType::Constant(rows,cols, minM1))); + + // min/max with scalar input + VERIFY_IS_APPROX(ArrayType::Constant(rows,cols, minM1), (m1.min)( minM1)); + VERIFY_IS_APPROX(m1, (m1.min)( maxM1)); + + VERIFY_IS_APPROX(ArrayType::Constant(rows,cols, maxM1), (m1.max)( maxM1)); + VERIFY_IS_APPROX(m1, (m1.max)( minM1)); + +} + +void test_array() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( array(Array<float, 1, 1>()) ); + CALL_SUBTEST_2( array(Array22f()) ); + CALL_SUBTEST_3( array(Array44d()) ); + CALL_SUBTEST_4( array(ArrayXXcf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_5( array(ArrayXXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_6( array(ArrayXXi(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + } + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( comparisons(Array<float, 1, 1>()) ); + CALL_SUBTEST_2( comparisons(Array22f()) ); + CALL_SUBTEST_3( comparisons(Array44d()) ); + CALL_SUBTEST_5( comparisons(ArrayXXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_6( comparisons(ArrayXXi(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + } + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( min_max(Array<float, 1, 1>()) ); + CALL_SUBTEST_2( min_max(Array22f()) ); + CALL_SUBTEST_3( min_max(Array44d()) ); + CALL_SUBTEST_5( min_max(ArrayXXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_6( min_max(ArrayXXi(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + } + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( array_real(Array<float, 1, 1>()) ); + CALL_SUBTEST_2( array_real(Array22f()) ); + CALL_SUBTEST_3( array_real(Array44d()) ); + CALL_SUBTEST_5( array_real(ArrayXXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + } + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_4( array_complex(ArrayXXcf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + } + + VERIFY((internal::is_same< internal::global_math_functions_filtering_base<int>::type, int >::value)); + VERIFY((internal::is_same< internal::global_math_functions_filtering_base<float>::type, float >::value)); + VERIFY((internal::is_same< internal::global_math_functions_filtering_base<Array2i>::type, ArrayBase<Array2i> >::value)); + typedef CwiseUnaryOp<internal::scalar_sum_op<double>, ArrayXd > Xpr; + VERIFY((internal::is_same< internal::global_math_functions_filtering_base<Xpr>::type, + ArrayBase<Xpr> + >::value)); +} diff --git a/test/array_for_matrix.cpp b/test/array_for_matrix.cpp new file mode 100644 index 000000000..4b637c3a6 --- /dev/null +++ b/test/array_for_matrix.cpp @@ -0,0 +1,205 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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<typename MatrixType> void array_for_matrix(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> ColVectorType; + typedef Matrix<Scalar, 1, MatrixType::ColsAtCompileTime> RowVectorType; + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols); + + ColVectorType cv1 = ColVectorType::Random(rows); + RowVectorType rv1 = RowVectorType::Random(cols); + + Scalar s1 = internal::random<Scalar>(), + s2 = internal::random<Scalar>(); + + // scalar addition + VERIFY_IS_APPROX(m1.array() + s1, s1 + m1.array()); + VERIFY_IS_APPROX((m1.array() + s1).matrix(), MatrixType::Constant(rows,cols,s1) + m1); + VERIFY_IS_APPROX(((m1*Scalar(2)).array() - s2).matrix(), (m1+m1) - MatrixType::Constant(rows,cols,s2) ); + m3 = m1; + m3.array() += s2; + VERIFY_IS_APPROX(m3, (m1.array() + s2).matrix()); + m3 = m1; + m3.array() -= s1; + VERIFY_IS_APPROX(m3, (m1.array() - s1).matrix()); + + // reductions + VERIFY_IS_MUCH_SMALLER_THAN(m1.colwise().sum().sum() - m1.sum(), m1.cwiseAbs().maxCoeff()); + VERIFY_IS_MUCH_SMALLER_THAN(m1.rowwise().sum().sum() - m1.sum(), m1.cwiseAbs().maxCoeff()); + VERIFY_IS_MUCH_SMALLER_THAN(m1.colwise().sum() + m2.colwise().sum() - (m1+m2).colwise().sum(), (m1+m2).cwiseAbs().maxCoeff()); + VERIFY_IS_MUCH_SMALLER_THAN(m1.rowwise().sum() - m2.rowwise().sum() - (m1-m2).rowwise().sum(), (m1-m2).cwiseAbs().maxCoeff()); + VERIFY_IS_APPROX(m1.colwise().sum(), m1.colwise().redux(internal::scalar_sum_op<Scalar>())); + + // vector-wise ops + m3 = m1; + VERIFY_IS_APPROX(m3.colwise() += cv1, m1.colwise() + cv1); + m3 = m1; + VERIFY_IS_APPROX(m3.colwise() -= cv1, m1.colwise() - cv1); + m3 = m1; + VERIFY_IS_APPROX(m3.rowwise() += rv1, m1.rowwise() + rv1); + m3 = m1; + VERIFY_IS_APPROX(m3.rowwise() -= rv1, m1.rowwise() - rv1); + + // empty objects + VERIFY_IS_APPROX(m1.block(0,0,0,cols).colwise().sum(), RowVectorType::Zero(cols)); + VERIFY_IS_APPROX(m1.block(0,0,rows,0).rowwise().prod(), ColVectorType::Ones(rows)); + + // verify the const accessors exist + const Scalar& ref_m1 = m.matrix().array().coeffRef(0); + const Scalar& ref_m2 = m.matrix().array().coeffRef(0,0); + const Scalar& ref_a1 = m.array().matrix().coeffRef(0); + const Scalar& ref_a2 = m.array().matrix().coeffRef(0,0); + VERIFY(&ref_a1 == &ref_m1); + VERIFY(&ref_a2 == &ref_m2); +} + +template<typename MatrixType> void comparisons(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + Index rows = m.rows(); + Index cols = m.cols(); + + Index r = internal::random<Index>(0, rows-1), + c = internal::random<Index>(0, cols-1); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols); + + VERIFY(((m1.array() + Scalar(1)) > m1.array()).all()); + VERIFY(((m1.array() - Scalar(1)) < m1.array()).all()); + if (rows*cols>1) + { + m3 = m1; + m3(r,c) += 1; + VERIFY(! (m1.array() < m3.array()).all() ); + VERIFY(! (m1.array() > m3.array()).all() ); + } + + // comparisons to scalar + VERIFY( (m1.array() != (m1(r,c)+1) ).any() ); + VERIFY( (m1.array() > (m1(r,c)-1) ).any() ); + VERIFY( (m1.array() < (m1(r,c)+1) ).any() ); + VERIFY( (m1.array() == m1(r,c) ).any() ); + + // test Select + VERIFY_IS_APPROX( (m1.array()<m2.array()).select(m1,m2), m1.cwiseMin(m2) ); + VERIFY_IS_APPROX( (m1.array()>m2.array()).select(m1,m2), m1.cwiseMax(m2) ); + Scalar mid = (m1.cwiseAbs().minCoeff() + m1.cwiseAbs().maxCoeff())/Scalar(2); + for (int j=0; j<cols; ++j) + for (int i=0; i<rows; ++i) + m3(i,j) = internal::abs(m1(i,j))<mid ? 0 : m1(i,j); + VERIFY_IS_APPROX( (m1.array().abs()<MatrixType::Constant(rows,cols,mid).array()) + .select(MatrixType::Zero(rows,cols),m1), m3); + // shorter versions: + VERIFY_IS_APPROX( (m1.array().abs()<MatrixType::Constant(rows,cols,mid).array()) + .select(0,m1), m3); + VERIFY_IS_APPROX( (m1.array().abs()>=MatrixType::Constant(rows,cols,mid).array()) + .select(m1,0), m3); + // even shorter version: + VERIFY_IS_APPROX( (m1.array().abs()<mid).select(0,m1), m3); + + // count + VERIFY(((m1.array().abs()+1)>RealScalar(0.1)).count() == rows*cols); + + typedef Matrix<typename MatrixType::Index, Dynamic, 1> VectorOfIndices; + + // TODO allows colwise/rowwise for array + VERIFY_IS_APPROX(((m1.array().abs()+1)>RealScalar(0.1)).matrix().colwise().count(), VectorOfIndices::Constant(cols,rows).transpose()); + VERIFY_IS_APPROX(((m1.array().abs()+1)>RealScalar(0.1)).matrix().rowwise().count(), VectorOfIndices::Constant(rows, cols)); +} + +template<typename VectorType> void lpNorm(const VectorType& v) +{ + VectorType u = VectorType::Random(v.size()); + + VERIFY_IS_APPROX(u.template lpNorm<Infinity>(), u.cwiseAbs().maxCoeff()); + VERIFY_IS_APPROX(u.template lpNorm<1>(), u.cwiseAbs().sum()); + VERIFY_IS_APPROX(u.template lpNorm<2>(), internal::sqrt(u.array().abs().square().sum())); + VERIFY_IS_APPROX(internal::pow(u.template lpNorm<5>(), typename VectorType::RealScalar(5)), u.array().abs().pow(5).sum()); +} + +template<typename MatrixType> void cwise_min_max(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols); + + // min/max with array + Scalar maxM1 = m1.maxCoeff(); + Scalar minM1 = m1.minCoeff(); + + VERIFY_IS_APPROX(MatrixType::Constant(rows,cols, minM1), m1.cwiseMin(MatrixType::Constant(rows,cols, minM1))); + VERIFY_IS_APPROX(m1, m1.cwiseMin(MatrixType::Constant(rows,cols, maxM1))); + + VERIFY_IS_APPROX(MatrixType::Constant(rows,cols, maxM1), m1.cwiseMax(MatrixType::Constant(rows,cols, maxM1))); + VERIFY_IS_APPROX(m1, m1.cwiseMax(MatrixType::Constant(rows,cols, minM1))); + + // min/max with scalar input + VERIFY_IS_APPROX(MatrixType::Constant(rows,cols, minM1), m1.cwiseMin( minM1)); + VERIFY_IS_APPROX(m1, m1.cwiseMin( maxM1)); + + VERIFY_IS_APPROX(MatrixType::Constant(rows,cols, maxM1), m1.cwiseMax( maxM1)); + VERIFY_IS_APPROX(m1, m1.cwiseMax( minM1)); + +} + +void test_array_for_matrix() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( array_for_matrix(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( array_for_matrix(Matrix2f()) ); + CALL_SUBTEST_3( array_for_matrix(Matrix4d()) ); + CALL_SUBTEST_4( array_for_matrix(MatrixXcf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_5( array_for_matrix(MatrixXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_6( array_for_matrix(MatrixXi(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + } + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( comparisons(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( comparisons(Matrix2f()) ); + CALL_SUBTEST_3( comparisons(Matrix4d()) ); + CALL_SUBTEST_5( comparisons(MatrixXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_6( comparisons(MatrixXi(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + } + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( cwise_min_max(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( cwise_min_max(Matrix2f()) ); + CALL_SUBTEST_3( cwise_min_max(Matrix4d()) ); + CALL_SUBTEST_5( cwise_min_max(MatrixXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_6( cwise_min_max(MatrixXi(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + } + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( lpNorm(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( lpNorm(Vector2f()) ); + CALL_SUBTEST_7( lpNorm(Vector3d()) ); + CALL_SUBTEST_8( lpNorm(Vector4f()) ); + CALL_SUBTEST_5( lpNorm(VectorXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_4( lpNorm(VectorXcf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + } +} diff --git a/test/array_replicate.cpp b/test/array_replicate.cpp new file mode 100644 index 000000000..94da7425b --- /dev/null +++ b/test/array_replicate.cpp @@ -0,0 +1,70 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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<typename MatrixType> void replicate(const MatrixType& m) +{ + /* this test covers the following files: + Replicate.cpp + */ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<Scalar, Dynamic, Dynamic> MatrixX; + typedef Matrix<Scalar, Dynamic, 1> VectorX; + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols); + + VectorType v1 = VectorType::Random(rows); + + MatrixX x1, x2; + VectorX vx1; + + int f1 = internal::random<int>(1,10), + f2 = internal::random<int>(1,10); + + x1.resize(rows*f1,cols*f2); + for(int j=0; j<f2; j++) + for(int i=0; i<f1; i++) + x1.block(i*rows,j*cols,rows,cols) = m1; + VERIFY_IS_APPROX(x1, m1.replicate(f1,f2)); + + x2.resize(2*rows,3*cols); + x2 << m2, m2, m2, + m2, m2, m2; + VERIFY_IS_APPROX(x2, (m2.template replicate<2,3>())); + + x2.resize(rows,f1); + for (int j=0; j<f1; ++j) + x2.col(j) = v1; + VERIFY_IS_APPROX(x2, v1.rowwise().replicate(f1)); + + vx1.resize(rows*f2); + for (int j=0; j<f2; ++j) + vx1.segment(j*rows,rows) = v1; + VERIFY_IS_APPROX(vx1, v1.colwise().replicate(f2)); +} + +void test_array_replicate() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( replicate(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( replicate(Vector2f()) ); + CALL_SUBTEST_3( replicate(Vector3d()) ); + CALL_SUBTEST_4( replicate(Vector4f()) ); + CALL_SUBTEST_5( replicate(VectorXf(16)) ); + CALL_SUBTEST_6( replicate(VectorXcd(10)) ); + } +} diff --git a/test/array_reverse.cpp b/test/array_reverse.cpp new file mode 100644 index 000000000..fbe7a9901 --- /dev/null +++ b/test/array_reverse.cpp @@ -0,0 +1,128 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// Copyright (C) 2009 Ricard Marxer <email@ricardmarxer.com> +// +// 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" +#include <iostream> + +using namespace std; + +template<typename MatrixType> void reverse(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + Index rows = m.rows(); + Index cols = m.cols(); + + // this test relies a lot on Random.h, and there's not much more that we can do + // to test it, hence I consider that we will have tested Random.h + MatrixType m1 = MatrixType::Random(rows, cols); + VectorType v1 = VectorType::Random(rows); + + MatrixType m1_r = m1.reverse(); + // Verify that MatrixBase::reverse() works + for ( int i = 0; i < rows; i++ ) { + for ( int j = 0; j < cols; j++ ) { + VERIFY_IS_APPROX(m1_r(i, j), m1(rows - 1 - i, cols - 1 - j)); + } + } + + Reverse<MatrixType> m1_rd(m1); + // Verify that a Reverse default (in both directions) of an expression works + for ( int i = 0; i < rows; i++ ) { + for ( int j = 0; j < cols; j++ ) { + VERIFY_IS_APPROX(m1_rd(i, j), m1(rows - 1 - i, cols - 1 - j)); + } + } + + Reverse<MatrixType, BothDirections> m1_rb(m1); + // Verify that a Reverse in both directions of an expression works + for ( int i = 0; i < rows; i++ ) { + for ( int j = 0; j < cols; j++ ) { + VERIFY_IS_APPROX(m1_rb(i, j), m1(rows - 1 - i, cols - 1 - j)); + } + } + + Reverse<MatrixType, Vertical> m1_rv(m1); + // Verify that a Reverse in the vertical directions of an expression works + for ( int i = 0; i < rows; i++ ) { + for ( int j = 0; j < cols; j++ ) { + VERIFY_IS_APPROX(m1_rv(i, j), m1(rows - 1 - i, j)); + } + } + + Reverse<MatrixType, Horizontal> m1_rh(m1); + // Verify that a Reverse in the horizontal directions of an expression works + for ( int i = 0; i < rows; i++ ) { + for ( int j = 0; j < cols; j++ ) { + VERIFY_IS_APPROX(m1_rh(i, j), m1(i, cols - 1 - j)); + } + } + + VectorType v1_r = v1.reverse(); + // Verify that a VectorType::reverse() of an expression works + for ( int i = 0; i < rows; i++ ) { + VERIFY_IS_APPROX(v1_r(i), v1(rows - 1 - i)); + } + + MatrixType m1_cr = m1.colwise().reverse(); + // Verify that PartialRedux::reverse() works (for colwise()) + for ( int i = 0; i < rows; i++ ) { + for ( int j = 0; j < cols; j++ ) { + VERIFY_IS_APPROX(m1_cr(i, j), m1(rows - 1 - i, j)); + } + } + + MatrixType m1_rr = m1.rowwise().reverse(); + // Verify that PartialRedux::reverse() works (for rowwise()) + for ( int i = 0; i < rows; i++ ) { + for ( int j = 0; j < cols; j++ ) { + VERIFY_IS_APPROX(m1_rr(i, j), m1(i, cols - 1 - j)); + } + } + + Scalar x = internal::random<Scalar>(); + + Index r = internal::random<Index>(0, rows-1), + c = internal::random<Index>(0, cols-1); + + m1.reverse()(r, c) = x; + VERIFY_IS_APPROX(x, m1(rows - 1 - r, cols - 1 - c)); + + /* + m1.colwise().reverse()(r, c) = x; + VERIFY_IS_APPROX(x, m1(rows - 1 - r, c)); + + m1.rowwise().reverse()(r, c) = x; + VERIFY_IS_APPROX(x, m1(r, cols - 1 - c)); + */ +} + +void test_array_reverse() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( reverse(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( reverse(Matrix2f()) ); + CALL_SUBTEST_3( reverse(Matrix4f()) ); + CALL_SUBTEST_4( reverse(Matrix4d()) ); + CALL_SUBTEST_5( reverse(MatrixXcf(3, 3)) ); + CALL_SUBTEST_6( reverse(MatrixXi(6, 3)) ); + CALL_SUBTEST_7( reverse(MatrixXcd(20, 20)) ); + CALL_SUBTEST_8( reverse(Matrix<float, 100, 100>()) ); + CALL_SUBTEST_9( reverse(Matrix<float,Dynamic,Dynamic,RowMajor>(6,3)) ); + } +#ifdef EIGEN_TEST_PART_3 + Vector4f x; x << 1, 2, 3, 4; + Vector4f y; y << 4, 3, 2, 1; + VERIFY(x.reverse()[1] == 3); + VERIFY(x.reverse() == y); +#endif +} diff --git a/test/bandmatrix.cpp b/test/bandmatrix.cpp new file mode 100644 index 000000000..5e4e8e07b --- /dev/null +++ b/test/bandmatrix.cpp @@ -0,0 +1,74 @@ +// This file is triangularView of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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<typename MatrixType> void bandmatrix(const MatrixType& _m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrixType; + + Index rows = _m.rows(); + Index cols = _m.cols(); + Index supers = _m.supers(); + Index subs = _m.subs(); + + MatrixType m(rows,cols,supers,subs); + + DenseMatrixType dm1(rows,cols); + dm1.setZero(); + + m.diagonal().setConstant(123); + dm1.diagonal().setConstant(123); + for (int i=1; i<=m.supers();++i) + { + m.diagonal(i).setConstant(static_cast<RealScalar>(i)); + dm1.diagonal(i).setConstant(static_cast<RealScalar>(i)); + } + for (int i=1; i<=m.subs();++i) + { + m.diagonal(-i).setConstant(-static_cast<RealScalar>(i)); + dm1.diagonal(-i).setConstant(-static_cast<RealScalar>(i)); + } + //std::cerr << m.m_data << "\n\n" << m.toDense() << "\n\n" << dm1 << "\n\n\n\n"; + VERIFY_IS_APPROX(dm1,m.toDenseMatrix()); + + for (int i=0; i<cols; ++i) + { + m.col(i).setConstant(static_cast<RealScalar>(i+1)); + dm1.col(i).setConstant(static_cast<RealScalar>(i+1)); + } + Index d = (std::min)(rows,cols); + Index a = std::max<Index>(0,cols-d-supers); + Index b = std::max<Index>(0,rows-d-subs); + if(a>0) dm1.block(0,d+supers,rows,a).setZero(); + dm1.block(0,supers+1,cols-supers-1-a,cols-supers-1-a).template triangularView<Upper>().setZero(); + dm1.block(subs+1,0,rows-subs-1-b,rows-subs-1-b).template triangularView<Lower>().setZero(); + if(b>0) dm1.block(d+subs,0,b,cols).setZero(); + //std::cerr << m.m_data << "\n\n" << m.toDense() << "\n\n" << dm1 << "\n\n"; + VERIFY_IS_APPROX(dm1,m.toDenseMatrix()); + +} + +using Eigen::internal::BandMatrix; + +void test_bandmatrix() +{ + typedef BandMatrix<float>::Index Index; + + for(int i = 0; i < 10*g_repeat ; i++) { + Index rows = internal::random<Index>(1,10); + Index cols = internal::random<Index>(1,10); + Index sups = internal::random<Index>(0,cols-1); + Index subs = internal::random<Index>(0,rows-1); + CALL_SUBTEST(bandmatrix(BandMatrix<float>(rows,cols,sups,subs)) ); + } +} diff --git a/test/basicstuff.cpp b/test/basicstuff.cpp new file mode 100644 index 000000000..48db531c1 --- /dev/null +++ b/test/basicstuff.cpp @@ -0,0 +1,215 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#define EIGEN_NO_STATIC_ASSERT + +#include "main.h" + +template<typename MatrixType> void basicStuff(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> SquareMatrixType; + + Index rows = m.rows(); + Index cols = m.cols(); + + // this test relies a lot on Random.h, and there's not much more that we can do + // to test it, hence I consider that we will have tested Random.h + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols), + mzero = MatrixType::Zero(rows, cols), + square = Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime>::Random(rows, rows); + VectorType v1 = VectorType::Random(rows), + vzero = VectorType::Zero(rows); + SquareMatrixType sm1 = SquareMatrixType::Random(rows,rows), sm2(rows,rows); + + Scalar x = 0; + while(x == Scalar(0)) x = internal::random<Scalar>(); + + Index r = internal::random<Index>(0, rows-1), + c = internal::random<Index>(0, cols-1); + + m1.coeffRef(r,c) = x; + VERIFY_IS_APPROX(x, m1.coeff(r,c)); + m1(r,c) = x; + VERIFY_IS_APPROX(x, m1(r,c)); + v1.coeffRef(r) = x; + VERIFY_IS_APPROX(x, v1.coeff(r)); + v1(r) = x; + VERIFY_IS_APPROX(x, v1(r)); + v1[r] = x; + VERIFY_IS_APPROX(x, v1[r]); + + VERIFY_IS_APPROX( v1, v1); + VERIFY_IS_NOT_APPROX( v1, 2*v1); + VERIFY_IS_MUCH_SMALLER_THAN( vzero, v1); + if(!NumTraits<Scalar>::IsInteger) + VERIFY_IS_MUCH_SMALLER_THAN( vzero, v1.norm()); + VERIFY_IS_NOT_MUCH_SMALLER_THAN(v1, v1); + VERIFY_IS_APPROX( vzero, v1-v1); + VERIFY_IS_APPROX( m1, m1); + VERIFY_IS_NOT_APPROX( m1, 2*m1); + VERIFY_IS_MUCH_SMALLER_THAN( mzero, m1); + VERIFY_IS_NOT_MUCH_SMALLER_THAN(m1, m1); + VERIFY_IS_APPROX( mzero, m1-m1); + + // always test operator() on each read-only expression class, + // in order to check const-qualifiers. + // indeed, if an expression class (here Zero) is meant to be read-only, + // hence has no _write() method, the corresponding MatrixBase method (here zero()) + // should return a const-qualified object so that it is the const-qualified + // operator() that gets called, which in turn calls _read(). + VERIFY_IS_MUCH_SMALLER_THAN(MatrixType::Zero(rows,cols)(r,c), static_cast<Scalar>(1)); + + // now test copying a row-vector into a (column-)vector and conversely. + square.col(r) = square.row(r).eval(); + Matrix<Scalar, 1, MatrixType::RowsAtCompileTime> rv(rows); + Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> cv(rows); + rv = square.row(r); + cv = square.col(r); + + VERIFY_IS_APPROX(rv, cv.transpose()); + + if(cols!=1 && rows!=1 && MatrixType::SizeAtCompileTime!=Dynamic) + { + VERIFY_RAISES_ASSERT(m1 = (m2.block(0,0, rows-1, cols-1))); + } + + if(cols!=1 && rows!=1) + { + VERIFY_RAISES_ASSERT(m1[0]); + VERIFY_RAISES_ASSERT((m1+m1)[0]); + } + + VERIFY_IS_APPROX(m3 = m1,m1); + MatrixType m4; + VERIFY_IS_APPROX(m4 = m1,m1); + + m3.real() = m1.real(); + VERIFY_IS_APPROX(static_cast<const MatrixType&>(m3).real(), static_cast<const MatrixType&>(m1).real()); + VERIFY_IS_APPROX(static_cast<const MatrixType&>(m3).real(), m1.real()); + + // check == / != operators + VERIFY(m1==m1); + VERIFY(m1!=m2); + VERIFY(!(m1==m2)); + VERIFY(!(m1!=m1)); + m1 = m2; + VERIFY(m1==m2); + VERIFY(!(m1!=m2)); + + // check automatic transposition + sm2.setZero(); + for(typename MatrixType::Index i=0;i<rows;++i) + sm2.col(i) = sm1.row(i); + VERIFY_IS_APPROX(sm2,sm1.transpose()); + + sm2.setZero(); + for(typename MatrixType::Index i=0;i<rows;++i) + sm2.col(i).noalias() = sm1.row(i); + VERIFY_IS_APPROX(sm2,sm1.transpose()); + + sm2.setZero(); + for(typename MatrixType::Index i=0;i<rows;++i) + sm2.col(i).noalias() += sm1.row(i); + VERIFY_IS_APPROX(sm2,sm1.transpose()); + + sm2.setZero(); + for(typename MatrixType::Index i=0;i<rows;++i) + sm2.col(i).noalias() -= sm1.row(i); + VERIFY_IS_APPROX(sm2,-sm1.transpose()); +} + +template<typename MatrixType> void basicStuffComplex(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<RealScalar, MatrixType::RowsAtCompileTime, MatrixType::ColsAtCompileTime> RealMatrixType; + + Index rows = m.rows(); + Index cols = m.cols(); + + Scalar s1 = internal::random<Scalar>(), + s2 = internal::random<Scalar>(); + + VERIFY(internal::real(s1)==internal::real_ref(s1)); + VERIFY(internal::imag(s1)==internal::imag_ref(s1)); + internal::real_ref(s1) = internal::real(s2); + internal::imag_ref(s1) = internal::imag(s2); + VERIFY(internal::isApprox(s1, s2, NumTraits<RealScalar>::epsilon())); + // extended precision in Intel FPUs means that s1 == s2 in the line above is not guaranteed. + + RealMatrixType rm1 = RealMatrixType::Random(rows,cols), + rm2 = RealMatrixType::Random(rows,cols); + MatrixType cm(rows,cols); + cm.real() = rm1; + cm.imag() = rm2; + VERIFY_IS_APPROX(static_cast<const MatrixType&>(cm).real(), rm1); + VERIFY_IS_APPROX(static_cast<const MatrixType&>(cm).imag(), rm2); + rm1.setZero(); + rm2.setZero(); + rm1 = cm.real(); + rm2 = cm.imag(); + VERIFY_IS_APPROX(static_cast<const MatrixType&>(cm).real(), rm1); + VERIFY_IS_APPROX(static_cast<const MatrixType&>(cm).imag(), rm2); + cm.real().setZero(); + VERIFY(static_cast<const MatrixType&>(cm).real().isZero()); + VERIFY(!static_cast<const MatrixType&>(cm).imag().isZero()); +} + +#ifdef EIGEN_TEST_PART_2 +void casting() +{ + Matrix4f m = Matrix4f::Random(), m2; + Matrix4d n = m.cast<double>(); + VERIFY(m.isApprox(n.cast<float>())); + m2 = m.cast<float>(); // check the specialization when NewType == Type + VERIFY(m.isApprox(m2)); +} +#endif + +template <typename Scalar> +void fixedSizeMatrixConstruction() +{ + const Scalar raw[3] = {1,2,3}; + Matrix<Scalar,3,1> m(raw); + Array<Scalar,3,1> a(raw); + VERIFY(m(0) == 1); + VERIFY(m(1) == 2); + VERIFY(m(2) == 3); + VERIFY(a(0) == 1); + VERIFY(a(1) == 2); + VERIFY(a(2) == 3); +} + +void test_basicstuff() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( basicStuff(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( basicStuff(Matrix4d()) ); + CALL_SUBTEST_3( basicStuff(MatrixXcf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_4( basicStuff(MatrixXi(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_5( basicStuff(MatrixXcd(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_6( basicStuff(Matrix<float, 100, 100>()) ); + CALL_SUBTEST_7( basicStuff(Matrix<long double,Dynamic,Dynamic>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE),internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + + CALL_SUBTEST_3( basicStuffComplex(MatrixXcf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_5( basicStuffComplex(MatrixXcd(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + } + + CALL_SUBTEST_1(fixedSizeMatrixConstruction<unsigned char>()); + CALL_SUBTEST_1(fixedSizeMatrixConstruction<double>()); + CALL_SUBTEST_1(fixedSizeMatrixConstruction<double>()); + + CALL_SUBTEST_2(casting()); +} diff --git a/test/bicgstab.cpp b/test/bicgstab.cpp new file mode 100644 index 000000000..f327e2fac --- /dev/null +++ b/test/bicgstab.cpp @@ -0,0 +1,30 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Gael Guennebaud <g.gael@free.fr> +// +// 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 "sparse_solver.h" +#include <Eigen/IterativeLinearSolvers> + +template<typename T> void test_bicgstab_T() +{ + BiCGSTAB<SparseMatrix<T>, DiagonalPreconditioner<T> > bicgstab_colmajor_diag; + BiCGSTAB<SparseMatrix<T>, IdentityPreconditioner > bicgstab_colmajor_I; + BiCGSTAB<SparseMatrix<T>, IncompleteLUT<T> > bicgstab_colmajor_ilut; + //BiCGSTAB<SparseMatrix<T>, SSORPreconditioner<T> > bicgstab_colmajor_ssor; + + CALL_SUBTEST( check_sparse_square_solving(bicgstab_colmajor_diag) ); +// CALL_SUBTEST( check_sparse_square_solving(bicgstab_colmajor_I) ); + CALL_SUBTEST( check_sparse_square_solving(bicgstab_colmajor_ilut) ); + //CALL_SUBTEST( check_sparse_square_solving(bicgstab_colmajor_ssor) ); +} + +void test_bicgstab() +{ + CALL_SUBTEST_1(test_bicgstab_T<double>()); + CALL_SUBTEST_2(test_bicgstab_T<std::complex<double> >()); +} diff --git a/test/block.cpp b/test/block.cpp new file mode 100644 index 000000000..0969262ca --- /dev/null +++ b/test/block.cpp @@ -0,0 +1,222 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2006-2010 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#define EIGEN_NO_STATIC_ASSERT // otherwise we fail at compile time on unused paths +#include "main.h" + +template<typename MatrixType> void block(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<Scalar, 1, MatrixType::ColsAtCompileTime> RowVectorType; + typedef Matrix<Scalar, Dynamic, Dynamic> DynamicMatrixType; + typedef Matrix<Scalar, Dynamic, 1> DynamicVectorType; + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m1_copy = m1, + m2 = MatrixType::Random(rows, cols), + m3(rows, cols), + ones = MatrixType::Ones(rows, cols); + VectorType v1 = VectorType::Random(rows); + + Scalar s1 = internal::random<Scalar>(); + + Index r1 = internal::random<Index>(0,rows-1); + Index r2 = internal::random<Index>(r1,rows-1); + Index c1 = internal::random<Index>(0,cols-1); + Index c2 = internal::random<Index>(c1,cols-1); + + //check row() and col() + VERIFY_IS_EQUAL(m1.col(c1).transpose(), m1.transpose().row(c1)); + //check operator(), both constant and non-constant, on row() and col() + m1 = m1_copy; + m1.row(r1) += s1 * m1_copy.row(r2); + VERIFY_IS_APPROX(m1.row(r1), m1_copy.row(r1) + s1 * m1_copy.row(r2)); + // check nested block xpr on lhs + m1.row(r1).row(0) += s1 * m1_copy.row(r2); + VERIFY_IS_APPROX(m1.row(r1), m1_copy.row(r1) + Scalar(2) * s1 * m1_copy.row(r2)); + m1 = m1_copy; + m1.col(c1) += s1 * m1_copy.col(c2); + VERIFY_IS_APPROX(m1.col(c1), m1_copy.col(c1) + s1 * m1_copy.col(c2)); + m1.col(c1).col(0) += s1 * m1_copy.col(c2); + VERIFY_IS_APPROX(m1.col(c1), m1_copy.col(c1) + Scalar(2) * s1 * m1_copy.col(c2)); + + //check block() + Matrix<Scalar,Dynamic,Dynamic> b1(1,1); b1(0,0) = m1(r1,c1); + + RowVectorType br1(m1.block(r1,0,1,cols)); + VectorType bc1(m1.block(0,c1,rows,1)); + VERIFY_IS_EQUAL(b1, m1.block(r1,c1,1,1)); + VERIFY_IS_EQUAL(m1.row(r1), br1); + VERIFY_IS_EQUAL(m1.col(c1), bc1); + //check operator(), both constant and non-constant, on block() + m1.block(r1,c1,r2-r1+1,c2-c1+1) = s1 * m2.block(0, 0, r2-r1+1,c2-c1+1); + m1.block(r1,c1,r2-r1+1,c2-c1+1)(r2-r1,c2-c1) = m2.block(0, 0, r2-r1+1,c2-c1+1)(0,0); + + enum { + BlockRows = 2, + BlockCols = 5 + }; + if (rows>=5 && cols>=8) + { + // test fixed block() as lvalue + m1.template block<BlockRows,BlockCols>(1,1) *= s1; + // test operator() on fixed block() both as constant and non-constant + m1.template block<BlockRows,BlockCols>(1,1)(0, 3) = m1.template block<2,5>(1,1)(1,2); + // check that fixed block() and block() agree + Matrix<Scalar,Dynamic,Dynamic> b = m1.template block<BlockRows,BlockCols>(3,3); + VERIFY_IS_EQUAL(b, m1.block(3,3,BlockRows,BlockCols)); + } + + if (rows>2) + { + // test sub vectors + VERIFY_IS_EQUAL(v1.template head<2>(), v1.block(0,0,2,1)); + VERIFY_IS_EQUAL(v1.template head<2>(), v1.head(2)); + VERIFY_IS_EQUAL(v1.template head<2>(), v1.segment(0,2)); + VERIFY_IS_EQUAL(v1.template head<2>(), v1.template segment<2>(0)); + Index i = rows-2; + VERIFY_IS_EQUAL(v1.template tail<2>(), v1.block(i,0,2,1)); + VERIFY_IS_EQUAL(v1.template tail<2>(), v1.tail(2)); + VERIFY_IS_EQUAL(v1.template tail<2>(), v1.segment(i,2)); + VERIFY_IS_EQUAL(v1.template tail<2>(), v1.template segment<2>(i)); + i = internal::random<Index>(0,rows-2); + VERIFY_IS_EQUAL(v1.segment(i,2), v1.template segment<2>(i)); + } + + // stress some basic stuffs with block matrices + VERIFY(internal::real(ones.col(c1).sum()) == RealScalar(rows)); + VERIFY(internal::real(ones.row(r1).sum()) == RealScalar(cols)); + + VERIFY(internal::real(ones.col(c1).dot(ones.col(c2))) == RealScalar(rows)); + VERIFY(internal::real(ones.row(r1).dot(ones.row(r2))) == RealScalar(cols)); + + // now test some block-inside-of-block. + + // expressions with direct access + VERIFY_IS_EQUAL( (m1.block(r1,c1,rows-r1,cols-c1).block(r2-r1,c2-c1,rows-r2,cols-c2)) , (m1.block(r2,c2,rows-r2,cols-c2)) ); + VERIFY_IS_EQUAL( (m1.block(r1,c1,r2-r1+1,c2-c1+1).row(0)) , (m1.row(r1).segment(c1,c2-c1+1)) ); + VERIFY_IS_EQUAL( (m1.block(r1,c1,r2-r1+1,c2-c1+1).col(0)) , (m1.col(c1).segment(r1,r2-r1+1)) ); + VERIFY_IS_EQUAL( (m1.block(r1,c1,r2-r1+1,c2-c1+1).transpose().col(0)) , (m1.row(r1).segment(c1,c2-c1+1)).transpose() ); + VERIFY_IS_EQUAL( (m1.transpose().block(c1,r1,c2-c1+1,r2-r1+1).col(0)) , (m1.row(r1).segment(c1,c2-c1+1)).transpose() ); + + // expressions without direct access + VERIFY_IS_EQUAL( ((m1+m2).block(r1,c1,rows-r1,cols-c1).block(r2-r1,c2-c1,rows-r2,cols-c2)) , ((m1+m2).block(r2,c2,rows-r2,cols-c2)) ); + VERIFY_IS_EQUAL( ((m1+m2).block(r1,c1,r2-r1+1,c2-c1+1).row(0)) , ((m1+m2).row(r1).segment(c1,c2-c1+1)) ); + VERIFY_IS_EQUAL( ((m1+m2).block(r1,c1,r2-r1+1,c2-c1+1).col(0)) , ((m1+m2).col(c1).segment(r1,r2-r1+1)) ); + VERIFY_IS_EQUAL( ((m1+m2).block(r1,c1,r2-r1+1,c2-c1+1).transpose().col(0)) , ((m1+m2).row(r1).segment(c1,c2-c1+1)).transpose() ); + VERIFY_IS_EQUAL( ((m1+m2).transpose().block(c1,r1,c2-c1+1,r2-r1+1).col(0)) , ((m1+m2).row(r1).segment(c1,c2-c1+1)).transpose() ); + + // evaluation into plain matrices from expressions with direct access (stress MapBase) + DynamicMatrixType dm; + DynamicVectorType dv; + dm.setZero(); + dm = m1.block(r1,c1,rows-r1,cols-c1).block(r2-r1,c2-c1,rows-r2,cols-c2); + VERIFY_IS_EQUAL(dm, (m1.block(r2,c2,rows-r2,cols-c2))); + dm.setZero(); + dv.setZero(); + dm = m1.block(r1,c1,r2-r1+1,c2-c1+1).row(0).transpose(); + dv = m1.row(r1).segment(c1,c2-c1+1); + VERIFY_IS_EQUAL(dv, dm); + dm.setZero(); + dv.setZero(); + dm = m1.col(c1).segment(r1,r2-r1+1); + dv = m1.block(r1,c1,r2-r1+1,c2-c1+1).col(0); + VERIFY_IS_EQUAL(dv, dm); + dm.setZero(); + dv.setZero(); + dm = m1.block(r1,c1,r2-r1+1,c2-c1+1).transpose().col(0); + dv = m1.row(r1).segment(c1,c2-c1+1); + VERIFY_IS_EQUAL(dv, dm); + dm.setZero(); + dv.setZero(); + dm = m1.row(r1).segment(c1,c2-c1+1).transpose(); + dv = m1.transpose().block(c1,r1,c2-c1+1,r2-r1+1).col(0); + VERIFY_IS_EQUAL(dv, dm); +} + + +template<typename MatrixType> +void compare_using_data_and_stride(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + Index rows = m.rows(); + Index cols = m.cols(); + Index size = m.size(); + Index innerStride = m.innerStride(); + Index outerStride = m.outerStride(); + Index rowStride = m.rowStride(); + Index colStride = m.colStride(); + const typename MatrixType::Scalar* data = m.data(); + + for(int j=0;j<cols;++j) + for(int i=0;i<rows;++i) + VERIFY(m.coeff(i,j) == data[i*rowStride + j*colStride]); + + if(!MatrixType::IsVectorAtCompileTime) + { + for(int j=0;j<cols;++j) + for(int i=0;i<rows;++i) + VERIFY(m.coeff(i,j) == data[(MatrixType::Flags&RowMajorBit) + ? i*outerStride + j*innerStride + : j*outerStride + i*innerStride]); + } + + if(MatrixType::IsVectorAtCompileTime) + { + VERIFY(innerStride == int((&m.coeff(1))-(&m.coeff(0)))); + for (int i=0;i<size;++i) + VERIFY(m.coeff(i) == data[i*innerStride]); + } +} + +template<typename MatrixType> +void data_and_stride(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + Index rows = m.rows(); + Index cols = m.cols(); + + Index r1 = internal::random<Index>(0,rows-1); + Index r2 = internal::random<Index>(r1,rows-1); + Index c1 = internal::random<Index>(0,cols-1); + Index c2 = internal::random<Index>(c1,cols-1); + + MatrixType m1 = MatrixType::Random(rows, cols); + compare_using_data_and_stride(m1.block(r1, c1, r2-r1+1, c2-c1+1)); + compare_using_data_and_stride(m1.transpose().block(c1, r1, c2-c1+1, r2-r1+1)); + compare_using_data_and_stride(m1.row(r1)); + compare_using_data_and_stride(m1.col(c1)); + compare_using_data_and_stride(m1.row(r1).transpose()); + compare_using_data_and_stride(m1.col(c1).transpose()); +} + +void test_block() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( block(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( block(Matrix4d()) ); + CALL_SUBTEST_3( block(MatrixXcf(3, 3)) ); + CALL_SUBTEST_4( block(MatrixXi(8, 12)) ); + CALL_SUBTEST_5( block(MatrixXcd(20, 20)) ); + CALL_SUBTEST_6( block(MatrixXf(20, 20)) ); + + CALL_SUBTEST_8( block(Matrix<float,Dynamic,4>(3, 4)) ); + +#ifndef EIGEN_DEFAULT_TO_ROW_MAJOR + CALL_SUBTEST_6( data_and_stride(MatrixXf(internal::random(5,50), internal::random(5,50))) ); + CALL_SUBTEST_7( data_and_stride(Matrix<int,Dynamic,Dynamic,RowMajor>(internal::random(5,50), internal::random(5,50))) ); +#endif + } +} diff --git a/test/cholesky.cpp b/test/cholesky.cpp new file mode 100644 index 000000000..14e01c006 --- /dev/null +++ b/test/cholesky.cpp @@ -0,0 +1,310 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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/. + +#ifndef EIGEN_NO_ASSERTION_CHECKING +#define EIGEN_NO_ASSERTION_CHECKING +#endif + +static int nb_temporaries; + +#define EIGEN_DENSE_STORAGE_CTOR_PLUGIN { if(size!=0) nb_temporaries++; } + +#include "main.h" +#include <Eigen/Cholesky> +#include <Eigen/QR> + +#define VERIFY_EVALUATION_COUNT(XPR,N) {\ + nb_temporaries = 0; \ + XPR; \ + if(nb_temporaries!=N) std::cerr << "nb_temporaries == " << nb_temporaries << "\n"; \ + VERIFY( (#XPR) && nb_temporaries==N ); \ + } + +template<typename MatrixType,template <typename,int> class CholType> void test_chol_update(const MatrixType& symm) +{ + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + MatrixType symmLo = symm.template triangularView<Lower>(); + MatrixType symmUp = symm.template triangularView<Upper>(); + MatrixType symmCpy = symm; + + CholType<MatrixType,Lower> chollo(symmLo); + CholType<MatrixType,Upper> cholup(symmUp); + + for (int k=0; k<10; ++k) + { + VectorType vec = VectorType::Random(symm.rows()); + RealScalar sigma = internal::random<RealScalar>(); + symmCpy += sigma * vec * vec.adjoint(); + + // we are doing some downdates, so it might be the case that the matrix is not SPD anymore + CholType<MatrixType,Lower> chol(symmCpy); + if(chol.info()!=Success) + break; + + chollo.rankUpdate(vec, sigma); + VERIFY_IS_APPROX(symmCpy, chollo.reconstructedMatrix()); + + cholup.rankUpdate(vec, sigma); + VERIFY_IS_APPROX(symmCpy, cholup.reconstructedMatrix()); + } +} + +template<typename MatrixType> void cholesky(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + /* this test covers the following files: + LLT.h LDLT.h + */ + Index rows = m.rows(); + Index cols = m.cols(); + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> SquareMatrixType; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + MatrixType a0 = MatrixType::Random(rows,cols); + VectorType vecB = VectorType::Random(rows), vecX(rows); + MatrixType matB = MatrixType::Random(rows,cols), matX(rows,cols); + SquareMatrixType symm = a0 * a0.adjoint(); + // let's make sure the matrix is not singular or near singular + for (int k=0; k<3; ++k) + { + MatrixType a1 = MatrixType::Random(rows,cols); + symm += a1 * a1.adjoint(); + } + + SquareMatrixType symmUp = symm.template triangularView<Upper>(); + SquareMatrixType symmLo = symm.template triangularView<Lower>(); + + // to test if really Cholesky only uses the upper triangular part, uncomment the following + // FIXME: currently that fails !! + //symm.template part<StrictlyLower>().setZero(); + + { + LLT<SquareMatrixType,Lower> chollo(symmLo); + VERIFY_IS_APPROX(symm, chollo.reconstructedMatrix()); + vecX = chollo.solve(vecB); + VERIFY_IS_APPROX(symm * vecX, vecB); + matX = chollo.solve(matB); + VERIFY_IS_APPROX(symm * matX, matB); + + // test the upper mode + LLT<SquareMatrixType,Upper> cholup(symmUp); + VERIFY_IS_APPROX(symm, cholup.reconstructedMatrix()); + vecX = cholup.solve(vecB); + VERIFY_IS_APPROX(symm * vecX, vecB); + matX = cholup.solve(matB); + VERIFY_IS_APPROX(symm * matX, matB); + + MatrixType neg = -symmLo; + chollo.compute(neg); + VERIFY(chollo.info()==NumericalIssue); + + VERIFY_IS_APPROX(MatrixType(chollo.matrixL().transpose().conjugate()), MatrixType(chollo.matrixU())); + VERIFY_IS_APPROX(MatrixType(chollo.matrixU().transpose().conjugate()), MatrixType(chollo.matrixL())); + VERIFY_IS_APPROX(MatrixType(cholup.matrixL().transpose().conjugate()), MatrixType(cholup.matrixU())); + VERIFY_IS_APPROX(MatrixType(cholup.matrixU().transpose().conjugate()), MatrixType(cholup.matrixL())); + } + + // LDLT + { + int sign = internal::random<int>()%2 ? 1 : -1; + + if(sign == -1) + { + symm = -symm; // test a negative matrix + } + + SquareMatrixType symmUp = symm.template triangularView<Upper>(); + SquareMatrixType symmLo = symm.template triangularView<Lower>(); + + LDLT<SquareMatrixType,Lower> ldltlo(symmLo); + VERIFY_IS_APPROX(symm, ldltlo.reconstructedMatrix()); + vecX = ldltlo.solve(vecB); + VERIFY_IS_APPROX(symm * vecX, vecB); + matX = ldltlo.solve(matB); + VERIFY_IS_APPROX(symm * matX, matB); + + LDLT<SquareMatrixType,Upper> ldltup(symmUp); + VERIFY_IS_APPROX(symm, ldltup.reconstructedMatrix()); + vecX = ldltup.solve(vecB); + VERIFY_IS_APPROX(symm * vecX, vecB); + matX = ldltup.solve(matB); + VERIFY_IS_APPROX(symm * matX, matB); + + VERIFY_IS_APPROX(MatrixType(ldltlo.matrixL().transpose().conjugate()), MatrixType(ldltlo.matrixU())); + VERIFY_IS_APPROX(MatrixType(ldltlo.matrixU().transpose().conjugate()), MatrixType(ldltlo.matrixL())); + VERIFY_IS_APPROX(MatrixType(ldltup.matrixL().transpose().conjugate()), MatrixType(ldltup.matrixU())); + VERIFY_IS_APPROX(MatrixType(ldltup.matrixU().transpose().conjugate()), MatrixType(ldltup.matrixL())); + + if(MatrixType::RowsAtCompileTime==Dynamic) + { + // note : each inplace permutation requires a small temporary vector (mask) + + // check inplace solve + matX = matB; + VERIFY_EVALUATION_COUNT(matX = ldltlo.solve(matX), 0); + VERIFY_IS_APPROX(matX, ldltlo.solve(matB).eval()); + + + matX = matB; + VERIFY_EVALUATION_COUNT(matX = ldltup.solve(matX), 0); + VERIFY_IS_APPROX(matX, ldltup.solve(matB).eval()); + } + + // restore + if(sign == -1) + symm = -symm; + } + + // test some special use cases of SelfCwiseBinaryOp: + MatrixType m1 = MatrixType::Random(rows,cols), m2(rows,cols); + m2 = m1; + m2 += symmLo.template selfadjointView<Lower>().llt().solve(matB); + VERIFY_IS_APPROX(m2, m1 + symmLo.template selfadjointView<Lower>().llt().solve(matB)); + m2 = m1; + m2 -= symmLo.template selfadjointView<Lower>().llt().solve(matB); + VERIFY_IS_APPROX(m2, m1 - symmLo.template selfadjointView<Lower>().llt().solve(matB)); + m2 = m1; + m2.noalias() += symmLo.template selfadjointView<Lower>().llt().solve(matB); + VERIFY_IS_APPROX(m2, m1 + symmLo.template selfadjointView<Lower>().llt().solve(matB)); + m2 = m1; + m2.noalias() -= symmLo.template selfadjointView<Lower>().llt().solve(matB); + VERIFY_IS_APPROX(m2, m1 - symmLo.template selfadjointView<Lower>().llt().solve(matB)); + + // update/downdate + CALL_SUBTEST(( test_chol_update<SquareMatrixType,LLT>(symm) )); + CALL_SUBTEST(( test_chol_update<SquareMatrixType,LDLT>(symm) )); +} + +template<typename MatrixType> void cholesky_cplx(const MatrixType& m) +{ + // classic test + cholesky(m); + + // test mixing real/scalar types + + typedef typename MatrixType::Index Index; + + Index rows = m.rows(); + Index cols = m.cols(); + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<RealScalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> RealMatrixType; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + RealMatrixType a0 = RealMatrixType::Random(rows,cols); + VectorType vecB = VectorType::Random(rows), vecX(rows); + MatrixType matB = MatrixType::Random(rows,cols), matX(rows,cols); + RealMatrixType symm = a0 * a0.adjoint(); + // let's make sure the matrix is not singular or near singular + for (int k=0; k<3; ++k) + { + RealMatrixType a1 = RealMatrixType::Random(rows,cols); + symm += a1 * a1.adjoint(); + } + + { + RealMatrixType symmLo = symm.template triangularView<Lower>(); + + LLT<RealMatrixType,Lower> chollo(symmLo); + VERIFY_IS_APPROX(symm, chollo.reconstructedMatrix()); + vecX = chollo.solve(vecB); + VERIFY_IS_APPROX(symm * vecX, vecB); +// matX = chollo.solve(matB); +// VERIFY_IS_APPROX(symm * matX, matB); + } + + // LDLT + { + int sign = internal::random<int>()%2 ? 1 : -1; + + if(sign == -1) + { + symm = -symm; // test a negative matrix + } + + RealMatrixType symmLo = symm.template triangularView<Lower>(); + + LDLT<RealMatrixType,Lower> ldltlo(symmLo); + VERIFY_IS_APPROX(symm, ldltlo.reconstructedMatrix()); + vecX = ldltlo.solve(vecB); + VERIFY_IS_APPROX(symm * vecX, vecB); +// matX = ldltlo.solve(matB); +// VERIFY_IS_APPROX(symm * matX, matB); + } +} + +// regression test for bug 241 +template<typename MatrixType> void cholesky_bug241(const MatrixType& m) +{ + eigen_assert(m.rows() == 2 && m.cols() == 2); + + typedef typename MatrixType::Scalar Scalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + MatrixType matA; + matA << 1, 1, 1, 1; + VectorType vecB; + vecB << 1, 1; + VectorType vecX = matA.ldlt().solve(vecB); + VERIFY_IS_APPROX(matA * vecX, vecB); +} + +template<typename MatrixType> void cholesky_verify_assert() +{ + MatrixType tmp; + + LLT<MatrixType> llt; + VERIFY_RAISES_ASSERT(llt.matrixL()) + VERIFY_RAISES_ASSERT(llt.matrixU()) + VERIFY_RAISES_ASSERT(llt.solve(tmp)) + VERIFY_RAISES_ASSERT(llt.solveInPlace(&tmp)) + + LDLT<MatrixType> ldlt; + VERIFY_RAISES_ASSERT(ldlt.matrixL()) + VERIFY_RAISES_ASSERT(ldlt.permutationP()) + VERIFY_RAISES_ASSERT(ldlt.vectorD()) + VERIFY_RAISES_ASSERT(ldlt.isPositive()) + VERIFY_RAISES_ASSERT(ldlt.isNegative()) + VERIFY_RAISES_ASSERT(ldlt.solve(tmp)) + VERIFY_RAISES_ASSERT(ldlt.solveInPlace(&tmp)) +} + +void test_cholesky() +{ + int s; + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( cholesky(Matrix<double,1,1>()) ); + CALL_SUBTEST_3( cholesky(Matrix2d()) ); + CALL_SUBTEST_3( cholesky_bug241(Matrix2d()) ); + CALL_SUBTEST_4( cholesky(Matrix3f()) ); + CALL_SUBTEST_5( cholesky(Matrix4d()) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE); + CALL_SUBTEST_2( cholesky(MatrixXd(s,s)) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2); + CALL_SUBTEST_6( cholesky_cplx(MatrixXcd(s,s)) ); + } + + CALL_SUBTEST_4( cholesky_verify_assert<Matrix3f>() ); + CALL_SUBTEST_7( cholesky_verify_assert<Matrix3d>() ); + CALL_SUBTEST_8( cholesky_verify_assert<MatrixXf>() ); + CALL_SUBTEST_2( cholesky_verify_assert<MatrixXd>() ); + + // Test problem size constructors + CALL_SUBTEST_9( LLT<MatrixXf>(10) ); + CALL_SUBTEST_9( LDLT<MatrixXf>(10) ); + + EIGEN_UNUSED_VARIABLE(s) +} diff --git a/test/cholmod_support.cpp b/test/cholmod_support.cpp new file mode 100644 index 000000000..8f8be3c0e --- /dev/null +++ b/test/cholmod_support.cpp @@ -0,0 +1,56 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Gael Guennebaud <g.gael@free.fr> +// +// 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 "sparse_solver.h" + +#include <Eigen/CholmodSupport> + +template<typename T> void test_cholmod_T() +{ + CholmodDecomposition<SparseMatrix<T>, Lower> g_chol_colmajor_lower; g_chol_colmajor_lower.setMode(CholmodSupernodalLLt); + CholmodDecomposition<SparseMatrix<T>, Upper> g_chol_colmajor_upper; g_chol_colmajor_upper.setMode(CholmodSupernodalLLt); + CholmodDecomposition<SparseMatrix<T>, Lower> g_llt_colmajor_lower; g_llt_colmajor_lower.setMode(CholmodSimplicialLLt); + CholmodDecomposition<SparseMatrix<T>, Upper> g_llt_colmajor_upper; g_llt_colmajor_upper.setMode(CholmodSimplicialLLt); + CholmodDecomposition<SparseMatrix<T>, Lower> g_ldlt_colmajor_lower; g_ldlt_colmajor_lower.setMode(CholmodLDLt); + CholmodDecomposition<SparseMatrix<T>, Upper> g_ldlt_colmajor_upper; g_ldlt_colmajor_upper.setMode(CholmodLDLt); + + CholmodSupernodalLLT<SparseMatrix<T>, Lower> chol_colmajor_lower; + CholmodSupernodalLLT<SparseMatrix<T>, Upper> chol_colmajor_upper; + CholmodSimplicialLLT<SparseMatrix<T>, Lower> llt_colmajor_lower; + CholmodSimplicialLLT<SparseMatrix<T>, Upper> llt_colmajor_upper; + CholmodSimplicialLDLT<SparseMatrix<T>, Lower> ldlt_colmajor_lower; + CholmodSimplicialLDLT<SparseMatrix<T>, Upper> ldlt_colmajor_upper; + + check_sparse_spd_solving(g_chol_colmajor_lower); + check_sparse_spd_solving(g_chol_colmajor_upper); + check_sparse_spd_solving(g_llt_colmajor_lower); + check_sparse_spd_solving(g_llt_colmajor_upper); + check_sparse_spd_solving(g_ldlt_colmajor_lower); + check_sparse_spd_solving(g_ldlt_colmajor_upper); + + check_sparse_spd_solving(chol_colmajor_lower); + check_sparse_spd_solving(chol_colmajor_upper); + check_sparse_spd_solving(llt_colmajor_lower); + check_sparse_spd_solving(llt_colmajor_upper); + check_sparse_spd_solving(ldlt_colmajor_lower); + check_sparse_spd_solving(ldlt_colmajor_upper); + +// check_sparse_spd_determinant(chol_colmajor_lower); +// check_sparse_spd_determinant(chol_colmajor_upper); +// check_sparse_spd_determinant(llt_colmajor_lower); +// check_sparse_spd_determinant(llt_colmajor_upper); +// check_sparse_spd_determinant(ldlt_colmajor_lower); +// check_sparse_spd_determinant(ldlt_colmajor_upper); +} + +void test_cholmod_support() +{ + CALL_SUBTEST_1(test_cholmod_T<double>()); + CALL_SUBTEST_2(test_cholmod_T<std::complex<double> >()); +} diff --git a/test/commainitializer.cpp b/test/commainitializer.cpp new file mode 100644 index 000000000..99102b966 --- /dev/null +++ b/test/commainitializer.cpp @@ -0,0 +1,46 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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" + +void test_commainitializer() +{ + Matrix3d m3; + Matrix4d m4; + + VERIFY_RAISES_ASSERT( (m3 << 1, 2, 3, 4, 5, 6, 7, 8) ); + + #ifndef _MSC_VER + VERIFY_RAISES_ASSERT( (m3 << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) ); + #endif + + double data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + Matrix3d ref = Map<Matrix<double,3,3,RowMajor> >(data); + + m3 = Matrix3d::Random(); + m3 << 1, 2, 3, 4, 5, 6, 7, 8, 9; + VERIFY_IS_APPROX(m3, ref ); + + Vector3d vec[3]; + vec[0] << 1, 4, 7; + vec[1] << 2, 5, 8; + vec[2] << 3, 6, 9; + m3 = Matrix3d::Random(); + m3 << vec[0], vec[1], vec[2]; + VERIFY_IS_APPROX(m3, ref); + + vec[0] << 1, 2, 3; + vec[1] << 4, 5, 6; + vec[2] << 7, 8, 9; + m3 = Matrix3d::Random(); + m3 << vec[0].transpose(), + 4, 5, 6, + vec[2].transpose(); + VERIFY_IS_APPROX(m3, ref); +} diff --git a/test/conjugate_gradient.cpp b/test/conjugate_gradient.cpp new file mode 100644 index 000000000..869051b31 --- /dev/null +++ b/test/conjugate_gradient.cpp @@ -0,0 +1,30 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Gael Guennebaud <g.gael@free.fr> +// +// 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 "sparse_solver.h" +#include <Eigen/IterativeLinearSolvers> + +template<typename T> void test_conjugate_gradient_T() +{ + ConjugateGradient<SparseMatrix<T>, Lower> cg_colmajor_lower_diag; + ConjugateGradient<SparseMatrix<T>, Upper> cg_colmajor_upper_diag; + ConjugateGradient<SparseMatrix<T>, Lower, IdentityPreconditioner> cg_colmajor_lower_I; + ConjugateGradient<SparseMatrix<T>, Upper, IdentityPreconditioner> cg_colmajor_upper_I; + + CALL_SUBTEST( check_sparse_spd_solving(cg_colmajor_lower_diag) ); + CALL_SUBTEST( check_sparse_spd_solving(cg_colmajor_upper_diag) ); + CALL_SUBTEST( check_sparse_spd_solving(cg_colmajor_lower_I) ); + CALL_SUBTEST( check_sparse_spd_solving(cg_colmajor_upper_I) ); +} + +void test_conjugate_gradient() +{ + CALL_SUBTEST_1(test_conjugate_gradient_T<double>()); + CALL_SUBTEST_2(test_conjugate_gradient_T<std::complex<double> >()); +} diff --git a/test/conservative_resize.cpp b/test/conservative_resize.cpp new file mode 100644 index 000000000..4d11e4075 --- /dev/null +++ b/test/conservative_resize.cpp @@ -0,0 +1,114 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Hauke Heibel <hauke.heibel@gmail.com> +// +// 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" + +#include <Eigen/Core> + +using namespace Eigen; + +template <typename Scalar, int Storage> +void run_matrix_tests() +{ + typedef Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Storage> MatrixType; + typedef typename MatrixType::Index Index; + + MatrixType m, n; + + // boundary cases ... + m = n = MatrixType::Random(50,50); + m.conservativeResize(1,50); + VERIFY_IS_APPROX(m, n.block(0,0,1,50)); + + m = n = MatrixType::Random(50,50); + m.conservativeResize(50,1); + VERIFY_IS_APPROX(m, n.block(0,0,50,1)); + + m = n = MatrixType::Random(50,50); + m.conservativeResize(50,50); + VERIFY_IS_APPROX(m, n.block(0,0,50,50)); + + // random shrinking ... + for (int i=0; i<25; ++i) + { + const Index rows = internal::random<Index>(1,50); + const Index cols = internal::random<Index>(1,50); + m = n = MatrixType::Random(50,50); + m.conservativeResize(rows,cols); + VERIFY_IS_APPROX(m, n.block(0,0,rows,cols)); + } + + // random growing with zeroing ... + for (int i=0; i<25; ++i) + { + const Index rows = internal::random<Index>(50,75); + const Index cols = internal::random<Index>(50,75); + m = n = MatrixType::Random(50,50); + m.conservativeResizeLike(MatrixType::Zero(rows,cols)); + VERIFY_IS_APPROX(m.block(0,0,n.rows(),n.cols()), n); + VERIFY( rows<=50 || m.block(50,0,rows-50,cols).sum() == Scalar(0) ); + VERIFY( cols<=50 || m.block(0,50,rows,cols-50).sum() == Scalar(0) ); + } +} + +template <typename Scalar> +void run_vector_tests() +{ + typedef Matrix<Scalar, 1, Eigen::Dynamic> MatrixType; + + MatrixType m, n; + + // boundary cases ... + m = n = MatrixType::Random(50); + m.conservativeResize(1); + VERIFY_IS_APPROX(m, n.segment(0,1)); + + m = n = MatrixType::Random(50); + m.conservativeResize(50); + VERIFY_IS_APPROX(m, n.segment(0,50)); + + // random shrinking ... + for (int i=0; i<50; ++i) + { + const int size = internal::random<int>(1,50); + m = n = MatrixType::Random(50); + m.conservativeResize(size); + VERIFY_IS_APPROX(m, n.segment(0,size)); + } + + // random growing with zeroing ... + for (int i=0; i<50; ++i) + { + const int size = internal::random<int>(50,100); + m = n = MatrixType::Random(50); + m.conservativeResizeLike(MatrixType::Zero(size)); + VERIFY_IS_APPROX(m.segment(0,50), n); + VERIFY( size<=50 || m.segment(50,size-50).sum() == Scalar(0) ); + } +} + +void test_conservative_resize() +{ + CALL_SUBTEST_1((run_matrix_tests<int, Eigen::RowMajor>())); + CALL_SUBTEST_1((run_matrix_tests<int, Eigen::ColMajor>())); + CALL_SUBTEST_2((run_matrix_tests<float, Eigen::RowMajor>())); + CALL_SUBTEST_2((run_matrix_tests<float, Eigen::ColMajor>())); + CALL_SUBTEST_3((run_matrix_tests<double, Eigen::RowMajor>())); + CALL_SUBTEST_3((run_matrix_tests<double, Eigen::ColMajor>())); + CALL_SUBTEST_4((run_matrix_tests<std::complex<float>, Eigen::RowMajor>())); + CALL_SUBTEST_4((run_matrix_tests<std::complex<float>, Eigen::ColMajor>())); + CALL_SUBTEST_5((run_matrix_tests<std::complex<double>, Eigen::RowMajor>())); + CALL_SUBTEST_6((run_matrix_tests<std::complex<double>, Eigen::ColMajor>())); + + CALL_SUBTEST_1((run_vector_tests<int>())); + CALL_SUBTEST_2((run_vector_tests<float>())); + CALL_SUBTEST_3((run_vector_tests<double>())); + CALL_SUBTEST_4((run_vector_tests<std::complex<float> >())); + CALL_SUBTEST_5((run_vector_tests<std::complex<double> >())); +} diff --git a/test/corners.cpp b/test/corners.cpp new file mode 100644 index 000000000..4705c5f05 --- /dev/null +++ b/test/corners.cpp @@ -0,0 +1,98 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2006-2010 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" + +#define COMPARE_CORNER(A,B) \ + VERIFY_IS_EQUAL(matrix.A, matrix.B); \ + VERIFY_IS_EQUAL(const_matrix.A, const_matrix.B); + +template<typename MatrixType> void corners(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + Index rows = m.rows(); + Index cols = m.cols(); + + Index r = internal::random<Index>(1,rows); + Index c = internal::random<Index>(1,cols); + + MatrixType matrix = MatrixType::Random(rows,cols); + const MatrixType const_matrix = MatrixType::Random(rows,cols); + + COMPARE_CORNER(topLeftCorner(r,c), block(0,0,r,c)); + COMPARE_CORNER(topRightCorner(r,c), block(0,cols-c,r,c)); + COMPARE_CORNER(bottomLeftCorner(r,c), block(rows-r,0,r,c)); + COMPARE_CORNER(bottomRightCorner(r,c), block(rows-r,cols-c,r,c)); + + Index sr = internal::random<Index>(1,rows) - 1; + Index nr = internal::random<Index>(1,rows-sr); + Index sc = internal::random<Index>(1,cols) - 1; + Index nc = internal::random<Index>(1,cols-sc); + + COMPARE_CORNER(topRows(r), block(0,0,r,cols)); + COMPARE_CORNER(middleRows(sr,nr), block(sr,0,nr,cols)); + COMPARE_CORNER(bottomRows(r), block(rows-r,0,r,cols)); + COMPARE_CORNER(leftCols(c), block(0,0,rows,c)); + COMPARE_CORNER(middleCols(sc,nc), block(0,sc,rows,nc)); + COMPARE_CORNER(rightCols(c), block(0,cols-c,rows,c)); +} + +template<typename MatrixType, int CRows, int CCols, int SRows, int SCols> void corners_fixedsize() +{ + MatrixType matrix = MatrixType::Random(); + const MatrixType const_matrix = MatrixType::Random(); + + enum { + rows = MatrixType::RowsAtCompileTime, + cols = MatrixType::ColsAtCompileTime, + r = CRows, + c = CCols, + sr = SRows, + sc = SCols + }; + + VERIFY_IS_EQUAL((matrix.template topLeftCorner<r,c>()), (matrix.template block<r,c>(0,0))); + VERIFY_IS_EQUAL((matrix.template topRightCorner<r,c>()), (matrix.template block<r,c>(0,cols-c))); + VERIFY_IS_EQUAL((matrix.template bottomLeftCorner<r,c>()), (matrix.template block<r,c>(rows-r,0))); + VERIFY_IS_EQUAL((matrix.template bottomRightCorner<r,c>()), (matrix.template block<r,c>(rows-r,cols-c))); + + VERIFY_IS_EQUAL((matrix.template topRows<r>()), (matrix.template block<r,cols>(0,0))); + VERIFY_IS_EQUAL((matrix.template middleRows<r>(sr)), (matrix.template block<r,cols>(sr,0))); + VERIFY_IS_EQUAL((matrix.template bottomRows<r>()), (matrix.template block<r,cols>(rows-r,0))); + VERIFY_IS_EQUAL((matrix.template leftCols<c>()), (matrix.template block<rows,c>(0,0))); + VERIFY_IS_EQUAL((matrix.template middleCols<c>(sc)), (matrix.template block<rows,c>(0,sc))); + VERIFY_IS_EQUAL((matrix.template rightCols<c>()), (matrix.template block<rows,c>(0,cols-c))); + + VERIFY_IS_EQUAL((const_matrix.template topLeftCorner<r,c>()), (const_matrix.template block<r,c>(0,0))); + VERIFY_IS_EQUAL((const_matrix.template topRightCorner<r,c>()), (const_matrix.template block<r,c>(0,cols-c))); + VERIFY_IS_EQUAL((const_matrix.template bottomLeftCorner<r,c>()), (const_matrix.template block<r,c>(rows-r,0))); + VERIFY_IS_EQUAL((const_matrix.template bottomRightCorner<r,c>()), (const_matrix.template block<r,c>(rows-r,cols-c))); + + VERIFY_IS_EQUAL((const_matrix.template topRows<r>()), (const_matrix.template block<r,cols>(0,0))); + VERIFY_IS_EQUAL((const_matrix.template middleRows<r>(sr)), (const_matrix.template block<r,cols>(sr,0))); + VERIFY_IS_EQUAL((const_matrix.template bottomRows<r>()), (const_matrix.template block<r,cols>(rows-r,0))); + VERIFY_IS_EQUAL((const_matrix.template leftCols<c>()), (const_matrix.template block<rows,c>(0,0))); + VERIFY_IS_EQUAL((const_matrix.template middleCols<c>(sc)), (const_matrix.template block<rows,c>(0,sc))); + VERIFY_IS_EQUAL((const_matrix.template rightCols<c>()), (const_matrix.template block<rows,c>(0,cols-c))); +} + +void test_corners() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( corners(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( corners(Matrix4d()) ); + CALL_SUBTEST_3( corners(Matrix<int,10,12>()) ); + CALL_SUBTEST_4( corners(MatrixXcf(5, 7)) ); + CALL_SUBTEST_5( corners(MatrixXf(21, 20)) ); + + CALL_SUBTEST_1(( corners_fixedsize<Matrix<float, 1, 1>, 1, 1, 0, 0>() )); + CALL_SUBTEST_2(( corners_fixedsize<Matrix4d,2,2,1,1>() )); + CALL_SUBTEST_3(( corners_fixedsize<Matrix<int,10,12>,4,7,5,2>() )); + } +} diff --git a/test/cwiseop.cpp b/test/cwiseop.cpp new file mode 100644 index 000000000..ca6e4211c --- /dev/null +++ b/test/cwiseop.cpp @@ -0,0 +1,165 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#define EIGEN2_SUPPORT +#define EIGEN_NO_STATIC_ASSERT +#include "main.h" +#include <functional> + +#ifdef min +#undef min +#endif + +#ifdef max +#undef max +#endif + +using namespace std; + +template<typename Scalar> struct AddIfNull { + const Scalar operator() (const Scalar a, const Scalar b) const {return a<=1e-3 ? b : a;} + enum { Cost = NumTraits<Scalar>::AddCost }; +}; + +template<typename MatrixType> void cwiseops(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols), + m4(rows, cols), + mzero = MatrixType::Zero(rows, cols), + mones = MatrixType::Ones(rows, cols), + identity = Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> + ::Identity(rows, rows); + VectorType vzero = VectorType::Zero(rows), + vones = VectorType::Ones(rows), + v3(rows); + + Index r = internal::random<Index>(0, rows-1), + c = internal::random<Index>(0, cols-1); + + Scalar s1 = internal::random<Scalar>(); + + // test Zero, Ones, Constant, and the set* variants + m3 = MatrixType::Constant(rows, cols, s1); + for (int j=0; j<cols; ++j) + for (int i=0; i<rows; ++i) + { + VERIFY_IS_APPROX(mzero(i,j), Scalar(0)); + VERIFY_IS_APPROX(mones(i,j), Scalar(1)); + VERIFY_IS_APPROX(m3(i,j), s1); + } + VERIFY(mzero.isZero()); + VERIFY(mones.isOnes()); + VERIFY(m3.isConstant(s1)); + VERIFY(identity.isIdentity()); + VERIFY_IS_APPROX(m4.setConstant(s1), m3); + VERIFY_IS_APPROX(m4.setConstant(rows,cols,s1), m3); + VERIFY_IS_APPROX(m4.setZero(), mzero); + VERIFY_IS_APPROX(m4.setZero(rows,cols), mzero); + VERIFY_IS_APPROX(m4.setOnes(), mones); + VERIFY_IS_APPROX(m4.setOnes(rows,cols), mones); + m4.fill(s1); + VERIFY_IS_APPROX(m4, m3); + + VERIFY_IS_APPROX(v3.setConstant(rows, s1), VectorType::Constant(rows,s1)); + VERIFY_IS_APPROX(v3.setZero(rows), vzero); + VERIFY_IS_APPROX(v3.setOnes(rows), vones); + + m2 = m2.template binaryExpr<AddIfNull<Scalar> >(mones); + + VERIFY_IS_APPROX(m1.cwise().pow(2), m1.cwise().abs2()); + VERIFY_IS_APPROX(m1.cwise().pow(2), m1.cwise().square()); + VERIFY_IS_APPROX(m1.cwise().pow(3), m1.cwise().cube()); + + VERIFY_IS_APPROX(m1 + mones, m1.cwise()+Scalar(1)); + VERIFY_IS_APPROX(m1 - mones, m1.cwise()-Scalar(1)); + m3 = m1; m3.cwise() += 1; + VERIFY_IS_APPROX(m1 + mones, m3); + m3 = m1; m3.cwise() -= 1; + VERIFY_IS_APPROX(m1 - mones, m3); + + VERIFY_IS_APPROX(m2, m2.cwise() * mones); + VERIFY_IS_APPROX(m1.cwise() * m2, m2.cwise() * m1); + m3 = m1; + m3.cwise() *= m2; + VERIFY_IS_APPROX(m3, m1.cwise() * m2); + + VERIFY_IS_APPROX(mones, m2.cwise()/m2); + if(!NumTraits<Scalar>::IsInteger) + { + VERIFY_IS_APPROX(m1.cwise() / m2, m1.cwise() * (m2.cwise().inverse())); + m3 = m1.cwise().abs().cwise().sqrt(); + VERIFY_IS_APPROX(m3.cwise().square(), m1.cwise().abs()); + VERIFY_IS_APPROX(m1.cwise().square().cwise().sqrt(), m1.cwise().abs()); + VERIFY_IS_APPROX(m1.cwise().abs().cwise().log().cwise().exp() , m1.cwise().abs()); + + VERIFY_IS_APPROX(m1.cwise().pow(2), m1.cwise().square()); + m3 = (m1.cwise().abs().cwise()<=RealScalar(0.01)).select(mones,m1); + VERIFY_IS_APPROX(m3.cwise().pow(-1), m3.cwise().inverse()); + m3 = m1.cwise().abs(); + VERIFY_IS_APPROX(m3.cwise().pow(RealScalar(0.5)), m3.cwise().sqrt()); + +// VERIFY_IS_APPROX(m1.cwise().tan(), m1.cwise().sin().cwise() / m1.cwise().cos()); + VERIFY_IS_APPROX(mones, m1.cwise().sin().cwise().square() + m1.cwise().cos().cwise().square()); + m3 = m1; + m3.cwise() /= m2; + VERIFY_IS_APPROX(m3, m1.cwise() / m2); + } + + // check min + VERIFY_IS_APPROX( m1.cwise().min(m2), m2.cwise().min(m1) ); + VERIFY_IS_APPROX( m1.cwise().min(m1+mones), m1 ); + VERIFY_IS_APPROX( m1.cwise().min(m1-mones), m1-mones ); + + // check max + VERIFY_IS_APPROX( m1.cwise().max(m2), m2.cwise().max(m1) ); + VERIFY_IS_APPROX( m1.cwise().max(m1-mones), m1 ); + VERIFY_IS_APPROX( m1.cwise().max(m1+mones), m1+mones ); + + VERIFY( (m1.cwise() == m1).all() ); + VERIFY( (m1.cwise() != m2).any() ); + VERIFY(!(m1.cwise() == (m1+mones)).any() ); + if (rows*cols>1) + { + m3 = m1; + m3(r,c) += 1; + VERIFY( (m1.cwise() == m3).any() ); + VERIFY( !(m1.cwise() == m3).all() ); + } + VERIFY( (m1.cwise().min(m2).cwise() <= m2).all() ); + VERIFY( (m1.cwise().max(m2).cwise() >= m2).all() ); + VERIFY( (m1.cwise().min(m2).cwise() < (m1+mones)).all() ); + VERIFY( (m1.cwise().max(m2).cwise() > (m1-mones)).all() ); + + VERIFY( (m1.cwise()<m1.unaryExpr(bind2nd(plus<Scalar>(), Scalar(1)))).all() ); + VERIFY( !(m1.cwise()<m1.unaryExpr(bind2nd(minus<Scalar>(), Scalar(1)))).all() ); + VERIFY( !(m1.cwise()>m1.unaryExpr(bind2nd(plus<Scalar>(), Scalar(1)))).any() ); +} + +void test_cwiseop() +{ + for(int i = 0; i < g_repeat ; i++) { + CALL_SUBTEST_1( cwiseops(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( cwiseops(Matrix4d()) ); + CALL_SUBTEST_3( cwiseops(MatrixXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_4( cwiseops(MatrixXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_5( cwiseops(MatrixXi(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_6( cwiseops(MatrixXd(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + } +} diff --git a/test/determinant.cpp b/test/determinant.cpp new file mode 100644 index 000000000..e93f2f297 --- /dev/null +++ b/test/determinant.cpp @@ -0,0 +1,67 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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" +#include <Eigen/LU> + +template<typename MatrixType> void determinant(const MatrixType& m) +{ + /* this test covers the following files: + Determinant.h + */ + typedef typename MatrixType::Index Index; + Index size = m.rows(); + + MatrixType m1(size, size), m2(size, size); + m1.setRandom(); + m2.setRandom(); + typedef typename MatrixType::Scalar Scalar; + Scalar x = internal::random<Scalar>(); + VERIFY_IS_APPROX(MatrixType::Identity(size, size).determinant(), Scalar(1)); + VERIFY_IS_APPROX((m1*m2).eval().determinant(), m1.determinant() * m2.determinant()); + if(size==1) return; + Index i = internal::random<Index>(0, size-1); + Index j; + do { + j = internal::random<Index>(0, size-1); + } while(j==i); + m2 = m1; + m2.row(i).swap(m2.row(j)); + VERIFY_IS_APPROX(m2.determinant(), -m1.determinant()); + m2 = m1; + m2.col(i).swap(m2.col(j)); + VERIFY_IS_APPROX(m2.determinant(), -m1.determinant()); + VERIFY_IS_APPROX(m2.determinant(), m2.transpose().determinant()); + VERIFY_IS_APPROX(internal::conj(m2.determinant()), m2.adjoint().determinant()); + m2 = m1; + m2.row(i) += x*m2.row(j); + VERIFY_IS_APPROX(m2.determinant(), m1.determinant()); + m2 = m1; + m2.row(i) *= x; + VERIFY_IS_APPROX(m2.determinant(), m1.determinant() * x); + + // check empty matrix + VERIFY_IS_APPROX(m2.block(0,0,0,0).determinant(), Scalar(1)); +} + +void test_determinant() +{ + int s; + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( determinant(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( determinant(Matrix<double, 2, 2>()) ); + CALL_SUBTEST_3( determinant(Matrix<double, 3, 3>()) ); + CALL_SUBTEST_4( determinant(Matrix<double, 4, 4>()) ); + CALL_SUBTEST_5( determinant(Matrix<std::complex<double>, 10, 10>()) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/4); + CALL_SUBTEST_6( determinant(MatrixXd(s, s)) ); + } + EIGEN_UNUSED_VARIABLE(s) +} diff --git a/test/diagonal.cpp b/test/diagonal.cpp new file mode 100644 index 000000000..95cd10372 --- /dev/null +++ b/test/diagonal.cpp @@ -0,0 +1,83 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2006-2010 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename MatrixType> void diagonal(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<Scalar, 1, MatrixType::ColsAtCompileTime> RowVectorType; + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols); + + //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>1 ? 1 : 0, + N2 = MatrixType::RowsAtCompileTime>2 ? -2 : 0 + }; + + // check sub/super diagonal + if(m1.template diagonal<N1>().RowsAtCompileTime!=Dynamic) + { + VERIFY(m1.template diagonal<N1>().RowsAtCompileTime == m1.diagonal(N1).size()); + } + if(m1.template diagonal<N2>().RowsAtCompileTime!=Dynamic) + { + VERIFY(m1.template diagonal<N2>().RowsAtCompileTime == m1.diagonal(N2).size()); + } + + m2.template diagonal<N1>() = 2 * m1.template diagonal<N1>(); + VERIFY_IS_APPROX(m2.template diagonal<N1>(), static_cast<Scalar>(2) * m1.diagonal(N1)); + m2.template diagonal<N1>()[0] *= 3; + VERIFY_IS_APPROX(m2.template diagonal<N1>()[0], static_cast<Scalar>(6) * m1.template diagonal<N1>()[0]); + + + m2.template diagonal<N2>() = 2 * m1.template diagonal<N2>(); + m2.template diagonal<N2>()[0] *= 3; + VERIFY_IS_APPROX(m2.template diagonal<N2>()[0], static_cast<Scalar>(6) * m1.template diagonal<N2>()[0]); + + m2.diagonal(N1) = 2 * m1.diagonal(N1); + VERIFY_IS_APPROX(m2.diagonal<N1>(), static_cast<Scalar>(2) * m1.diagonal(N1)); + m2.diagonal(N1)[0] *= 3; + VERIFY_IS_APPROX(m2.diagonal(N1)[0], static_cast<Scalar>(6) * m1.diagonal(N1)[0]); + + m2.diagonal(N2) = 2 * m1.diagonal(N2); + VERIFY_IS_APPROX(m2.diagonal<N2>(), static_cast<Scalar>(2) * m1.diagonal(N2)); + m2.diagonal(N2)[0] *= 3; + VERIFY_IS_APPROX(m2.diagonal(N2)[0], static_cast<Scalar>(6) * m1.diagonal(N2)[0]); + } +} + +void test_diagonal() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( diagonal(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_1( diagonal(Matrix<float, 4, 9>()) ); + CALL_SUBTEST_1( diagonal(Matrix<float, 7, 3>()) ); + CALL_SUBTEST_2( diagonal(Matrix4d()) ); + CALL_SUBTEST_2( diagonal(MatrixXcf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_2( diagonal(MatrixXi(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_2( diagonal(MatrixXcd(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_1( diagonal(MatrixXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_1( diagonal(Matrix<float,Dynamic,4>(3, 4)) ); + } +} diff --git a/test/diagonalmatrices.cpp b/test/diagonalmatrices.cpp new file mode 100644 index 000000000..3f5776dfc --- /dev/null +++ b/test/diagonalmatrices.cpp @@ -0,0 +1,94 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +using namespace std; +template<typename MatrixType> void diagonalmatrices(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + enum { Rows = MatrixType::RowsAtCompileTime, Cols = MatrixType::ColsAtCompileTime }; + typedef Matrix<Scalar, Rows, 1> VectorType; + typedef Matrix<Scalar, 1, Cols> RowVectorType; + typedef Matrix<Scalar, Rows, Rows> SquareMatrixType; + typedef DiagonalMatrix<Scalar, Rows> LeftDiagonalMatrix; + typedef DiagonalMatrix<Scalar, Cols> RightDiagonalMatrix; + typedef Matrix<Scalar, Rows==Dynamic?Dynamic:2*Rows, Cols==Dynamic?Dynamic:2*Cols> BigMatrix; + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols); + VectorType v1 = VectorType::Random(rows), + v2 = VectorType::Random(rows); + RowVectorType rv1 = RowVectorType::Random(cols), + rv2 = RowVectorType::Random(cols); + LeftDiagonalMatrix ldm1(v1), ldm2(v2); + RightDiagonalMatrix rdm1(rv1), rdm2(rv2); + + SquareMatrixType sq_m1 (v1.asDiagonal()); + VERIFY_IS_APPROX(sq_m1, v1.asDiagonal().toDenseMatrix()); + sq_m1 = v1.asDiagonal(); + VERIFY_IS_APPROX(sq_m1, v1.asDiagonal().toDenseMatrix()); + SquareMatrixType sq_m2 = v1.asDiagonal(); + VERIFY_IS_APPROX(sq_m1, sq_m2); + + ldm1 = v1.asDiagonal(); + LeftDiagonalMatrix ldm3(v1); + VERIFY_IS_APPROX(ldm1.diagonal(), ldm3.diagonal()); + LeftDiagonalMatrix ldm4 = v1.asDiagonal(); + VERIFY_IS_APPROX(ldm1.diagonal(), ldm4.diagonal()); + + sq_m1.block(0,0,rows,rows) = ldm1; + VERIFY_IS_APPROX(sq_m1, ldm1.toDenseMatrix()); + sq_m1.transpose() = ldm1; + VERIFY_IS_APPROX(sq_m1, ldm1.toDenseMatrix()); + + Index i = internal::random<Index>(0, rows-1); + Index j = internal::random<Index>(0, cols-1); + + VERIFY_IS_APPROX( ((ldm1 * m1)(i,j)) , ldm1.diagonal()(i) * m1(i,j) ); + VERIFY_IS_APPROX( ((ldm1 * (m1+m2))(i,j)) , ldm1.diagonal()(i) * (m1+m2)(i,j) ); + VERIFY_IS_APPROX( ((m1 * rdm1)(i,j)) , rdm1.diagonal()(j) * m1(i,j) ); + VERIFY_IS_APPROX( ((v1.asDiagonal() * m1)(i,j)) , v1(i) * m1(i,j) ); + VERIFY_IS_APPROX( ((m1 * rv1.asDiagonal())(i,j)) , rv1(j) * m1(i,j) ); + VERIFY_IS_APPROX( (((v1+v2).asDiagonal() * m1)(i,j)) , (v1+v2)(i) * m1(i,j) ); + VERIFY_IS_APPROX( (((v1+v2).asDiagonal() * (m1+m2))(i,j)) , (v1+v2)(i) * (m1+m2)(i,j) ); + VERIFY_IS_APPROX( ((m1 * (rv1+rv2).asDiagonal())(i,j)) , (rv1+rv2)(j) * m1(i,j) ); + VERIFY_IS_APPROX( (((m1+m2) * (rv1+rv2).asDiagonal())(i,j)) , (rv1+rv2)(j) * (m1+m2)(i,j) ); + + BigMatrix big; + big.setZero(2*rows, 2*cols); + + big.block(i,j,rows,cols) = m1; + big.block(i,j,rows,cols) = v1.asDiagonal() * big.block(i,j,rows,cols); + + VERIFY_IS_APPROX((big.block(i,j,rows,cols)) , v1.asDiagonal() * m1 ); + + big.block(i,j,rows,cols) = m1; + big.block(i,j,rows,cols) = big.block(i,j,rows,cols) * rv1.asDiagonal(); + VERIFY_IS_APPROX((big.block(i,j,rows,cols)) , m1 * rv1.asDiagonal() ); + +} + +void test_diagonalmatrices() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( diagonalmatrices(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( diagonalmatrices(Matrix3f()) ); + CALL_SUBTEST_3( diagonalmatrices(Matrix<double,3,3,RowMajor>()) ); + CALL_SUBTEST_4( diagonalmatrices(Matrix4d()) ); + CALL_SUBTEST_5( diagonalmatrices(Matrix<float,4,4,RowMajor>()) ); + CALL_SUBTEST_6( diagonalmatrices(MatrixXcf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_7( diagonalmatrices(MatrixXi(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_8( diagonalmatrices(Matrix<double,Dynamic,Dynamic,RowMajor>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_9( diagonalmatrices(MatrixXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + } +} diff --git a/test/dontalign.cpp b/test/dontalign.cpp new file mode 100644 index 000000000..4643cfed6 --- /dev/null +++ b/test/dontalign.cpp @@ -0,0 +1,63 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#if defined EIGEN_TEST_PART_1 || defined EIGEN_TEST_PART_2 || defined EIGEN_TEST_PART_3 || defined EIGEN_TEST_PART_4 +#define EIGEN_DONT_ALIGN +#elif defined EIGEN_TEST_PART_5 || defined EIGEN_TEST_PART_6 || defined EIGEN_TEST_PART_7 || defined EIGEN_TEST_PART_8 +#define EIGEN_DONT_ALIGN_STATICALLY +#endif + +#include "main.h" +#include <Eigen/Dense> + +template<typename MatrixType> +void dontalign(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> SquareMatrixType; + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType a = MatrixType::Random(rows,cols); + SquareMatrixType square = SquareMatrixType::Random(rows,rows); + VectorType v = VectorType::Random(rows); + + VERIFY_IS_APPROX(v, square * square.colPivHouseholderQr().solve(v)); + square = square.inverse().eval(); + a = square * a; + square = square*square; + v = square * v; + v = a.adjoint() * v; + VERIFY(square.determinant() != Scalar(0)); + + // bug 219: MapAligned() was giving an assert with EIGEN_DONT_ALIGN, because Map Flags were miscomputed + Scalar* array = internal::aligned_new<Scalar>(rows); + v = VectorType::MapAligned(array, rows); + internal::aligned_delete(array, rows); +} + +void test_dontalign() +{ +#if defined EIGEN_TEST_PART_1 || defined EIGEN_TEST_PART_5 + dontalign(Matrix3d()); + dontalign(Matrix4f()); +#elif defined EIGEN_TEST_PART_2 || defined EIGEN_TEST_PART_6 + dontalign(Matrix3cd()); + dontalign(Matrix4cf()); +#elif defined EIGEN_TEST_PART_3 || defined EIGEN_TEST_PART_7 + dontalign(Matrix<float, 32, 32>()); + dontalign(Matrix<std::complex<float>, 32, 32>()); +#elif defined EIGEN_TEST_PART_4 || defined EIGEN_TEST_PART_8 + dontalign(MatrixXd(32, 32)); + dontalign(MatrixXcf(32, 32)); +#endif +} diff --git a/test/dynalloc.cpp b/test/dynalloc.cpp new file mode 100644 index 000000000..c7ccbffef --- /dev/null +++ b/test/dynalloc.cpp @@ -0,0 +1,133 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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" + +#if EIGEN_ALIGN +#define ALIGNMENT 16 +#else +#define ALIGNMENT 1 +#endif + +void check_handmade_aligned_malloc() +{ + for(int i = 1; i < 1000; i++) + { + char *p = (char*)internal::handmade_aligned_malloc(i); + VERIFY(size_t(p)%ALIGNMENT==0); + // if the buffer is wrongly allocated this will give a bad write --> check with valgrind + for(int j = 0; j < i; j++) p[j]=0; + internal::handmade_aligned_free(p); + } +} + +void check_aligned_malloc() +{ + for(int i = 1; i < 1000; i++) + { + char *p = (char*)internal::aligned_malloc(i); + VERIFY(size_t(p)%ALIGNMENT==0); + // if the buffer is wrongly allocated this will give a bad write --> check with valgrind + for(int j = 0; j < i; j++) p[j]=0; + internal::aligned_free(p); + } +} + +void check_aligned_new() +{ + for(int i = 1; i < 1000; i++) + { + float *p = internal::aligned_new<float>(i); + VERIFY(size_t(p)%ALIGNMENT==0); + // if the buffer is wrongly allocated this will give a bad write --> check with valgrind + for(int j = 0; j < i; j++) p[j]=0; + internal::aligned_delete(p,i); + } +} + +void check_aligned_stack_alloc() +{ + for(int i = 1; i < 1000; i++) + { + ei_declare_aligned_stack_constructed_variable(float,p,i,0); + VERIFY(size_t(p)%ALIGNMENT==0); + // if the buffer is wrongly allocated this will give a bad write --> check with valgrind + for(int j = 0; j < i; j++) p[j]=0; + } +} + + +// test compilation with both a struct and a class... +struct MyStruct +{ + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + char dummychar; + Vector4f avec; +}; + +class MyClassA +{ + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + char dummychar; + Vector4f avec; +}; + +template<typename T> void check_dynaligned() +{ + T* obj = new T; + VERIFY(size_t(obj)%ALIGNMENT==0); + delete obj; +} + +void test_dynalloc() +{ + // low level dynamic memory allocation + CALL_SUBTEST(check_handmade_aligned_malloc()); + CALL_SUBTEST(check_aligned_malloc()); + CALL_SUBTEST(check_aligned_new()); + CALL_SUBTEST(check_aligned_stack_alloc()); + + for (int i=0; i<g_repeat*100; ++i) + { + CALL_SUBTEST(check_dynaligned<Vector4f>() ); + CALL_SUBTEST(check_dynaligned<Vector2d>() ); + CALL_SUBTEST(check_dynaligned<Matrix4f>() ); + CALL_SUBTEST(check_dynaligned<Vector4d>() ); + CALL_SUBTEST(check_dynaligned<Vector4i>() ); + } + + // check static allocation, who knows ? + #if EIGEN_ALIGN_STATICALLY + { + MyStruct foo0; VERIFY(size_t(foo0.avec.data())%ALIGNMENT==0); + MyClassA fooA; VERIFY(size_t(fooA.avec.data())%ALIGNMENT==0); + } + + // dynamic allocation, single object + for (int i=0; i<g_repeat*100; ++i) + { + MyStruct *foo0 = new MyStruct(); VERIFY(size_t(foo0->avec.data())%ALIGNMENT==0); + MyClassA *fooA = new MyClassA(); VERIFY(size_t(fooA->avec.data())%ALIGNMENT==0); + delete foo0; + delete fooA; + } + + // dynamic allocation, array + const int N = 10; + for (int i=0; i<g_repeat*100; ++i) + { + MyStruct *foo0 = new MyStruct[N]; VERIFY(size_t(foo0->avec.data())%ALIGNMENT==0); + MyClassA *fooA = new MyClassA[N]; VERIFY(size_t(fooA->avec.data())%ALIGNMENT==0); + delete[] foo0; + delete[] fooA; + } + #endif + +} diff --git a/test/eigen2/CMakeLists.txt b/test/eigen2/CMakeLists.txt new file mode 100644 index 000000000..84931e037 --- /dev/null +++ b/test/eigen2/CMakeLists.txt @@ -0,0 +1,60 @@ +add_custom_target(eigen2_buildtests) +add_custom_target(eigen2_check COMMAND "ctest -R eigen2") +add_dependencies(eigen2_check eigen2_buildtests) +add_dependencies(buildtests eigen2_buildtests) + +add_definitions("-DEIGEN2_SUPPORT_STAGE10_FULL_EIGEN2_API") + +ei_add_test(eigen2_meta) +ei_add_test(eigen2_sizeof) +ei_add_test(eigen2_dynalloc) +ei_add_test(eigen2_nomalloc) +#ei_add_test(eigen2_first_aligned) +ei_add_test(eigen2_mixingtypes) +#ei_add_test(eigen2_packetmath) +ei_add_test(eigen2_unalignedassert) +#ei_add_test(eigen2_vectorization_logic) +ei_add_test(eigen2_basicstuff) +ei_add_test(eigen2_linearstructure) +ei_add_test(eigen2_cwiseop) +ei_add_test(eigen2_sum) +ei_add_test(eigen2_product_small) +ei_add_test(eigen2_product_large ${EI_OFLAG}) +ei_add_test(eigen2_adjoint) +ei_add_test(eigen2_submatrices) +ei_add_test(eigen2_miscmatrices) +ei_add_test(eigen2_commainitializer) +ei_add_test(eigen2_smallvectors) +ei_add_test(eigen2_map) +ei_add_test(eigen2_array) +ei_add_test(eigen2_triangular) +ei_add_test(eigen2_cholesky " " "${GSL_LIBRARIES}") +ei_add_test(eigen2_lu ${EI_OFLAG}) +ei_add_test(eigen2_determinant ${EI_OFLAG}) +ei_add_test(eigen2_inverse) +ei_add_test(eigen2_qr) +ei_add_test(eigen2_eigensolver " " "${GSL_LIBRARIES}") +ei_add_test(eigen2_svd) +ei_add_test(eigen2_geometry) +ei_add_test(eigen2_geometry_with_eigen2_prefix) +ei_add_test(eigen2_hyperplane) +ei_add_test(eigen2_parametrizedline) +ei_add_test(eigen2_alignedbox) +ei_add_test(eigen2_regression) +ei_add_test(eigen2_stdvector) +ei_add_test(eigen2_newstdvector) +if(QT4_FOUND) + ei_add_test(eigen2_qtvector " " "${QT_QTCORE_LIBRARY}") +endif(QT4_FOUND) +# no support for eigen2 sparse module +# if(NOT EIGEN_DEFAULT_TO_ROW_MAJOR) +# ei_add_test(eigen2_sparse_vector) +# ei_add_test(eigen2_sparse_basic) +# ei_add_test(eigen2_sparse_solvers " " "${SPARSE_LIBS}") +# ei_add_test(eigen2_sparse_product) +# endif() +ei_add_test(eigen2_swap) +ei_add_test(eigen2_visitor) +ei_add_test(eigen2_bug_132) + +ei_add_test(eigen2_prec_inverse_4x4 ${EI_OFLAG}) diff --git a/test/eigen2/eigen2_adjoint.cpp b/test/eigen2/eigen2_adjoint.cpp new file mode 100644 index 000000000..8ec9c9202 --- /dev/null +++ b/test/eigen2/eigen2_adjoint.cpp @@ -0,0 +1,101 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename MatrixType> void adjoint(const MatrixType& m) +{ + /* this test covers the following files: + Transpose.h Conjugate.h Dot.h + */ + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> SquareMatrixType; + int rows = m.rows(); + int cols = m.cols(); + + RealScalar largerEps = test_precision<RealScalar>(); + if (ei_is_same_type<RealScalar,float>::ret) + largerEps = RealScalar(1e-3f); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols), + mzero = MatrixType::Zero(rows, cols), + identity = SquareMatrixType::Identity(rows, rows), + square = SquareMatrixType::Random(rows, rows); + VectorType v1 = VectorType::Random(rows), + v2 = VectorType::Random(rows), + v3 = VectorType::Random(rows), + vzero = VectorType::Zero(rows); + + Scalar s1 = ei_random<Scalar>(), + s2 = ei_random<Scalar>(); + + // check basic compatibility of adjoint, transpose, conjugate + VERIFY_IS_APPROX(m1.transpose().conjugate().adjoint(), m1); + VERIFY_IS_APPROX(m1.adjoint().conjugate().transpose(), m1); + + // check multiplicative behavior + VERIFY_IS_APPROX((m1.adjoint() * m2).adjoint(), m2.adjoint() * m1); + VERIFY_IS_APPROX((s1 * m1).adjoint(), ei_conj(s1) * m1.adjoint()); + + // check basic properties of dot, norm, norm2 + typedef typename NumTraits<Scalar>::Real RealScalar; + VERIFY(ei_isApprox((s1 * v1 + s2 * v2).eigen2_dot(v3), s1 * v1.eigen2_dot(v3) + s2 * v2.eigen2_dot(v3), largerEps)); + VERIFY(ei_isApprox(v3.eigen2_dot(s1 * v1 + s2 * v2), ei_conj(s1)*v3.eigen2_dot(v1)+ei_conj(s2)*v3.eigen2_dot(v2), largerEps)); + VERIFY_IS_APPROX(ei_conj(v1.eigen2_dot(v2)), v2.eigen2_dot(v1)); + VERIFY_IS_APPROX(ei_real(v1.eigen2_dot(v1)), v1.squaredNorm()); + if(NumTraits<Scalar>::HasFloatingPoint) + VERIFY_IS_APPROX(v1.squaredNorm(), v1.norm() * v1.norm()); + VERIFY_IS_MUCH_SMALLER_THAN(ei_abs(vzero.eigen2_dot(v1)), static_cast<RealScalar>(1)); + if(NumTraits<Scalar>::HasFloatingPoint) + VERIFY_IS_MUCH_SMALLER_THAN(vzero.norm(), static_cast<RealScalar>(1)); + + // check compatibility of dot and adjoint + VERIFY(ei_isApprox(v1.eigen2_dot(square * v2), (square.adjoint() * v1).eigen2_dot(v2), largerEps)); + + // like in testBasicStuff, test operator() to check const-qualification + int r = ei_random<int>(0, rows-1), + c = ei_random<int>(0, cols-1); + VERIFY_IS_APPROX(m1.conjugate()(r,c), ei_conj(m1(r,c))); + VERIFY_IS_APPROX(m1.adjoint()(c,r), ei_conj(m1(r,c))); + + if(NumTraits<Scalar>::HasFloatingPoint) + { + // check that Random().normalized() works: tricky as the random xpr must be evaluated by + // normalized() in order to produce a consistent result. + VERIFY_IS_APPROX(VectorType::Random(rows).normalized().norm(), RealScalar(1)); + } + + // check inplace transpose + m3 = m1; + m3.transposeInPlace(); + VERIFY_IS_APPROX(m3,m1.transpose()); + m3.transposeInPlace(); + VERIFY_IS_APPROX(m3,m1); + +} + +void test_eigen2_adjoint() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( adjoint(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( adjoint(Matrix3d()) ); + CALL_SUBTEST_3( adjoint(Matrix4f()) ); + CALL_SUBTEST_4( adjoint(MatrixXcf(4, 4)) ); + CALL_SUBTEST_5( adjoint(MatrixXi(8, 12)) ); + CALL_SUBTEST_6( adjoint(MatrixXf(21, 21)) ); + } + // test a large matrix only once + CALL_SUBTEST_7( adjoint(Matrix<float, 100, 100>()) ); +} + diff --git a/test/eigen2/eigen2_alignedbox.cpp b/test/eigen2/eigen2_alignedbox.cpp new file mode 100644 index 000000000..35043b958 --- /dev/null +++ b/test/eigen2/eigen2_alignedbox.cpp @@ -0,0 +1,60 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// 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" +#include <Eigen/Geometry> +#include <Eigen/LU> +#include <Eigen/QR> + +template<typename BoxType> void alignedbox(const BoxType& _box) +{ + /* this test covers the following files: + AlignedBox.h + */ + + const int dim = _box.dim(); + typedef typename BoxType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, BoxType::AmbientDimAtCompileTime, 1> VectorType; + + VectorType p0 = VectorType::Random(dim); + VectorType p1 = VectorType::Random(dim); + RealScalar s1 = ei_random<RealScalar>(0,1); + + BoxType b0(dim); + BoxType b1(VectorType::Random(dim),VectorType::Random(dim)); + BoxType b2; + + b0.extend(p0); + b0.extend(p1); + VERIFY(b0.contains(p0*s1+(Scalar(1)-s1)*p1)); + VERIFY(!b0.contains(p0 + (1+s1)*(p1-p0))); + + (b2 = b0).extend(b1); + VERIFY(b2.contains(b0)); + VERIFY(b2.contains(b1)); + VERIFY_IS_APPROX(b2.clamp(b0), b0); + + // casting + const int Dim = BoxType::AmbientDimAtCompileTime; + typedef typename GetDifferentType<Scalar>::type OtherScalar; + AlignedBox<OtherScalar,Dim> hp1f = b0.template cast<OtherScalar>(); + VERIFY_IS_APPROX(hp1f.template cast<Scalar>(),b0); + AlignedBox<Scalar,Dim> hp1d = b0.template cast<Scalar>(); + VERIFY_IS_APPROX(hp1d.template cast<Scalar>(),b0); +} + +void test_eigen2_alignedbox() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( alignedbox(AlignedBox<float,2>()) ); + CALL_SUBTEST_2( alignedbox(AlignedBox<float,3>()) ); + CALL_SUBTEST_3( alignedbox(AlignedBox<double,4>()) ); + } +} diff --git a/test/eigen2/eigen2_array.cpp b/test/eigen2/eigen2_array.cpp new file mode 100644 index 000000000..c1ff40ce7 --- /dev/null +++ b/test/eigen2/eigen2_array.cpp @@ -0,0 +1,142 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// 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" +#include <Eigen/Array> + +template<typename MatrixType> void array(const MatrixType& m) +{ + /* this test covers the following files: + Array.cpp + */ + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + int rows = m.rows(); + int cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols); + + Scalar s1 = ei_random<Scalar>(), + s2 = ei_random<Scalar>(); + + // scalar addition + VERIFY_IS_APPROX(m1.cwise() + s1, s1 + m1.cwise()); + VERIFY_IS_APPROX(m1.cwise() + s1, MatrixType::Constant(rows,cols,s1) + m1); + VERIFY_IS_APPROX((m1*Scalar(2)).cwise() - s2, (m1+m1) - MatrixType::Constant(rows,cols,s2) ); + m3 = m1; + m3.cwise() += s2; + VERIFY_IS_APPROX(m3, m1.cwise() + s2); + m3 = m1; + m3.cwise() -= s1; + VERIFY_IS_APPROX(m3, m1.cwise() - s1); + + // reductions + VERIFY_IS_APPROX(m1.colwise().sum().sum(), m1.sum()); + VERIFY_IS_APPROX(m1.rowwise().sum().sum(), m1.sum()); + if (!ei_isApprox(m1.sum(), (m1+m2).sum())) + VERIFY_IS_NOT_APPROX(((m1+m2).rowwise().sum()).sum(), m1.sum()); + VERIFY_IS_APPROX(m1.colwise().sum(), m1.colwise().redux(internal::scalar_sum_op<Scalar>())); +} + +template<typename MatrixType> void comparisons(const MatrixType& m) +{ + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + int rows = m.rows(); + int cols = m.cols(); + + int r = ei_random<int>(0, rows-1), + c = ei_random<int>(0, cols-1); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols); + + VERIFY(((m1.cwise() + Scalar(1)).cwise() > m1).all()); + VERIFY(((m1.cwise() - Scalar(1)).cwise() < m1).all()); + if (rows*cols>1) + { + m3 = m1; + m3(r,c) += 1; + VERIFY(! (m1.cwise() < m3).all() ); + VERIFY(! (m1.cwise() > m3).all() ); + } + + // comparisons to scalar + VERIFY( (m1.cwise() != (m1(r,c)+1) ).any() ); + VERIFY( (m1.cwise() > (m1(r,c)-1) ).any() ); + VERIFY( (m1.cwise() < (m1(r,c)+1) ).any() ); + VERIFY( (m1.cwise() == m1(r,c) ).any() ); + + // test Select + VERIFY_IS_APPROX( (m1.cwise()<m2).select(m1,m2), m1.cwise().min(m2) ); + VERIFY_IS_APPROX( (m1.cwise()>m2).select(m1,m2), m1.cwise().max(m2) ); + Scalar mid = (m1.cwise().abs().minCoeff() + m1.cwise().abs().maxCoeff())/Scalar(2); + for (int j=0; j<cols; ++j) + for (int i=0; i<rows; ++i) + m3(i,j) = ei_abs(m1(i,j))<mid ? 0 : m1(i,j); + VERIFY_IS_APPROX( (m1.cwise().abs().cwise()<MatrixType::Constant(rows,cols,mid)) + .select(MatrixType::Zero(rows,cols),m1), m3); + // shorter versions: + VERIFY_IS_APPROX( (m1.cwise().abs().cwise()<MatrixType::Constant(rows,cols,mid)) + .select(0,m1), m3); + VERIFY_IS_APPROX( (m1.cwise().abs().cwise()>=MatrixType::Constant(rows,cols,mid)) + .select(m1,0), m3); + // even shorter version: + VERIFY_IS_APPROX( (m1.cwise().abs().cwise()<mid).select(0,m1), m3); + + // count + VERIFY(((m1.cwise().abs().cwise()+1).cwise()>RealScalar(0.1)).count() == rows*cols); + VERIFY_IS_APPROX(((m1.cwise().abs().cwise()+1).cwise()>RealScalar(0.1)).colwise().count().template cast<int>(), RowVectorXi::Constant(cols,rows)); + VERIFY_IS_APPROX(((m1.cwise().abs().cwise()+1).cwise()>RealScalar(0.1)).rowwise().count().template cast<int>(), VectorXi::Constant(rows, cols)); +} + +template<typename VectorType> void lpNorm(const VectorType& v) +{ + VectorType u = VectorType::Random(v.size()); + + VERIFY_IS_APPROX(u.template lpNorm<Infinity>(), u.cwise().abs().maxCoeff()); + VERIFY_IS_APPROX(u.template lpNorm<1>(), u.cwise().abs().sum()); + VERIFY_IS_APPROX(u.template lpNorm<2>(), ei_sqrt(u.cwise().abs().cwise().square().sum())); + VERIFY_IS_APPROX(ei_pow(u.template lpNorm<5>(), typename VectorType::RealScalar(5)), u.cwise().abs().cwise().pow(5).sum()); +} + +void test_eigen2_array() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( array(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( array(Matrix2f()) ); + CALL_SUBTEST_3( array(Matrix4d()) ); + CALL_SUBTEST_4( array(MatrixXcf(3, 3)) ); + CALL_SUBTEST_5( array(MatrixXf(8, 12)) ); + CALL_SUBTEST_6( array(MatrixXi(8, 12)) ); + } + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( comparisons(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( comparisons(Matrix2f()) ); + CALL_SUBTEST_3( comparisons(Matrix4d()) ); + CALL_SUBTEST_5( comparisons(MatrixXf(8, 12)) ); + CALL_SUBTEST_6( comparisons(MatrixXi(8, 12)) ); + } + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( lpNorm(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( lpNorm(Vector2f()) ); + CALL_SUBTEST_3( lpNorm(Vector3d()) ); + CALL_SUBTEST_4( lpNorm(Vector4f()) ); + CALL_SUBTEST_5( lpNorm(VectorXf(16)) ); + CALL_SUBTEST_7( lpNorm(VectorXcd(10)) ); + } +} diff --git a/test/eigen2/eigen2_basicstuff.cpp b/test/eigen2/eigen2_basicstuff.cpp new file mode 100644 index 000000000..4fa16d5ae --- /dev/null +++ b/test/eigen2/eigen2_basicstuff.cpp @@ -0,0 +1,108 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename MatrixType> void basicStuff(const MatrixType& m) +{ + typedef typename MatrixType::Scalar Scalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + int rows = m.rows(); + int cols = m.cols(); + + // this test relies a lot on Random.h, and there's not much more that we can do + // to test it, hence I consider that we will have tested Random.h + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols), + mzero = MatrixType::Zero(rows, cols), + identity = Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> + ::Identity(rows, rows), + square = Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime>::Random(rows, rows); + VectorType v1 = VectorType::Random(rows), + v2 = VectorType::Random(rows), + vzero = VectorType::Zero(rows); + + Scalar x = ei_random<Scalar>(); + + int r = ei_random<int>(0, rows-1), + c = ei_random<int>(0, cols-1); + + m1.coeffRef(r,c) = x; + VERIFY_IS_APPROX(x, m1.coeff(r,c)); + m1(r,c) = x; + VERIFY_IS_APPROX(x, m1(r,c)); + v1.coeffRef(r) = x; + VERIFY_IS_APPROX(x, v1.coeff(r)); + v1(r) = x; + VERIFY_IS_APPROX(x, v1(r)); + v1[r] = x; + VERIFY_IS_APPROX(x, v1[r]); + + VERIFY_IS_APPROX( v1, v1); + VERIFY_IS_NOT_APPROX( v1, 2*v1); + VERIFY_IS_MUCH_SMALLER_THAN( vzero, v1); + if(NumTraits<Scalar>::HasFloatingPoint) + VERIFY_IS_MUCH_SMALLER_THAN( vzero, v1.norm()); + VERIFY_IS_NOT_MUCH_SMALLER_THAN(v1, v1); + VERIFY_IS_APPROX( vzero, v1-v1); + VERIFY_IS_APPROX( m1, m1); + VERIFY_IS_NOT_APPROX( m1, 2*m1); + VERIFY_IS_MUCH_SMALLER_THAN( mzero, m1); + VERIFY_IS_NOT_MUCH_SMALLER_THAN(m1, m1); + VERIFY_IS_APPROX( mzero, m1-m1); + + // always test operator() on each read-only expression class, + // in order to check const-qualifiers. + // indeed, if an expression class (here Zero) is meant to be read-only, + // hence has no _write() method, the corresponding MatrixBase method (here zero()) + // should return a const-qualified object so that it is the const-qualified + // operator() that gets called, which in turn calls _read(). + VERIFY_IS_MUCH_SMALLER_THAN(MatrixType::Zero(rows,cols)(r,c), static_cast<Scalar>(1)); + + // now test copying a row-vector into a (column-)vector and conversely. + square.col(r) = square.row(r).eval(); + Matrix<Scalar, 1, MatrixType::RowsAtCompileTime> rv(rows); + Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> cv(rows); + rv = square.row(r); + cv = square.col(r); + VERIFY_IS_APPROX(rv, cv.transpose()); + + if(cols!=1 && rows!=1 && MatrixType::SizeAtCompileTime!=Dynamic) + { + VERIFY_RAISES_ASSERT(m1 = (m2.block(0,0, rows-1, cols-1))); + } + + VERIFY_IS_APPROX(m3 = m1,m1); + MatrixType m4; + VERIFY_IS_APPROX(m4 = m1,m1); + + // test swap + m3 = m1; + m1.swap(m2); + VERIFY_IS_APPROX(m3, m2); + if(rows*cols>=3) + { + VERIFY_IS_NOT_APPROX(m3, m1); + } +} + +void test_eigen2_basicstuff() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( basicStuff(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( basicStuff(Matrix4d()) ); + CALL_SUBTEST_3( basicStuff(MatrixXcf(3, 3)) ); + CALL_SUBTEST_4( basicStuff(MatrixXi(8, 12)) ); + CALL_SUBTEST_5( basicStuff(MatrixXcd(20, 20)) ); + CALL_SUBTEST_6( basicStuff(Matrix<float, 100, 100>()) ); + CALL_SUBTEST_7( basicStuff(Matrix<long double,Dynamic,Dynamic>(10,10)) ); + } +} diff --git a/test/eigen2/eigen2_bug_132.cpp b/test/eigen2/eigen2_bug_132.cpp new file mode 100644 index 000000000..7fe3610ce --- /dev/null +++ b/test/eigen2/eigen2_bug_132.cpp @@ -0,0 +1,26 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2010 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" + +void test_eigen2_bug_132() { + int size = 100; + MatrixXd A(size, size); + VectorXd b(size), c(size); + { + VectorXd y = A.transpose() * (b-c); // bug 132: infinite recursion in coeffRef + VectorXd z = (b-c).transpose() * A; // bug 132: infinite recursion in coeffRef + } + + // the following ones weren't failing, but let's include them for completeness: + { + VectorXd y = A * (b-c); + VectorXd z = (b-c).transpose() * A.transpose(); + } +} diff --git a/test/eigen2/eigen2_cholesky.cpp b/test/eigen2/eigen2_cholesky.cpp new file mode 100644 index 000000000..9c4b6f561 --- /dev/null +++ b/test/eigen2/eigen2_cholesky.cpp @@ -0,0 +1,113 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// 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/. + +#define EIGEN_NO_ASSERTION_CHECKING +#include "main.h" +#include <Eigen/Cholesky> +#include <Eigen/LU> + +#ifdef HAS_GSL +#include "gsl_helper.h" +#endif + +template<typename MatrixType> void cholesky(const MatrixType& m) +{ + /* this test covers the following files: + LLT.h LDLT.h + */ + int rows = m.rows(); + int cols = m.cols(); + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> SquareMatrixType; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + MatrixType a0 = MatrixType::Random(rows,cols); + VectorType vecB = VectorType::Random(rows), vecX(rows); + MatrixType matB = MatrixType::Random(rows,cols), matX(rows,cols); + SquareMatrixType symm = a0 * a0.adjoint(); + // let's make sure the matrix is not singular or near singular + MatrixType a1 = MatrixType::Random(rows,cols); + symm += a1 * a1.adjoint(); + + #ifdef HAS_GSL + if (ei_is_same_type<RealScalar,double>::ret) + { + typedef GslTraits<Scalar> Gsl; + typename Gsl::Matrix gMatA=0, gSymm=0; + typename Gsl::Vector gVecB=0, gVecX=0; + convert<MatrixType>(symm, gSymm); + convert<MatrixType>(symm, gMatA); + convert<VectorType>(vecB, gVecB); + convert<VectorType>(vecB, gVecX); + Gsl::cholesky(gMatA); + Gsl::cholesky_solve(gMatA, gVecB, gVecX); + VectorType vecX(rows), _vecX, _vecB; + convert(gVecX, _vecX); + symm.llt().solve(vecB, &vecX); + Gsl::prod(gSymm, gVecX, gVecB); + convert(gVecB, _vecB); + // test gsl itself ! + VERIFY_IS_APPROX(vecB, _vecB); + VERIFY_IS_APPROX(vecX, _vecX); + + Gsl::free(gMatA); + Gsl::free(gSymm); + Gsl::free(gVecB); + Gsl::free(gVecX); + } + #endif + + { + LDLT<SquareMatrixType> ldlt(symm); + VERIFY(ldlt.isPositiveDefinite()); + // in eigen3, LDLT is pivoting + //VERIFY_IS_APPROX(symm, ldlt.matrixL() * ldlt.vectorD().asDiagonal() * ldlt.matrixL().adjoint()); + ldlt.solve(vecB, &vecX); + VERIFY_IS_APPROX(symm * vecX, vecB); + ldlt.solve(matB, &matX); + VERIFY_IS_APPROX(symm * matX, matB); + } + + { + LLT<SquareMatrixType> chol(symm); + VERIFY(chol.isPositiveDefinite()); + VERIFY_IS_APPROX(symm, chol.matrixL() * chol.matrixL().adjoint()); + chol.solve(vecB, &vecX); + VERIFY_IS_APPROX(symm * vecX, vecB); + chol.solve(matB, &matX); + VERIFY_IS_APPROX(symm * matX, matB); + } + +#if 0 // cholesky is not rank-revealing anyway + // test isPositiveDefinite on non definite matrix + if (rows>4) + { + SquareMatrixType symm = a0.block(0,0,rows,cols-4) * a0.block(0,0,rows,cols-4).adjoint(); + LLT<SquareMatrixType> chol(symm); + VERIFY(!chol.isPositiveDefinite()); + LDLT<SquareMatrixType> cholnosqrt(symm); + VERIFY(!cholnosqrt.isPositiveDefinite()); + } +#endif +} + +void test_eigen2_cholesky() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( cholesky(Matrix<double,1,1>()) ); + CALL_SUBTEST_2( cholesky(Matrix2d()) ); + CALL_SUBTEST_3( cholesky(Matrix3f()) ); + CALL_SUBTEST_4( cholesky(Matrix4d()) ); + CALL_SUBTEST_5( cholesky(MatrixXcd(7,7)) ); + CALL_SUBTEST_6( cholesky(MatrixXf(17,17)) ); + CALL_SUBTEST_7( cholesky(MatrixXd(33,33)) ); + } +} diff --git a/test/eigen2/eigen2_commainitializer.cpp b/test/eigen2/eigen2_commainitializer.cpp new file mode 100644 index 000000000..e0f901e0b --- /dev/null +++ b/test/eigen2/eigen2_commainitializer.cpp @@ -0,0 +1,46 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// 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" + +void test_eigen2_commainitializer() +{ + Matrix3d m3; + Matrix4d m4; + + VERIFY_RAISES_ASSERT( (m3 << 1, 2, 3, 4, 5, 6, 7, 8) ); + + #ifndef _MSC_VER + VERIFY_RAISES_ASSERT( (m3 << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) ); + #endif + + double data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + Matrix3d ref = Map<Matrix<double,3,3,RowMajor> >(data); + + m3 = Matrix3d::Random(); + m3 << 1, 2, 3, 4, 5, 6, 7, 8, 9; + VERIFY_IS_APPROX(m3, ref ); + + Vector3d vec[3]; + vec[0] << 1, 4, 7; + vec[1] << 2, 5, 8; + vec[2] << 3, 6, 9; + m3 = Matrix3d::Random(); + m3 << vec[0], vec[1], vec[2]; + VERIFY_IS_APPROX(m3, ref); + + vec[0] << 1, 2, 3; + vec[1] << 4, 5, 6; + vec[2] << 7, 8, 9; + m3 = Matrix3d::Random(); + m3 << vec[0].transpose(), + 4, 5, 6, + vec[2].transpose(); + VERIFY_IS_APPROX(m3, ref); +} diff --git a/test/eigen2/eigen2_cwiseop.cpp b/test/eigen2/eigen2_cwiseop.cpp new file mode 100644 index 000000000..4391d19b4 --- /dev/null +++ b/test/eigen2/eigen2_cwiseop.cpp @@ -0,0 +1,158 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <functional> +#include <Eigen/Array> + +using namespace std; + +template<typename Scalar> struct AddIfNull { + const Scalar operator() (const Scalar a, const Scalar b) const {return a<=1e-3 ? b : a;} + enum { Cost = NumTraits<Scalar>::AddCost }; +}; + +template<typename MatrixType> void cwiseops(const MatrixType& m) +{ + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + int rows = m.rows(); + int cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols), + m4(rows, cols), + mzero = MatrixType::Zero(rows, cols), + mones = MatrixType::Ones(rows, cols), + identity = Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> + ::Identity(rows, rows), + square = Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime>::Random(rows, rows); + VectorType v1 = VectorType::Random(rows), + v2 = VectorType::Random(rows), + vzero = VectorType::Zero(rows), + vones = VectorType::Ones(rows), + v3(rows); + + int r = ei_random<int>(0, rows-1), + c = ei_random<int>(0, cols-1); + + Scalar s1 = ei_random<Scalar>(); + + // test Zero, Ones, Constant, and the set* variants + m3 = MatrixType::Constant(rows, cols, s1); + for (int j=0; j<cols; ++j) + for (int i=0; i<rows; ++i) + { + VERIFY_IS_APPROX(mzero(i,j), Scalar(0)); + VERIFY_IS_APPROX(mones(i,j), Scalar(1)); + VERIFY_IS_APPROX(m3(i,j), s1); + } + VERIFY(mzero.isZero()); + VERIFY(mones.isOnes()); + VERIFY(m3.isConstant(s1)); + VERIFY(identity.isIdentity()); + VERIFY_IS_APPROX(m4.setConstant(s1), m3); + VERIFY_IS_APPROX(m4.setConstant(rows,cols,s1), m3); + VERIFY_IS_APPROX(m4.setZero(), mzero); + VERIFY_IS_APPROX(m4.setZero(rows,cols), mzero); + VERIFY_IS_APPROX(m4.setOnes(), mones); + VERIFY_IS_APPROX(m4.setOnes(rows,cols), mones); + m4.fill(s1); + VERIFY_IS_APPROX(m4, m3); + + VERIFY_IS_APPROX(v3.setConstant(rows, s1), VectorType::Constant(rows,s1)); + VERIFY_IS_APPROX(v3.setZero(rows), vzero); + VERIFY_IS_APPROX(v3.setOnes(rows), vones); + + m2 = m2.template binaryExpr<AddIfNull<Scalar> >(mones); + + VERIFY_IS_APPROX(m1.cwise().pow(2), m1.cwise().abs2()); + VERIFY_IS_APPROX(m1.cwise().pow(2), m1.cwise().square()); + VERIFY_IS_APPROX(m1.cwise().pow(3), m1.cwise().cube()); + + VERIFY_IS_APPROX(m1 + mones, m1.cwise()+Scalar(1)); + VERIFY_IS_APPROX(m1 - mones, m1.cwise()-Scalar(1)); + m3 = m1; m3.cwise() += 1; + VERIFY_IS_APPROX(m1 + mones, m3); + m3 = m1; m3.cwise() -= 1; + VERIFY_IS_APPROX(m1 - mones, m3); + + VERIFY_IS_APPROX(m2, m2.cwise() * mones); + VERIFY_IS_APPROX(m1.cwise() * m2, m2.cwise() * m1); + m3 = m1; + m3.cwise() *= m2; + VERIFY_IS_APPROX(m3, m1.cwise() * m2); + + VERIFY_IS_APPROX(mones, m2.cwise()/m2); + if(NumTraits<Scalar>::HasFloatingPoint) + { + VERIFY_IS_APPROX(m1.cwise() / m2, m1.cwise() * (m2.cwise().inverse())); + m3 = m1.cwise().abs().cwise().sqrt(); + VERIFY_IS_APPROX(m3.cwise().square(), m1.cwise().abs()); + VERIFY_IS_APPROX(m1.cwise().square().cwise().sqrt(), m1.cwise().abs()); + VERIFY_IS_APPROX(m1.cwise().abs().cwise().log().cwise().exp() , m1.cwise().abs()); + + VERIFY_IS_APPROX(m1.cwise().pow(2), m1.cwise().square()); + m3 = (m1.cwise().abs().cwise()<=RealScalar(0.01)).select(mones,m1); + VERIFY_IS_APPROX(m3.cwise().pow(-1), m3.cwise().inverse()); + m3 = m1.cwise().abs(); + VERIFY_IS_APPROX(m3.cwise().pow(RealScalar(0.5)), m3.cwise().sqrt()); + +// VERIFY_IS_APPROX(m1.cwise().tan(), m1.cwise().sin().cwise() / m1.cwise().cos()); + VERIFY_IS_APPROX(mones, m1.cwise().sin().cwise().square() + m1.cwise().cos().cwise().square()); + m3 = m1; + m3.cwise() /= m2; + VERIFY_IS_APPROX(m3, m1.cwise() / m2); + } + + // check min + VERIFY_IS_APPROX( m1.cwise().min(m2), m2.cwise().min(m1) ); + VERIFY_IS_APPROX( m1.cwise().min(m1+mones), m1 ); + VERIFY_IS_APPROX( m1.cwise().min(m1-mones), m1-mones ); + + // check max + VERIFY_IS_APPROX( m1.cwise().max(m2), m2.cwise().max(m1) ); + VERIFY_IS_APPROX( m1.cwise().max(m1-mones), m1 ); + VERIFY_IS_APPROX( m1.cwise().max(m1+mones), m1+mones ); + + VERIFY( (m1.cwise() == m1).all() ); + VERIFY( (m1.cwise() != m2).any() ); + VERIFY(!(m1.cwise() == (m1+mones)).any() ); + if (rows*cols>1) + { + m3 = m1; + m3(r,c) += 1; + VERIFY( (m1.cwise() == m3).any() ); + VERIFY( !(m1.cwise() == m3).all() ); + } + VERIFY( (m1.cwise().min(m2).cwise() <= m2).all() ); + VERIFY( (m1.cwise().max(m2).cwise() >= m2).all() ); + VERIFY( (m1.cwise().min(m2).cwise() < (m1+mones)).all() ); + VERIFY( (m1.cwise().max(m2).cwise() > (m1-mones)).all() ); + + VERIFY( (m1.cwise()<m1.unaryExpr(bind2nd(plus<Scalar>(), Scalar(1)))).all() ); + VERIFY( !(m1.cwise()<m1.unaryExpr(bind2nd(minus<Scalar>(), Scalar(1)))).all() ); + VERIFY( !(m1.cwise()>m1.unaryExpr(bind2nd(plus<Scalar>(), Scalar(1)))).any() ); +} + +void test_eigen2_cwiseop() +{ + for(int i = 0; i < g_repeat ; i++) { + CALL_SUBTEST_1( cwiseops(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( cwiseops(Matrix4d()) ); + CALL_SUBTEST_3( cwiseops(MatrixXf(3, 3)) ); + CALL_SUBTEST_3( cwiseops(MatrixXf(22, 22)) ); + CALL_SUBTEST_4( cwiseops(MatrixXi(8, 12)) ); + CALL_SUBTEST_5( cwiseops(MatrixXd(20, 20)) ); + } +} diff --git a/test/eigen2/eigen2_determinant.cpp b/test/eigen2/eigen2_determinant.cpp new file mode 100644 index 000000000..c7b4ad053 --- /dev/null +++ b/test/eigen2/eigen2_determinant.cpp @@ -0,0 +1,61 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// 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" +#include <Eigen/LU> + +template<typename MatrixType> void determinant(const MatrixType& m) +{ + /* this test covers the following files: + Determinant.h + */ + int size = m.rows(); + + MatrixType m1(size, size), m2(size, size); + m1.setRandom(); + m2.setRandom(); + typedef typename MatrixType::Scalar Scalar; + Scalar x = ei_random<Scalar>(); + VERIFY_IS_APPROX(MatrixType::Identity(size, size).determinant(), Scalar(1)); + VERIFY_IS_APPROX((m1*m2).determinant(), m1.determinant() * m2.determinant()); + if(size==1) return; + int i = ei_random<int>(0, size-1); + int j; + do { + j = ei_random<int>(0, size-1); + } while(j==i); + m2 = m1; + m2.row(i).swap(m2.row(j)); + VERIFY_IS_APPROX(m2.determinant(), -m1.determinant()); + m2 = m1; + m2.col(i).swap(m2.col(j)); + VERIFY_IS_APPROX(m2.determinant(), -m1.determinant()); + VERIFY_IS_APPROX(m2.determinant(), m2.transpose().determinant()); + VERIFY_IS_APPROX(ei_conj(m2.determinant()), m2.adjoint().determinant()); + m2 = m1; + m2.row(i) += x*m2.row(j); + VERIFY_IS_APPROX(m2.determinant(), m1.determinant()); + m2 = m1; + m2.row(i) *= x; + VERIFY_IS_APPROX(m2.determinant(), m1.determinant() * x); +} + +void test_eigen2_determinant() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( determinant(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( determinant(Matrix<double, 2, 2>()) ); + CALL_SUBTEST_3( determinant(Matrix<double, 3, 3>()) ); + CALL_SUBTEST_4( determinant(Matrix<double, 4, 4>()) ); + CALL_SUBTEST_5( determinant(Matrix<std::complex<double>, 10, 10>()) ); + CALL_SUBTEST_6( determinant(MatrixXd(20, 20)) ); + } + CALL_SUBTEST_6( determinant(MatrixXd(200, 200)) ); +} diff --git a/test/eigen2/eigen2_dynalloc.cpp b/test/eigen2/eigen2_dynalloc.cpp new file mode 100644 index 000000000..1891a9e33 --- /dev/null +++ b/test/eigen2/eigen2_dynalloc.cpp @@ -0,0 +1,131 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// 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" + +#if EIGEN_ARCH_WANTS_ALIGNMENT +#define ALIGNMENT 16 +#else +#define ALIGNMENT 1 +#endif + +void check_handmade_aligned_malloc() +{ + for(int i = 1; i < 1000; i++) + { + char *p = (char*)ei_handmade_aligned_malloc(i); + VERIFY(std::size_t(p)%ALIGNMENT==0); + // if the buffer is wrongly allocated this will give a bad write --> check with valgrind + for(int j = 0; j < i; j++) p[j]=0; + ei_handmade_aligned_free(p); + } +} + +void check_aligned_malloc() +{ + for(int i = 1; i < 1000; i++) + { + char *p = (char*)ei_aligned_malloc(i); + VERIFY(std::size_t(p)%ALIGNMENT==0); + // if the buffer is wrongly allocated this will give a bad write --> check with valgrind + for(int j = 0; j < i; j++) p[j]=0; + ei_aligned_free(p); + } +} + +void check_aligned_new() +{ + for(int i = 1; i < 1000; i++) + { + float *p = ei_aligned_new<float>(i); + VERIFY(std::size_t(p)%ALIGNMENT==0); + // if the buffer is wrongly allocated this will give a bad write --> check with valgrind + for(int j = 0; j < i; j++) p[j]=0; + ei_aligned_delete(p,i); + } +} + +void check_aligned_stack_alloc() +{ + for(int i = 1; i < 1000; i++) + { + ei_declare_aligned_stack_constructed_variable(float, p, i, 0); + VERIFY(std::size_t(p)%ALIGNMENT==0); + // if the buffer is wrongly allocated this will give a bad write --> check with valgrind + for(int j = 0; j < i; j++) p[j]=0; + } +} + + +// test compilation with both a struct and a class... +struct MyStruct +{ + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + char dummychar; + Vector4f avec; +}; + +class MyClassA +{ + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + char dummychar; + Vector4f avec; +}; + +template<typename T> void check_dynaligned() +{ + T* obj = new T; + VERIFY(std::size_t(obj)%ALIGNMENT==0); + delete obj; +} + +void test_eigen2_dynalloc() +{ + // low level dynamic memory allocation + CALL_SUBTEST(check_handmade_aligned_malloc()); + CALL_SUBTEST(check_aligned_malloc()); + CALL_SUBTEST(check_aligned_new()); + CALL_SUBTEST(check_aligned_stack_alloc()); + + for (int i=0; i<g_repeat*100; ++i) + { + CALL_SUBTEST( check_dynaligned<Vector4f>() ); + CALL_SUBTEST( check_dynaligned<Vector2d>() ); + CALL_SUBTEST( check_dynaligned<Matrix4f>() ); + CALL_SUBTEST( check_dynaligned<Vector4d>() ); + CALL_SUBTEST( check_dynaligned<Vector4i>() ); + } + + // check static allocation, who knows ? + { + MyStruct foo0; VERIFY(std::size_t(foo0.avec.data())%ALIGNMENT==0); + MyClassA fooA; VERIFY(std::size_t(fooA.avec.data())%ALIGNMENT==0); + } + + // dynamic allocation, single object + for (int i=0; i<g_repeat*100; ++i) + { + MyStruct *foo0 = new MyStruct(); VERIFY(std::size_t(foo0->avec.data())%ALIGNMENT==0); + MyClassA *fooA = new MyClassA(); VERIFY(std::size_t(fooA->avec.data())%ALIGNMENT==0); + delete foo0; + delete fooA; + } + + // dynamic allocation, array + const int N = 10; + for (int i=0; i<g_repeat*100; ++i) + { + MyStruct *foo0 = new MyStruct[N]; VERIFY(std::size_t(foo0->avec.data())%ALIGNMENT==0); + MyClassA *fooA = new MyClassA[N]; VERIFY(std::size_t(fooA->avec.data())%ALIGNMENT==0); + delete[] foo0; + delete[] fooA; + } + +} diff --git a/test/eigen2/eigen2_eigensolver.cpp b/test/eigen2/eigen2_eigensolver.cpp new file mode 100644 index 000000000..48b4ace43 --- /dev/null +++ b/test/eigen2/eigen2_eigensolver.cpp @@ -0,0 +1,146 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// 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" +#include <Eigen/QR> + +#ifdef HAS_GSL +#include "gsl_helper.h" +#endif + +template<typename MatrixType> void selfadjointeigensolver(const MatrixType& m) +{ + /* this test covers the following files: + EigenSolver.h, SelfAdjointEigenSolver.h (and indirectly: Tridiagonalization.h) + */ + int rows = m.rows(); + int cols = m.cols(); + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<RealScalar, MatrixType::RowsAtCompileTime, 1> RealVectorType; + typedef typename std::complex<typename NumTraits<typename MatrixType::Scalar>::Real> Complex; + + RealScalar largerEps = 10*test_precision<RealScalar>(); + + MatrixType a = MatrixType::Random(rows,cols); + MatrixType a1 = MatrixType::Random(rows,cols); + MatrixType symmA = a.adjoint() * a + a1.adjoint() * a1; + + MatrixType b = MatrixType::Random(rows,cols); + MatrixType b1 = MatrixType::Random(rows,cols); + MatrixType symmB = b.adjoint() * b + b1.adjoint() * b1; + + SelfAdjointEigenSolver<MatrixType> eiSymm(symmA); + // generalized eigen pb + SelfAdjointEigenSolver<MatrixType> eiSymmGen(symmA, symmB); + + #ifdef HAS_GSL + if (ei_is_same_type<RealScalar,double>::ret) + { + typedef GslTraits<Scalar> Gsl; + typename Gsl::Matrix gEvec=0, gSymmA=0, gSymmB=0; + typename GslTraits<RealScalar>::Vector gEval=0; + RealVectorType _eval; + MatrixType _evec; + convert<MatrixType>(symmA, gSymmA); + convert<MatrixType>(symmB, gSymmB); + convert<MatrixType>(symmA, gEvec); + gEval = GslTraits<RealScalar>::createVector(rows); + + Gsl::eigen_symm(gSymmA, gEval, gEvec); + convert(gEval, _eval); + convert(gEvec, _evec); + + // test gsl itself ! + VERIFY((symmA * _evec).isApprox(_evec * _eval.asDiagonal(), largerEps)); + + // compare with eigen + VERIFY_IS_APPROX(_eval, eiSymm.eigenvalues()); + VERIFY_IS_APPROX(_evec.cwise().abs(), eiSymm.eigenvectors().cwise().abs()); + + // generalized pb + Gsl::eigen_symm_gen(gSymmA, gSymmB, gEval, gEvec); + convert(gEval, _eval); + convert(gEvec, _evec); + // test GSL itself: + VERIFY((symmA * _evec).isApprox(symmB * (_evec * _eval.asDiagonal()), largerEps)); + + // compare with eigen + MatrixType normalized_eivec = eiSymmGen.eigenvectors()*eiSymmGen.eigenvectors().colwise().norm().asDiagonal().inverse(); + VERIFY_IS_APPROX(_eval, eiSymmGen.eigenvalues()); + VERIFY_IS_APPROX(_evec.cwiseAbs(), normalized_eivec.cwiseAbs()); + + Gsl::free(gSymmA); + Gsl::free(gSymmB); + GslTraits<RealScalar>::free(gEval); + Gsl::free(gEvec); + } + #endif + + VERIFY((symmA * eiSymm.eigenvectors()).isApprox( + eiSymm.eigenvectors() * eiSymm.eigenvalues().asDiagonal(), largerEps)); + + // generalized eigen problem Ax = lBx + VERIFY((symmA * eiSymmGen.eigenvectors()).isApprox( + symmB * (eiSymmGen.eigenvectors() * eiSymmGen.eigenvalues().asDiagonal()), largerEps)); + + MatrixType sqrtSymmA = eiSymm.operatorSqrt(); + VERIFY_IS_APPROX(symmA, sqrtSymmA*sqrtSymmA); + VERIFY_IS_APPROX(sqrtSymmA, symmA*eiSymm.operatorInverseSqrt()); +} + +template<typename MatrixType> void eigensolver(const MatrixType& m) +{ + /* this test covers the following files: + EigenSolver.h + */ + int rows = m.rows(); + int cols = m.cols(); + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<RealScalar, MatrixType::RowsAtCompileTime, 1> RealVectorType; + typedef typename std::complex<typename NumTraits<typename MatrixType::Scalar>::Real> Complex; + + // RealScalar largerEps = 10*test_precision<RealScalar>(); + + MatrixType a = MatrixType::Random(rows,cols); + MatrixType a1 = MatrixType::Random(rows,cols); + MatrixType symmA = a.adjoint() * a + a1.adjoint() * a1; + + EigenSolver<MatrixType> ei0(symmA); + VERIFY_IS_APPROX(symmA * ei0.pseudoEigenvectors(), ei0.pseudoEigenvectors() * ei0.pseudoEigenvalueMatrix()); + VERIFY_IS_APPROX((symmA.template cast<Complex>()) * (ei0.pseudoEigenvectors().template cast<Complex>()), + (ei0.pseudoEigenvectors().template cast<Complex>()) * (ei0.eigenvalues().asDiagonal())); + + EigenSolver<MatrixType> ei1(a); + VERIFY_IS_APPROX(a * ei1.pseudoEigenvectors(), ei1.pseudoEigenvectors() * ei1.pseudoEigenvalueMatrix()); + VERIFY_IS_APPROX(a.template cast<Complex>() * ei1.eigenvectors(), + ei1.eigenvectors() * ei1.eigenvalues().asDiagonal()); + +} + +void test_eigen2_eigensolver() +{ + for(int i = 0; i < g_repeat; i++) { + // very important to test a 3x3 matrix since we provide a special path for it + CALL_SUBTEST_1( selfadjointeigensolver(Matrix3f()) ); + CALL_SUBTEST_2( selfadjointeigensolver(Matrix4d()) ); + CALL_SUBTEST_3( selfadjointeigensolver(MatrixXf(7,7)) ); + CALL_SUBTEST_4( selfadjointeigensolver(MatrixXcd(5,5)) ); + CALL_SUBTEST_5( selfadjointeigensolver(MatrixXd(19,19)) ); + + CALL_SUBTEST_6( eigensolver(Matrix4f()) ); + CALL_SUBTEST_5( eigensolver(MatrixXd(17,17)) ); + } +} + diff --git a/test/eigen2/eigen2_first_aligned.cpp b/test/eigen2/eigen2_first_aligned.cpp new file mode 100644 index 000000000..51bb3cad1 --- /dev/null +++ b/test/eigen2/eigen2_first_aligned.cpp @@ -0,0 +1,49 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename Scalar> +void test_eigen2_first_aligned_helper(Scalar *array, int size) +{ + const int packet_size = sizeof(Scalar) * ei_packet_traits<Scalar>::size; + VERIFY(((std::size_t(array) + sizeof(Scalar) * ei_alignmentOffset(array, size)) % packet_size) == 0); +} + +template<typename Scalar> +void test_eigen2_none_aligned_helper(Scalar *array, int size) +{ + VERIFY(ei_packet_traits<Scalar>::size == 1 || ei_alignmentOffset(array, size) == size); +} + +struct some_non_vectorizable_type { float x; }; + +void test_eigen2_first_aligned() +{ + EIGEN_ALIGN_128 float array_float[100]; + test_first_aligned_helper(array_float, 50); + test_first_aligned_helper(array_float+1, 50); + test_first_aligned_helper(array_float+2, 50); + test_first_aligned_helper(array_float+3, 50); + test_first_aligned_helper(array_float+4, 50); + test_first_aligned_helper(array_float+5, 50); + + EIGEN_ALIGN_128 double array_double[100]; + test_first_aligned_helper(array_double, 50); + test_first_aligned_helper(array_double+1, 50); + test_first_aligned_helper(array_double+2, 50); + + double *array_double_plus_4_bytes = (double*)(std::size_t(array_double)+4); + test_none_aligned_helper(array_double_plus_4_bytes, 50); + test_none_aligned_helper(array_double_plus_4_bytes+1, 50); + + some_non_vectorizable_type array_nonvec[100]; + test_first_aligned_helper(array_nonvec, 100); + test_none_aligned_helper(array_nonvec, 100); +} diff --git a/test/eigen2/eigen2_geometry.cpp b/test/eigen2/eigen2_geometry.cpp new file mode 100644 index 000000000..70b4ab5f1 --- /dev/null +++ b/test/eigen2/eigen2_geometry.cpp @@ -0,0 +1,431 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// 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" +#include <Eigen/Geometry> +#include <Eigen/LU> +#include <Eigen/SVD> + +template<typename Scalar> void geometry(void) +{ + /* this test covers the following files: + Cross.h Quaternion.h, Transform.cpp + */ + + typedef Matrix<Scalar,2,2> Matrix2; + typedef Matrix<Scalar,3,3> Matrix3; + typedef Matrix<Scalar,4,4> Matrix4; + typedef Matrix<Scalar,2,1> Vector2; + typedef Matrix<Scalar,3,1> Vector3; + typedef Matrix<Scalar,4,1> Vector4; + typedef Quaternion<Scalar> Quaternionx; + typedef AngleAxis<Scalar> AngleAxisx; + typedef Transform<Scalar,2> Transform2; + typedef Transform<Scalar,3> Transform3; + typedef Scaling<Scalar,2> Scaling2; + typedef Scaling<Scalar,3> Scaling3; + typedef Translation<Scalar,2> Translation2; + typedef Translation<Scalar,3> Translation3; + + Scalar largeEps = test_precision<Scalar>(); + if (ei_is_same_type<Scalar,float>::ret) + largeEps = 1e-2f; + + Vector3 v0 = Vector3::Random(), + v1 = Vector3::Random(), + v2 = Vector3::Random(); + Vector2 u0 = Vector2::Random(); + Matrix3 matrot1; + + Scalar a = ei_random<Scalar>(-Scalar(M_PI), Scalar(M_PI)); + + // cross product + VERIFY_IS_MUCH_SMALLER_THAN(v1.cross(v2).eigen2_dot(v1), Scalar(1)); + Matrix3 m; + m << v0.normalized(), + (v0.cross(v1)).normalized(), + (v0.cross(v1).cross(v0)).normalized(); + VERIFY(m.isUnitary()); + + // Quaternion: Identity(), setIdentity(); + Quaternionx q1, q2; + q2.setIdentity(); + VERIFY_IS_APPROX(Quaternionx(Quaternionx::Identity()).coeffs(), q2.coeffs()); + q1.coeffs().setRandom(); + VERIFY_IS_APPROX(q1.coeffs(), (q1*q2).coeffs()); + + // unitOrthogonal + VERIFY_IS_MUCH_SMALLER_THAN(u0.unitOrthogonal().eigen2_dot(u0), Scalar(1)); + VERIFY_IS_MUCH_SMALLER_THAN(v0.unitOrthogonal().eigen2_dot(v0), Scalar(1)); + VERIFY_IS_APPROX(u0.unitOrthogonal().norm(), Scalar(1)); + VERIFY_IS_APPROX(v0.unitOrthogonal().norm(), Scalar(1)); + + + VERIFY_IS_APPROX(v0, AngleAxisx(a, v0.normalized()) * v0); + VERIFY_IS_APPROX(-v0, AngleAxisx(Scalar(M_PI), v0.unitOrthogonal()) * v0); + VERIFY_IS_APPROX(ei_cos(a)*v0.squaredNorm(), v0.eigen2_dot(AngleAxisx(a, v0.unitOrthogonal()) * v0)); + m = AngleAxisx(a, v0.normalized()).toRotationMatrix().adjoint(); + VERIFY_IS_APPROX(Matrix3::Identity(), m * AngleAxisx(a, v0.normalized())); + VERIFY_IS_APPROX(Matrix3::Identity(), AngleAxisx(a, v0.normalized()) * m); + + q1 = AngleAxisx(a, v0.normalized()); + q2 = AngleAxisx(a, v1.normalized()); + + // angular distance + Scalar refangle = ei_abs(AngleAxisx(q1.inverse()*q2).angle()); + if (refangle>Scalar(M_PI)) + refangle = Scalar(2)*Scalar(M_PI) - refangle; + + if((q1.coeffs()-q2.coeffs()).norm() > 10*largeEps) + { + VERIFY(ei_isApprox(q1.angularDistance(q2), refangle, largeEps)); + } + + // rotation matrix conversion + VERIFY_IS_APPROX(q1 * v2, q1.toRotationMatrix() * v2); + VERIFY_IS_APPROX(q1 * q2 * v2, + q1.toRotationMatrix() * q2.toRotationMatrix() * v2); + + VERIFY( (q2*q1).isApprox(q1*q2, largeEps) || !(q2 * q1 * v2).isApprox( + q1.toRotationMatrix() * q2.toRotationMatrix() * v2)); + + q2 = q1.toRotationMatrix(); + VERIFY_IS_APPROX(q1*v1,q2*v1); + + matrot1 = AngleAxisx(Scalar(0.1), Vector3::UnitX()) + * AngleAxisx(Scalar(0.2), Vector3::UnitY()) + * AngleAxisx(Scalar(0.3), Vector3::UnitZ()); + VERIFY_IS_APPROX(matrot1 * v1, + AngleAxisx(Scalar(0.1), Vector3(1,0,0)).toRotationMatrix() + * (AngleAxisx(Scalar(0.2), Vector3(0,1,0)).toRotationMatrix() + * (AngleAxisx(Scalar(0.3), Vector3(0,0,1)).toRotationMatrix() * v1))); + + // angle-axis conversion + AngleAxisx aa = q1; + VERIFY_IS_APPROX(q1 * v1, Quaternionx(aa) * v1); + VERIFY_IS_NOT_APPROX(q1 * v1, Quaternionx(AngleAxisx(aa.angle()*2,aa.axis())) * v1); + + // from two vector creation + VERIFY_IS_APPROX(v2.normalized(),(q2.setFromTwoVectors(v1,v2)*v1).normalized()); + VERIFY_IS_APPROX(v2.normalized(),(q2.setFromTwoVectors(v1,v2)*v1).normalized()); + + // inverse and conjugate + VERIFY_IS_APPROX(q1 * (q1.inverse() * v1), v1); + VERIFY_IS_APPROX(q1 * (q1.conjugate() * v1), v1); + + // AngleAxis + VERIFY_IS_APPROX(AngleAxisx(a,v1.normalized()).toRotationMatrix(), + Quaternionx(AngleAxisx(a,v1.normalized())).toRotationMatrix()); + + AngleAxisx aa1; + m = q1.toRotationMatrix(); + aa1 = m; + VERIFY_IS_APPROX(AngleAxisx(m).toRotationMatrix(), + Quaternionx(m).toRotationMatrix()); + + // Transform + // TODO complete the tests ! + a = 0; + while (ei_abs(a)<Scalar(0.1)) + a = ei_random<Scalar>(-Scalar(0.4)*Scalar(M_PI), Scalar(0.4)*Scalar(M_PI)); + q1 = AngleAxisx(a, v0.normalized()); + Transform3 t0, t1, t2; + // first test setIdentity() and Identity() + t0.setIdentity(); + VERIFY_IS_APPROX(t0.matrix(), Transform3::MatrixType::Identity()); + t0.matrix().setZero(); + t0 = Transform3::Identity(); + VERIFY_IS_APPROX(t0.matrix(), Transform3::MatrixType::Identity()); + + t0.linear() = q1.toRotationMatrix(); + t1.setIdentity(); + t1.linear() = q1.toRotationMatrix(); + + v0 << 50, 2, 1;//= ei_random_matrix<Vector3>().cwiseProduct(Vector3(10,2,0.5)); + t0.scale(v0); + t1.prescale(v0); + + VERIFY_IS_APPROX( (t0 * Vector3(1,0,0)).norm(), v0.x()); + //VERIFY(!ei_isApprox((t1 * Vector3(1,0,0)).norm(), v0.x())); + + t0.setIdentity(); + t1.setIdentity(); + v1 << 1, 2, 3; + t0.linear() = q1.toRotationMatrix(); + t0.pretranslate(v0); + t0.scale(v1); + t1.linear() = q1.conjugate().toRotationMatrix(); + t1.prescale(v1.cwise().inverse()); + t1.translate(-v0); + + VERIFY((t0.matrix() * t1.matrix()).isIdentity(test_precision<Scalar>())); + + t1.fromPositionOrientationScale(v0, q1, v1); + VERIFY_IS_APPROX(t1.matrix(), t0.matrix()); + VERIFY_IS_APPROX(t1*v1, t0*v1); + + t0.setIdentity(); t0.scale(v0).rotate(q1.toRotationMatrix()); + t1.setIdentity(); t1.scale(v0).rotate(q1); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + t0.setIdentity(); t0.scale(v0).rotate(AngleAxisx(q1)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + VERIFY_IS_APPROX(t0.scale(a).matrix(), t1.scale(Vector3::Constant(a)).matrix()); + VERIFY_IS_APPROX(t0.prescale(a).matrix(), t1.prescale(Vector3::Constant(a)).matrix()); + + // More transform constructors, operator=, operator*= + + Matrix3 mat3 = Matrix3::Random(); + Matrix4 mat4; + mat4 << mat3 , Vector3::Zero() , Vector4::Zero().transpose(); + Transform3 tmat3(mat3), tmat4(mat4); + tmat4.matrix()(3,3) = Scalar(1); + VERIFY_IS_APPROX(tmat3.matrix(), tmat4.matrix()); + + Scalar a3 = ei_random<Scalar>(-Scalar(M_PI), Scalar(M_PI)); + Vector3 v3 = Vector3::Random().normalized(); + AngleAxisx aa3(a3, v3); + Transform3 t3(aa3); + Transform3 t4; + t4 = aa3; + VERIFY_IS_APPROX(t3.matrix(), t4.matrix()); + t4.rotate(AngleAxisx(-a3,v3)); + VERIFY_IS_APPROX(t4.matrix(), Matrix4::Identity()); + t4 *= aa3; + VERIFY_IS_APPROX(t3.matrix(), t4.matrix()); + + v3 = Vector3::Random(); + Translation3 tv3(v3); + Transform3 t5(tv3); + t4 = tv3; + VERIFY_IS_APPROX(t5.matrix(), t4.matrix()); + t4.translate(-v3); + VERIFY_IS_APPROX(t4.matrix(), Matrix4::Identity()); + t4 *= tv3; + VERIFY_IS_APPROX(t5.matrix(), t4.matrix()); + + Scaling3 sv3(v3); + Transform3 t6(sv3); + t4 = sv3; + VERIFY_IS_APPROX(t6.matrix(), t4.matrix()); + t4.scale(v3.cwise().inverse()); + VERIFY_IS_APPROX(t4.matrix(), Matrix4::Identity()); + t4 *= sv3; + VERIFY_IS_APPROX(t6.matrix(), t4.matrix()); + + // matrix * transform + VERIFY_IS_APPROX(Transform3(t3.matrix()*t4).matrix(), Transform3(t3*t4).matrix()); + + // chained Transform product + VERIFY_IS_APPROX(((t3*t4)*t5).matrix(), (t3*(t4*t5)).matrix()); + + // check that Transform product doesn't have aliasing problems + t5 = t4; + t5 = t5*t5; + VERIFY_IS_APPROX(t5, t4*t4); + + // 2D transformation + Transform2 t20, t21; + Vector2 v20 = Vector2::Random(); + Vector2 v21 = Vector2::Random(); + for (int k=0; k<2; ++k) + if (ei_abs(v21[k])<Scalar(1e-3)) v21[k] = Scalar(1e-3); + t21.setIdentity(); + t21.linear() = Rotation2D<Scalar>(a).toRotationMatrix(); + VERIFY_IS_APPROX(t20.fromPositionOrientationScale(v20,a,v21).matrix(), + t21.pretranslate(v20).scale(v21).matrix()); + + t21.setIdentity(); + t21.linear() = Rotation2D<Scalar>(-a).toRotationMatrix(); + VERIFY( (t20.fromPositionOrientationScale(v20,a,v21) + * (t21.prescale(v21.cwise().inverse()).translate(-v20))).matrix().isIdentity(test_precision<Scalar>()) ); + + // Transform - new API + // 3D + t0.setIdentity(); + t0.rotate(q1).scale(v0).translate(v0); + // mat * scaling and mat * translation + t1 = (Matrix3(q1) * Scaling3(v0)) * Translation3(v0); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + // mat * transformation and scaling * translation + t1 = Matrix3(q1) * (Scaling3(v0) * Translation3(v0)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + t0.setIdentity(); + t0.prerotate(q1).prescale(v0).pretranslate(v0); + // translation * scaling and transformation * mat + t1 = (Translation3(v0) * Scaling3(v0)) * Matrix3(q1); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + // scaling * mat and translation * mat + t1 = Translation3(v0) * (Scaling3(v0) * Matrix3(q1)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + t0.setIdentity(); + t0.scale(v0).translate(v0).rotate(q1); + // translation * mat and scaling * transformation + t1 = Scaling3(v0) * (Translation3(v0) * Matrix3(q1)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + // transformation * scaling + t0.scale(v0); + t1 = t1 * Scaling3(v0); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + // transformation * translation + t0.translate(v0); + t1 = t1 * Translation3(v0); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + // translation * transformation + t0.pretranslate(v0); + t1 = Translation3(v0) * t1; + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // transform * quaternion + t0.rotate(q1); + t1 = t1 * q1; + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // translation * quaternion + t0.translate(v1).rotate(q1); + t1 = t1 * (Translation3(v1) * q1); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // scaling * quaternion + t0.scale(v1).rotate(q1); + t1 = t1 * (Scaling3(v1) * q1); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // quaternion * transform + t0.prerotate(q1); + t1 = q1 * t1; + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // quaternion * translation + t0.rotate(q1).translate(v1); + t1 = t1 * (q1 * Translation3(v1)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // quaternion * scaling + t0.rotate(q1).scale(v1); + t1 = t1 * (q1 * Scaling3(v1)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // translation * vector + t0.setIdentity(); + t0.translate(v0); + VERIFY_IS_APPROX(t0 * v1, Translation3(v0) * v1); + + // scaling * vector + t0.setIdentity(); + t0.scale(v0); + VERIFY_IS_APPROX(t0 * v1, Scaling3(v0) * v1); + + // test transform inversion + t0.setIdentity(); + t0.translate(v0); + t0.linear().setRandom(); + VERIFY_IS_APPROX(t0.inverse(Affine), t0.matrix().inverse()); + t0.setIdentity(); + t0.translate(v0).rotate(q1); + VERIFY_IS_APPROX(t0.inverse(Isometry), t0.matrix().inverse()); + + // test extract rotation and scaling + t0.setIdentity(); + t0.translate(v0).rotate(q1).scale(v1); + VERIFY_IS_APPROX(t0.rotation() * v1, Matrix3(q1) * v1); + + Matrix3 mat_rotation, mat_scaling; + t0.setIdentity(); + t0.translate(v0).rotate(q1).scale(v1); + t0.computeRotationScaling(&mat_rotation, &mat_scaling); + VERIFY_IS_APPROX(t0.linear(), mat_rotation * mat_scaling); + VERIFY_IS_APPROX(mat_rotation*mat_rotation.adjoint(), Matrix3::Identity()); + VERIFY_IS_APPROX(mat_rotation.determinant(), Scalar(1)); + t0.computeScalingRotation(&mat_scaling, &mat_rotation); + VERIFY_IS_APPROX(t0.linear(), mat_scaling * mat_rotation); + VERIFY_IS_APPROX(mat_rotation*mat_rotation.adjoint(), Matrix3::Identity()); + VERIFY_IS_APPROX(mat_rotation.determinant(), Scalar(1)); + + // test casting + Transform<float,3> t1f = t1.template cast<float>(); + VERIFY_IS_APPROX(t1f.template cast<Scalar>(),t1); + Transform<double,3> t1d = t1.template cast<double>(); + VERIFY_IS_APPROX(t1d.template cast<Scalar>(),t1); + + Translation3 tr1(v0); + Translation<float,3> tr1f = tr1.template cast<float>(); + VERIFY_IS_APPROX(tr1f.template cast<Scalar>(),tr1); + Translation<double,3> tr1d = tr1.template cast<double>(); + VERIFY_IS_APPROX(tr1d.template cast<Scalar>(),tr1); + + Scaling3 sc1(v0); + Scaling<float,3> sc1f = sc1.template cast<float>(); + VERIFY_IS_APPROX(sc1f.template cast<Scalar>(),sc1); + Scaling<double,3> sc1d = sc1.template cast<double>(); + VERIFY_IS_APPROX(sc1d.template cast<Scalar>(),sc1); + + Quaternion<float> q1f = q1.template cast<float>(); + VERIFY_IS_APPROX(q1f.template cast<Scalar>(),q1); + Quaternion<double> q1d = q1.template cast<double>(); + VERIFY_IS_APPROX(q1d.template cast<Scalar>(),q1); + + AngleAxis<float> aa1f = aa1.template cast<float>(); + VERIFY_IS_APPROX(aa1f.template cast<Scalar>(),aa1); + AngleAxis<double> aa1d = aa1.template cast<double>(); + VERIFY_IS_APPROX(aa1d.template cast<Scalar>(),aa1); + + Rotation2D<Scalar> r2d1(ei_random<Scalar>()); + Rotation2D<float> r2d1f = r2d1.template cast<float>(); + VERIFY_IS_APPROX(r2d1f.template cast<Scalar>(),r2d1); + Rotation2D<double> r2d1d = r2d1.template cast<double>(); + VERIFY_IS_APPROX(r2d1d.template cast<Scalar>(),r2d1); + + m = q1; +// m.col(1) = Vector3(0,ei_random<Scalar>(),ei_random<Scalar>()).normalized(); +// m.col(0) = Vector3(-1,0,0).normalized(); +// m.col(2) = m.col(0).cross(m.col(1)); + #define VERIFY_EULER(I,J,K, X,Y,Z) { \ + Vector3 ea = m.eulerAngles(I,J,K); \ + Matrix3 m1 = Matrix3(AngleAxisx(ea[0], Vector3::Unit##X()) * AngleAxisx(ea[1], Vector3::Unit##Y()) * AngleAxisx(ea[2], Vector3::Unit##Z())); \ + VERIFY_IS_APPROX(m, Matrix3(AngleAxisx(ea[0], Vector3::Unit##X()) * AngleAxisx(ea[1], Vector3::Unit##Y()) * AngleAxisx(ea[2], Vector3::Unit##Z()))); \ + } + VERIFY_EULER(0,1,2, X,Y,Z); + VERIFY_EULER(0,1,0, X,Y,X); + VERIFY_EULER(0,2,1, X,Z,Y); + VERIFY_EULER(0,2,0, X,Z,X); + + VERIFY_EULER(1,2,0, Y,Z,X); + VERIFY_EULER(1,2,1, Y,Z,Y); + VERIFY_EULER(1,0,2, Y,X,Z); + VERIFY_EULER(1,0,1, Y,X,Y); + + VERIFY_EULER(2,0,1, Z,X,Y); + VERIFY_EULER(2,0,2, Z,X,Z); + VERIFY_EULER(2,1,0, Z,Y,X); + VERIFY_EULER(2,1,2, Z,Y,Z); + + // colwise/rowwise cross product + mat3.setRandom(); + Vector3 vec3 = Vector3::Random(); + Matrix3 mcross; + int i = ei_random<int>(0,2); + mcross = mat3.colwise().cross(vec3); + VERIFY_IS_APPROX(mcross.col(i), mat3.col(i).cross(vec3)); + mcross = mat3.rowwise().cross(vec3); + VERIFY_IS_APPROX(mcross.row(i), mat3.row(i).cross(vec3)); + + +} + +void test_eigen2_geometry() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( geometry<float>() ); + CALL_SUBTEST_2( geometry<double>() ); + } +} diff --git a/test/eigen2/eigen2_geometry_with_eigen2_prefix.cpp b/test/eigen2/eigen2_geometry_with_eigen2_prefix.cpp new file mode 100644 index 000000000..f83b57249 --- /dev/null +++ b/test/eigen2/eigen2_geometry_with_eigen2_prefix.cpp @@ -0,0 +1,434 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// 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/. + +#define EIGEN2_SUPPORT_STAGE15_RESOLVE_API_CONFLICTS_WARN + +#include "main.h" +#include <Eigen/Geometry> +#include <Eigen/LU> +#include <Eigen/SVD> + +template<typename Scalar> void geometry(void) +{ + /* this test covers the following files: + Cross.h Quaternion.h, Transform.cpp + */ + + typedef Matrix<Scalar,2,2> Matrix2; + typedef Matrix<Scalar,3,3> Matrix3; + typedef Matrix<Scalar,4,4> Matrix4; + typedef Matrix<Scalar,2,1> Vector2; + typedef Matrix<Scalar,3,1> Vector3; + typedef Matrix<Scalar,4,1> Vector4; + typedef eigen2_Quaternion<Scalar> Quaternionx; + typedef eigen2_AngleAxis<Scalar> AngleAxisx; + typedef eigen2_Transform<Scalar,2> Transform2; + typedef eigen2_Transform<Scalar,3> Transform3; + typedef eigen2_Scaling<Scalar,2> Scaling2; + typedef eigen2_Scaling<Scalar,3> Scaling3; + typedef eigen2_Translation<Scalar,2> Translation2; + typedef eigen2_Translation<Scalar,3> Translation3; + + Scalar largeEps = test_precision<Scalar>(); + if (ei_is_same_type<Scalar,float>::ret) + largeEps = 1e-2f; + + Vector3 v0 = Vector3::Random(), + v1 = Vector3::Random(), + v2 = Vector3::Random(); + Vector2 u0 = Vector2::Random(); + Matrix3 matrot1; + + Scalar a = ei_random<Scalar>(-Scalar(M_PI), Scalar(M_PI)); + + // cross product + VERIFY_IS_MUCH_SMALLER_THAN(v1.cross(v2).eigen2_dot(v1), Scalar(1)); + Matrix3 m; + m << v0.normalized(), + (v0.cross(v1)).normalized(), + (v0.cross(v1).cross(v0)).normalized(); + VERIFY(m.isUnitary()); + + // Quaternion: Identity(), setIdentity(); + Quaternionx q1, q2; + q2.setIdentity(); + VERIFY_IS_APPROX(Quaternionx(Quaternionx::Identity()).coeffs(), q2.coeffs()); + q1.coeffs().setRandom(); + VERIFY_IS_APPROX(q1.coeffs(), (q1*q2).coeffs()); + + // unitOrthogonal + VERIFY_IS_MUCH_SMALLER_THAN(u0.unitOrthogonal().eigen2_dot(u0), Scalar(1)); + VERIFY_IS_MUCH_SMALLER_THAN(v0.unitOrthogonal().eigen2_dot(v0), Scalar(1)); + VERIFY_IS_APPROX(u0.unitOrthogonal().norm(), Scalar(1)); + VERIFY_IS_APPROX(v0.unitOrthogonal().norm(), Scalar(1)); + + + VERIFY_IS_APPROX(v0, AngleAxisx(a, v0.normalized()) * v0); + VERIFY_IS_APPROX(-v0, AngleAxisx(Scalar(M_PI), v0.unitOrthogonal()) * v0); + VERIFY_IS_APPROX(ei_cos(a)*v0.squaredNorm(), v0.eigen2_dot(AngleAxisx(a, v0.unitOrthogonal()) * v0)); + m = AngleAxisx(a, v0.normalized()).toRotationMatrix().adjoint(); + VERIFY_IS_APPROX(Matrix3::Identity(), m * AngleAxisx(a, v0.normalized())); + VERIFY_IS_APPROX(Matrix3::Identity(), AngleAxisx(a, v0.normalized()) * m); + + q1 = AngleAxisx(a, v0.normalized()); + q2 = AngleAxisx(a, v1.normalized()); + + // angular distance + Scalar refangle = ei_abs(AngleAxisx(q1.inverse()*q2).angle()); + if (refangle>Scalar(M_PI)) + refangle = Scalar(2)*Scalar(M_PI) - refangle; + + if((q1.coeffs()-q2.coeffs()).norm() > 10*largeEps) + { + VERIFY(ei_isApprox(q1.angularDistance(q2), refangle, largeEps)); + } + + // rotation matrix conversion + VERIFY_IS_APPROX(q1 * v2, q1.toRotationMatrix() * v2); + VERIFY_IS_APPROX(q1 * q2 * v2, + q1.toRotationMatrix() * q2.toRotationMatrix() * v2); + + VERIFY( (q2*q1).isApprox(q1*q2, largeEps) || !(q2 * q1 * v2).isApprox( + q1.toRotationMatrix() * q2.toRotationMatrix() * v2)); + + q2 = q1.toRotationMatrix(); + VERIFY_IS_APPROX(q1*v1,q2*v1); + + matrot1 = AngleAxisx(Scalar(0.1), Vector3::UnitX()) + * AngleAxisx(Scalar(0.2), Vector3::UnitY()) + * AngleAxisx(Scalar(0.3), Vector3::UnitZ()); + VERIFY_IS_APPROX(matrot1 * v1, + AngleAxisx(Scalar(0.1), Vector3(1,0,0)).toRotationMatrix() + * (AngleAxisx(Scalar(0.2), Vector3(0,1,0)).toRotationMatrix() + * (AngleAxisx(Scalar(0.3), Vector3(0,0,1)).toRotationMatrix() * v1))); + + // angle-axis conversion + AngleAxisx aa = q1; + VERIFY_IS_APPROX(q1 * v1, Quaternionx(aa) * v1); + VERIFY_IS_NOT_APPROX(q1 * v1, Quaternionx(AngleAxisx(aa.angle()*2,aa.axis())) * v1); + + // from two vector creation + VERIFY_IS_APPROX(v2.normalized(),(q2.setFromTwoVectors(v1,v2)*v1).normalized()); + VERIFY_IS_APPROX(v2.normalized(),(q2.setFromTwoVectors(v1,v2)*v1).normalized()); + + // inverse and conjugate + VERIFY_IS_APPROX(q1 * (q1.inverse() * v1), v1); + VERIFY_IS_APPROX(q1 * (q1.conjugate() * v1), v1); + + // AngleAxis + VERIFY_IS_APPROX(AngleAxisx(a,v1.normalized()).toRotationMatrix(), + Quaternionx(AngleAxisx(a,v1.normalized())).toRotationMatrix()); + + AngleAxisx aa1; + m = q1.toRotationMatrix(); + aa1 = m; + VERIFY_IS_APPROX(AngleAxisx(m).toRotationMatrix(), + Quaternionx(m).toRotationMatrix()); + + // Transform + // TODO complete the tests ! + a = 0; + while (ei_abs(a)<Scalar(0.1)) + a = ei_random<Scalar>(-Scalar(0.4)*Scalar(M_PI), Scalar(0.4)*Scalar(M_PI)); + q1 = AngleAxisx(a, v0.normalized()); + Transform3 t0, t1, t2; + // first test setIdentity() and Identity() + t0.setIdentity(); + VERIFY_IS_APPROX(t0.matrix(), Transform3::MatrixType::Identity()); + t0.matrix().setZero(); + t0 = Transform3::Identity(); + VERIFY_IS_APPROX(t0.matrix(), Transform3::MatrixType::Identity()); + + t0.linear() = q1.toRotationMatrix(); + t1.setIdentity(); + t1.linear() = q1.toRotationMatrix(); + + v0 << 50, 2, 1;//= ei_random_matrix<Vector3>().cwiseProduct(Vector3(10,2,0.5)); + t0.scale(v0); + t1.prescale(v0); + + VERIFY_IS_APPROX( (t0 * Vector3(1,0,0)).norm(), v0.x()); + //VERIFY(!ei_isApprox((t1 * Vector3(1,0,0)).norm(), v0.x())); + + t0.setIdentity(); + t1.setIdentity(); + v1 << 1, 2, 3; + t0.linear() = q1.toRotationMatrix(); + t0.pretranslate(v0); + t0.scale(v1); + t1.linear() = q1.conjugate().toRotationMatrix(); + t1.prescale(v1.cwise().inverse()); + t1.translate(-v0); + + VERIFY((t0.matrix() * t1.matrix()).isIdentity(test_precision<Scalar>())); + + t1.fromPositionOrientationScale(v0, q1, v1); + VERIFY_IS_APPROX(t1.matrix(), t0.matrix()); + VERIFY_IS_APPROX(t1*v1, t0*v1); + + t0.setIdentity(); t0.scale(v0).rotate(q1.toRotationMatrix()); + t1.setIdentity(); t1.scale(v0).rotate(q1); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + t0.setIdentity(); t0.scale(v0).rotate(AngleAxisx(q1)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + VERIFY_IS_APPROX(t0.scale(a).matrix(), t1.scale(Vector3::Constant(a)).matrix()); + VERIFY_IS_APPROX(t0.prescale(a).matrix(), t1.prescale(Vector3::Constant(a)).matrix()); + + // More transform constructors, operator=, operator*= + + Matrix3 mat3 = Matrix3::Random(); + Matrix4 mat4; + mat4 << mat3 , Vector3::Zero() , Vector4::Zero().transpose(); + Transform3 tmat3(mat3), tmat4(mat4); + tmat4.matrix()(3,3) = Scalar(1); + VERIFY_IS_APPROX(tmat3.matrix(), tmat4.matrix()); + + Scalar a3 = ei_random<Scalar>(-Scalar(M_PI), Scalar(M_PI)); + Vector3 v3 = Vector3::Random().normalized(); + AngleAxisx aa3(a3, v3); + Transform3 t3(aa3); + Transform3 t4; + t4 = aa3; + VERIFY_IS_APPROX(t3.matrix(), t4.matrix()); + t4.rotate(AngleAxisx(-a3,v3)); + VERIFY_IS_APPROX(t4.matrix(), Matrix4::Identity()); + t4 *= aa3; + VERIFY_IS_APPROX(t3.matrix(), t4.matrix()); + + v3 = Vector3::Random(); + Translation3 tv3(v3); + Transform3 t5(tv3); + t4 = tv3; + VERIFY_IS_APPROX(t5.matrix(), t4.matrix()); + t4.translate(-v3); + VERIFY_IS_APPROX(t4.matrix(), Matrix4::Identity()); + t4 *= tv3; + VERIFY_IS_APPROX(t5.matrix(), t4.matrix()); + + Scaling3 sv3(v3); + Transform3 t6(sv3); + t4 = sv3; + VERIFY_IS_APPROX(t6.matrix(), t4.matrix()); + t4.scale(v3.cwise().inverse()); + VERIFY_IS_APPROX(t4.matrix(), Matrix4::Identity()); + t4 *= sv3; + VERIFY_IS_APPROX(t6.matrix(), t4.matrix()); + + // matrix * transform + VERIFY_IS_APPROX(Transform3(t3.matrix()*t4).matrix(), Transform3(t3*t4).matrix()); + + // chained Transform product + VERIFY_IS_APPROX(((t3*t4)*t5).matrix(), (t3*(t4*t5)).matrix()); + + // check that Transform product doesn't have aliasing problems + t5 = t4; + t5 = t5*t5; + VERIFY_IS_APPROX(t5, t4*t4); + + // 2D transformation + Transform2 t20, t21; + Vector2 v20 = Vector2::Random(); + Vector2 v21 = Vector2::Random(); + for (int k=0; k<2; ++k) + if (ei_abs(v21[k])<Scalar(1e-3)) v21[k] = Scalar(1e-3); + t21.setIdentity(); + t21.linear() = Rotation2D<Scalar>(a).toRotationMatrix(); + VERIFY_IS_APPROX(t20.fromPositionOrientationScale(v20,a,v21).matrix(), + t21.pretranslate(v20).scale(v21).matrix()); + + t21.setIdentity(); + t21.linear() = Rotation2D<Scalar>(-a).toRotationMatrix(); + VERIFY( (t20.fromPositionOrientationScale(v20,a,v21) + * (t21.prescale(v21.cwise().inverse()).translate(-v20))).matrix().isIdentity(test_precision<Scalar>()) ); + + // Transform - new API + // 3D + t0.setIdentity(); + t0.rotate(q1).scale(v0).translate(v0); + // mat * scaling and mat * translation + t1 = (Matrix3(q1) * Scaling3(v0)) * Translation3(v0); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + // mat * transformation and scaling * translation + t1 = Matrix3(q1) * (Scaling3(v0) * Translation3(v0)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + t0.setIdentity(); + t0.prerotate(q1).prescale(v0).pretranslate(v0); + // translation * scaling and transformation * mat + t1 = (Translation3(v0) * Scaling3(v0)) * Matrix3(q1); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + // scaling * mat and translation * mat + t1 = Translation3(v0) * (Scaling3(v0) * Matrix3(q1)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + t0.setIdentity(); + t0.scale(v0).translate(v0).rotate(q1); + // translation * mat and scaling * transformation + t1 = Scaling3(v0) * (Translation3(v0) * Matrix3(q1)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + // transformation * scaling + t0.scale(v0); + t1 = t1 * Scaling3(v0); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + // transformation * translation + t0.translate(v0); + t1 = t1 * Translation3(v0); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + // translation * transformation + t0.pretranslate(v0); + t1 = Translation3(v0) * t1; + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // transform * quaternion + t0.rotate(q1); + t1 = t1 * q1; + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // translation * quaternion + t0.translate(v1).rotate(q1); + t1 = t1 * (Translation3(v1) * q1); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // scaling * quaternion + t0.scale(v1).rotate(q1); + t1 = t1 * (Scaling3(v1) * q1); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // quaternion * transform + t0.prerotate(q1); + t1 = q1 * t1; + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // quaternion * translation + t0.rotate(q1).translate(v1); + t1 = t1 * (q1 * Translation3(v1)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // quaternion * scaling + t0.rotate(q1).scale(v1); + t1 = t1 * (q1 * Scaling3(v1)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // translation * vector + t0.setIdentity(); + t0.translate(v0); + VERIFY_IS_APPROX(t0 * v1, Translation3(v0) * v1); + + // scaling * vector + t0.setIdentity(); + t0.scale(v0); + VERIFY_IS_APPROX(t0 * v1, Scaling3(v0) * v1); + + // test transform inversion + t0.setIdentity(); + t0.translate(v0); + t0.linear().setRandom(); + VERIFY_IS_APPROX(t0.inverse(Affine), t0.matrix().inverse()); + t0.setIdentity(); + t0.translate(v0).rotate(q1); + VERIFY_IS_APPROX(t0.inverse(Isometry), t0.matrix().inverse()); + + // test extract rotation and scaling + t0.setIdentity(); + t0.translate(v0).rotate(q1).scale(v1); + VERIFY_IS_APPROX(t0.rotation() * v1, Matrix3(q1) * v1); + + Matrix3 mat_rotation, mat_scaling; + t0.setIdentity(); + t0.translate(v0).rotate(q1).scale(v1); + t0.computeRotationScaling(&mat_rotation, &mat_scaling); + VERIFY_IS_APPROX(t0.linear(), mat_rotation * mat_scaling); + VERIFY_IS_APPROX(mat_rotation*mat_rotation.adjoint(), Matrix3::Identity()); + VERIFY_IS_APPROX(mat_rotation.determinant(), Scalar(1)); + t0.computeScalingRotation(&mat_scaling, &mat_rotation); + VERIFY_IS_APPROX(t0.linear(), mat_scaling * mat_rotation); + VERIFY_IS_APPROX(mat_rotation*mat_rotation.adjoint(), Matrix3::Identity()); + VERIFY_IS_APPROX(mat_rotation.determinant(), Scalar(1)); + + // test casting + eigen2_Transform<float,3> t1f = t1.template cast<float>(); + VERIFY_IS_APPROX(t1f.template cast<Scalar>(),t1); + eigen2_Transform<double,3> t1d = t1.template cast<double>(); + VERIFY_IS_APPROX(t1d.template cast<Scalar>(),t1); + + Translation3 tr1(v0); + eigen2_Translation<float,3> tr1f = tr1.template cast<float>(); + VERIFY_IS_APPROX(tr1f.template cast<Scalar>(),tr1); + eigen2_Translation<double,3> tr1d = tr1.template cast<double>(); + VERIFY_IS_APPROX(tr1d.template cast<Scalar>(),tr1); + + Scaling3 sc1(v0); + eigen2_Scaling<float,3> sc1f = sc1.template cast<float>(); + VERIFY_IS_APPROX(sc1f.template cast<Scalar>(),sc1); + eigen2_Scaling<double,3> sc1d = sc1.template cast<double>(); + VERIFY_IS_APPROX(sc1d.template cast<Scalar>(),sc1); + + eigen2_Quaternion<float> q1f = q1.template cast<float>(); + VERIFY_IS_APPROX(q1f.template cast<Scalar>(),q1); + eigen2_Quaternion<double> q1d = q1.template cast<double>(); + VERIFY_IS_APPROX(q1d.template cast<Scalar>(),q1); + + eigen2_AngleAxis<float> aa1f = aa1.template cast<float>(); + VERIFY_IS_APPROX(aa1f.template cast<Scalar>(),aa1); + eigen2_AngleAxis<double> aa1d = aa1.template cast<double>(); + VERIFY_IS_APPROX(aa1d.template cast<Scalar>(),aa1); + + eigen2_Rotation2D<Scalar> r2d1(ei_random<Scalar>()); + eigen2_Rotation2D<float> r2d1f = r2d1.template cast<float>(); + VERIFY_IS_APPROX(r2d1f.template cast<Scalar>(),r2d1); + eigen2_Rotation2D<double> r2d1d = r2d1.template cast<double>(); + VERIFY_IS_APPROX(r2d1d.template cast<Scalar>(),r2d1); + + m = q1; +// m.col(1) = Vector3(0,ei_random<Scalar>(),ei_random<Scalar>()).normalized(); +// m.col(0) = Vector3(-1,0,0).normalized(); +// m.col(2) = m.col(0).cross(m.col(1)); + #define VERIFY_EULER(I,J,K, X,Y,Z) { \ + Vector3 ea = m.eulerAngles(I,J,K); \ + Matrix3 m1 = Matrix3(AngleAxisx(ea[0], Vector3::Unit##X()) * AngleAxisx(ea[1], Vector3::Unit##Y()) * AngleAxisx(ea[2], Vector3::Unit##Z())); \ + VERIFY_IS_APPROX(m, Matrix3(AngleAxisx(ea[0], Vector3::Unit##X()) * AngleAxisx(ea[1], Vector3::Unit##Y()) * AngleAxisx(ea[2], Vector3::Unit##Z()))); \ + } + VERIFY_EULER(0,1,2, X,Y,Z); + VERIFY_EULER(0,1,0, X,Y,X); + VERIFY_EULER(0,2,1, X,Z,Y); + VERIFY_EULER(0,2,0, X,Z,X); + + VERIFY_EULER(1,2,0, Y,Z,X); + VERIFY_EULER(1,2,1, Y,Z,Y); + VERIFY_EULER(1,0,2, Y,X,Z); + VERIFY_EULER(1,0,1, Y,X,Y); + + VERIFY_EULER(2,0,1, Z,X,Y); + VERIFY_EULER(2,0,2, Z,X,Z); + VERIFY_EULER(2,1,0, Z,Y,X); + VERIFY_EULER(2,1,2, Z,Y,Z); + + // colwise/rowwise cross product + mat3.setRandom(); + Vector3 vec3 = Vector3::Random(); + Matrix3 mcross; + int i = ei_random<int>(0,2); + mcross = mat3.colwise().cross(vec3); + VERIFY_IS_APPROX(mcross.col(i), mat3.col(i).cross(vec3)); + mcross = mat3.rowwise().cross(vec3); + VERIFY_IS_APPROX(mcross.row(i), mat3.row(i).cross(vec3)); + + +} + +void test_eigen2_geometry_with_eigen2_prefix() +{ + std::cout << "eigen2 support: " << EIGEN2_SUPPORT_STAGE << std::endl; + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( geometry<float>() ); + CALL_SUBTEST_2( geometry<double>() ); + } +} diff --git a/test/eigen2/eigen2_hyperplane.cpp b/test/eigen2/eigen2_hyperplane.cpp new file mode 100644 index 000000000..f3f85e14d --- /dev/null +++ b/test/eigen2/eigen2_hyperplane.cpp @@ -0,0 +1,126 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/Geometry> +#include <Eigen/LU> +#include <Eigen/QR> + +template<typename HyperplaneType> void hyperplane(const HyperplaneType& _plane) +{ + /* this test covers the following files: + Hyperplane.h + */ + + const int dim = _plane.dim(); + typedef typename HyperplaneType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, HyperplaneType::AmbientDimAtCompileTime, 1> VectorType; + typedef Matrix<Scalar, HyperplaneType::AmbientDimAtCompileTime, + HyperplaneType::AmbientDimAtCompileTime> MatrixType; + + VectorType p0 = VectorType::Random(dim); + VectorType p1 = VectorType::Random(dim); + + VectorType n0 = VectorType::Random(dim).normalized(); + VectorType n1 = VectorType::Random(dim).normalized(); + + HyperplaneType pl0(n0, p0); + HyperplaneType pl1(n1, p1); + HyperplaneType pl2 = pl1; + + Scalar s0 = ei_random<Scalar>(); + Scalar s1 = ei_random<Scalar>(); + + VERIFY_IS_APPROX( n1.eigen2_dot(n1), Scalar(1) ); + + VERIFY_IS_MUCH_SMALLER_THAN( pl0.absDistance(p0), Scalar(1) ); + VERIFY_IS_APPROX( pl1.signedDistance(p1 + n1 * s0), s0 ); + VERIFY_IS_MUCH_SMALLER_THAN( pl1.signedDistance(pl1.projection(p0)), Scalar(1) ); + VERIFY_IS_MUCH_SMALLER_THAN( pl1.absDistance(p1 + pl1.normal().unitOrthogonal() * s1), Scalar(1) ); + + // transform + if (!NumTraits<Scalar>::IsComplex) + { + MatrixType rot = MatrixType::Random(dim,dim).qr().matrixQ(); + Scaling<Scalar,HyperplaneType::AmbientDimAtCompileTime> scaling(VectorType::Random()); + Translation<Scalar,HyperplaneType::AmbientDimAtCompileTime> translation(VectorType::Random()); + + pl2 = pl1; + VERIFY_IS_MUCH_SMALLER_THAN( pl2.transform(rot).absDistance(rot * p1), Scalar(1) ); + pl2 = pl1; + VERIFY_IS_MUCH_SMALLER_THAN( pl2.transform(rot,Isometry).absDistance(rot * p1), Scalar(1) ); + pl2 = pl1; + VERIFY_IS_MUCH_SMALLER_THAN( pl2.transform(rot*scaling).absDistance((rot*scaling) * p1), Scalar(1) ); + pl2 = pl1; + VERIFY_IS_MUCH_SMALLER_THAN( pl2.transform(rot*scaling*translation) + .absDistance((rot*scaling*translation) * p1), Scalar(1) ); + pl2 = pl1; + VERIFY_IS_MUCH_SMALLER_THAN( pl2.transform(rot*translation,Isometry) + .absDistance((rot*translation) * p1), Scalar(1) ); + } + + // casting + const int Dim = HyperplaneType::AmbientDimAtCompileTime; + typedef typename GetDifferentType<Scalar>::type OtherScalar; + Hyperplane<OtherScalar,Dim> hp1f = pl1.template cast<OtherScalar>(); + VERIFY_IS_APPROX(hp1f.template cast<Scalar>(),pl1); + Hyperplane<Scalar,Dim> hp1d = pl1.template cast<Scalar>(); + VERIFY_IS_APPROX(hp1d.template cast<Scalar>(),pl1); +} + +template<typename Scalar> void lines() +{ + typedef Hyperplane<Scalar, 2> HLine; + typedef ParametrizedLine<Scalar, 2> PLine; + typedef Matrix<Scalar,2,1> Vector; + typedef Matrix<Scalar,3,1> CoeffsType; + + for(int i = 0; i < 10; i++) + { + Vector center = Vector::Random(); + Vector u = Vector::Random(); + Vector v = Vector::Random(); + Scalar a = ei_random<Scalar>(); + while (ei_abs(a-1) < 1e-4) a = ei_random<Scalar>(); + while (u.norm() < 1e-4) u = Vector::Random(); + while (v.norm() < 1e-4) v = Vector::Random(); + + HLine line_u = HLine::Through(center + u, center + a*u); + HLine line_v = HLine::Through(center + v, center + a*v); + + // the line equations should be normalized so that a^2+b^2=1 + VERIFY_IS_APPROX(line_u.normal().norm(), Scalar(1)); + VERIFY_IS_APPROX(line_v.normal().norm(), Scalar(1)); + + Vector result = line_u.intersection(line_v); + + // the lines should intersect at the point we called "center" + VERIFY_IS_APPROX(result, center); + + // check conversions between two types of lines + PLine pl(line_u); // gcc 3.3 will commit suicide if we don't name this variable + CoeffsType converted_coeffs(HLine(pl).coeffs()); + converted_coeffs *= line_u.coeffs()(0)/converted_coeffs(0); + VERIFY(line_u.coeffs().isApprox(converted_coeffs)); + } +} + +void test_eigen2_hyperplane() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( hyperplane(Hyperplane<float,2>()) ); + CALL_SUBTEST_2( hyperplane(Hyperplane<float,3>()) ); + CALL_SUBTEST_3( hyperplane(Hyperplane<double,4>()) ); + CALL_SUBTEST_4( hyperplane(Hyperplane<std::complex<double>,5>()) ); + CALL_SUBTEST_5( lines<float>() ); + CALL_SUBTEST_6( lines<double>() ); + } +} diff --git a/test/eigen2/eigen2_inverse.cpp b/test/eigen2/eigen2_inverse.cpp new file mode 100644 index 000000000..5de1dfefa --- /dev/null +++ b/test/eigen2/eigen2_inverse.cpp @@ -0,0 +1,63 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/LU> + +template<typename MatrixType> void inverse(const MatrixType& m) +{ + /* this test covers the following files: + Inverse.h + */ + int rows = m.rows(); + int cols = m.cols(); + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, 1> VectorType; + + MatrixType m1 = MatrixType::Random(rows, cols), + m2(rows, cols), + mzero = MatrixType::Zero(rows, cols), + identity = MatrixType::Identity(rows, rows); + + while(ei_abs(m1.determinant()) < RealScalar(0.1) && rows <= 8) + { + m1 = MatrixType::Random(rows, cols); + } + + m2 = m1.inverse(); + VERIFY_IS_APPROX(m1, m2.inverse() ); + + m1.computeInverse(&m2); + VERIFY_IS_APPROX(m1, m2.inverse() ); + + VERIFY_IS_APPROX((Scalar(2)*m2).inverse(), m2.inverse()*Scalar(0.5)); + + VERIFY_IS_APPROX(identity, m1.inverse() * m1 ); + VERIFY_IS_APPROX(identity, m1 * m1.inverse() ); + + VERIFY_IS_APPROX(m1, m1.inverse().inverse() ); + + // since for the general case we implement separately row-major and col-major, test that + VERIFY_IS_APPROX(m1.transpose().inverse(), m1.inverse().transpose()); +} + +void test_eigen2_inverse() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( inverse(Matrix<double,1,1>()) ); + CALL_SUBTEST_2( inverse(Matrix2d()) ); + CALL_SUBTEST_3( inverse(Matrix3f()) ); + CALL_SUBTEST_4( inverse(Matrix4f()) ); + CALL_SUBTEST_5( inverse(MatrixXf(8,8)) ); + CALL_SUBTEST_6( inverse(MatrixXcd(7,7)) ); + } +} diff --git a/test/eigen2/eigen2_linearstructure.cpp b/test/eigen2/eigen2_linearstructure.cpp new file mode 100644 index 000000000..22f02c396 --- /dev/null +++ b/test/eigen2/eigen2_linearstructure.cpp @@ -0,0 +1,84 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename MatrixType> void linearStructure(const MatrixType& m) +{ + /* this test covers the following files: + Sum.h Difference.h Opposite.h ScalarMultiple.h + */ + + typedef typename MatrixType::Scalar Scalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + int rows = m.rows(); + int cols = m.cols(); + + // this test relies a lot on Random.h, and there's not much more that we can do + // to test it, hence I consider that we will have tested Random.h + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols), + mzero = MatrixType::Zero(rows, cols); + + Scalar s1 = ei_random<Scalar>(); + while (ei_abs(s1)<1e-3) s1 = ei_random<Scalar>(); + + int r = ei_random<int>(0, rows-1), + c = ei_random<int>(0, cols-1); + + VERIFY_IS_APPROX(-(-m1), m1); + VERIFY_IS_APPROX(m1+m1, 2*m1); + VERIFY_IS_APPROX(m1+m2-m1, m2); + VERIFY_IS_APPROX(-m2+m1+m2, m1); + VERIFY_IS_APPROX(m1*s1, s1*m1); + VERIFY_IS_APPROX((m1+m2)*s1, s1*m1+s1*m2); + VERIFY_IS_APPROX((-m1+m2)*s1, -s1*m1+s1*m2); + m3 = m2; m3 += m1; + VERIFY_IS_APPROX(m3, m1+m2); + m3 = m2; m3 -= m1; + VERIFY_IS_APPROX(m3, m2-m1); + m3 = m2; m3 *= s1; + VERIFY_IS_APPROX(m3, s1*m2); + if(NumTraits<Scalar>::HasFloatingPoint) + { + m3 = m2; m3 /= s1; + VERIFY_IS_APPROX(m3, m2/s1); + } + + // again, test operator() to check const-qualification + VERIFY_IS_APPROX((-m1)(r,c), -(m1(r,c))); + VERIFY_IS_APPROX((m1-m2)(r,c), (m1(r,c))-(m2(r,c))); + VERIFY_IS_APPROX((m1+m2)(r,c), (m1(r,c))+(m2(r,c))); + VERIFY_IS_APPROX((s1*m1)(r,c), s1*(m1(r,c))); + VERIFY_IS_APPROX((m1*s1)(r,c), (m1(r,c))*s1); + if(NumTraits<Scalar>::HasFloatingPoint) + VERIFY_IS_APPROX((m1/s1)(r,c), (m1(r,c))/s1); + + // use .block to disable vectorization and compare to the vectorized version + VERIFY_IS_APPROX(m1+m1.block(0,0,rows,cols), m1+m1); + VERIFY_IS_APPROX(m1.cwise() * m1.block(0,0,rows,cols), m1.cwise() * m1); + VERIFY_IS_APPROX(m1 - m1.block(0,0,rows,cols), m1 - m1); + VERIFY_IS_APPROX(m1.block(0,0,rows,cols) * s1, m1 * s1); +} + +void test_eigen2_linearstructure() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( linearStructure(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( linearStructure(Matrix2f()) ); + CALL_SUBTEST_3( linearStructure(Vector3d()) ); + CALL_SUBTEST_4( linearStructure(Matrix4d()) ); + CALL_SUBTEST_5( linearStructure(MatrixXcf(3, 3)) ); + CALL_SUBTEST_6( linearStructure(MatrixXf(8, 12)) ); + CALL_SUBTEST_7( linearStructure(MatrixXi(8, 12)) ); + CALL_SUBTEST_8( linearStructure(MatrixXcd(20, 20)) ); + } +} diff --git a/test/eigen2/eigen2_lu.cpp b/test/eigen2/eigen2_lu.cpp new file mode 100644 index 000000000..e993b1c7c --- /dev/null +++ b/test/eigen2/eigen2_lu.cpp @@ -0,0 +1,122 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/LU> + +template<typename Derived> +void doSomeRankPreservingOperations(Eigen::MatrixBase<Derived>& m) +{ + typedef typename Derived::RealScalar RealScalar; + for(int a = 0; a < 3*(m.rows()+m.cols()); a++) + { + RealScalar d = Eigen::ei_random<RealScalar>(-1,1); + int i = Eigen::ei_random<int>(0,m.rows()-1); // i is a random row number + int j; + do { + j = Eigen::ei_random<int>(0,m.rows()-1); + } while (i==j); // j is another one (must be different) + m.row(i) += d * m.row(j); + + i = Eigen::ei_random<int>(0,m.cols()-1); // i is a random column number + do { + j = Eigen::ei_random<int>(0,m.cols()-1); + } while (i==j); // j is another one (must be different) + m.col(i) += d * m.col(j); + } +} + +template<typename MatrixType> void lu_non_invertible() +{ + /* this test covers the following files: + LU.h + */ + // NOTE there seems to be a problem with too small sizes -- could easily lie in the doSomeRankPreservingOperations function + int rows = ei_random<int>(20,200), cols = ei_random<int>(20,200), cols2 = ei_random<int>(20,200); + int rank = ei_random<int>(1, std::min(rows, cols)-1); + + MatrixType m1(rows, cols), m2(cols, cols2), m3(rows, cols2), k(1,1); + m1 = MatrixType::Random(rows,cols); + if(rows <= cols) + for(int i = rank; i < rows; i++) m1.row(i).setZero(); + else + for(int i = rank; i < cols; i++) m1.col(i).setZero(); + doSomeRankPreservingOperations(m1); + + LU<MatrixType> lu(m1); + typename LU<MatrixType>::KernelResultType m1kernel = lu.kernel(); + typename LU<MatrixType>::ImageResultType m1image = lu.image(); + + VERIFY(rank == lu.rank()); + VERIFY(cols - lu.rank() == lu.dimensionOfKernel()); + VERIFY(!lu.isInjective()); + VERIFY(!lu.isInvertible()); + VERIFY(lu.isSurjective() == (lu.rank() == rows)); + VERIFY((m1 * m1kernel).isMuchSmallerThan(m1)); + VERIFY(m1image.lu().rank() == rank); + MatrixType sidebyside(m1.rows(), m1.cols() + m1image.cols()); + sidebyside << m1, m1image; + VERIFY(sidebyside.lu().rank() == rank); + m2 = MatrixType::Random(cols,cols2); + m3 = m1*m2; + m2 = MatrixType::Random(cols,cols2); + lu.solve(m3, &m2); + VERIFY_IS_APPROX(m3, m1*m2); + /* solve now always returns true + m3 = MatrixType::Random(rows,cols2); + VERIFY(!lu.solve(m3, &m2)); + */ +} + +template<typename MatrixType> void lu_invertible() +{ + /* this test covers the following files: + LU.h + */ + typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar; + int size = ei_random<int>(10,200); + + MatrixType m1(size, size), m2(size, size), m3(size, size); + m1 = MatrixType::Random(size,size); + + if (ei_is_same_type<RealScalar,float>::ret) + { + // let's build a matrix more stable to inverse + MatrixType a = MatrixType::Random(size,size*2); + m1 += a * a.adjoint(); + } + + LU<MatrixType> lu(m1); + VERIFY(0 == lu.dimensionOfKernel()); + VERIFY(size == lu.rank()); + VERIFY(lu.isInjective()); + VERIFY(lu.isSurjective()); + VERIFY(lu.isInvertible()); + VERIFY(lu.image().lu().isInvertible()); + m3 = MatrixType::Random(size,size); + lu.solve(m3, &m2); + VERIFY_IS_APPROX(m3, m1*m2); + VERIFY_IS_APPROX(m2, lu.inverse()*m3); + m3 = MatrixType::Random(size,size); + VERIFY(lu.solve(m3, &m2)); +} + +void test_eigen2_lu() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( lu_non_invertible<MatrixXf>() ); + CALL_SUBTEST_2( lu_non_invertible<MatrixXd>() ); + CALL_SUBTEST_3( lu_non_invertible<MatrixXcf>() ); + CALL_SUBTEST_4( lu_non_invertible<MatrixXcd>() ); + CALL_SUBTEST_1( lu_invertible<MatrixXf>() ); + CALL_SUBTEST_2( lu_invertible<MatrixXd>() ); + CALL_SUBTEST_3( lu_invertible<MatrixXcf>() ); + CALL_SUBTEST_4( lu_invertible<MatrixXcd>() ); + } +} diff --git a/test/eigen2/eigen2_map.cpp b/test/eigen2/eigen2_map.cpp new file mode 100644 index 000000000..4a1c4e11a --- /dev/null +++ b/test/eigen2/eigen2_map.cpp @@ -0,0 +1,114 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2007-2010 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename VectorType> void map_class_vector(const VectorType& m) +{ + typedef typename VectorType::Scalar Scalar; + + int size = m.size(); + + // test Map.h + Scalar* array1 = ei_aligned_new<Scalar>(size); + Scalar* array2 = ei_aligned_new<Scalar>(size); + Scalar* array3 = new Scalar[size+1]; + Scalar* array3unaligned = std::size_t(array3)%16 == 0 ? array3+1 : array3; + + Map<VectorType, Aligned>(array1, size) = VectorType::Random(size); + Map<VectorType>(array2, size) = Map<VectorType>(array1, size); + Map<VectorType>(array3unaligned, size) = Map<VectorType>((const Scalar*)array1, size); // test non-const-correctness support in eigen2 + VectorType ma1 = Map<VectorType>(array1, size); + VectorType ma2 = Map<VectorType, Aligned>(array2, size); + VectorType ma3 = Map<VectorType>(array3unaligned, size); + VERIFY_IS_APPROX(ma1, ma2); + VERIFY_IS_APPROX(ma1, ma3); + + ei_aligned_delete(array1, size); + ei_aligned_delete(array2, size); + delete[] array3; +} + +template<typename MatrixType> void map_class_matrix(const MatrixType& m) +{ + typedef typename MatrixType::Scalar Scalar; + + int rows = m.rows(), cols = m.cols(), size = rows*cols; + + // test Map.h + Scalar* array1 = ei_aligned_new<Scalar>(size); + for(int i = 0; i < size; i++) array1[i] = Scalar(1); + Scalar* array2 = ei_aligned_new<Scalar>(size); + for(int i = 0; i < size; i++) array2[i] = Scalar(1); + Scalar* array3 = new Scalar[size+1]; + for(int i = 0; i < size+1; i++) array3[i] = Scalar(1); + Scalar* array3unaligned = std::size_t(array3)%16 == 0 ? array3+1 : array3; + Map<MatrixType, Aligned>(array1, rows, cols) = MatrixType::Ones(rows,cols); + Map<MatrixType>(array2, rows, cols) = Map<MatrixType>((const Scalar*)array1, rows, cols); // test non-const-correctness support in eigen2 + Map<MatrixType>(array3unaligned, rows, cols) = Map<MatrixType>(array1, rows, cols); + MatrixType ma1 = Map<MatrixType>(array1, rows, cols); + MatrixType ma2 = Map<MatrixType, Aligned>(array2, rows, cols); + VERIFY_IS_APPROX(ma1, ma2); + MatrixType ma3 = Map<MatrixType>(array3unaligned, rows, cols); + VERIFY_IS_APPROX(ma1, ma3); + + ei_aligned_delete(array1, size); + ei_aligned_delete(array2, size); + delete[] array3; +} + +template<typename VectorType> void map_static_methods(const VectorType& m) +{ + typedef typename VectorType::Scalar Scalar; + + int size = m.size(); + + // test Map.h + Scalar* array1 = ei_aligned_new<Scalar>(size); + Scalar* array2 = ei_aligned_new<Scalar>(size); + Scalar* array3 = new Scalar[size+1]; + Scalar* array3unaligned = std::size_t(array3)%16 == 0 ? array3+1 : array3; + + VectorType::MapAligned(array1, size) = VectorType::Random(size); + VectorType::Map(array2, size) = VectorType::Map(array1, size); + VectorType::Map(array3unaligned, size) = VectorType::Map(array1, size); + VectorType ma1 = VectorType::Map(array1, size); + VectorType ma2 = VectorType::MapAligned(array2, size); + VectorType ma3 = VectorType::Map(array3unaligned, size); + VERIFY_IS_APPROX(ma1, ma2); + VERIFY_IS_APPROX(ma1, ma3); + + ei_aligned_delete(array1, size); + ei_aligned_delete(array2, size); + delete[] array3; +} + + +void test_eigen2_map() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( map_class_vector(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( map_class_vector(Vector4d()) ); + CALL_SUBTEST_3( map_class_vector(RowVector4f()) ); + CALL_SUBTEST_4( map_class_vector(VectorXcf(8)) ); + CALL_SUBTEST_5( map_class_vector(VectorXi(12)) ); + + CALL_SUBTEST_1( map_class_matrix(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( map_class_matrix(Matrix4d()) ); + CALL_SUBTEST_6( map_class_matrix(Matrix<float,3,5>()) ); + CALL_SUBTEST_4( map_class_matrix(MatrixXcf(ei_random<int>(1,10),ei_random<int>(1,10))) ); + CALL_SUBTEST_5( map_class_matrix(MatrixXi(ei_random<int>(1,10),ei_random<int>(1,10))) ); + + CALL_SUBTEST_1( map_static_methods(Matrix<double, 1, 1>()) ); + CALL_SUBTEST_2( map_static_methods(Vector3f()) ); + CALL_SUBTEST_7( map_static_methods(RowVector3d()) ); + CALL_SUBTEST_4( map_static_methods(VectorXcd(8)) ); + CALL_SUBTEST_5( map_static_methods(VectorXf(12)) ); + } +} diff --git a/test/eigen2/eigen2_meta.cpp b/test/eigen2/eigen2_meta.cpp new file mode 100644 index 000000000..1d01bd84d --- /dev/null +++ b/test/eigen2/eigen2_meta.cpp @@ -0,0 +1,60 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// 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" + +void test_eigen2_meta() +{ + typedef float & FloatRef; + typedef const float & ConstFloatRef; + + VERIFY((ei_meta_if<(3<4),ei_meta_true, ei_meta_false>::ret::ret)); + VERIFY(( ei_is_same_type<float,float>::ret)); + VERIFY((!ei_is_same_type<float,double>::ret)); + VERIFY((!ei_is_same_type<float,float&>::ret)); + VERIFY((!ei_is_same_type<float,const float&>::ret)); + + VERIFY(( ei_is_same_type<float,ei_cleantype<const float&>::type >::ret)); + VERIFY(( ei_is_same_type<float,ei_cleantype<const float*>::type >::ret)); + VERIFY(( ei_is_same_type<float,ei_cleantype<const float*&>::type >::ret)); + VERIFY(( ei_is_same_type<float,ei_cleantype<float**>::type >::ret)); + VERIFY(( ei_is_same_type<float,ei_cleantype<float**&>::type >::ret)); + VERIFY(( ei_is_same_type<float,ei_cleantype<float* const *&>::type >::ret)); + VERIFY(( ei_is_same_type<float,ei_cleantype<float* const>::type >::ret)); + + VERIFY(( ei_is_same_type<float*,ei_unconst<const float*>::type >::ret)); + VERIFY(( ei_is_same_type<float&,ei_unconst<const float&>::type >::ret)); + VERIFY(( ei_is_same_type<float&,ei_unconst<ConstFloatRef>::type >::ret)); + + VERIFY(( ei_is_same_type<float&,ei_unconst<float&>::type >::ret)); + VERIFY(( ei_is_same_type<float,ei_unref<float&>::type >::ret)); + VERIFY(( ei_is_same_type<const float,ei_unref<const float&>::type >::ret)); + VERIFY(( ei_is_same_type<float,ei_unpointer<float*>::type >::ret)); + VERIFY(( ei_is_same_type<const float,ei_unpointer<const float*>::type >::ret)); + VERIFY(( ei_is_same_type<float,ei_unpointer<float* const >::type >::ret)); + + VERIFY(ei_meta_sqrt<1>::ret == 1); + #define VERIFY_META_SQRT(X) VERIFY(ei_meta_sqrt<X>::ret == int(ei_sqrt(double(X)))) + VERIFY_META_SQRT(2); + VERIFY_META_SQRT(3); + VERIFY_META_SQRT(4); + VERIFY_META_SQRT(5); + VERIFY_META_SQRT(6); + VERIFY_META_SQRT(8); + VERIFY_META_SQRT(9); + VERIFY_META_SQRT(15); + VERIFY_META_SQRT(16); + VERIFY_META_SQRT(17); + VERIFY_META_SQRT(255); + VERIFY_META_SQRT(256); + VERIFY_META_SQRT(257); + VERIFY_META_SQRT(1023); + VERIFY_META_SQRT(1024); + VERIFY_META_SQRT(1025); +} diff --git a/test/eigen2/eigen2_miscmatrices.cpp b/test/eigen2/eigen2_miscmatrices.cpp new file mode 100644 index 000000000..8bbb20cc8 --- /dev/null +++ b/test/eigen2/eigen2_miscmatrices.cpp @@ -0,0 +1,48 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename MatrixType> void miscMatrices(const MatrixType& m) +{ + /* this test covers the following files: + DiagonalMatrix.h Ones.h + */ + + typedef typename MatrixType::Scalar Scalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<Scalar, 1, MatrixType::ColsAtCompileTime> RowVectorType; + int rows = m.rows(); + int cols = m.cols(); + + int r = ei_random<int>(0, rows-1), r2 = ei_random<int>(0, rows-1), c = ei_random<int>(0, cols-1); + VERIFY_IS_APPROX(MatrixType::Ones(rows,cols)(r,c), static_cast<Scalar>(1)); + MatrixType m1 = MatrixType::Ones(rows,cols); + VERIFY_IS_APPROX(m1(r,c), static_cast<Scalar>(1)); + VectorType v1 = VectorType::Random(rows); + v1[0]; + Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> + square = v1.asDiagonal(); + if(r==r2) VERIFY_IS_APPROX(square(r,r2), v1[r]); + else VERIFY_IS_MUCH_SMALLER_THAN(square(r,r2), static_cast<Scalar>(1)); + square = MatrixType::Zero(rows, rows); + square.diagonal() = VectorType::Ones(rows); + VERIFY_IS_APPROX(square, MatrixType::Identity(rows, rows)); +} + +void test_eigen2_miscmatrices() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( miscMatrices(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( miscMatrices(Matrix4d()) ); + CALL_SUBTEST_3( miscMatrices(MatrixXcf(3, 3)) ); + CALL_SUBTEST_4( miscMatrices(MatrixXi(8, 12)) ); + CALL_SUBTEST_5( miscMatrices(MatrixXcd(20, 20)) ); + } +} diff --git a/test/eigen2/eigen2_mixingtypes.cpp b/test/eigen2/eigen2_mixingtypes.cpp new file mode 100644 index 000000000..fb5ac5dda --- /dev/null +++ b/test/eigen2/eigen2_mixingtypes.cpp @@ -0,0 +1,77 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#ifndef EIGEN_NO_STATIC_ASSERT +#define EIGEN_NO_STATIC_ASSERT // turn static asserts into runtime asserts in order to check them +#endif + +#ifndef EIGEN_DONT_VECTORIZE +#define EIGEN_DONT_VECTORIZE // SSE intrinsics aren't designed to allow mixing types +#endif + +#include "main.h" + + +template<int SizeAtCompileType> void mixingtypes(int size = SizeAtCompileType) +{ + typedef Matrix<float, SizeAtCompileType, SizeAtCompileType> Mat_f; + typedef Matrix<double, SizeAtCompileType, SizeAtCompileType> Mat_d; + typedef Matrix<std::complex<float>, SizeAtCompileType, SizeAtCompileType> Mat_cf; + typedef Matrix<std::complex<double>, SizeAtCompileType, SizeAtCompileType> Mat_cd; + typedef Matrix<float, SizeAtCompileType, 1> Vec_f; + typedef Matrix<double, SizeAtCompileType, 1> Vec_d; + typedef Matrix<std::complex<float>, SizeAtCompileType, 1> Vec_cf; + typedef Matrix<std::complex<double>, SizeAtCompileType, 1> Vec_cd; + + Mat_f mf(size,size); + Mat_d md(size,size); + Mat_cf mcf(size,size); + Mat_cd mcd(size,size); + Vec_f vf(size,1); + Vec_d vd(size,1); + Vec_cf vcf(size,1); + Vec_cd vcd(size,1); + + mf+mf; + VERIFY_RAISES_ASSERT(mf+md); + VERIFY_RAISES_ASSERT(mf+mcf); + VERIFY_RAISES_ASSERT(vf=vd); + VERIFY_RAISES_ASSERT(vf+=vd); + VERIFY_RAISES_ASSERT(mcd=md); + + mf*mf; + md*mcd; + mcd*md; + mf*vcf; + mcf*vf; + mcf *= mf; + vcd = md*vcd; + vcf = mcf*vf; +#if 0 + // these are know generating hard build errors in eigen3 + VERIFY_RAISES_ASSERT(mf*md); + VERIFY_RAISES_ASSERT(mcf*mcd); + VERIFY_RAISES_ASSERT(mcf*vcd); + VERIFY_RAISES_ASSERT(vcf = mf*vf); + + vf.eigen2_dot(vf); + VERIFY_RAISES_ASSERT(vd.eigen2_dot(vf)); + VERIFY_RAISES_ASSERT(vcf.eigen2_dot(vf)); // yeah eventually we should allow this but i'm too lazy to make that change now in Dot.h + // especially as that might be rewritten as cwise product .sum() which would make that automatic. +#endif +} + +void test_eigen2_mixingtypes() +{ + // check that our operator new is indeed called: + CALL_SUBTEST_1(mixingtypes<3>()); + CALL_SUBTEST_2(mixingtypes<4>()); + CALL_SUBTEST_3(mixingtypes<Dynamic>(20)); +} diff --git a/test/eigen2/eigen2_newstdvector.cpp b/test/eigen2/eigen2_newstdvector.cpp new file mode 100644 index 000000000..5f9009901 --- /dev/null +++ b/test/eigen2/eigen2_newstdvector.cpp @@ -0,0 +1,149 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#define EIGEN_USE_NEW_STDVECTOR +#include "main.h" +#include <Eigen/StdVector> +#include <Eigen/Geometry> + +template<typename MatrixType> +void check_stdvector_matrix(const MatrixType& m) +{ + int rows = m.rows(); + int cols = m.cols(); + MatrixType x = MatrixType::Random(rows,cols), y = MatrixType::Random(rows,cols); + std::vector<MatrixType,Eigen::aligned_allocator<MatrixType> > v(10, MatrixType(rows,cols)), w(20, y); + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.resize(22,y); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((std::size_t)&(v[22]) == (std::size_t)&(v[21]) + sizeof(MatrixType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + MatrixType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(unsigned int i=23; i<v.size(); ++i) + { + VERIFY(v[i]==w[(i-23)%w.size()]); + } +} + +template<typename TransformType> +void check_stdvector_transform(const TransformType&) +{ + typedef typename TransformType::MatrixType MatrixType; + TransformType x(MatrixType::Random()), y(MatrixType::Random()); + std::vector<TransformType,Eigen::aligned_allocator<TransformType> > v(10), w(20, y); + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.resize(22,y); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((std::size_t)&(v[22]) == (std::size_t)&(v[21]) + sizeof(TransformType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + TransformType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(unsigned int i=23; i<v.size(); ++i) + { + VERIFY(v[i].matrix()==w[(i-23)%w.size()].matrix()); + } +} + +template<typename QuaternionType> +void check_stdvector_quaternion(const QuaternionType&) +{ + typedef typename QuaternionType::Coefficients Coefficients; + QuaternionType x(Coefficients::Random()), y(Coefficients::Random()); + std::vector<QuaternionType,Eigen::aligned_allocator<QuaternionType> > v(10), w(20, y); + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.resize(22,y); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((std::size_t)&(v[22]) == (std::size_t)&(v[21]) + sizeof(QuaternionType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + QuaternionType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(unsigned int i=23; i<v.size(); ++i) + { + VERIFY(v[i].coeffs()==w[(i-23)%w.size()].coeffs()); + } +} + +void test_eigen2_newstdvector() +{ + // some non vectorizable fixed sizes + CALL_SUBTEST_1(check_stdvector_matrix(Vector2f())); + CALL_SUBTEST_1(check_stdvector_matrix(Matrix3f())); + CALL_SUBTEST_1(check_stdvector_matrix(Matrix3d())); + + // some vectorizable fixed sizes + CALL_SUBTEST_2(check_stdvector_matrix(Matrix2f())); + CALL_SUBTEST_2(check_stdvector_matrix(Vector4f())); + CALL_SUBTEST_2(check_stdvector_matrix(Matrix4f())); + CALL_SUBTEST_2(check_stdvector_matrix(Matrix4d())); + + // some dynamic sizes + CALL_SUBTEST_3(check_stdvector_matrix(MatrixXd(1,1))); + CALL_SUBTEST_3(check_stdvector_matrix(VectorXd(20))); + CALL_SUBTEST_3(check_stdvector_matrix(RowVectorXf(20))); + CALL_SUBTEST_3(check_stdvector_matrix(MatrixXcf(10,10))); + + // some Transform + CALL_SUBTEST_4(check_stdvector_transform(Transform2f())); + CALL_SUBTEST_4(check_stdvector_transform(Transform3f())); + CALL_SUBTEST_4(check_stdvector_transform(Transform3d())); + //CALL_SUBTEST(check_stdvector_transform(Transform4d())); + + // some Quaternion + CALL_SUBTEST_5(check_stdvector_quaternion(Quaternionf())); + CALL_SUBTEST_5(check_stdvector_quaternion(Quaterniond())); +} diff --git a/test/eigen2/eigen2_nomalloc.cpp b/test/eigen2/eigen2_nomalloc.cpp new file mode 100644 index 000000000..e234abe4b --- /dev/null +++ b/test/eigen2/eigen2_nomalloc.cpp @@ -0,0 +1,63 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +// this hack is needed to make this file compiles with -pedantic (gcc) +#ifdef __GNUC__ +#define throw(X) +#endif +// discard stack allocation as that too bypasses malloc +#define EIGEN_STACK_ALLOCATION_LIMIT 0 +// any heap allocation will raise an assert +#define EIGEN_NO_MALLOC + +#include "main.h" + +template<typename MatrixType> void nomalloc(const MatrixType& m) +{ + /* this test check no dynamic memory allocation are issued with fixed-size matrices + */ + + typedef typename MatrixType::Scalar Scalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + int rows = m.rows(); + int cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols), + mzero = MatrixType::Zero(rows, cols), + identity = Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> + ::Identity(rows, rows), + square = Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> + ::Random(rows, rows); + VectorType v1 = VectorType::Random(rows), + v2 = VectorType::Random(rows), + vzero = VectorType::Zero(rows); + + Scalar s1 = ei_random<Scalar>(); + + int r = ei_random<int>(0, rows-1), + c = ei_random<int>(0, cols-1); + + VERIFY_IS_APPROX((m1+m2)*s1, s1*m1+s1*m2); + VERIFY_IS_APPROX((m1+m2)(r,c), (m1(r,c))+(m2(r,c))); + VERIFY_IS_APPROX(m1.cwise() * m1.block(0,0,rows,cols), m1.cwise() * m1); + VERIFY_IS_APPROX((m1*m1.transpose())*m2, m1*(m1.transpose()*m2)); +} + +void test_eigen2_nomalloc() +{ + // check that our operator new is indeed called: + VERIFY_RAISES_ASSERT(MatrixXd dummy = MatrixXd::Random(3,3)); + CALL_SUBTEST_1( nomalloc(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( nomalloc(Matrix4d()) ); + CALL_SUBTEST_3( nomalloc(Matrix<float,32,32>()) ); +} diff --git a/test/eigen2/eigen2_packetmath.cpp b/test/eigen2/eigen2_packetmath.cpp new file mode 100644 index 000000000..b1f325fe7 --- /dev/null +++ b/test/eigen2/eigen2_packetmath.cpp @@ -0,0 +1,132 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" + +// using namespace Eigen; + +template<typename Scalar> bool areApprox(const Scalar* a, const Scalar* b, int size) +{ + for (int i=0; i<size; ++i) + if (!ei_isApprox(a[i],b[i])) return false; + return true; +} + +#define CHECK_CWISE(REFOP, POP) { \ + for (int i=0; i<PacketSize; ++i) \ + ref[i] = REFOP(data1[i], data1[i+PacketSize]); \ + ei_pstore(data2, POP(ei_pload(data1), ei_pload(data1+PacketSize))); \ + VERIFY(areApprox(ref, data2, PacketSize) && #POP); \ +} + +#define REF_ADD(a,b) ((a)+(b)) +#define REF_SUB(a,b) ((a)-(b)) +#define REF_MUL(a,b) ((a)*(b)) +#define REF_DIV(a,b) ((a)/(b)) + +namespace std { + +template<> const complex<float>& min(const complex<float>& a, const complex<float>& b) +{ return a.real() < b.real() ? a : b; } + +template<> const complex<float>& max(const complex<float>& a, const complex<float>& b) +{ return a.real() < b.real() ? b : a; } + +} + +template<typename Scalar> void packetmath() +{ + typedef typename ei_packet_traits<Scalar>::type Packet; + const int PacketSize = ei_packet_traits<Scalar>::size; + + const int size = PacketSize*4; + EIGEN_ALIGN_128 Scalar data1[ei_packet_traits<Scalar>::size*4]; + EIGEN_ALIGN_128 Scalar data2[ei_packet_traits<Scalar>::size*4]; + EIGEN_ALIGN_128 Packet packets[PacketSize*2]; + EIGEN_ALIGN_128 Scalar ref[ei_packet_traits<Scalar>::size*4]; + for (int i=0; i<size; ++i) + { + data1[i] = ei_random<Scalar>(); + data2[i] = ei_random<Scalar>(); + } + + ei_pstore(data2, ei_pload(data1)); + VERIFY(areApprox(data1, data2, PacketSize) && "aligned load/store"); + + for (int offset=0; offset<PacketSize; ++offset) + { + ei_pstore(data2, ei_ploadu(data1+offset)); + VERIFY(areApprox(data1+offset, data2, PacketSize) && "ei_ploadu"); + } + + for (int offset=0; offset<PacketSize; ++offset) + { + ei_pstoreu(data2+offset, ei_pload(data1)); + VERIFY(areApprox(data1, data2+offset, PacketSize) && "ei_pstoreu"); + } + + for (int offset=0; offset<PacketSize; ++offset) + { + packets[0] = ei_pload(data1); + packets[1] = ei_pload(data1+PacketSize); + if (offset==0) ei_palign<0>(packets[0], packets[1]); + else if (offset==1) ei_palign<1>(packets[0], packets[1]); + else if (offset==2) ei_palign<2>(packets[0], packets[1]); + else if (offset==3) ei_palign<3>(packets[0], packets[1]); + ei_pstore(data2, packets[0]); + + for (int i=0; i<PacketSize; ++i) + ref[i] = data1[i+offset]; + + typedef Matrix<Scalar, PacketSize, 1> Vector; + VERIFY(areApprox(ref, data2, PacketSize) && "ei_palign"); + } + + CHECK_CWISE(REF_ADD, ei_padd); + CHECK_CWISE(REF_SUB, ei_psub); + CHECK_CWISE(REF_MUL, ei_pmul); + #ifndef EIGEN_VECTORIZE_ALTIVEC + if (!ei_is_same_type<Scalar,int>::ret) + CHECK_CWISE(REF_DIV, ei_pdiv); + #endif + CHECK_CWISE(std::min, ei_pmin); + CHECK_CWISE(std::max, ei_pmax); + + for (int i=0; i<PacketSize; ++i) + ref[i] = data1[0]; + ei_pstore(data2, ei_pset1(data1[0])); + VERIFY(areApprox(ref, data2, PacketSize) && "ei_pset1"); + + VERIFY(ei_isApprox(data1[0], ei_pfirst(ei_pload(data1))) && "ei_pfirst"); + + ref[0] = 0; + for (int i=0; i<PacketSize; ++i) + ref[0] += data1[i]; + VERIFY(ei_isApprox(ref[0], ei_predux(ei_pload(data1))) && "ei_predux"); + + for (int j=0; j<PacketSize; ++j) + { + ref[j] = 0; + for (int i=0; i<PacketSize; ++i) + ref[j] += data1[i+j*PacketSize]; + packets[j] = ei_pload(data1+j*PacketSize); + } + ei_pstore(data2, ei_preduxp(packets)); + VERIFY(areApprox(ref, data2, PacketSize) && "ei_preduxp"); +} + +void test_eigen2_packetmath() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( packetmath<float>() ); + CALL_SUBTEST_2( packetmath<double>() ); + CALL_SUBTEST_3( packetmath<int>() ); + CALL_SUBTEST_4( packetmath<std::complex<float> >() ); + } +} diff --git a/test/eigen2/eigen2_parametrizedline.cpp b/test/eigen2/eigen2_parametrizedline.cpp new file mode 100644 index 000000000..814728870 --- /dev/null +++ b/test/eigen2/eigen2_parametrizedline.cpp @@ -0,0 +1,62 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/Geometry> +#include <Eigen/LU> +#include <Eigen/QR> + +template<typename LineType> void parametrizedline(const LineType& _line) +{ + /* this test covers the following files: + ParametrizedLine.h + */ + + const int dim = _line.dim(); + typedef typename LineType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, LineType::AmbientDimAtCompileTime, 1> VectorType; + typedef Matrix<Scalar, LineType::AmbientDimAtCompileTime, + LineType::AmbientDimAtCompileTime> MatrixType; + + VectorType p0 = VectorType::Random(dim); + VectorType p1 = VectorType::Random(dim); + + VectorType d0 = VectorType::Random(dim).normalized(); + + LineType l0(p0, d0); + + Scalar s0 = ei_random<Scalar>(); + Scalar s1 = ei_abs(ei_random<Scalar>()); + + VERIFY_IS_MUCH_SMALLER_THAN( l0.distance(p0), RealScalar(1) ); + VERIFY_IS_MUCH_SMALLER_THAN( l0.distance(p0+s0*d0), RealScalar(1) ); + VERIFY_IS_APPROX( (l0.projection(p1)-p1).norm(), l0.distance(p1) ); + VERIFY_IS_MUCH_SMALLER_THAN( l0.distance(l0.projection(p1)), RealScalar(1) ); + VERIFY_IS_APPROX( Scalar(l0.distance((p0+s0*d0) + d0.unitOrthogonal() * s1)), s1 ); + + // casting + const int Dim = LineType::AmbientDimAtCompileTime; + typedef typename GetDifferentType<Scalar>::type OtherScalar; + ParametrizedLine<OtherScalar,Dim> hp1f = l0.template cast<OtherScalar>(); + VERIFY_IS_APPROX(hp1f.template cast<Scalar>(),l0); + ParametrizedLine<Scalar,Dim> hp1d = l0.template cast<Scalar>(); + VERIFY_IS_APPROX(hp1d.template cast<Scalar>(),l0); +} + +void test_eigen2_parametrizedline() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( parametrizedline(ParametrizedLine<float,2>()) ); + CALL_SUBTEST_2( parametrizedline(ParametrizedLine<float,3>()) ); + CALL_SUBTEST_3( parametrizedline(ParametrizedLine<double,4>()) ); + CALL_SUBTEST_4( parametrizedline(ParametrizedLine<std::complex<double>,5>()) ); + } +} diff --git a/test/eigen2/eigen2_prec_inverse_4x4.cpp b/test/eigen2/eigen2_prec_inverse_4x4.cpp new file mode 100644 index 000000000..8bfa55694 --- /dev/null +++ b/test/eigen2/eigen2_prec_inverse_4x4.cpp @@ -0,0 +1,84 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/LU> +#include <algorithm> + +template<typename T> std::string type_name() { return "other"; } +template<> std::string type_name<float>() { return "float"; } +template<> std::string type_name<double>() { return "double"; } +template<> std::string type_name<int>() { return "int"; } +template<> std::string type_name<std::complex<float> >() { return "complex<float>"; } +template<> std::string type_name<std::complex<double> >() { return "complex<double>"; } +template<> std::string type_name<std::complex<int> >() { return "complex<int>"; } + +#define EIGEN_DEBUG_VAR(x) std::cerr << #x << " = " << x << std::endl; + +template<typename T> inline typename NumTraits<T>::Real epsilon() +{ + return std::numeric_limits<typename NumTraits<T>::Real>::epsilon(); +} + +template<typename MatrixType> void inverse_permutation_4x4() +{ + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + Vector4i indices(0,1,2,3); + for(int i = 0; i < 24; ++i) + { + MatrixType m = MatrixType::Zero(); + m(indices(0),0) = 1; + m(indices(1),1) = 1; + m(indices(2),2) = 1; + m(indices(3),3) = 1; + MatrixType inv = m.inverse(); + double error = double( (m*inv-MatrixType::Identity()).norm() / epsilon<Scalar>() ); + VERIFY(error == 0.0); + std::next_permutation(indices.data(),indices.data()+4); + } +} + +template<typename MatrixType> void inverse_general_4x4(int repeat) +{ + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + double error_sum = 0., error_max = 0.; + for(int i = 0; i < repeat; ++i) + { + MatrixType m; + RealScalar absdet; + do { + m = MatrixType::Random(); + absdet = ei_abs(m.determinant()); + } while(absdet < 10 * epsilon<Scalar>()); + MatrixType inv = m.inverse(); + double error = double( (m*inv-MatrixType::Identity()).norm() * absdet / epsilon<Scalar>() ); + error_sum += error; + error_max = std::max(error_max, error); + } + std::cerr << "inverse_general_4x4, Scalar = " << type_name<Scalar>() << std::endl; + double error_avg = error_sum / repeat; + EIGEN_DEBUG_VAR(error_avg); + EIGEN_DEBUG_VAR(error_max); + VERIFY(error_avg < (NumTraits<Scalar>::IsComplex ? 8.0 : 1.25)); + VERIFY(error_max < (NumTraits<Scalar>::IsComplex ? 64.0 : 20.0)); +} + +void test_eigen2_prec_inverse_4x4() +{ + CALL_SUBTEST_1((inverse_permutation_4x4<Matrix4f>())); + CALL_SUBTEST_1(( inverse_general_4x4<Matrix4f>(200000 * g_repeat) )); + + CALL_SUBTEST_2((inverse_permutation_4x4<Matrix<double,4,4,RowMajor> >())); + CALL_SUBTEST_2(( inverse_general_4x4<Matrix<double,4,4,RowMajor> >(200000 * g_repeat) )); + + CALL_SUBTEST_3((inverse_permutation_4x4<Matrix4cf>())); + CALL_SUBTEST_3((inverse_general_4x4<Matrix4cf>(50000 * g_repeat))); +} diff --git a/test/eigen2/eigen2_product_large.cpp b/test/eigen2/eigen2_product_large.cpp new file mode 100644 index 000000000..5149ef748 --- /dev/null +++ b/test/eigen2/eigen2_product_large.cpp @@ -0,0 +1,45 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" + +void test_eigen2_product_large() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( product(MatrixXf(ei_random<int>(1,320), ei_random<int>(1,320))) ); + CALL_SUBTEST_2( product(MatrixXd(ei_random<int>(1,320), ei_random<int>(1,320))) ); + CALL_SUBTEST_3( product(MatrixXi(ei_random<int>(1,320), ei_random<int>(1,320))) ); + CALL_SUBTEST_4( product(MatrixXcf(ei_random<int>(1,50), ei_random<int>(1,50))) ); + CALL_SUBTEST_5( product(Matrix<float,Dynamic,Dynamic,RowMajor>(ei_random<int>(1,320), ei_random<int>(1,320))) ); + } + +#ifdef EIGEN_TEST_PART_6 + { + // test a specific issue in DiagonalProduct + int N = 1000000; + VectorXf v = VectorXf::Ones(N); + MatrixXf m = MatrixXf::Ones(N,3); + m = (v+v).asDiagonal() * m; + VERIFY_IS_APPROX(m, MatrixXf::Constant(N,3,2)); + } + + { + // test deferred resizing in Matrix::operator= + MatrixXf a = MatrixXf::Random(10,4), b = MatrixXf::Random(4,10), c = a; + VERIFY_IS_APPROX((a = a * b), (c * b).eval()); + } + + { + MatrixXf mat1(10,10); mat1.setRandom(); + MatrixXf mat2(32,10); mat2.setRandom(); + MatrixXf result = mat1.row(2)*mat2.transpose(); + VERIFY_IS_APPROX(result, (mat1.row(2)*mat2.transpose()).eval()); + } +#endif +} diff --git a/test/eigen2/eigen2_product_small.cpp b/test/eigen2/eigen2_product_small.cpp new file mode 100644 index 000000000..4cd8c102f --- /dev/null +++ b/test/eigen2/eigen2_product_small.cpp @@ -0,0 +1,22 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#define EIGEN_NO_STATIC_ASSERT +#include "product.h" + +void test_eigen2_product_small() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( product(Matrix<float, 3, 2>()) ); + CALL_SUBTEST_2( product(Matrix<int, 3, 5>()) ); + CALL_SUBTEST_3( product(Matrix3d()) ); + CALL_SUBTEST_4( product(Matrix4d()) ); + CALL_SUBTEST_5( product(Matrix4f()) ); + } +} diff --git a/test/eigen2/eigen2_qr.cpp b/test/eigen2/eigen2_qr.cpp new file mode 100644 index 000000000..76977e4c1 --- /dev/null +++ b/test/eigen2/eigen2_qr.cpp @@ -0,0 +1,69 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// 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" +#include <Eigen/QR> + +template<typename MatrixType> void qr(const MatrixType& m) +{ + /* this test covers the following files: + QR.h + */ + int rows = m.rows(); + int cols = m.cols(); + + typedef typename MatrixType::Scalar Scalar; + typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, MatrixType::ColsAtCompileTime> SquareMatrixType; + typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, 1> VectorType; + + MatrixType a = MatrixType::Random(rows,cols); + QR<MatrixType> qrOfA(a); + VERIFY_IS_APPROX(a, qrOfA.matrixQ() * qrOfA.matrixR()); + VERIFY_IS_NOT_APPROX(a+MatrixType::Identity(rows, cols), qrOfA.matrixQ() * qrOfA.matrixR()); + + #if 0 // eigenvalues module not yet ready + SquareMatrixType b = a.adjoint() * a; + + // check tridiagonalization + Tridiagonalization<SquareMatrixType> tridiag(b); + VERIFY_IS_APPROX(b, tridiag.matrixQ() * tridiag.matrixT() * tridiag.matrixQ().adjoint()); + + // check hessenberg decomposition + HessenbergDecomposition<SquareMatrixType> hess(b); + VERIFY_IS_APPROX(b, hess.matrixQ() * hess.matrixH() * hess.matrixQ().adjoint()); + VERIFY_IS_APPROX(tridiag.matrixT(), hess.matrixH()); + b = SquareMatrixType::Random(cols,cols); + hess.compute(b); + VERIFY_IS_APPROX(b, hess.matrixQ() * hess.matrixH() * hess.matrixQ().adjoint()); + #endif +} + +void test_eigen2_qr() +{ + for(int i = 0; i < 1; i++) { + CALL_SUBTEST_1( qr(Matrix2f()) ); + CALL_SUBTEST_2( qr(Matrix4d()) ); + CALL_SUBTEST_3( qr(MatrixXf(12,8)) ); + CALL_SUBTEST_4( qr(MatrixXcd(5,5)) ); + CALL_SUBTEST_4( qr(MatrixXcd(7,3)) ); + } + +#ifdef EIGEN_TEST_PART_5 + // small isFullRank test + { + Matrix3d mat; + mat << 1, 45, 1, 2, 2, 2, 1, 2, 3; + VERIFY(mat.qr().isFullRank()); + mat << 1, 1, 1, 2, 2, 2, 1, 2, 3; + //always returns true in eigen2support + //VERIFY(!mat.qr().isFullRank()); + } + +#endif +} diff --git a/test/eigen2/eigen2_qtvector.cpp b/test/eigen2/eigen2_qtvector.cpp new file mode 100644 index 000000000..6cfb58a26 --- /dev/null +++ b/test/eigen2/eigen2_qtvector.cpp @@ -0,0 +1,158 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#define EIGEN_WORK_AROUND_QT_BUG_CALLING_WRONG_OPERATOR_NEW_FIXED_IN_QT_4_5 + +#include "main.h" + +#include <Eigen/Geometry> +#include <Eigen/QtAlignedMalloc> + +#include <QtCore/QVector> + +template<typename MatrixType> +void check_qtvector_matrix(const MatrixType& m) +{ + int rows = m.rows(); + int cols = m.cols(); + MatrixType x = MatrixType::Random(rows,cols), y = MatrixType::Random(rows,cols); + QVector<MatrixType> v(10, MatrixType(rows,cols)), w(20, y); + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], y); + } + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.fill(y,22); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((size_t)&(v[22]) == (size_t)&(v[21]) + sizeof(MatrixType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + MatrixType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(int i=23; i<v.size(); ++i) + { + VERIFY(v[i]==w[(i-23)%w.size()]); + } +} + +template<typename TransformType> +void check_qtvector_transform(const TransformType&) +{ + typedef typename TransformType::MatrixType MatrixType; + TransformType x(MatrixType::Random()), y(MatrixType::Random()); + QVector<TransformType> v(10), w(20, y); + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.fill(y,22); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((size_t)&(v[22]) == (size_t)&(v[21]) + sizeof(TransformType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + TransformType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(unsigned int i=23; int(i)<v.size(); ++i) + { + VERIFY(v[i].matrix()==w[(i-23)%w.size()].matrix()); + } +} + +template<typename QuaternionType> +void check_qtvector_quaternion(const QuaternionType&) +{ + typedef typename QuaternionType::Coefficients Coefficients; + QuaternionType x(Coefficients::Random()), y(Coefficients::Random()); + QVector<QuaternionType> v(10), w(20, y); + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.fill(y,22); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((size_t)&(v[22]) == (size_t)&(v[21]) + sizeof(QuaternionType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + QuaternionType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(unsigned int i=23; int(i)<v.size(); ++i) + { + VERIFY(v[i].coeffs()==w[(i-23)%w.size()].coeffs()); + } +} + +void test_eigen2_qtvector() +{ + // some non vectorizable fixed sizes + CALL_SUBTEST_1(check_qtvector_matrix(Vector2f())); + CALL_SUBTEST_1(check_qtvector_matrix(Matrix3f())); + CALL_SUBTEST_1(check_qtvector_matrix(Matrix3d())); + + // some vectorizable fixed sizes + CALL_SUBTEST_2(check_qtvector_matrix(Matrix2f())); + CALL_SUBTEST_2(check_qtvector_matrix(Vector4f())); + CALL_SUBTEST_2(check_qtvector_matrix(Matrix4f())); + CALL_SUBTEST_2(check_qtvector_matrix(Matrix4d())); + + // some dynamic sizes + CALL_SUBTEST_3(check_qtvector_matrix(MatrixXd(1,1))); + CALL_SUBTEST_3(check_qtvector_matrix(VectorXd(20))); + CALL_SUBTEST_3(check_qtvector_matrix(RowVectorXf(20))); + CALL_SUBTEST_3(check_qtvector_matrix(MatrixXcf(10,10))); + + // some Transform + CALL_SUBTEST_4(check_qtvector_transform(Transform2f())); + CALL_SUBTEST_4(check_qtvector_transform(Transform3f())); + CALL_SUBTEST_4(check_qtvector_transform(Transform3d())); + //CALL_SUBTEST_4(check_qtvector_transform(Transform4d())); + + // some Quaternion + CALL_SUBTEST_5(check_qtvector_quaternion(Quaternionf())); + CALL_SUBTEST_5(check_qtvector_quaternion(Quaternionf())); +} diff --git a/test/eigen2/eigen2_regression.cpp b/test/eigen2/eigen2_regression.cpp new file mode 100644 index 000000000..c68b58da8 --- /dev/null +++ b/test/eigen2/eigen2_regression.cpp @@ -0,0 +1,136 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/LeastSquares> + +template<typename VectorType, + typename HyperplaneType> +void makeNoisyCohyperplanarPoints(int numPoints, + VectorType **points, + HyperplaneType *hyperplane, + typename VectorType::Scalar noiseAmplitude) +{ + typedef typename VectorType::Scalar Scalar; + const int size = points[0]->size(); + // pick a random hyperplane, store the coefficients of its equation + hyperplane->coeffs().resize(size + 1); + for(int j = 0; j < size + 1; j++) + { + do { + hyperplane->coeffs().coeffRef(j) = ei_random<Scalar>(); + } while(ei_abs(hyperplane->coeffs().coeff(j)) < 0.5); + } + + // now pick numPoints random points on this hyperplane + for(int i = 0; i < numPoints; i++) + { + VectorType& cur_point = *(points[i]); + do + { + cur_point = VectorType::Random(size)/*.normalized()*/; + // project cur_point onto the hyperplane + Scalar x = - (hyperplane->coeffs().start(size).cwise()*cur_point).sum(); + cur_point *= hyperplane->coeffs().coeff(size) / x; + } while( cur_point.norm() < 0.5 + || cur_point.norm() > 2.0 ); + } + + // add some noise to these points + for(int i = 0; i < numPoints; i++ ) + *(points[i]) += noiseAmplitude * VectorType::Random(size); +} + +template<typename VectorType> +void check_linearRegression(int numPoints, + VectorType **points, + const VectorType& original, + typename VectorType::Scalar tolerance) +{ + int size = points[0]->size(); + assert(size==2); + VectorType result(size); + linearRegression(numPoints, points, &result, 1); + typename VectorType::Scalar error = (result - original).norm() / original.norm(); + VERIFY(ei_abs(error) < ei_abs(tolerance)); +} + +template<typename VectorType, + typename HyperplaneType> +void check_fitHyperplane(int numPoints, + VectorType **points, + const HyperplaneType& original, + typename VectorType::Scalar tolerance) +{ + int size = points[0]->size(); + HyperplaneType result(size); + fitHyperplane(numPoints, points, &result); + result.coeffs() *= original.coeffs().coeff(size)/result.coeffs().coeff(size); + typename VectorType::Scalar error = (result.coeffs() - original.coeffs()).norm() / original.coeffs().norm(); + std::cout << ei_abs(error) << " xxx " << ei_abs(tolerance) << std::endl; + VERIFY(ei_abs(error) < ei_abs(tolerance)); +} + +void test_eigen2_regression() +{ + for(int i = 0; i < g_repeat; i++) + { +#ifdef EIGEN_TEST_PART_1 + { + Vector2f points2f [1000]; + Vector2f *points2f_ptrs [1000]; + for(int i = 0; i < 1000; i++) points2f_ptrs[i] = &(points2f[i]); + Vector2f coeffs2f; + Hyperplane<float,2> coeffs3f; + makeNoisyCohyperplanarPoints(1000, points2f_ptrs, &coeffs3f, 0.01f); + coeffs2f[0] = -coeffs3f.coeffs()[0]/coeffs3f.coeffs()[1]; + coeffs2f[1] = -coeffs3f.coeffs()[2]/coeffs3f.coeffs()[1]; + CALL_SUBTEST(check_linearRegression(10, points2f_ptrs, coeffs2f, 0.05f)); + CALL_SUBTEST(check_linearRegression(100, points2f_ptrs, coeffs2f, 0.01f)); + CALL_SUBTEST(check_linearRegression(1000, points2f_ptrs, coeffs2f, 0.002f)); + } +#endif +#ifdef EIGEN_TEST_PART_2 + { + Vector2f points2f [1000]; + Vector2f *points2f_ptrs [1000]; + for(int i = 0; i < 1000; i++) points2f_ptrs[i] = &(points2f[i]); + Hyperplane<float,2> coeffs3f; + makeNoisyCohyperplanarPoints(1000, points2f_ptrs, &coeffs3f, 0.01f); + CALL_SUBTEST(check_fitHyperplane(10, points2f_ptrs, coeffs3f, 0.05f)); + CALL_SUBTEST(check_fitHyperplane(100, points2f_ptrs, coeffs3f, 0.01f)); + CALL_SUBTEST(check_fitHyperplane(1000, points2f_ptrs, coeffs3f, 0.002f)); + } +#endif +#ifdef EIGEN_TEST_PART_3 + { + Vector4d points4d [1000]; + Vector4d *points4d_ptrs [1000]; + for(int i = 0; i < 1000; i++) points4d_ptrs[i] = &(points4d[i]); + Hyperplane<double,4> coeffs5d; + makeNoisyCohyperplanarPoints(1000, points4d_ptrs, &coeffs5d, 0.01); + CALL_SUBTEST(check_fitHyperplane(10, points4d_ptrs, coeffs5d, 0.05)); + CALL_SUBTEST(check_fitHyperplane(100, points4d_ptrs, coeffs5d, 0.01)); + CALL_SUBTEST(check_fitHyperplane(1000, points4d_ptrs, coeffs5d, 0.002)); + } +#endif +#ifdef EIGEN_TEST_PART_4 + { + VectorXcd *points11cd_ptrs[1000]; + for(int i = 0; i < 1000; i++) points11cd_ptrs[i] = new VectorXcd(11); + Hyperplane<std::complex<double>,Dynamic> *coeffs12cd = new Hyperplane<std::complex<double>,Dynamic>(11); + makeNoisyCohyperplanarPoints(1000, points11cd_ptrs, coeffs12cd, 0.01); + CALL_SUBTEST(check_fitHyperplane(100, points11cd_ptrs, *coeffs12cd, 0.025)); + CALL_SUBTEST(check_fitHyperplane(1000, points11cd_ptrs, *coeffs12cd, 0.006)); + delete coeffs12cd; + for(int i = 0; i < 1000; i++) delete points11cd_ptrs[i]; + } +#endif + } +} diff --git a/test/eigen2/eigen2_sizeof.cpp b/test/eigen2/eigen2_sizeof.cpp new file mode 100644 index 000000000..ec1af5a06 --- /dev/null +++ b/test/eigen2/eigen2_sizeof.cpp @@ -0,0 +1,31 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// 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<typename MatrixType> void verifySizeOf(const MatrixType&) +{ + typedef typename MatrixType::Scalar Scalar; + if (MatrixType::RowsAtCompileTime!=Dynamic && MatrixType::ColsAtCompileTime!=Dynamic) + VERIFY(sizeof(MatrixType)==sizeof(Scalar)*MatrixType::SizeAtCompileTime); + else + VERIFY(sizeof(MatrixType)==sizeof(Scalar*) + 2 * sizeof(typename MatrixType::Index)); +} + +void test_eigen2_sizeof() +{ + CALL_SUBTEST( verifySizeOf(Matrix<float, 1, 1>()) ); + CALL_SUBTEST( verifySizeOf(Matrix4d()) ); + CALL_SUBTEST( verifySizeOf(Matrix<double, 4, 2>()) ); + CALL_SUBTEST( verifySizeOf(Matrix<bool, 7, 5>()) ); + CALL_SUBTEST( verifySizeOf(MatrixXcf(3, 3)) ); + CALL_SUBTEST( verifySizeOf(MatrixXi(8, 12)) ); + CALL_SUBTEST( verifySizeOf(MatrixXcd(20, 20)) ); + CALL_SUBTEST( verifySizeOf(Matrix<float, 100, 100>()) ); +} diff --git a/test/eigen2/eigen2_smallvectors.cpp b/test/eigen2/eigen2_smallvectors.cpp new file mode 100644 index 000000000..03962b17d --- /dev/null +++ b/test/eigen2/eigen2_smallvectors.cpp @@ -0,0 +1,42 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename Scalar> void smallVectors() +{ + typedef Matrix<Scalar, 1, 2> V2; + typedef Matrix<Scalar, 3, 1> V3; + typedef Matrix<Scalar, 1, 4> V4; + Scalar x1 = ei_random<Scalar>(), + x2 = ei_random<Scalar>(), + x3 = ei_random<Scalar>(), + x4 = ei_random<Scalar>(); + V2 v2(x1, x2); + V3 v3(x1, x2, x3); + V4 v4(x1, x2, x3, x4); + VERIFY_IS_APPROX(x1, v2.x()); + VERIFY_IS_APPROX(x1, v3.x()); + VERIFY_IS_APPROX(x1, v4.x()); + VERIFY_IS_APPROX(x2, v2.y()); + VERIFY_IS_APPROX(x2, v3.y()); + VERIFY_IS_APPROX(x2, v4.y()); + VERIFY_IS_APPROX(x3, v3.z()); + VERIFY_IS_APPROX(x3, v4.z()); + VERIFY_IS_APPROX(x4, v4.w()); +} + +void test_eigen2_smallvectors() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST( smallVectors<int>() ); + CALL_SUBTEST( smallVectors<float>() ); + CALL_SUBTEST( smallVectors<double>() ); + } +} diff --git a/test/eigen2/eigen2_sparse_basic.cpp b/test/eigen2/eigen2_sparse_basic.cpp new file mode 100644 index 000000000..049077670 --- /dev/null +++ b/test/eigen2/eigen2_sparse_basic.cpp @@ -0,0 +1,317 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Daniel Gomez Ferro <dgomezferro@gmail.com> +// +// 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 "sparse.h" + +template<typename SetterType,typename DenseType, typename Scalar, int Options> +bool test_random_setter(SparseMatrix<Scalar,Options>& sm, const DenseType& ref, const std::vector<Vector2i>& nonzeroCoords) +{ + typedef SparseMatrix<Scalar,Options> SparseType; + { + sm.setZero(); + SetterType w(sm); + std::vector<Vector2i> remaining = nonzeroCoords; + while(!remaining.empty()) + { + int i = ei_random<int>(0,remaining.size()-1); + w(remaining[i].x(),remaining[i].y()) = ref.coeff(remaining[i].x(),remaining[i].y()); + remaining[i] = remaining.back(); + remaining.pop_back(); + } + } + return sm.isApprox(ref); +} + +template<typename SetterType,typename DenseType, typename T> +bool test_random_setter(DynamicSparseMatrix<T>& sm, const DenseType& ref, const std::vector<Vector2i>& nonzeroCoords) +{ + sm.setZero(); + std::vector<Vector2i> remaining = nonzeroCoords; + while(!remaining.empty()) + { + int i = ei_random<int>(0,remaining.size()-1); + sm.coeffRef(remaining[i].x(),remaining[i].y()) = ref.coeff(remaining[i].x(),remaining[i].y()); + remaining[i] = remaining.back(); + remaining.pop_back(); + } + return sm.isApprox(ref); +} + +template<typename SparseMatrixType> void sparse_basic(const SparseMatrixType& ref) +{ + const int rows = ref.rows(); + const int cols = ref.cols(); + typedef typename SparseMatrixType::Scalar Scalar; + enum { Flags = SparseMatrixType::Flags }; + + double density = std::max(8./(rows*cols), 0.01); + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; + typedef Matrix<Scalar,Dynamic,1> DenseVector; + Scalar eps = 1e-6; + + SparseMatrixType m(rows, cols); + DenseMatrix refMat = DenseMatrix::Zero(rows, cols); + DenseVector vec1 = DenseVector::Random(rows); + Scalar s1 = ei_random<Scalar>(); + + std::vector<Vector2i> zeroCoords; + std::vector<Vector2i> nonzeroCoords; + initSparse<Scalar>(density, refMat, m, 0, &zeroCoords, &nonzeroCoords); + + if (zeroCoords.size()==0 || nonzeroCoords.size()==0) + return; + + // test coeff and coeffRef + for (int i=0; i<(int)zeroCoords.size(); ++i) + { + VERIFY_IS_MUCH_SMALLER_THAN( m.coeff(zeroCoords[i].x(),zeroCoords[i].y()), eps ); + if(ei_is_same_type<SparseMatrixType,SparseMatrix<Scalar,Flags> >::ret) + VERIFY_RAISES_ASSERT( m.coeffRef(zeroCoords[0].x(),zeroCoords[0].y()) = 5 ); + } + VERIFY_IS_APPROX(m, refMat); + + m.coeffRef(nonzeroCoords[0].x(), nonzeroCoords[0].y()) = Scalar(5); + refMat.coeffRef(nonzeroCoords[0].x(), nonzeroCoords[0].y()) = Scalar(5); + + VERIFY_IS_APPROX(m, refMat); + /* + // test InnerIterators and Block expressions + for (int t=0; t<10; ++t) + { + int j = ei_random<int>(0,cols-1); + int i = ei_random<int>(0,rows-1); + int w = ei_random<int>(1,cols-j-1); + int h = ei_random<int>(1,rows-i-1); + +// VERIFY_IS_APPROX(m.block(i,j,h,w), refMat.block(i,j,h,w)); + for(int c=0; c<w; c++) + { + VERIFY_IS_APPROX(m.block(i,j,h,w).col(c), refMat.block(i,j,h,w).col(c)); + for(int r=0; r<h; r++) + { +// VERIFY_IS_APPROX(m.block(i,j,h,w).col(c).coeff(r), refMat.block(i,j,h,w).col(c).coeff(r)); + } + } +// for(int r=0; r<h; r++) +// { +// VERIFY_IS_APPROX(m.block(i,j,h,w).row(r), refMat.block(i,j,h,w).row(r)); +// for(int c=0; c<w; c++) +// { +// VERIFY_IS_APPROX(m.block(i,j,h,w).row(r).coeff(c), refMat.block(i,j,h,w).row(r).coeff(c)); +// } +// } + } + + for(int c=0; c<cols; c++) + { + VERIFY_IS_APPROX(m.col(c) + m.col(c), (m + m).col(c)); + VERIFY_IS_APPROX(m.col(c) + m.col(c), refMat.col(c) + refMat.col(c)); + } + + for(int r=0; r<rows; r++) + { + VERIFY_IS_APPROX(m.row(r) + m.row(r), (m + m).row(r)); + VERIFY_IS_APPROX(m.row(r) + m.row(r), refMat.row(r) + refMat.row(r)); + } + */ + + // test SparseSetters + // coherent setter + // TODO extend the MatrixSetter +// { +// m.setZero(); +// VERIFY_IS_NOT_APPROX(m, refMat); +// SparseSetter<SparseMatrixType, FullyCoherentAccessPattern> w(m); +// for (int i=0; i<nonzeroCoords.size(); ++i) +// { +// w->coeffRef(nonzeroCoords[i].x(),nonzeroCoords[i].y()) = refMat.coeff(nonzeroCoords[i].x(),nonzeroCoords[i].y()); +// } +// } +// VERIFY_IS_APPROX(m, refMat); + + // random setter +// { +// m.setZero(); +// VERIFY_IS_NOT_APPROX(m, refMat); +// SparseSetter<SparseMatrixType, RandomAccessPattern> w(m); +// std::vector<Vector2i> remaining = nonzeroCoords; +// while(!remaining.empty()) +// { +// int i = ei_random<int>(0,remaining.size()-1); +// w->coeffRef(remaining[i].x(),remaining[i].y()) = refMat.coeff(remaining[i].x(),remaining[i].y()); +// remaining[i] = remaining.back(); +// remaining.pop_back(); +// } +// } +// VERIFY_IS_APPROX(m, refMat); + + VERIFY(( test_random_setter<RandomSetter<SparseMatrixType, StdMapTraits> >(m,refMat,nonzeroCoords) )); + #ifdef EIGEN_UNORDERED_MAP_SUPPORT + VERIFY(( test_random_setter<RandomSetter<SparseMatrixType, StdUnorderedMapTraits> >(m,refMat,nonzeroCoords) )); + #endif + #ifdef _DENSE_HASH_MAP_H_ + VERIFY(( test_random_setter<RandomSetter<SparseMatrixType, GoogleDenseHashMapTraits> >(m,refMat,nonzeroCoords) )); + #endif + #ifdef _SPARSE_HASH_MAP_H_ + VERIFY(( test_random_setter<RandomSetter<SparseMatrixType, GoogleSparseHashMapTraits> >(m,refMat,nonzeroCoords) )); + #endif + + // test fillrand + { + DenseMatrix m1(rows,cols); + m1.setZero(); + SparseMatrixType m2(rows,cols); + m2.startFill(); + for (int j=0; j<cols; ++j) + { + for (int k=0; k<rows/2; ++k) + { + int i = ei_random<int>(0,rows-1); + if (m1.coeff(i,j)==Scalar(0)) + m2.fillrand(i,j) = m1(i,j) = ei_random<Scalar>(); + } + } + m2.endFill(); + VERIFY_IS_APPROX(m2,m1); + } + + // test RandomSetter + /*{ + SparseMatrixType m1(rows,cols), m2(rows,cols); + DenseMatrix refM1 = DenseMatrix::Zero(rows, rows); + initSparse<Scalar>(density, refM1, m1); + { + Eigen::RandomSetter<SparseMatrixType > setter(m2); + for (int j=0; j<m1.outerSize(); ++j) + for (typename SparseMatrixType::InnerIterator i(m1,j); i; ++i) + setter(i.index(), j) = i.value(); + } + VERIFY_IS_APPROX(m1, m2); + }*/ +// std::cerr << m.transpose() << "\n\n" << refMat.transpose() << "\n\n"; +// VERIFY_IS_APPROX(m, refMat); + + // test basic computations + { + DenseMatrix refM1 = DenseMatrix::Zero(rows, rows); + DenseMatrix refM2 = DenseMatrix::Zero(rows, rows); + DenseMatrix refM3 = DenseMatrix::Zero(rows, rows); + DenseMatrix refM4 = DenseMatrix::Zero(rows, rows); + SparseMatrixType m1(rows, rows); + SparseMatrixType m2(rows, rows); + SparseMatrixType m3(rows, rows); + SparseMatrixType m4(rows, rows); + initSparse<Scalar>(density, refM1, m1); + initSparse<Scalar>(density, refM2, m2); + initSparse<Scalar>(density, refM3, m3); + initSparse<Scalar>(density, refM4, m4); + + VERIFY_IS_APPROX(m1+m2, refM1+refM2); + VERIFY_IS_APPROX(m1+m2+m3, refM1+refM2+refM3); + VERIFY_IS_APPROX(m3.cwise()*(m1+m2), refM3.cwise()*(refM1+refM2)); + VERIFY_IS_APPROX(m1*s1-m2, refM1*s1-refM2); + + VERIFY_IS_APPROX(m1*=s1, refM1*=s1); + VERIFY_IS_APPROX(m1/=s1, refM1/=s1); + + VERIFY_IS_APPROX(m1+=m2, refM1+=refM2); + VERIFY_IS_APPROX(m1-=m2, refM1-=refM2); + + VERIFY_IS_APPROX(m1.col(0).eigen2_dot(refM2.row(0)), refM1.col(0).eigen2_dot(refM2.row(0))); + + refM4.setRandom(); + // sparse cwise* dense + VERIFY_IS_APPROX(m3.cwise()*refM4, refM3.cwise()*refM4); +// VERIFY_IS_APPROX(m3.cwise()/refM4, refM3.cwise()/refM4); + } + + // test innerVector() + { + DenseMatrix refMat2 = DenseMatrix::Zero(rows, rows); + SparseMatrixType m2(rows, rows); + initSparse<Scalar>(density, refMat2, m2); + int j0 = ei_random(0,rows-1); + int j1 = ei_random(0,rows-1); + VERIFY_IS_APPROX(m2.innerVector(j0), refMat2.col(j0)); + VERIFY_IS_APPROX(m2.innerVector(j0)+m2.innerVector(j1), refMat2.col(j0)+refMat2.col(j1)); + //m2.innerVector(j0) = 2*m2.innerVector(j1); + //refMat2.col(j0) = 2*refMat2.col(j1); + //VERIFY_IS_APPROX(m2, refMat2); + } + + // test innerVectors() + { + DenseMatrix refMat2 = DenseMatrix::Zero(rows, rows); + SparseMatrixType m2(rows, rows); + initSparse<Scalar>(density, refMat2, m2); + int j0 = ei_random(0,rows-2); + int j1 = ei_random(0,rows-2); + int n0 = ei_random<int>(1,rows-std::max(j0,j1)); + VERIFY_IS_APPROX(m2.innerVectors(j0,n0), refMat2.block(0,j0,rows,n0)); + VERIFY_IS_APPROX(m2.innerVectors(j0,n0)+m2.innerVectors(j1,n0), + refMat2.block(0,j0,rows,n0)+refMat2.block(0,j1,rows,n0)); + //m2.innerVectors(j0,n0) = m2.innerVectors(j0,n0) + m2.innerVectors(j1,n0); + //refMat2.block(0,j0,rows,n0) = refMat2.block(0,j0,rows,n0) + refMat2.block(0,j1,rows,n0); + } + + // test transpose + { + DenseMatrix refMat2 = DenseMatrix::Zero(rows, rows); + SparseMatrixType m2(rows, rows); + initSparse<Scalar>(density, refMat2, m2); + VERIFY_IS_APPROX(m2.transpose().eval(), refMat2.transpose().eval()); + VERIFY_IS_APPROX(m2.transpose(), refMat2.transpose()); + } + + // test prune + { + SparseMatrixType m2(rows, rows); + DenseMatrix refM2(rows, rows); + refM2.setZero(); + int countFalseNonZero = 0; + int countTrueNonZero = 0; + m2.startFill(); + for (int j=0; j<m2.outerSize(); ++j) + for (int i=0; i<m2.innerSize(); ++i) + { + float x = ei_random<float>(0,1); + if (x<0.1) + { + // do nothing + } + else if (x<0.5) + { + countFalseNonZero++; + m2.fill(i,j) = Scalar(0); + } + else + { + countTrueNonZero++; + m2.fill(i,j) = refM2(i,j) = Scalar(1); + } + } + m2.endFill(); + VERIFY(countFalseNonZero+countTrueNonZero == m2.nonZeros()); + VERIFY_IS_APPROX(m2, refM2); + m2.prune(1); + VERIFY(countTrueNonZero==m2.nonZeros()); + VERIFY_IS_APPROX(m2, refM2); + } +} + +void test_eigen2_sparse_basic() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( sparse_basic(SparseMatrix<double>(8, 8)) ); + CALL_SUBTEST_2( sparse_basic(SparseMatrix<std::complex<double> >(16, 16)) ); + CALL_SUBTEST_1( sparse_basic(SparseMatrix<double>(33, 33)) ); + + CALL_SUBTEST_3( sparse_basic(DynamicSparseMatrix<double>(8, 8)) ); + } +} diff --git a/test/eigen2/eigen2_sparse_product.cpp b/test/eigen2/eigen2_sparse_product.cpp new file mode 100644 index 000000000..d28e76dff --- /dev/null +++ b/test/eigen2/eigen2_sparse_product.cpp @@ -0,0 +1,115 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Daniel Gomez Ferro <dgomezferro@gmail.com> +// +// 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 "sparse.h" + +template<typename SparseMatrixType> void sparse_product(const SparseMatrixType& ref) +{ + const int rows = ref.rows(); + const int cols = ref.cols(); + typedef typename SparseMatrixType::Scalar Scalar; + enum { Flags = SparseMatrixType::Flags }; + + double density = std::max(8./(rows*cols), 0.01); + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; + typedef Matrix<Scalar,Dynamic,1> DenseVector; + + // test matrix-matrix product + { + DenseMatrix refMat2 = DenseMatrix::Zero(rows, rows); + DenseMatrix refMat3 = DenseMatrix::Zero(rows, rows); + DenseMatrix refMat4 = DenseMatrix::Zero(rows, rows); + DenseMatrix dm4 = DenseMatrix::Zero(rows, rows); + SparseMatrixType m2(rows, rows); + SparseMatrixType m3(rows, rows); + SparseMatrixType m4(rows, rows); + initSparse<Scalar>(density, refMat2, m2); + initSparse<Scalar>(density, refMat3, m3); + initSparse<Scalar>(density, refMat4, m4); + VERIFY_IS_APPROX(m4=m2*m3, refMat4=refMat2*refMat3); + VERIFY_IS_APPROX(m4=m2.transpose()*m3, refMat4=refMat2.transpose()*refMat3); + VERIFY_IS_APPROX(m4=m2.transpose()*m3.transpose(), refMat4=refMat2.transpose()*refMat3.transpose()); + VERIFY_IS_APPROX(m4=m2*m3.transpose(), refMat4=refMat2*refMat3.transpose()); + + // sparse * dense + VERIFY_IS_APPROX(dm4=m2*refMat3, refMat4=refMat2*refMat3); + VERIFY_IS_APPROX(dm4=m2*refMat3.transpose(), refMat4=refMat2*refMat3.transpose()); + VERIFY_IS_APPROX(dm4=m2.transpose()*refMat3, refMat4=refMat2.transpose()*refMat3); + VERIFY_IS_APPROX(dm4=m2.transpose()*refMat3.transpose(), refMat4=refMat2.transpose()*refMat3.transpose()); + + // dense * sparse + VERIFY_IS_APPROX(dm4=refMat2*m3, refMat4=refMat2*refMat3); + VERIFY_IS_APPROX(dm4=refMat2*m3.transpose(), refMat4=refMat2*refMat3.transpose()); + VERIFY_IS_APPROX(dm4=refMat2.transpose()*m3, refMat4=refMat2.transpose()*refMat3); + VERIFY_IS_APPROX(dm4=refMat2.transpose()*m3.transpose(), refMat4=refMat2.transpose()*refMat3.transpose()); + + VERIFY_IS_APPROX(m3=m3*m3, refMat3=refMat3*refMat3); + } + + // test matrix - diagonal product + if(false) // it compiles, but the precision is terrible. probably doesn't matter in this branch.... + { + DenseMatrix refM2 = DenseMatrix::Zero(rows, rows); + DenseMatrix refM3 = DenseMatrix::Zero(rows, rows); + DiagonalMatrix<DenseVector> d1(DenseVector::Random(rows)); + SparseMatrixType m2(rows, rows); + SparseMatrixType m3(rows, rows); + initSparse<Scalar>(density, refM2, m2); + initSparse<Scalar>(density, refM3, m3); + VERIFY_IS_APPROX(m3=m2*d1, refM3=refM2*d1); + VERIFY_IS_APPROX(m3=m2.transpose()*d1, refM3=refM2.transpose()*d1); + VERIFY_IS_APPROX(m3=d1*m2, refM3=d1*refM2); + VERIFY_IS_APPROX(m3=d1*m2.transpose(), refM3=d1 * refM2.transpose()); + } + + // test self adjoint products + { + DenseMatrix b = DenseMatrix::Random(rows, rows); + DenseMatrix x = DenseMatrix::Random(rows, rows); + DenseMatrix refX = DenseMatrix::Random(rows, rows); + DenseMatrix refUp = DenseMatrix::Zero(rows, rows); + DenseMatrix refLo = DenseMatrix::Zero(rows, rows); + DenseMatrix refS = DenseMatrix::Zero(rows, rows); + SparseMatrixType mUp(rows, rows); + SparseMatrixType mLo(rows, rows); + SparseMatrixType mS(rows, rows); + do { + initSparse<Scalar>(density, refUp, mUp, ForceRealDiag|/*ForceNonZeroDiag|*/MakeUpperTriangular); + } while (refUp.isZero()); + refLo = refUp.transpose().conjugate(); + mLo = mUp.transpose().conjugate(); + refS = refUp + refLo; + refS.diagonal() *= 0.5; + mS = mUp + mLo; + for (int k=0; k<mS.outerSize(); ++k) + for (typename SparseMatrixType::InnerIterator it(mS,k); it; ++it) + if (it.index() == k) + it.valueRef() *= 0.5; + + VERIFY_IS_APPROX(refS.adjoint(), refS); + VERIFY_IS_APPROX(mS.transpose().conjugate(), mS); + VERIFY_IS_APPROX(mS, refS); + VERIFY_IS_APPROX(x=mS*b, refX=refS*b); + VERIFY_IS_APPROX(x=mUp.template marked<UpperTriangular|SelfAdjoint>()*b, refX=refS*b); + VERIFY_IS_APPROX(x=mLo.template marked<LowerTriangular|SelfAdjoint>()*b, refX=refS*b); + VERIFY_IS_APPROX(x=mS.template marked<SelfAdjoint>()*b, refX=refS*b); + } + +} + +void test_eigen2_sparse_product() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( sparse_product(SparseMatrix<double>(8, 8)) ); + CALL_SUBTEST_2( sparse_product(SparseMatrix<std::complex<double> >(16, 16)) ); + CALL_SUBTEST_1( sparse_product(SparseMatrix<double>(33, 33)) ); + + CALL_SUBTEST_3( sparse_product(DynamicSparseMatrix<double>(8, 8)) ); + } +} diff --git a/test/eigen2/eigen2_sparse_solvers.cpp b/test/eigen2/eigen2_sparse_solvers.cpp new file mode 100644 index 000000000..3aef27ab4 --- /dev/null +++ b/test/eigen2/eigen2_sparse_solvers.cpp @@ -0,0 +1,200 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Daniel Gomez Ferro <dgomezferro@gmail.com> +// +// 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 "sparse.h" + +template<typename Scalar> void +initSPD(double density, + Matrix<Scalar,Dynamic,Dynamic>& refMat, + SparseMatrix<Scalar>& sparseMat) +{ + Matrix<Scalar,Dynamic,Dynamic> aux(refMat.rows(),refMat.cols()); + initSparse(density,refMat,sparseMat); + refMat = refMat * refMat.adjoint(); + for (int k=0; k<2; ++k) + { + initSparse(density,aux,sparseMat,ForceNonZeroDiag); + refMat += aux * aux.adjoint(); + } + sparseMat.startFill(); + for (int j=0 ; j<sparseMat.cols(); ++j) + for (int i=j ; i<sparseMat.rows(); ++i) + if (refMat(i,j)!=Scalar(0)) + sparseMat.fill(i,j) = refMat(i,j); + sparseMat.endFill(); +} + +template<typename Scalar> void sparse_solvers(int rows, int cols) +{ + double density = std::max(8./(rows*cols), 0.01); + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; + typedef Matrix<Scalar,Dynamic,1> DenseVector; + // Scalar eps = 1e-6; + + DenseVector vec1 = DenseVector::Random(rows); + + std::vector<Vector2i> zeroCoords; + std::vector<Vector2i> nonzeroCoords; + + // test triangular solver + { + DenseVector vec2 = vec1, vec3 = vec1; + SparseMatrix<Scalar> m2(rows, cols); + DenseMatrix refMat2 = DenseMatrix::Zero(rows, cols); + + // lower + initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeLowerTriangular, &zeroCoords, &nonzeroCoords); + VERIFY_IS_APPROX(refMat2.template marked<LowerTriangular>().solveTriangular(vec2), + m2.template marked<LowerTriangular>().solveTriangular(vec3)); + + // lower - transpose + initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeLowerTriangular, &zeroCoords, &nonzeroCoords); + VERIFY_IS_APPROX(refMat2.template marked<LowerTriangular>().transpose().solveTriangular(vec2), + m2.template marked<LowerTriangular>().transpose().solveTriangular(vec3)); + + // upper + initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeUpperTriangular, &zeroCoords, &nonzeroCoords); + VERIFY_IS_APPROX(refMat2.template marked<UpperTriangular>().solveTriangular(vec2), + m2.template marked<UpperTriangular>().solveTriangular(vec3)); + + // upper - transpose + initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeUpperTriangular, &zeroCoords, &nonzeroCoords); + VERIFY_IS_APPROX(refMat2.template marked<UpperTriangular>().transpose().solveTriangular(vec2), + m2.template marked<UpperTriangular>().transpose().solveTriangular(vec3)); + } + + // test LLT + { + // TODO fix the issue with complex (see SparseLLT::solveInPlace) + SparseMatrix<Scalar> m2(rows, cols); + DenseMatrix refMat2(rows, cols); + + DenseVector b = DenseVector::Random(cols); + DenseVector refX(cols), x(cols); + + initSPD(density, refMat2, m2); + + refMat2.llt().solve(b, &refX); + typedef SparseMatrix<Scalar,LowerTriangular|SelfAdjoint> SparseSelfAdjointMatrix; + if (!NumTraits<Scalar>::IsComplex) + { + x = b; + SparseLLT<SparseSelfAdjointMatrix> (m2).solveInPlace(x); + VERIFY(refX.isApprox(x,test_precision<Scalar>()) && "LLT: default"); + } + #ifdef EIGEN_CHOLMOD_SUPPORT + x = b; + SparseLLT<SparseSelfAdjointMatrix,Cholmod>(m2).solveInPlace(x); + VERIFY(refX.isApprox(x,test_precision<Scalar>()) && "LLT: cholmod"); + #endif + if (!NumTraits<Scalar>::IsComplex) + { + #ifdef EIGEN_TAUCS_SUPPORT + x = b; + SparseLLT<SparseSelfAdjointMatrix,Taucs>(m2,IncompleteFactorization).solveInPlace(x); + VERIFY(refX.isApprox(x,test_precision<Scalar>()) && "LLT: taucs (IncompleteFactorization)"); + x = b; + SparseLLT<SparseSelfAdjointMatrix,Taucs>(m2,SupernodalMultifrontal).solveInPlace(x); + VERIFY(refX.isApprox(x,test_precision<Scalar>()) && "LLT: taucs (SupernodalMultifrontal)"); + x = b; + SparseLLT<SparseSelfAdjointMatrix,Taucs>(m2,SupernodalLeftLooking).solveInPlace(x); + VERIFY(refX.isApprox(x,test_precision<Scalar>()) && "LLT: taucs (SupernodalLeftLooking)"); + #endif + } + } + + // test LDLT + if (!NumTraits<Scalar>::IsComplex) + { + // TODO fix the issue with complex (see SparseLDLT::solveInPlace) + SparseMatrix<Scalar> m2(rows, cols); + DenseMatrix refMat2(rows, cols); + + DenseVector b = DenseVector::Random(cols); + DenseVector refX(cols), x(cols); + + //initSPD(density, refMat2, m2); + initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeUpperTriangular, 0, 0); + refMat2 += refMat2.adjoint(); + refMat2.diagonal() *= 0.5; + + refMat2.ldlt().solve(b, &refX); + typedef SparseMatrix<Scalar,UpperTriangular|SelfAdjoint> SparseSelfAdjointMatrix; + x = b; + SparseLDLT<SparseSelfAdjointMatrix> ldlt(m2); + if (ldlt.succeeded()) + ldlt.solveInPlace(x); + VERIFY(refX.isApprox(x,test_precision<Scalar>()) && "LDLT: default"); + } + + // test LU + { + static int count = 0; + SparseMatrix<Scalar> m2(rows, cols); + DenseMatrix refMat2(rows, cols); + + DenseVector b = DenseVector::Random(cols); + DenseVector refX(cols), x(cols); + + initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag, &zeroCoords, &nonzeroCoords); + + LU<DenseMatrix> refLu(refMat2); + refLu.solve(b, &refX); + #if defined(EIGEN_SUPERLU_SUPPORT) || defined(EIGEN_UMFPACK_SUPPORT) + Scalar refDet = refLu.determinant(); + #endif + x.setZero(); + // // SparseLU<SparseMatrix<Scalar> > (m2).solve(b,&x); + // // VERIFY(refX.isApprox(x,test_precision<Scalar>()) && "LU: default"); + #ifdef EIGEN_SUPERLU_SUPPORT + { + x.setZero(); + SparseLU<SparseMatrix<Scalar>,SuperLU> slu(m2); + if (slu.succeeded()) + { + if (slu.solve(b,&x)) { + VERIFY(refX.isApprox(x,test_precision<Scalar>()) && "LU: SuperLU"); + } + // std::cerr << refDet << " == " << slu.determinant() << "\n"; + if (count==0) { + VERIFY_IS_APPROX(refDet,slu.determinant()); // FIXME det is not very stable for complex + } + } + } + #endif + #ifdef EIGEN_UMFPACK_SUPPORT + { + // check solve + x.setZero(); + SparseLU<SparseMatrix<Scalar>,UmfPack> slu(m2); + if (slu.succeeded()) { + if (slu.solve(b,&x)) { + if (count==0) { + VERIFY(refX.isApprox(x,test_precision<Scalar>()) && "LU: umfpack"); // FIXME solve is not very stable for complex + } + } + VERIFY_IS_APPROX(refDet,slu.determinant()); + // TODO check the extracted data + //std::cerr << slu.matrixL() << "\n"; + } + } + #endif + count++; + } + +} + +void test_eigen2_sparse_solvers() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( sparse_solvers<double>(8, 8) ); + CALL_SUBTEST_2( sparse_solvers<std::complex<double> >(16, 16) ); + CALL_SUBTEST_1( sparse_solvers<double>(101, 101) ); + } +} diff --git a/test/eigen2/eigen2_sparse_vector.cpp b/test/eigen2/eigen2_sparse_vector.cpp new file mode 100644 index 000000000..e6d2d77a1 --- /dev/null +++ b/test/eigen2/eigen2_sparse_vector.cpp @@ -0,0 +1,84 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Daniel Gomez Ferro <dgomezferro@gmail.com> +// +// 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 "sparse.h" + +template<typename Scalar> void sparse_vector(int rows, int cols) +{ + double densityMat = std::max(8./(rows*cols), 0.01); + double densityVec = std::max(8./float(rows), 0.1); + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; + typedef Matrix<Scalar,Dynamic,1> DenseVector; + typedef SparseVector<Scalar> SparseVectorType; + typedef SparseMatrix<Scalar> SparseMatrixType; + Scalar eps = 1e-6; + + SparseMatrixType m1(rows,cols); + SparseVectorType v1(rows), v2(rows), v3(rows); + DenseMatrix refM1 = DenseMatrix::Zero(rows, cols); + DenseVector refV1 = DenseVector::Random(rows), + refV2 = DenseVector::Random(rows), + refV3 = DenseVector::Random(rows); + + std::vector<int> zerocoords, nonzerocoords; + initSparse<Scalar>(densityVec, refV1, v1, &zerocoords, &nonzerocoords); + initSparse<Scalar>(densityMat, refM1, m1); + + initSparse<Scalar>(densityVec, refV2, v2); + initSparse<Scalar>(densityVec, refV3, v3); + + Scalar s1 = ei_random<Scalar>(); + + // test coeff and coeffRef + for (unsigned int i=0; i<zerocoords.size(); ++i) + { + VERIFY_IS_MUCH_SMALLER_THAN( v1.coeff(zerocoords[i]), eps ); + //VERIFY_RAISES_ASSERT( v1.coeffRef(zerocoords[i]) = 5 ); + } + { + VERIFY(int(nonzerocoords.size()) == v1.nonZeros()); + int j=0; + for (typename SparseVectorType::InnerIterator it(v1); it; ++it,++j) + { + VERIFY(nonzerocoords[j]==it.index()); + VERIFY(it.value()==v1.coeff(it.index())); + VERIFY(it.value()==refV1.coeff(it.index())); + } + } + VERIFY_IS_APPROX(v1, refV1); + + v1.coeffRef(nonzerocoords[0]) = Scalar(5); + refV1.coeffRef(nonzerocoords[0]) = Scalar(5); + VERIFY_IS_APPROX(v1, refV1); + + VERIFY_IS_APPROX(v1+v2, refV1+refV2); + VERIFY_IS_APPROX(v1+v2+v3, refV1+refV2+refV3); + + VERIFY_IS_APPROX(v1*s1-v2, refV1*s1-refV2); + + VERIFY_IS_APPROX(v1*=s1, refV1*=s1); + VERIFY_IS_APPROX(v1/=s1, refV1/=s1); + + VERIFY_IS_APPROX(v1+=v2, refV1+=refV2); + VERIFY_IS_APPROX(v1-=v2, refV1-=refV2); + + VERIFY_IS_APPROX(v1.eigen2_dot(v2), refV1.eigen2_dot(refV2)); + VERIFY_IS_APPROX(v1.eigen2_dot(refV2), refV1.eigen2_dot(refV2)); + +} + +void test_eigen2_sparse_vector() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( sparse_vector<double>(8, 8) ); + CALL_SUBTEST_2( sparse_vector<std::complex<double> >(16, 16) ); + CALL_SUBTEST_1( sparse_vector<double>(299, 535) ); + } +} + diff --git a/test/eigen2/eigen2_stdvector.cpp b/test/eigen2/eigen2_stdvector.cpp new file mode 100644 index 000000000..6ab05c20a --- /dev/null +++ b/test/eigen2/eigen2_stdvector.cpp @@ -0,0 +1,148 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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 <Eigen/StdVector> +#include "main.h" +#include <Eigen/Geometry> + +template<typename MatrixType> +void check_stdvector_matrix(const MatrixType& m) +{ + int rows = m.rows(); + int cols = m.cols(); + MatrixType x = MatrixType::Random(rows,cols), y = MatrixType::Random(rows,cols); + std::vector<MatrixType, aligned_allocator<MatrixType> > v(10, MatrixType(rows,cols)), w(20, y); + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.resize(22,y); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((std::size_t)&(v[22]) == (std::size_t)&(v[21]) + sizeof(MatrixType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + MatrixType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(unsigned int i=23; i<v.size(); ++i) + { + VERIFY(v[i]==w[(i-23)%w.size()]); + } +} + +template<typename TransformType> +void check_stdvector_transform(const TransformType&) +{ + typedef typename TransformType::MatrixType MatrixType; + TransformType x(MatrixType::Random()), y(MatrixType::Random()); + std::vector<TransformType, aligned_allocator<TransformType> > v(10), w(20, y); + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.resize(22,y); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((std::size_t)&(v[22]) == (std::size_t)&(v[21]) + sizeof(TransformType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + TransformType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(unsigned int i=23; i<v.size(); ++i) + { + VERIFY(v[i].matrix()==w[(i-23)%w.size()].matrix()); + } +} + +template<typename QuaternionType> +void check_stdvector_quaternion(const QuaternionType&) +{ + typedef typename QuaternionType::Coefficients Coefficients; + QuaternionType x(Coefficients::Random()), y(Coefficients::Random()); + std::vector<QuaternionType, aligned_allocator<QuaternionType> > v(10), w(20, y); + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.resize(22,y); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((std::size_t)&(v[22]) == (std::size_t)&(v[21]) + sizeof(QuaternionType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + QuaternionType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(unsigned int i=23; i<v.size(); ++i) + { + VERIFY(v[i].coeffs()==w[(i-23)%w.size()].coeffs()); + } +} + +void test_eigen2_stdvector() +{ + // some non vectorizable fixed sizes + CALL_SUBTEST_1(check_stdvector_matrix(Vector2f())); + CALL_SUBTEST_1(check_stdvector_matrix(Matrix3f())); + CALL_SUBTEST_1(check_stdvector_matrix(Matrix3d())); + + // some vectorizable fixed sizes + CALL_SUBTEST_2(check_stdvector_matrix(Matrix2f())); + CALL_SUBTEST_2(check_stdvector_matrix(Vector4f())); + CALL_SUBTEST_2(check_stdvector_matrix(Matrix4f())); + CALL_SUBTEST_2(check_stdvector_matrix(Matrix4d())); + + // some dynamic sizes + CALL_SUBTEST_3(check_stdvector_matrix(MatrixXd(1,1))); + CALL_SUBTEST_3(check_stdvector_matrix(VectorXd(20))); + CALL_SUBTEST_3(check_stdvector_matrix(RowVectorXf(20))); + CALL_SUBTEST_3(check_stdvector_matrix(MatrixXcf(10,10))); + + // some Transform + CALL_SUBTEST_4(check_stdvector_transform(Transform2f())); + CALL_SUBTEST_4(check_stdvector_transform(Transform3f())); + CALL_SUBTEST_4(check_stdvector_transform(Transform3d())); + //CALL_SUBTEST_4(check_stdvector_transform(Transform4d())); + + // some Quaternion + CALL_SUBTEST_5(check_stdvector_quaternion(Quaternionf())); + CALL_SUBTEST_5(check_stdvector_quaternion(Quaternionf())); +} diff --git a/test/eigen2/eigen2_submatrices.cpp b/test/eigen2/eigen2_submatrices.cpp new file mode 100644 index 000000000..c5d3f243d --- /dev/null +++ b/test/eigen2/eigen2_submatrices.cpp @@ -0,0 +1,148 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" + +// check minor separately in order to avoid the possible creation of a zero-sized +// array. Comes from a compilation error with gcc-3.4 or gcc-4 with -ansi -pedantic. +// Another solution would be to declare the array like this: T m_data[Size==0?1:Size]; in ei_matrix_storage +// but this is probably not bad to raise such an error at compile time... +template<typename Scalar, int _Rows, int _Cols> struct CheckMinor +{ + typedef Matrix<Scalar, _Rows, _Cols> MatrixType; + CheckMinor(MatrixType& m1, int r1, int c1) + { + int rows = m1.rows(); + int cols = m1.cols(); + + Matrix<Scalar, Dynamic, Dynamic> mi = m1.minor(0,0).eval(); + VERIFY_IS_APPROX(mi, m1.block(1,1,rows-1,cols-1)); + mi = m1.minor(r1,c1); + VERIFY_IS_APPROX(mi.transpose(), m1.transpose().minor(c1,r1)); + //check operator(), both constant and non-constant, on minor() + m1.minor(r1,c1)(0,0) = m1.minor(0,0)(0,0); + } +}; + +template<typename Scalar> struct CheckMinor<Scalar,1,1> +{ + typedef Matrix<Scalar, 1, 1> MatrixType; + CheckMinor(MatrixType&, int, int) {} +}; + +template<typename MatrixType> void submatrices(const MatrixType& m) +{ + /* this test covers the following files: + Row.h Column.h Block.h Minor.h DiagonalCoeffs.h + */ + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<Scalar, 1, MatrixType::ColsAtCompileTime> RowVectorType; + int rows = m.rows(); + int cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols), + mzero = MatrixType::Zero(rows, cols), + ones = MatrixType::Ones(rows, cols), + identity = Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> + ::Identity(rows, rows), + square = Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> + ::Random(rows, rows); + VectorType v1 = VectorType::Random(rows), + v2 = VectorType::Random(rows), + v3 = VectorType::Random(rows), + vzero = VectorType::Zero(rows); + + Scalar s1 = ei_random<Scalar>(); + + int r1 = ei_random<int>(0,rows-1); + int r2 = ei_random<int>(r1,rows-1); + int c1 = ei_random<int>(0,cols-1); + int c2 = ei_random<int>(c1,cols-1); + + //check row() and col() + VERIFY_IS_APPROX(m1.col(c1).transpose(), m1.transpose().row(c1)); + VERIFY_IS_APPROX(square.row(r1).eigen2_dot(m1.col(c1)), (square.lazy() * m1.conjugate())(r1,c1)); + //check operator(), both constant and non-constant, on row() and col() + m1.row(r1) += s1 * m1.row(r2); + m1.col(c1) += s1 * m1.col(c2); + + //check block() + Matrix<Scalar,Dynamic,Dynamic> b1(1,1); b1(0,0) = m1(r1,c1); + RowVectorType br1(m1.block(r1,0,1,cols)); + VectorType bc1(m1.block(0,c1,rows,1)); + VERIFY_IS_APPROX(b1, m1.block(r1,c1,1,1)); + VERIFY_IS_APPROX(m1.row(r1), br1); + VERIFY_IS_APPROX(m1.col(c1), bc1); + //check operator(), both constant and non-constant, on block() + m1.block(r1,c1,r2-r1+1,c2-c1+1) = s1 * m2.block(0, 0, r2-r1+1,c2-c1+1); + m1.block(r1,c1,r2-r1+1,c2-c1+1)(r2-r1,c2-c1) = m2.block(0, 0, r2-r1+1,c2-c1+1)(0,0); + + //check minor() + CheckMinor<Scalar, MatrixType::RowsAtCompileTime, MatrixType::ColsAtCompileTime> checkminor(m1,r1,c1); + + //check diagonal() + VERIFY_IS_APPROX(m1.diagonal(), m1.transpose().diagonal()); + m2.diagonal() = 2 * m1.diagonal(); + m2.diagonal()[0] *= 3; + VERIFY_IS_APPROX(m2.diagonal()[0], static_cast<Scalar>(6) * m1.diagonal()[0]); + + enum { + BlockRows = EIGEN_SIZE_MIN_PREFER_FIXED(MatrixType::RowsAtCompileTime,2), + BlockCols = EIGEN_SIZE_MIN_PREFER_FIXED(MatrixType::ColsAtCompileTime,5) + }; + if (rows>=5 && cols>=8) + { + // test fixed block() as lvalue + m1.template block<BlockRows,BlockCols>(1,1) *= s1; + // test operator() on fixed block() both as constant and non-constant + m1.template block<BlockRows,BlockCols>(1,1)(0, 3) = m1.template block<2,5>(1,1)(1,2); + // check that fixed block() and block() agree + Matrix<Scalar,Dynamic,Dynamic> b = m1.template block<BlockRows,BlockCols>(3,3); + VERIFY_IS_APPROX(b, m1.block(3,3,BlockRows,BlockCols)); + } + + if (rows>2) + { + // test sub vectors + VERIFY_IS_APPROX(v1.template start<2>(), v1.block(0,0,2,1)); + VERIFY_IS_APPROX(v1.template start<2>(), v1.start(2)); + VERIFY_IS_APPROX(v1.template start<2>(), v1.segment(0,2)); + VERIFY_IS_APPROX(v1.template start<2>(), v1.template segment<2>(0)); + int i = rows-2; + VERIFY_IS_APPROX(v1.template end<2>(), v1.block(i,0,2,1)); + VERIFY_IS_APPROX(v1.template end<2>(), v1.end(2)); + VERIFY_IS_APPROX(v1.template end<2>(), v1.segment(i,2)); + VERIFY_IS_APPROX(v1.template end<2>(), v1.template segment<2>(i)); + i = ei_random(0,rows-2); + VERIFY_IS_APPROX(v1.segment(i,2), v1.template segment<2>(i)); + } + + // stress some basic stuffs with block matrices + VERIFY(ei_real(ones.col(c1).sum()) == RealScalar(rows)); + VERIFY(ei_real(ones.row(r1).sum()) == RealScalar(cols)); + + VERIFY(ei_real(ones.col(c1).eigen2_dot(ones.col(c2))) == RealScalar(rows)); + VERIFY(ei_real(ones.row(r1).eigen2_dot(ones.row(r2))) == RealScalar(cols)); +} + +void test_eigen2_submatrices() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( submatrices(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( submatrices(Matrix4d()) ); + CALL_SUBTEST_3( submatrices(MatrixXcf(3, 3)) ); + CALL_SUBTEST_4( submatrices(MatrixXi(8, 12)) ); + CALL_SUBTEST_5( submatrices(MatrixXcd(20, 20)) ); + CALL_SUBTEST_6( submatrices(MatrixXf(20, 20)) ); + } +} diff --git a/test/eigen2/eigen2_sum.cpp b/test/eigen2/eigen2_sum.cpp new file mode 100644 index 000000000..b47057caa --- /dev/null +++ b/test/eigen2/eigen2_sum.cpp @@ -0,0 +1,71 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename MatrixType> void matrixSum(const MatrixType& m) +{ + typedef typename MatrixType::Scalar Scalar; + + int rows = m.rows(); + int cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols); + + VERIFY_IS_MUCH_SMALLER_THAN(MatrixType::Zero(rows, cols).sum(), Scalar(1)); + VERIFY_IS_APPROX(MatrixType::Ones(rows, cols).sum(), Scalar(float(rows*cols))); // the float() here to shut up excessive MSVC warning about int->complex conversion being lossy + Scalar x = Scalar(0); + for(int i = 0; i < rows; i++) for(int j = 0; j < cols; j++) x += m1(i,j); + VERIFY_IS_APPROX(m1.sum(), x); +} + +template<typename VectorType> void vectorSum(const VectorType& w) +{ + typedef typename VectorType::Scalar Scalar; + int size = w.size(); + + VectorType v = VectorType::Random(size); + for(int i = 1; i < size; i++) + { + Scalar s = Scalar(0); + for(int j = 0; j < i; j++) s += v[j]; + VERIFY_IS_APPROX(s, v.start(i).sum()); + } + + for(int i = 0; i < size-1; i++) + { + Scalar s = Scalar(0); + for(int j = i; j < size; j++) s += v[j]; + VERIFY_IS_APPROX(s, v.end(size-i).sum()); + } + + for(int i = 0; i < size/2; i++) + { + Scalar s = Scalar(0); + for(int j = i; j < size-i; j++) s += v[j]; + VERIFY_IS_APPROX(s, v.segment(i, size-2*i).sum()); + } +} + +void test_eigen2_sum() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( matrixSum(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( matrixSum(Matrix2f()) ); + CALL_SUBTEST_3( matrixSum(Matrix4d()) ); + CALL_SUBTEST_4( matrixSum(MatrixXcf(3, 3)) ); + CALL_SUBTEST_5( matrixSum(MatrixXf(8, 12)) ); + CALL_SUBTEST_6( matrixSum(MatrixXi(8, 12)) ); + } + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_5( vectorSum(VectorXf(5)) ); + CALL_SUBTEST_7( vectorSum(VectorXd(10)) ); + CALL_SUBTEST_5( vectorSum(VectorXf(33)) ); + } +} diff --git a/test/eigen2/eigen2_svd.cpp b/test/eigen2/eigen2_svd.cpp new file mode 100644 index 000000000..d4689a56f --- /dev/null +++ b/test/eigen2/eigen2_svd.cpp @@ -0,0 +1,87 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// 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" +#include <Eigen/SVD> + +template<typename MatrixType> void svd(const MatrixType& m) +{ + /* this test covers the following files: + SVD.h + */ + int rows = m.rows(); + int cols = m.cols(); + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + MatrixType a = MatrixType::Random(rows,cols); + Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> b = + Matrix<Scalar, MatrixType::RowsAtCompileTime, 1>::Random(rows,1); + Matrix<Scalar, MatrixType::ColsAtCompileTime, 1> x(cols,1), x2(cols,1); + + RealScalar largerEps = test_precision<RealScalar>(); + if (ei_is_same_type<RealScalar,float>::ret) + largerEps = 1e-3f; + + { + SVD<MatrixType> svd(a); + MatrixType sigma = MatrixType::Zero(rows,cols); + MatrixType matU = MatrixType::Zero(rows,rows); + sigma.block(0,0,cols,cols) = svd.singularValues().asDiagonal(); + matU.block(0,0,rows,cols) = svd.matrixU(); + VERIFY_IS_APPROX(a, matU * sigma * svd.matrixV().transpose()); + } + + + if (rows==cols) + { + if (ei_is_same_type<RealScalar,float>::ret) + { + MatrixType a1 = MatrixType::Random(rows,cols); + a += a * a.adjoint() + a1 * a1.adjoint(); + } + SVD<MatrixType> svd(a); + svd.solve(b, &x); + VERIFY_IS_APPROX(a * x,b); + } + + + if(rows==cols) + { + SVD<MatrixType> svd(a); + MatrixType unitary, positive; + svd.computeUnitaryPositive(&unitary, &positive); + VERIFY_IS_APPROX(unitary * unitary.adjoint(), MatrixType::Identity(unitary.rows(),unitary.rows())); + VERIFY_IS_APPROX(positive, positive.adjoint()); + for(int i = 0; i < rows; i++) VERIFY(positive.diagonal()[i] >= 0); // cheap necessary (not sufficient) condition for positivity + VERIFY_IS_APPROX(unitary*positive, a); + + svd.computePositiveUnitary(&positive, &unitary); + VERIFY_IS_APPROX(unitary * unitary.adjoint(), MatrixType::Identity(unitary.rows(),unitary.rows())); + VERIFY_IS_APPROX(positive, positive.adjoint()); + for(int i = 0; i < rows; i++) VERIFY(positive.diagonal()[i] >= 0); // cheap necessary (not sufficient) condition for positivity + VERIFY_IS_APPROX(positive*unitary, a); + } +} + +void test_eigen2_svd() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( svd(Matrix3f()) ); + CALL_SUBTEST_2( svd(Matrix4d()) ); + CALL_SUBTEST_3( svd(MatrixXf(7,7)) ); + CALL_SUBTEST_4( svd(MatrixXd(14,7)) ); + // complex are not implemented yet +// CALL_SUBTEST( svd(MatrixXcd(6,6)) ); +// CALL_SUBTEST( svd(MatrixXcf(3,3)) ); + SVD<MatrixXf> s; + MatrixXf m = MatrixXf::Random(10,1); + s.compute(m); + } +} diff --git a/test/eigen2/eigen2_swap.cpp b/test/eigen2/eigen2_swap.cpp new file mode 100644 index 000000000..f3a8846d9 --- /dev/null +++ b/test/eigen2/eigen2_swap.cpp @@ -0,0 +1,83 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#define EIGEN_NO_STATIC_ASSERT +#include "main.h" + +template<typename T> +struct other_matrix_type +{ + typedef int type; +}; + +template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols> +struct other_matrix_type<Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> > +{ + typedef Matrix<_Scalar, _Rows, _Cols, _Options^RowMajor, _MaxRows, _MaxCols> type; +}; + +template<typename MatrixType> void swap(const MatrixType& m) +{ + typedef typename other_matrix_type<MatrixType>::type OtherMatrixType; + typedef typename MatrixType::Scalar Scalar; + + ei_assert((!ei_is_same_type<MatrixType,OtherMatrixType>::ret)); + int rows = m.rows(); + int cols = m.cols(); + + // construct 3 matrix guaranteed to be distinct + MatrixType m1 = MatrixType::Random(rows,cols); + MatrixType m2 = MatrixType::Random(rows,cols) + Scalar(100) * MatrixType::Identity(rows,cols); + OtherMatrixType m3 = OtherMatrixType::Random(rows,cols) + Scalar(200) * OtherMatrixType::Identity(rows,cols); + + MatrixType m1_copy = m1; + MatrixType m2_copy = m2; + OtherMatrixType m3_copy = m3; + + // test swapping 2 matrices of same type + m1.swap(m2); + VERIFY_IS_APPROX(m1,m2_copy); + VERIFY_IS_APPROX(m2,m1_copy); + m1 = m1_copy; + m2 = m2_copy; + + // test swapping 2 matrices of different types + m1.swap(m3); + VERIFY_IS_APPROX(m1,m3_copy); + VERIFY_IS_APPROX(m3,m1_copy); + m1 = m1_copy; + m3 = m3_copy; + + // test swapping matrix with expression + m1.swap(m2.block(0,0,rows,cols)); + VERIFY_IS_APPROX(m1,m2_copy); + VERIFY_IS_APPROX(m2,m1_copy); + m1 = m1_copy; + m2 = m2_copy; + + // test swapping two expressions of different types + m1.transpose().swap(m3.transpose()); + VERIFY_IS_APPROX(m1,m3_copy); + VERIFY_IS_APPROX(m3,m1_copy); + m1 = m1_copy; + m3 = m3_copy; + + // test assertion on mismatching size -- matrix case + VERIFY_RAISES_ASSERT(m1.swap(m1.row(0))); + // test assertion on mismatching size -- xpr case + VERIFY_RAISES_ASSERT(m1.row(0).swap(m1)); +} + +void test_eigen2_swap() +{ + CALL_SUBTEST_1( swap(Matrix3f()) ); // fixed size, no vectorization + CALL_SUBTEST_1( swap(Matrix4d()) ); // fixed size, possible vectorization + CALL_SUBTEST_1( swap(MatrixXd(3,3)) ); // dyn size, no vectorization + CALL_SUBTEST_1( swap(MatrixXf(30,30)) ); // dyn size, possible vectorization +} diff --git a/test/eigen2/eigen2_triangular.cpp b/test/eigen2/eigen2_triangular.cpp new file mode 100644 index 000000000..3748c7d71 --- /dev/null +++ b/test/eigen2/eigen2_triangular.cpp @@ -0,0 +1,158 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@gmail.com> +// +// 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<typename MatrixType> void triangular(const MatrixType& m) +{ + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + RealScalar largerEps = 10*test_precision<RealScalar>(); + + int rows = m.rows(); + int cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols), + m4(rows, cols), + r1(rows, cols), + r2(rows, cols), + mzero = MatrixType::Zero(rows, cols), + mones = MatrixType::Ones(rows, cols), + identity = Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> + ::Identity(rows, rows), + square = Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> + ::Random(rows, rows); + VectorType v1 = VectorType::Random(rows), + v2 = VectorType::Random(rows), + vzero = VectorType::Zero(rows); + + MatrixType m1up = m1.template part<Eigen::UpperTriangular>(); + MatrixType m2up = m2.template part<Eigen::UpperTriangular>(); + + if (rows*cols>1) + { + VERIFY(m1up.isUpperTriangular()); + VERIFY(m2up.transpose().isLowerTriangular()); + VERIFY(!m2.isLowerTriangular()); + } + +// VERIFY_IS_APPROX(m1up.transpose() * m2, m1.upper().transpose().lower() * m2); + + // test overloaded operator+= + r1.setZero(); + r2.setZero(); + r1.template part<Eigen::UpperTriangular>() += m1; + r2 += m1up; + VERIFY_IS_APPROX(r1,r2); + + // test overloaded operator= + m1.setZero(); + m1.template part<Eigen::UpperTriangular>() = (m2.transpose() * m2).lazy(); + m3 = m2.transpose() * m2; + VERIFY_IS_APPROX(m3.template part<Eigen::LowerTriangular>().transpose(), m1); + + // test overloaded operator= + m1.setZero(); + m1.template part<Eigen::LowerTriangular>() = (m2.transpose() * m2).lazy(); + VERIFY_IS_APPROX(m3.template part<Eigen::LowerTriangular>(), m1); + + VERIFY_IS_APPROX(m3.template part<Diagonal>(), m3.diagonal().asDiagonal()); + + m1 = MatrixType::Random(rows, cols); + for (int i=0; i<rows; ++i) + while (ei_abs2(m1(i,i))<1e-3) m1(i,i) = ei_random<Scalar>(); + + Transpose<MatrixType> trm4(m4); + // test back and forward subsitution + m3 = m1.template part<Eigen::LowerTriangular>(); + VERIFY(m3.template marked<Eigen::LowerTriangular>().solveTriangular(m3).cwise().abs().isIdentity(test_precision<RealScalar>())); + VERIFY(m3.transpose().template marked<Eigen::UpperTriangular>() + .solveTriangular(m3.transpose()).cwise().abs().isIdentity(test_precision<RealScalar>())); + // check M * inv(L) using in place API + m4 = m3; + m3.transpose().template marked<Eigen::UpperTriangular>().solveTriangularInPlace(trm4); + VERIFY(m4.cwise().abs().isIdentity(test_precision<RealScalar>())); + + m3 = m1.template part<Eigen::UpperTriangular>(); + VERIFY(m3.template marked<Eigen::UpperTriangular>().solveTriangular(m3).cwise().abs().isIdentity(test_precision<RealScalar>())); + VERIFY(m3.transpose().template marked<Eigen::LowerTriangular>() + .solveTriangular(m3.transpose()).cwise().abs().isIdentity(test_precision<RealScalar>())); + // check M * inv(U) using in place API + m4 = m3; + m3.transpose().template marked<Eigen::LowerTriangular>().solveTriangularInPlace(trm4); + VERIFY(m4.cwise().abs().isIdentity(test_precision<RealScalar>())); + + m3 = m1.template part<Eigen::UpperTriangular>(); + VERIFY(m2.isApprox(m3 * (m3.template marked<Eigen::UpperTriangular>().solveTriangular(m2)), largerEps)); + m3 = m1.template part<Eigen::LowerTriangular>(); + VERIFY(m2.isApprox(m3 * (m3.template marked<Eigen::LowerTriangular>().solveTriangular(m2)), largerEps)); + + VERIFY((m1.template part<Eigen::UpperTriangular>() * m2.template part<Eigen::UpperTriangular>()).isUpperTriangular()); + + // test swap + m1.setOnes(); + m2.setZero(); + m2.template part<Eigen::UpperTriangular>().swap(m1); + m3.setZero(); + m3.template part<Eigen::UpperTriangular>().setOnes(); + VERIFY_IS_APPROX(m2,m3); + +} + +void selfadjoint() +{ + Matrix2i m; + m << 1, 2, + 3, 4; + + Matrix2i m1 = Matrix2i::Zero(); + m1.part<SelfAdjoint>() = m; + Matrix2i ref1; + ref1 << 1, 2, + 2, 4; + VERIFY(m1 == ref1); + + Matrix2i m2 = Matrix2i::Zero(); + m2.part<SelfAdjoint>() = m.part<UpperTriangular>(); + Matrix2i ref2; + ref2 << 1, 2, + 2, 4; + VERIFY(m2 == ref2); + + Matrix2i m3 = Matrix2i::Zero(); + m3.part<SelfAdjoint>() = m.part<LowerTriangular>(); + Matrix2i ref3; + ref3 << 1, 0, + 0, 4; + VERIFY(m3 == ref3); + + // example inspired from bug 159 + int array[] = {1, 2, 3, 4}; + Matrix2i::Map(array).part<SelfAdjoint>() = Matrix2i::Random().part<LowerTriangular>(); + + std::cout << "hello\n" << array << std::endl; +} + +void test_eigen2_triangular() +{ + CALL_SUBTEST_8( selfadjoint() ); + for(int i = 0; i < g_repeat ; i++) { + CALL_SUBTEST_1( triangular(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( triangular(Matrix<float, 2, 2>()) ); + CALL_SUBTEST_3( triangular(Matrix3d()) ); + CALL_SUBTEST_4( triangular(MatrixXcf(4, 4)) ); + CALL_SUBTEST_5( triangular(Matrix<std::complex<float>,8, 8>()) ); + CALL_SUBTEST_6( triangular(MatrixXd(17,17)) ); + CALL_SUBTEST_7( triangular(Matrix<float,Dynamic,Dynamic,RowMajor>(5, 5)) ); + } +} diff --git a/test/eigen2/eigen2_unalignedassert.cpp b/test/eigen2/eigen2_unalignedassert.cpp new file mode 100644 index 000000000..d10b6f538 --- /dev/null +++ b/test/eigen2/eigen2_unalignedassert.cpp @@ -0,0 +1,116 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" + +struct Good1 +{ + MatrixXd m; // good: m will allocate its own array, taking care of alignment. + Good1() : m(20,20) {} +}; + +struct Good2 +{ + Matrix3d m; // good: m's size isn't a multiple of 16 bytes, so m doesn't have to be aligned +}; + +struct Good3 +{ + Vector2f m; // good: same reason +}; + +struct Bad4 +{ + Vector2d m; // bad: sizeof(m)%16==0 so alignment is required +}; + +struct Bad5 +{ + Matrix<float, 2, 6> m; // bad: same reason +}; + +struct Bad6 +{ + Matrix<double, 3, 4> m; // bad: same reason +}; + +struct Good7 +{ + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + Vector2d m; + float f; // make the struct have sizeof%16!=0 to make it a little more tricky when we allow an array of 2 such objects +}; + +struct Good8 +{ + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + float f; // try the f at first -- the EIGEN_ALIGN_128 attribute of m should make that still work + Matrix4f m; +}; + +struct Good9 +{ + Matrix<float,2,2,DontAlign> m; // good: no alignment requested + float f; +}; + +template<bool Align> struct Depends +{ + EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(Align) + Vector2d m; + float f; +}; + +template<typename T> +void check_unalignedassert_good() +{ + T *x, *y; + x = new T; + delete x; + y = new T[2]; + delete[] y; +} + +#if EIGEN_ARCH_WANTS_ALIGNMENT +template<typename T> +void check_unalignedassert_bad() +{ + float buf[sizeof(T)+16]; + float *unaligned = buf; + while((reinterpret_cast<std::size_t>(unaligned)&0xf)==0) ++unaligned; // make sure unaligned is really unaligned + T *x = ::new(static_cast<void*>(unaligned)) T; + x->~T(); +} +#endif + +void unalignedassert() +{ + check_unalignedassert_good<Good1>(); + check_unalignedassert_good<Good2>(); + check_unalignedassert_good<Good3>(); +#if EIGEN_ARCH_WANTS_ALIGNMENT + VERIFY_RAISES_ASSERT(check_unalignedassert_bad<Bad4>()); + VERIFY_RAISES_ASSERT(check_unalignedassert_bad<Bad5>()); + VERIFY_RAISES_ASSERT(check_unalignedassert_bad<Bad6>()); +#endif + + check_unalignedassert_good<Good7>(); + check_unalignedassert_good<Good8>(); + check_unalignedassert_good<Good9>(); + check_unalignedassert_good<Depends<true> >(); + +#if EIGEN_ARCH_WANTS_ALIGNMENT + VERIFY_RAISES_ASSERT(check_unalignedassert_bad<Depends<false> >()); +#endif +} + +void test_eigen2_unalignedassert() +{ + CALL_SUBTEST(unalignedassert()); +} diff --git a/test/eigen2/eigen2_visitor.cpp b/test/eigen2/eigen2_visitor.cpp new file mode 100644 index 000000000..4781991de --- /dev/null +++ b/test/eigen2/eigen2_visitor.cpp @@ -0,0 +1,116 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename MatrixType> void matrixVisitor(const MatrixType& p) +{ + typedef typename MatrixType::Scalar Scalar; + + int rows = p.rows(); + int cols = p.cols(); + + // construct a random matrix where all coefficients are different + MatrixType m; + m = MatrixType::Random(rows, cols); + for(int i = 0; i < m.size(); i++) + for(int i2 = 0; i2 < i; i2++) + while(m(i) == m(i2)) // yes, == + m(i) = ei_random<Scalar>(); + + Scalar minc = Scalar(1000), maxc = Scalar(-1000); + int minrow=0,mincol=0,maxrow=0,maxcol=0; + for(int j = 0; j < cols; j++) + for(int i = 0; i < rows; i++) + { + if(m(i,j) < minc) + { + minc = m(i,j); + minrow = i; + mincol = j; + } + if(m(i,j) > maxc) + { + maxc = m(i,j); + maxrow = i; + maxcol = j; + } + } + int eigen_minrow, eigen_mincol, eigen_maxrow, eigen_maxcol; + Scalar eigen_minc, eigen_maxc; + eigen_minc = m.minCoeff(&eigen_minrow,&eigen_mincol); + eigen_maxc = m.maxCoeff(&eigen_maxrow,&eigen_maxcol); + VERIFY(minrow == eigen_minrow); + VERIFY(maxrow == eigen_maxrow); + VERIFY(mincol == eigen_mincol); + VERIFY(maxcol == eigen_maxcol); + VERIFY_IS_APPROX(minc, eigen_minc); + VERIFY_IS_APPROX(maxc, eigen_maxc); + VERIFY_IS_APPROX(minc, m.minCoeff()); + VERIFY_IS_APPROX(maxc, m.maxCoeff()); +} + +template<typename VectorType> void vectorVisitor(const VectorType& w) +{ + typedef typename VectorType::Scalar Scalar; + + int size = w.size(); + + // construct a random vector where all coefficients are different + VectorType v; + v = VectorType::Random(size); + for(int i = 0; i < size; i++) + for(int i2 = 0; i2 < i; i2++) + while(v(i) == v(i2)) // yes, == + v(i) = ei_random<Scalar>(); + + Scalar minc = Scalar(1000), maxc = Scalar(-1000); + int minidx=0,maxidx=0; + for(int i = 0; i < size; i++) + { + if(v(i) < minc) + { + minc = v(i); + minidx = i; + } + if(v(i) > maxc) + { + maxc = v(i); + maxidx = i; + } + } + int eigen_minidx, eigen_maxidx; + Scalar eigen_minc, eigen_maxc; + eigen_minc = v.minCoeff(&eigen_minidx); + eigen_maxc = v.maxCoeff(&eigen_maxidx); + VERIFY(minidx == eigen_minidx); + VERIFY(maxidx == eigen_maxidx); + VERIFY_IS_APPROX(minc, eigen_minc); + VERIFY_IS_APPROX(maxc, eigen_maxc); + VERIFY_IS_APPROX(minc, v.minCoeff()); + VERIFY_IS_APPROX(maxc, v.maxCoeff()); +} + +void test_eigen2_visitor() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( matrixVisitor(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( matrixVisitor(Matrix2f()) ); + CALL_SUBTEST_3( matrixVisitor(Matrix4d()) ); + CALL_SUBTEST_4( matrixVisitor(MatrixXd(8, 12)) ); + CALL_SUBTEST_5( matrixVisitor(Matrix<double,Dynamic,Dynamic,RowMajor>(20, 20)) ); + CALL_SUBTEST_6( matrixVisitor(MatrixXi(8, 12)) ); + } + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_7( vectorVisitor(Vector4f()) ); + CALL_SUBTEST_4( vectorVisitor(VectorXd(10)) ); + CALL_SUBTEST_4( vectorVisitor(RowVectorXd(10)) ); + CALL_SUBTEST_8( vectorVisitor(VectorXf(33)) ); + } +} diff --git a/test/eigen2/gsl_helper.h b/test/eigen2/gsl_helper.h new file mode 100644 index 000000000..d1d854533 --- /dev/null +++ b/test/eigen2/gsl_helper.h @@ -0,0 +1,175 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// 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/. + +#ifndef EIGEN_GSL_HELPER +#define EIGEN_GSL_HELPER + +#include <Eigen/Core> + +#include <gsl/gsl_blas.h> +#include <gsl/gsl_multifit.h> +#include <gsl/gsl_eigen.h> +#include <gsl/gsl_linalg.h> +#include <gsl/gsl_complex.h> +#include <gsl/gsl_complex_math.h> + +namespace Eigen { + +template<typename Scalar, bool IsComplex = NumTraits<Scalar>::IsComplex> struct GslTraits +{ + typedef gsl_matrix* Matrix; + typedef gsl_vector* Vector; + static Matrix createMatrix(int rows, int cols) { return gsl_matrix_alloc(rows,cols); } + static Vector createVector(int size) { return gsl_vector_alloc(size); } + static void free(Matrix& m) { gsl_matrix_free(m); m=0; } + static void free(Vector& m) { gsl_vector_free(m); m=0; } + static void prod(const Matrix& m, const Vector& v, Vector& x) { gsl_blas_dgemv(CblasNoTrans,1,m,v,0,x); } + static void cholesky(Matrix& m) { gsl_linalg_cholesky_decomp(m); } + static void cholesky_solve(const Matrix& m, const Vector& b, Vector& x) { gsl_linalg_cholesky_solve(m,b,x); } + static void eigen_symm(const Matrix& m, Vector& eval, Matrix& evec) + { + gsl_eigen_symmv_workspace * w = gsl_eigen_symmv_alloc(m->size1); + Matrix a = createMatrix(m->size1, m->size2); + gsl_matrix_memcpy(a, m); + gsl_eigen_symmv(a,eval,evec,w); + gsl_eigen_symmv_sort(eval, evec, GSL_EIGEN_SORT_VAL_ASC); + gsl_eigen_symmv_free(w); + free(a); + } + static void eigen_symm_gen(const Matrix& m, const Matrix& _b, Vector& eval, Matrix& evec) + { + gsl_eigen_gensymmv_workspace * w = gsl_eigen_gensymmv_alloc(m->size1); + Matrix a = createMatrix(m->size1, m->size2); + Matrix b = createMatrix(_b->size1, _b->size2); + gsl_matrix_memcpy(a, m); + gsl_matrix_memcpy(b, _b); + gsl_eigen_gensymmv(a,b,eval,evec,w); + gsl_eigen_symmv_sort(eval, evec, GSL_EIGEN_SORT_VAL_ASC); + gsl_eigen_gensymmv_free(w); + free(a); + } +}; + +template<typename Scalar> struct GslTraits<Scalar,true> +{ + typedef gsl_matrix_complex* Matrix; + typedef gsl_vector_complex* Vector; + static Matrix createMatrix(int rows, int cols) { return gsl_matrix_complex_alloc(rows,cols); } + static Vector createVector(int size) { return gsl_vector_complex_alloc(size); } + static void free(Matrix& m) { gsl_matrix_complex_free(m); m=0; } + static void free(Vector& m) { gsl_vector_complex_free(m); m=0; } + static void cholesky(Matrix& m) { gsl_linalg_complex_cholesky_decomp(m); } + static void cholesky_solve(const Matrix& m, const Vector& b, Vector& x) { gsl_linalg_complex_cholesky_solve(m,b,x); } + static void prod(const Matrix& m, const Vector& v, Vector& x) + { gsl_blas_zgemv(CblasNoTrans,gsl_complex_rect(1,0),m,v,gsl_complex_rect(0,0),x); } + static void eigen_symm(const Matrix& m, gsl_vector* &eval, Matrix& evec) + { + gsl_eigen_hermv_workspace * w = gsl_eigen_hermv_alloc(m->size1); + Matrix a = createMatrix(m->size1, m->size2); + gsl_matrix_complex_memcpy(a, m); + gsl_eigen_hermv(a,eval,evec,w); + gsl_eigen_hermv_sort(eval, evec, GSL_EIGEN_SORT_VAL_ASC); + gsl_eigen_hermv_free(w); + free(a); + } + static void eigen_symm_gen(const Matrix& m, const Matrix& _b, gsl_vector* &eval, Matrix& evec) + { + gsl_eigen_genhermv_workspace * w = gsl_eigen_genhermv_alloc(m->size1); + Matrix a = createMatrix(m->size1, m->size2); + Matrix b = createMatrix(_b->size1, _b->size2); + gsl_matrix_complex_memcpy(a, m); + gsl_matrix_complex_memcpy(b, _b); + gsl_eigen_genhermv(a,b,eval,evec,w); + gsl_eigen_hermv_sort(eval, evec, GSL_EIGEN_SORT_VAL_ASC); + gsl_eigen_genhermv_free(w); + free(a); + } +}; + +template<typename MatrixType> +void convert(const MatrixType& m, gsl_matrix* &res) +{ +// if (res) +// gsl_matrix_free(res); + res = gsl_matrix_alloc(m.rows(), m.cols()); + for (int i=0 ; i<m.rows() ; ++i) + for (int j=0 ; j<m.cols(); ++j) + gsl_matrix_set(res, i, j, m(i,j)); +} + +template<typename MatrixType> +void convert(const gsl_matrix* m, MatrixType& res) +{ + res.resize(int(m->size1), int(m->size2)); + for (int i=0 ; i<res.rows() ; ++i) + for (int j=0 ; j<res.cols(); ++j) + res(i,j) = gsl_matrix_get(m,i,j); +} + +template<typename VectorType> +void convert(const VectorType& m, gsl_vector* &res) +{ + if (res) gsl_vector_free(res); + res = gsl_vector_alloc(m.size()); + for (int i=0 ; i<m.size() ; ++i) + gsl_vector_set(res, i, m[i]); +} + +template<typename VectorType> +void convert(const gsl_vector* m, VectorType& res) +{ + res.resize (m->size); + for (int i=0 ; i<res.rows() ; ++i) + res[i] = gsl_vector_get(m, i); +} + +template<typename MatrixType> +void convert(const MatrixType& m, gsl_matrix_complex* &res) +{ + res = gsl_matrix_complex_alloc(m.rows(), m.cols()); + for (int i=0 ; i<m.rows() ; ++i) + for (int j=0 ; j<m.cols(); ++j) + { + gsl_matrix_complex_set(res, i, j, + gsl_complex_rect(m(i,j).real(), m(i,j).imag())); + } +} + +template<typename MatrixType> +void convert(const gsl_matrix_complex* m, MatrixType& res) +{ + res.resize(int(m->size1), int(m->size2)); + for (int i=0 ; i<res.rows() ; ++i) + for (int j=0 ; j<res.cols(); ++j) + res(i,j) = typename MatrixType::Scalar( + GSL_REAL(gsl_matrix_complex_get(m,i,j)), + GSL_IMAG(gsl_matrix_complex_get(m,i,j))); +} + +template<typename VectorType> +void convert(const VectorType& m, gsl_vector_complex* &res) +{ + res = gsl_vector_complex_alloc(m.size()); + for (int i=0 ; i<m.size() ; ++i) + gsl_vector_complex_set(res, i, gsl_complex_rect(m[i].real(), m[i].imag())); +} + +template<typename VectorType> +void convert(const gsl_vector_complex* m, VectorType& res) +{ + res.resize(m->size); + for (int i=0 ; i<res.rows() ; ++i) + res[i] = typename VectorType::Scalar( + GSL_REAL(gsl_vector_complex_get(m, i)), + GSL_IMAG(gsl_vector_complex_get(m, i))); +} + +} + +#endif // EIGEN_GSL_HELPER diff --git a/test/eigen2/main.h b/test/eigen2/main.h new file mode 100644 index 000000000..ad2ba1994 --- /dev/null +++ b/test/eigen2/main.h @@ -0,0 +1,399 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> +// +// 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 <cstdlib> +#include <ctime> +#include <iostream> +#include <string> +#include <vector> + +#ifndef EIGEN_TEST_FUNC +#error EIGEN_TEST_FUNC must be defined +#endif + +#define DEFAULT_REPEAT 10 + +namespace Eigen +{ + static std::vector<std::string> g_test_stack; + static int g_repeat; +} + +#define EI_PP_MAKE_STRING2(S) #S +#define EI_PP_MAKE_STRING(S) EI_PP_MAKE_STRING2(S) + +#define EI_PP_CAT2(a,b) a ## b +#define EI_PP_CAT(a,b) EI_PP_CAT2(a,b) + +#ifndef EIGEN_NO_ASSERTION_CHECKING + + namespace Eigen + { + static const bool should_raise_an_assert = false; + + // Used to avoid to raise two exceptions at a time in which + // case the exception is not properly caught. + // This may happen when a second exceptions is raise in a destructor. + static bool no_more_assert = false; + + struct eigen_assert_exception + { + eigen_assert_exception(void) {} + ~eigen_assert_exception() { Eigen::no_more_assert = false; } + }; + } + + // If EIGEN_DEBUG_ASSERTS is defined and if no assertion is raised while + // one should have been, then the list of excecuted assertions is printed out. + // + // EIGEN_DEBUG_ASSERTS is not enabled by default as it + // significantly increases the compilation time + // and might even introduce side effects that would hide + // some memory errors. + #ifdef EIGEN_DEBUG_ASSERTS + + namespace Eigen + { + static bool ei_push_assert = false; + static std::vector<std::string> eigen_assert_list; + } + + #define eigen_assert(a) \ + if( (!(a)) && (!no_more_assert) ) \ + { \ + Eigen::no_more_assert = true; \ + throw Eigen::eigen_assert_exception(); \ + } \ + else if (Eigen::ei_push_assert) \ + { \ + eigen_assert_list.push_back(std::string(EI_PP_MAKE_STRING(__FILE__)" ("EI_PP_MAKE_STRING(__LINE__)") : "#a) ); \ + } + + #define VERIFY_RAISES_ASSERT(a) \ + { \ + Eigen::no_more_assert = false; \ + try { \ + Eigen::eigen_assert_list.clear(); \ + Eigen::ei_push_assert = true; \ + a; \ + Eigen::ei_push_assert = false; \ + std::cerr << "One of the following asserts should have been raised:\n"; \ + for (uint ai=0 ; ai<eigen_assert_list.size() ; ++ai) \ + std::cerr << " " << eigen_assert_list[ai] << "\n"; \ + VERIFY(Eigen::should_raise_an_assert && # a); \ + } catch (Eigen::eigen_assert_exception e) { \ + Eigen::ei_push_assert = false; VERIFY(true); \ + } \ + } + + #else // EIGEN_DEBUG_ASSERTS + + #undef eigen_assert + + // see bug 89. The copy_bool here is working around a bug in gcc <= 4.3 + #define eigen_assert(a) \ + if( (!Eigen::internal::copy_bool(a)) && (!no_more_assert) ) \ + { \ + Eigen::no_more_assert = true; \ + throw Eigen::eigen_assert_exception(); \ + } + + #define VERIFY_RAISES_ASSERT(a) { \ + Eigen::no_more_assert = false; \ + try { a; VERIFY(Eigen::should_raise_an_assert && # a); } \ + catch (Eigen::eigen_assert_exception e) { VERIFY(true); } \ + } + + #endif // EIGEN_DEBUG_ASSERTS + + #define EIGEN_USE_CUSTOM_ASSERT + +#else // EIGEN_NO_ASSERTION_CHECKING + + #define VERIFY_RAISES_ASSERT(a) {} + +#endif // EIGEN_NO_ASSERTION_CHECKING + + +#define EIGEN_INTERNAL_DEBUGGING +#define EIGEN_NICE_RANDOM +#include <Eigen/Array> + + +#define VERIFY(a) do { if (!(a)) { \ + std::cerr << "Test " << g_test_stack.back() << " failed in "EI_PP_MAKE_STRING(__FILE__) << " (" << EI_PP_MAKE_STRING(__LINE__) << ")" \ + << std::endl << " " << EI_PP_MAKE_STRING(a) << std::endl << std::endl; \ + abort(); \ + } } while (0) + +#define VERIFY_IS_APPROX(a, b) VERIFY(test_ei_isApprox(a, b)) +#define VERIFY_IS_NOT_APPROX(a, b) VERIFY(!test_ei_isApprox(a, b)) +#define VERIFY_IS_MUCH_SMALLER_THAN(a, b) VERIFY(test_ei_isMuchSmallerThan(a, b)) +#define VERIFY_IS_NOT_MUCH_SMALLER_THAN(a, b) VERIFY(!test_ei_isMuchSmallerThan(a, b)) +#define VERIFY_IS_APPROX_OR_LESS_THAN(a, b) VERIFY(test_ei_isApproxOrLessThan(a, b)) +#define VERIFY_IS_NOT_APPROX_OR_LESS_THAN(a, b) VERIFY(!test_ei_isApproxOrLessThan(a, b)) + +#define CALL_SUBTEST(FUNC) do { \ + g_test_stack.push_back(EI_PP_MAKE_STRING(FUNC)); \ + FUNC; \ + g_test_stack.pop_back(); \ + } while (0) + +namespace Eigen { + +template<typename T> inline typename NumTraits<T>::Real test_precision(); +template<> inline int test_precision<int>() { return 0; } +template<> inline float test_precision<float>() { return 1e-3f; } +template<> inline double test_precision<double>() { return 1e-6; } +template<> inline float test_precision<std::complex<float> >() { return test_precision<float>(); } +template<> inline double test_precision<std::complex<double> >() { return test_precision<double>(); } +template<> inline long double test_precision<long double>() { return 1e-6; } + +inline bool test_ei_isApprox(const int& a, const int& b) +{ return ei_isApprox(a, b, test_precision<int>()); } +inline bool test_ei_isMuchSmallerThan(const int& a, const int& b) +{ return ei_isMuchSmallerThan(a, b, test_precision<int>()); } +inline bool test_ei_isApproxOrLessThan(const int& a, const int& b) +{ return ei_isApproxOrLessThan(a, b, test_precision<int>()); } + +inline bool test_ei_isApprox(const float& a, const float& b) +{ return ei_isApprox(a, b, test_precision<float>()); } +inline bool test_ei_isMuchSmallerThan(const float& a, const float& b) +{ return ei_isMuchSmallerThan(a, b, test_precision<float>()); } +inline bool test_ei_isApproxOrLessThan(const float& a, const float& b) +{ return ei_isApproxOrLessThan(a, b, test_precision<float>()); } + +inline bool test_ei_isApprox(const double& a, const double& b) +{ return ei_isApprox(a, b, test_precision<double>()); } +inline bool test_ei_isMuchSmallerThan(const double& a, const double& b) +{ return ei_isMuchSmallerThan(a, b, test_precision<double>()); } +inline bool test_ei_isApproxOrLessThan(const double& a, const double& b) +{ return ei_isApproxOrLessThan(a, b, test_precision<double>()); } + +inline bool test_ei_isApprox(const std::complex<float>& a, const std::complex<float>& b) +{ return ei_isApprox(a, b, test_precision<std::complex<float> >()); } +inline bool test_ei_isMuchSmallerThan(const std::complex<float>& a, const std::complex<float>& b) +{ return ei_isMuchSmallerThan(a, b, test_precision<std::complex<float> >()); } + +inline bool test_ei_isApprox(const std::complex<double>& a, const std::complex<double>& b) +{ return ei_isApprox(a, b, test_precision<std::complex<double> >()); } +inline bool test_ei_isMuchSmallerThan(const std::complex<double>& a, const std::complex<double>& b) +{ return ei_isMuchSmallerThan(a, b, test_precision<std::complex<double> >()); } + +inline bool test_ei_isApprox(const long double& a, const long double& b) +{ return ei_isApprox(a, b, test_precision<long double>()); } +inline bool test_ei_isMuchSmallerThan(const long double& a, const long double& b) +{ return ei_isMuchSmallerThan(a, b, test_precision<long double>()); } +inline bool test_ei_isApproxOrLessThan(const long double& a, const long double& b) +{ return ei_isApproxOrLessThan(a, b, test_precision<long double>()); } + +template<typename Type1, typename Type2> +inline bool test_ei_isApprox(const Type1& a, const Type2& b) +{ + return a.isApprox(b, test_precision<typename Type1::Scalar>()); +} + +template<typename Derived1, typename Derived2> +inline bool test_ei_isMuchSmallerThan(const MatrixBase<Derived1>& m1, + const MatrixBase<Derived2>& m2) +{ + return m1.isMuchSmallerThan(m2, test_precision<typename ei_traits<Derived1>::Scalar>()); +} + +template<typename Derived> +inline bool test_ei_isMuchSmallerThan(const MatrixBase<Derived>& m, + const typename NumTraits<typename ei_traits<Derived>::Scalar>::Real& s) +{ + return m.isMuchSmallerThan(s, test_precision<typename ei_traits<Derived>::Scalar>()); +} + +} // end namespace Eigen + +template<typename T> struct GetDifferentType; + +template<> struct GetDifferentType<float> { typedef double type; }; +template<> struct GetDifferentType<double> { typedef float type; }; +template<typename T> struct GetDifferentType<std::complex<T> > +{ typedef std::complex<typename GetDifferentType<T>::type> type; }; + +// forward declaration of the main test function +void EI_PP_CAT(test_,EIGEN_TEST_FUNC)(); + +using namespace Eigen; + +#ifdef EIGEN_TEST_PART_1 +#define CALL_SUBTEST_1(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_1(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_2 +#define CALL_SUBTEST_2(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_2(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_3 +#define CALL_SUBTEST_3(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_3(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_4 +#define CALL_SUBTEST_4(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_4(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_5 +#define CALL_SUBTEST_5(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_5(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_6 +#define CALL_SUBTEST_6(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_6(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_7 +#define CALL_SUBTEST_7(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_7(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_8 +#define CALL_SUBTEST_8(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_8(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_9 +#define CALL_SUBTEST_9(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_9(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_10 +#define CALL_SUBTEST_10(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_10(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_11 +#define CALL_SUBTEST_11(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_11(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_12 +#define CALL_SUBTEST_12(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_12(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_13 +#define CALL_SUBTEST_13(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_13(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_14 +#define CALL_SUBTEST_14(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_14(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_15 +#define CALL_SUBTEST_15(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_15(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_16 +#define CALL_SUBTEST_16(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_16(FUNC) +#endif + + + +int main(int argc, char *argv[]) +{ + bool has_set_repeat = false; + bool has_set_seed = false; + bool need_help = false; + unsigned int seed = 0; + int repeat = DEFAULT_REPEAT; + + for(int i = 1; i < argc; i++) + { + if(argv[i][0] == 'r') + { + if(has_set_repeat) + { + std::cout << "Argument " << argv[i] << " conflicting with a former argument" << std::endl; + return 1; + } + repeat = std::atoi(argv[i]+1); + has_set_repeat = true; + if(repeat <= 0) + { + std::cout << "Invalid \'repeat\' value " << argv[i]+1 << std::endl; + return 1; + } + } + else if(argv[i][0] == 's') + { + if(has_set_seed) + { + std::cout << "Argument " << argv[i] << " conflicting with a former argument" << std::endl; + return 1; + } + seed = int(std::strtoul(argv[i]+1, 0, 10)); + has_set_seed = true; + bool ok = seed!=0; + if(!ok) + { + std::cout << "Invalid \'seed\' value " << argv[i]+1 << std::endl; + return 1; + } + } + else + { + need_help = true; + } + } + + if(need_help) + { + std::cout << "This test application takes the following optional arguments:" << std::endl; + std::cout << " rN Repeat each test N times (default: " << DEFAULT_REPEAT << ")" << std::endl; + std::cout << " sN Use N as seed for random numbers (default: based on current time)" << std::endl; + return 1; + } + + if(!has_set_seed) seed = (unsigned int) std::time(NULL); + if(!has_set_repeat) repeat = DEFAULT_REPEAT; + + std::cout << "Initializing random number generator with seed " << seed << std::endl; + std::srand(seed); + std::cout << "Repeating each test " << repeat << " times" << std::endl; + + Eigen::g_repeat = repeat; + Eigen::g_test_stack.push_back(EI_PP_MAKE_STRING(EIGEN_TEST_FUNC)); + + EI_PP_CAT(test_,EIGEN_TEST_FUNC)(); + return 0; +} + + + diff --git a/test/eigen2/product.h b/test/eigen2/product.h new file mode 100644 index 000000000..2c9604d9a --- /dev/null +++ b/test/eigen2/product.h @@ -0,0 +1,132 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/Array> +#include <Eigen/QR> + +template<typename Derived1, typename Derived2> +bool areNotApprox(const MatrixBase<Derived1>& m1, const MatrixBase<Derived2>& m2, typename Derived1::RealScalar epsilon = precision<typename Derived1::RealScalar>()) +{ + return !((m1-m2).cwise().abs2().maxCoeff() < epsilon * epsilon + * std::max(m1.cwise().abs2().maxCoeff(), m2.cwise().abs2().maxCoeff())); +} + +template<typename MatrixType> void product(const MatrixType& m) +{ + /* this test covers the following files: + Identity.h Product.h + */ + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::FloatingPoint FloatingPoint; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> RowVectorType; + typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, 1> ColVectorType; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> RowSquareMatrixType; + typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, MatrixType::ColsAtCompileTime> ColSquareMatrixType; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::ColsAtCompileTime, + MatrixType::Options^RowMajor> OtherMajorMatrixType; + + int rows = m.rows(); + int cols = m.cols(); + + // this test relies a lot on Random.h, and there's not much more that we can do + // to test it, hence I consider that we will have tested Random.h + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols), + mzero = MatrixType::Zero(rows, cols); + RowSquareMatrixType + identity = RowSquareMatrixType::Identity(rows, rows), + square = RowSquareMatrixType::Random(rows, rows), + res = RowSquareMatrixType::Random(rows, rows); + ColSquareMatrixType + square2 = ColSquareMatrixType::Random(cols, cols), + res2 = ColSquareMatrixType::Random(cols, cols); + RowVectorType v1 = RowVectorType::Random(rows), + v2 = RowVectorType::Random(rows), + vzero = RowVectorType::Zero(rows); + ColVectorType vc2 = ColVectorType::Random(cols), vcres(cols); + OtherMajorMatrixType tm1 = m1; + + Scalar s1 = ei_random<Scalar>(); + + int r = ei_random<int>(0, rows-1), + c = ei_random<int>(0, cols-1); + + // begin testing Product.h: only associativity for now + // (we use Transpose.h but this doesn't count as a test for it) + + VERIFY_IS_APPROX((m1*m1.transpose())*m2, m1*(m1.transpose()*m2)); + m3 = m1; + m3 *= m1.transpose() * m2; + VERIFY_IS_APPROX(m3, m1 * (m1.transpose()*m2)); + VERIFY_IS_APPROX(m3, m1.lazy() * (m1.transpose()*m2)); + + // continue testing Product.h: distributivity + VERIFY_IS_APPROX(square*(m1 + m2), square*m1+square*m2); + VERIFY_IS_APPROX(square*(m1 - m2), square*m1-square*m2); + + // continue testing Product.h: compatibility with ScalarMultiple.h + VERIFY_IS_APPROX(s1*(square*m1), (s1*square)*m1); + VERIFY_IS_APPROX(s1*(square*m1), square*(m1*s1)); + + // again, test operator() to check const-qualification + s1 += (square.lazy() * m1)(r,c); + + // test Product.h together with Identity.h + VERIFY_IS_APPROX(v1, identity*v1); + VERIFY_IS_APPROX(v1.transpose(), v1.transpose() * identity); + // again, test operator() to check const-qualification + VERIFY_IS_APPROX(MatrixType::Identity(rows, cols)(r,c), static_cast<Scalar>(r==c)); + + if (rows!=cols) + VERIFY_RAISES_ASSERT(m3 = m1*m1); + + // test the previous tests were not screwed up because operator* returns 0 + // (we use the more accurate default epsilon) + if (NumTraits<Scalar>::HasFloatingPoint && std::min(rows,cols)>1) + { + VERIFY(areNotApprox(m1.transpose()*m2,m2.transpose()*m1)); + } + + // test optimized operator+= path + res = square; + res += (m1 * m2.transpose()).lazy(); + VERIFY_IS_APPROX(res, square + m1 * m2.transpose()); + if (NumTraits<Scalar>::HasFloatingPoint && std::min(rows,cols)>1) + { + VERIFY(areNotApprox(res,square + m2 * m1.transpose())); + } + vcres = vc2; + vcres += (m1.transpose() * v1).lazy(); + VERIFY_IS_APPROX(vcres, vc2 + m1.transpose() * v1); + tm1 = m1; + VERIFY_IS_APPROX(tm1.transpose() * v1, m1.transpose() * v1); + VERIFY_IS_APPROX(v1.transpose() * tm1, v1.transpose() * m1); + + // test submatrix and matrix/vector product + for (int i=0; i<rows; ++i) + res.row(i) = m1.row(i) * m2.transpose(); + VERIFY_IS_APPROX(res, m1 * m2.transpose()); + // the other way round: + for (int i=0; i<rows; ++i) + res.col(i) = m1 * m2.transpose().col(i); + VERIFY_IS_APPROX(res, m1 * m2.transpose()); + + res2 = square2; + res2 += (m1.transpose() * m2).lazy(); + VERIFY_IS_APPROX(res2, square2 + m1.transpose() * m2); + + if (NumTraits<Scalar>::HasFloatingPoint && std::min(rows,cols)>1) + { + VERIFY(areNotApprox(res2,square2 + m2.transpose() * m1)); + } +} + diff --git a/test/eigen2/runtest.sh b/test/eigen2/runtest.sh new file mode 100755 index 000000000..bc693af13 --- /dev/null +++ b/test/eigen2/runtest.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +black='\E[30m' +red='\E[31m' +green='\E[32m' +yellow='\E[33m' +blue='\E[34m' +magenta='\E[35m' +cyan='\E[36m' +white='\E[37m' + +if make test_$1 > /dev/null 2> .runtest.log ; then + if ! ./test_$1 r20 > /dev/null 2> .runtest.log ; then + echo -e $red Test $1 failed: $black + echo -e $blue + cat .runtest.log + echo -e $black + exit 1 + else + echo -e $green Test $1 passed$black + fi +else + echo -e $red Build of target $1 failed: $black + echo -e $blue + cat .runtest.log + echo -e $black + exit 1 +fi diff --git a/test/eigen2/sparse.h b/test/eigen2/sparse.h new file mode 100644 index 000000000..e12f89990 --- /dev/null +++ b/test/eigen2/sparse.h @@ -0,0 +1,154 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008 Daniel Gomez Ferro <dgomezferro@gmail.com> +// +// 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/. + +#ifndef EIGEN_TESTSPARSE_H + +#include "main.h" + +#if EIGEN_GNUC_AT_LEAST(4,0) && !defined __ICC +#include <tr1/unordered_map> +#define EIGEN_UNORDERED_MAP_SUPPORT +namespace std { + using std::tr1::unordered_map; +} +#endif + +#ifdef EIGEN_GOOGLEHASH_SUPPORT + #include <google/sparse_hash_map> +#endif + +#include <Eigen/Cholesky> +#include <Eigen/LU> +#include <Eigen/Sparse> + +enum { + ForceNonZeroDiag = 1, + MakeLowerTriangular = 2, + MakeUpperTriangular = 4, + ForceRealDiag = 8 +}; + +/* Initializes both a sparse and dense matrix with same random values, + * and a ratio of \a density non zero entries. + * \param flags is a union of ForceNonZeroDiag, MakeLowerTriangular and MakeUpperTriangular + * allowing to control the shape of the matrix. + * \param zeroCoords and nonzeroCoords allows to get the coordinate lists of the non zero, + * and zero coefficients respectively. + */ +template<typename Scalar> void +initSparse(double density, + Matrix<Scalar,Dynamic,Dynamic>& refMat, + SparseMatrix<Scalar>& sparseMat, + int flags = 0, + std::vector<Vector2i>* zeroCoords = 0, + std::vector<Vector2i>* nonzeroCoords = 0) +{ + sparseMat.startFill(int(refMat.rows()*refMat.cols()*density)); + for(int j=0; j<refMat.cols(); j++) + { + for(int i=0; i<refMat.rows(); i++) + { + Scalar v = (ei_random<double>(0,1) < density) ? ei_random<Scalar>() : Scalar(0); + if ((flags&ForceNonZeroDiag) && (i==j)) + { + v = ei_random<Scalar>()*Scalar(3.); + v = v*v + Scalar(5.); + } + if ((flags & MakeLowerTriangular) && j>i) + v = Scalar(0); + else if ((flags & MakeUpperTriangular) && j<i) + v = Scalar(0); + + if ((flags&ForceRealDiag) && (i==j)) + v = ei_real(v); + + if (v!=Scalar(0)) + { + sparseMat.fill(i,j) = v; + if (nonzeroCoords) + nonzeroCoords->push_back(Vector2i(i,j)); + } + else if (zeroCoords) + { + zeroCoords->push_back(Vector2i(i,j)); + } + refMat(i,j) = v; + } + } + sparseMat.endFill(); +} + +template<typename Scalar> void +initSparse(double density, + Matrix<Scalar,Dynamic,Dynamic>& refMat, + DynamicSparseMatrix<Scalar>& sparseMat, + int flags = 0, + std::vector<Vector2i>* zeroCoords = 0, + std::vector<Vector2i>* nonzeroCoords = 0) +{ + sparseMat.startFill(int(refMat.rows()*refMat.cols()*density)); + for(int j=0; j<refMat.cols(); j++) + { + for(int i=0; i<refMat.rows(); i++) + { + Scalar v = (ei_random<double>(0,1) < density) ? ei_random<Scalar>() : Scalar(0); + if ((flags&ForceNonZeroDiag) && (i==j)) + { + v = ei_random<Scalar>()*Scalar(3.); + v = v*v + Scalar(5.); + } + if ((flags & MakeLowerTriangular) && j>i) + v = Scalar(0); + else if ((flags & MakeUpperTriangular) && j<i) + v = Scalar(0); + + if ((flags&ForceRealDiag) && (i==j)) + v = ei_real(v); + + if (v!=Scalar(0)) + { + sparseMat.fill(i,j) = v; + if (nonzeroCoords) + nonzeroCoords->push_back(Vector2i(i,j)); + } + else if (zeroCoords) + { + zeroCoords->push_back(Vector2i(i,j)); + } + refMat(i,j) = v; + } + } + sparseMat.endFill(); +} + +template<typename Scalar> void +initSparse(double density, + Matrix<Scalar,Dynamic,1>& refVec, + SparseVector<Scalar>& sparseVec, + std::vector<int>* zeroCoords = 0, + std::vector<int>* nonzeroCoords = 0) +{ + sparseVec.reserve(int(refVec.size()*density)); + sparseVec.setZero(); + for(int i=0; i<refVec.size(); i++) + { + Scalar v = (ei_random<double>(0,1) < density) ? ei_random<Scalar>() : Scalar(0); + if (v!=Scalar(0)) + { + sparseVec.fill(i) = v; + if (nonzeroCoords) + nonzeroCoords->push_back(i); + } + else if (zeroCoords) + zeroCoords->push_back(i); + refVec[i] = v; + } +} + +#endif // EIGEN_TESTSPARSE_H diff --git a/test/eigen2/testsuite.cmake b/test/eigen2/testsuite.cmake new file mode 100644 index 000000000..12b6bfa2e --- /dev/null +++ b/test/eigen2/testsuite.cmake @@ -0,0 +1,197 @@ + +#################################################################### +# +# Usage: +# - create a new folder, let's call it cdash +# - in that folder, do: +# ctest -S path/to/eigen2/test/testsuite.cmake[,option1=value1[,option2=value2]] +# +# Options: +# - EIGEN_CXX: compiler, eg.: g++-4.2 +# default: default c++ compiler +# - EIGEN_SITE: eg, INRIA-Bdx_pc-gael, or the name of the contributor, etc. +# default: hostname +# - EIGEN_BUILD_STRING: a string which identify the system/compiler. It should be formed like that: +# <OS_name>-<OS_version>-<arch>-<compiler-version> +# with: +# <OS_name> = opensuse, debian, osx, windows, cygwin, freebsd, solaris, etc. +# <OS_version> = 11.1, XP, vista, leopard, etc. +# <arch> = i386, x86_64, ia64, powerpc, etc. +# <compiler-version> = gcc-4.3.2, icc-11.0, MSVC-2008, etc. +# - EIGEN_EXPLICIT_VECTORIZATION: novec, SSE2, Altivec +# default: SSE2 for x86_64 systems, novec otherwise +# Its value is automatically appended to EIGEN_BUILD_STRING +# - EIGEN_CMAKE_DIR: path to cmake executable +# - EIGEN_MODE: dashboard model, can be Experimental, Nightly, or Continuous +# default: Nightly +# - EIGEN_WORK_DIR: directory used to download the source files and make the builds +# default: folder which contains this script +# - EIGEN_CMAKE_ARGS: additional arguments passed to cmake +# - CTEST_SOURCE_DIRECTORY: path to eigen's src (use a new and empty folder, not the one you are working on) +# default: <EIGEN_WORK_DIR>/src +# - CTEST_BINARY_DIRECTORY: build directory +# default: <EIGEN_WORK_DIR>/nightly-<EIGEN_CXX> +# +# Here is an example running several compilers on a linux system: +# #!/bin/bash +# ARCH=`uname -m` +# SITE=`hostname` +# VERSION=opensuse-11.1 +# WORK_DIR=/home/gael/Coding/eigen2/cdash +# # get the last version of the script +# wget http://bitbucket.org/eigen/eigen/raw/tip/test/testsuite.cmake -o $WORK_DIR/testsuite.cmake +# COMMON="ctest -S $WORK_DIR/testsuite.cmake,EIGEN_WORK_DIR=$WORK_DIR,EIGEN_SITE=$SITE,EIGEN_MODE=$1,EIGEN_BUILD_STRING=$OS_VERSION-$ARCH" +# $COMMON-gcc-3.4.6,EIGEN_CXX=g++-3.4 +# $COMMON-gcc-4.0.1,EIGEN_CXX=g++-4.0.1 +# $COMMON-gcc-4.3.2,EIGEN_CXX=g++-4.3,EIGEN_EXPLICIT_VECTORIZATION=novec +# $COMMON-gcc-4.3.2,EIGEN_CXX=g++-4.3,EIGEN_EXPLICIT_VECTORIZATION=SSE2 +# $COMMON-icc-11.0,EIGEN_CXX=icpc +# +#################################################################### + +# process the arguments + +set(ARGLIST ${CTEST_SCRIPT_ARG}) +while(${ARGLIST} MATCHES ".+.*") + + # pick first + string(REGEX MATCH "([^,]*)(,.*)?" DUMMY ${ARGLIST}) + SET(TOP ${CMAKE_MATCH_1}) + + # remove first + string(REGEX MATCHALL "[^,]*,(.*)" DUMMY ${ARGLIST}) + SET(ARGLIST ${CMAKE_MATCH_1}) + + # decompose as a pair key=value + string(REGEX MATCH "([^=]*)(=.*)?" DUMMY ${TOP}) + SET(KEY ${CMAKE_MATCH_1}) + + string(REGEX MATCH "[^=]*=(.*)" DUMMY ${TOP}) + SET(VALUE ${CMAKE_MATCH_1}) + + # set the variable to the specified value + if(VALUE) + SET(${KEY} ${VALUE}) + else(VALUE) + SET(${KEY} ON) + endif(VALUE) + +endwhile(${ARGLIST} MATCHES ".+.*") + +#################################################################### +# Automatically set some user variables if they have not been defined manually +#################################################################### +cmake_minimum_required(VERSION 2.6 FATAL_ERROR) + +if(NOT EIGEN_SITE) + site_name(EIGEN_SITE) +endif(NOT EIGEN_SITE) + +if(NOT EIGEN_CMAKE_DIR) + SET(EIGEN_CMAKE_DIR "") +endif(NOT EIGEN_CMAKE_DIR) + +if(NOT EIGEN_BUILD_STRING) + + # let's try to find all information we need to make the build string ourself + + # OS + build_name(EIGEN_OS_VERSION) + + # arch + set(EIGEN_ARCH ${CMAKE_SYSTEM_PROCESSOR}) + if(WIN32) + set(EIGEN_ARCH $ENV{PROCESSOR_ARCHITECTURE}) + else(WIN32) + execute_process(COMMAND uname -m OUTPUT_VARIABLE EIGEN_ARCH OUTPUT_STRIP_TRAILING_WHITESPACE) + endif(WIN32) + + set(EIGEN_BUILD_STRING ${EIGEN_OS_VERSION}${EIGEN_ARCH}-${EIGEN_CXX}) + +endif(NOT EIGEN_BUILD_STRING) + +if(DEFINED EIGEN_EXPLICIT_VECTORIZATION) + set(EIGEN_BUILD_STRING ${EIGEN_BUILD_STRING}-${EIGEN_EXPLICIT_VECTORIZATION}) +endif(DEFINED EIGEN_EXPLICIT_VECTORIZATION) + +if(NOT EIGEN_WORK_DIR) + set(EIGEN_WORK_DIR ${CTEST_SCRIPT_DIRECTORY}) +endif(NOT EIGEN_WORK_DIR) + +if(NOT CTEST_SOURCE_DIRECTORY) + SET (CTEST_SOURCE_DIRECTORY "${EIGEN_WORK_DIR}/src") +endif(NOT CTEST_SOURCE_DIRECTORY) + +if(NOT CTEST_BINARY_DIRECTORY) + SET (CTEST_BINARY_DIRECTORY "${EIGEN_WORK_DIR}/nightly_${EIGEN_CXX}") +endif(NOT CTEST_BINARY_DIRECTORY) + +if(NOT EIGEN_MODE) + set(EIGEN_MODE Nightly) +endif(NOT EIGEN_MODE) + +## mandatory variables (the default should be ok in most cases): + +SET (CTEST_CVS_COMMAND "hg") +SET (CTEST_CVS_CHECKOUT "${CTEST_CVS_COMMAND} clone -r 2.0 http://bitbucket.org/eigen/eigen \"${CTEST_SOURCE_DIRECTORY}\"") + +# which ctest command to use for running the dashboard +SET (CTEST_COMMAND "${EIGEN_CMAKE_DIR}ctest -D ${EIGEN_MODE}") + +# what cmake command to use for configuring this dashboard +SET (CTEST_CMAKE_COMMAND "${EIGEN_CMAKE_DIR}cmake -DEIGEN_BUILD_TESTS=on ") + +#################################################################### +# The values in this section are optional you can either +# have them or leave them commented out +#################################################################### + +# this make sure we get consistent outputs +SET($ENV{LC_MESSAGES} "en_EN") + +# should ctest wipe the binary tree before running +SET(CTEST_START_WITH_EMPTY_BINARY_DIRECTORY TRUE) +SET(CTEST_BACKUP_AND_RESTORE TRUE) + +# this is the initial cache to use for the binary tree, be careful to escape +# any quotes inside of this string if you use it +if(WIN32 AND NOT UNIX) + #message(SEND_ERROR "win32") + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -G \"NMake Makefiles\" -DCMAKE_MAKE_PROGRAM=nmake") + SET (CTEST_INITIAL_CACHE " + MAKECOMMAND:STRING=nmake -i + CMAKE_MAKE_PROGRAM:FILEPATH=nmake + CMAKE_GENERATOR:INTERNAL=NMake Makefiles + BUILDNAME:STRING=${EIGEN_BUILD_STRING} + SITE:STRING=${EIGEN_SITE} + ") +else(WIN32 AND NOT UNIX) + SET (CTEST_INITIAL_CACHE " + BUILDNAME:STRING=${EIGEN_BUILD_STRING} + SITE:STRING=${EIGEN_SITE} + ") +endif(WIN32 AND NOT UNIX) + +# set any extra environment variables to use during the execution of the script here: + +if(EIGEN_CXX) + set(CTEST_ENVIRONMENT "CXX=${EIGEN_CXX}") +endif(EIGEN_CXX) + +if(DEFINED EIGEN_EXPLICIT_VECTORIZATION) + if(EIGEN_EXPLICIT_VECTORIZATION MATCHES SSE2) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -DEIGEN_TEST_SSE2=ON") + elseif(EIGEN_EXPLICIT_VECTORIZATION MATCHES SSE3) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -DEIGEN_TEST_SSE2=ON -DEIGEN_TEST_SSE3=ON") + elseif(EIGEN_EXPLICIT_VECTORIZATION MATCHES Altivec) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -DEIGEN_TEST_ALTIVEC=ON") + elseif(EIGEN_EXPLICIT_VECTORIZATION MATCHES novec) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -DEIGEN_TEST_NO_EXPLICIT_VECTORIZATION=ON") + else(EIGEN_EXPLICIT_VECTORIZATION MATCHES SSE2) + message(FATAL_ERROR "Invalid value for EIGEN_EXPLICIT_VECTORIZATION (${EIGEN_EXPLICIT_VECTORIZATION}), must be: novec, SSE2, SSE3, Altivec") + endif(EIGEN_EXPLICIT_VECTORIZATION MATCHES SSE2) +endif(DEFINED EIGEN_EXPLICIT_VECTORIZATION) + +if(DEFINED EIGEN_CMAKE_ARGS) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} ${EIGEN_CMAKE_ARGS}") +endif(DEFINED EIGEN_CMAKE_ARGS) diff --git a/test/eigen2support.cpp b/test/eigen2support.cpp new file mode 100644 index 000000000..7e02bdf5b --- /dev/null +++ b/test/eigen2support.cpp @@ -0,0 +1,64 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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/. + +#define EIGEN2_SUPPORT + +#include "main.h" + +template<typename MatrixType> void eigen2support(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m3(rows, cols); + + Scalar s1 = internal::random<Scalar>(), + s2 = internal::random<Scalar>(); + + // scalar addition + VERIFY_IS_APPROX(m1.cwise() + s1, s1 + m1.cwise()); + VERIFY_IS_APPROX(m1.cwise() + s1, MatrixType::Constant(rows,cols,s1) + m1); + VERIFY_IS_APPROX((m1*Scalar(2)).cwise() - s2, (m1+m1) - MatrixType::Constant(rows,cols,s2) ); + m3 = m1; + m3.cwise() += s2; + VERIFY_IS_APPROX(m3, m1.cwise() + s2); + m3 = m1; + m3.cwise() -= s1; + VERIFY_IS_APPROX(m3, m1.cwise() - s1); + + VERIFY_IS_EQUAL((m1.corner(TopLeft,1,1)), (m1.block(0,0,1,1))); + VERIFY_IS_EQUAL((m1.template corner<1,1>(TopLeft)), (m1.template block<1,1>(0,0))); + VERIFY_IS_EQUAL((m1.col(0).start(1)), (m1.col(0).segment(0,1))); + VERIFY_IS_EQUAL((m1.col(0).template start<1>()), (m1.col(0).segment(0,1))); + VERIFY_IS_EQUAL((m1.col(0).end(1)), (m1.col(0).segment(rows-1,1))); + VERIFY_IS_EQUAL((m1.col(0).template end<1>()), (m1.col(0).segment(rows-1,1))); + + using namespace internal; + VERIFY_IS_EQUAL(ei_cos(s1), cos(s1)); + VERIFY_IS_EQUAL(ei_real(s1), real(s1)); + VERIFY_IS_EQUAL(ei_abs2(s1), abs2(s1)); + + m1.minor(0,0); +} + +void test_eigen2support() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( eigen2support(Matrix<double,1,1>()) ); + CALL_SUBTEST_2( eigen2support(MatrixXd(1,1)) ); + CALL_SUBTEST_4( eigen2support(Matrix3f()) ); + CALL_SUBTEST_5( eigen2support(Matrix4d()) ); + CALL_SUBTEST_2( eigen2support(MatrixXf(200,200)) ); + CALL_SUBTEST_6( eigen2support(MatrixXcd(100,100)) ); + } +} diff --git a/test/eigensolver_complex.cpp b/test/eigensolver_complex.cpp new file mode 100644 index 000000000..0c2059512 --- /dev/null +++ b/test/eigensolver_complex.cpp @@ -0,0 +1,115 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2010 Jitse Niesen <jitse@maths.leeds.ac.uk> +// +// 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" +#include <limits> +#include <Eigen/Eigenvalues> +#include <Eigen/LU> + +/* Check that two column vectors are approximately equal upto permutations, + by checking that the k-th power sums are equal for k = 1, ..., vec1.rows() */ +template<typename VectorType> +void verify_is_approx_upto_permutation(const VectorType& vec1, const VectorType& vec2) +{ + typedef typename NumTraits<typename VectorType::Scalar>::Real RealScalar; + + VERIFY(vec1.cols() == 1); + VERIFY(vec2.cols() == 1); + VERIFY(vec1.rows() == vec2.rows()); + for (int k = 1; k <= vec1.rows(); ++k) + { + VERIFY_IS_APPROX(vec1.array().pow(RealScalar(k)).sum(), vec2.array().pow(RealScalar(k)).sum()); + } +} + + +template<typename MatrixType> void eigensolver(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + /* this test covers the following files: + ComplexEigenSolver.h, and indirectly ComplexSchur.h + */ + Index rows = m.rows(); + Index cols = m.cols(); + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<RealScalar, MatrixType::RowsAtCompileTime, 1> RealVectorType; + typedef typename std::complex<typename NumTraits<typename MatrixType::Scalar>::Real> Complex; + + MatrixType a = MatrixType::Random(rows,cols); + MatrixType symmA = a.adjoint() * a; + + ComplexEigenSolver<MatrixType> ei0(symmA); + VERIFY_IS_EQUAL(ei0.info(), Success); + VERIFY_IS_APPROX(symmA * ei0.eigenvectors(), ei0.eigenvectors() * ei0.eigenvalues().asDiagonal()); + + ComplexEigenSolver<MatrixType> ei1(a); + VERIFY_IS_EQUAL(ei1.info(), Success); + VERIFY_IS_APPROX(a * ei1.eigenvectors(), ei1.eigenvectors() * ei1.eigenvalues().asDiagonal()); + // Note: If MatrixType is real then a.eigenvalues() uses EigenSolver and thus + // another algorithm so results may differ slightly + verify_is_approx_upto_permutation(a.eigenvalues(), ei1.eigenvalues()); + + ComplexEigenSolver<MatrixType> eiNoEivecs(a, false); + VERIFY_IS_EQUAL(eiNoEivecs.info(), Success); + VERIFY_IS_APPROX(ei1.eigenvalues(), eiNoEivecs.eigenvalues()); + + // Regression test for issue #66 + MatrixType z = MatrixType::Zero(rows,cols); + ComplexEigenSolver<MatrixType> eiz(z); + VERIFY((eiz.eigenvalues().cwiseEqual(0)).all()); + + MatrixType id = MatrixType::Identity(rows, cols); + VERIFY_IS_APPROX(id.operatorNorm(), RealScalar(1)); + + if (rows > 1) + { + // Test matrix with NaN + a(0,0) = std::numeric_limits<typename MatrixType::RealScalar>::quiet_NaN(); + ComplexEigenSolver<MatrixType> eiNaN(a); + VERIFY_IS_EQUAL(eiNaN.info(), NoConvergence); + } +} + +template<typename MatrixType> void eigensolver_verify_assert(const MatrixType& m) +{ + ComplexEigenSolver<MatrixType> eig; + VERIFY_RAISES_ASSERT(eig.eigenvectors()); + VERIFY_RAISES_ASSERT(eig.eigenvalues()); + + MatrixType a = MatrixType::Random(m.rows(),m.cols()); + eig.compute(a, false); + VERIFY_RAISES_ASSERT(eig.eigenvectors()); +} + +void test_eigensolver_complex() +{ + int s; + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( eigensolver(Matrix4cf()) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/4); + CALL_SUBTEST_2( eigensolver(MatrixXcd(s,s)) ); + CALL_SUBTEST_3( eigensolver(Matrix<std::complex<float>, 1, 1>()) ); + CALL_SUBTEST_4( eigensolver(Matrix3f()) ); + } + + CALL_SUBTEST_1( eigensolver_verify_assert(Matrix4cf()) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/4); + CALL_SUBTEST_2( eigensolver_verify_assert(MatrixXcd(s,s)) ); + CALL_SUBTEST_3( eigensolver_verify_assert(Matrix<std::complex<float>, 1, 1>()) ); + CALL_SUBTEST_4( eigensolver_verify_assert(Matrix3f()) ); + + // Test problem size constructors + CALL_SUBTEST_5(ComplexEigenSolver<MatrixXf>(s)); + + EIGEN_UNUSED_VARIABLE(s) +} diff --git a/test/eigensolver_generic.cpp b/test/eigensolver_generic.cpp new file mode 100644 index 000000000..0b55ccd93 --- /dev/null +++ b/test/eigensolver_generic.cpp @@ -0,0 +1,115 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2010 Jitse Niesen <jitse@maths.leeds.ac.uk> +// +// 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" +#include <limits> +#include <Eigen/Eigenvalues> + +template<typename MatrixType> void eigensolver(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + /* this test covers the following files: + EigenSolver.h + */ + Index rows = m.rows(); + Index cols = m.cols(); + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<RealScalar, MatrixType::RowsAtCompileTime, 1> RealVectorType; + typedef typename std::complex<typename NumTraits<typename MatrixType::Scalar>::Real> Complex; + + MatrixType a = MatrixType::Random(rows,cols); + MatrixType a1 = MatrixType::Random(rows,cols); + MatrixType symmA = a.adjoint() * a + a1.adjoint() * a1; + + EigenSolver<MatrixType> ei0(symmA); + VERIFY_IS_EQUAL(ei0.info(), Success); + VERIFY_IS_APPROX(symmA * ei0.pseudoEigenvectors(), ei0.pseudoEigenvectors() * ei0.pseudoEigenvalueMatrix()); + VERIFY_IS_APPROX((symmA.template cast<Complex>()) * (ei0.pseudoEigenvectors().template cast<Complex>()), + (ei0.pseudoEigenvectors().template cast<Complex>()) * (ei0.eigenvalues().asDiagonal())); + + EigenSolver<MatrixType> ei1(a); + VERIFY_IS_EQUAL(ei1.info(), Success); + VERIFY_IS_APPROX(a * ei1.pseudoEigenvectors(), ei1.pseudoEigenvectors() * ei1.pseudoEigenvalueMatrix()); + VERIFY_IS_APPROX(a.template cast<Complex>() * ei1.eigenvectors(), + ei1.eigenvectors() * ei1.eigenvalues().asDiagonal()); + VERIFY_IS_APPROX(ei1.eigenvectors().colwise().norm(), RealVectorType::Ones(rows).transpose()); + VERIFY_IS_APPROX(a.eigenvalues(), ei1.eigenvalues()); + + EigenSolver<MatrixType> eiNoEivecs(a, false); + VERIFY_IS_EQUAL(eiNoEivecs.info(), Success); + VERIFY_IS_APPROX(ei1.eigenvalues(), eiNoEivecs.eigenvalues()); + VERIFY_IS_APPROX(ei1.pseudoEigenvalueMatrix(), eiNoEivecs.pseudoEigenvalueMatrix()); + + MatrixType id = MatrixType::Identity(rows, cols); + VERIFY_IS_APPROX(id.operatorNorm(), RealScalar(1)); + + if (rows > 2) + { + // Test matrix with NaN + a(0,0) = std::numeric_limits<typename MatrixType::RealScalar>::quiet_NaN(); + EigenSolver<MatrixType> eiNaN(a); + VERIFY_IS_EQUAL(eiNaN.info(), NoConvergence); + } +} + +template<typename MatrixType> void eigensolver_verify_assert(const MatrixType& m) +{ + EigenSolver<MatrixType> eig; + VERIFY_RAISES_ASSERT(eig.eigenvectors()); + VERIFY_RAISES_ASSERT(eig.pseudoEigenvectors()); + VERIFY_RAISES_ASSERT(eig.pseudoEigenvalueMatrix()); + VERIFY_RAISES_ASSERT(eig.eigenvalues()); + + MatrixType a = MatrixType::Random(m.rows(),m.cols()); + eig.compute(a, false); + VERIFY_RAISES_ASSERT(eig.eigenvectors()); + VERIFY_RAISES_ASSERT(eig.pseudoEigenvectors()); +} + +void test_eigensolver_generic() +{ + int s; + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( eigensolver(Matrix4f()) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/4); + CALL_SUBTEST_2( eigensolver(MatrixXd(s,s)) ); + + // some trivial but implementation-wise tricky cases + CALL_SUBTEST_2( eigensolver(MatrixXd(1,1)) ); + CALL_SUBTEST_2( eigensolver(MatrixXd(2,2)) ); + CALL_SUBTEST_3( eigensolver(Matrix<double,1,1>()) ); + CALL_SUBTEST_4( eigensolver(Matrix2d()) ); + } + + CALL_SUBTEST_1( eigensolver_verify_assert(Matrix4f()) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/4); + CALL_SUBTEST_2( eigensolver_verify_assert(MatrixXd(s,s)) ); + CALL_SUBTEST_3( eigensolver_verify_assert(Matrix<double,1,1>()) ); + CALL_SUBTEST_4( eigensolver_verify_assert(Matrix2d()) ); + + // Test problem size constructors + CALL_SUBTEST_5(EigenSolver<MatrixXf>(s)); + + // regression test for bug 410 + CALL_SUBTEST_2( + { + MatrixXd A(1,1); + A(0,0) = std::sqrt(-1.); + Eigen::EigenSolver<MatrixXd> solver(A); + MatrixXd V(1, 1); + V(0,0) = solver.eigenvectors()(0,0).real(); + } + ); + + EIGEN_UNUSED_VARIABLE(s) +} diff --git a/test/eigensolver_selfadjoint.cpp b/test/eigensolver_selfadjoint.cpp new file mode 100644 index 000000000..02dbdb429 --- /dev/null +++ b/test/eigensolver_selfadjoint.cpp @@ -0,0 +1,146 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2010 Jitse Niesen <jitse@maths.leeds.ac.uk> +// +// 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" +#include <limits> +#include <Eigen/Eigenvalues> + +template<typename MatrixType> void selfadjointeigensolver(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + /* this test covers the following files: + EigenSolver.h, SelfAdjointEigenSolver.h (and indirectly: Tridiagonalization.h) + */ + Index rows = m.rows(); + Index cols = m.cols(); + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<RealScalar, MatrixType::RowsAtCompileTime, 1> RealVectorType; + typedef typename std::complex<typename NumTraits<typename MatrixType::Scalar>::Real> Complex; + + RealScalar largerEps = 10*test_precision<RealScalar>(); + + MatrixType a = MatrixType::Random(rows,cols); + MatrixType a1 = MatrixType::Random(rows,cols); + MatrixType symmA = a.adjoint() * a + a1.adjoint() * a1; + symmA.template triangularView<StrictlyUpper>().setZero(); + + MatrixType b = MatrixType::Random(rows,cols); + MatrixType b1 = MatrixType::Random(rows,cols); + MatrixType symmB = b.adjoint() * b + b1.adjoint() * b1; + symmB.template triangularView<StrictlyUpper>().setZero(); + + SelfAdjointEigenSolver<MatrixType> eiSymm(symmA); + SelfAdjointEigenSolver<MatrixType> eiDirect; + eiDirect.computeDirect(symmA); + // generalized eigen pb + GeneralizedSelfAdjointEigenSolver<MatrixType> eiSymmGen(symmA, symmB); + + VERIFY_IS_EQUAL(eiSymm.info(), Success); + VERIFY((symmA.template selfadjointView<Lower>() * eiSymm.eigenvectors()).isApprox( + eiSymm.eigenvectors() * eiSymm.eigenvalues().asDiagonal(), largerEps)); + VERIFY_IS_APPROX(symmA.template selfadjointView<Lower>().eigenvalues(), eiSymm.eigenvalues()); + + VERIFY_IS_EQUAL(eiDirect.info(), Success); + VERIFY((symmA.template selfadjointView<Lower>() * eiDirect.eigenvectors()).isApprox( + eiDirect.eigenvectors() * eiDirect.eigenvalues().asDiagonal(), largerEps)); + VERIFY_IS_APPROX(symmA.template selfadjointView<Lower>().eigenvalues(), eiDirect.eigenvalues()); + + SelfAdjointEigenSolver<MatrixType> eiSymmNoEivecs(symmA, false); + VERIFY_IS_EQUAL(eiSymmNoEivecs.info(), Success); + VERIFY_IS_APPROX(eiSymm.eigenvalues(), eiSymmNoEivecs.eigenvalues()); + + // generalized eigen problem Ax = lBx + eiSymmGen.compute(symmA, symmB,Ax_lBx); + VERIFY_IS_EQUAL(eiSymmGen.info(), Success); + VERIFY((symmA.template selfadjointView<Lower>() * eiSymmGen.eigenvectors()).isApprox( + symmB.template selfadjointView<Lower>() * (eiSymmGen.eigenvectors() * eiSymmGen.eigenvalues().asDiagonal()), largerEps)); + + // generalized eigen problem BAx = lx + eiSymmGen.compute(symmA, symmB,BAx_lx); + VERIFY_IS_EQUAL(eiSymmGen.info(), Success); + VERIFY((symmB.template selfadjointView<Lower>() * (symmA.template selfadjointView<Lower>() * eiSymmGen.eigenvectors())).isApprox( + (eiSymmGen.eigenvectors() * eiSymmGen.eigenvalues().asDiagonal()), largerEps)); + + // generalized eigen problem ABx = lx + eiSymmGen.compute(symmA, symmB,ABx_lx); + VERIFY_IS_EQUAL(eiSymmGen.info(), Success); + VERIFY((symmA.template selfadjointView<Lower>() * (symmB.template selfadjointView<Lower>() * eiSymmGen.eigenvectors())).isApprox( + (eiSymmGen.eigenvectors() * eiSymmGen.eigenvalues().asDiagonal()), largerEps)); + + + MatrixType sqrtSymmA = eiSymm.operatorSqrt(); + VERIFY_IS_APPROX(MatrixType(symmA.template selfadjointView<Lower>()), sqrtSymmA*sqrtSymmA); + VERIFY_IS_APPROX(sqrtSymmA, symmA.template selfadjointView<Lower>()*eiSymm.operatorInverseSqrt()); + + MatrixType id = MatrixType::Identity(rows, cols); + VERIFY_IS_APPROX(id.template selfadjointView<Lower>().operatorNorm(), RealScalar(1)); + + SelfAdjointEigenSolver<MatrixType> eiSymmUninitialized; + VERIFY_RAISES_ASSERT(eiSymmUninitialized.info()); + VERIFY_RAISES_ASSERT(eiSymmUninitialized.eigenvalues()); + VERIFY_RAISES_ASSERT(eiSymmUninitialized.eigenvectors()); + VERIFY_RAISES_ASSERT(eiSymmUninitialized.operatorSqrt()); + VERIFY_RAISES_ASSERT(eiSymmUninitialized.operatorInverseSqrt()); + + eiSymmUninitialized.compute(symmA, false); + VERIFY_RAISES_ASSERT(eiSymmUninitialized.eigenvectors()); + VERIFY_RAISES_ASSERT(eiSymmUninitialized.operatorSqrt()); + VERIFY_RAISES_ASSERT(eiSymmUninitialized.operatorInverseSqrt()); + + // test Tridiagonalization's methods + Tridiagonalization<MatrixType> tridiag(symmA); + // FIXME tridiag.matrixQ().adjoint() does not work + VERIFY_IS_APPROX(MatrixType(symmA.template selfadjointView<Lower>()), tridiag.matrixQ() * tridiag.matrixT().eval() * MatrixType(tridiag.matrixQ()).adjoint()); + + if (rows > 1) + { + // Test matrix with NaN + symmA(0,0) = std::numeric_limits<typename MatrixType::RealScalar>::quiet_NaN(); + SelfAdjointEigenSolver<MatrixType> eiSymmNaN(symmA); + VERIFY_IS_EQUAL(eiSymmNaN.info(), NoConvergence); + } +} + +void test_eigensolver_selfadjoint() +{ + int s; + for(int i = 0; i < g_repeat; i++) { + // very important to test 3x3 and 2x2 matrices since we provide special paths for them + CALL_SUBTEST_1( selfadjointeigensolver(Matrix2d()) ); + CALL_SUBTEST_1( selfadjointeigensolver(Matrix3f()) ); + CALL_SUBTEST_2( selfadjointeigensolver(Matrix4d()) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/4); + CALL_SUBTEST_3( selfadjointeigensolver(MatrixXf(s,s)) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/4); + CALL_SUBTEST_4( selfadjointeigensolver(MatrixXd(s,s)) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/4); + CALL_SUBTEST_5( selfadjointeigensolver(MatrixXcd(s,s)) ); + + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/4); + CALL_SUBTEST_9( selfadjointeigensolver(Matrix<std::complex<double>,Dynamic,Dynamic,RowMajor>(s,s)) ); + + // some trivial but implementation-wise tricky cases + CALL_SUBTEST_4( selfadjointeigensolver(MatrixXd(1,1)) ); + CALL_SUBTEST_4( selfadjointeigensolver(MatrixXd(2,2)) ); + CALL_SUBTEST_6( selfadjointeigensolver(Matrix<double,1,1>()) ); + CALL_SUBTEST_7( selfadjointeigensolver(Matrix<double,2,2>()) ); + } + + // Test problem size constructors + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/4); + CALL_SUBTEST_8(SelfAdjointEigenSolver<MatrixXf>(s)); + CALL_SUBTEST_8(Tridiagonalization<MatrixXf>(s)); + + EIGEN_UNUSED_VARIABLE(s) +} + diff --git a/test/exceptions.cpp b/test/exceptions.cpp new file mode 100644 index 000000000..8c48b2f7b --- /dev/null +++ b/test/exceptions.cpp @@ -0,0 +1,109 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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/. + + +// Various sanity tests with exceptions: +// - no memory leak when a custom scalar type trow an exceptions +// - todo: complete the list of tests! + +#define EIGEN_STACK_ALLOCATION_LIMIT 100000000 + +#include "main.h" + +struct my_exception +{ + my_exception() {} + ~my_exception() {} +}; + +class ScalarWithExceptions +{ + public: + ScalarWithExceptions() { init(); } + ScalarWithExceptions(const float& _v) { init(); *v = _v; } + ScalarWithExceptions(const ScalarWithExceptions& other) { init(); *v = *(other.v); } + ~ScalarWithExceptions() { + delete v; + instances--; + } + + void init() { + v = new float; + instances++; + } + + ScalarWithExceptions operator+(const ScalarWithExceptions& other) const + { + countdown--; + if(countdown<=0) + throw my_exception(); + return ScalarWithExceptions(*v+*other.v); + } + + ScalarWithExceptions operator-(const ScalarWithExceptions& other) const + { return ScalarWithExceptions(*v-*other.v); } + + ScalarWithExceptions operator*(const ScalarWithExceptions& other) const + { return ScalarWithExceptions((*v)*(*other.v)); } + + ScalarWithExceptions& operator+=(const ScalarWithExceptions& other) + { *v+=*other.v; return *this; } + ScalarWithExceptions& operator-=(const ScalarWithExceptions& other) + { *v-=*other.v; return *this; } + ScalarWithExceptions& operator=(const ScalarWithExceptions& other) + { *v = *(other.v); return *this; } + + bool operator==(const ScalarWithExceptions& other) const + { return *v==*other.v; } + bool operator!=(const ScalarWithExceptions& other) const + { return *v!=*other.v; } + + float* v; + static int instances; + static int countdown; +}; + +int ScalarWithExceptions::instances = 0; +int ScalarWithExceptions::countdown = 0; + + +#define CHECK_MEMLEAK(OP) { \ + ScalarWithExceptions::countdown = 100; \ + int before = ScalarWithExceptions::instances; \ + bool exception_thrown = false; \ + try { OP; } \ + catch (my_exception) { \ + exception_thrown = true; \ + VERIFY(ScalarWithExceptions::instances==before && "memory leak detected in " && EIGEN_MAKESTRING(OP)); \ + } \ + VERIFY(exception_thrown && " no exception thrown in " && EIGEN_MAKESTRING(OP)); \ + } + +void memoryleak() +{ + typedef Eigen::Matrix<ScalarWithExceptions,Dynamic,1> VectorType; + typedef Eigen::Matrix<ScalarWithExceptions,Dynamic,Dynamic> MatrixType; + + { + int n = 50; + VectorType v0(n), v1(n); + MatrixType m0(n,n), m1(n,n), m2(n,n); + v0.setOnes(); v1.setOnes(); + m0.setOnes(); m1.setOnes(); m2.setOnes(); + CHECK_MEMLEAK(v0 = m0 * m1 * v1); + CHECK_MEMLEAK(m2 = m0 * m1 * m2); + CHECK_MEMLEAK((v0+v1).dot(v0+v1)); + } + VERIFY(ScalarWithExceptions::instances==0 && "global memory leak detected in " && EIGEN_MAKESTRING(OP)); \ +} + +void test_exceptions() +{ + CALL_SUBTEST( memoryleak() ); +} diff --git a/test/first_aligned.cpp b/test/first_aligned.cpp new file mode 100644 index 000000000..467f94510 --- /dev/null +++ b/test/first_aligned.cpp @@ -0,0 +1,51 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename Scalar> +void test_first_aligned_helper(Scalar *array, int size) +{ + const int packet_size = sizeof(Scalar) * internal::packet_traits<Scalar>::size; + VERIFY(((size_t(array) + sizeof(Scalar) * internal::first_aligned(array, size)) % packet_size) == 0); +} + +template<typename Scalar> +void test_none_aligned_helper(Scalar *array, int size) +{ + EIGEN_UNUSED_VARIABLE(array); + EIGEN_UNUSED_VARIABLE(size); + VERIFY(internal::packet_traits<Scalar>::size == 1 || internal::first_aligned(array, size) == size); +} + +struct some_non_vectorizable_type { float x; }; + +void test_first_aligned() +{ + EIGEN_ALIGN16 float array_float[100]; + test_first_aligned_helper(array_float, 50); + test_first_aligned_helper(array_float+1, 50); + test_first_aligned_helper(array_float+2, 50); + test_first_aligned_helper(array_float+3, 50); + test_first_aligned_helper(array_float+4, 50); + test_first_aligned_helper(array_float+5, 50); + + EIGEN_ALIGN16 double array_double[100]; + test_first_aligned_helper(array_double, 50); + test_first_aligned_helper(array_double+1, 50); + test_first_aligned_helper(array_double+2, 50); + + double *array_double_plus_4_bytes = (double*)(size_t(array_double)+4); + test_none_aligned_helper(array_double_plus_4_bytes, 50); + test_none_aligned_helper(array_double_plus_4_bytes+1, 50); + + some_non_vectorizable_type array_nonvec[100]; + test_first_aligned_helper(array_nonvec, 100); + test_none_aligned_helper(array_nonvec, 100); +} diff --git a/test/geo_alignedbox.cpp b/test/geo_alignedbox.cpp new file mode 100644 index 000000000..5886f9181 --- /dev/null +++ b/test/geo_alignedbox.cpp @@ -0,0 +1,171 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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" +#include <Eigen/Geometry> +#include <Eigen/LU> +#include <Eigen/QR> + +#include<iostream> +using namespace std; + +template<typename BoxType> void alignedbox(const BoxType& _box) +{ + /* this test covers the following files: + AlignedBox.h + */ + typedef typename BoxType::Index Index; + typedef typename BoxType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, BoxType::AmbientDimAtCompileTime, 1> VectorType; + + const Index dim = _box.dim(); + + VectorType p0 = VectorType::Random(dim); + VectorType p1 = VectorType::Random(dim); + while( p1 == p0 ){ + p1 = VectorType::Random(dim); } + RealScalar s1 = internal::random<RealScalar>(0,1); + + BoxType b0(dim); + BoxType b1(VectorType::Random(dim),VectorType::Random(dim)); + BoxType b2; + + b0.extend(p0); + b0.extend(p1); + VERIFY(b0.contains(p0*s1+(Scalar(1)-s1)*p1)); + + (b2 = b0).extend(b1); + VERIFY(b2.contains(b0)); + VERIFY(b2.contains(b1)); + VERIFY_IS_APPROX(b2.clamp(b0), b0); + + + // alignment -- make sure there is no memory alignment assertion + BoxType *bp0 = new BoxType(dim); + BoxType *bp1 = new BoxType(dim); + bp0->extend(*bp1); + delete bp0; + delete bp1; + + // sampling + for( int i=0; i<10; ++i ) + { + VectorType r = b0.sample(); + VERIFY(b0.contains(r)); + } + +} + + + +template<typename BoxType> +void alignedboxCastTests(const BoxType& _box) +{ + // casting + typedef typename BoxType::Index Index; + typedef typename BoxType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, BoxType::AmbientDimAtCompileTime, 1> VectorType; + + const Index dim = _box.dim(); + + VectorType p0 = VectorType::Random(dim); + VectorType p1 = VectorType::Random(dim); + + BoxType b0(dim); + + b0.extend(p0); + b0.extend(p1); + + const int Dim = BoxType::AmbientDimAtCompileTime; + typedef typename GetDifferentType<Scalar>::type OtherScalar; + AlignedBox<OtherScalar,Dim> hp1f = b0.template cast<OtherScalar>(); + VERIFY_IS_APPROX(hp1f.template cast<Scalar>(),b0); + AlignedBox<Scalar,Dim> hp1d = b0.template cast<Scalar>(); + VERIFY_IS_APPROX(hp1d.template cast<Scalar>(),b0); +} + + +void specificTest1() +{ + Vector2f m; m << -1.0f, -2.0f; + Vector2f M; M << 1.0f, 5.0f; + + typedef AlignedBox2f BoxType; + BoxType box( m, M ); + + Vector2f sides = M-m; + VERIFY_IS_APPROX(sides, box.sizes() ); + VERIFY_IS_APPROX(sides[1], box.sizes()[1] ); + VERIFY_IS_APPROX(sides[1], box.sizes().maxCoeff() ); + VERIFY_IS_APPROX(sides[0], box.sizes().minCoeff() ); + + VERIFY_IS_APPROX( 14.0f, box.volume() ); + VERIFY_IS_APPROX( 53.0f, box.diagonal().squaredNorm() ); + VERIFY_IS_APPROX( internal::sqrt( 53.0f ), box.diagonal().norm() ); + + VERIFY_IS_APPROX( m, box.corner( BoxType::BottomLeft ) ); + VERIFY_IS_APPROX( M, box.corner( BoxType::TopRight ) ); + Vector2f bottomRight; bottomRight << M[0], m[1]; + Vector2f topLeft; topLeft << m[0], M[1]; + VERIFY_IS_APPROX( bottomRight, box.corner( BoxType::BottomRight ) ); + VERIFY_IS_APPROX( topLeft, box.corner( BoxType::TopLeft ) ); +} + + +void specificTest2() +{ + Vector3i m; m << -1, -2, 0; + Vector3i M; M << 1, 5, 3; + + typedef AlignedBox3i BoxType; + BoxType box( m, M ); + + Vector3i sides = M-m; + VERIFY_IS_APPROX(sides, box.sizes() ); + VERIFY_IS_APPROX(sides[1], box.sizes()[1] ); + VERIFY_IS_APPROX(sides[1], box.sizes().maxCoeff() ); + VERIFY_IS_APPROX(sides[0], box.sizes().minCoeff() ); + + VERIFY_IS_APPROX( 42, box.volume() ); + VERIFY_IS_APPROX( 62, box.diagonal().squaredNorm() ); + + VERIFY_IS_APPROX( m, box.corner( BoxType::BottomLeftFloor ) ); + VERIFY_IS_APPROX( M, box.corner( BoxType::TopRightCeil ) ); + Vector3i bottomRightFloor; bottomRightFloor << M[0], m[1], m[2]; + Vector3i topLeftFloor; topLeftFloor << m[0], M[1], m[2]; + VERIFY_IS_APPROX( bottomRightFloor, box.corner( BoxType::BottomRightFloor ) ); + VERIFY_IS_APPROX( topLeftFloor, box.corner( BoxType::TopLeftFloor ) ); +} + + +void test_geo_alignedbox() +{ + for(int i = 0; i < g_repeat; i++) + { + CALL_SUBTEST_1( alignedbox(AlignedBox2f()) ); + CALL_SUBTEST_2( alignedboxCastTests(AlignedBox2f()) ); + + CALL_SUBTEST_3( alignedbox(AlignedBox3f()) ); + CALL_SUBTEST_4( alignedboxCastTests(AlignedBox3f()) ); + + CALL_SUBTEST_5( alignedbox(AlignedBox4d()) ); + CALL_SUBTEST_6( alignedboxCastTests(AlignedBox4d()) ); + + CALL_SUBTEST_7( alignedbox(AlignedBox1d()) ); + CALL_SUBTEST_8( alignedboxCastTests(AlignedBox1d()) ); + + CALL_SUBTEST_9( alignedbox(AlignedBox1i()) ); + CALL_SUBTEST_10( alignedbox(AlignedBox2i()) ); + CALL_SUBTEST_11( alignedbox(AlignedBox3i()) ); + } + CALL_SUBTEST_12( specificTest1() ); + CALL_SUBTEST_13( specificTest2() ); +} diff --git a/test/geo_eulerangles.cpp b/test/geo_eulerangles.cpp new file mode 100644 index 000000000..9bf149d2a --- /dev/null +++ b/test/geo_eulerangles.cpp @@ -0,0 +1,54 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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" +#include <Eigen/Geometry> +#include <Eigen/LU> +#include <Eigen/SVD> + +template<typename Scalar> void eulerangles(void) +{ + typedef Matrix<Scalar,3,3> Matrix3; + typedef Matrix<Scalar,3,1> Vector3; + typedef Quaternion<Scalar> Quaternionx; + typedef AngleAxis<Scalar> AngleAxisx; + + Scalar a = internal::random<Scalar>(-Scalar(M_PI), Scalar(M_PI)); + Quaternionx q1; + q1 = AngleAxisx(a, Vector3::Random().normalized()); + Matrix3 m; + m = q1; + + #define VERIFY_EULER(I,J,K, X,Y,Z) { \ + Vector3 ea = m.eulerAngles(I,J,K); \ + VERIFY_IS_APPROX(m, Matrix3(AngleAxisx(ea[0], Vector3::Unit##X()) * AngleAxisx(ea[1], Vector3::Unit##Y()) * AngleAxisx(ea[2], Vector3::Unit##Z()))); \ + } + VERIFY_EULER(0,1,2, X,Y,Z); + VERIFY_EULER(0,1,0, X,Y,X); + VERIFY_EULER(0,2,1, X,Z,Y); + VERIFY_EULER(0,2,0, X,Z,X); + + VERIFY_EULER(1,2,0, Y,Z,X); + VERIFY_EULER(1,2,1, Y,Z,Y); + VERIFY_EULER(1,0,2, Y,X,Z); + VERIFY_EULER(1,0,1, Y,X,Y); + + VERIFY_EULER(2,0,1, Z,X,Y); + VERIFY_EULER(2,0,2, Z,X,Z); + VERIFY_EULER(2,1,0, Z,Y,X); + VERIFY_EULER(2,1,2, Z,Y,Z); +} + +void test_geo_eulerangles() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( eulerangles<float>() ); + CALL_SUBTEST_2( eulerangles<double>() ); + } +} diff --git a/test/geo_homogeneous.cpp b/test/geo_homogeneous.cpp new file mode 100644 index 000000000..c91bde819 --- /dev/null +++ b/test/geo_homogeneous.cpp @@ -0,0 +1,103 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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" +#include <Eigen/Geometry> + +template<typename Scalar,int Size> void homogeneous(void) +{ + /* this test covers the following files: + Homogeneous.h + */ + + typedef Matrix<Scalar,Size,Size> MatrixType; + typedef Matrix<Scalar,Size,1, ColMajor> VectorType; + + typedef Matrix<Scalar,Size+1,Size> HMatrixType; + typedef Matrix<Scalar,Size+1,1> HVectorType; + + typedef Matrix<Scalar,Size,Size+1> T1MatrixType; + typedef Matrix<Scalar,Size+1,Size+1> T2MatrixType; + typedef Matrix<Scalar,Size+1,Size> T3MatrixType; + + VectorType v0 = VectorType::Random(), + ones = VectorType::Ones(); + + HVectorType hv0 = HVectorType::Random(); + + MatrixType m0 = MatrixType::Random(); + + HMatrixType hm0 = HMatrixType::Random(); + + hv0 << v0, 1; + VERIFY_IS_APPROX(v0.homogeneous(), hv0); + VERIFY_IS_APPROX(v0, hv0.hnormalized()); + + hm0 << m0, ones.transpose(); + VERIFY_IS_APPROX(m0.colwise().homogeneous(), hm0); + VERIFY_IS_APPROX(m0, hm0.colwise().hnormalized()); + hm0.row(Size-1).setRandom(); + for(int j=0; j<Size; ++j) + m0.col(j) = hm0.col(j).head(Size) / hm0(Size,j); + VERIFY_IS_APPROX(m0, hm0.colwise().hnormalized()); + + T1MatrixType t1 = T1MatrixType::Random(); + VERIFY_IS_APPROX(t1 * (v0.homogeneous().eval()), t1 * v0.homogeneous()); + VERIFY_IS_APPROX(t1 * (m0.colwise().homogeneous().eval()), t1 * m0.colwise().homogeneous()); + + T2MatrixType t2 = T2MatrixType::Random(); + VERIFY_IS_APPROX(t2 * (v0.homogeneous().eval()), t2 * v0.homogeneous()); + VERIFY_IS_APPROX(t2 * (m0.colwise().homogeneous().eval()), t2 * m0.colwise().homogeneous()); + + VERIFY_IS_APPROX((v0.transpose().rowwise().homogeneous().eval()) * t2, + v0.transpose().rowwise().homogeneous() * t2); + m0.transpose().rowwise().homogeneous().eval(); + VERIFY_IS_APPROX((m0.transpose().rowwise().homogeneous().eval()) * t2, + m0.transpose().rowwise().homogeneous() * t2); + + T3MatrixType t3 = T3MatrixType::Random(); + VERIFY_IS_APPROX((v0.transpose().rowwise().homogeneous().eval()) * t3, + v0.transpose().rowwise().homogeneous() * t3); + VERIFY_IS_APPROX((m0.transpose().rowwise().homogeneous().eval()) * t3, + m0.transpose().rowwise().homogeneous() * t3); + + // test product with a Transform object + Transform<Scalar, Size, Affine> aff; + Transform<Scalar, Size, AffineCompact> caff; + Transform<Scalar, Size, Projective> proj; + Matrix<Scalar, Size, Dynamic> pts; + Matrix<Scalar, Size+1, Dynamic> pts1, pts2; + + aff.affine().setRandom(); + proj = caff = aff; + pts.setRandom(Size,internal::random<int>(1,20)); + + pts1 = pts.colwise().homogeneous(); + VERIFY_IS_APPROX(aff * pts.colwise().homogeneous(), (aff * pts1).colwise().hnormalized()); + VERIFY_IS_APPROX(caff * pts.colwise().homogeneous(), (caff * pts1).colwise().hnormalized()); + VERIFY_IS_APPROX(proj * pts.colwise().homogeneous(), (proj * pts1)); + + VERIFY_IS_APPROX((aff * pts1).colwise().hnormalized(), aff * pts); + VERIFY_IS_APPROX((caff * pts1).colwise().hnormalized(), caff * pts); + + pts2 = pts1; + pts2.row(Size).setRandom(); + VERIFY_IS_APPROX((aff * pts2).colwise().hnormalized(), aff * pts2.colwise().hnormalized()); + VERIFY_IS_APPROX((caff * pts2).colwise().hnormalized(), caff * pts2.colwise().hnormalized()); + VERIFY_IS_APPROX((proj * pts2).colwise().hnormalized(), (proj * pts2.colwise().hnormalized().colwise().homogeneous()).colwise().hnormalized()); +} + +void test_geo_homogeneous() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1(( homogeneous<float,1>() )); + CALL_SUBTEST_2(( homogeneous<double,3>() )); + CALL_SUBTEST_3(( homogeneous<double,8>() )); + } +} diff --git a/test/geo_hyperplane.cpp b/test/geo_hyperplane.cpp new file mode 100644 index 000000000..3fc80c4c7 --- /dev/null +++ b/test/geo_hyperplane.cpp @@ -0,0 +1,157 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/Geometry> +#include <Eigen/LU> +#include <Eigen/QR> + +template<typename HyperplaneType> void hyperplane(const HyperplaneType& _plane) +{ + /* this test covers the following files: + Hyperplane.h + */ + typedef typename HyperplaneType::Index Index; + const Index dim = _plane.dim(); + enum { Options = HyperplaneType::Options }; + typedef typename HyperplaneType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, HyperplaneType::AmbientDimAtCompileTime, 1> VectorType; + typedef Matrix<Scalar, HyperplaneType::AmbientDimAtCompileTime, + HyperplaneType::AmbientDimAtCompileTime> MatrixType; + + VectorType p0 = VectorType::Random(dim); + VectorType p1 = VectorType::Random(dim); + + VectorType n0 = VectorType::Random(dim).normalized(); + VectorType n1 = VectorType::Random(dim).normalized(); + + HyperplaneType pl0(n0, p0); + HyperplaneType pl1(n1, p1); + HyperplaneType pl2 = pl1; + + Scalar s0 = internal::random<Scalar>(); + Scalar s1 = internal::random<Scalar>(); + + VERIFY_IS_APPROX( n1.dot(n1), Scalar(1) ); + + VERIFY_IS_MUCH_SMALLER_THAN( pl0.absDistance(p0), Scalar(1) ); + VERIFY_IS_APPROX( pl1.signedDistance(p1 + n1 * s0), s0 ); + VERIFY_IS_MUCH_SMALLER_THAN( pl1.signedDistance(pl1.projection(p0)), Scalar(1) ); + VERIFY_IS_MUCH_SMALLER_THAN( pl1.absDistance(p1 + pl1.normal().unitOrthogonal() * s1), Scalar(1) ); + + // transform + if (!NumTraits<Scalar>::IsComplex) + { + MatrixType rot = MatrixType::Random(dim,dim).householderQr().householderQ(); + DiagonalMatrix<Scalar,HyperplaneType::AmbientDimAtCompileTime> scaling(VectorType::Random()); + Translation<Scalar,HyperplaneType::AmbientDimAtCompileTime> translation(VectorType::Random()); + + pl2 = pl1; + VERIFY_IS_MUCH_SMALLER_THAN( pl2.transform(rot).absDistance(rot * p1), Scalar(1) ); + pl2 = pl1; + VERIFY_IS_MUCH_SMALLER_THAN( pl2.transform(rot,Isometry).absDistance(rot * p1), Scalar(1) ); + pl2 = pl1; + VERIFY_IS_MUCH_SMALLER_THAN( pl2.transform(rot*scaling).absDistance((rot*scaling) * p1), Scalar(1) ); + pl2 = pl1; + VERIFY_IS_MUCH_SMALLER_THAN( pl2.transform(rot*scaling*translation) + .absDistance((rot*scaling*translation) * p1), Scalar(1) ); + pl2 = pl1; + VERIFY_IS_MUCH_SMALLER_THAN( pl2.transform(rot*translation,Isometry) + .absDistance((rot*translation) * p1), Scalar(1) ); + } + + // casting + const int Dim = HyperplaneType::AmbientDimAtCompileTime; + typedef typename GetDifferentType<Scalar>::type OtherScalar; + Hyperplane<OtherScalar,Dim,Options> hp1f = pl1.template cast<OtherScalar>(); + VERIFY_IS_APPROX(hp1f.template cast<Scalar>(),pl1); + Hyperplane<Scalar,Dim,Options> hp1d = pl1.template cast<Scalar>(); + VERIFY_IS_APPROX(hp1d.template cast<Scalar>(),pl1); +} + +template<typename Scalar> void lines() +{ + typedef Hyperplane<Scalar, 2> HLine; + typedef ParametrizedLine<Scalar, 2> PLine; + typedef Matrix<Scalar,2,1> Vector; + typedef Matrix<Scalar,3,1> CoeffsType; + + for(int i = 0; i < 10; i++) + { + Vector center = Vector::Random(); + Vector u = Vector::Random(); + Vector v = Vector::Random(); + Scalar a = internal::random<Scalar>(); + while (internal::abs(a-1) < 1e-4) a = internal::random<Scalar>(); + while (u.norm() < 1e-4) u = Vector::Random(); + while (v.norm() < 1e-4) v = Vector::Random(); + + HLine line_u = HLine::Through(center + u, center + a*u); + HLine line_v = HLine::Through(center + v, center + a*v); + + // the line equations should be normalized so that a^2+b^2=1 + VERIFY_IS_APPROX(line_u.normal().norm(), Scalar(1)); + VERIFY_IS_APPROX(line_v.normal().norm(), Scalar(1)); + + Vector result = line_u.intersection(line_v); + + // the lines should intersect at the point we called "center" + VERIFY_IS_APPROX(result, center); + + // check conversions between two types of lines + PLine pl(line_u); // gcc 3.3 will commit suicide if we don't name this variable + CoeffsType converted_coeffs = HLine(pl).coeffs(); + converted_coeffs *= (line_u.coeffs()[0])/(converted_coeffs[0]); + VERIFY(line_u.coeffs().isApprox(converted_coeffs)); + } +} + +template<typename Scalar> void hyperplane_alignment() +{ + typedef Hyperplane<Scalar,3,AutoAlign> Plane3a; + typedef Hyperplane<Scalar,3,DontAlign> Plane3u; + + EIGEN_ALIGN16 Scalar array1[4]; + EIGEN_ALIGN16 Scalar array2[4]; + EIGEN_ALIGN16 Scalar array3[4+1]; + Scalar* array3u = array3+1; + + Plane3a *p1 = ::new(reinterpret_cast<void*>(array1)) Plane3a; + Plane3u *p2 = ::new(reinterpret_cast<void*>(array2)) Plane3u; + Plane3u *p3 = ::new(reinterpret_cast<void*>(array3u)) Plane3u; + + p1->coeffs().setRandom(); + *p2 = *p1; + *p3 = *p1; + + VERIFY_IS_APPROX(p1->coeffs(), p2->coeffs()); + VERIFY_IS_APPROX(p1->coeffs(), p3->coeffs()); + + #if defined(EIGEN_VECTORIZE) && EIGEN_ALIGN_STATICALLY + if(internal::packet_traits<Scalar>::Vectorizable) + VERIFY_RAISES_ASSERT((::new(reinterpret_cast<void*>(array3u)) Plane3a)); + #endif +} + + +void test_geo_hyperplane() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( hyperplane(Hyperplane<float,2>()) ); + CALL_SUBTEST_2( hyperplane(Hyperplane<float,3>()) ); + CALL_SUBTEST_2( hyperplane(Hyperplane<float,3,DontAlign>()) ); + CALL_SUBTEST_2( hyperplane_alignment<float>() ); + CALL_SUBTEST_3( hyperplane(Hyperplane<double,4>()) ); + CALL_SUBTEST_4( hyperplane(Hyperplane<std::complex<double>,5>()) ); + CALL_SUBTEST_1( lines<float>() ); + CALL_SUBTEST_3( lines<double>() ); + } +} diff --git a/test/geo_orthomethods.cpp b/test/geo_orthomethods.cpp new file mode 100644 index 000000000..c836dae40 --- /dev/null +++ b/test/geo_orthomethods.cpp @@ -0,0 +1,121 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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" +#include <Eigen/Geometry> +#include <Eigen/LU> +#include <Eigen/SVD> + +/* this test covers the following files: + Geometry/OrthoMethods.h +*/ + +template<typename Scalar> void orthomethods_3() +{ + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar,3,3> Matrix3; + typedef Matrix<Scalar,3,1> Vector3; + + typedef Matrix<Scalar,4,1> Vector4; + + Vector3 v0 = Vector3::Random(), + v1 = Vector3::Random(), + v2 = Vector3::Random(); + + // cross product + VERIFY_IS_MUCH_SMALLER_THAN(v1.cross(v2).dot(v1), Scalar(1)); + VERIFY_IS_MUCH_SMALLER_THAN(v1.dot(v1.cross(v2)), Scalar(1)); + VERIFY_IS_MUCH_SMALLER_THAN(v1.cross(v2).dot(v2), Scalar(1)); + VERIFY_IS_MUCH_SMALLER_THAN(v2.dot(v1.cross(v2)), Scalar(1)); + Matrix3 mat3; + mat3 << v0.normalized(), + (v0.cross(v1)).normalized(), + (v0.cross(v1).cross(v0)).normalized(); + VERIFY(mat3.isUnitary()); + + + // colwise/rowwise cross product + mat3.setRandom(); + Vector3 vec3 = Vector3::Random(); + Matrix3 mcross; + int i = internal::random<int>(0,2); + mcross = mat3.colwise().cross(vec3); + VERIFY_IS_APPROX(mcross.col(i), mat3.col(i).cross(vec3)); + mcross = mat3.rowwise().cross(vec3); + VERIFY_IS_APPROX(mcross.row(i), mat3.row(i).cross(vec3)); + + // cross3 + Vector4 v40 = Vector4::Random(), + v41 = Vector4::Random(), + v42 = Vector4::Random(); + v40.w() = v41.w() = v42.w() = 0; + v42.template head<3>() = v40.template head<3>().cross(v41.template head<3>()); + VERIFY_IS_APPROX(v40.cross3(v41), v42); + + // check mixed product + typedef Matrix<RealScalar, 3, 1> RealVector3; + RealVector3 rv1 = RealVector3::Random(); + VERIFY_IS_APPROX(v1.cross(rv1.template cast<Scalar>()), v1.cross(rv1)); + VERIFY_IS_APPROX(rv1.template cast<Scalar>().cross(v1), rv1.cross(v1)); +} + +template<typename Scalar, int Size> void orthomethods(int size=Size) +{ + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar,Size,1> VectorType; + typedef Matrix<Scalar,3,Size> Matrix3N; + typedef Matrix<Scalar,Size,3> MatrixN3; + typedef Matrix<Scalar,3,1> Vector3; + + VectorType v0 = VectorType::Random(size); + + // unitOrthogonal + VERIFY_IS_MUCH_SMALLER_THAN(v0.unitOrthogonal().dot(v0), Scalar(1)); + VERIFY_IS_APPROX(v0.unitOrthogonal().norm(), RealScalar(1)); + + if (size>=3) + { + v0.template head<2>().setZero(); + v0.tail(size-2).setRandom(); + + VERIFY_IS_MUCH_SMALLER_THAN(v0.unitOrthogonal().dot(v0), Scalar(1)); + VERIFY_IS_APPROX(v0.unitOrthogonal().norm(), RealScalar(1)); + } + + // colwise/rowwise cross product + Vector3 vec3 = Vector3::Random(); + int i = internal::random<int>(0,size-1); + + Matrix3N mat3N(3,size), mcross3N(3,size); + mat3N.setRandom(); + mcross3N = mat3N.colwise().cross(vec3); + VERIFY_IS_APPROX(mcross3N.col(i), mat3N.col(i).cross(vec3)); + + MatrixN3 matN3(size,3), mcrossN3(size,3); + matN3.setRandom(); + mcrossN3 = matN3.rowwise().cross(vec3); + VERIFY_IS_APPROX(mcrossN3.row(i), matN3.row(i).cross(vec3)); +} + +void test_geo_orthomethods() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( orthomethods_3<float>() ); + CALL_SUBTEST_2( orthomethods_3<double>() ); + CALL_SUBTEST_4( orthomethods_3<std::complex<double> >() ); + CALL_SUBTEST_1( (orthomethods<float,2>()) ); + CALL_SUBTEST_2( (orthomethods<double,2>()) ); + CALL_SUBTEST_1( (orthomethods<float,3>()) ); + CALL_SUBTEST_2( (orthomethods<double,3>()) ); + CALL_SUBTEST_3( (orthomethods<float,7>()) ); + CALL_SUBTEST_4( (orthomethods<std::complex<double>,8>()) ); + CALL_SUBTEST_5( (orthomethods<float,Dynamic>(36)) ); + CALL_SUBTEST_6( (orthomethods<double,Dynamic>(35)) ); + } +} diff --git a/test/geo_parametrizedline.cpp b/test/geo_parametrizedline.cpp new file mode 100644 index 000000000..4e1f845dd --- /dev/null +++ b/test/geo_parametrizedline.cpp @@ -0,0 +1,105 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/Geometry> +#include <Eigen/LU> +#include <Eigen/QR> + +template<typename LineType> void parametrizedline(const LineType& _line) +{ + /* this test covers the following files: + ParametrizedLine.h + */ + typedef typename LineType::Index Index; + const Index dim = _line.dim(); + typedef typename LineType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, LineType::AmbientDimAtCompileTime, 1> VectorType; + typedef Matrix<Scalar, LineType::AmbientDimAtCompileTime, + LineType::AmbientDimAtCompileTime> MatrixType; + typedef Hyperplane<Scalar,LineType::AmbientDimAtCompileTime> HyperplaneType; + + VectorType p0 = VectorType::Random(dim); + VectorType p1 = VectorType::Random(dim); + + VectorType d0 = VectorType::Random(dim).normalized(); + + LineType l0(p0, d0); + + Scalar s0 = internal::random<Scalar>(); + Scalar s1 = internal::abs(internal::random<Scalar>()); + + VERIFY_IS_MUCH_SMALLER_THAN( l0.distance(p0), RealScalar(1) ); + VERIFY_IS_MUCH_SMALLER_THAN( l0.distance(p0+s0*d0), RealScalar(1) ); + VERIFY_IS_APPROX( (l0.projection(p1)-p1).norm(), l0.distance(p1) ); + VERIFY_IS_MUCH_SMALLER_THAN( l0.distance(l0.projection(p1)), RealScalar(1) ); + VERIFY_IS_APPROX( Scalar(l0.distance((p0+s0*d0) + d0.unitOrthogonal() * s1)), s1 ); + + // casting + const int Dim = LineType::AmbientDimAtCompileTime; + typedef typename GetDifferentType<Scalar>::type OtherScalar; + ParametrizedLine<OtherScalar,Dim> hp1f = l0.template cast<OtherScalar>(); + VERIFY_IS_APPROX(hp1f.template cast<Scalar>(),l0); + ParametrizedLine<Scalar,Dim> hp1d = l0.template cast<Scalar>(); + VERIFY_IS_APPROX(hp1d.template cast<Scalar>(),l0); + + // intersections + VectorType p2 = VectorType::Random(dim); + VectorType n2 = VectorType::Random(dim).normalized(); + HyperplaneType hp(p2,n2); + Scalar t = l0.intersectionParameter(hp); + VectorType pi = l0.pointAt(t); + VERIFY_IS_MUCH_SMALLER_THAN(hp.signedDistance(pi), RealScalar(1)); + VERIFY_IS_MUCH_SMALLER_THAN(l0.distance(pi), RealScalar(1)); + VERIFY_IS_APPROX(l0.intersectionPoint(hp), pi); +} + +template<typename Scalar> void parametrizedline_alignment() +{ + typedef ParametrizedLine<Scalar,4,AutoAlign> Line4a; + typedef ParametrizedLine<Scalar,4,DontAlign> Line4u; + + EIGEN_ALIGN16 Scalar array1[8]; + EIGEN_ALIGN16 Scalar array2[8]; + EIGEN_ALIGN16 Scalar array3[8+1]; + Scalar* array3u = array3+1; + + Line4a *p1 = ::new(reinterpret_cast<void*>(array1)) Line4a; + Line4u *p2 = ::new(reinterpret_cast<void*>(array2)) Line4u; + Line4u *p3 = ::new(reinterpret_cast<void*>(array3u)) Line4u; + + p1->origin().setRandom(); + p1->direction().setRandom(); + *p2 = *p1; + *p3 = *p1; + + VERIFY_IS_APPROX(p1->origin(), p2->origin()); + VERIFY_IS_APPROX(p1->origin(), p3->origin()); + VERIFY_IS_APPROX(p1->direction(), p2->direction()); + VERIFY_IS_APPROX(p1->direction(), p3->direction()); + + #if defined(EIGEN_VECTORIZE) && EIGEN_ALIGN_STATICALLY + if(internal::packet_traits<Scalar>::Vectorizable) + VERIFY_RAISES_ASSERT((::new(reinterpret_cast<void*>(array3u)) Line4a)); + #endif +} + +void test_geo_parametrizedline() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( parametrizedline(ParametrizedLine<float,2>()) ); + CALL_SUBTEST_2( parametrizedline(ParametrizedLine<float,3>()) ); + CALL_SUBTEST_2( parametrizedline_alignment<float>() ); + CALL_SUBTEST_3( parametrizedline(ParametrizedLine<double,4>()) ); + CALL_SUBTEST_3( parametrizedline_alignment<double>() ); + CALL_SUBTEST_4( parametrizedline(ParametrizedLine<std::complex<double>,5>()) ); + } +} diff --git a/test/geo_quaternion.cpp b/test/geo_quaternion.cpp new file mode 100644 index 000000000..6e6922864 --- /dev/null +++ b/test/geo_quaternion.cpp @@ -0,0 +1,255 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2009 Mathieu Gautier <mathieu.gautier@cea.fr> +// +// 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" +#include <Eigen/Geometry> +#include <Eigen/LU> +#include <Eigen/SVD> + +template<typename T> T bounded_acos(T v) +{ + using std::acos; + using std::min; + using std::max; + return acos((max)(T(-1),(min)(v,T(1)))); +} + +template<typename QuatType> void check_slerp(const QuatType& q0, const QuatType& q1) +{ + typedef typename QuatType::Scalar Scalar; + typedef Matrix<Scalar,3,1> VectorType; + typedef AngleAxis<Scalar> AA; + + Scalar largeEps = test_precision<Scalar>(); + + Scalar theta_tot = AA(q1*q0.inverse()).angle(); + if(theta_tot>M_PI) + theta_tot = 2.*M_PI-theta_tot; + for(Scalar t=0; t<=1.001; t+=0.1) + { + QuatType q = q0.slerp(t,q1); + Scalar theta = AA(q*q0.inverse()).angle(); + VERIFY(internal::abs(q.norm() - 1) < largeEps); + if(theta_tot==0) VERIFY(theta_tot==0); + else VERIFY(internal::abs(theta/theta_tot - t) < largeEps); + } +} + +template<typename Scalar, int Options> void quaternion(void) +{ + /* this test covers the following files: + Quaternion.h + */ + + typedef Matrix<Scalar,3,3> Matrix3; + typedef Matrix<Scalar,3,1> Vector3; + typedef Matrix<Scalar,4,1> Vector4; + typedef Quaternion<Scalar,Options> Quaternionx; + typedef AngleAxis<Scalar> AngleAxisx; + + Scalar largeEps = test_precision<Scalar>(); + if (internal::is_same<Scalar,float>::value) + largeEps = 1e-3f; + + Scalar eps = internal::random<Scalar>() * Scalar(1e-2); + + Vector3 v0 = Vector3::Random(), + v1 = Vector3::Random(), + v2 = Vector3::Random(), + v3 = Vector3::Random(); + + Scalar a = internal::random<Scalar>(-Scalar(M_PI), Scalar(M_PI)), + b = internal::random<Scalar>(-Scalar(M_PI), Scalar(M_PI)); + + // Quaternion: Identity(), setIdentity(); + Quaternionx q1, q2; + q2.setIdentity(); + VERIFY_IS_APPROX(Quaternionx(Quaternionx::Identity()).coeffs(), q2.coeffs()); + q1.coeffs().setRandom(); + VERIFY_IS_APPROX(q1.coeffs(), (q1*q2).coeffs()); + + // concatenation + q1 *= q2; + + q1 = AngleAxisx(a, v0.normalized()); + q2 = AngleAxisx(a, v1.normalized()); + + // angular distance + Scalar refangle = internal::abs(AngleAxisx(q1.inverse()*q2).angle()); + if (refangle>Scalar(M_PI)) + refangle = Scalar(2)*Scalar(M_PI) - refangle; + + if((q1.coeffs()-q2.coeffs()).norm() > 10*largeEps) + { + VERIFY_IS_MUCH_SMALLER_THAN(internal::abs(q1.angularDistance(q2) - refangle), Scalar(1)); + } + + // rotation matrix conversion + VERIFY_IS_APPROX(q1 * v2, q1.toRotationMatrix() * v2); + VERIFY_IS_APPROX(q1 * q2 * v2, + q1.toRotationMatrix() * q2.toRotationMatrix() * v2); + + VERIFY( (q2*q1).isApprox(q1*q2, largeEps) + || !(q2 * q1 * v2).isApprox(q1.toRotationMatrix() * q2.toRotationMatrix() * v2)); + + q2 = q1.toRotationMatrix(); + VERIFY_IS_APPROX(q1*v1,q2*v1); + + + // angle-axis conversion + AngleAxisx aa = AngleAxisx(q1); + VERIFY_IS_APPROX(q1 * v1, Quaternionx(aa) * v1); + + // Do not execute the test if the rotation angle is almost zero, or + // the rotation axis and v1 are almost parallel. + if (internal::abs(aa.angle()) > 5*test_precision<Scalar>() + && (aa.axis() - v1.normalized()).norm() < 1.99 + && (aa.axis() + v1.normalized()).norm() < 1.99) + { + VERIFY_IS_NOT_APPROX(q1 * v1, Quaternionx(AngleAxisx(aa.angle()*2,aa.axis())) * v1); + } + + // from two vector creation + VERIFY_IS_APPROX( v2.normalized(),(q2.setFromTwoVectors(v1, v2)*v1).normalized()); + VERIFY_IS_APPROX( v1.normalized(),(q2.setFromTwoVectors(v1, v1)*v1).normalized()); + VERIFY_IS_APPROX(-v1.normalized(),(q2.setFromTwoVectors(v1,-v1)*v1).normalized()); + if (internal::is_same<Scalar,double>::value) + { + v3 = (v1.array()+eps).matrix(); + VERIFY_IS_APPROX( v3.normalized(),(q2.setFromTwoVectors(v1, v3)*v1).normalized()); + VERIFY_IS_APPROX(-v3.normalized(),(q2.setFromTwoVectors(v1,-v3)*v1).normalized()); + } + + // from two vector creation static function + VERIFY_IS_APPROX( v2.normalized(),(Quaternionx::FromTwoVectors(v1, v2)*v1).normalized()); + VERIFY_IS_APPROX( v1.normalized(),(Quaternionx::FromTwoVectors(v1, v1)*v1).normalized()); + VERIFY_IS_APPROX(-v1.normalized(),(Quaternionx::FromTwoVectors(v1,-v1)*v1).normalized()); + if (internal::is_same<Scalar,double>::value) + { + v3 = (v1.array()+eps).matrix(); + VERIFY_IS_APPROX( v3.normalized(),(Quaternionx::FromTwoVectors(v1, v3)*v1).normalized()); + VERIFY_IS_APPROX(-v3.normalized(),(Quaternionx::FromTwoVectors(v1,-v3)*v1).normalized()); + } + + // inverse and conjugate + VERIFY_IS_APPROX(q1 * (q1.inverse() * v1), v1); + VERIFY_IS_APPROX(q1 * (q1.conjugate() * v1), v1); + + // test casting + Quaternion<float> q1f = q1.template cast<float>(); + VERIFY_IS_APPROX(q1f.template cast<Scalar>(),q1); + Quaternion<double> q1d = q1.template cast<double>(); + VERIFY_IS_APPROX(q1d.template cast<Scalar>(),q1); + + // test bug 369 - improper alignment. + Quaternionx *q = new Quaternionx; + delete q; + + q1 = AngleAxisx(a, v0.normalized()); + q2 = AngleAxisx(b, v1.normalized()); + check_slerp(q1,q2); + + q1 = AngleAxisx(b, v1.normalized()); + q2 = AngleAxisx(b+M_PI, v1.normalized()); + check_slerp(q1,q2); + + q1 = AngleAxisx(b, v1.normalized()); + q2 = AngleAxisx(-b, -v1.normalized()); + check_slerp(q1,q2); + + q1.coeffs() = Vector4::Random().normalized(); + q2.coeffs() = -q1.coeffs(); + check_slerp(q1,q2); +} + +template<typename Scalar> void mapQuaternion(void){ + typedef Map<Quaternion<Scalar>, Aligned> MQuaternionA; + typedef Map<Quaternion<Scalar> > MQuaternionUA; + typedef Map<const Quaternion<Scalar> > MCQuaternionUA; + typedef Quaternion<Scalar> Quaternionx; + + EIGEN_ALIGN16 Scalar array1[4]; + EIGEN_ALIGN16 Scalar array2[4]; + EIGEN_ALIGN16 Scalar array3[4+1]; + Scalar* array3unaligned = array3+1; + +// std::cerr << array1 << " " << array2 << " " << array3 << "\n"; + MQuaternionA(array1).coeffs().setRandom(); + (MQuaternionA(array2)) = MQuaternionA(array1); + (MQuaternionUA(array3unaligned)) = MQuaternionA(array1); + + Quaternionx q1 = MQuaternionA(array1); + Quaternionx q2 = MQuaternionA(array2); + Quaternionx q3 = MQuaternionUA(array3unaligned); + Quaternionx q4 = MCQuaternionUA(array3unaligned); + + VERIFY_IS_APPROX(q1.coeffs(), q2.coeffs()); + VERIFY_IS_APPROX(q1.coeffs(), q3.coeffs()); + VERIFY_IS_APPROX(q4.coeffs(), q3.coeffs()); + #ifdef EIGEN_VECTORIZE + if(internal::packet_traits<Scalar>::Vectorizable) + VERIFY_RAISES_ASSERT((MQuaternionA(array3unaligned))); + #endif +} + +template<typename Scalar> void quaternionAlignment(void){ + typedef Quaternion<Scalar,AutoAlign> QuaternionA; + typedef Quaternion<Scalar,DontAlign> QuaternionUA; + + EIGEN_ALIGN16 Scalar array1[4]; + EIGEN_ALIGN16 Scalar array2[4]; + EIGEN_ALIGN16 Scalar array3[4+1]; + Scalar* arrayunaligned = array3+1; + + QuaternionA *q1 = ::new(reinterpret_cast<void*>(array1)) QuaternionA; + QuaternionUA *q2 = ::new(reinterpret_cast<void*>(array2)) QuaternionUA; + QuaternionUA *q3 = ::new(reinterpret_cast<void*>(arrayunaligned)) QuaternionUA; + + q1->coeffs().setRandom(); + *q2 = *q1; + *q3 = *q1; + + VERIFY_IS_APPROX(q1->coeffs(), q2->coeffs()); + VERIFY_IS_APPROX(q1->coeffs(), q3->coeffs()); + #if defined(EIGEN_VECTORIZE) && EIGEN_ALIGN_STATICALLY + if(internal::packet_traits<Scalar>::Vectorizable) + VERIFY_RAISES_ASSERT((::new(reinterpret_cast<void*>(arrayunaligned)) QuaternionA)); + #endif +} + +template<typename PlainObjectType> void check_const_correctness(const PlainObjectType&) +{ + // there's a lot that we can't test here while still having this test compile! + // the only possible approach would be to run a script trying to compile stuff and checking that it fails. + // CMake can help with that. + + // verify that map-to-const don't have LvalueBit + typedef typename internal::add_const<PlainObjectType>::type ConstPlainObjectType; + VERIFY( !(internal::traits<Map<ConstPlainObjectType> >::Flags & LvalueBit) ); + VERIFY( !(internal::traits<Map<ConstPlainObjectType, Aligned> >::Flags & LvalueBit) ); + VERIFY( !(Map<ConstPlainObjectType>::Flags & LvalueBit) ); + VERIFY( !(Map<ConstPlainObjectType, Aligned>::Flags & LvalueBit) ); +} + +void test_geo_quaternion() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1(( quaternion<float,AutoAlign>() )); + CALL_SUBTEST_1( check_const_correctness(Quaternionf()) ); + CALL_SUBTEST_2(( quaternion<double,AutoAlign>() )); + CALL_SUBTEST_2( check_const_correctness(Quaterniond()) ); + CALL_SUBTEST_3(( quaternion<float,DontAlign>() )); + CALL_SUBTEST_4(( quaternion<double,DontAlign>() )); + CALL_SUBTEST_5(( quaternionAlignment<float>() )); + CALL_SUBTEST_6(( quaternionAlignment<double>() )); + CALL_SUBTEST_1( mapQuaternion<float>() ); + CALL_SUBTEST_2( mapQuaternion<double>() ); + } +} diff --git a/test/geo_transformations.cpp b/test/geo_transformations.cpp new file mode 100644 index 000000000..f4d65aabc --- /dev/null +++ b/test/geo_transformations.cpp @@ -0,0 +1,487 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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" +#include <Eigen/Geometry> +#include <Eigen/LU> +#include <Eigen/SVD> + +template<typename Scalar, int Mode, int Options> void non_projective_only() +{ + /* this test covers the following files: + Cross.h Quaternion.h, Transform.cpp + */ + typedef Matrix<Scalar,2,2> Matrix2; + typedef Matrix<Scalar,3,3> Matrix3; + typedef Matrix<Scalar,4,4> Matrix4; + typedef Matrix<Scalar,2,1> Vector2; + typedef Matrix<Scalar,3,1> Vector3; + typedef Matrix<Scalar,4,1> Vector4; + typedef Quaternion<Scalar> Quaternionx; + typedef AngleAxis<Scalar> AngleAxisx; + typedef Transform<Scalar,2,Mode,Options> Transform2; + typedef Transform<Scalar,3,Mode,Options> Transform3; + typedef Transform<Scalar,2,Isometry,Options> Isometry2; + typedef Transform<Scalar,3,Isometry,Options> Isometry3; + typedef typename Transform3::MatrixType MatrixType; + typedef DiagonalMatrix<Scalar,2> AlignedScaling2; + typedef DiagonalMatrix<Scalar,3> AlignedScaling3; + typedef Translation<Scalar,2> Translation2; + typedef Translation<Scalar,3> Translation3; + + Vector3 v0 = Vector3::Random(), + v1 = Vector3::Random(); + + Transform3 t0, t1, t2; + + Scalar a = internal::random<Scalar>(-Scalar(M_PI), Scalar(M_PI)); + + Quaternionx q1, q2; + + q1 = AngleAxisx(a, v0.normalized()); + + t0 = Transform3::Identity(); + VERIFY_IS_APPROX(t0.matrix(), Transform3::MatrixType::Identity()); + + t0.linear() = q1.toRotationMatrix(); + + v0 << 50, 2, 1; + t0.scale(v0); + + VERIFY_IS_APPROX( (t0 * Vector3(1,0,0)).template head<3>().norm(), v0.x()); + + t0.setIdentity(); + t1.setIdentity(); + v1 << 1, 2, 3; + t0.linear() = q1.toRotationMatrix(); + t0.pretranslate(v0); + t0.scale(v1); + t1.linear() = q1.conjugate().toRotationMatrix(); + t1.prescale(v1.cwiseInverse()); + t1.translate(-v0); + + VERIFY((t0 * t1).matrix().isIdentity(test_precision<Scalar>())); + + t1.fromPositionOrientationScale(v0, q1, v1); + VERIFY_IS_APPROX(t1.matrix(), t0.matrix()); + VERIFY_IS_APPROX(t1*v1, t0*v1); + + // translation * vector + t0.setIdentity(); + t0.translate(v0); + VERIFY_IS_APPROX((t0 * v1).template head<3>(), Translation3(v0) * v1); + + // AlignedScaling * vector + t0.setIdentity(); + t0.scale(v0); + VERIFY_IS_APPROX((t0 * v1).template head<3>(), AlignedScaling3(v0) * v1); +} + +template<typename Scalar, int Mode, int Options> void transformations() +{ + /* this test covers the following files: + Cross.h Quaternion.h, Transform.cpp + */ + typedef Matrix<Scalar,2,2> Matrix2; + typedef Matrix<Scalar,3,3> Matrix3; + typedef Matrix<Scalar,4,4> Matrix4; + typedef Matrix<Scalar,2,1> Vector2; + typedef Matrix<Scalar,3,1> Vector3; + typedef Matrix<Scalar,4,1> Vector4; + typedef Quaternion<Scalar> Quaternionx; + typedef AngleAxis<Scalar> AngleAxisx; + typedef Transform<Scalar,2,Mode,Options> Transform2; + typedef Transform<Scalar,3,Mode,Options> Transform3; + typedef Transform<Scalar,2,Isometry,Options> Isometry2; + typedef Transform<Scalar,3,Isometry,Options> Isometry3; + typedef typename Transform3::MatrixType MatrixType; + typedef DiagonalMatrix<Scalar,2> AlignedScaling2; + typedef DiagonalMatrix<Scalar,3> AlignedScaling3; + typedef Translation<Scalar,2> Translation2; + typedef Translation<Scalar,3> Translation3; + + Vector3 v0 = Vector3::Random(), + v1 = Vector3::Random(); + Matrix3 matrot1, m; + + Scalar a = internal::random<Scalar>(-Scalar(M_PI), Scalar(M_PI)); + Scalar s0 = internal::random<Scalar>(); + + VERIFY_IS_APPROX(v0, AngleAxisx(a, v0.normalized()) * v0); + VERIFY_IS_APPROX(-v0, AngleAxisx(Scalar(M_PI), v0.unitOrthogonal()) * v0); + VERIFY_IS_APPROX(internal::cos(a)*v0.squaredNorm(), v0.dot(AngleAxisx(a, v0.unitOrthogonal()) * v0)); + m = AngleAxisx(a, v0.normalized()).toRotationMatrix().adjoint(); + VERIFY_IS_APPROX(Matrix3::Identity(), m * AngleAxisx(a, v0.normalized())); + VERIFY_IS_APPROX(Matrix3::Identity(), AngleAxisx(a, v0.normalized()) * m); + + Quaternionx q1, q2; + q1 = AngleAxisx(a, v0.normalized()); + q2 = AngleAxisx(a, v1.normalized()); + + // rotation matrix conversion + matrot1 = AngleAxisx(Scalar(0.1), Vector3::UnitX()) + * AngleAxisx(Scalar(0.2), Vector3::UnitY()) + * AngleAxisx(Scalar(0.3), Vector3::UnitZ()); + VERIFY_IS_APPROX(matrot1 * v1, + AngleAxisx(Scalar(0.1), Vector3(1,0,0)).toRotationMatrix() + * (AngleAxisx(Scalar(0.2), Vector3(0,1,0)).toRotationMatrix() + * (AngleAxisx(Scalar(0.3), Vector3(0,0,1)).toRotationMatrix() * v1))); + + // angle-axis conversion + AngleAxisx aa = AngleAxisx(q1); + VERIFY_IS_APPROX(q1 * v1, Quaternionx(aa) * v1); + VERIFY_IS_NOT_APPROX(q1 * v1, Quaternionx(AngleAxisx(aa.angle()*2,aa.axis())) * v1); + + aa.fromRotationMatrix(aa.toRotationMatrix()); + VERIFY_IS_APPROX(q1 * v1, Quaternionx(aa) * v1); + VERIFY_IS_NOT_APPROX(q1 * v1, Quaternionx(AngleAxisx(aa.angle()*2,aa.axis())) * v1); + + // AngleAxis + VERIFY_IS_APPROX(AngleAxisx(a,v1.normalized()).toRotationMatrix(), + Quaternionx(AngleAxisx(a,v1.normalized())).toRotationMatrix()); + + AngleAxisx aa1; + m = q1.toRotationMatrix(); + aa1 = m; + VERIFY_IS_APPROX(AngleAxisx(m).toRotationMatrix(), + Quaternionx(m).toRotationMatrix()); + + // Transform + // TODO complete the tests ! + a = 0; + while (internal::abs(a)<Scalar(0.1)) + a = internal::random<Scalar>(-Scalar(0.4)*Scalar(M_PI), Scalar(0.4)*Scalar(M_PI)); + q1 = AngleAxisx(a, v0.normalized()); + Transform3 t0, t1, t2; + + // first test setIdentity() and Identity() + t0.setIdentity(); + VERIFY_IS_APPROX(t0.matrix(), Transform3::MatrixType::Identity()); + t0.matrix().setZero(); + t0 = Transform3::Identity(); + VERIFY_IS_APPROX(t0.matrix(), Transform3::MatrixType::Identity()); + + t0.setIdentity(); + t1.setIdentity(); + v1 << 1, 2, 3; + t0.linear() = q1.toRotationMatrix(); + t0.pretranslate(v0); + t0.scale(v1); + t1.linear() = q1.conjugate().toRotationMatrix(); + t1.prescale(v1.cwiseInverse()); + t1.translate(-v0); + + VERIFY((t0 * t1).matrix().isIdentity(test_precision<Scalar>())); + + t1.fromPositionOrientationScale(v0, q1, v1); + VERIFY_IS_APPROX(t1.matrix(), t0.matrix()); + + t0.setIdentity(); t0.scale(v0).rotate(q1.toRotationMatrix()); + t1.setIdentity(); t1.scale(v0).rotate(q1); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + t0.setIdentity(); t0.scale(v0).rotate(AngleAxisx(q1)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + VERIFY_IS_APPROX(t0.scale(a).matrix(), t1.scale(Vector3::Constant(a)).matrix()); + VERIFY_IS_APPROX(t0.prescale(a).matrix(), t1.prescale(Vector3::Constant(a)).matrix()); + + // More transform constructors, operator=, operator*= + + Matrix3 mat3 = Matrix3::Random(); + Matrix4 mat4; + mat4 << mat3 , Vector3::Zero() , Vector4::Zero().transpose(); + Transform3 tmat3(mat3), tmat4(mat4); + if(Mode!=int(AffineCompact)) + tmat4.matrix()(3,3) = Scalar(1); + VERIFY_IS_APPROX(tmat3.matrix(), tmat4.matrix()); + + Scalar a3 = internal::random<Scalar>(-Scalar(M_PI), Scalar(M_PI)); + Vector3 v3 = Vector3::Random().normalized(); + AngleAxisx aa3(a3, v3); + Transform3 t3(aa3); + Transform3 t4; + t4 = aa3; + VERIFY_IS_APPROX(t3.matrix(), t4.matrix()); + t4.rotate(AngleAxisx(-a3,v3)); + VERIFY_IS_APPROX(t4.matrix(), MatrixType::Identity()); + t4 *= aa3; + VERIFY_IS_APPROX(t3.matrix(), t4.matrix()); + + v3 = Vector3::Random(); + Translation3 tv3(v3); + Transform3 t5(tv3); + t4 = tv3; + VERIFY_IS_APPROX(t5.matrix(), t4.matrix()); + t4.translate(-v3); + VERIFY_IS_APPROX(t4.matrix(), MatrixType::Identity()); + t4 *= tv3; + VERIFY_IS_APPROX(t5.matrix(), t4.matrix()); + + AlignedScaling3 sv3(v3); + Transform3 t6(sv3); + t4 = sv3; + VERIFY_IS_APPROX(t6.matrix(), t4.matrix()); + t4.scale(v3.cwiseInverse()); + VERIFY_IS_APPROX(t4.matrix(), MatrixType::Identity()); + t4 *= sv3; + VERIFY_IS_APPROX(t6.matrix(), t4.matrix()); + + // matrix * transform + VERIFY_IS_APPROX((t3.matrix()*t4).matrix(), (t3*t4).matrix()); + + // chained Transform product + VERIFY_IS_APPROX(((t3*t4)*t5).matrix(), (t3*(t4*t5)).matrix()); + + // check that Transform product doesn't have aliasing problems + t5 = t4; + t5 = t5*t5; + VERIFY_IS_APPROX(t5, t4*t4); + + // 2D transformation + Transform2 t20, t21; + Vector2 v20 = Vector2::Random(); + Vector2 v21 = Vector2::Random(); + for (int k=0; k<2; ++k) + if (internal::abs(v21[k])<Scalar(1e-3)) v21[k] = Scalar(1e-3); + t21.setIdentity(); + t21.linear() = Rotation2D<Scalar>(a).toRotationMatrix(); + VERIFY_IS_APPROX(t20.fromPositionOrientationScale(v20,a,v21).matrix(), + t21.pretranslate(v20).scale(v21).matrix()); + + t21.setIdentity(); + t21.linear() = Rotation2D<Scalar>(-a).toRotationMatrix(); + VERIFY( (t20.fromPositionOrientationScale(v20,a,v21) + * (t21.prescale(v21.cwiseInverse()).translate(-v20))).matrix().isIdentity(test_precision<Scalar>()) ); + + // Transform - new API + // 3D + t0.setIdentity(); + t0.rotate(q1).scale(v0).translate(v0); + // mat * aligned scaling and mat * translation + t1 = (Matrix3(q1) * AlignedScaling3(v0)) * Translation3(v0); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + t1 = (Matrix3(q1) * Eigen::Scaling(v0)) * Translation3(v0); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + t1 = (q1 * Eigen::Scaling(v0)) * Translation3(v0); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + // mat * transformation and aligned scaling * translation + t1 = Matrix3(q1) * (AlignedScaling3(v0) * Translation3(v0)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + + t0.setIdentity(); + t0.scale(s0).translate(v0); + t1 = Eigen::Scaling(s0) * Translation3(v0); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + t0.prescale(s0); + t1 = Eigen::Scaling(s0) * t1; + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + t0 = t3; + t0.scale(s0); + t1 = t3 * Eigen::Scaling(s0,s0,s0); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + t0.prescale(s0); + t1 = Eigen::Scaling(s0,s0,s0) * t1; + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + + t0.setIdentity(); + t0.prerotate(q1).prescale(v0).pretranslate(v0); + // translation * aligned scaling and transformation * mat + t1 = (Translation3(v0) * AlignedScaling3(v0)) * Transform3(q1); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + // scaling * mat and translation * mat + t1 = Translation3(v0) * (AlignedScaling3(v0) * Transform3(q1)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + t0.setIdentity(); + t0.scale(v0).translate(v0).rotate(q1); + // translation * mat and aligned scaling * transformation + t1 = AlignedScaling3(v0) * (Translation3(v0) * Transform3(q1)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + // transformation * aligned scaling + t0.scale(v0); + t1 *= AlignedScaling3(v0); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + // transformation * translation + t0.translate(v0); + t1 = t1 * Translation3(v0); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + // translation * transformation + t0.pretranslate(v0); + t1 = Translation3(v0) * t1; + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // transform * quaternion + t0.rotate(q1); + t1 = t1 * q1; + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // translation * quaternion + t0.translate(v1).rotate(q1); + t1 = t1 * (Translation3(v1) * q1); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // aligned scaling * quaternion + t0.scale(v1).rotate(q1); + t1 = t1 * (AlignedScaling3(v1) * q1); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // quaternion * transform + t0.prerotate(q1); + t1 = q1 * t1; + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // quaternion * translation + t0.rotate(q1).translate(v1); + t1 = t1 * (q1 * Translation3(v1)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // quaternion * aligned scaling + t0.rotate(q1).scale(v1); + t1 = t1 * (q1 * AlignedScaling3(v1)); + VERIFY_IS_APPROX(t0.matrix(), t1.matrix()); + + // test transform inversion + t0.setIdentity(); + t0.translate(v0); + t0.linear().setRandom(); + Matrix4 t044 = Matrix4::Zero(); + t044(3,3) = 1; + t044.block(0,0,t0.matrix().rows(),4) = t0.matrix(); + VERIFY_IS_APPROX(t0.inverse(Affine).matrix(), t044.inverse().block(0,0,t0.matrix().rows(),4)); + t0.setIdentity(); + t0.translate(v0).rotate(q1); + t044 = Matrix4::Zero(); + t044(3,3) = 1; + t044.block(0,0,t0.matrix().rows(),4) = t0.matrix(); + VERIFY_IS_APPROX(t0.inverse(Isometry).matrix(), t044.inverse().block(0,0,t0.matrix().rows(),4)); + + Matrix3 mat_rotation, mat_scaling; + t0.setIdentity(); + t0.translate(v0).rotate(q1).scale(v1); + t0.computeRotationScaling(&mat_rotation, &mat_scaling); + VERIFY_IS_APPROX(t0.linear(), mat_rotation * mat_scaling); + VERIFY_IS_APPROX(mat_rotation*mat_rotation.adjoint(), Matrix3::Identity()); + VERIFY_IS_APPROX(mat_rotation.determinant(), Scalar(1)); + t0.computeScalingRotation(&mat_scaling, &mat_rotation); + VERIFY_IS_APPROX(t0.linear(), mat_scaling * mat_rotation); + VERIFY_IS_APPROX(mat_rotation*mat_rotation.adjoint(), Matrix3::Identity()); + VERIFY_IS_APPROX(mat_rotation.determinant(), Scalar(1)); + + // test casting + Transform<float,3,Mode> t1f = t1.template cast<float>(); + VERIFY_IS_APPROX(t1f.template cast<Scalar>(),t1); + Transform<double,3,Mode> t1d = t1.template cast<double>(); + VERIFY_IS_APPROX(t1d.template cast<Scalar>(),t1); + + Translation3 tr1(v0); + Translation<float,3> tr1f = tr1.template cast<float>(); + VERIFY_IS_APPROX(tr1f.template cast<Scalar>(),tr1); + Translation<double,3> tr1d = tr1.template cast<double>(); + VERIFY_IS_APPROX(tr1d.template cast<Scalar>(),tr1); + + AngleAxis<float> aa1f = aa1.template cast<float>(); + VERIFY_IS_APPROX(aa1f.template cast<Scalar>(),aa1); + AngleAxis<double> aa1d = aa1.template cast<double>(); + VERIFY_IS_APPROX(aa1d.template cast<Scalar>(),aa1); + + Rotation2D<Scalar> r2d1(internal::random<Scalar>()); + Rotation2D<float> r2d1f = r2d1.template cast<float>(); + VERIFY_IS_APPROX(r2d1f.template cast<Scalar>(),r2d1); + Rotation2D<double> r2d1d = r2d1.template cast<double>(); + VERIFY_IS_APPROX(r2d1d.template cast<Scalar>(),r2d1); + + t20 = Translation2(v20) * (Rotation2D<Scalar>(s0) * Scaling(s0)); + t21 = Translation2(v20) * Rotation2D<Scalar>(s0) * Scaling(s0); + VERIFY_IS_APPROX(t20,t21); +} + +template<typename Scalar> void transform_alignment() +{ + typedef Transform<Scalar,3,Projective,AutoAlign> Projective3a; + typedef Transform<Scalar,3,Projective,DontAlign> Projective3u; + + EIGEN_ALIGN16 Scalar array1[16]; + EIGEN_ALIGN16 Scalar array2[16]; + EIGEN_ALIGN16 Scalar array3[16+1]; + Scalar* array3u = array3+1; + + Projective3a *p1 = ::new(reinterpret_cast<void*>(array1)) Projective3a; + Projective3u *p2 = ::new(reinterpret_cast<void*>(array2)) Projective3u; + Projective3u *p3 = ::new(reinterpret_cast<void*>(array3u)) Projective3u; + + p1->matrix().setRandom(); + *p2 = *p1; + *p3 = *p1; + + VERIFY_IS_APPROX(p1->matrix(), p2->matrix()); + VERIFY_IS_APPROX(p1->matrix(), p3->matrix()); + + VERIFY_IS_APPROX( (*p1) * (*p1), (*p2)*(*p3)); + + #if defined(EIGEN_VECTORIZE) && EIGEN_ALIGN_STATICALLY + if(internal::packet_traits<Scalar>::Vectorizable) + VERIFY_RAISES_ASSERT((::new(reinterpret_cast<void*>(array3u)) Projective3a)); + #endif +} + +template<typename Scalar, int Dim, int Options> void transform_products() +{ + typedef Matrix<Scalar,Dim+1,Dim+1> Mat; + typedef Transform<Scalar,Dim,Projective,Options> Proj; + typedef Transform<Scalar,Dim,Affine,Options> Aff; + typedef Transform<Scalar,Dim,AffineCompact,Options> AffC; + + Proj p; p.matrix().setRandom(); + Aff a; a.linear().setRandom(); a.translation().setRandom(); + AffC ac = a; + + Mat p_m(p.matrix()), a_m(a.matrix()); + + VERIFY_IS_APPROX((p*p).matrix(), p_m*p_m); + VERIFY_IS_APPROX((a*a).matrix(), a_m*a_m); + VERIFY_IS_APPROX((p*a).matrix(), p_m*a_m); + VERIFY_IS_APPROX((a*p).matrix(), a_m*p_m); + VERIFY_IS_APPROX((ac*a).matrix(), a_m*a_m); + VERIFY_IS_APPROX((a*ac).matrix(), a_m*a_m); + VERIFY_IS_APPROX((p*ac).matrix(), p_m*a_m); + VERIFY_IS_APPROX((ac*p).matrix(), a_m*p_m); +} + +void test_geo_transformations() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1(( transformations<double,Affine,AutoAlign>() )); + CALL_SUBTEST_1(( non_projective_only<double,Affine,AutoAlign>() )); + + CALL_SUBTEST_2(( transformations<float,AffineCompact,AutoAlign>() )); + CALL_SUBTEST_2(( non_projective_only<float,AffineCompact,AutoAlign>() )); + CALL_SUBTEST_2(( transform_alignment<float>() )); + + CALL_SUBTEST_3(( transformations<double,Projective,AutoAlign>() )); + CALL_SUBTEST_3(( transformations<double,Projective,DontAlign>() )); + CALL_SUBTEST_3(( transform_alignment<double>() )); + + CALL_SUBTEST_4(( transformations<float,Affine,RowMajor|AutoAlign>() )); + CALL_SUBTEST_4(( non_projective_only<float,Affine,RowMajor>() )); + + CALL_SUBTEST_5(( transformations<double,AffineCompact,RowMajor|AutoAlign>() )); + CALL_SUBTEST_5(( non_projective_only<double,AffineCompact,RowMajor>() )); + + CALL_SUBTEST_6(( transformations<double,Projective,RowMajor|AutoAlign>() )); + CALL_SUBTEST_6(( transformations<double,Projective,RowMajor|DontAlign>() )); + + + CALL_SUBTEST_7(( transform_products<double,3,RowMajor|AutoAlign>() )); + CALL_SUBTEST_7(( transform_products<float,2,AutoAlign>() )); + } +} diff --git a/test/hessenberg.cpp b/test/hessenberg.cpp new file mode 100644 index 000000000..96bc19e2e --- /dev/null +++ b/test/hessenberg.cpp @@ -0,0 +1,62 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2010 Jitse Niesen <jitse@maths.leeds.ac.uk> +// +// 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" +#include <Eigen/Eigenvalues> + +template<typename Scalar,int Size> void hessenberg(int size = Size) +{ + typedef Matrix<Scalar,Size,Size> MatrixType; + + // Test basic functionality: A = U H U* and H is Hessenberg + for(int counter = 0; counter < g_repeat; ++counter) { + MatrixType m = MatrixType::Random(size,size); + HessenbergDecomposition<MatrixType> hess(m); + MatrixType Q = hess.matrixQ(); + MatrixType H = hess.matrixH(); + VERIFY_IS_APPROX(m, Q * H * Q.adjoint()); + for(int row = 2; row < size; ++row) { + for(int col = 0; col < row-1; ++col) { + VERIFY(H(row,col) == (typename MatrixType::Scalar)0); + } + } + } + + // Test whether compute() and constructor returns same result + MatrixType A = MatrixType::Random(size, size); + HessenbergDecomposition<MatrixType> cs1; + cs1.compute(A); + HessenbergDecomposition<MatrixType> cs2(A); + VERIFY_IS_EQUAL(cs1.matrixH().eval(), cs2.matrixH().eval()); + MatrixType cs1Q = cs1.matrixQ(); + MatrixType cs2Q = cs2.matrixQ(); + VERIFY_IS_EQUAL(cs1Q, cs2Q); + + // Test assertions for when used uninitialized + HessenbergDecomposition<MatrixType> hessUninitialized; + VERIFY_RAISES_ASSERT( hessUninitialized.matrixH() ); + VERIFY_RAISES_ASSERT( hessUninitialized.matrixQ() ); + VERIFY_RAISES_ASSERT( hessUninitialized.householderCoefficients() ); + VERIFY_RAISES_ASSERT( hessUninitialized.packedMatrix() ); + + // TODO: Add tests for packedMatrix() and householderCoefficients() +} + +void test_hessenberg() +{ + CALL_SUBTEST_1(( hessenberg<std::complex<double>,1>() )); + CALL_SUBTEST_2(( hessenberg<std::complex<double>,2>() )); + CALL_SUBTEST_3(( hessenberg<std::complex<float>,4>() )); + CALL_SUBTEST_4(( hessenberg<float,Dynamic>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE)) )); + CALL_SUBTEST_5(( hessenberg<std::complex<double>,Dynamic>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE)) )); + + // Test problem size constructors + CALL_SUBTEST_6(HessenbergDecomposition<MatrixXf>(10)); +} diff --git a/test/householder.cpp b/test/householder.cpp new file mode 100644 index 000000000..203dce46c --- /dev/null +++ b/test/householder.cpp @@ -0,0 +1,123 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009-2010 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/QR> + +template<typename MatrixType> void householder(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + static bool even = true; + even = !even; + /* this test covers the following files: + Householder.h + */ + Index rows = m.rows(); + Index cols = m.cols(); + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<Scalar, internal::decrement_size<MatrixType::RowsAtCompileTime>::ret, 1> EssentialVectorType; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> SquareMatrixType; + typedef Matrix<Scalar, Dynamic, MatrixType::ColsAtCompileTime> HBlockMatrixType; + typedef Matrix<Scalar, Dynamic, 1> HCoeffsVectorType; + + typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, MatrixType::ColsAtCompileTime> RightSquareMatrixType; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, Dynamic> VBlockMatrixType; + typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, MatrixType::RowsAtCompileTime> TMatrixType; + + Matrix<Scalar, EIGEN_SIZE_MAX(MatrixType::RowsAtCompileTime,MatrixType::ColsAtCompileTime), 1> _tmp((std::max)(rows,cols)); + Scalar* tmp = &_tmp.coeffRef(0,0); + + Scalar beta; + RealScalar alpha; + EssentialVectorType essential; + + VectorType v1 = VectorType::Random(rows), v2; + v2 = v1; + v1.makeHouseholder(essential, beta, alpha); + v1.applyHouseholderOnTheLeft(essential,beta,tmp); + VERIFY_IS_APPROX(v1.norm(), v2.norm()); + if(rows>=2) VERIFY_IS_MUCH_SMALLER_THAN(v1.tail(rows-1).norm(), v1.norm()); + v1 = VectorType::Random(rows); + v2 = v1; + v1.applyHouseholderOnTheLeft(essential,beta,tmp); + VERIFY_IS_APPROX(v1.norm(), v2.norm()); + + MatrixType m1(rows, cols), + m2(rows, cols); + + v1 = VectorType::Random(rows); + if(even) v1.tail(rows-1).setZero(); + m1.colwise() = v1; + m2 = m1; + m1.col(0).makeHouseholder(essential, beta, alpha); + m1.applyHouseholderOnTheLeft(essential,beta,tmp); + VERIFY_IS_APPROX(m1.norm(), m2.norm()); + if(rows>=2) VERIFY_IS_MUCH_SMALLER_THAN(m1.block(1,0,rows-1,cols).norm(), m1.norm()); + VERIFY_IS_MUCH_SMALLER_THAN(internal::imag(m1(0,0)), internal::real(m1(0,0))); + VERIFY_IS_APPROX(internal::real(m1(0,0)), alpha); + + v1 = VectorType::Random(rows); + if(even) v1.tail(rows-1).setZero(); + SquareMatrixType m3(rows,rows), m4(rows,rows); + m3.rowwise() = v1.transpose(); + m4 = m3; + m3.row(0).makeHouseholder(essential, beta, alpha); + m3.applyHouseholderOnTheRight(essential,beta,tmp); + VERIFY_IS_APPROX(m3.norm(), m4.norm()); + if(rows>=2) VERIFY_IS_MUCH_SMALLER_THAN(m3.block(0,1,rows,rows-1).norm(), m3.norm()); + VERIFY_IS_MUCH_SMALLER_THAN(internal::imag(m3(0,0)), internal::real(m3(0,0))); + VERIFY_IS_APPROX(internal::real(m3(0,0)), alpha); + + // test householder sequence on the left with a shift + + Index shift = internal::random<Index>(0, std::max<Index>(rows-2,0)); + Index brows = rows - shift; + m1.setRandom(rows, cols); + HBlockMatrixType hbm = m1.block(shift,0,brows,cols); + HouseholderQR<HBlockMatrixType> qr(hbm); + m2 = m1; + m2.block(shift,0,brows,cols) = qr.matrixQR(); + HCoeffsVectorType hc = qr.hCoeffs().conjugate(); + HouseholderSequence<MatrixType, HCoeffsVectorType> hseq(m2, hc); + hseq.setLength(hc.size()).setShift(shift); + VERIFY(hseq.length() == hc.size()); + VERIFY(hseq.shift() == shift); + + MatrixType m5 = m2; + m5.block(shift,0,brows,cols).template triangularView<StrictlyLower>().setZero(); + VERIFY_IS_APPROX(hseq * m5, m1); // test applying hseq directly + m3 = hseq; + VERIFY_IS_APPROX(m3 * m5, m1); // test evaluating hseq to a dense matrix, then applying + + // test householder sequence on the right with a shift + + TMatrixType tm2 = m2.transpose(); + HouseholderSequence<TMatrixType, HCoeffsVectorType, OnTheRight> rhseq(tm2, hc); + rhseq.setLength(hc.size()).setShift(shift); + VERIFY_IS_APPROX(rhseq * m5, m1); // test applying rhseq directly + m3 = rhseq; + VERIFY_IS_APPROX(m3 * m5, m1); // test evaluating rhseq to a dense matrix, then applying +} + +void test_householder() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( householder(Matrix<double,2,2>()) ); + CALL_SUBTEST_2( householder(Matrix<float,2,3>()) ); + CALL_SUBTEST_3( householder(Matrix<double,3,5>()) ); + CALL_SUBTEST_4( householder(Matrix<float,4,4>()) ); + CALL_SUBTEST_5( householder(MatrixXd(internal::random<int>(1,EIGEN_TEST_MAX_SIZE),internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_6( householder(MatrixXcf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE),internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_7( householder(MatrixXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE),internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_8( householder(Matrix<double,1,1>()) ); + } +} diff --git a/test/integer_types.cpp b/test/integer_types.cpp new file mode 100644 index 000000000..950f8e9be --- /dev/null +++ b/test/integer_types.cpp @@ -0,0 +1,161 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2010 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#define EIGEN_NO_STATIC_ASSERT + +#include "main.h" + +#undef VERIFY_IS_APPROX +#define VERIFY_IS_APPROX(a, b) VERIFY((a)==(b)); +#undef VERIFY_IS_NOT_APPROX +#define VERIFY_IS_NOT_APPROX(a, b) VERIFY((a)!=(b)); + +template<typename MatrixType> void signed_integer_type_tests(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + + enum { is_signed = (Scalar(-1) > Scalar(0)) ? 0 : 1 }; + VERIFY(is_signed == 1); + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1(rows, cols), + m2 = MatrixType::Random(rows, cols), + mzero = MatrixType::Zero(rows, cols); + + do { + m1 = MatrixType::Random(rows, cols); + } while(m1 == mzero || m1 == m2); + + // check linear structure + + Scalar s1; + do { + s1 = internal::random<Scalar>(); + } while(s1 == 0); + + VERIFY_IS_EQUAL(-(-m1), m1); + VERIFY_IS_EQUAL(-m2+m1+m2, m1); + VERIFY_IS_EQUAL((-m1+m2)*s1, -s1*m1+s1*m2); +} + +template<typename MatrixType> void integer_type_tests(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + + VERIFY(NumTraits<Scalar>::IsInteger); + enum { is_signed = (Scalar(-1) > Scalar(0)) ? 0 : 1 }; + VERIFY(int(NumTraits<Scalar>::IsSigned) == is_signed); + + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + Index rows = m.rows(); + Index cols = m.cols(); + + // this test relies a lot on Random.h, and there's not much more that we can do + // to test it, hence I consider that we will have tested Random.h + MatrixType m1(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols), + mzero = MatrixType::Zero(rows, cols); + + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> SquareMatrixType; + SquareMatrixType identity = SquareMatrixType::Identity(rows, rows), + square = SquareMatrixType::Random(rows, rows); + VectorType v1(rows), + v2 = VectorType::Random(rows), + vzero = VectorType::Zero(rows); + + do { + m1 = MatrixType::Random(rows, cols); + } while(m1 == mzero || m1 == m2); + + do { + v1 = VectorType::Random(rows); + } while(v1 == vzero || v1 == v2); + + VERIFY_IS_APPROX( v1, v1); + VERIFY_IS_NOT_APPROX( v1, 2*v1); + VERIFY_IS_APPROX( vzero, v1-v1); + VERIFY_IS_APPROX( m1, m1); + VERIFY_IS_NOT_APPROX( m1, 2*m1); + VERIFY_IS_APPROX( mzero, m1-m1); + + VERIFY_IS_APPROX(m3 = m1,m1); + MatrixType m4; + VERIFY_IS_APPROX(m4 = m1,m1); + + m3.real() = m1.real(); + VERIFY_IS_APPROX(static_cast<const MatrixType&>(m3).real(), static_cast<const MatrixType&>(m1).real()); + VERIFY_IS_APPROX(static_cast<const MatrixType&>(m3).real(), m1.real()); + + // check == / != operators + VERIFY(m1==m1); + VERIFY(m1!=m2); + VERIFY(!(m1==m2)); + VERIFY(!(m1!=m1)); + m1 = m2; + VERIFY(m1==m2); + VERIFY(!(m1!=m2)); + + // check linear structure + + Scalar s1; + do { + s1 = internal::random<Scalar>(); + } while(s1 == 0); + + VERIFY_IS_EQUAL(m1+m1, 2*m1); + VERIFY_IS_EQUAL(m1+m2-m1, m2); + VERIFY_IS_EQUAL(m1*s1, s1*m1); + VERIFY_IS_EQUAL((m1+m2)*s1, s1*m1+s1*m2); + m3 = m2; m3 += m1; + VERIFY_IS_EQUAL(m3, m1+m2); + m3 = m2; m3 -= m1; + VERIFY_IS_EQUAL(m3, m2-m1); + m3 = m2; m3 *= s1; + VERIFY_IS_EQUAL(m3, s1*m2); + + // check matrix product. + + VERIFY_IS_APPROX(identity * m1, m1); + VERIFY_IS_APPROX(square * (m1 + m2), square * m1 + square * m2); + VERIFY_IS_APPROX((m1 + m2).transpose() * square, m1.transpose() * square + m2.transpose() * square); + VERIFY_IS_APPROX((m1 * m2.transpose()) * m1, m1 * (m2.transpose() * m1)); +} + +void test_integer_types() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( integer_type_tests(Matrix<unsigned int, 1, 1>()) ); + CALL_SUBTEST_1( integer_type_tests(Matrix<unsigned long, 3, 4>()) ); + + CALL_SUBTEST_2( integer_type_tests(Matrix<long, 2, 2>()) ); + CALL_SUBTEST_2( signed_integer_type_tests(Matrix<long, 2, 2>()) ); + + CALL_SUBTEST_3( integer_type_tests(Matrix<char, 2, Dynamic>(2, 10)) ); + CALL_SUBTEST_3( signed_integer_type_tests(Matrix<signed char, 2, Dynamic>(2, 10)) ); + + CALL_SUBTEST_4( integer_type_tests(Matrix<unsigned char, 3, 3>()) ); + CALL_SUBTEST_4( integer_type_tests(Matrix<unsigned char, Dynamic, Dynamic>(20, 20)) ); + + CALL_SUBTEST_5( integer_type_tests(Matrix<short, Dynamic, 4>(7, 4)) ); + CALL_SUBTEST_5( signed_integer_type_tests(Matrix<short, Dynamic, 4>(7, 4)) ); + + CALL_SUBTEST_6( integer_type_tests(Matrix<unsigned short, 4, 4>()) ); + + CALL_SUBTEST_7( integer_type_tests(Matrix<long long, 11, 13>()) ); + CALL_SUBTEST_7( signed_integer_type_tests(Matrix<long long, 11, 13>()) ); + + CALL_SUBTEST_8( integer_type_tests(Matrix<unsigned long long, Dynamic, 5>(1, 5)) ); + } +} diff --git a/test/inverse.cpp b/test/inverse.cpp new file mode 100644 index 000000000..cff42dd8d --- /dev/null +++ b/test/inverse.cpp @@ -0,0 +1,102 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/LU> + +template<typename MatrixType> void inverse(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + /* this test covers the following files: + Inverse.h + */ + Index rows = m.rows(); + Index cols = m.cols(); + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, 1> VectorType; + + MatrixType m1(rows, cols), + m2(rows, cols), + identity = MatrixType::Identity(rows, rows); + createRandomPIMatrixOfRank(rows,rows,rows,m1); + m2 = m1.inverse(); + VERIFY_IS_APPROX(m1, m2.inverse() ); + + VERIFY_IS_APPROX((Scalar(2)*m2).inverse(), m2.inverse()*Scalar(0.5)); + + VERIFY_IS_APPROX(identity, m1.inverse() * m1 ); + VERIFY_IS_APPROX(identity, m1 * m1.inverse() ); + + VERIFY_IS_APPROX(m1, m1.inverse().inverse() ); + + // since for the general case we implement separately row-major and col-major, test that + VERIFY_IS_APPROX(MatrixType(m1.transpose().inverse()), MatrixType(m1.inverse().transpose())); + +#if !defined(EIGEN_TEST_PART_5) && !defined(EIGEN_TEST_PART_6) + //computeInverseAndDetWithCheck tests + //First: an invertible matrix + bool invertible; + RealScalar det; + + m2.setZero(); + m1.computeInverseAndDetWithCheck(m2, det, invertible); + VERIFY(invertible); + VERIFY_IS_APPROX(identity, m1*m2); + VERIFY_IS_APPROX(det, m1.determinant()); + + m2.setZero(); + m1.computeInverseWithCheck(m2, invertible); + VERIFY(invertible); + VERIFY_IS_APPROX(identity, m1*m2); + + //Second: a rank one matrix (not invertible, except for 1x1 matrices) + VectorType v3 = VectorType::Random(rows); + MatrixType m3 = v3*v3.transpose(), m4(rows,cols); + m3.computeInverseAndDetWithCheck(m4, det, invertible); + VERIFY( rows==1 ? invertible : !invertible ); + VERIFY_IS_MUCH_SMALLER_THAN(internal::abs(det-m3.determinant()), RealScalar(1)); + m3.computeInverseWithCheck(m4, invertible); + VERIFY( rows==1 ? invertible : !invertible ); +#endif + + // check in-place inversion + if(MatrixType::RowsAtCompileTime>=2 && MatrixType::RowsAtCompileTime<=4) + { + // in-place is forbidden + VERIFY_RAISES_ASSERT(m1 = m1.inverse()); + } + else + { + m2 = m1.inverse(); + m1 = m1.inverse(); + VERIFY_IS_APPROX(m1,m2); + } +} + +void test_inverse() +{ + int s; + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( inverse(Matrix<double,1,1>()) ); + CALL_SUBTEST_2( inverse(Matrix2d()) ); + CALL_SUBTEST_3( inverse(Matrix3f()) ); + CALL_SUBTEST_4( inverse(Matrix4f()) ); + CALL_SUBTEST_4( inverse(Matrix<float,4,4,DontAlign>()) ); + s = internal::random<int>(50,320); + CALL_SUBTEST_5( inverse(MatrixXf(s,s)) ); + s = internal::random<int>(25,100); + CALL_SUBTEST_6( inverse(MatrixXcd(s,s)) ); + CALL_SUBTEST_7( inverse(Matrix4d()) ); + CALL_SUBTEST_7( inverse(Matrix<double,4,4,DontAlign>()) ); + } + EIGEN_UNUSED_VARIABLE(s) +} diff --git a/test/jacobi.cpp b/test/jacobi.cpp new file mode 100644 index 000000000..f64f5d08f --- /dev/null +++ b/test/jacobi.cpp @@ -0,0 +1,81 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/SVD> + +template<typename MatrixType, typename JacobiScalar> +void jacobi(const MatrixType& m = MatrixType()) +{ + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::Index Index; + Index rows = m.rows(); + Index cols = m.cols(); + + enum { + RowsAtCompileTime = MatrixType::RowsAtCompileTime, + ColsAtCompileTime = MatrixType::ColsAtCompileTime + }; + + typedef Matrix<JacobiScalar, 2, 1> JacobiVector; + + const MatrixType a(MatrixType::Random(rows, cols)); + + JacobiVector v = JacobiVector::Random().normalized(); + JacobiScalar c = v.x(), s = v.y(); + JacobiRotation<JacobiScalar> rot(c, s); + + { + Index p = internal::random<Index>(0, rows-1); + Index q; + do { + q = internal::random<Index>(0, rows-1); + } while (q == p); + + MatrixType b = a; + b.applyOnTheLeft(p, q, rot); + VERIFY_IS_APPROX(b.row(p), c * a.row(p) + internal::conj(s) * a.row(q)); + VERIFY_IS_APPROX(b.row(q), -s * a.row(p) + internal::conj(c) * a.row(q)); + } + + { + Index p = internal::random<Index>(0, cols-1); + Index q; + do { + q = internal::random<Index>(0, cols-1); + } while (q == p); + + MatrixType b = a; + b.applyOnTheRight(p, q, rot); + VERIFY_IS_APPROX(b.col(p), c * a.col(p) - s * a.col(q)); + VERIFY_IS_APPROX(b.col(q), internal::conj(s) * a.col(p) + internal::conj(c) * a.col(q)); + } +} + +void test_jacobi() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1(( jacobi<Matrix3f, float>() )); + CALL_SUBTEST_2(( jacobi<Matrix4d, double>() )); + CALL_SUBTEST_3(( jacobi<Matrix4cf, float>() )); + CALL_SUBTEST_3(( jacobi<Matrix4cf, std::complex<float> >() )); + + int r = internal::random<int>(2, internal::random<int>(1,EIGEN_TEST_MAX_SIZE)/2), + c = internal::random<int>(2, internal::random<int>(1,EIGEN_TEST_MAX_SIZE)/2); + CALL_SUBTEST_4(( jacobi<MatrixXf, float>(MatrixXf(r,c)) )); + CALL_SUBTEST_5(( jacobi<MatrixXcd, double>(MatrixXcd(r,c)) )); + CALL_SUBTEST_5(( jacobi<MatrixXcd, std::complex<double> >(MatrixXcd(r,c)) )); + // complex<float> is really important to test as it is the only way to cover conjugation issues in certain unaligned paths + CALL_SUBTEST_6(( jacobi<MatrixXcf, float>(MatrixXcf(r,c)) )); + CALL_SUBTEST_6(( jacobi<MatrixXcf, std::complex<float> >(MatrixXcf(r,c)) )); + (void) r; + (void) c; + } +} diff --git a/test/jacobisvd.cpp b/test/jacobisvd.cpp new file mode 100644 index 000000000..f6c567829 --- /dev/null +++ b/test/jacobisvd.cpp @@ -0,0 +1,350 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +// discard stack allocation as that too bypasses malloc +#define EIGEN_STACK_ALLOCATION_LIMIT 0 +#define EIGEN_RUNTIME_NO_MALLOC +#include "main.h" +#include <Eigen/SVD> + +template<typename MatrixType, int QRPreconditioner> +void jacobisvd_check_full(const MatrixType& m, const JacobiSVD<MatrixType, QRPreconditioner>& svd) +{ + typedef typename MatrixType::Index Index; + Index rows = m.rows(); + Index cols = m.cols(); + + enum { + RowsAtCompileTime = MatrixType::RowsAtCompileTime, + ColsAtCompileTime = MatrixType::ColsAtCompileTime + }; + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, RowsAtCompileTime, RowsAtCompileTime> MatrixUType; + typedef Matrix<Scalar, ColsAtCompileTime, ColsAtCompileTime> MatrixVType; + typedef Matrix<Scalar, RowsAtCompileTime, 1> ColVectorType; + typedef Matrix<Scalar, ColsAtCompileTime, 1> InputVectorType; + + MatrixType sigma = MatrixType::Zero(rows,cols); + sigma.diagonal() = svd.singularValues().template cast<Scalar>(); + MatrixUType u = svd.matrixU(); + MatrixVType v = svd.matrixV(); + + VERIFY_IS_APPROX(m, u * sigma * v.adjoint()); + VERIFY_IS_UNITARY(u); + VERIFY_IS_UNITARY(v); +} + +template<typename MatrixType, int QRPreconditioner> +void jacobisvd_compare_to_full(const MatrixType& m, + unsigned int computationOptions, + const JacobiSVD<MatrixType, QRPreconditioner>& referenceSvd) +{ + typedef typename MatrixType::Index Index; + Index rows = m.rows(); + Index cols = m.cols(); + Index diagSize = (std::min)(rows, cols); + + JacobiSVD<MatrixType, QRPreconditioner> svd(m, computationOptions); + + VERIFY_IS_APPROX(svd.singularValues(), referenceSvd.singularValues()); + if(computationOptions & ComputeFullU) + VERIFY_IS_APPROX(svd.matrixU(), referenceSvd.matrixU()); + if(computationOptions & ComputeThinU) + VERIFY_IS_APPROX(svd.matrixU(), referenceSvd.matrixU().leftCols(diagSize)); + if(computationOptions & ComputeFullV) + VERIFY_IS_APPROX(svd.matrixV(), referenceSvd.matrixV()); + if(computationOptions & ComputeThinV) + VERIFY_IS_APPROX(svd.matrixV(), referenceSvd.matrixV().leftCols(diagSize)); +} + +template<typename MatrixType, int QRPreconditioner> +void jacobisvd_solve(const MatrixType& m, unsigned int computationOptions) +{ + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::Index Index; + Index rows = m.rows(); + Index cols = m.cols(); + + enum { + RowsAtCompileTime = MatrixType::RowsAtCompileTime, + ColsAtCompileTime = MatrixType::ColsAtCompileTime + }; + + typedef Matrix<Scalar, RowsAtCompileTime, Dynamic> RhsType; + typedef Matrix<Scalar, ColsAtCompileTime, Dynamic> SolutionType; + + RhsType rhs = RhsType::Random(rows, internal::random<Index>(1, cols)); + JacobiSVD<MatrixType, QRPreconditioner> svd(m, computationOptions); + SolutionType x = svd.solve(rhs); + // evaluate normal equation which works also for least-squares solutions + VERIFY_IS_APPROX(m.adjoint()*m*x,m.adjoint()*rhs); +} + +template<typename MatrixType, int QRPreconditioner> +void jacobisvd_test_all_computation_options(const MatrixType& m) +{ + if (QRPreconditioner == NoQRPreconditioner && m.rows() != m.cols()) + return; + JacobiSVD<MatrixType, QRPreconditioner> fullSvd(m, ComputeFullU|ComputeFullV); + + jacobisvd_check_full(m, fullSvd); + jacobisvd_solve<MatrixType, QRPreconditioner>(m, ComputeFullU | ComputeFullV); + + if(QRPreconditioner == FullPivHouseholderQRPreconditioner) + return; + + jacobisvd_compare_to_full(m, ComputeFullU, fullSvd); + jacobisvd_compare_to_full(m, ComputeFullV, fullSvd); + jacobisvd_compare_to_full(m, 0, fullSvd); + + if (MatrixType::ColsAtCompileTime == Dynamic) { + // thin U/V are only available with dynamic number of columns + jacobisvd_compare_to_full(m, ComputeFullU|ComputeThinV, fullSvd); + jacobisvd_compare_to_full(m, ComputeThinV, fullSvd); + jacobisvd_compare_to_full(m, ComputeThinU|ComputeFullV, fullSvd); + jacobisvd_compare_to_full(m, ComputeThinU , fullSvd); + jacobisvd_compare_to_full(m, ComputeThinU|ComputeThinV, fullSvd); + jacobisvd_solve<MatrixType, QRPreconditioner>(m, ComputeFullU | ComputeThinV); + jacobisvd_solve<MatrixType, QRPreconditioner>(m, ComputeThinU | ComputeFullV); + jacobisvd_solve<MatrixType, QRPreconditioner>(m, ComputeThinU | ComputeThinV); + + // test reconstruction + typedef typename MatrixType::Index Index; + Index diagSize = (std::min)(m.rows(), m.cols()); + JacobiSVD<MatrixType, QRPreconditioner> svd(m, ComputeThinU | ComputeThinV); + VERIFY_IS_APPROX(m, svd.matrixU().leftCols(diagSize) * svd.singularValues().asDiagonal() * svd.matrixV().leftCols(diagSize).adjoint()); + } +} + +template<typename MatrixType> +void jacobisvd(const MatrixType& a = MatrixType(), bool pickrandom = true) +{ + MatrixType m = pickrandom ? MatrixType::Random(a.rows(), a.cols()) : a; + + jacobisvd_test_all_computation_options<MatrixType, FullPivHouseholderQRPreconditioner>(m); + jacobisvd_test_all_computation_options<MatrixType, ColPivHouseholderQRPreconditioner>(m); + jacobisvd_test_all_computation_options<MatrixType, HouseholderQRPreconditioner>(m); + jacobisvd_test_all_computation_options<MatrixType, NoQRPreconditioner>(m); +} + +template<typename MatrixType> void jacobisvd_verify_assert(const MatrixType& m) +{ + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::Index Index; + Index rows = m.rows(); + Index cols = m.cols(); + + enum { + RowsAtCompileTime = MatrixType::RowsAtCompileTime, + ColsAtCompileTime = MatrixType::ColsAtCompileTime + }; + + typedef Matrix<Scalar, RowsAtCompileTime, 1> RhsType; + + RhsType rhs(rows); + + JacobiSVD<MatrixType> svd; + VERIFY_RAISES_ASSERT(svd.matrixU()) + VERIFY_RAISES_ASSERT(svd.singularValues()) + VERIFY_RAISES_ASSERT(svd.matrixV()) + VERIFY_RAISES_ASSERT(svd.solve(rhs)) + + MatrixType a = MatrixType::Zero(rows, cols); + a.setZero(); + svd.compute(a, 0); + VERIFY_RAISES_ASSERT(svd.matrixU()) + VERIFY_RAISES_ASSERT(svd.matrixV()) + svd.singularValues(); + VERIFY_RAISES_ASSERT(svd.solve(rhs)) + + if (ColsAtCompileTime == Dynamic) + { + svd.compute(a, ComputeThinU); + svd.matrixU(); + VERIFY_RAISES_ASSERT(svd.matrixV()) + VERIFY_RAISES_ASSERT(svd.solve(rhs)) + + svd.compute(a, ComputeThinV); + svd.matrixV(); + VERIFY_RAISES_ASSERT(svd.matrixU()) + VERIFY_RAISES_ASSERT(svd.solve(rhs)) + + JacobiSVD<MatrixType, FullPivHouseholderQRPreconditioner> svd_fullqr; + VERIFY_RAISES_ASSERT(svd_fullqr.compute(a, ComputeFullU|ComputeThinV)) + VERIFY_RAISES_ASSERT(svd_fullqr.compute(a, ComputeThinU|ComputeThinV)) + VERIFY_RAISES_ASSERT(svd_fullqr.compute(a, ComputeThinU|ComputeFullV)) + } + else + { + VERIFY_RAISES_ASSERT(svd.compute(a, ComputeThinU)) + VERIFY_RAISES_ASSERT(svd.compute(a, ComputeThinV)) + } +} + +template<typename MatrixType> +void jacobisvd_method() +{ + enum { Size = MatrixType::RowsAtCompileTime }; + typedef typename MatrixType::RealScalar RealScalar; + typedef Matrix<RealScalar, Size, 1> RealVecType; + MatrixType m = MatrixType::Identity(); + VERIFY_IS_APPROX(m.jacobiSvd().singularValues(), RealVecType::Ones()); + VERIFY_RAISES_ASSERT(m.jacobiSvd().matrixU()); + VERIFY_RAISES_ASSERT(m.jacobiSvd().matrixV()); + VERIFY_IS_APPROX(m.jacobiSvd(ComputeFullU|ComputeFullV).solve(m), m); +} + +// work around stupid msvc error when constructing at compile time an expression that involves +// a division by zero, even if the numeric type has floating point +template<typename Scalar> +EIGEN_DONT_INLINE Scalar zero() { return Scalar(0); } + +// workaround aggressive optimization in ICC +template<typename T> EIGEN_DONT_INLINE T sub(T a, T b) { return a - b; } + +template<typename MatrixType> +void jacobisvd_inf_nan() +{ + // all this function does is verify we don't iterate infinitely on nan/inf values + + JacobiSVD<MatrixType> svd; + typedef typename MatrixType::Scalar Scalar; + Scalar some_inf = Scalar(1) / zero<Scalar>(); + VERIFY(sub(some_inf, some_inf) != sub(some_inf, some_inf)); + svd.compute(MatrixType::Constant(10,10,some_inf), ComputeFullU | ComputeFullV); + + Scalar some_nan = zero<Scalar>() / zero<Scalar>(); + VERIFY(some_nan != some_nan); + svd.compute(MatrixType::Constant(10,10,some_nan), ComputeFullU | ComputeFullV); + + MatrixType m = MatrixType::Zero(10,10); + m(internal::random<int>(0,9), internal::random<int>(0,9)) = some_inf; + svd.compute(m, ComputeFullU | ComputeFullV); + + m = MatrixType::Zero(10,10); + m(internal::random<int>(0,9), internal::random<int>(0,9)) = some_nan; + svd.compute(m, ComputeFullU | ComputeFullV); +} + +// Regression test for bug 286: JacobiSVD loops indefinitely with some +// matrices containing denormal numbers. +void jacobisvd_bug286() +{ +#if defined __INTEL_COMPILER +// shut up warning #239: floating point underflow +#pragma warning push +#pragma warning disable 239 +#endif + Matrix2d M; + M << -7.90884e-313, -4.94e-324, + 0, 5.60844e-313; +#if defined __INTEL_COMPILER +#pragma warning pop +#endif + JacobiSVD<Matrix2d> svd; + svd.compute(M); // just check we don't loop indefinitely +} + +void jacobisvd_preallocate() +{ + Vector3f v(3.f, 2.f, 1.f); + MatrixXf m = v.asDiagonal(); + + internal::set_is_malloc_allowed(false); + VERIFY_RAISES_ASSERT(VectorXf v(10);) + JacobiSVD<MatrixXf> svd; + internal::set_is_malloc_allowed(true); + svd.compute(m); + VERIFY_IS_APPROX(svd.singularValues(), v); + + JacobiSVD<MatrixXf> svd2(3,3); + internal::set_is_malloc_allowed(false); + svd2.compute(m); + internal::set_is_malloc_allowed(true); + VERIFY_IS_APPROX(svd2.singularValues(), v); + VERIFY_RAISES_ASSERT(svd2.matrixU()); + VERIFY_RAISES_ASSERT(svd2.matrixV()); + svd2.compute(m, ComputeFullU | ComputeFullV); + VERIFY_IS_APPROX(svd2.matrixU(), Matrix3f::Identity()); + VERIFY_IS_APPROX(svd2.matrixV(), Matrix3f::Identity()); + internal::set_is_malloc_allowed(false); + svd2.compute(m); + internal::set_is_malloc_allowed(true); + + JacobiSVD<MatrixXf> svd3(3,3,ComputeFullU|ComputeFullV); + internal::set_is_malloc_allowed(false); + svd2.compute(m); + internal::set_is_malloc_allowed(true); + VERIFY_IS_APPROX(svd2.singularValues(), v); + VERIFY_IS_APPROX(svd2.matrixU(), Matrix3f::Identity()); + VERIFY_IS_APPROX(svd2.matrixV(), Matrix3f::Identity()); + internal::set_is_malloc_allowed(false); + svd2.compute(m, ComputeFullU|ComputeFullV); + internal::set_is_malloc_allowed(true); +} + +void test_jacobisvd() +{ + CALL_SUBTEST_3(( jacobisvd_verify_assert(Matrix3f()) )); + CALL_SUBTEST_4(( jacobisvd_verify_assert(Matrix4d()) )); + CALL_SUBTEST_7(( jacobisvd_verify_assert(MatrixXf(10,12)) )); + CALL_SUBTEST_8(( jacobisvd_verify_assert(MatrixXcd(7,5)) )); + + for(int i = 0; i < g_repeat; i++) { + Matrix2cd m; + m << 0, 1, + 0, 1; + CALL_SUBTEST_1(( jacobisvd(m, false) )); + m << 1, 0, + 1, 0; + CALL_SUBTEST_1(( jacobisvd(m, false) )); + + Matrix2d n; + n << 0, 0, + 0, 0; + CALL_SUBTEST_2(( jacobisvd(n, false) )); + n << 0, 0, + 0, 1; + CALL_SUBTEST_2(( jacobisvd(n, false) )); + + CALL_SUBTEST_3(( jacobisvd<Matrix3f>() )); + CALL_SUBTEST_4(( jacobisvd<Matrix4d>() )); + CALL_SUBTEST_5(( jacobisvd<Matrix<float,3,5> >() )); + CALL_SUBTEST_6(( jacobisvd<Matrix<double,Dynamic,2> >(Matrix<double,Dynamic,2>(10,2)) )); + + int r = internal::random<int>(1, 30), + c = internal::random<int>(1, 30); + CALL_SUBTEST_7(( jacobisvd<MatrixXf>(MatrixXf(r,c)) )); + CALL_SUBTEST_8(( jacobisvd<MatrixXcd>(MatrixXcd(r,c)) )); + (void) r; + (void) c; + + // Test on inf/nan matrix + CALL_SUBTEST_7( jacobisvd_inf_nan<MatrixXf>() ); + } + + CALL_SUBTEST_7(( jacobisvd<MatrixXf>(MatrixXf(internal::random<int>(EIGEN_TEST_MAX_SIZE/4, EIGEN_TEST_MAX_SIZE/2), internal::random<int>(EIGEN_TEST_MAX_SIZE/4, EIGEN_TEST_MAX_SIZE/2))) )); + CALL_SUBTEST_8(( jacobisvd<MatrixXcd>(MatrixXcd(internal::random<int>(EIGEN_TEST_MAX_SIZE/4, EIGEN_TEST_MAX_SIZE/3), internal::random<int>(EIGEN_TEST_MAX_SIZE/4, EIGEN_TEST_MAX_SIZE/3))) )); + + // test matrixbase method + CALL_SUBTEST_1(( jacobisvd_method<Matrix2cd>() )); + CALL_SUBTEST_3(( jacobisvd_method<Matrix3f>() )); + + // Test problem size constructors + CALL_SUBTEST_7( JacobiSVD<MatrixXf>(10,10) ); + + // Check that preallocation avoids subsequent mallocs + CALL_SUBTEST_9( jacobisvd_preallocate() ); + + // Regression check for bug 286 + CALL_SUBTEST_2( jacobisvd_bug286() ); +} diff --git a/test/linearstructure.cpp b/test/linearstructure.cpp new file mode 100644 index 000000000..fd071c995 --- /dev/null +++ b/test/linearstructure.cpp @@ -0,0 +1,83 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename MatrixType> void linearStructure(const MatrixType& m) +{ + /* this test covers the following files: + CwiseUnaryOp.h, CwiseBinaryOp.h, SelfCwiseBinaryOp.h + */ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + + Index rows = m.rows(); + Index cols = m.cols(); + + // this test relies a lot on Random.h, and there's not much more that we can do + // to test it, hence I consider that we will have tested Random.h + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols); + + Scalar s1 = internal::random<Scalar>(); + while (internal::abs(s1)<1e-3) s1 = internal::random<Scalar>(); + + Index r = internal::random<Index>(0, rows-1), + c = internal::random<Index>(0, cols-1); + + VERIFY_IS_APPROX(-(-m1), m1); + VERIFY_IS_APPROX(m1+m1, 2*m1); + VERIFY_IS_APPROX(m1+m2-m1, m2); + VERIFY_IS_APPROX(-m2+m1+m2, m1); + VERIFY_IS_APPROX(m1*s1, s1*m1); + VERIFY_IS_APPROX((m1+m2)*s1, s1*m1+s1*m2); + VERIFY_IS_APPROX((-m1+m2)*s1, -s1*m1+s1*m2); + m3 = m2; m3 += m1; + VERIFY_IS_APPROX(m3, m1+m2); + m3 = m2; m3 -= m1; + VERIFY_IS_APPROX(m3, m2-m1); + m3 = m2; m3 *= s1; + VERIFY_IS_APPROX(m3, s1*m2); + if(!NumTraits<Scalar>::IsInteger) + { + m3 = m2; m3 /= s1; + VERIFY_IS_APPROX(m3, m2/s1); + } + + // again, test operator() to check const-qualification + VERIFY_IS_APPROX((-m1)(r,c), -(m1(r,c))); + VERIFY_IS_APPROX((m1-m2)(r,c), (m1(r,c))-(m2(r,c))); + VERIFY_IS_APPROX((m1+m2)(r,c), (m1(r,c))+(m2(r,c))); + VERIFY_IS_APPROX((s1*m1)(r,c), s1*(m1(r,c))); + VERIFY_IS_APPROX((m1*s1)(r,c), (m1(r,c))*s1); + if(!NumTraits<Scalar>::IsInteger) + VERIFY_IS_APPROX((m1/s1)(r,c), (m1(r,c))/s1); + + // use .block to disable vectorization and compare to the vectorized version + VERIFY_IS_APPROX(m1+m1.block(0,0,rows,cols), m1+m1); + VERIFY_IS_APPROX(m1.cwiseProduct(m1.block(0,0,rows,cols)), m1.cwiseProduct(m1)); + VERIFY_IS_APPROX(m1 - m1.block(0,0,rows,cols), m1 - m1); + VERIFY_IS_APPROX(m1.block(0,0,rows,cols) * s1, m1 * s1); +} + +void test_linearstructure() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( linearStructure(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( linearStructure(Matrix2f()) ); + CALL_SUBTEST_3( linearStructure(Vector3d()) ); + CALL_SUBTEST_4( linearStructure(Matrix4d()) ); + CALL_SUBTEST_5( linearStructure(MatrixXcf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2), internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2))) ); + CALL_SUBTEST_6( linearStructure(MatrixXf (internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_7( linearStructure(MatrixXi (internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_8( linearStructure(MatrixXcd(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2), internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2))) ); + CALL_SUBTEST_9( linearStructure(ArrayXXf (internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + } +} diff --git a/test/lu.cpp b/test/lu.cpp new file mode 100644 index 000000000..6cbcb0a95 --- /dev/null +++ b/test/lu.cpp @@ -0,0 +1,207 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/LU> +using namespace std; + +template<typename MatrixType> void lu_non_invertible() +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + /* this test covers the following files: + LU.h + */ + Index rows, cols, cols2; + if(MatrixType::RowsAtCompileTime==Dynamic) + { + rows = internal::random<Index>(2,EIGEN_TEST_MAX_SIZE); + } + else + { + rows = MatrixType::RowsAtCompileTime; + } + if(MatrixType::ColsAtCompileTime==Dynamic) + { + cols = internal::random<Index>(2,EIGEN_TEST_MAX_SIZE); + cols2 = internal::random<int>(2,EIGEN_TEST_MAX_SIZE); + } + else + { + cols2 = cols = MatrixType::ColsAtCompileTime; + } + + enum { + RowsAtCompileTime = MatrixType::RowsAtCompileTime, + ColsAtCompileTime = MatrixType::ColsAtCompileTime + }; + typedef typename internal::kernel_retval_base<FullPivLU<MatrixType> >::ReturnType KernelMatrixType; + typedef typename internal::image_retval_base<FullPivLU<MatrixType> >::ReturnType ImageMatrixType; + typedef Matrix<typename MatrixType::Scalar, ColsAtCompileTime, ColsAtCompileTime> + CMatrixType; + typedef Matrix<typename MatrixType::Scalar, RowsAtCompileTime, RowsAtCompileTime> + RMatrixType; + + Index rank = internal::random<Index>(1, (std::min)(rows, cols)-1); + + // The image of the zero matrix should consist of a single (zero) column vector + VERIFY((MatrixType::Zero(rows,cols).fullPivLu().image(MatrixType::Zero(rows,cols)).cols() == 1)); + + MatrixType m1(rows, cols), m3(rows, cols2); + CMatrixType m2(cols, cols2); + createRandomPIMatrixOfRank(rank, rows, cols, m1); + + FullPivLU<MatrixType> lu; + + // The special value 0.01 below works well in tests. Keep in mind that we're only computing the rank + // of singular values are either 0 or 1. + // So it's not clear at all that the epsilon should play any role there. + lu.setThreshold(RealScalar(0.01)); + lu.compute(m1); + + MatrixType u(rows,cols); + u = lu.matrixLU().template triangularView<Upper>(); + RMatrixType l = RMatrixType::Identity(rows,rows); + l.block(0,0,rows,(std::min)(rows,cols)).template triangularView<StrictlyLower>() + = lu.matrixLU().block(0,0,rows,(std::min)(rows,cols)); + + VERIFY_IS_APPROX(lu.permutationP() * m1 * lu.permutationQ(), l*u); + + KernelMatrixType m1kernel = lu.kernel(); + ImageMatrixType m1image = lu.image(m1); + + VERIFY_IS_APPROX(m1, lu.reconstructedMatrix()); + VERIFY(rank == lu.rank()); + VERIFY(cols - lu.rank() == lu.dimensionOfKernel()); + VERIFY(!lu.isInjective()); + VERIFY(!lu.isInvertible()); + VERIFY(!lu.isSurjective()); + VERIFY((m1 * m1kernel).isMuchSmallerThan(m1)); + VERIFY(m1image.fullPivLu().rank() == rank); + VERIFY_IS_APPROX(m1 * m1.adjoint() * m1image, m1image); + + m2 = CMatrixType::Random(cols,cols2); + m3 = m1*m2; + m2 = CMatrixType::Random(cols,cols2); + // test that the code, which does resize(), may be applied to an xpr + m2.block(0,0,m2.rows(),m2.cols()) = lu.solve(m3); + VERIFY_IS_APPROX(m3, m1*m2); +} + +template<typename MatrixType> void lu_invertible() +{ + /* this test covers the following files: + LU.h + */ + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar; + int size = internal::random<int>(1,EIGEN_TEST_MAX_SIZE); + + MatrixType m1(size, size), m2(size, size), m3(size, size); + FullPivLU<MatrixType> lu; + lu.setThreshold(RealScalar(0.01)); + do { + m1 = MatrixType::Random(size,size); + lu.compute(m1); + } while(!lu.isInvertible()); + + VERIFY_IS_APPROX(m1, lu.reconstructedMatrix()); + VERIFY(0 == lu.dimensionOfKernel()); + VERIFY(lu.kernel().cols() == 1); // the kernel() should consist of a single (zero) column vector + VERIFY(size == lu.rank()); + VERIFY(lu.isInjective()); + VERIFY(lu.isSurjective()); + VERIFY(lu.isInvertible()); + VERIFY(lu.image(m1).fullPivLu().isInvertible()); + m3 = MatrixType::Random(size,size); + m2 = lu.solve(m3); + VERIFY_IS_APPROX(m3, m1*m2); + VERIFY_IS_APPROX(m2, lu.inverse()*m3); +} + +template<typename MatrixType> void lu_partial_piv() +{ + /* this test covers the following files: + PartialPivLU.h + */ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar; + Index rows = internal::random<Index>(1,4); + Index cols = rows; + + MatrixType m1(cols, rows); + m1.setRandom(); + PartialPivLU<MatrixType> plu(m1); + + VERIFY_IS_APPROX(m1, plu.reconstructedMatrix()); +} + +template<typename MatrixType> void lu_verify_assert() +{ + MatrixType tmp; + + FullPivLU<MatrixType> lu; + VERIFY_RAISES_ASSERT(lu.matrixLU()) + VERIFY_RAISES_ASSERT(lu.permutationP()) + VERIFY_RAISES_ASSERT(lu.permutationQ()) + VERIFY_RAISES_ASSERT(lu.kernel()) + VERIFY_RAISES_ASSERT(lu.image(tmp)) + VERIFY_RAISES_ASSERT(lu.solve(tmp)) + VERIFY_RAISES_ASSERT(lu.determinant()) + VERIFY_RAISES_ASSERT(lu.rank()) + VERIFY_RAISES_ASSERT(lu.dimensionOfKernel()) + VERIFY_RAISES_ASSERT(lu.isInjective()) + VERIFY_RAISES_ASSERT(lu.isSurjective()) + VERIFY_RAISES_ASSERT(lu.isInvertible()) + VERIFY_RAISES_ASSERT(lu.inverse()) + + PartialPivLU<MatrixType> plu; + VERIFY_RAISES_ASSERT(plu.matrixLU()) + VERIFY_RAISES_ASSERT(plu.permutationP()) + VERIFY_RAISES_ASSERT(plu.solve(tmp)) + VERIFY_RAISES_ASSERT(plu.determinant()) + VERIFY_RAISES_ASSERT(plu.inverse()) +} + +void test_lu() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( lu_non_invertible<Matrix3f>() ); + CALL_SUBTEST_1( lu_verify_assert<Matrix3f>() ); + + CALL_SUBTEST_2( (lu_non_invertible<Matrix<double, 4, 6> >()) ); + CALL_SUBTEST_2( (lu_verify_assert<Matrix<double, 4, 6> >()) ); + + CALL_SUBTEST_3( lu_non_invertible<MatrixXf>() ); + CALL_SUBTEST_3( lu_invertible<MatrixXf>() ); + CALL_SUBTEST_3( lu_verify_assert<MatrixXf>() ); + + CALL_SUBTEST_4( lu_non_invertible<MatrixXd>() ); + CALL_SUBTEST_4( lu_invertible<MatrixXd>() ); + CALL_SUBTEST_4( lu_partial_piv<MatrixXd>() ); + CALL_SUBTEST_4( lu_verify_assert<MatrixXd>() ); + + CALL_SUBTEST_5( lu_non_invertible<MatrixXcf>() ); + CALL_SUBTEST_5( lu_invertible<MatrixXcf>() ); + CALL_SUBTEST_5( lu_verify_assert<MatrixXcf>() ); + + CALL_SUBTEST_6( lu_non_invertible<MatrixXcd>() ); + CALL_SUBTEST_6( lu_invertible<MatrixXcd>() ); + CALL_SUBTEST_6( lu_partial_piv<MatrixXcd>() ); + CALL_SUBTEST_6( lu_verify_assert<MatrixXcd>() ); + + CALL_SUBTEST_7(( lu_non_invertible<Matrix<float,Dynamic,16> >() )); + + // Test problem size constructors + CALL_SUBTEST_9( PartialPivLU<MatrixXf>(10) ); + CALL_SUBTEST_9( FullPivLU<MatrixXf>(10, 20); ); + } +} diff --git a/test/main.h b/test/main.h new file mode 100644 index 000000000..2da327c17 --- /dev/null +++ b/test/main.h @@ -0,0 +1,472 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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 <cstdlib> +#include <cerrno> +#include <ctime> +#include <iostream> +#include <fstream> +#include <string> +#include <vector> +#include <typeinfo> +#include <limits> +#include <algorithm> +#include <sstream> +#include <complex> +#include <deque> +#include <queue> + +#define min(A,B) please_protect_your_min_with_parentheses +#define max(A,B) please_protect_your_max_with_parentheses + +#define FORBIDDEN_IDENTIFIER (this_identifier_is_forbidden_to_avoid_clashes) this_identifier_is_forbidden_to_avoid_clashes +// B0 is defined in POSIX header termios.h +#define B0 FORBIDDEN_IDENTIFIER + +// the following file is automatically generated by cmake +#include "split_test_helper.h" + +#ifdef NDEBUG +#undef NDEBUG +#endif + +// bounds integer values for AltiVec +#ifdef __ALTIVEC__ +#define EIGEN_MAKING_DOCS +#endif + +#ifndef EIGEN_TEST_FUNC +#error EIGEN_TEST_FUNC must be defined +#endif + +#define DEFAULT_REPEAT 10 + +#ifdef __ICC +// disable warning #279: controlling expression is constant +#pragma warning disable 279 +#endif + +namespace Eigen +{ + static std::vector<std::string> g_test_stack; + static int g_repeat; + static unsigned int g_seed; + static bool g_has_set_repeat, g_has_set_seed; +} + +#define EI_PP_MAKE_STRING2(S) #S +#define EI_PP_MAKE_STRING(S) EI_PP_MAKE_STRING2(S) + +#define EIGEN_DEFAULT_IO_FORMAT IOFormat(4, 0, " ", "\n", "", "", "", "") + +#ifndef EIGEN_NO_ASSERTION_CHECKING + + namespace Eigen + { + static const bool should_raise_an_assert = false; + + // Used to avoid to raise two exceptions at a time in which + // case the exception is not properly caught. + // This may happen when a second exceptions is triggered in a destructor. + static bool no_more_assert = false; + static bool report_on_cerr_on_assert_failure = true; + + struct eigen_assert_exception + { + eigen_assert_exception(void) {} + ~eigen_assert_exception() { Eigen::no_more_assert = false; } + }; + } + // If EIGEN_DEBUG_ASSERTS is defined and if no assertion is triggered while + // one should have been, then the list of excecuted assertions is printed out. + // + // EIGEN_DEBUG_ASSERTS is not enabled by default as it + // significantly increases the compilation time + // and might even introduce side effects that would hide + // some memory errors. + #ifdef EIGEN_DEBUG_ASSERTS + + namespace Eigen + { + namespace internal + { + static bool push_assert = false; + } + static std::vector<std::string> eigen_assert_list; + } + #define eigen_assert(a) \ + if( (!(a)) && (!no_more_assert) ) \ + { \ + if(report_on_cerr_on_assert_failure) \ + std::cerr << #a << " " __FILE__ << "(" << __LINE__ << ")\n"; \ + Eigen::no_more_assert = true; \ + throw Eigen::eigen_assert_exception(); \ + } \ + else if (Eigen::internal::push_assert) \ + { \ + eigen_assert_list.push_back(std::string(EI_PP_MAKE_STRING(__FILE__) " (" EI_PP_MAKE_STRING(__LINE__) ") : " #a) ); \ + } + + #define VERIFY_RAISES_ASSERT(a) \ + { \ + Eigen::no_more_assert = false; \ + Eigen::eigen_assert_list.clear(); \ + Eigen::internal::push_assert = true; \ + Eigen::report_on_cerr_on_assert_failure = false; \ + try { \ + a; \ + std::cerr << "One of the following asserts should have been triggered:\n"; \ + for (uint ai=0 ; ai<eigen_assert_list.size() ; ++ai) \ + std::cerr << " " << eigen_assert_list[ai] << "\n"; \ + VERIFY(Eigen::should_raise_an_assert && # a); \ + } catch (Eigen::eigen_assert_exception) { \ + Eigen::internal::push_assert = false; VERIFY(true); \ + } \ + Eigen::report_on_cerr_on_assert_failure = true; \ + Eigen::internal::push_assert = false; \ + } + + #else // EIGEN_DEBUG_ASSERTS + // see bug 89. The copy_bool here is working around a bug in gcc <= 4.3 + #define eigen_assert(a) \ + if( (!Eigen::internal::copy_bool(a)) && (!no_more_assert) )\ + { \ + Eigen::no_more_assert = true; \ + if(report_on_cerr_on_assert_failure) \ + eigen_plain_assert(a); \ + else \ + throw Eigen::eigen_assert_exception(); \ + } + #define VERIFY_RAISES_ASSERT(a) { \ + Eigen::no_more_assert = false; \ + Eigen::report_on_cerr_on_assert_failure = false; \ + try { \ + a; \ + VERIFY(Eigen::should_raise_an_assert && # a); \ + } \ + catch (Eigen::eigen_assert_exception&) { VERIFY(true); } \ + Eigen::report_on_cerr_on_assert_failure = true; \ + } + + #endif // EIGEN_DEBUG_ASSERTS + + #define EIGEN_USE_CUSTOM_ASSERT + +#else // EIGEN_NO_ASSERTION_CHECKING + + #define VERIFY_RAISES_ASSERT(a) {} + +#endif // EIGEN_NO_ASSERTION_CHECKING + + +#define EIGEN_INTERNAL_DEBUGGING +#include <Eigen/QR> // required for createRandomPIMatrixOfRank + +static void verify_impl(bool condition, const char *testname, const char *file, int line, const char *condition_as_string) +{ + if (!condition) + { + std::cerr << "Test " << testname << " failed in " << file << " (" << line << ")" \ + << std::endl << " " << condition_as_string << std::endl << std::endl; \ + abort(); + } +} + +#define VERIFY(a) ::verify_impl(a, g_test_stack.back().c_str(), __FILE__, __LINE__, EI_PP_MAKE_STRING(a)) + +#define VERIFY_IS_EQUAL(a, b) VERIFY(test_is_equal(a, b)) +#define VERIFY_IS_APPROX(a, b) VERIFY(test_isApprox(a, b)) +#define VERIFY_IS_NOT_APPROX(a, b) VERIFY(!test_isApprox(a, b)) +#define VERIFY_IS_MUCH_SMALLER_THAN(a, b) VERIFY(test_isMuchSmallerThan(a, b)) +#define VERIFY_IS_NOT_MUCH_SMALLER_THAN(a, b) VERIFY(!test_isMuchSmallerThan(a, b)) +#define VERIFY_IS_APPROX_OR_LESS_THAN(a, b) VERIFY(test_isApproxOrLessThan(a, b)) +#define VERIFY_IS_NOT_APPROX_OR_LESS_THAN(a, b) VERIFY(!test_isApproxOrLessThan(a, b)) + +#define VERIFY_IS_UNITARY(a) VERIFY(test_isUnitary(a)) + +#define CALL_SUBTEST(FUNC) do { \ + g_test_stack.push_back(EI_PP_MAKE_STRING(FUNC)); \ + FUNC; \ + g_test_stack.pop_back(); \ + } while (0) + + +namespace Eigen { + +template<typename T> inline typename NumTraits<T>::Real test_precision() { return NumTraits<T>::dummy_precision(); } +template<> inline float test_precision<float>() { return 1e-3f; } +template<> inline double test_precision<double>() { return 1e-6; } +template<> inline float test_precision<std::complex<float> >() { return test_precision<float>(); } +template<> inline double test_precision<std::complex<double> >() { return test_precision<double>(); } +template<> inline long double test_precision<long double>() { return 1e-6; } + +inline bool test_isApprox(const int& a, const int& b) +{ return internal::isApprox(a, b, test_precision<int>()); } +inline bool test_isMuchSmallerThan(const int& a, const int& b) +{ return internal::isMuchSmallerThan(a, b, test_precision<int>()); } +inline bool test_isApproxOrLessThan(const int& a, const int& b) +{ return internal::isApproxOrLessThan(a, b, test_precision<int>()); } + +inline bool test_isApprox(const float& a, const float& b) +{ return internal::isApprox(a, b, test_precision<float>()); } +inline bool test_isMuchSmallerThan(const float& a, const float& b) +{ return internal::isMuchSmallerThan(a, b, test_precision<float>()); } +inline bool test_isApproxOrLessThan(const float& a, const float& b) +{ return internal::isApproxOrLessThan(a, b, test_precision<float>()); } +inline bool test_isApprox(const double& a, const double& b) +{ return internal::isApprox(a, b, test_precision<double>()); } + +inline bool test_isMuchSmallerThan(const double& a, const double& b) +{ return internal::isMuchSmallerThan(a, b, test_precision<double>()); } +inline bool test_isApproxOrLessThan(const double& a, const double& b) +{ return internal::isApproxOrLessThan(a, b, test_precision<double>()); } + +inline bool test_isApprox(const std::complex<float>& a, const std::complex<float>& b) +{ return internal::isApprox(a, b, test_precision<std::complex<float> >()); } +inline bool test_isMuchSmallerThan(const std::complex<float>& a, const std::complex<float>& b) +{ return internal::isMuchSmallerThan(a, b, test_precision<std::complex<float> >()); } + +inline bool test_isApprox(const std::complex<double>& a, const std::complex<double>& b) +{ return internal::isApprox(a, b, test_precision<std::complex<double> >()); } +inline bool test_isMuchSmallerThan(const std::complex<double>& a, const std::complex<double>& b) +{ return internal::isMuchSmallerThan(a, b, test_precision<std::complex<double> >()); } + +inline bool test_isApprox(const long double& a, const long double& b) +{ + bool ret = internal::isApprox(a, b, test_precision<long double>()); + if (!ret) std::cerr + << std::endl << " actual = " << a + << std::endl << " expected = " << b << std::endl << std::endl; + return ret; +} + +inline bool test_isMuchSmallerThan(const long double& a, const long double& b) +{ return internal::isMuchSmallerThan(a, b, test_precision<long double>()); } +inline bool test_isApproxOrLessThan(const long double& a, const long double& b) +{ return internal::isApproxOrLessThan(a, b, test_precision<long double>()); } + +template<typename Type1, typename Type2> +inline bool test_isApprox(const Type1& a, const Type2& b) +{ + return a.isApprox(b, test_precision<typename Type1::Scalar>()); +} + +// The idea behind this function is to compare the two scalars a and b where +// the scalar ref is a hint about the expected order of magnitude of a and b. +// Therefore, if for some reason a and b are very small compared to ref, +// we won't issue a false negative. +// This test could be: abs(a-b) <= eps * ref +// However, it seems that simply comparing a+ref and b+ref is more sensitive to true error. +template<typename Scalar,typename ScalarRef> +inline bool test_isApproxWithRef(const Scalar& a, const Scalar& b, const ScalarRef& ref) +{ + return test_isApprox(a+ref, b+ref); +} + +template<typename Derived1, typename Derived2> +inline bool test_isMuchSmallerThan(const MatrixBase<Derived1>& m1, + const MatrixBase<Derived2>& m2) +{ + return m1.isMuchSmallerThan(m2, test_precision<typename internal::traits<Derived1>::Scalar>()); +} + +template<typename Derived> +inline bool test_isMuchSmallerThan(const MatrixBase<Derived>& m, + const typename NumTraits<typename internal::traits<Derived>::Scalar>::Real& s) +{ + return m.isMuchSmallerThan(s, test_precision<typename internal::traits<Derived>::Scalar>()); +} + +template<typename Derived> +inline bool test_isUnitary(const MatrixBase<Derived>& m) +{ + return m.isUnitary(test_precision<typename internal::traits<Derived>::Scalar>()); +} + +template<typename T, typename U> +bool test_is_equal(const T& actual, const U& expected) +{ + if (actual==expected) + return true; + // false: + std::cerr + << std::endl << " actual = " << actual + << std::endl << " expected = " << expected << std::endl << std::endl; + return false; +} + +/** Creates a random Partial Isometry matrix of given rank. + * + * A partial isometry is a matrix all of whose singular values are either 0 or 1. + * This is very useful to test rank-revealing algorithms. + */ +template<typename MatrixType> +void createRandomPIMatrixOfRank(typename MatrixType::Index desired_rank, typename MatrixType::Index rows, typename MatrixType::Index cols, MatrixType& m) +{ + typedef typename internal::traits<MatrixType>::Index Index; + typedef typename internal::traits<MatrixType>::Scalar Scalar; + enum { Rows = MatrixType::RowsAtCompileTime, Cols = MatrixType::ColsAtCompileTime }; + + typedef Matrix<Scalar, Dynamic, 1> VectorType; + typedef Matrix<Scalar, Rows, Rows> MatrixAType; + typedef Matrix<Scalar, Cols, Cols> MatrixBType; + + if(desired_rank == 0) + { + m.setZero(rows,cols); + return; + } + + if(desired_rank == 1) + { + // here we normalize the vectors to get a partial isometry + m = VectorType::Random(rows).normalized() * VectorType::Random(cols).normalized().transpose(); + return; + } + + MatrixAType a = MatrixAType::Random(rows,rows); + MatrixType d = MatrixType::Identity(rows,cols); + MatrixBType b = MatrixBType::Random(cols,cols); + + // set the diagonal such that only desired_rank non-zero entries reamain + const Index diag_size = (std::min)(d.rows(),d.cols()); + if(diag_size != desired_rank) + d.diagonal().segment(desired_rank, diag_size-desired_rank) = VectorType::Zero(diag_size-desired_rank); + + HouseholderQR<MatrixAType> qra(a); + HouseholderQR<MatrixBType> qrb(b); + m = qra.householderQ() * d * qrb.householderQ(); +} + +template<typename PermutationVectorType> +void randomPermutationVector(PermutationVectorType& v, typename PermutationVectorType::Index size) +{ + typedef typename PermutationVectorType::Index Index; + typedef typename PermutationVectorType::Scalar Scalar; + v.resize(size); + for(Index i = 0; i < size; ++i) v(i) = Scalar(i); + if(size == 1) return; + for(Index n = 0; n < 3 * size; ++n) + { + Index i = internal::random<Index>(0, size-1); + Index j; + do j = internal::random<Index>(0, size-1); while(j==i); + std::swap(v(i), v(j)); + } +} + +} // end namespace Eigen + +template<typename T> struct GetDifferentType; + +template<> struct GetDifferentType<float> { typedef double type; }; +template<> struct GetDifferentType<double> { typedef float type; }; +template<typename T> struct GetDifferentType<std::complex<T> > +{ typedef std::complex<typename GetDifferentType<T>::type> type; }; + +template<typename T> std::string type_name() { return "other"; } +template<> std::string type_name<float>() { return "float"; } +template<> std::string type_name<double>() { return "double"; } +template<> std::string type_name<int>() { return "int"; } +template<> std::string type_name<std::complex<float> >() { return "complex<float>"; } +template<> std::string type_name<std::complex<double> >() { return "complex<double>"; } +template<> std::string type_name<std::complex<int> >() { return "complex<int>"; } + +// forward declaration of the main test function +void EIGEN_CAT(test_,EIGEN_TEST_FUNC)(); + +using namespace Eigen; + +void set_repeat_from_string(const char *str) +{ + errno = 0; + g_repeat = int(strtoul(str, 0, 10)); + if(errno || g_repeat <= 0) + { + std::cout << "Invalid repeat value " << str << std::endl; + exit(EXIT_FAILURE); + } + g_has_set_repeat = true; +} + +void set_seed_from_string(const char *str) +{ + errno = 0; + g_seed = strtoul(str, 0, 10); + if(errno || g_seed == 0) + { + std::cout << "Invalid seed value " << str << std::endl; + exit(EXIT_FAILURE); + } + g_has_set_seed = true; +} + +int main(int argc, char *argv[]) +{ + g_has_set_repeat = false; + g_has_set_seed = false; + bool need_help = false; + + for(int i = 1; i < argc; i++) + { + if(argv[i][0] == 'r') + { + if(g_has_set_repeat) + { + std::cout << "Argument " << argv[i] << " conflicting with a former argument" << std::endl; + return 1; + } + set_repeat_from_string(argv[i]+1); + } + else if(argv[i][0] == 's') + { + if(g_has_set_seed) + { + std::cout << "Argument " << argv[i] << " conflicting with a former argument" << std::endl; + return 1; + } + set_seed_from_string(argv[i]+1); + } + else + { + need_help = true; + } + } + + if(need_help) + { + std::cout << "This test application takes the following optional arguments:" << std::endl; + std::cout << " rN Repeat each test N times (default: " << DEFAULT_REPEAT << ")" << std::endl; + std::cout << " sN Use N as seed for random numbers (default: based on current time)" << std::endl; + std::cout << std::endl; + std::cout << "If defined, the environment variables EIGEN_REPEAT and EIGEN_SEED" << std::endl; + std::cout << "will be used as default values for these parameters." << std::endl; + return 1; + } + + char *env_EIGEN_REPEAT = getenv("EIGEN_REPEAT"); + if(!g_has_set_repeat && env_EIGEN_REPEAT) + set_repeat_from_string(env_EIGEN_REPEAT); + char *env_EIGEN_SEED = getenv("EIGEN_SEED"); + if(!g_has_set_seed && env_EIGEN_SEED) + set_seed_from_string(env_EIGEN_SEED); + + if(!g_has_set_seed) g_seed = (unsigned int) time(NULL); + if(!g_has_set_repeat) g_repeat = DEFAULT_REPEAT; + + std::cout << "Initializing random number generator with seed " << g_seed << std::endl; + srand(g_seed); + std::cout << "Repeating each test " << g_repeat << " times" << std::endl; + + Eigen::g_test_stack.push_back(EI_PP_MAKE_STRING(EIGEN_TEST_FUNC)); + + EIGEN_CAT(test_,EIGEN_TEST_FUNC)(); + return 0; +} diff --git a/test/map.cpp b/test/map.cpp new file mode 100644 index 000000000..fe983e802 --- /dev/null +++ b/test/map.cpp @@ -0,0 +1,144 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2006-2010 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#ifndef EIGEN_NO_STATIC_ASSERT +#define EIGEN_NO_STATIC_ASSERT // turn static asserts into runtime asserts in order to check them +#endif + +#include "main.h" + +template<typename VectorType> void map_class_vector(const VectorType& m) +{ + typedef typename VectorType::Index Index; + typedef typename VectorType::Scalar Scalar; + + Index size = m.size(); + + // test Map.h + Scalar* array1 = internal::aligned_new<Scalar>(size); + Scalar* array2 = internal::aligned_new<Scalar>(size); + Scalar* array3 = new Scalar[size+1]; + Scalar* array3unaligned = size_t(array3)%16 == 0 ? array3+1 : array3; + + Map<VectorType, Aligned>(array1, size) = VectorType::Random(size); + Map<VectorType, Aligned>(array2, size) = Map<VectorType,Aligned>(array1, size); + Map<VectorType>(array3unaligned, size) = Map<VectorType>(array1, size); + VectorType ma1 = Map<VectorType, Aligned>(array1, size); + VectorType ma2 = Map<VectorType, Aligned>(array2, size); + VectorType ma3 = Map<VectorType>(array3unaligned, size); + VERIFY_IS_EQUAL(ma1, ma2); + VERIFY_IS_EQUAL(ma1, ma3); + #ifdef EIGEN_VECTORIZE + if(internal::packet_traits<Scalar>::Vectorizable) + VERIFY_RAISES_ASSERT((Map<VectorType,Aligned>(array3unaligned, size))) + #endif + + internal::aligned_delete(array1, size); + internal::aligned_delete(array2, size); + delete[] array3; +} + +template<typename MatrixType> void map_class_matrix(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + + Index rows = m.rows(), cols = m.cols(), size = rows*cols; + + // test Map.h + Scalar* array1 = internal::aligned_new<Scalar>(size); + for(int i = 0; i < size; i++) array1[i] = Scalar(1); + Scalar* array2 = internal::aligned_new<Scalar>(size); + for(int i = 0; i < size; i++) array2[i] = Scalar(1); + Scalar* array3 = new Scalar[size+1]; + for(int i = 0; i < size+1; i++) array3[i] = Scalar(1); + Scalar* array3unaligned = size_t(array3)%16 == 0 ? array3+1 : array3; + Map<MatrixType, Aligned>(array1, rows, cols) = MatrixType::Ones(rows,cols); + Map<MatrixType>(array2, rows, cols) = Map<MatrixType>(array1, rows, cols); + Map<MatrixType>(array3unaligned, rows, cols) = Map<MatrixType>(array1, rows, cols); + MatrixType ma1 = Map<MatrixType>(array1, rows, cols); + MatrixType ma2 = Map<MatrixType, Aligned>(array2, rows, cols); + VERIFY_IS_EQUAL(ma1, ma2); + MatrixType ma3 = Map<MatrixType>(array3unaligned, rows, cols); + VERIFY_IS_EQUAL(ma1, ma3); + + internal::aligned_delete(array1, size); + internal::aligned_delete(array2, size); + delete[] array3; +} + +template<typename VectorType> void map_static_methods(const VectorType& m) +{ + typedef typename VectorType::Index Index; + typedef typename VectorType::Scalar Scalar; + + Index size = m.size(); + + // test Map.h + Scalar* array1 = internal::aligned_new<Scalar>(size); + Scalar* array2 = internal::aligned_new<Scalar>(size); + Scalar* array3 = new Scalar[size+1]; + Scalar* array3unaligned = size_t(array3)%16 == 0 ? array3+1 : array3; + + VectorType::MapAligned(array1, size) = VectorType::Random(size); + VectorType::Map(array2, size) = VectorType::Map(array1, size); + VectorType::Map(array3unaligned, size) = VectorType::Map(array1, size); + VectorType ma1 = VectorType::Map(array1, size); + VectorType ma2 = VectorType::MapAligned(array2, size); + VectorType ma3 = VectorType::Map(array3unaligned, size); + VERIFY_IS_EQUAL(ma1, ma2); + VERIFY_IS_EQUAL(ma1, ma3); + + internal::aligned_delete(array1, size); + internal::aligned_delete(array2, size); + delete[] array3; +} + +template<typename PlainObjectType> void check_const_correctness(const PlainObjectType&) +{ + typedef typename PlainObjectType::Index Index; + typedef typename PlainObjectType::Scalar Scalar; + + // there's a lot that we can't test here while still having this test compile! + // the only possible approach would be to run a script trying to compile stuff and checking that it fails. + // CMake can help with that. + + // verify that map-to-const don't have LvalueBit + typedef typename internal::add_const<PlainObjectType>::type ConstPlainObjectType; + VERIFY( !(internal::traits<Map<ConstPlainObjectType> >::Flags & LvalueBit) ); + VERIFY( !(internal::traits<Map<ConstPlainObjectType, Aligned> >::Flags & LvalueBit) ); + VERIFY( !(Map<ConstPlainObjectType>::Flags & LvalueBit) ); + VERIFY( !(Map<ConstPlainObjectType, Aligned>::Flags & LvalueBit) ); +} + +void test_map() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( map_class_vector(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_1( check_const_correctness(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( map_class_vector(Vector4d()) ); + CALL_SUBTEST_2( check_const_correctness(Matrix4d()) ); + CALL_SUBTEST_3( map_class_vector(RowVector4f()) ); + CALL_SUBTEST_4( map_class_vector(VectorXcf(8)) ); + CALL_SUBTEST_5( map_class_vector(VectorXi(12)) ); + CALL_SUBTEST_5( check_const_correctness(VectorXi(12)) ); + + CALL_SUBTEST_1( map_class_matrix(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( map_class_matrix(Matrix4d()) ); + CALL_SUBTEST_11( map_class_matrix(Matrix<float,3,5>()) ); + CALL_SUBTEST_4( map_class_matrix(MatrixXcf(internal::random<int>(1,10),internal::random<int>(1,10))) ); + CALL_SUBTEST_5( map_class_matrix(MatrixXi(internal::random<int>(1,10),internal::random<int>(1,10))) ); + + CALL_SUBTEST_6( map_static_methods(Matrix<double, 1, 1>()) ); + CALL_SUBTEST_7( map_static_methods(Vector3f()) ); + CALL_SUBTEST_8( map_static_methods(RowVector3d()) ); + CALL_SUBTEST_9( map_static_methods(VectorXcd(8)) ); + CALL_SUBTEST_10( map_static_methods(VectorXf(12)) ); + } +} diff --git a/test/mapstaticmethods.cpp b/test/mapstaticmethods.cpp new file mode 100644 index 000000000..5b512bde4 --- /dev/null +++ b/test/mapstaticmethods.cpp @@ -0,0 +1,173 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" + +float *ptr; +const float *const_ptr; + +template<typename PlainObjectType, + bool IsDynamicSize = PlainObjectType::SizeAtCompileTime == Dynamic, + bool IsVector = PlainObjectType::IsVectorAtCompileTime +> +struct mapstaticmethods_impl {}; + +template<typename PlainObjectType, bool IsVector> +struct mapstaticmethods_impl<PlainObjectType, false, IsVector> +{ + static void run(const PlainObjectType& m) + { + mapstaticmethods_impl<PlainObjectType, true, IsVector>::run(m); + + int i = internal::random<int>(2,5), j = internal::random<int>(2,5); + + PlainObjectType::Map(ptr).setZero(); + PlainObjectType::MapAligned(ptr).setZero(); + PlainObjectType::Map(const_ptr).sum(); + PlainObjectType::MapAligned(const_ptr).sum(); + + PlainObjectType::Map(ptr, InnerStride<>(i)).setZero(); + PlainObjectType::MapAligned(ptr, InnerStride<>(i)).setZero(); + PlainObjectType::Map(const_ptr, InnerStride<>(i)).sum(); + PlainObjectType::MapAligned(const_ptr, InnerStride<>(i)).sum(); + + PlainObjectType::Map(ptr, InnerStride<2>()).setZero(); + PlainObjectType::MapAligned(ptr, InnerStride<3>()).setZero(); + PlainObjectType::Map(const_ptr, InnerStride<4>()).sum(); + PlainObjectType::MapAligned(const_ptr, InnerStride<5>()).sum(); + + PlainObjectType::Map(ptr, OuterStride<>(i)).setZero(); + PlainObjectType::MapAligned(ptr, OuterStride<>(i)).setZero(); + PlainObjectType::Map(const_ptr, OuterStride<>(i)).sum(); + PlainObjectType::MapAligned(const_ptr, OuterStride<>(i)).sum(); + + PlainObjectType::Map(ptr, OuterStride<2>()).setZero(); + PlainObjectType::MapAligned(ptr, OuterStride<3>()).setZero(); + PlainObjectType::Map(const_ptr, OuterStride<4>()).sum(); + PlainObjectType::MapAligned(const_ptr, OuterStride<5>()).sum(); + + PlainObjectType::Map(ptr, Stride<Dynamic, Dynamic>(i,j)).setZero(); + PlainObjectType::MapAligned(ptr, Stride<2,Dynamic>(2,i)).setZero(); + PlainObjectType::Map(const_ptr, Stride<Dynamic,3>(i,3)).sum(); + PlainObjectType::MapAligned(const_ptr, Stride<Dynamic, Dynamic>(i,j)).sum(); + + PlainObjectType::Map(ptr, Stride<2,3>()).setZero(); + PlainObjectType::MapAligned(ptr, Stride<3,4>()).setZero(); + PlainObjectType::Map(const_ptr, Stride<2,4>()).sum(); + PlainObjectType::MapAligned(const_ptr, Stride<5,3>()).sum(); + } +}; + +template<typename PlainObjectType> +struct mapstaticmethods_impl<PlainObjectType, true, false> +{ + static void run(const PlainObjectType& m) + { + int rows = m.rows(), cols = m.cols(); + + int i = internal::random<int>(2,5), j = internal::random<int>(2,5); + + PlainObjectType::Map(ptr, rows, cols).setZero(); + PlainObjectType::MapAligned(ptr, rows, cols).setZero(); + PlainObjectType::Map(const_ptr, rows, cols).sum(); + PlainObjectType::MapAligned(const_ptr, rows, cols).sum(); + + PlainObjectType::Map(ptr, rows, cols, InnerStride<>(i)).setZero(); + PlainObjectType::MapAligned(ptr, rows, cols, InnerStride<>(i)).setZero(); + PlainObjectType::Map(const_ptr, rows, cols, InnerStride<>(i)).sum(); + PlainObjectType::MapAligned(const_ptr, rows, cols, InnerStride<>(i)).sum(); + + PlainObjectType::Map(ptr, rows, cols, InnerStride<2>()).setZero(); + PlainObjectType::MapAligned(ptr, rows, cols, InnerStride<3>()).setZero(); + PlainObjectType::Map(const_ptr, rows, cols, InnerStride<4>()).sum(); + PlainObjectType::MapAligned(const_ptr, rows, cols, InnerStride<5>()).sum(); + + PlainObjectType::Map(ptr, rows, cols, OuterStride<>(i)).setZero(); + PlainObjectType::MapAligned(ptr, rows, cols, OuterStride<>(i)).setZero(); + PlainObjectType::Map(const_ptr, rows, cols, OuterStride<>(i)).sum(); + PlainObjectType::MapAligned(const_ptr, rows, cols, OuterStride<>(i)).sum(); + + PlainObjectType::Map(ptr, rows, cols, OuterStride<2>()).setZero(); + PlainObjectType::MapAligned(ptr, rows, cols, OuterStride<3>()).setZero(); + PlainObjectType::Map(const_ptr, rows, cols, OuterStride<4>()).sum(); + PlainObjectType::MapAligned(const_ptr, rows, cols, OuterStride<5>()).sum(); + + PlainObjectType::Map(ptr, rows, cols, Stride<Dynamic, Dynamic>(i,j)).setZero(); + PlainObjectType::MapAligned(ptr, rows, cols, Stride<2,Dynamic>(2,i)).setZero(); + PlainObjectType::Map(const_ptr, rows, cols, Stride<Dynamic,3>(i,3)).sum(); + PlainObjectType::MapAligned(const_ptr, rows, cols, Stride<Dynamic, Dynamic>(i,j)).sum(); + + PlainObjectType::Map(ptr, rows, cols, Stride<2,3>()).setZero(); + PlainObjectType::MapAligned(ptr, rows, cols, Stride<3,4>()).setZero(); + PlainObjectType::Map(const_ptr, rows, cols, Stride<2,4>()).sum(); + PlainObjectType::MapAligned(const_ptr, rows, cols, Stride<5,3>()).sum(); + } +}; + +template<typename PlainObjectType> +struct mapstaticmethods_impl<PlainObjectType, true, true> +{ + static void run(const PlainObjectType& v) + { + int size = v.size(); + + int i = internal::random<int>(2,5); + + PlainObjectType::Map(ptr, size).setZero(); + PlainObjectType::MapAligned(ptr, size).setZero(); + PlainObjectType::Map(const_ptr, size).sum(); + PlainObjectType::MapAligned(const_ptr, size).sum(); + + PlainObjectType::Map(ptr, size, InnerStride<>(i)).setZero(); + PlainObjectType::MapAligned(ptr, size, InnerStride<>(i)).setZero(); + PlainObjectType::Map(const_ptr, size, InnerStride<>(i)).sum(); + PlainObjectType::MapAligned(const_ptr, size, InnerStride<>(i)).sum(); + + PlainObjectType::Map(ptr, size, InnerStride<2>()).setZero(); + PlainObjectType::MapAligned(ptr, size, InnerStride<3>()).setZero(); + PlainObjectType::Map(const_ptr, size, InnerStride<4>()).sum(); + PlainObjectType::MapAligned(const_ptr, size, InnerStride<5>()).sum(); + } +}; + +template<typename PlainObjectType> +void mapstaticmethods(const PlainObjectType& m) +{ + mapstaticmethods_impl<PlainObjectType>::run(m); + VERIFY(true); // just to avoid 'unused function' warning +} + +void test_mapstaticmethods() +{ + ptr = internal::aligned_new<float>(1000); + for(int i = 0; i < 1000; i++) ptr[i] = float(i); + + const_ptr = ptr; + + CALL_SUBTEST_1(( mapstaticmethods(Matrix<float, 1, 1>()) )); + CALL_SUBTEST_1(( mapstaticmethods(Vector2f()) )); + CALL_SUBTEST_2(( mapstaticmethods(Vector3f()) )); + CALL_SUBTEST_2(( mapstaticmethods(Matrix2f()) )); + CALL_SUBTEST_3(( mapstaticmethods(Matrix4f()) )); + CALL_SUBTEST_3(( mapstaticmethods(Array4f()) )); + CALL_SUBTEST_4(( mapstaticmethods(Array3f()) )); + CALL_SUBTEST_4(( mapstaticmethods(Array33f()) )); + CALL_SUBTEST_5(( mapstaticmethods(Array44f()) )); + CALL_SUBTEST_5(( mapstaticmethods(VectorXf(1)) )); + CALL_SUBTEST_5(( mapstaticmethods(VectorXf(8)) )); + CALL_SUBTEST_6(( mapstaticmethods(MatrixXf(1,1)) )); + CALL_SUBTEST_6(( mapstaticmethods(MatrixXf(5,7)) )); + CALL_SUBTEST_7(( mapstaticmethods(ArrayXf(1)) )); + CALL_SUBTEST_7(( mapstaticmethods(ArrayXf(5)) )); + CALL_SUBTEST_8(( mapstaticmethods(ArrayXXf(1,1)) )); + CALL_SUBTEST_8(( mapstaticmethods(ArrayXXf(8,6)) )); + + internal::aligned_delete(ptr, 1000); +} + diff --git a/test/mapstride.cpp b/test/mapstride.cpp new file mode 100644 index 000000000..fe35b9d23 --- /dev/null +++ b/test/mapstride.cpp @@ -0,0 +1,146 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2010 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<int Alignment,typename VectorType> void map_class_vector(const VectorType& m) +{ + typedef typename VectorType::Index Index; + typedef typename VectorType::Scalar Scalar; + + Index size = m.size(); + + VectorType v = VectorType::Random(size); + + Index arraysize = 3*size; + + Scalar* a_array = internal::aligned_new<Scalar>(arraysize+1); + Scalar* array = a_array; + if(Alignment!=Aligned) + array = (Scalar*)(ptrdiff_t(a_array) + (internal::packet_traits<Scalar>::AlignedOnScalar?sizeof(Scalar):sizeof(typename NumTraits<Scalar>::Real))); + + { + Map<VectorType, Alignment, InnerStride<3> > map(array, size); + map = v; + for(int i = 0; i < size; ++i) + { + VERIFY(array[3*i] == v[i]); + VERIFY(map[i] == v[i]); + } + } + + { + Map<VectorType, Unaligned, InnerStride<Dynamic> > map(array, size, InnerStride<Dynamic>(2)); + map = v; + for(int i = 0; i < size; ++i) + { + VERIFY(array[2*i] == v[i]); + VERIFY(map[i] == v[i]); + } + } + + internal::aligned_delete(a_array, arraysize+1); +} + +template<int Alignment,typename MatrixType> void map_class_matrix(const MatrixType& _m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + + Index rows = _m.rows(), cols = _m.cols(); + + MatrixType m = MatrixType::Random(rows,cols); + + Index arraysize = 2*(rows+4)*(cols+4); + + Scalar* a_array = internal::aligned_new<Scalar>(arraysize+1); + Scalar* array = a_array; + if(Alignment!=Aligned) + array = (Scalar*)(ptrdiff_t(a_array) + (internal::packet_traits<Scalar>::AlignedOnScalar?sizeof(Scalar):sizeof(typename NumTraits<Scalar>::Real))); + + // test no inner stride and some dynamic outer stride + { + Map<MatrixType, Alignment, OuterStride<Dynamic> > map(array, rows, cols, OuterStride<Dynamic>(m.innerSize()+1)); + map = m; + VERIFY(map.outerStride() == map.innerSize()+1); + for(int i = 0; i < m.outerSize(); ++i) + for(int j = 0; j < m.innerSize(); ++j) + { + VERIFY(array[map.outerStride()*i+j] == m.coeffByOuterInner(i,j)); + VERIFY(map.coeffByOuterInner(i,j) == m.coeffByOuterInner(i,j)); + } + } + + // test no inner stride and an outer stride of +4. This is quite important as for fixed-size matrices, + // this allows to hit the special case where it's vectorizable. + { + enum { + InnerSize = MatrixType::InnerSizeAtCompileTime, + OuterStrideAtCompileTime = InnerSize==Dynamic ? Dynamic : InnerSize+4 + }; + Map<MatrixType, Alignment, OuterStride<OuterStrideAtCompileTime> > + map(array, rows, cols, OuterStride<OuterStrideAtCompileTime>(m.innerSize()+4)); + map = m; + VERIFY(map.outerStride() == map.innerSize()+4); + for(int i = 0; i < m.outerSize(); ++i) + for(int j = 0; j < m.innerSize(); ++j) + { + VERIFY(array[map.outerStride()*i+j] == m.coeffByOuterInner(i,j)); + VERIFY(map.coeffByOuterInner(i,j) == m.coeffByOuterInner(i,j)); + } + } + + // test both inner stride and outer stride + { + Map<MatrixType, Alignment, Stride<Dynamic,Dynamic> > map(array, rows, cols, Stride<Dynamic,Dynamic>(2*m.innerSize()+1, 2)); + map = m; + VERIFY(map.outerStride() == 2*map.innerSize()+1); + VERIFY(map.innerStride() == 2); + for(int i = 0; i < m.outerSize(); ++i) + for(int j = 0; j < m.innerSize(); ++j) + { + VERIFY(array[map.outerStride()*i+map.innerStride()*j] == m.coeffByOuterInner(i,j)); + VERIFY(map.coeffByOuterInner(i,j) == m.coeffByOuterInner(i,j)); + } + } + + internal::aligned_delete(a_array, arraysize+1); +} + +void test_mapstride() +{ + for(int i = 0; i < g_repeat; i++) { + EIGEN_UNUSED int maxn = 30; + CALL_SUBTEST_1( map_class_vector<Aligned>(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_1( map_class_vector<Unaligned>(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( map_class_vector<Aligned>(Vector4d()) ); + CALL_SUBTEST_2( map_class_vector<Unaligned>(Vector4d()) ); + CALL_SUBTEST_3( map_class_vector<Aligned>(RowVector4f()) ); + CALL_SUBTEST_3( map_class_vector<Unaligned>(RowVector4f()) ); + CALL_SUBTEST_4( map_class_vector<Aligned>(VectorXcf(internal::random<int>(1,maxn))) ); + CALL_SUBTEST_4( map_class_vector<Unaligned>(VectorXcf(internal::random<int>(1,maxn))) ); + CALL_SUBTEST_5( map_class_vector<Aligned>(VectorXi(internal::random<int>(1,maxn))) ); + CALL_SUBTEST_5( map_class_vector<Unaligned>(VectorXi(internal::random<int>(1,maxn))) ); + + CALL_SUBTEST_1( map_class_matrix<Aligned>(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_1( map_class_matrix<Unaligned>(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( map_class_matrix<Aligned>(Matrix4d()) ); + CALL_SUBTEST_2( map_class_matrix<Unaligned>(Matrix4d()) ); + CALL_SUBTEST_3( map_class_matrix<Aligned>(Matrix<float,3,5>()) ); + CALL_SUBTEST_3( map_class_matrix<Unaligned>(Matrix<float,3,5>()) ); + CALL_SUBTEST_3( map_class_matrix<Aligned>(Matrix<float,4,8>()) ); + CALL_SUBTEST_3( map_class_matrix<Unaligned>(Matrix<float,4,8>()) ); + CALL_SUBTEST_4( map_class_matrix<Aligned>(MatrixXcf(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) ); + CALL_SUBTEST_4( map_class_matrix<Unaligned>(MatrixXcf(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) ); + CALL_SUBTEST_5( map_class_matrix<Aligned>(MatrixXi(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) ); + CALL_SUBTEST_5( map_class_matrix<Unaligned>(MatrixXi(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) ); + CALL_SUBTEST_6( map_class_matrix<Aligned>(MatrixXcd(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) ); + CALL_SUBTEST_6( map_class_matrix<Unaligned>(MatrixXcd(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) ); + } +} diff --git a/test/meta.cpp b/test/meta.cpp new file mode 100644 index 000000000..dc1d128d5 --- /dev/null +++ b/test/meta.cpp @@ -0,0 +1,76 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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" + +void test_meta() +{ + typedef float & FloatRef; + typedef const float & ConstFloatRef; + + VERIFY((internal::conditional<(3<4),internal::true_type, internal::false_type>::type::value)); + VERIFY(( internal::is_same<float,float>::value)); + VERIFY((!internal::is_same<float,double>::value)); + VERIFY((!internal::is_same<float,float&>::value)); + VERIFY((!internal::is_same<float,const float&>::value)); + + VERIFY(( internal::is_same<float,internal::remove_all<const float&>::type >::value)); + VERIFY(( internal::is_same<float,internal::remove_all<const float*>::type >::value)); + VERIFY(( internal::is_same<float,internal::remove_all<const float*&>::type >::value)); + VERIFY(( internal::is_same<float,internal::remove_all<float**>::type >::value)); + VERIFY(( internal::is_same<float,internal::remove_all<float**&>::type >::value)); + VERIFY(( internal::is_same<float,internal::remove_all<float* const *&>::type >::value)); + VERIFY(( internal::is_same<float,internal::remove_all<float* const>::type >::value)); + + // test add_const + VERIFY(( internal::is_same< internal::add_const<float>::type, const float >::value)); + VERIFY(( internal::is_same< internal::add_const<float*>::type, float* const>::value)); + VERIFY(( internal::is_same< internal::add_const<float const*>::type, float const* const>::value)); + VERIFY(( internal::is_same< internal::add_const<float&>::type, float& >::value)); + + // test remove_const + VERIFY(( internal::is_same< internal::remove_const<float const* const>::type, float const* >::value)); + VERIFY(( internal::is_same< internal::remove_const<float const*>::type, float const* >::value)); + VERIFY(( internal::is_same< internal::remove_const<float* const>::type, float* >::value)); + + // test add_const_on_value_type + VERIFY(( internal::is_same< internal::add_const_on_value_type<float&>::type, float const& >::value)); + VERIFY(( internal::is_same< internal::add_const_on_value_type<float*>::type, float const* >::value)); + + VERIFY(( internal::is_same< internal::add_const_on_value_type<float>::type, const float >::value)); + VERIFY(( internal::is_same< internal::add_const_on_value_type<const float>::type, const float >::value)); + + VERIFY(( internal::is_same< internal::add_const_on_value_type<const float* const>::type, const float* const>::value)); + VERIFY(( internal::is_same< internal::add_const_on_value_type<float* const>::type, const float* const>::value)); + + VERIFY(( internal::is_same<float,internal::remove_reference<float&>::type >::value)); + VERIFY(( internal::is_same<const float,internal::remove_reference<const float&>::type >::value)); + VERIFY(( internal::is_same<float,internal::remove_pointer<float*>::type >::value)); + VERIFY(( internal::is_same<const float,internal::remove_pointer<const float*>::type >::value)); + VERIFY(( internal::is_same<float,internal::remove_pointer<float* const >::type >::value)); + + VERIFY(internal::meta_sqrt<1>::ret == 1); + #define VERIFY_META_SQRT(X) VERIFY(internal::meta_sqrt<X>::ret == int(internal::sqrt(double(X)))) + VERIFY_META_SQRT(2); + VERIFY_META_SQRT(3); + VERIFY_META_SQRT(4); + VERIFY_META_SQRT(5); + VERIFY_META_SQRT(6); + VERIFY_META_SQRT(8); + VERIFY_META_SQRT(9); + VERIFY_META_SQRT(15); + VERIFY_META_SQRT(16); + VERIFY_META_SQRT(17); + VERIFY_META_SQRT(255); + VERIFY_META_SQRT(256); + VERIFY_META_SQRT(257); + VERIFY_META_SQRT(1023); + VERIFY_META_SQRT(1024); + VERIFY_META_SQRT(1025); +} diff --git a/test/miscmatrices.cpp b/test/miscmatrices.cpp new file mode 100644 index 000000000..af0481cfe --- /dev/null +++ b/test/miscmatrices.cpp @@ -0,0 +1,48 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename MatrixType> void miscMatrices(const MatrixType& m) +{ + /* this test covers the following files: + DiagonalMatrix.h Ones.h + */ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<Scalar, 1, MatrixType::ColsAtCompileTime> RowVectorType; + Index rows = m.rows(); + Index cols = m.cols(); + + Index r = internal::random<Index>(0, rows-1), r2 = internal::random<Index>(0, rows-1), c = internal::random<Index>(0, cols-1); + VERIFY_IS_APPROX(MatrixType::Ones(rows,cols)(r,c), static_cast<Scalar>(1)); + MatrixType m1 = MatrixType::Ones(rows,cols); + VERIFY_IS_APPROX(m1(r,c), static_cast<Scalar>(1)); + VectorType v1 = VectorType::Random(rows); + v1[0]; + Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> + square(v1.asDiagonal()); + if(r==r2) VERIFY_IS_APPROX(square(r,r2), v1[r]); + else VERIFY_IS_MUCH_SMALLER_THAN(square(r,r2), static_cast<Scalar>(1)); + square = MatrixType::Zero(rows, rows); + square.diagonal() = VectorType::Ones(rows); + VERIFY_IS_APPROX(square, MatrixType::Identity(rows, rows)); +} + +void test_miscmatrices() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( miscMatrices(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( miscMatrices(Matrix4d()) ); + CALL_SUBTEST_3( miscMatrices(MatrixXcf(3, 3)) ); + CALL_SUBTEST_4( miscMatrices(MatrixXi(8, 12)) ); + CALL_SUBTEST_5( miscMatrices(MatrixXcd(20, 20)) ); + } +} diff --git a/test/mixingtypes.cpp b/test/mixingtypes.cpp new file mode 100644 index 000000000..6c2f74875 --- /dev/null +++ b/test/mixingtypes.cpp @@ -0,0 +1,132 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +// work around "uninitialized" warnings and give that option some testing +#define EIGEN_INITIALIZE_MATRICES_BY_ZERO + +#ifndef EIGEN_NO_STATIC_ASSERT +#define EIGEN_NO_STATIC_ASSERT // turn static asserts into runtime asserts in order to check them +#endif + +// #ifndef EIGEN_DONT_VECTORIZE +// #define EIGEN_DONT_VECTORIZE // SSE intrinsics aren't designed to allow mixing types +// #endif + +#include "main.h" + +using namespace std; + +template<int SizeAtCompileType> void mixingtypes(int size = SizeAtCompileType) +{ + typedef std::complex<float> CF; + typedef std::complex<double> CD; + typedef Matrix<float, SizeAtCompileType, SizeAtCompileType> Mat_f; + typedef Matrix<double, SizeAtCompileType, SizeAtCompileType> Mat_d; + typedef Matrix<std::complex<float>, SizeAtCompileType, SizeAtCompileType> Mat_cf; + typedef Matrix<std::complex<double>, SizeAtCompileType, SizeAtCompileType> Mat_cd; + typedef Matrix<float, SizeAtCompileType, 1> Vec_f; + typedef Matrix<double, SizeAtCompileType, 1> Vec_d; + typedef Matrix<std::complex<float>, SizeAtCompileType, 1> Vec_cf; + typedef Matrix<std::complex<double>, SizeAtCompileType, 1> Vec_cd; + + Mat_f mf = Mat_f::Random(size,size); + Mat_d md = mf.template cast<double>(); + Mat_cf mcf = Mat_cf::Random(size,size); + Mat_cd mcd = mcf.template cast<complex<double> >(); + Vec_f vf = Vec_f::Random(size,1); + Vec_d vd = vf.template cast<double>(); + Vec_cf vcf = Vec_cf::Random(size,1); + Vec_cd vcd = vcf.template cast<complex<double> >(); + float sf = internal::random<float>(); + double sd = internal::random<double>(); + complex<float> scf = internal::random<complex<float> >(); + complex<double> scd = internal::random<complex<double> >(); + + + mf+mf; + VERIFY_RAISES_ASSERT(mf+md); + VERIFY_RAISES_ASSERT(mf+mcf); + VERIFY_RAISES_ASSERT(vf=vd); + VERIFY_RAISES_ASSERT(vf+=vd); + VERIFY_RAISES_ASSERT(mcd=md); + + // check scalar products + VERIFY_IS_APPROX(vcf * sf , vcf * complex<float>(sf)); + VERIFY_IS_APPROX(sd * vcd, complex<double>(sd) * vcd); + VERIFY_IS_APPROX(vf * scf , vf.template cast<complex<float> >() * scf); + VERIFY_IS_APPROX(scd * vd, scd * vd.template cast<complex<double> >()); + + // check dot product + vf.dot(vf); +#if 0 // we get other compilation errors here than just static asserts + VERIFY_RAISES_ASSERT(vd.dot(vf)); +#endif + VERIFY_IS_APPROX(vcf.dot(vf), vcf.dot(vf.template cast<complex<float> >())); + + // check diagonal product + VERIFY_IS_APPROX(vf.asDiagonal() * mcf, vf.template cast<complex<float> >().asDiagonal() * mcf); + VERIFY_IS_APPROX(vcd.asDiagonal() * md, vcd.asDiagonal() * md.template cast<complex<double> >()); + VERIFY_IS_APPROX(mcf * vf.asDiagonal(), mcf * vf.template cast<complex<float> >().asDiagonal()); + VERIFY_IS_APPROX(md * vcd.asDiagonal(), md.template cast<complex<double> >() * vcd.asDiagonal()); +// vd.asDiagonal() * mf; // does not even compile +// vcd.asDiagonal() * mf; // does not even compile + + // check inner product + VERIFY_IS_APPROX((vf.transpose() * vcf).value(), (vf.template cast<complex<float> >().transpose() * vcf).value()); + + // check outer product + VERIFY_IS_APPROX((vf * vcf.transpose()).eval(), (vf.template cast<complex<float> >() * vcf.transpose()).eval()); + + // coeff wise product + + VERIFY_IS_APPROX((vf * vcf.transpose()).eval(), (vf.template cast<complex<float> >() * vcf.transpose()).eval()); + + Mat_cd mcd2 = mcd; + VERIFY_IS_APPROX(mcd.array() *= md.array(), mcd2.array() *= md.array().template cast<std::complex<double> >()); + + // check matrix-matrix products + + VERIFY_IS_APPROX(sd*md*mcd, (sd*md).template cast<CD>().eval()*mcd); + VERIFY_IS_APPROX(sd*mcd*md, sd*mcd*md.template cast<CD>()); + VERIFY_IS_APPROX(scd*md*mcd, scd*md.template cast<CD>().eval()*mcd); + VERIFY_IS_APPROX(scd*mcd*md, scd*mcd*md.template cast<CD>()); + + VERIFY_IS_APPROX(sf*mf*mcf, sf*mf.template cast<CF>()*mcf); + VERIFY_IS_APPROX(sf*mcf*mf, sf*mcf*mf.template cast<CF>()); + VERIFY_IS_APPROX(scf*mf*mcf, scf*mf.template cast<CF>()*mcf); + VERIFY_IS_APPROX(scf*mcf*mf, scf*mcf*mf.template cast<CF>()); + + VERIFY_IS_APPROX(sf*mf*vcf, (sf*mf).template cast<CF>().eval()*vcf); + VERIFY_IS_APPROX(scf*mf*vcf,(scf*mf.template cast<CF>()).eval()*vcf); + VERIFY_IS_APPROX(sf*mcf*vf, sf*mcf*vf.template cast<CF>()); + VERIFY_IS_APPROX(scf*mcf*vf,scf*mcf*vf.template cast<CF>()); + + VERIFY_IS_APPROX(sf*vcf.adjoint()*mf, sf*vcf.adjoint()*mf.template cast<CF>().eval()); + VERIFY_IS_APPROX(scf*vcf.adjoint()*mf, scf*vcf.adjoint()*mf.template cast<CF>().eval()); + VERIFY_IS_APPROX(sf*vf.adjoint()*mcf, sf*vf.adjoint().template cast<CF>().eval()*mcf); + VERIFY_IS_APPROX(scf*vf.adjoint()*mcf, scf*vf.adjoint().template cast<CF>().eval()*mcf); + + VERIFY_IS_APPROX(sd*md*vcd, (sd*md).template cast<CD>().eval()*vcd); + VERIFY_IS_APPROX(scd*md*vcd,(scd*md.template cast<CD>()).eval()*vcd); + VERIFY_IS_APPROX(sd*mcd*vd, sd*mcd*vd.template cast<CD>().eval()); + VERIFY_IS_APPROX(scd*mcd*vd,scd*mcd*vd.template cast<CD>().eval()); + + VERIFY_IS_APPROX(sd*vcd.adjoint()*md, sd*vcd.adjoint()*md.template cast<CD>().eval()); + VERIFY_IS_APPROX(scd*vcd.adjoint()*md, scd*vcd.adjoint()*md.template cast<CD>().eval()); + VERIFY_IS_APPROX(sd*vd.adjoint()*mcd, sd*vd.adjoint().template cast<CD>().eval()*mcd); + VERIFY_IS_APPROX(scd*vd.adjoint()*mcd, scd*vd.adjoint().template cast<CD>().eval()*mcd); +} + +void test_mixingtypes() +{ + CALL_SUBTEST_1(mixingtypes<3>()); + CALL_SUBTEST_2(mixingtypes<4>()); + CALL_SUBTEST_3(mixingtypes<Dynamic>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE))); +} diff --git a/test/nesting_ops.cpp b/test/nesting_ops.cpp new file mode 100644 index 000000000..938ebcb7a --- /dev/null +++ b/test/nesting_ops.cpp @@ -0,0 +1,41 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2010 Hauke Heibel <hauke.heibel@gmail.com> +// +// 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 <typename MatrixType> void run_nesting_ops(const MatrixType& _m) +{ + typename MatrixType::Nested m(_m); + typedef typename MatrixType::Scalar Scalar; + +#ifdef NDEBUG + const bool is_debug = false; +#else + const bool is_debug = true; +#endif + + // Make really sure that we are in debug mode! We don't want any type of + // inlining for these tests to pass. + VERIFY(is_debug); + + // The only intention of these tests is to ensure that this code does + // not trigger any asserts or segmentation faults... more to come. + VERIFY_IS_APPROX( (m.transpose() * m).diagonal().sum(), (m.transpose() * m).diagonal().sum() ); + VERIFY_IS_APPROX( (m.transpose() * m).diagonal().array().abs().sum(), (m.transpose() * m).diagonal().array().abs().sum() ); + + VERIFY_IS_APPROX( (m.transpose() * m).array().abs().sum(), (m.transpose() * m).array().abs().sum() ); +} + +void test_nesting_ops() +{ + CALL_SUBTEST_1(run_nesting_ops(MatrixXf::Random(25,25))); + CALL_SUBTEST_2(run_nesting_ops(MatrixXd::Random(25,25))); + CALL_SUBTEST_3(run_nesting_ops(Matrix4f::Random())); + CALL_SUBTEST_4(run_nesting_ops(Matrix4d::Random())); +} diff --git a/test/nomalloc.cpp b/test/nomalloc.cpp new file mode 100644 index 000000000..d4ffcefcb --- /dev/null +++ b/test/nomalloc.cpp @@ -0,0 +1,174 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +// this hack is needed to make this file compiles with -pedantic (gcc) +#ifdef __GNUC__ +#define throw(X) +#endif +// discard stack allocation as that too bypasses malloc +#define EIGEN_STACK_ALLOCATION_LIMIT 0 +// any heap allocation will raise an assert +#define EIGEN_NO_MALLOC + +#include "main.h" +#include <Eigen/Cholesky> +#include <Eigen/Eigenvalues> +#include <Eigen/LU> +#include <Eigen/QR> +#include <Eigen/SVD> + +template<typename MatrixType> void nomalloc(const MatrixType& m) +{ + /* this test check no dynamic memory allocation are issued with fixed-size matrices + */ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols); + + Scalar s1 = internal::random<Scalar>(); + + Index r = internal::random<Index>(0, rows-1), + c = internal::random<Index>(0, cols-1); + + VERIFY_IS_APPROX((m1+m2)*s1, s1*m1+s1*m2); + VERIFY_IS_APPROX((m1+m2)(r,c), (m1(r,c))+(m2(r,c))); + VERIFY_IS_APPROX(m1.cwiseProduct(m1.block(0,0,rows,cols)), (m1.array()*m1.array()).matrix()); + VERIFY_IS_APPROX((m1*m1.transpose())*m2, m1*(m1.transpose()*m2)); + + m2.col(0).noalias() = m1 * m1.col(0); + m2.col(0).noalias() -= m1.adjoint() * m1.col(0); + m2.col(0).noalias() -= m1 * m1.row(0).adjoint(); + m2.col(0).noalias() -= m1.adjoint() * m1.row(0).adjoint(); + + m2.row(0).noalias() = m1.row(0) * m1; + m2.row(0).noalias() -= m1.row(0) * m1.adjoint(); + m2.row(0).noalias() -= m1.col(0).adjoint() * m1; + m2.row(0).noalias() -= m1.col(0).adjoint() * m1.adjoint(); + VERIFY_IS_APPROX(m2,m2); + + m2.col(0).noalias() = m1.template triangularView<Upper>() * m1.col(0); + m2.col(0).noalias() -= m1.adjoint().template triangularView<Upper>() * m1.col(0); + m2.col(0).noalias() -= m1.template triangularView<Upper>() * m1.row(0).adjoint(); + m2.col(0).noalias() -= m1.adjoint().template triangularView<Upper>() * m1.row(0).adjoint(); + + m2.row(0).noalias() = m1.row(0) * m1.template triangularView<Upper>(); + m2.row(0).noalias() -= m1.row(0) * m1.adjoint().template triangularView<Upper>(); + m2.row(0).noalias() -= m1.col(0).adjoint() * m1.template triangularView<Upper>(); + m2.row(0).noalias() -= m1.col(0).adjoint() * m1.adjoint().template triangularView<Upper>(); + VERIFY_IS_APPROX(m2,m2); + + m2.col(0).noalias() = m1.template selfadjointView<Upper>() * m1.col(0); + m2.col(0).noalias() -= m1.adjoint().template selfadjointView<Upper>() * m1.col(0); + m2.col(0).noalias() -= m1.template selfadjointView<Upper>() * m1.row(0).adjoint(); + m2.col(0).noalias() -= m1.adjoint().template selfadjointView<Upper>() * m1.row(0).adjoint(); + + m2.row(0).noalias() = m1.row(0) * m1.template selfadjointView<Upper>(); + m2.row(0).noalias() -= m1.row(0) * m1.adjoint().template selfadjointView<Upper>(); + m2.row(0).noalias() -= m1.col(0).adjoint() * m1.template selfadjointView<Upper>(); + m2.row(0).noalias() -= m1.col(0).adjoint() * m1.adjoint().template selfadjointView<Upper>(); + VERIFY_IS_APPROX(m2,m2); + + m2.template selfadjointView<Lower>().rankUpdate(m1.col(0),-1); + m2.template selfadjointView<Lower>().rankUpdate(m1.row(0),-1); + + // The following fancy matrix-matrix products are not safe yet regarding static allocation +// m1 += m1.template triangularView<Upper>() * m2.col(; +// m1.template selfadjointView<Lower>().rankUpdate(m2); +// m1 += m1.template triangularView<Upper>() * m2; +// m1 += m1.template selfadjointView<Lower>() * m2; +// VERIFY_IS_APPROX(m1,m1); +} + +template<typename Scalar> +void ctms_decompositions() +{ + const int maxSize = 16; + const int size = 12; + + typedef Eigen::Matrix<Scalar, + Eigen::Dynamic, Eigen::Dynamic, + 0, + maxSize, maxSize> Matrix; + + typedef Eigen::Matrix<Scalar, + Eigen::Dynamic, 1, + 0, + maxSize, 1> Vector; + + typedef Eigen::Matrix<std::complex<Scalar>, + Eigen::Dynamic, Eigen::Dynamic, + 0, + maxSize, maxSize> ComplexMatrix; + + const Matrix A(Matrix::Random(size, size)), B(Matrix::Random(size, size)); + Matrix X(size,size); + const ComplexMatrix complexA(ComplexMatrix::Random(size, size)); + const Matrix saA = A.adjoint() * A; + const Vector b(Vector::Random(size)); + Vector x(size); + + // Cholesky module + Eigen::LLT<Matrix> LLT; LLT.compute(A); + X = LLT.solve(B); + x = LLT.solve(b); + Eigen::LDLT<Matrix> LDLT; LDLT.compute(A); + X = LDLT.solve(B); + x = LDLT.solve(b); + + // Eigenvalues module + Eigen::HessenbergDecomposition<ComplexMatrix> hessDecomp; hessDecomp.compute(complexA); + Eigen::ComplexSchur<ComplexMatrix> cSchur(size); cSchur.compute(complexA); + Eigen::ComplexEigenSolver<ComplexMatrix> cEigSolver; cEigSolver.compute(complexA); + Eigen::EigenSolver<Matrix> eigSolver; eigSolver.compute(A); + Eigen::SelfAdjointEigenSolver<Matrix> saEigSolver(size); saEigSolver.compute(saA); + Eigen::Tridiagonalization<Matrix> tridiag; tridiag.compute(saA); + + // LU module + Eigen::PartialPivLU<Matrix> ppLU; ppLU.compute(A); + X = ppLU.solve(B); + x = ppLU.solve(b); + Eigen::FullPivLU<Matrix> fpLU; fpLU.compute(A); + X = fpLU.solve(B); + x = fpLU.solve(b); + + // QR module + Eigen::HouseholderQR<Matrix> hQR; hQR.compute(A); + X = hQR.solve(B); + x = hQR.solve(b); + Eigen::ColPivHouseholderQR<Matrix> cpQR; cpQR.compute(A); + X = cpQR.solve(B); + x = cpQR.solve(b); + Eigen::FullPivHouseholderQR<Matrix> fpQR; fpQR.compute(A); + // FIXME X = fpQR.solve(B); + x = fpQR.solve(b); + + // SVD module + Eigen::JacobiSVD<Matrix> jSVD; jSVD.compute(A, ComputeFullU | ComputeFullV); +} + +void test_nomalloc() +{ + // check that our operator new is indeed called: + VERIFY_RAISES_ASSERT(MatrixXd dummy(MatrixXd::Random(3,3))); + CALL_SUBTEST_1(nomalloc(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2(nomalloc(Matrix4d()) ); + CALL_SUBTEST_3(nomalloc(Matrix<float,32,32>()) ); + + // Check decomposition modules with dynamic matrices that have a known compile-time max size (ctms) + CALL_SUBTEST_4(ctms_decompositions<float>()); + +} diff --git a/test/nullary.cpp b/test/nullary.cpp new file mode 100644 index 000000000..1220e3f97 --- /dev/null +++ b/test/nullary.cpp @@ -0,0 +1,123 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2010-2011 Jitse Niesen <jitse@maths.leeds.ac.uk> +// +// 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<typename MatrixType> +bool equalsIdentity(const MatrixType& A) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + Scalar zero = static_cast<Scalar>(0); + + bool offDiagOK = true; + for (Index i = 0; i < A.rows(); ++i) { + for (Index j = i+1; j < A.cols(); ++j) { + offDiagOK = offDiagOK && (A(i,j) == zero); + } + } + for (Index i = 0; i < A.rows(); ++i) { + for (Index j = 0; j < (std::min)(i, A.cols()); ++j) { + offDiagOK = offDiagOK && (A(i,j) == zero); + } + } + + bool diagOK = (A.diagonal().array() == 1).all(); + return offDiagOK && diagOK; +} + +template<typename VectorType> +void testVectorType(const VectorType& base) +{ + typedef typename internal::traits<VectorType>::Index Index; + typedef typename internal::traits<VectorType>::Scalar Scalar; + + const Index size = base.size(); + + Scalar high = internal::random<Scalar>(-500,500); + Scalar low = (size == 1 ? high : internal::random<Scalar>(-500,500)); + if (low>high) std::swap(low,high); + + const Scalar step = ((size == 1) ? 1 : (high-low)/(size-1)); + + // check whether the result yields what we expect it to do + VectorType m(base); + m.setLinSpaced(size,low,high); + + VectorType n(size); + for (int i=0; i<size; ++i) + n(i) = low+i*step; + + VERIFY_IS_APPROX(m,n); + + // random access version + m = VectorType::LinSpaced(size,low,high); + VERIFY_IS_APPROX(m,n); + + // Assignment of a RowVectorXd to a MatrixXd (regression test for bug #79). + VERIFY( (MatrixXd(RowVectorXd::LinSpaced(3, 0, 1)) - RowVector3d(0, 0.5, 1)).norm() < std::numeric_limits<Scalar>::epsilon() ); + + // These guys sometimes fail! This is not good. Any ideas how to fix them!? + //VERIFY( m(m.size()-1) == high ); + //VERIFY( m(0) == low ); + + // sequential access version + m = VectorType::LinSpaced(Sequential,size,low,high); + VERIFY_IS_APPROX(m,n); + + // These guys sometimes fail! This is not good. Any ideas how to fix them!? + //VERIFY( m(m.size()-1) == high ); + //VERIFY( m(0) == low ); + + // check whether everything works with row and col major vectors + Matrix<Scalar,Dynamic,1> row_vector(size); + Matrix<Scalar,1,Dynamic> col_vector(size); + row_vector.setLinSpaced(size,low,high); + col_vector.setLinSpaced(size,low,high); + VERIFY( row_vector.isApprox(col_vector.transpose(), NumTraits<Scalar>::epsilon())); + + Matrix<Scalar,Dynamic,1> size_changer(size+50); + size_changer.setLinSpaced(size,low,high); + VERIFY( size_changer.size() == size ); + + typedef Matrix<Scalar,1,1> ScalarMatrix; + ScalarMatrix scalar; + scalar.setLinSpaced(1,low,high); + VERIFY_IS_APPROX( scalar, ScalarMatrix::Constant(high) ); + VERIFY_IS_APPROX( ScalarMatrix::LinSpaced(1,low,high), ScalarMatrix::Constant(high) ); +} + +template<typename MatrixType> +void testMatrixType(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + const Index rows = m.rows(); + const Index cols = m.cols(); + + MatrixType A; + A.setIdentity(rows, cols); + VERIFY(equalsIdentity(A)); + VERIFY(equalsIdentity(MatrixType::Identity(rows, cols))); +} + +void test_nullary() +{ + CALL_SUBTEST_1( testMatrixType(Matrix2d()) ); + CALL_SUBTEST_2( testMatrixType(MatrixXcf(internal::random<int>(1,300),internal::random<int>(1,300))) ); + CALL_SUBTEST_3( testMatrixType(MatrixXf(internal::random<int>(1,300),internal::random<int>(1,300))) ); + + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_4( testVectorType(VectorXd(internal::random<int>(1,300))) ); + CALL_SUBTEST_5( testVectorType(Vector4d()) ); // regression test for bug 232 + CALL_SUBTEST_6( testVectorType(Vector3d()) ); + CALL_SUBTEST_7( testVectorType(VectorXf(internal::random<int>(1,300))) ); + CALL_SUBTEST_8( testVectorType(Vector3f()) ); + CALL_SUBTEST_8( testVectorType(Matrix<float,1,1>()) ); + } +} diff --git a/test/packetmath.cpp b/test/packetmath.cpp new file mode 100644 index 000000000..c1464e994 --- /dev/null +++ b/test/packetmath.cpp @@ -0,0 +1,345 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" + +// using namespace Eigen; + +namespace Eigen { +namespace internal { +template<typename T> T negate(const T& x) { return -x; } +} +} + +template<typename Scalar> bool isApproxAbs(const Scalar& a, const Scalar& b, const typename NumTraits<Scalar>::Real& refvalue) +{ + return internal::isMuchSmallerThan(a-b, refvalue); +} + +template<typename Scalar> bool areApproxAbs(const Scalar* a, const Scalar* b, int size, const typename NumTraits<Scalar>::Real& refvalue) +{ + for (int i=0; i<size; ++i) + { + if (!isApproxAbs(a[i],b[i],refvalue)) + { + std::cout << "[" << Map<const Matrix<Scalar,1,Dynamic> >(a,size) << "]" << " != " << Map<const Matrix<Scalar,1,Dynamic> >(b,size) << "\n"; + return false; + } + } + return true; +} + +template<typename Scalar> bool areApprox(const Scalar* a, const Scalar* b, int size) +{ + for (int i=0; i<size; ++i) + { + if (!internal::isApprox(a[i],b[i])) + { + std::cout << "[" << Map<const Matrix<Scalar,1,Dynamic> >(a,size) << "]" << " != " << Map<const Matrix<Scalar,1,Dynamic> >(b,size) << "\n"; + return false; + } + } + return true; +} + + +#define CHECK_CWISE2(REFOP, POP) { \ + for (int i=0; i<PacketSize; ++i) \ + ref[i] = REFOP(data1[i], data1[i+PacketSize]); \ + internal::pstore(data2, POP(internal::pload<Packet>(data1), internal::pload<Packet>(data1+PacketSize))); \ + VERIFY(areApprox(ref, data2, PacketSize) && #POP); \ +} + +#define CHECK_CWISE1(REFOP, POP) { \ + for (int i=0; i<PacketSize; ++i) \ + ref[i] = REFOP(data1[i]); \ + internal::pstore(data2, POP(internal::pload<Packet>(data1))); \ + VERIFY(areApprox(ref, data2, PacketSize) && #POP); \ +} + +template<bool Cond,typename Packet> +struct packet_helper +{ + template<typename T> + inline Packet load(const T* from) const { return internal::pload<Packet>(from); } + + template<typename T> + inline void store(T* to, const Packet& x) const { internal::pstore(to,x); } +}; + +template<typename Packet> +struct packet_helper<false,Packet> +{ + template<typename T> + inline T load(const T* from) const { return *from; } + + template<typename T> + inline void store(T* to, const T& x) const { *to = x; } +}; + +#define CHECK_CWISE1_IF(COND, REFOP, POP) if(COND) { \ + packet_helper<COND,Packet> h; \ + for (int i=0; i<PacketSize; ++i) \ + ref[i] = REFOP(data1[i]); \ + h.store(data2, POP(h.load(data1))); \ + VERIFY(areApprox(ref, data2, PacketSize) && #POP); \ +} + +#define REF_ADD(a,b) ((a)+(b)) +#define REF_SUB(a,b) ((a)-(b)) +#define REF_MUL(a,b) ((a)*(b)) +#define REF_DIV(a,b) ((a)/(b)) + +template<typename Scalar> void packetmath() +{ + typedef typename internal::packet_traits<Scalar>::type Packet; + const int PacketSize = internal::packet_traits<Scalar>::size; + typedef typename NumTraits<Scalar>::Real RealScalar; + + const int size = PacketSize*4; + EIGEN_ALIGN16 Scalar data1[internal::packet_traits<Scalar>::size*4]; + EIGEN_ALIGN16 Scalar data2[internal::packet_traits<Scalar>::size*4]; + EIGEN_ALIGN16 Packet packets[PacketSize*2]; + EIGEN_ALIGN16 Scalar ref[internal::packet_traits<Scalar>::size*4]; + RealScalar refvalue = 0; + for (int i=0; i<size; ++i) + { + data1[i] = internal::random<Scalar>()/RealScalar(PacketSize); + data2[i] = internal::random<Scalar>()/RealScalar(PacketSize); + refvalue = (std::max)(refvalue,internal::abs(data1[i])); + } + + internal::pstore(data2, internal::pload<Packet>(data1)); + VERIFY(areApprox(data1, data2, PacketSize) && "aligned load/store"); + + for (int offset=0; offset<PacketSize; ++offset) + { + internal::pstore(data2, internal::ploadu<Packet>(data1+offset)); + VERIFY(areApprox(data1+offset, data2, PacketSize) && "internal::ploadu"); + } + + for (int offset=0; offset<PacketSize; ++offset) + { + internal::pstoreu(data2+offset, internal::pload<Packet>(data1)); + VERIFY(areApprox(data1, data2+offset, PacketSize) && "internal::pstoreu"); + } + + for (int offset=0; offset<PacketSize; ++offset) + { + packets[0] = internal::pload<Packet>(data1); + packets[1] = internal::pload<Packet>(data1+PacketSize); + if (offset==0) internal::palign<0>(packets[0], packets[1]); + else if (offset==1) internal::palign<1>(packets[0], packets[1]); + else if (offset==2) internal::palign<2>(packets[0], packets[1]); + else if (offset==3) internal::palign<3>(packets[0], packets[1]); + internal::pstore(data2, packets[0]); + + for (int i=0; i<PacketSize; ++i) + ref[i] = data1[i+offset]; + + typedef Matrix<Scalar, PacketSize, 1> Vector; + VERIFY(areApprox(ref, data2, PacketSize) && "internal::palign"); + } + + CHECK_CWISE2(REF_ADD, internal::padd); + CHECK_CWISE2(REF_SUB, internal::psub); + CHECK_CWISE2(REF_MUL, internal::pmul); + #ifndef EIGEN_VECTORIZE_ALTIVEC + if (!internal::is_same<Scalar,int>::value) + CHECK_CWISE2(REF_DIV, internal::pdiv); + #endif + CHECK_CWISE1(internal::negate, internal::pnegate); + CHECK_CWISE1(internal::conj, internal::pconj); + + for(int offset=0;offset<3;++offset) + { + for (int i=0; i<PacketSize; ++i) + ref[i] = data1[offset]; + internal::pstore(data2, internal::pset1<Packet>(data1[offset])); + VERIFY(areApprox(ref, data2, PacketSize) && "internal::pset1"); + } + + VERIFY(internal::isApprox(data1[0], internal::pfirst(internal::pload<Packet>(data1))) && "internal::pfirst"); + + if(PacketSize>1) + { + for(int offset=0;offset<4;++offset) + { + for(int i=0;i<PacketSize/2;++i) + ref[2*i+0] = ref[2*i+1] = data1[offset+i]; + internal::pstore(data2,internal::ploaddup<Packet>(data1+offset)); + VERIFY(areApprox(ref, data2, PacketSize) && "ploaddup"); + } + } + + ref[0] = 0; + for (int i=0; i<PacketSize; ++i) + ref[0] += data1[i]; + VERIFY(isApproxAbs(ref[0], internal::predux(internal::pload<Packet>(data1)), refvalue) && "internal::predux"); + + ref[0] = 1; + for (int i=0; i<PacketSize; ++i) + ref[0] *= data1[i]; + VERIFY(internal::isApprox(ref[0], internal::predux_mul(internal::pload<Packet>(data1))) && "internal::predux_mul"); + + for (int j=0; j<PacketSize; ++j) + { + ref[j] = 0; + for (int i=0; i<PacketSize; ++i) + ref[j] += data1[i+j*PacketSize]; + packets[j] = internal::pload<Packet>(data1+j*PacketSize); + } + internal::pstore(data2, internal::preduxp(packets)); + VERIFY(areApproxAbs(ref, data2, PacketSize, refvalue) && "internal::preduxp"); + + for (int i=0; i<PacketSize; ++i) + ref[i] = data1[PacketSize-i-1]; + internal::pstore(data2, internal::preverse(internal::pload<Packet>(data1))); + VERIFY(areApprox(ref, data2, PacketSize) && "internal::preverse"); +} + +template<typename Scalar> void packetmath_real() +{ + typedef typename internal::packet_traits<Scalar>::type Packet; + const int PacketSize = internal::packet_traits<Scalar>::size; + + const int size = PacketSize*4; + EIGEN_ALIGN16 Scalar data1[internal::packet_traits<Scalar>::size*4]; + EIGEN_ALIGN16 Scalar data2[internal::packet_traits<Scalar>::size*4]; + EIGEN_ALIGN16 Scalar ref[internal::packet_traits<Scalar>::size*4]; + + for (int i=0; i<size; ++i) + { + data1[i] = internal::random<Scalar>(-1e3,1e3); + data2[i] = internal::random<Scalar>(-1e3,1e3); + } + CHECK_CWISE1_IF(internal::packet_traits<Scalar>::HasSin, internal::sin, internal::psin); + CHECK_CWISE1_IF(internal::packet_traits<Scalar>::HasCos, internal::cos, internal::pcos); + CHECK_CWISE1_IF(internal::packet_traits<Scalar>::HasTan, internal::tan, internal::ptan); + + for (int i=0; i<size; ++i) + { + data1[i] = internal::random<Scalar>(-1,1); + data2[i] = internal::random<Scalar>(-1,1); + } + CHECK_CWISE1_IF(internal::packet_traits<Scalar>::HasASin, internal::asin, internal::pasin); + CHECK_CWISE1_IF(internal::packet_traits<Scalar>::HasACos, internal::acos, internal::pacos); + + for (int i=0; i<size; ++i) + { + data1[i] = internal::random<Scalar>(-87,88); + data2[i] = internal::random<Scalar>(-87,88); + } + CHECK_CWISE1_IF(internal::packet_traits<Scalar>::HasExp, internal::exp, internal::pexp); + + for (int i=0; i<size; ++i) + { + data1[i] = internal::random<Scalar>(0,1e6); + data2[i] = internal::random<Scalar>(0,1e6); + } + CHECK_CWISE1_IF(internal::packet_traits<Scalar>::HasLog, internal::log, internal::plog); + CHECK_CWISE1_IF(internal::packet_traits<Scalar>::HasSqrt, internal::sqrt, internal::psqrt); + + ref[0] = data1[0]; + for (int i=0; i<PacketSize; ++i) + ref[0] = (std::min)(ref[0],data1[i]); + VERIFY(internal::isApprox(ref[0], internal::predux_min(internal::pload<Packet>(data1))) && "internal::predux_min"); + + CHECK_CWISE2((std::min), internal::pmin); + CHECK_CWISE2((std::max), internal::pmax); + CHECK_CWISE1(internal::abs, internal::pabs); + + ref[0] = data1[0]; + for (int i=0; i<PacketSize; ++i) + ref[0] = (std::max)(ref[0],data1[i]); + VERIFY(internal::isApprox(ref[0], internal::predux_max(internal::pload<Packet>(data1))) && "internal::predux_max"); + + for (int i=0; i<PacketSize; ++i) + ref[i] = data1[0]+Scalar(i); + internal::pstore(data2, internal::plset(data1[0])); + VERIFY(areApprox(ref, data2, PacketSize) && "internal::plset"); +} + +template<typename Scalar,bool ConjLhs,bool ConjRhs> void test_conj_helper(Scalar* data1, Scalar* data2, Scalar* ref, Scalar* pval) +{ + typedef typename internal::packet_traits<Scalar>::type Packet; + const int PacketSize = internal::packet_traits<Scalar>::size; + + internal::conj_if<ConjLhs> cj0; + internal::conj_if<ConjRhs> cj1; + internal::conj_helper<Scalar,Scalar,ConjLhs,ConjRhs> cj; + internal::conj_helper<Packet,Packet,ConjLhs,ConjRhs> pcj; + + for(int i=0;i<PacketSize;++i) + { + ref[i] = cj0(data1[i]) * cj1(data2[i]); + VERIFY(internal::isApprox(ref[i], cj.pmul(data1[i],data2[i])) && "conj_helper pmul"); + } + internal::pstore(pval,pcj.pmul(internal::pload<Packet>(data1),internal::pload<Packet>(data2))); + VERIFY(areApprox(ref, pval, PacketSize) && "conj_helper pmul"); + + for(int i=0;i<PacketSize;++i) + { + Scalar tmp = ref[i]; + ref[i] += cj0(data1[i]) * cj1(data2[i]); + VERIFY(internal::isApprox(ref[i], cj.pmadd(data1[i],data2[i],tmp)) && "conj_helper pmadd"); + } + internal::pstore(pval,pcj.pmadd(internal::pload<Packet>(data1),internal::pload<Packet>(data2),internal::pload<Packet>(pval))); + VERIFY(areApprox(ref, pval, PacketSize) && "conj_helper pmadd"); +} + +template<typename Scalar> void packetmath_complex() +{ + typedef typename internal::packet_traits<Scalar>::type Packet; + const int PacketSize = internal::packet_traits<Scalar>::size; + + const int size = PacketSize*4; + EIGEN_ALIGN16 Scalar data1[PacketSize*4]; + EIGEN_ALIGN16 Scalar data2[PacketSize*4]; + EIGEN_ALIGN16 Scalar ref[PacketSize*4]; + EIGEN_ALIGN16 Scalar pval[PacketSize*4]; + + for (int i=0; i<size; ++i) + { + data1[i] = internal::random<Scalar>() * Scalar(1e2); + data2[i] = internal::random<Scalar>() * Scalar(1e2); + } + + test_conj_helper<Scalar,false,false> (data1,data2,ref,pval); + test_conj_helper<Scalar,false,true> (data1,data2,ref,pval); + test_conj_helper<Scalar,true,false> (data1,data2,ref,pval); + test_conj_helper<Scalar,true,true> (data1,data2,ref,pval); + + { + for(int i=0;i<PacketSize;++i) + ref[i] = Scalar(std::imag(data1[i]),std::real(data1[i])); + internal::pstore(pval,internal::pcplxflip(internal::pload<Packet>(data1))); + VERIFY(areApprox(ref, pval, PacketSize) && "pcplxflip"); + } + + +} + +void test_packetmath() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( packetmath<float>() ); + CALL_SUBTEST_2( packetmath<double>() ); + CALL_SUBTEST_3( packetmath<int>() ); + CALL_SUBTEST_1( packetmath<std::complex<float> >() ); + CALL_SUBTEST_2( packetmath<std::complex<double> >() ); + + CALL_SUBTEST_1( packetmath_real<float>() ); + CALL_SUBTEST_2( packetmath_real<double>() ); + + CALL_SUBTEST_1( packetmath_complex<std::complex<float> >() ); + CALL_SUBTEST_2( packetmath_complex<std::complex<double> >() ); + } +} diff --git a/test/pardiso_support.cpp b/test/pardiso_support.cpp new file mode 100644 index 000000000..67efad6d8 --- /dev/null +++ b/test/pardiso_support.cpp @@ -0,0 +1,29 @@ +/* + Intel Copyright (C) .... +*/ + +#include "sparse_solver.h" +#include <Eigen/PardisoSupport> + +template<typename T> void test_pardiso_T() +{ + PardisoLLT < SparseMatrix<T, RowMajor>, Lower> pardiso_llt_lower; + PardisoLLT < SparseMatrix<T, RowMajor>, Upper> pardiso_llt_upper; + PardisoLDLT < SparseMatrix<T, RowMajor>, Lower> pardiso_ldlt_lower; + PardisoLDLT < SparseMatrix<T, RowMajor>, Upper> pardiso_ldlt_upper; + PardisoLU < SparseMatrix<T, RowMajor> > pardiso_lu; + + check_sparse_spd_solving(pardiso_llt_lower); + check_sparse_spd_solving(pardiso_llt_upper); + check_sparse_spd_solving(pardiso_ldlt_lower); + check_sparse_spd_solving(pardiso_ldlt_upper); + check_sparse_square_solving(pardiso_lu); +} + +void test_pardiso_support() +{ + CALL_SUBTEST_1(test_pardiso_T<float>()); + CALL_SUBTEST_2(test_pardiso_T<double>()); + CALL_SUBTEST_3(test_pardiso_T< std::complex<float> >()); + CALL_SUBTEST_4(test_pardiso_T< std::complex<double> >()); +} diff --git a/test/pastix_support.cpp b/test/pastix_support.cpp new file mode 100644 index 000000000..0e57227f9 --- /dev/null +++ b/test/pastix_support.cpp @@ -0,0 +1,44 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2012 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2012 Désiré Nuentsa-Wakam <desire.nuentsa_wakam@inria.fr> +// +// 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 "sparse_solver.h" +#include <Eigen/PaStiXSupport> +#include <unsupported/Eigen/SparseExtra> + + +template<typename T> void test_pastix_T() +{ + PastixLLT< SparseMatrix<T, ColMajor>, Eigen::Lower > pastix_llt_lower; + PastixLDLT< SparseMatrix<T, ColMajor>, Eigen::Lower > pastix_ldlt_lower; + PastixLLT< SparseMatrix<T, ColMajor>, Eigen::Upper > pastix_llt_upper; + PastixLDLT< SparseMatrix<T, ColMajor>, Eigen::Upper > pastix_ldlt_upper; + PastixLU< SparseMatrix<T, ColMajor> > pastix_lu; + + check_sparse_spd_solving(pastix_llt_lower); + check_sparse_spd_solving(pastix_ldlt_lower); + check_sparse_spd_solving(pastix_llt_upper); + check_sparse_spd_solving(pastix_ldlt_upper); + check_sparse_square_solving(pastix_lu); +} + +// There is no support for selfadjoint matrices with PaStiX. +// Complex symmetric matrices should pass though +template<typename T> void test_pastix_T_LU() +{ + PastixLU< SparseMatrix<T, ColMajor> > pastix_lu; + check_sparse_square_solving(pastix_lu); +} + +void test_pastix_support() +{ + CALL_SUBTEST_1(test_pastix_T<float>()); + CALL_SUBTEST_2(test_pastix_T<double>()); + CALL_SUBTEST_3( (test_pastix_T_LU<std::complex<float> >()) ); + CALL_SUBTEST_4(test_pastix_T_LU<std::complex<double> >()); +}
\ No newline at end of file diff --git a/test/permutationmatrices.cpp b/test/permutationmatrices.cpp new file mode 100644 index 000000000..00f666ccd --- /dev/null +++ b/test/permutationmatrices.cpp @@ -0,0 +1,117 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" + +using namespace std; +template<typename MatrixType> void permutationmatrices(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + enum { Rows = MatrixType::RowsAtCompileTime, Cols = MatrixType::ColsAtCompileTime, + Options = MatrixType::Options }; + typedef PermutationMatrix<Rows> LeftPermutationType; + typedef Matrix<int, Rows, 1> LeftPermutationVectorType; + typedef Map<LeftPermutationType> MapLeftPerm; + typedef PermutationMatrix<Cols> RightPermutationType; + typedef Matrix<int, Cols, 1> RightPermutationVectorType; + typedef Map<RightPermutationType> MapRightPerm; + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m_original = MatrixType::Random(rows,cols); + LeftPermutationVectorType lv; + randomPermutationVector(lv, rows); + LeftPermutationType lp(lv); + RightPermutationVectorType rv; + randomPermutationVector(rv, cols); + RightPermutationType rp(rv); + MatrixType m_permuted = lp * m_original * rp; + + for (int i=0; i<rows; i++) + for (int j=0; j<cols; j++) + VERIFY_IS_APPROX(m_permuted(lv(i),j), m_original(i,rv(j))); + + Matrix<Scalar,Rows,Rows> lm(lp); + Matrix<Scalar,Cols,Cols> rm(rp); + + VERIFY_IS_APPROX(m_permuted, lm*m_original*rm); + + VERIFY_IS_APPROX(lp.inverse()*m_permuted*rp.inverse(), m_original); + VERIFY_IS_APPROX(lv.asPermutation().inverse()*m_permuted*rv.asPermutation().inverse(), m_original); + VERIFY_IS_APPROX(MapLeftPerm(lv.data(),lv.size()).inverse()*m_permuted*MapRightPerm(rv.data(),rv.size()).inverse(), m_original); + + VERIFY((lp*lp.inverse()).toDenseMatrix().isIdentity()); + VERIFY((lv.asPermutation()*lv.asPermutation().inverse()).toDenseMatrix().isIdentity()); + VERIFY((MapLeftPerm(lv.data(),lv.size())*MapLeftPerm(lv.data(),lv.size()).inverse()).toDenseMatrix().isIdentity()); + + LeftPermutationVectorType lv2; + randomPermutationVector(lv2, rows); + LeftPermutationType lp2(lv2); + Matrix<Scalar,Rows,Rows> lm2(lp2); + VERIFY_IS_APPROX((lp*lp2).toDenseMatrix().template cast<Scalar>(), lm*lm2); + VERIFY_IS_APPROX((lv.asPermutation()*lv2.asPermutation()).toDenseMatrix().template cast<Scalar>(), lm*lm2); + VERIFY_IS_APPROX((MapLeftPerm(lv.data(),lv.size())*MapLeftPerm(lv2.data(),lv2.size())).toDenseMatrix().template cast<Scalar>(), lm*lm2); + + LeftPermutationType identityp; + identityp.setIdentity(rows); + VERIFY_IS_APPROX(m_original, identityp*m_original); + + // check inplace permutations + m_permuted = m_original; + m_permuted = lp.inverse() * m_permuted; + VERIFY_IS_APPROX(m_permuted, lp.inverse()*m_original); + + m_permuted = m_original; + m_permuted = m_permuted * rp.inverse(); + VERIFY_IS_APPROX(m_permuted, m_original*rp.inverse()); + + m_permuted = m_original; + m_permuted = lp * m_permuted; + VERIFY_IS_APPROX(m_permuted, lp*m_original); + + m_permuted = m_original; + m_permuted = m_permuted * rp; + VERIFY_IS_APPROX(m_permuted, m_original*rp); + + if(rows>1 && cols>1) + { + lp2 = lp; + Index i = internal::random<Index>(0, rows-1); + Index j; + do j = internal::random<Index>(0, rows-1); while(j==i); + lp2.applyTranspositionOnTheLeft(i, j); + lm = lp; + lm.row(i).swap(lm.row(j)); + VERIFY_IS_APPROX(lm, lp2.toDenseMatrix().template cast<Scalar>()); + + RightPermutationType rp2 = rp; + i = internal::random<Index>(0, cols-1); + do j = internal::random<Index>(0, cols-1); while(j==i); + rp2.applyTranspositionOnTheRight(i, j); + rm = rp; + rm.col(i).swap(rm.col(j)); + VERIFY_IS_APPROX(rm, rp2.toDenseMatrix().template cast<Scalar>()); + } +} + +void test_permutationmatrices() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( permutationmatrices(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( permutationmatrices(Matrix3f()) ); + CALL_SUBTEST_3( permutationmatrices(Matrix<double,3,3,RowMajor>()) ); + CALL_SUBTEST_4( permutationmatrices(Matrix4d()) ); + CALL_SUBTEST_5( permutationmatrices(Matrix<double,40,60>()) ); + CALL_SUBTEST_6( permutationmatrices(Matrix<double,Dynamic,Dynamic,RowMajor>(20, 30)) ); + CALL_SUBTEST_7( permutationmatrices(MatrixXcf(15, 10)) ); + } +} diff --git a/test/prec_inverse_4x4.cpp b/test/prec_inverse_4x4.cpp new file mode 100644 index 000000000..f7d0aff70 --- /dev/null +++ b/test/prec_inverse_4x4.cpp @@ -0,0 +1,68 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/LU> +#include <algorithm> + +template<typename MatrixType> void inverse_permutation_4x4() +{ + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + Vector4i indices(0,1,2,3); + for(int i = 0; i < 24; ++i) + { + MatrixType m = PermutationMatrix<4>(indices); + MatrixType inv = m.inverse(); + double error = double( (m*inv-MatrixType::Identity()).norm() / NumTraits<Scalar>::epsilon() ); + EIGEN_DEBUG_VAR(error) + VERIFY(error == 0.0); + std::next_permutation(indices.data(),indices.data()+4); + } +} + +template<typename MatrixType> void inverse_general_4x4(int repeat) +{ + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + double error_sum = 0., error_max = 0.; + for(int i = 0; i < repeat; ++i) + { + MatrixType m; + RealScalar absdet; + do { + m = MatrixType::Random(); + absdet = internal::abs(m.determinant()); + } while(absdet < NumTraits<Scalar>::epsilon()); + MatrixType inv = m.inverse(); + double error = double( (m*inv-MatrixType::Identity()).norm() * absdet / NumTraits<Scalar>::epsilon() ); + error_sum += error; + error_max = (std::max)(error_max, error); + } + std::cerr << "inverse_general_4x4, Scalar = " << type_name<Scalar>() << std::endl; + double error_avg = error_sum / repeat; + EIGEN_DEBUG_VAR(error_avg); + EIGEN_DEBUG_VAR(error_max); + // FIXME that 1.25 used to be a 1.0 until the NumTraits changes on 28 April 2010, what's going wrong?? + // FIXME that 1.25 used to be 1.2 until we tested gcc 4.1 on 30 June 2010 and got 1.21. + VERIFY(error_avg < (NumTraits<Scalar>::IsComplex ? 8.0 : 1.25)); + VERIFY(error_max < (NumTraits<Scalar>::IsComplex ? 64.0 : 20.0)); +} + +void test_prec_inverse_4x4() +{ + CALL_SUBTEST_1((inverse_permutation_4x4<Matrix4f>())); + CALL_SUBTEST_1(( inverse_general_4x4<Matrix4f>(200000 * g_repeat) )); + + CALL_SUBTEST_2((inverse_permutation_4x4<Matrix<double,4,4,RowMajor> >())); + CALL_SUBTEST_2(( inverse_general_4x4<Matrix<double,4,4,RowMajor> >(200000 * g_repeat) )); + + CALL_SUBTEST_3((inverse_permutation_4x4<Matrix4cf>())); + CALL_SUBTEST_3((inverse_general_4x4<Matrix4cf>(50000 * g_repeat))); +} diff --git a/test/product.h b/test/product.h new file mode 100644 index 000000000..4aa9fd56d --- /dev/null +++ b/test/product.h @@ -0,0 +1,143 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/QR> + +template<typename Derived1, typename Derived2> +bool areNotApprox(const MatrixBase<Derived1>& m1, const MatrixBase<Derived2>& m2, typename Derived1::RealScalar epsilon = NumTraits<typename Derived1::RealScalar>::dummy_precision()) +{ + return !((m1-m2).cwiseAbs2().maxCoeff() < epsilon * epsilon + * (std::max)(m1.cwiseAbs2().maxCoeff(), m2.cwiseAbs2().maxCoeff())); +} + +template<typename MatrixType> void product(const MatrixType& m) +{ + /* this test covers the following files: + Identity.h Product.h + */ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::NonInteger NonInteger; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> RowVectorType; + typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, 1> ColVectorType; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> RowSquareMatrixType; + typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, MatrixType::ColsAtCompileTime> ColSquareMatrixType; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::ColsAtCompileTime, + MatrixType::Flags&RowMajorBit?ColMajor:RowMajor> OtherMajorMatrixType; + + Index rows = m.rows(); + Index cols = m.cols(); + + // this test relies a lot on Random.h, and there's not much more that we can do + // to test it, hence I consider that we will have tested Random.h + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols); + RowSquareMatrixType + identity = RowSquareMatrixType::Identity(rows, rows), + square = RowSquareMatrixType::Random(rows, rows), + res = RowSquareMatrixType::Random(rows, rows); + ColSquareMatrixType + square2 = ColSquareMatrixType::Random(cols, cols), + res2 = ColSquareMatrixType::Random(cols, cols); + RowVectorType v1 = RowVectorType::Random(rows); + ColVectorType vc2 = ColVectorType::Random(cols), vcres(cols); + OtherMajorMatrixType tm1 = m1; + + Scalar s1 = internal::random<Scalar>(); + + Index r = internal::random<Index>(0, rows-1), + c = internal::random<Index>(0, cols-1), + c2 = internal::random<Index>(0, cols-1); + + // begin testing Product.h: only associativity for now + // (we use Transpose.h but this doesn't count as a test for it) + VERIFY_IS_APPROX((m1*m1.transpose())*m2, m1*(m1.transpose()*m2)); + m3 = m1; + m3 *= m1.transpose() * m2; + VERIFY_IS_APPROX(m3, m1 * (m1.transpose()*m2)); + VERIFY_IS_APPROX(m3, m1 * (m1.transpose()*m2)); + + // continue testing Product.h: distributivity + VERIFY_IS_APPROX(square*(m1 + m2), square*m1+square*m2); + VERIFY_IS_APPROX(square*(m1 - m2), square*m1-square*m2); + + // continue testing Product.h: compatibility with ScalarMultiple.h + VERIFY_IS_APPROX(s1*(square*m1), (s1*square)*m1); + VERIFY_IS_APPROX(s1*(square*m1), square*(m1*s1)); + + // test Product.h together with Identity.h + VERIFY_IS_APPROX(v1, identity*v1); + VERIFY_IS_APPROX(v1.transpose(), v1.transpose() * identity); + // again, test operator() to check const-qualification + VERIFY_IS_APPROX(MatrixType::Identity(rows, cols)(r,c), static_cast<Scalar>(r==c)); + + if (rows!=cols) + VERIFY_RAISES_ASSERT(m3 = m1*m1); + + // test the previous tests were not screwed up because operator* returns 0 + // (we use the more accurate default epsilon) + if (!NumTraits<Scalar>::IsInteger && (std::min)(rows,cols)>1) + { + VERIFY(areNotApprox(m1.transpose()*m2,m2.transpose()*m1)); + } + + // test optimized operator+= path + res = square; + res.noalias() += m1 * m2.transpose(); + VERIFY_IS_APPROX(res, square + m1 * m2.transpose()); + if (!NumTraits<Scalar>::IsInteger && (std::min)(rows,cols)>1) + { + VERIFY(areNotApprox(res,square + m2 * m1.transpose())); + } + vcres = vc2; + vcres.noalias() += m1.transpose() * v1; + VERIFY_IS_APPROX(vcres, vc2 + m1.transpose() * v1); + + // test optimized operator-= path + res = square; + res.noalias() -= m1 * m2.transpose(); + VERIFY_IS_APPROX(res, square - (m1 * m2.transpose())); + if (!NumTraits<Scalar>::IsInteger && (std::min)(rows,cols)>1) + { + VERIFY(areNotApprox(res,square - m2 * m1.transpose())); + } + vcres = vc2; + vcres.noalias() -= m1.transpose() * v1; + VERIFY_IS_APPROX(vcres, vc2 - m1.transpose() * v1); + + tm1 = m1; + VERIFY_IS_APPROX(tm1.transpose() * v1, m1.transpose() * v1); + VERIFY_IS_APPROX(v1.transpose() * tm1, v1.transpose() * m1); + + // test submatrix and matrix/vector product + for (int i=0; i<rows; ++i) + res.row(i) = m1.row(i) * m2.transpose(); + VERIFY_IS_APPROX(res, m1 * m2.transpose()); + // the other way round: + for (int i=0; i<rows; ++i) + res.col(i) = m1 * m2.transpose().col(i); + VERIFY_IS_APPROX(res, m1 * m2.transpose()); + + res2 = square2; + res2.noalias() += m1.transpose() * m2; + VERIFY_IS_APPROX(res2, square2 + m1.transpose() * m2); + if (!NumTraits<Scalar>::IsInteger && (std::min)(rows,cols)>1) + { + VERIFY(areNotApprox(res2,square2 + m2.transpose() * m1)); + } + + VERIFY_IS_APPROX(res.col(r).noalias() = square.adjoint() * square.col(r), (square.adjoint() * square.col(r)).eval()); + VERIFY_IS_APPROX(res.col(r).noalias() = square * square.col(r), (square * square.col(r)).eval()); + + // inner product + Scalar x = square2.row(c) * square2.col(c2); + VERIFY_IS_APPROX(x, square2.row(c).transpose().cwiseProduct(square2.col(c2)).sum()); +} diff --git a/test/product_extra.cpp b/test/product_extra.cpp new file mode 100644 index 000000000..9a6bf0792 --- /dev/null +++ b/test/product_extra.cpp @@ -0,0 +1,148 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename MatrixType> void product_extra(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::NonInteger NonInteger; + typedef Matrix<Scalar, 1, Dynamic> RowVectorType; + typedef Matrix<Scalar, Dynamic, 1> ColVectorType; + typedef Matrix<Scalar, Dynamic, Dynamic, + MatrixType::Flags&RowMajorBit> OtherMajorMatrixType; + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols), + mzero = MatrixType::Zero(rows, cols), + identity = MatrixType::Identity(rows, rows), + square = MatrixType::Random(rows, rows), + res = MatrixType::Random(rows, rows), + square2 = MatrixType::Random(cols, cols), + res2 = MatrixType::Random(cols, cols); + RowVectorType v1 = RowVectorType::Random(rows), vrres(rows); + ColVectorType vc2 = ColVectorType::Random(cols), vcres(cols); + OtherMajorMatrixType tm1 = m1; + + Scalar s1 = internal::random<Scalar>(), + s2 = internal::random<Scalar>(), + s3 = internal::random<Scalar>(); + + VERIFY_IS_APPROX(m3.noalias() = m1 * m2.adjoint(), m1 * m2.adjoint().eval()); + VERIFY_IS_APPROX(m3.noalias() = m1.adjoint() * square.adjoint(), m1.adjoint().eval() * square.adjoint().eval()); + VERIFY_IS_APPROX(m3.noalias() = m1.adjoint() * m2, m1.adjoint().eval() * m2); + VERIFY_IS_APPROX(m3.noalias() = (s1 * m1.adjoint()) * m2, (s1 * m1.adjoint()).eval() * m2); + VERIFY_IS_APPROX(m3.noalias() = ((s1 * m1).adjoint()) * m2, (internal::conj(s1) * m1.adjoint()).eval() * m2); + VERIFY_IS_APPROX(m3.noalias() = (- m1.adjoint() * s1) * (s3 * m2), (- m1.adjoint() * s1).eval() * (s3 * m2).eval()); + VERIFY_IS_APPROX(m3.noalias() = (s2 * m1.adjoint() * s1) * m2, (s2 * m1.adjoint() * s1).eval() * m2); + VERIFY_IS_APPROX(m3.noalias() = (-m1*s2) * s1*m2.adjoint(), (-m1*s2).eval() * (s1*m2.adjoint()).eval()); + + // a very tricky case where a scale factor has to be automatically conjugated: + VERIFY_IS_APPROX( m1.adjoint() * (s1*m2).conjugate(), (m1.adjoint()).eval() * ((s1*m2).conjugate()).eval()); + + + // test all possible conjugate combinations for the four matrix-vector product cases: + + VERIFY_IS_APPROX((-m1.conjugate() * s2) * (s1 * vc2), + (-m1.conjugate()*s2).eval() * (s1 * vc2).eval()); + VERIFY_IS_APPROX((-m1 * s2) * (s1 * vc2.conjugate()), + (-m1*s2).eval() * (s1 * vc2.conjugate()).eval()); + VERIFY_IS_APPROX((-m1.conjugate() * s2) * (s1 * vc2.conjugate()), + (-m1.conjugate()*s2).eval() * (s1 * vc2.conjugate()).eval()); + + VERIFY_IS_APPROX((s1 * vc2.transpose()) * (-m1.adjoint() * s2), + (s1 * vc2.transpose()).eval() * (-m1.adjoint()*s2).eval()); + VERIFY_IS_APPROX((s1 * vc2.adjoint()) * (-m1.transpose() * s2), + (s1 * vc2.adjoint()).eval() * (-m1.transpose()*s2).eval()); + VERIFY_IS_APPROX((s1 * vc2.adjoint()) * (-m1.adjoint() * s2), + (s1 * vc2.adjoint()).eval() * (-m1.adjoint()*s2).eval()); + + VERIFY_IS_APPROX((-m1.adjoint() * s2) * (s1 * v1.transpose()), + (-m1.adjoint()*s2).eval() * (s1 * v1.transpose()).eval()); + VERIFY_IS_APPROX((-m1.transpose() * s2) * (s1 * v1.adjoint()), + (-m1.transpose()*s2).eval() * (s1 * v1.adjoint()).eval()); + VERIFY_IS_APPROX((-m1.adjoint() * s2) * (s1 * v1.adjoint()), + (-m1.adjoint()*s2).eval() * (s1 * v1.adjoint()).eval()); + + VERIFY_IS_APPROX((s1 * v1) * (-m1.conjugate() * s2), + (s1 * v1).eval() * (-m1.conjugate()*s2).eval()); + VERIFY_IS_APPROX((s1 * v1.conjugate()) * (-m1 * s2), + (s1 * v1.conjugate()).eval() * (-m1*s2).eval()); + VERIFY_IS_APPROX((s1 * v1.conjugate()) * (-m1.conjugate() * s2), + (s1 * v1.conjugate()).eval() * (-m1.conjugate()*s2).eval()); + + VERIFY_IS_APPROX((-m1.adjoint() * s2) * (s1 * v1.adjoint()), + (-m1.adjoint()*s2).eval() * (s1 * v1.adjoint()).eval()); + + // test the vector-matrix product with non aligned starts + Index i = internal::random<Index>(0,m1.rows()-2); + Index j = internal::random<Index>(0,m1.cols()-2); + Index r = internal::random<Index>(1,m1.rows()-i); + Index c = internal::random<Index>(1,m1.cols()-j); + Index i2 = internal::random<Index>(0,m1.rows()-1); + Index j2 = internal::random<Index>(0,m1.cols()-1); + + VERIFY_IS_APPROX(m1.col(j2).adjoint() * m1.block(0,j,m1.rows(),c), m1.col(j2).adjoint().eval() * m1.block(0,j,m1.rows(),c).eval()); + VERIFY_IS_APPROX(m1.block(i,0,r,m1.cols()) * m1.row(i2).adjoint(), m1.block(i,0,r,m1.cols()).eval() * m1.row(i2).adjoint().eval()); + + // regression test + MatrixType tmp = m1 * m1.adjoint() * s1; + VERIFY_IS_APPROX(tmp, m1 * m1.adjoint() * s1); +} + +// Regression test for bug reported at http://forum.kde.org/viewtopic.php?f=74&t=96947 +void mat_mat_scalar_scalar_product() +{ + Eigen::Matrix2Xd dNdxy(2, 3); + dNdxy << -0.5, 0.5, 0, + -0.3, 0, 0.3; + double det = 6.0, wt = 0.5; + VERIFY_IS_APPROX(dNdxy.transpose()*dNdxy*det*wt, det*wt*dNdxy.transpose()*dNdxy); +} + +void zero_sized_objects() +{ + // Bug 127 + // + // a product of the form lhs*rhs with + // + // lhs: + // rows = 1, cols = 4 + // RowsAtCompileTime = 1, ColsAtCompileTime = -1 + // MaxRowsAtCompileTime = 1, MaxColsAtCompileTime = 5 + // + // rhs: + // rows = 4, cols = 0 + // RowsAtCompileTime = -1, ColsAtCompileTime = -1 + // MaxRowsAtCompileTime = 5, MaxColsAtCompileTime = 1 + // + // was failing on a runtime assertion, because it had been mis-compiled as a dot product because Product.h was using the + // max-sizes to detect size 1 indicating vectors, and that didn't account for 0-sized object with max-size 1. + + Matrix<float,1,Dynamic,RowMajor,1,5> a(1,4); + Matrix<float,Dynamic,Dynamic,ColMajor,5,1> b(4,0); + a*b; +} + +void test_product_extra() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( product_extra(MatrixXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_2( product_extra(MatrixXd(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_2( mat_mat_scalar_scalar_product() ); + CALL_SUBTEST_3( product_extra(MatrixXcf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2), internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2))) ); + CALL_SUBTEST_4( product_extra(MatrixXcd(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2), internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2))) ); + CALL_SUBTEST_5( zero_sized_objects() ); + } +} diff --git a/test/product_large.cpp b/test/product_large.cpp new file mode 100644 index 000000000..03d7bd8ed --- /dev/null +++ b/test/product_large.cpp @@ -0,0 +1,64 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" + +void test_product_large() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( product(MatrixXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_2( product(MatrixXd(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_3( product(MatrixXi(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_4( product(MatrixXcf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2), internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2))) ); + CALL_SUBTEST_5( product(Matrix<float,Dynamic,Dynamic,RowMajor>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE), internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + } + +#if defined EIGEN_TEST_PART_6 + { + // test a specific issue in DiagonalProduct + int N = 1000000; + VectorXf v = VectorXf::Ones(N); + MatrixXf m = MatrixXf::Ones(N,3); + m = (v+v).asDiagonal() * m; + VERIFY_IS_APPROX(m, MatrixXf::Constant(N,3,2)); + } + + { + // test deferred resizing in Matrix::operator= + MatrixXf a = MatrixXf::Random(10,4), b = MatrixXf::Random(4,10), c = a; + VERIFY_IS_APPROX((a = a * b), (c * b).eval()); + } + + { + // check the functions to setup blocking sizes compile and do not segfault + // FIXME check they do what they are supposed to do !! + std::ptrdiff_t l1 = internal::random<int>(10000,20000); + std::ptrdiff_t l2 = internal::random<int>(1000000,2000000); + setCpuCacheSizes(l1,l2); + VERIFY(l1==l1CacheSize()); + VERIFY(l2==l2CacheSize()); + std::ptrdiff_t k1 = internal::random<int>(10,100)*16; + std::ptrdiff_t m1 = internal::random<int>(10,100)*16; + std::ptrdiff_t n1 = internal::random<int>(10,100)*16; + // only makes sure it compiles fine + internal::computeProductBlockingSizes<float,float>(k1,m1,n1); + } + + { + // test regression in row-vector by matrix (bad Map type) + MatrixXf mat1(10,32); mat1.setRandom(); + MatrixXf mat2(32,32); mat2.setRandom(); + MatrixXf r1 = mat1.row(2)*mat2.transpose(); + VERIFY_IS_APPROX(r1, (mat1.row(2)*mat2.transpose()).eval()); + + MatrixXf r2 = mat1.row(2)*mat2; + VERIFY_IS_APPROX(r2, (mat1.row(2)*mat2).eval()); + } +#endif +} diff --git a/test/product_mmtr.cpp b/test/product_mmtr.cpp new file mode 100644 index 000000000..879cfe16a --- /dev/null +++ b/test/product_mmtr.cpp @@ -0,0 +1,65 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2010 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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" + +#define CHECK_MMTR(DEST, TRI, OP) { \ + ref2 = ref1 = DEST; \ + DEST.template triangularView<TRI>() OP; \ + ref1 OP; \ + ref2.template triangularView<TRI>() = ref1; \ + VERIFY_IS_APPROX(DEST,ref2); \ + } + +template<typename Scalar> void mmtr(int size) +{ + typedef typename NumTraits<Scalar>::Real RealScalar; + + typedef Matrix<Scalar,Dynamic,Dynamic,ColMajor> MatrixColMaj; + typedef Matrix<Scalar,Dynamic,Dynamic,RowMajor> MatrixRowMaj; + + DenseIndex othersize = internal::random<DenseIndex>(1,200); + + MatrixColMaj matc(size, size); + MatrixRowMaj matr(size, size); + MatrixColMaj ref1(size, size), ref2(size, size); + + MatrixColMaj soc(size,othersize); soc.setRandom(); + MatrixColMaj osc(othersize,size); osc.setRandom(); + MatrixRowMaj sor(size,othersize); sor.setRandom(); + MatrixRowMaj osr(othersize,size); osr.setRandom(); + + Scalar s = internal::random<Scalar>(); + + CHECK_MMTR(matc, Lower, = s*soc*sor.adjoint()); + CHECK_MMTR(matc, Upper, = s*(soc*soc.adjoint())); + CHECK_MMTR(matr, Lower, = s*soc*soc.adjoint()); + CHECK_MMTR(matr, Upper, = soc*(s*sor.adjoint())); + + CHECK_MMTR(matc, Lower, += s*soc*soc.adjoint()); + CHECK_MMTR(matc, Upper, += s*(soc*sor.transpose())); + CHECK_MMTR(matr, Lower, += s*sor*soc.adjoint()); + CHECK_MMTR(matr, Upper, += soc*(s*soc.adjoint())); + + CHECK_MMTR(matc, Lower, -= s*soc*soc.adjoint()); + CHECK_MMTR(matc, Upper, -= s*(osc.transpose()*osc.conjugate())); + CHECK_MMTR(matr, Lower, -= s*soc*soc.adjoint()); + CHECK_MMTR(matr, Upper, -= soc*(s*soc.adjoint())); +} + +void test_product_mmtr() +{ + for(int i = 0; i < g_repeat ; i++) + { + CALL_SUBTEST_1((mmtr<float>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE)))); + CALL_SUBTEST_2((mmtr<double>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE)))); + CALL_SUBTEST_3((mmtr<std::complex<float> >(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2)))); + CALL_SUBTEST_4((mmtr<std::complex<double> >(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2)))); + } +} diff --git a/test/product_notemporary.cpp b/test/product_notemporary.cpp new file mode 100644 index 000000000..cf9dbdd03 --- /dev/null +++ b/test/product_notemporary.cpp @@ -0,0 +1,138 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +static int nb_temporaries; + +void on_temporary_creation(int size) { + // here's a great place to set a breakpoint when debugging failures in this test! + if(size!=0) nb_temporaries++; +} + + +#define EIGEN_DENSE_STORAGE_CTOR_PLUGIN { on_temporary_creation(size); } + +#include "main.h" + +#define VERIFY_EVALUATION_COUNT(XPR,N) {\ + nb_temporaries = 0; \ + XPR; \ + if(nb_temporaries!=N) std::cerr << "nb_temporaries == " << nb_temporaries << "\n"; \ + VERIFY( (#XPR) && nb_temporaries==N ); \ + } + +template<typename MatrixType> void product_notemporary(const MatrixType& m) +{ + /* This test checks the number of temporaries created + * during the evaluation of a complex expression */ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + typedef Matrix<Scalar, 1, Dynamic> RowVectorType; + typedef Matrix<Scalar, Dynamic, 1> ColVectorType; + typedef Matrix<Scalar, Dynamic, Dynamic, ColMajor> ColMajorMatrixType; + typedef Matrix<Scalar, Dynamic, Dynamic, RowMajor> RowMajorMatrixType; + + Index rows = m.rows(); + Index cols = m.cols(); + + ColMajorMatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols); + RowVectorType rv1 = RowVectorType::Random(rows), rvres(rows); + ColVectorType cv1 = ColVectorType::Random(cols), cvres(cols); + RowMajorMatrixType rm3(rows, cols); + + Scalar s1 = internal::random<Scalar>(), + s2 = internal::random<Scalar>(), + s3 = internal::random<Scalar>(); + + Index c0 = internal::random<Index>(4,cols-8), + c1 = internal::random<Index>(8,cols-c0), + r0 = internal::random<Index>(4,cols-8), + r1 = internal::random<Index>(8,rows-r0); + + VERIFY_EVALUATION_COUNT( m3 = (m1 * m2.adjoint()), 1); + VERIFY_EVALUATION_COUNT( m3.noalias() = m1 * m2.adjoint(), 0); + + VERIFY_EVALUATION_COUNT( m3.noalias() = s1 * (m1 * m2.transpose()), 0); + + VERIFY_EVALUATION_COUNT( m3.noalias() = s1 * m1 * s2 * m2.adjoint(), 0); + VERIFY_EVALUATION_COUNT( m3.noalias() = s1 * m1 * s2 * (m1*s3+m2*s2).adjoint(), 1); + VERIFY_EVALUATION_COUNT( m3.noalias() = (s1 * m1).adjoint() * s2 * m2, 0); + VERIFY_EVALUATION_COUNT( m3.noalias() += s1 * (-m1*s3).adjoint() * (s2 * m2 * s3), 0); + VERIFY_EVALUATION_COUNT( m3.noalias() -= s1 * (m1.transpose() * m2), 0); + + VERIFY_EVALUATION_COUNT(( m3.block(r0,r0,r1,r1).noalias() += -m1.block(r0,c0,r1,c1) * (s2*m2.block(r0,c0,r1,c1)).adjoint() ), 0); + VERIFY_EVALUATION_COUNT(( m3.block(r0,r0,r1,r1).noalias() -= s1 * m1.block(r0,c0,r1,c1) * m2.block(c0,r0,c1,r1) ), 0); + + // NOTE this is because the Block expression is not handled yet by our expression analyser + VERIFY_EVALUATION_COUNT(( m3.block(r0,r0,r1,r1).noalias() = s1 * m1.block(r0,c0,r1,c1) * (s1*m2).block(c0,r0,c1,r1) ), 1); + + VERIFY_EVALUATION_COUNT( m3.noalias() -= (s1 * m1).template triangularView<Lower>() * m2, 0); + VERIFY_EVALUATION_COUNT( rm3.noalias() = (s1 * m1.adjoint()).template triangularView<Upper>() * (m2+m2), 1); + VERIFY_EVALUATION_COUNT( rm3.noalias() = (s1 * m1.adjoint()).template triangularView<UnitUpper>() * m2.adjoint(), 0); + + // NOTE this is because the blas_traits require innerstride==1 to avoid a temporary, but that doesn't seem to be actually needed for the triangular products + VERIFY_EVALUATION_COUNT( rm3.col(c0).noalias() = (s1 * m1.adjoint()).template triangularView<UnitUpper>() * (s2*m2.row(c0)).adjoint(), 1); + + VERIFY_EVALUATION_COUNT( m1.template triangularView<Lower>().solveInPlace(m3), 0); + VERIFY_EVALUATION_COUNT( m1.adjoint().template triangularView<Lower>().solveInPlace(m3.transpose()), 0); + + VERIFY_EVALUATION_COUNT( m3.noalias() -= (s1 * m1).adjoint().template selfadjointView<Lower>() * (-m2*s3).adjoint(), 0); + VERIFY_EVALUATION_COUNT( m3.noalias() = s2 * m2.adjoint() * (s1 * m1.adjoint()).template selfadjointView<Upper>(), 0); + VERIFY_EVALUATION_COUNT( rm3.noalias() = (s1 * m1.adjoint()).template selfadjointView<Lower>() * m2.adjoint(), 0); + + // NOTE this is because the blas_traits require innerstride==1 to avoid a temporary, but that doesn't seem to be actually needed for the triangular products + VERIFY_EVALUATION_COUNT( m3.col(c0).noalias() = (s1 * m1).adjoint().template selfadjointView<Lower>() * (-m2.row(c0)*s3).adjoint(), 1); + VERIFY_EVALUATION_COUNT( m3.col(c0).noalias() -= (s1 * m1).adjoint().template selfadjointView<Upper>() * (-m2.row(c0)*s3).adjoint(), 1); + + VERIFY_EVALUATION_COUNT( m3.block(r0,c0,r1,c1).noalias() += m1.block(r0,r0,r1,r1).template selfadjointView<Upper>() * (s1*m2.block(r0,c0,r1,c1)), 0); + VERIFY_EVALUATION_COUNT( m3.block(r0,c0,r1,c1).noalias() = m1.block(r0,r0,r1,r1).template selfadjointView<Upper>() * m2.block(r0,c0,r1,c1), 0); + + VERIFY_EVALUATION_COUNT( m3.template selfadjointView<Lower>().rankUpdate(m2.adjoint()), 0); + + // Here we will get 1 temporary for each resize operation of the lhs operator; resize(r1,c1) would lead to zero temporaries + m3.resize(1,1); + VERIFY_EVALUATION_COUNT( m3.noalias() = m1.block(r0,r0,r1,r1).template selfadjointView<Lower>() * m2.block(r0,c0,r1,c1), 1); + m3.resize(1,1); + VERIFY_EVALUATION_COUNT( m3.noalias() = m1.block(r0,r0,r1,r1).template triangularView<UnitUpper>() * m2.block(r0,c0,r1,c1), 1); + + // Zero temporaries for lazy products ... + VERIFY_EVALUATION_COUNT( Scalar tmp = 0; tmp += Scalar(RealScalar(1)) / (m3.transpose().lazyProduct(m3)).diagonal().sum(), 0 ); + + // ... and even no temporary for even deeply (>=2) nested products + VERIFY_EVALUATION_COUNT( Scalar tmp = 0; tmp += Scalar(RealScalar(1)) / (m3.transpose() * m3).diagonal().sum(), 0 ); + VERIFY_EVALUATION_COUNT( Scalar tmp = 0; tmp += Scalar(RealScalar(1)) / (m3.transpose() * m3).diagonal().array().abs().sum(), 0 ); + + // Zero temporaries for ... CoeffBasedProductMode + // - does not work with GCC because of the <..>, we'ld need variadic macros ... + //VERIFY_EVALUATION_COUNT( m3.col(0).head<5>() * m3.col(0).transpose() + m3.col(0).head<5>() * m3.col(0).transpose(), 0 ); + + // Check matrix * vectors + VERIFY_EVALUATION_COUNT( cvres.noalias() = m1 * cv1, 0 ); + VERIFY_EVALUATION_COUNT( cvres.noalias() -= m1 * cv1, 0 ); + VERIFY_EVALUATION_COUNT( cvres.noalias() -= m1 * m2.col(0), 0 ); + VERIFY_EVALUATION_COUNT( cvres.noalias() -= m1 * rv1.adjoint(), 0 ); + VERIFY_EVALUATION_COUNT( cvres.noalias() -= m1 * m2.row(0).transpose(), 0 ); +} + +void test_product_notemporary() +{ + int s; + for(int i = 0; i < g_repeat; i++) { + s = internal::random<int>(16,EIGEN_TEST_MAX_SIZE); + CALL_SUBTEST_1( product_notemporary(MatrixXf(s, s)) ); + s = internal::random<int>(16,EIGEN_TEST_MAX_SIZE); + CALL_SUBTEST_2( product_notemporary(MatrixXd(s, s)) ); + s = internal::random<int>(16,EIGEN_TEST_MAX_SIZE/2); + CALL_SUBTEST_3( product_notemporary(MatrixXcf(s,s)) ); + s = internal::random<int>(16,EIGEN_TEST_MAX_SIZE/2); + CALL_SUBTEST_4( product_notemporary(MatrixXcd(s,s)) ); + } +} diff --git a/test/product_selfadjoint.cpp b/test/product_selfadjoint.cpp new file mode 100644 index 000000000..95693b155 --- /dev/null +++ b/test/product_selfadjoint.cpp @@ -0,0 +1,81 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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<typename MatrixType> void product_selfadjoint(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + typedef Matrix<Scalar, 1, MatrixType::RowsAtCompileTime> RowVectorType; + + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, Dynamic, RowMajor> RhsMatrixType; + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3; + VectorType v1 = VectorType::Random(rows), + v2 = VectorType::Random(rows), + v3(rows); + RowVectorType r1 = RowVectorType::Random(rows), + r2 = RowVectorType::Random(rows); + RhsMatrixType m4 = RhsMatrixType::Random(rows,10); + + Scalar s1 = internal::random<Scalar>(), + s2 = internal::random<Scalar>(), + s3 = internal::random<Scalar>(); + + m1 = (m1.adjoint() + m1).eval(); + + // rank2 update + m2 = m1.template triangularView<Lower>(); + m2.template selfadjointView<Lower>().rankUpdate(v1,v2); + VERIFY_IS_APPROX(m2, (m1 + v1 * v2.adjoint()+ v2 * v1.adjoint()).template triangularView<Lower>().toDenseMatrix()); + + m2 = m1.template triangularView<Upper>(); + m2.template selfadjointView<Upper>().rankUpdate(-v1,s2*v2,s3); + VERIFY_IS_APPROX(m2, (m1 + (s3*(-v1)*(s2*v2).adjoint()+internal::conj(s3)*(s2*v2)*(-v1).adjoint())).template triangularView<Upper>().toDenseMatrix()); + + m2 = m1.template triangularView<Upper>(); + m2.template selfadjointView<Upper>().rankUpdate(-s2*r1.adjoint(),r2.adjoint()*s3,s1); + VERIFY_IS_APPROX(m2, (m1 + s1*(-s2*r1.adjoint())*(r2.adjoint()*s3).adjoint() + internal::conj(s1)*(r2.adjoint()*s3) * (-s2*r1.adjoint()).adjoint()).template triangularView<Upper>().toDenseMatrix()); + + if (rows>1) + { + m2 = m1.template triangularView<Lower>(); + m2.block(1,1,rows-1,cols-1).template selfadjointView<Lower>().rankUpdate(v1.tail(rows-1),v2.head(cols-1)); + m3 = m1; + m3.block(1,1,rows-1,cols-1) += v1.tail(rows-1) * v2.head(cols-1).adjoint()+ v2.head(cols-1) * v1.tail(rows-1).adjoint(); + VERIFY_IS_APPROX(m2, m3.template triangularView<Lower>().toDenseMatrix()); + } +} + +void test_product_selfadjoint() +{ + int s; + for(int i = 0; i < g_repeat ; i++) { + CALL_SUBTEST_1( product_selfadjoint(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( product_selfadjoint(Matrix<float, 2, 2>()) ); + CALL_SUBTEST_3( product_selfadjoint(Matrix3d()) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2); + CALL_SUBTEST_4( product_selfadjoint(MatrixXcf(s, s)) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2); + CALL_SUBTEST_5( product_selfadjoint(MatrixXcd(s,s)) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE); + CALL_SUBTEST_6( product_selfadjoint(MatrixXd(s,s)) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE); + CALL_SUBTEST_7( product_selfadjoint(Matrix<float,Dynamic,Dynamic,RowMajor>(s,s)) ); + } + EIGEN_UNUSED_VARIABLE(s) +} diff --git a/test/product_small.cpp b/test/product_small.cpp new file mode 100644 index 000000000..8b132abb6 --- /dev/null +++ b/test/product_small.cpp @@ -0,0 +1,50 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#define EIGEN_NO_STATIC_ASSERT +#include "product.h" + +// regression test for bug 447 +void product1x1() +{ + Matrix<float,1,3> matAstatic; + Matrix<float,3,1> 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() ); +} + + +void test_product_small() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( product(Matrix<float, 3, 2>()) ); + CALL_SUBTEST_2( product(Matrix<int, 3, 5>()) ); + CALL_SUBTEST_3( product(Matrix3d()) ); + CALL_SUBTEST_4( product(Matrix4d()) ); + CALL_SUBTEST_5( product(Matrix4f()) ); + CALL_SUBTEST_6( product1x1() ); + } + +#ifdef EIGEN_TEST_PART_6 + { + // test compilation of (outer_product) * vector + Vector3f v = Vector3f::Random(); + VERIFY_IS_APPROX( (v * v.transpose()) * v, (v * v.transpose()).eval() * v); + } +#endif +} diff --git a/test/product_symm.cpp b/test/product_symm.cpp new file mode 100644 index 000000000..2f7a0d231 --- /dev/null +++ b/test/product_symm.cpp @@ -0,0 +1,96 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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<typename Scalar, int Size, int OtherSize> void symm(int size = Size, int othersize = OtherSize) +{ + typedef typename NumTraits<Scalar>::Real RealScalar; + + typedef Matrix<Scalar, Size, Size> MatrixType; + typedef Matrix<Scalar, Size, OtherSize> Rhs1; + typedef Matrix<Scalar, OtherSize, Size> Rhs2; + enum { order = OtherSize==1 ? 0 : RowMajor }; + typedef Matrix<Scalar, Size, OtherSize,order> Rhs3; + typedef typename MatrixType::Index Index; + + Index rows = size; + Index cols = size; + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), m3; + + m1 = (m1+m1.adjoint()).eval(); + + Rhs1 rhs1 = Rhs1::Random(cols, othersize), rhs12(cols, othersize), rhs13(cols, othersize); + Rhs2 rhs2 = Rhs2::Random(othersize, rows), rhs22(othersize, rows), rhs23(othersize, rows); + Rhs3 rhs3 = Rhs3::Random(cols, othersize), rhs32(cols, othersize), rhs33(cols, othersize); + + Scalar s1 = internal::random<Scalar>(), + s2 = internal::random<Scalar>(); + + m2 = m1.template triangularView<Lower>(); + m3 = m2.template selfadjointView<Lower>(); + VERIFY_IS_EQUAL(m1, m3); + VERIFY_IS_APPROX(rhs12 = (s1*m2).template selfadjointView<Lower>() * (s2*rhs1), + rhs13 = (s1*m1) * (s2*rhs1)); + + m2 = m1.template triangularView<Upper>(); rhs12.setRandom(); rhs13 = rhs12; + m3 = m2.template selfadjointView<Upper>(); + VERIFY_IS_EQUAL(m1, m3); + VERIFY_IS_APPROX(rhs12 += (s1*m2).template selfadjointView<Upper>() * (s2*rhs1), + rhs13 += (s1*m1) * (s2*rhs1)); + + m2 = m1.template triangularView<Lower>(); + VERIFY_IS_APPROX(rhs12 = (s1*m2).template selfadjointView<Lower>() * (s2*rhs2.adjoint()), + rhs13 = (s1*m1) * (s2*rhs2.adjoint())); + + m2 = m1.template triangularView<Upper>(); + VERIFY_IS_APPROX(rhs12 = (s1*m2).template selfadjointView<Upper>() * (s2*rhs2.adjoint()), + rhs13 = (s1*m1) * (s2*rhs2.adjoint())); + + m2 = m1.template triangularView<Upper>(); + VERIFY_IS_APPROX(rhs12 = (s1*m2.adjoint()).template selfadjointView<Lower>() * (s2*rhs2.adjoint()), + rhs13 = (s1*m1.adjoint()) * (s2*rhs2.adjoint())); + + // test row major = <...> + m2 = m1.template triangularView<Lower>(); rhs12.setRandom(); rhs13 = rhs12; + VERIFY_IS_APPROX(rhs12 -= (s1*m2).template selfadjointView<Lower>() * (s2*rhs3), + rhs13 -= (s1*m1) * (s2 * rhs3)); + + m2 = m1.template triangularView<Upper>(); + VERIFY_IS_APPROX(rhs12 = (s1*m2.adjoint()).template selfadjointView<Lower>() * (s2*rhs3).conjugate(), + rhs13 = (s1*m1.adjoint()) * (s2*rhs3).conjugate()); + + + m2 = m1.template triangularView<Upper>(); rhs13 = rhs12; + VERIFY_IS_APPROX(rhs12.noalias() += s1 * ((m2.adjoint()).template selfadjointView<Lower>() * (s2*rhs3).conjugate()), + rhs13 += (s1*m1.adjoint()) * (s2*rhs3).conjugate()); + + m2 = m1.template triangularView<Lower>(); + VERIFY_IS_APPROX(rhs22 = (rhs2) * (m2).template selfadjointView<Lower>(), rhs23 = (rhs2) * (m1)); + VERIFY_IS_APPROX(rhs22 = (s2*rhs2) * (s1*m2).template selfadjointView<Lower>(), rhs23 = (s2*rhs2) * (s1*m1)); + +} + +void test_product_symm() +{ + for(int i = 0; i < g_repeat ; i++) + { + CALL_SUBTEST_1(( symm<float,Dynamic,Dynamic>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE),internal::random<int>(1,EIGEN_TEST_MAX_SIZE)) )); + CALL_SUBTEST_2(( symm<double,Dynamic,Dynamic>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE),internal::random<int>(1,EIGEN_TEST_MAX_SIZE)) )); + CALL_SUBTEST_3(( symm<std::complex<float>,Dynamic,Dynamic>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2),internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2)) )); + CALL_SUBTEST_4(( symm<std::complex<double>,Dynamic,Dynamic>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2),internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2)) )); + + CALL_SUBTEST_5(( symm<float,Dynamic,1>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE)) )); + CALL_SUBTEST_6(( symm<double,Dynamic,1>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE)) )); + CALL_SUBTEST_7(( symm<std::complex<float>,Dynamic,1>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE)) )); + CALL_SUBTEST_8(( symm<std::complex<double>,Dynamic,1>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE)) )); + } +} diff --git a/test/product_syrk.cpp b/test/product_syrk.cpp new file mode 100644 index 000000000..5855c2181 --- /dev/null +++ b/test/product_syrk.cpp @@ -0,0 +1,98 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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<typename MatrixType> void syrk(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, Dynamic> Rhs1; + typedef Matrix<Scalar, Dynamic, MatrixType::RowsAtCompileTime> Rhs2; + typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, Dynamic,RowMajor> Rhs3; + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols); + + Rhs1 rhs1 = Rhs1::Random(internal::random<int>(1,320), cols); + Rhs2 rhs2 = Rhs2::Random(rows, internal::random<int>(1,320)); + Rhs3 rhs3 = Rhs3::Random(internal::random<int>(1,320), rows); + + Scalar s1 = internal::random<Scalar>(); + + Index c = internal::random<Index>(0,cols-1); + + m2.setZero(); + VERIFY_IS_APPROX((m2.template selfadjointView<Lower>().rankUpdate(rhs2,s1)._expression()), + ((s1 * rhs2 * rhs2.adjoint()).eval().template triangularView<Lower>().toDenseMatrix())); + + m2.setZero(); + VERIFY_IS_APPROX(m2.template selfadjointView<Upper>().rankUpdate(rhs2,s1)._expression(), + (s1 * rhs2 * rhs2.adjoint()).eval().template triangularView<Upper>().toDenseMatrix()); + + m2.setZero(); + VERIFY_IS_APPROX(m2.template selfadjointView<Lower>().rankUpdate(rhs1.adjoint(),s1)._expression(), + (s1 * rhs1.adjoint() * rhs1).eval().template triangularView<Lower>().toDenseMatrix()); + + m2.setZero(); + VERIFY_IS_APPROX(m2.template selfadjointView<Upper>().rankUpdate(rhs1.adjoint(),s1)._expression(), + (s1 * rhs1.adjoint() * rhs1).eval().template triangularView<Upper>().toDenseMatrix()); + + m2.setZero(); + VERIFY_IS_APPROX(m2.template selfadjointView<Lower>().rankUpdate(rhs3.adjoint(),s1)._expression(), + (s1 * rhs3.adjoint() * rhs3).eval().template triangularView<Lower>().toDenseMatrix()); + + m2.setZero(); + VERIFY_IS_APPROX(m2.template selfadjointView<Upper>().rankUpdate(rhs3.adjoint(),s1)._expression(), + (s1 * rhs3.adjoint() * rhs3).eval().template triangularView<Upper>().toDenseMatrix()); + + m2.setZero(); + VERIFY_IS_APPROX((m2.template selfadjointView<Lower>().rankUpdate(m1.col(c),s1)._expression()), + ((s1 * m1.col(c) * m1.col(c).adjoint()).eval().template triangularView<Lower>().toDenseMatrix())); + + m2.setZero(); + VERIFY_IS_APPROX((m2.template selfadjointView<Upper>().rankUpdate(m1.col(c),s1)._expression()), + ((s1 * m1.col(c) * m1.col(c).adjoint()).eval().template triangularView<Upper>().toDenseMatrix())); + + m2.setZero(); + VERIFY_IS_APPROX((m2.template selfadjointView<Lower>().rankUpdate(m1.col(c).conjugate(),s1)._expression()), + ((s1 * m1.col(c).conjugate() * m1.col(c).conjugate().adjoint()).eval().template triangularView<Lower>().toDenseMatrix())); + + m2.setZero(); + VERIFY_IS_APPROX((m2.template selfadjointView<Upper>().rankUpdate(m1.col(c).conjugate(),s1)._expression()), + ((s1 * m1.col(c).conjugate() * m1.col(c).conjugate().adjoint()).eval().template triangularView<Upper>().toDenseMatrix())); + + m2.setZero(); + VERIFY_IS_APPROX((m2.template selfadjointView<Lower>().rankUpdate(m1.row(c),s1)._expression()), + ((s1 * m1.row(c).transpose() * m1.row(c).transpose().adjoint()).eval().template triangularView<Lower>().toDenseMatrix())); + + m2.setZero(); + VERIFY_IS_APPROX((m2.template selfadjointView<Upper>().rankUpdate(m1.row(c).adjoint(),s1)._expression()), + ((s1 * m1.row(c).adjoint() * m1.row(c).adjoint().adjoint()).eval().template triangularView<Upper>().toDenseMatrix())); +} + +void test_product_syrk() +{ + for(int i = 0; i < g_repeat ; i++) + { + int s; + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE); + CALL_SUBTEST_1( syrk(MatrixXf(s, s)) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE); + CALL_SUBTEST_2( syrk(MatrixXd(s, s)) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2); + CALL_SUBTEST_3( syrk(MatrixXcf(s, s)) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2); + CALL_SUBTEST_4( syrk(MatrixXcd(s, s)) ); + } +} diff --git a/test/product_trmm.cpp b/test/product_trmm.cpp new file mode 100644 index 000000000..64244c18f --- /dev/null +++ b/test/product_trmm.cpp @@ -0,0 +1,108 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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<typename Scalar, int Mode, int TriOrder, int OtherOrder, int ResOrder, int OtherCols> +void trmm(int rows=internal::random<int>(1,EIGEN_TEST_MAX_SIZE), + int cols=internal::random<int>(1,EIGEN_TEST_MAX_SIZE), + int otherCols = OtherCols==Dynamic?internal::random<int>(1,EIGEN_TEST_MAX_SIZE):OtherCols) +{ + typedef typename NumTraits<Scalar>::Real RealScalar; + + typedef Matrix<Scalar,Dynamic,Dynamic,TriOrder> TriMatrix; + typedef Matrix<Scalar,Dynamic,OtherCols,OtherCols==1?ColMajor:OtherOrder> OnTheRight; + typedef Matrix<Scalar,OtherCols,Dynamic,OtherCols==1?RowMajor:OtherOrder> OnTheLeft; + + typedef Matrix<Scalar,Dynamic,OtherCols,OtherCols==1?ColMajor:ResOrder> ResXS; + typedef Matrix<Scalar,OtherCols,Dynamic,OtherCols==1?RowMajor:ResOrder> ResSX; + + TriMatrix mat(rows,cols), tri(rows,cols), triTr(cols,rows); + + OnTheRight ge_right(cols,otherCols); + OnTheLeft ge_left(otherCols,rows); + ResSX ge_sx, ge_sx_save; + ResXS ge_xs, ge_xs_save; + + Scalar s1 = internal::random<Scalar>(), + s2 = internal::random<Scalar>(); + + mat.setRandom(); + tri = mat.template triangularView<Mode>(); + triTr = mat.transpose().template triangularView<Mode>(); + ge_right.setRandom(); + ge_left.setRandom(); + + VERIFY_IS_APPROX( ge_xs = mat.template triangularView<Mode>() * ge_right, tri * ge_right); + VERIFY_IS_APPROX( ge_sx = ge_left * mat.template triangularView<Mode>(), ge_left * tri); + + VERIFY_IS_APPROX( ge_xs.noalias() = mat.template triangularView<Mode>() * ge_right, tri * ge_right); + VERIFY_IS_APPROX( ge_sx.noalias() = ge_left * mat.template triangularView<Mode>(), ge_left * tri); + + VERIFY_IS_APPROX( ge_xs.noalias() = (s1*mat.adjoint()).template triangularView<Mode>() * (s2*ge_left.transpose()), s1*triTr.conjugate() * (s2*ge_left.transpose())); + VERIFY_IS_APPROX( ge_sx.noalias() = ge_right.transpose() * mat.adjoint().template triangularView<Mode>(), ge_right.transpose() * triTr.conjugate()); + + VERIFY_IS_APPROX( ge_xs.noalias() = (s1*mat.adjoint()).template triangularView<Mode>() * (s2*ge_left.adjoint()), s1*triTr.conjugate() * (s2*ge_left.adjoint())); + VERIFY_IS_APPROX( ge_sx.noalias() = ge_right.adjoint() * mat.adjoint().template triangularView<Mode>(), ge_right.adjoint() * triTr.conjugate()); + + ge_xs_save = ge_xs; + VERIFY_IS_APPROX( (ge_xs_save + s1*triTr.conjugate() * (s2*ge_left.adjoint())).eval(), ge_xs.noalias() += (s1*mat.adjoint()).template triangularView<Mode>() * (s2*ge_left.adjoint()) ); + ge_sx_save = ge_sx; + VERIFY_IS_APPROX( ge_sx_save - (ge_right.adjoint() * (-s1 * triTr).conjugate()).eval(), ge_sx.noalias() -= (ge_right.adjoint() * (-s1 * mat).adjoint().template triangularView<Mode>()).eval()); + + VERIFY_IS_APPROX( ge_xs = (s1*mat).adjoint().template triangularView<Mode>() * ge_left.adjoint(), internal::conj(s1) * triTr.conjugate() * ge_left.adjoint()); + + // TODO check with sub-matrix expressions ? +} + +template<typename Scalar, int Mode, int TriOrder> +void trmv(int rows=internal::random<int>(1,EIGEN_TEST_MAX_SIZE), int cols=internal::random<int>(1,EIGEN_TEST_MAX_SIZE)) +{ + trmm<Scalar,Mode,TriOrder,ColMajor,ColMajor,1>(rows,cols,1); +} + +template<typename Scalar, int Mode, int TriOrder, int OtherOrder, int ResOrder> +void trmm(int rows=internal::random<int>(1,EIGEN_TEST_MAX_SIZE), int cols=internal::random<int>(1,EIGEN_TEST_MAX_SIZE), int otherCols = internal::random<int>(1,EIGEN_TEST_MAX_SIZE)) +{ + trmm<Scalar,Mode,TriOrder,OtherOrder,ResOrder,Dynamic>(rows,cols,otherCols); +} + +#define CALL_ALL_ORDERS(NB,SCALAR,MODE) \ + EIGEN_CAT(CALL_SUBTEST_,NB)((trmm<SCALAR, MODE, ColMajor,ColMajor,ColMajor>())); \ + EIGEN_CAT(CALL_SUBTEST_,NB)((trmm<SCALAR, MODE, ColMajor,ColMajor,RowMajor>())); \ + EIGEN_CAT(CALL_SUBTEST_,NB)((trmm<SCALAR, MODE, ColMajor,RowMajor,ColMajor>())); \ + EIGEN_CAT(CALL_SUBTEST_,NB)((trmm<SCALAR, MODE, ColMajor,RowMajor,RowMajor>())); \ + EIGEN_CAT(CALL_SUBTEST_,NB)((trmm<SCALAR, MODE, RowMajor,ColMajor,ColMajor>())); \ + EIGEN_CAT(CALL_SUBTEST_,NB)((trmm<SCALAR, MODE, RowMajor,ColMajor,RowMajor>())); \ + EIGEN_CAT(CALL_SUBTEST_,NB)((trmm<SCALAR, MODE, RowMajor,RowMajor,ColMajor>())); \ + EIGEN_CAT(CALL_SUBTEST_,NB)((trmm<SCALAR, MODE, RowMajor,RowMajor,RowMajor>())); \ + \ + EIGEN_CAT(CALL_SUBTEST_1,NB)((trmv<SCALAR, MODE, ColMajor>())); \ + EIGEN_CAT(CALL_SUBTEST_1,NB)((trmv<SCALAR, MODE, RowMajor>())); + + +#define CALL_ALL(NB,SCALAR) \ + CALL_ALL_ORDERS(EIGEN_CAT(1,NB),SCALAR,Upper) \ + CALL_ALL_ORDERS(EIGEN_CAT(2,NB),SCALAR,UnitUpper) \ + CALL_ALL_ORDERS(EIGEN_CAT(3,NB),SCALAR,StrictlyUpper) \ + CALL_ALL_ORDERS(EIGEN_CAT(1,NB),SCALAR,Lower) \ + CALL_ALL_ORDERS(EIGEN_CAT(2,NB),SCALAR,UnitLower) \ + CALL_ALL_ORDERS(EIGEN_CAT(3,NB),SCALAR,StrictlyLower) + + +void test_product_trmm() +{ + for(int i = 0; i < g_repeat ; i++) + { + CALL_ALL(1,float); // EIGEN_SUFFIXES;11;111;21;121;31;131 + CALL_ALL(2,double); // EIGEN_SUFFIXES;12;112;22;122;32;132 + CALL_ALL(3,std::complex<float>); // EIGEN_SUFFIXES;13;113;23;123;33;133 + CALL_ALL(4,std::complex<double>); // EIGEN_SUFFIXES;14;114;24;124;34;134 + } +} diff --git a/test/product_trmv.cpp b/test/product_trmv.cpp new file mode 100644 index 000000000..435018e8e --- /dev/null +++ b/test/product_trmv.cpp @@ -0,0 +1,89 @@ +// This file is triangularView of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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<typename MatrixType> void trmv(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + RealScalar largerEps = 10*test_precision<RealScalar>(); + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m3(rows, cols); + VectorType v1 = VectorType::Random(rows); + + Scalar s1 = internal::random<Scalar>(); + + m1 = MatrixType::Random(rows, cols); + + // check with a column-major matrix + m3 = m1.template triangularView<Eigen::Lower>(); + VERIFY((m3 * v1).isApprox(m1.template triangularView<Eigen::Lower>() * v1, largerEps)); + m3 = m1.template triangularView<Eigen::Upper>(); + VERIFY((m3 * v1).isApprox(m1.template triangularView<Eigen::Upper>() * v1, largerEps)); + m3 = m1.template triangularView<Eigen::UnitLower>(); + VERIFY((m3 * v1).isApprox(m1.template triangularView<Eigen::UnitLower>() * v1, largerEps)); + m3 = m1.template triangularView<Eigen::UnitUpper>(); + VERIFY((m3 * v1).isApprox(m1.template triangularView<Eigen::UnitUpper>() * v1, largerEps)); + + // check conjugated and scalar multiple expressions (col-major) + m3 = m1.template triangularView<Eigen::Lower>(); + VERIFY(((s1*m3).conjugate() * v1).isApprox((s1*m1).conjugate().template triangularView<Eigen::Lower>() * v1, largerEps)); + m3 = m1.template triangularView<Eigen::Upper>(); + VERIFY((m3.conjugate() * v1.conjugate()).isApprox(m1.conjugate().template triangularView<Eigen::Upper>() * v1.conjugate(), largerEps)); + + // check with a row-major matrix + m3 = m1.template triangularView<Eigen::Upper>(); + VERIFY((m3.transpose() * v1).isApprox(m1.transpose().template triangularView<Eigen::Lower>() * v1, largerEps)); + m3 = m1.template triangularView<Eigen::Lower>(); + VERIFY((m3.transpose() * v1).isApprox(m1.transpose().template triangularView<Eigen::Upper>() * v1, largerEps)); + m3 = m1.template triangularView<Eigen::UnitUpper>(); + VERIFY((m3.transpose() * v1).isApprox(m1.transpose().template triangularView<Eigen::UnitLower>() * v1, largerEps)); + m3 = m1.template triangularView<Eigen::UnitLower>(); + VERIFY((m3.transpose() * v1).isApprox(m1.transpose().template triangularView<Eigen::UnitUpper>() * v1, largerEps)); + + // check conjugated and scalar multiple expressions (row-major) + m3 = m1.template triangularView<Eigen::Upper>(); + VERIFY((m3.adjoint() * v1).isApprox(m1.adjoint().template triangularView<Eigen::Lower>() * v1, largerEps)); + m3 = m1.template triangularView<Eigen::Lower>(); + VERIFY((m3.adjoint() * (s1*v1.conjugate())).isApprox(m1.adjoint().template triangularView<Eigen::Upper>() * (s1*v1.conjugate()), largerEps)); + m3 = m1.template triangularView<Eigen::UnitUpper>(); + + // check transposed cases: + m3 = m1.template triangularView<Eigen::Lower>(); + VERIFY((v1.transpose() * m3).isApprox(v1.transpose() * m1.template triangularView<Eigen::Lower>(), largerEps)); + VERIFY((v1.adjoint() * m3).isApprox(v1.adjoint() * m1.template triangularView<Eigen::Lower>(), largerEps)); + VERIFY((v1.adjoint() * m3.adjoint()).isApprox(v1.adjoint() * m1.template triangularView<Eigen::Lower>().adjoint(), largerEps)); + + // TODO check with sub-matrices +} + +void test_product_trmv() +{ + int s; + for(int i = 0; i < g_repeat ; i++) { + CALL_SUBTEST_1( trmv(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( trmv(Matrix<float, 2, 2>()) ); + CALL_SUBTEST_3( trmv(Matrix3d()) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2); + CALL_SUBTEST_4( trmv(MatrixXcf(s,s)) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2); + CALL_SUBTEST_5( trmv(MatrixXcd(s,s)) ); + s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE); + CALL_SUBTEST_6( trmv(Matrix<float,Dynamic,Dynamic,RowMajor>(s, s)) ); + } + EIGEN_UNUSED_VARIABLE(s); +} diff --git a/test/product_trsolve.cpp b/test/product_trsolve.cpp new file mode 100644 index 000000000..69892b3a8 --- /dev/null +++ b/test/product_trsolve.cpp @@ -0,0 +1,93 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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" + +#define VERIFY_TRSM(TRI,XB) { \ + (XB).setRandom(); ref = (XB); \ + (TRI).solveInPlace(XB); \ + VERIFY_IS_APPROX((TRI).toDenseMatrix() * (XB), ref); \ + (XB).setRandom(); ref = (XB); \ + (XB) = (TRI).solve(XB); \ + VERIFY_IS_APPROX((TRI).toDenseMatrix() * (XB), ref); \ + } + +#define VERIFY_TRSM_ONTHERIGHT(TRI,XB) { \ + (XB).setRandom(); ref = (XB); \ + (TRI).transpose().template solveInPlace<OnTheRight>(XB.transpose()); \ + VERIFY_IS_APPROX((XB).transpose() * (TRI).transpose().toDenseMatrix(), ref.transpose()); \ + (XB).setRandom(); ref = (XB); \ + (XB).transpose() = (TRI).transpose().template solve<OnTheRight>(XB.transpose()); \ + VERIFY_IS_APPROX((XB).transpose() * (TRI).transpose().toDenseMatrix(), ref.transpose()); \ + } + +template<typename Scalar,int Size, int Cols> void trsolve(int size=Size,int cols=Cols) +{ + typedef typename NumTraits<Scalar>::Real RealScalar; + + Matrix<Scalar,Size,Size,ColMajor> cmLhs(size,size); + Matrix<Scalar,Size,Size,RowMajor> rmLhs(size,size); + + enum { colmajor = Size==1 ? RowMajor : ColMajor, + rowmajor = Cols==1 ? ColMajor : RowMajor }; + Matrix<Scalar,Size,Cols,colmajor> cmRhs(size,cols); + Matrix<Scalar,Size,Cols,rowmajor> rmRhs(size,cols); + Matrix<Scalar,Dynamic,Dynamic,colmajor> ref(size,cols); + + cmLhs.setRandom(); cmLhs *= static_cast<RealScalar>(0.1); cmLhs.diagonal().array() += static_cast<RealScalar>(1); + rmLhs.setRandom(); rmLhs *= static_cast<RealScalar>(0.1); rmLhs.diagonal().array() += static_cast<RealScalar>(1); + + VERIFY_TRSM(cmLhs.conjugate().template triangularView<Lower>(), cmRhs); + VERIFY_TRSM(cmLhs.adjoint() .template triangularView<Lower>(), cmRhs); + VERIFY_TRSM(cmLhs .template triangularView<Upper>(), cmRhs); + VERIFY_TRSM(cmLhs .template triangularView<Lower>(), rmRhs); + VERIFY_TRSM(cmLhs.conjugate().template triangularView<Upper>(), rmRhs); + VERIFY_TRSM(cmLhs.adjoint() .template triangularView<Upper>(), rmRhs); + + VERIFY_TRSM(cmLhs.conjugate().template triangularView<UnitLower>(), cmRhs); + VERIFY_TRSM(cmLhs .template triangularView<UnitUpper>(), rmRhs); + + VERIFY_TRSM(rmLhs .template triangularView<Lower>(), cmRhs); + VERIFY_TRSM(rmLhs.conjugate().template triangularView<UnitUpper>(), rmRhs); + + + VERIFY_TRSM_ONTHERIGHT(cmLhs.conjugate().template triangularView<Lower>(), cmRhs); + VERIFY_TRSM_ONTHERIGHT(cmLhs .template triangularView<Upper>(), cmRhs); + VERIFY_TRSM_ONTHERIGHT(cmLhs .template triangularView<Lower>(), rmRhs); + VERIFY_TRSM_ONTHERIGHT(cmLhs.conjugate().template triangularView<Upper>(), rmRhs); + + VERIFY_TRSM_ONTHERIGHT(cmLhs.conjugate().template triangularView<UnitLower>(), cmRhs); + VERIFY_TRSM_ONTHERIGHT(cmLhs .template triangularView<UnitUpper>(), rmRhs); + + VERIFY_TRSM_ONTHERIGHT(rmLhs .template triangularView<Lower>(), cmRhs); + VERIFY_TRSM_ONTHERIGHT(rmLhs.conjugate().template triangularView<UnitUpper>(), rmRhs); + + int c = internal::random<int>(0,cols-1); + VERIFY_TRSM(rmLhs.template triangularView<Lower>(), rmRhs.col(c)); + VERIFY_TRSM(cmLhs.template triangularView<Lower>(), rmRhs.col(c)); +} + +void test_product_trsolve() +{ + for(int i = 0; i < g_repeat ; i++) + { + // matrices + CALL_SUBTEST_1((trsolve<float,Dynamic,Dynamic>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE),internal::random<int>(1,EIGEN_TEST_MAX_SIZE)))); + CALL_SUBTEST_2((trsolve<double,Dynamic,Dynamic>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE),internal::random<int>(1,EIGEN_TEST_MAX_SIZE)))); + CALL_SUBTEST_3((trsolve<std::complex<float>,Dynamic,Dynamic>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2),internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2)))); + CALL_SUBTEST_4((trsolve<std::complex<double>,Dynamic,Dynamic>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2),internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2)))); + + // vectors + CALL_SUBTEST_1((trsolve<float,Dynamic,1>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE)))); + CALL_SUBTEST_5((trsolve<std::complex<double>,Dynamic,1>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE)))); + CALL_SUBTEST_6((trsolve<float,1,1>())); + CALL_SUBTEST_7((trsolve<float,1,2>())); + CALL_SUBTEST_8((trsolve<std::complex<float>,4,1>())); + } +} diff --git a/test/qr.cpp b/test/qr.cpp new file mode 100644 index 000000000..37fb7aa4d --- /dev/null +++ b/test/qr.cpp @@ -0,0 +1,126 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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" +#include <Eigen/QR> + +template<typename MatrixType> void qr(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + + Index rows = m.rows(); + Index cols = m.cols(); + + typedef typename MatrixType::Scalar Scalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> MatrixQType; + typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, 1> VectorType; + + MatrixType a = MatrixType::Random(rows,cols); + HouseholderQR<MatrixType> qrOfA(a); + + MatrixQType q = qrOfA.householderQ(); + VERIFY_IS_UNITARY(q); + + MatrixType r = qrOfA.matrixQR().template triangularView<Upper>(); + VERIFY_IS_APPROX(a, qrOfA.householderQ() * r); +} + +template<typename MatrixType, int Cols2> void qr_fixedsize() +{ + enum { Rows = MatrixType::RowsAtCompileTime, Cols = MatrixType::ColsAtCompileTime }; + typedef typename MatrixType::Scalar Scalar; + Matrix<Scalar,Rows,Cols> m1 = Matrix<Scalar,Rows,Cols>::Random(); + HouseholderQR<Matrix<Scalar,Rows,Cols> > qr(m1); + + Matrix<Scalar,Rows,Cols> r = qr.matrixQR(); + // FIXME need better way to construct trapezoid + for(int i = 0; i < Rows; i++) for(int j = 0; j < Cols; j++) if(i>j) r(i,j) = Scalar(0); + + VERIFY_IS_APPROX(m1, qr.householderQ() * r); + + Matrix<Scalar,Cols,Cols2> m2 = Matrix<Scalar,Cols,Cols2>::Random(Cols,Cols2); + Matrix<Scalar,Rows,Cols2> m3 = m1*m2; + m2 = Matrix<Scalar,Cols,Cols2>::Random(Cols,Cols2); + m2 = qr.solve(m3); + VERIFY_IS_APPROX(m3, m1*m2); +} + +template<typename MatrixType> void qr_invertible() +{ + typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar; + typedef typename MatrixType::Scalar Scalar; + + int size = internal::random<int>(10,50); + + MatrixType m1(size, size), m2(size, size), m3(size, size); + m1 = MatrixType::Random(size,size); + + if (internal::is_same<RealScalar,float>::value) + { + // let's build a matrix more stable to inverse + MatrixType a = MatrixType::Random(size,size*2); + m1 += a * a.adjoint(); + } + + HouseholderQR<MatrixType> qr(m1); + m3 = MatrixType::Random(size,size); + m2 = qr.solve(m3); + VERIFY_IS_APPROX(m3, m1*m2); + + // now construct a matrix with prescribed determinant + m1.setZero(); + for(int i = 0; i < size; i++) m1(i,i) = internal::random<Scalar>(); + RealScalar absdet = internal::abs(m1.diagonal().prod()); + m3 = qr.householderQ(); // get a unitary + m1 = m3 * m1 * m3; + qr.compute(m1); + VERIFY_IS_APPROX(absdet, qr.absDeterminant()); + VERIFY_IS_APPROX(internal::log(absdet), qr.logAbsDeterminant()); +} + +template<typename MatrixType> void qr_verify_assert() +{ + MatrixType tmp; + + HouseholderQR<MatrixType> qr; + VERIFY_RAISES_ASSERT(qr.matrixQR()) + VERIFY_RAISES_ASSERT(qr.solve(tmp)) + VERIFY_RAISES_ASSERT(qr.householderQ()) + VERIFY_RAISES_ASSERT(qr.absDeterminant()) + VERIFY_RAISES_ASSERT(qr.logAbsDeterminant()) +} + +void test_qr() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( qr(MatrixXf(internal::random<int>(1,EIGEN_TEST_MAX_SIZE),internal::random<int>(1,EIGEN_TEST_MAX_SIZE))) ); + CALL_SUBTEST_2( qr(MatrixXcd(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2),internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2))) ); + CALL_SUBTEST_3(( qr_fixedsize<Matrix<float,3,4>, 2 >() )); + CALL_SUBTEST_4(( qr_fixedsize<Matrix<double,6,2>, 4 >() )); + CALL_SUBTEST_5(( qr_fixedsize<Matrix<double,2,5>, 7 >() )); + CALL_SUBTEST_11( qr(Matrix<float,1,1>()) ); + } + + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( qr_invertible<MatrixXf>() ); + CALL_SUBTEST_6( qr_invertible<MatrixXd>() ); + CALL_SUBTEST_7( qr_invertible<MatrixXcf>() ); + CALL_SUBTEST_8( qr_invertible<MatrixXcd>() ); + } + + CALL_SUBTEST_9(qr_verify_assert<Matrix3f>()); + CALL_SUBTEST_10(qr_verify_assert<Matrix3d>()); + CALL_SUBTEST_1(qr_verify_assert<MatrixXf>()); + CALL_SUBTEST_6(qr_verify_assert<MatrixXd>()); + CALL_SUBTEST_7(qr_verify_assert<MatrixXcf>()); + CALL_SUBTEST_8(qr_verify_assert<MatrixXcd>()); + + // Test problem size constructors + CALL_SUBTEST_12(HouseholderQR<MatrixXf>(10, 20)); +} diff --git a/test/qr_colpivoting.cpp b/test/qr_colpivoting.cpp new file mode 100644 index 000000000..dd0812819 --- /dev/null +++ b/test/qr_colpivoting.cpp @@ -0,0 +1,150 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/QR> + +template<typename MatrixType> void qr() +{ + typedef typename MatrixType::Index Index; + + Index rows = internal::random<Index>(2,EIGEN_TEST_MAX_SIZE), cols = internal::random<Index>(2,EIGEN_TEST_MAX_SIZE), cols2 = internal::random<Index>(2,EIGEN_TEST_MAX_SIZE); + Index rank = internal::random<Index>(1, (std::min)(rows, cols)-1); + + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> MatrixQType; + typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, 1> VectorType; + MatrixType m1; + createRandomPIMatrixOfRank(rank,rows,cols,m1); + ColPivHouseholderQR<MatrixType> qr(m1); + VERIFY(rank == qr.rank()); + VERIFY(cols - qr.rank() == qr.dimensionOfKernel()); + VERIFY(!qr.isInjective()); + VERIFY(!qr.isInvertible()); + VERIFY(!qr.isSurjective()); + + MatrixQType q = qr.householderQ(); + VERIFY_IS_UNITARY(q); + + MatrixType r = qr.matrixQR().template triangularView<Upper>(); + MatrixType c = q * r * qr.colsPermutation().inverse(); + VERIFY_IS_APPROX(m1, c); + + MatrixType m2 = MatrixType::Random(cols,cols2); + MatrixType m3 = m1*m2; + m2 = MatrixType::Random(cols,cols2); + m2 = qr.solve(m3); + VERIFY_IS_APPROX(m3, m1*m2); +} + +template<typename MatrixType, int Cols2> void qr_fixedsize() +{ + enum { Rows = MatrixType::RowsAtCompileTime, Cols = MatrixType::ColsAtCompileTime }; + typedef typename MatrixType::Scalar Scalar; + int rank = internal::random<int>(1, (std::min)(int(Rows), int(Cols))-1); + Matrix<Scalar,Rows,Cols> m1; + createRandomPIMatrixOfRank(rank,Rows,Cols,m1); + ColPivHouseholderQR<Matrix<Scalar,Rows,Cols> > qr(m1); + VERIFY(rank == qr.rank()); + VERIFY(Cols - qr.rank() == qr.dimensionOfKernel()); + VERIFY(qr.isInjective() == (rank == Rows)); + VERIFY(qr.isSurjective() == (rank == Cols)); + VERIFY(qr.isInvertible() == (qr.isInjective() && qr.isSurjective())); + + Matrix<Scalar,Rows,Cols> r = qr.matrixQR().template triangularView<Upper>(); + Matrix<Scalar,Rows,Cols> c = qr.householderQ() * r * qr.colsPermutation().inverse(); + VERIFY_IS_APPROX(m1, c); + + Matrix<Scalar,Cols,Cols2> m2 = Matrix<Scalar,Cols,Cols2>::Random(Cols,Cols2); + Matrix<Scalar,Rows,Cols2> m3 = m1*m2; + m2 = Matrix<Scalar,Cols,Cols2>::Random(Cols,Cols2); + m2 = qr.solve(m3); + VERIFY_IS_APPROX(m3, m1*m2); +} + +template<typename MatrixType> void qr_invertible() +{ + typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar; + typedef typename MatrixType::Scalar Scalar; + + int size = internal::random<int>(10,50); + + MatrixType m1(size, size), m2(size, size), m3(size, size); + m1 = MatrixType::Random(size,size); + + if (internal::is_same<RealScalar,float>::value) + { + // let's build a matrix more stable to inverse + MatrixType a = MatrixType::Random(size,size*2); + m1 += a * a.adjoint(); + } + + ColPivHouseholderQR<MatrixType> qr(m1); + m3 = MatrixType::Random(size,size); + m2 = qr.solve(m3); + //VERIFY_IS_APPROX(m3, m1*m2); + + // now construct a matrix with prescribed determinant + m1.setZero(); + for(int i = 0; i < size; i++) m1(i,i) = internal::random<Scalar>(); + RealScalar absdet = internal::abs(m1.diagonal().prod()); + m3 = qr.householderQ(); // get a unitary + m1 = m3 * m1 * m3; + qr.compute(m1); + VERIFY_IS_APPROX(absdet, qr.absDeterminant()); + VERIFY_IS_APPROX(internal::log(absdet), qr.logAbsDeterminant()); +} + +template<typename MatrixType> void qr_verify_assert() +{ + MatrixType tmp; + + ColPivHouseholderQR<MatrixType> qr; + VERIFY_RAISES_ASSERT(qr.matrixQR()) + VERIFY_RAISES_ASSERT(qr.solve(tmp)) + VERIFY_RAISES_ASSERT(qr.householderQ()) + VERIFY_RAISES_ASSERT(qr.dimensionOfKernel()) + VERIFY_RAISES_ASSERT(qr.isInjective()) + VERIFY_RAISES_ASSERT(qr.isSurjective()) + VERIFY_RAISES_ASSERT(qr.isInvertible()) + VERIFY_RAISES_ASSERT(qr.inverse()) + VERIFY_RAISES_ASSERT(qr.absDeterminant()) + VERIFY_RAISES_ASSERT(qr.logAbsDeterminant()) +} + +void test_qr_colpivoting() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( qr<MatrixXf>() ); + CALL_SUBTEST_2( qr<MatrixXd>() ); + CALL_SUBTEST_3( qr<MatrixXcd>() ); + CALL_SUBTEST_4(( qr_fixedsize<Matrix<float,3,5>, 4 >() )); + CALL_SUBTEST_5(( qr_fixedsize<Matrix<double,6,2>, 3 >() )); + CALL_SUBTEST_5(( qr_fixedsize<Matrix<double,1,1>, 1 >() )); + } + + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( qr_invertible<MatrixXf>() ); + CALL_SUBTEST_2( qr_invertible<MatrixXd>() ); + CALL_SUBTEST_6( qr_invertible<MatrixXcf>() ); + CALL_SUBTEST_3( qr_invertible<MatrixXcd>() ); + } + + CALL_SUBTEST_7(qr_verify_assert<Matrix3f>()); + CALL_SUBTEST_8(qr_verify_assert<Matrix3d>()); + CALL_SUBTEST_1(qr_verify_assert<MatrixXf>()); + CALL_SUBTEST_2(qr_verify_assert<MatrixXd>()); + CALL_SUBTEST_6(qr_verify_assert<MatrixXcf>()); + CALL_SUBTEST_3(qr_verify_assert<MatrixXcd>()); + + // Test problem size constructors + CALL_SUBTEST_9(ColPivHouseholderQR<MatrixXf>(10, 20)); +} diff --git a/test/qr_fullpivoting.cpp b/test/qr_fullpivoting.cpp new file mode 100644 index 000000000..e5c9790c8 --- /dev/null +++ b/test/qr_fullpivoting.cpp @@ -0,0 +1,132 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/QR> + +template<typename MatrixType> void qr() +{ + typedef typename MatrixType::Index Index; + + Index rows = internal::random<Index>(20,200), cols = internal::random<int>(20,200), cols2 = internal::random<int>(20,200); + Index rank = internal::random<Index>(1, (std::min)(rows, cols)-1); + + typedef typename MatrixType::Scalar Scalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> MatrixQType; + typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, 1> VectorType; + MatrixType m1; + createRandomPIMatrixOfRank(rank,rows,cols,m1); + FullPivHouseholderQR<MatrixType> qr(m1); + VERIFY(rank == qr.rank()); + VERIFY(cols - qr.rank() == qr.dimensionOfKernel()); + VERIFY(!qr.isInjective()); + VERIFY(!qr.isInvertible()); + VERIFY(!qr.isSurjective()); + + MatrixType r = qr.matrixQR(); + + MatrixQType q = qr.matrixQ(); + VERIFY_IS_UNITARY(q); + + // FIXME need better way to construct trapezoid + for(int i = 0; i < rows; i++) for(int j = 0; j < cols; j++) if(i>j) r(i,j) = Scalar(0); + + MatrixType c = qr.matrixQ() * r * qr.colsPermutation().inverse(); + + VERIFY_IS_APPROX(m1, c); + + MatrixType m2 = MatrixType::Random(cols,cols2); + MatrixType m3 = m1*m2; + m2 = MatrixType::Random(cols,cols2); + m2 = qr.solve(m3); + VERIFY_IS_APPROX(m3, m1*m2); +} + +template<typename MatrixType> void qr_invertible() +{ + typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar; + typedef typename MatrixType::Scalar Scalar; + + int size = internal::random<int>(10,50); + + MatrixType m1(size, size), m2(size, size), m3(size, size); + m1 = MatrixType::Random(size,size); + + if (internal::is_same<RealScalar,float>::value) + { + // let's build a matrix more stable to inverse + MatrixType a = MatrixType::Random(size,size*2); + m1 += a * a.adjoint(); + } + + FullPivHouseholderQR<MatrixType> qr(m1); + VERIFY(qr.isInjective()); + VERIFY(qr.isInvertible()); + VERIFY(qr.isSurjective()); + + m3 = MatrixType::Random(size,size); + m2 = qr.solve(m3); + VERIFY_IS_APPROX(m3, m1*m2); + + // now construct a matrix with prescribed determinant + m1.setZero(); + for(int i = 0; i < size; i++) m1(i,i) = internal::random<Scalar>(); + RealScalar absdet = internal::abs(m1.diagonal().prod()); + m3 = qr.matrixQ(); // get a unitary + m1 = m3 * m1 * m3; + qr.compute(m1); + VERIFY_IS_APPROX(absdet, qr.absDeterminant()); + VERIFY_IS_APPROX(internal::log(absdet), qr.logAbsDeterminant()); +} + +template<typename MatrixType> void qr_verify_assert() +{ + MatrixType tmp; + + FullPivHouseholderQR<MatrixType> qr; + VERIFY_RAISES_ASSERT(qr.matrixQR()) + VERIFY_RAISES_ASSERT(qr.solve(tmp)) + VERIFY_RAISES_ASSERT(qr.matrixQ()) + VERIFY_RAISES_ASSERT(qr.dimensionOfKernel()) + VERIFY_RAISES_ASSERT(qr.isInjective()) + VERIFY_RAISES_ASSERT(qr.isSurjective()) + VERIFY_RAISES_ASSERT(qr.isInvertible()) + VERIFY_RAISES_ASSERT(qr.inverse()) + VERIFY_RAISES_ASSERT(qr.absDeterminant()) + VERIFY_RAISES_ASSERT(qr.logAbsDeterminant()) +} + +void test_qr_fullpivoting() +{ + for(int i = 0; i < 1; i++) { + // FIXME : very weird bug here +// CALL_SUBTEST(qr(Matrix2f()) ); + CALL_SUBTEST_1( qr<MatrixXf>() ); + CALL_SUBTEST_2( qr<MatrixXd>() ); + CALL_SUBTEST_3( qr<MatrixXcd>() ); + } + + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( qr_invertible<MatrixXf>() ); + CALL_SUBTEST_2( qr_invertible<MatrixXd>() ); + CALL_SUBTEST_4( qr_invertible<MatrixXcf>() ); + CALL_SUBTEST_3( qr_invertible<MatrixXcd>() ); + } + + CALL_SUBTEST_5(qr_verify_assert<Matrix3f>()); + CALL_SUBTEST_6(qr_verify_assert<Matrix3d>()); + CALL_SUBTEST_1(qr_verify_assert<MatrixXf>()); + CALL_SUBTEST_2(qr_verify_assert<MatrixXd>()); + CALL_SUBTEST_4(qr_verify_assert<MatrixXcf>()); + CALL_SUBTEST_3(qr_verify_assert<MatrixXcd>()); + + // Test problem size constructors + CALL_SUBTEST_7(FullPivHouseholderQR<MatrixXf>(10, 20)); +} diff --git a/test/qtvector.cpp b/test/qtvector.cpp new file mode 100644 index 000000000..2be885e48 --- /dev/null +++ b/test/qtvector.cpp @@ -0,0 +1,158 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#define EIGEN_WORK_AROUND_QT_BUG_CALLING_WRONG_OPERATOR_NEW_FIXED_IN_QT_4_5 + +#include "main.h" +#include <QtCore/QVector> +#include <Eigen/Geometry> +#include <Eigen/QtAlignedMalloc> + +template<typename MatrixType> +void check_qtvector_matrix(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + + Index rows = m.rows(); + Index cols = m.cols(); + MatrixType x = MatrixType::Random(rows,cols), y = MatrixType::Random(rows,cols); + QVector<MatrixType> v(10, MatrixType(rows,cols)), w(20, y); + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], y); + } + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.fill(y,22); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((size_t)&(v[22]) == (size_t)&(v[21]) + sizeof(MatrixType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + MatrixType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(int i=23; i<v.size(); ++i) + { + VERIFY(v[i]==w[(i-23)%w.size()]); + } +} + +template<typename TransformType> +void check_qtvector_transform(const TransformType&) +{ + typedef typename TransformType::MatrixType MatrixType; + TransformType x(MatrixType::Random()), y(MatrixType::Random()); + QVector<TransformType> v(10), w(20, y); + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.fill(y,22); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((size_t)&(v[22]) == (size_t)&(v[21]) + sizeof(TransformType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + TransformType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(unsigned int i=23; int(i)<v.size(); ++i) + { + VERIFY(v[i].matrix()==w[(i-23)%w.size()].matrix()); + } +} + +template<typename QuaternionType> +void check_qtvector_quaternion(const QuaternionType&) +{ + typedef typename QuaternionType::Coefficients Coefficients; + QuaternionType x(Coefficients::Random()), y(Coefficients::Random()); + QVector<QuaternionType> v(10), w(20, y); + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.fill(y,22); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((size_t)&(v[22]) == (size_t)&(v[21]) + sizeof(QuaternionType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + QuaternionType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(unsigned int i=23; int(i)<v.size(); ++i) + { + VERIFY(v[i].coeffs()==w[(i-23)%w.size()].coeffs()); + } +} + +void test_qtvector() +{ + // some non vectorizable fixed sizes + CALL_SUBTEST(check_qtvector_matrix(Vector2f())); + CALL_SUBTEST(check_qtvector_matrix(Matrix3f())); + CALL_SUBTEST(check_qtvector_matrix(Matrix3d())); + + // some vectorizable fixed sizes + CALL_SUBTEST(check_qtvector_matrix(Matrix2f())); + CALL_SUBTEST(check_qtvector_matrix(Vector4f())); + CALL_SUBTEST(check_qtvector_matrix(Matrix4f())); + CALL_SUBTEST(check_qtvector_matrix(Matrix4d())); + + // some dynamic sizes + CALL_SUBTEST(check_qtvector_matrix(MatrixXd(1,1))); + CALL_SUBTEST(check_qtvector_matrix(VectorXd(20))); + CALL_SUBTEST(check_qtvector_matrix(RowVectorXf(20))); + CALL_SUBTEST(check_qtvector_matrix(MatrixXcf(10,10))); + + // some Transform + CALL_SUBTEST(check_qtvector_transform(Affine2f())); + CALL_SUBTEST(check_qtvector_transform(Affine3f())); + CALL_SUBTEST(check_qtvector_transform(Affine3d())); + //CALL_SUBTEST(check_qtvector_transform(Transform4d())); + + // some Quaternion + CALL_SUBTEST(check_qtvector_quaternion(Quaternionf())); + CALL_SUBTEST(check_qtvector_quaternion(Quaternionf())); +} diff --git a/test/redux.cpp b/test/redux.cpp new file mode 100644 index 000000000..e07d4b1e4 --- /dev/null +++ b/test/redux.cpp @@ -0,0 +1,158 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename MatrixType> void matrixRedux(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols); + + // The entries of m1 are uniformly distributed in [0,1], so m1.prod() is very small. This may lead to test + // failures if we underflow into denormals. Thus, we scale so that entires are close to 1. + MatrixType m1_for_prod = MatrixType::Ones(rows, cols) + Scalar(0.2) * m1; + + VERIFY_IS_MUCH_SMALLER_THAN(MatrixType::Zero(rows, cols).sum(), Scalar(1)); + VERIFY_IS_APPROX(MatrixType::Ones(rows, cols).sum(), Scalar(float(rows*cols))); // the float() here to shut up excessive MSVC warning about int->complex conversion being lossy + Scalar s(0), p(1), minc(internal::real(m1.coeff(0))), maxc(internal::real(m1.coeff(0))); + for(int j = 0; j < cols; j++) + for(int i = 0; i < rows; i++) + { + s += m1(i,j); + p *= m1_for_prod(i,j); + minc = (std::min)(internal::real(minc), internal::real(m1(i,j))); + maxc = (std::max)(internal::real(maxc), internal::real(m1(i,j))); + } + const Scalar mean = s/Scalar(RealScalar(rows*cols)); + + VERIFY_IS_APPROX(m1.sum(), s); + VERIFY_IS_APPROX(m1.mean(), mean); + VERIFY_IS_APPROX(m1_for_prod.prod(), p); + VERIFY_IS_APPROX(m1.real().minCoeff(), internal::real(minc)); + VERIFY_IS_APPROX(m1.real().maxCoeff(), internal::real(maxc)); + + // test slice vectorization assuming assign is ok + Index r0 = internal::random<Index>(0,rows-1); + Index c0 = internal::random<Index>(0,cols-1); + Index r1 = internal::random<Index>(r0+1,rows)-r0; + Index c1 = internal::random<Index>(c0+1,cols)-c0; + VERIFY_IS_APPROX(m1.block(r0,c0,r1,c1).sum(), m1.block(r0,c0,r1,c1).eval().sum()); + VERIFY_IS_APPROX(m1.block(r0,c0,r1,c1).mean(), m1.block(r0,c0,r1,c1).eval().mean()); + VERIFY_IS_APPROX(m1_for_prod.block(r0,c0,r1,c1).prod(), m1_for_prod.block(r0,c0,r1,c1).eval().prod()); + VERIFY_IS_APPROX(m1.block(r0,c0,r1,c1).real().minCoeff(), m1.block(r0,c0,r1,c1).real().eval().minCoeff()); + VERIFY_IS_APPROX(m1.block(r0,c0,r1,c1).real().maxCoeff(), m1.block(r0,c0,r1,c1).real().eval().maxCoeff()); + + // test empty objects + VERIFY_IS_APPROX(m1.block(r0,c0,0,0).sum(), Scalar(0)); + VERIFY_IS_APPROX(m1.block(r0,c0,0,0).prod(), Scalar(1)); +} + +template<typename VectorType> void vectorRedux(const VectorType& w) +{ + typedef typename VectorType::Index Index; + typedef typename VectorType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + Index size = w.size(); + + VectorType v = VectorType::Random(size); + VectorType v_for_prod = VectorType::Ones(size) + Scalar(0.2) * v; // see comment above declaration of m1_for_prod + + for(int i = 1; i < size; i++) + { + Scalar s(0), p(1); + RealScalar minc(internal::real(v.coeff(0))), maxc(internal::real(v.coeff(0))); + for(int j = 0; j < i; j++) + { + s += v[j]; + p *= v_for_prod[j]; + minc = (std::min)(minc, internal::real(v[j])); + maxc = (std::max)(maxc, internal::real(v[j])); + } + VERIFY_IS_MUCH_SMALLER_THAN(internal::abs(s - v.head(i).sum()), Scalar(1)); + VERIFY_IS_APPROX(p, v_for_prod.head(i).prod()); + VERIFY_IS_APPROX(minc, v.real().head(i).minCoeff()); + VERIFY_IS_APPROX(maxc, v.real().head(i).maxCoeff()); + } + + for(int i = 0; i < size-1; i++) + { + Scalar s(0), p(1); + RealScalar minc(internal::real(v.coeff(i))), maxc(internal::real(v.coeff(i))); + for(int j = i; j < size; j++) + { + s += v[j]; + p *= v_for_prod[j]; + minc = (std::min)(minc, internal::real(v[j])); + maxc = (std::max)(maxc, internal::real(v[j])); + } + VERIFY_IS_MUCH_SMALLER_THAN(internal::abs(s - v.tail(size-i).sum()), Scalar(1)); + VERIFY_IS_APPROX(p, v_for_prod.tail(size-i).prod()); + VERIFY_IS_APPROX(minc, v.real().tail(size-i).minCoeff()); + VERIFY_IS_APPROX(maxc, v.real().tail(size-i).maxCoeff()); + } + + for(int i = 0; i < size/2; i++) + { + Scalar s(0), p(1); + RealScalar minc(internal::real(v.coeff(i))), maxc(internal::real(v.coeff(i))); + for(int j = i; j < size-i; j++) + { + s += v[j]; + p *= v_for_prod[j]; + minc = (std::min)(minc, internal::real(v[j])); + maxc = (std::max)(maxc, internal::real(v[j])); + } + VERIFY_IS_MUCH_SMALLER_THAN(internal::abs(s - v.segment(i, size-2*i).sum()), Scalar(1)); + VERIFY_IS_APPROX(p, v_for_prod.segment(i, size-2*i).prod()); + VERIFY_IS_APPROX(minc, v.real().segment(i, size-2*i).minCoeff()); + VERIFY_IS_APPROX(maxc, v.real().segment(i, size-2*i).maxCoeff()); + } + + // test empty objects + VERIFY_IS_APPROX(v.head(0).sum(), Scalar(0)); + VERIFY_IS_APPROX(v.tail(0).prod(), Scalar(1)); + VERIFY_RAISES_ASSERT(v.head(0).mean()); + VERIFY_RAISES_ASSERT(v.head(0).minCoeff()); + VERIFY_RAISES_ASSERT(v.head(0).maxCoeff()); +} + +void test_redux() +{ + // the max size cannot be too large, otherwise reduxion operations obviously generate large errors. + int maxsize = (std::min)(100,EIGEN_TEST_MAX_SIZE); + EIGEN_UNUSED_VARIABLE(maxsize); + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( matrixRedux(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_1( matrixRedux(Array<float, 1, 1>()) ); + CALL_SUBTEST_2( matrixRedux(Matrix2f()) ); + CALL_SUBTEST_2( matrixRedux(Array2f()) ); + CALL_SUBTEST_3( matrixRedux(Matrix4d()) ); + CALL_SUBTEST_3( matrixRedux(Array4d()) ); + CALL_SUBTEST_4( matrixRedux(MatrixXcf(internal::random<int>(1,maxsize), internal::random<int>(1,maxsize))) ); + CALL_SUBTEST_4( matrixRedux(ArrayXXcf(internal::random<int>(1,maxsize), internal::random<int>(1,maxsize))) ); + CALL_SUBTEST_5( matrixRedux(MatrixXd (internal::random<int>(1,maxsize), internal::random<int>(1,maxsize))) ); + CALL_SUBTEST_5( matrixRedux(ArrayXXd (internal::random<int>(1,maxsize), internal::random<int>(1,maxsize))) ); + CALL_SUBTEST_6( matrixRedux(MatrixXi (internal::random<int>(1,maxsize), internal::random<int>(1,maxsize))) ); + CALL_SUBTEST_6( matrixRedux(ArrayXXi (internal::random<int>(1,maxsize), internal::random<int>(1,maxsize))) ); + } + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_7( vectorRedux(Vector4f()) ); + CALL_SUBTEST_7( vectorRedux(Array4f()) ); + CALL_SUBTEST_5( vectorRedux(VectorXd(internal::random<int>(1,maxsize))) ); + CALL_SUBTEST_5( vectorRedux(ArrayXd(internal::random<int>(1,maxsize))) ); + CALL_SUBTEST_8( vectorRedux(VectorXf(internal::random<int>(1,maxsize))) ); + CALL_SUBTEST_8( vectorRedux(ArrayXf(internal::random<int>(1,maxsize))) ); + } +} diff --git a/test/resize.cpp b/test/resize.cpp new file mode 100644 index 000000000..4adaafe56 --- /dev/null +++ b/test/resize.cpp @@ -0,0 +1,41 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Keir Mierle <mierle@gmail.com> +// +// 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<DenseIndex rows, DenseIndex cols> +void resizeLikeTest() +{ + MatrixXf A(rows, cols); + MatrixXf B; + Matrix<double, rows, cols> C; + B.resizeLike(A); + C.resizeLike(B); // Shouldn't crash. + VERIFY(B.rows() == rows && B.cols() == cols); + + VectorXf x(rows); + RowVectorXf y; + y.resizeLike(x); + VERIFY(y.rows() == 1 && y.cols() == rows); + + y.resize(cols); + x.resizeLike(y); + VERIFY(x.rows() == cols && x.cols() == 1); +} + +void resizeLikeTest12() { resizeLikeTest<1,2>(); } +void resizeLikeTest1020() { resizeLikeTest<10,20>(); } +void resizeLikeTest31() { resizeLikeTest<3,1>(); } + +void test_resize() +{ + CALL_SUBTEST(resizeLikeTest12() ); + CALL_SUBTEST(resizeLikeTest1020() ); + CALL_SUBTEST(resizeLikeTest31() ); +} diff --git a/test/runtest.sh b/test/runtest.sh new file mode 100755 index 000000000..2be250819 --- /dev/null +++ b/test/runtest.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +black='\E[30m' +red='\E[31m' +green='\E[32m' +yellow='\E[33m' +blue='\E[34m' +magenta='\E[35m' +cyan='\E[36m' +white='\E[37m' + +if ! ./$1 > /dev/null 2> .runtest.log ; then + echo -e $red Test $1 failed: $black + echo -e $blue + cat .runtest.log + echo -e $black + exit 1 +else +echo -e $green Test $1 passed$black +fi diff --git a/test/schur_complex.cpp b/test/schur_complex.cpp new file mode 100644 index 000000000..a6f66ab02 --- /dev/null +++ b/test/schur_complex.cpp @@ -0,0 +1,74 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2010 Jitse Niesen <jitse@maths.leeds.ac.uk> +// +// 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" +#include <limits> +#include <Eigen/Eigenvalues> + +template<typename MatrixType> void schur(int size = MatrixType::ColsAtCompileTime) +{ + typedef typename ComplexSchur<MatrixType>::ComplexScalar ComplexScalar; + typedef typename ComplexSchur<MatrixType>::ComplexMatrixType ComplexMatrixType; + + // Test basic functionality: T is triangular and A = U T U* + for(int counter = 0; counter < g_repeat; ++counter) { + MatrixType A = MatrixType::Random(size, size); + ComplexSchur<MatrixType> schurOfA(A); + VERIFY_IS_EQUAL(schurOfA.info(), Success); + ComplexMatrixType U = schurOfA.matrixU(); + ComplexMatrixType T = schurOfA.matrixT(); + for(int row = 1; row < size; ++row) { + for(int col = 0; col < row; ++col) { + VERIFY(T(row,col) == (typename MatrixType::Scalar)0); + } + } + VERIFY_IS_APPROX(A.template cast<ComplexScalar>(), U * T * U.adjoint()); + } + + // Test asserts when not initialized + ComplexSchur<MatrixType> csUninitialized; + VERIFY_RAISES_ASSERT(csUninitialized.matrixT()); + VERIFY_RAISES_ASSERT(csUninitialized.matrixU()); + VERIFY_RAISES_ASSERT(csUninitialized.info()); + + // Test whether compute() and constructor returns same result + MatrixType A = MatrixType::Random(size, size); + ComplexSchur<MatrixType> cs1; + cs1.compute(A); + ComplexSchur<MatrixType> cs2(A); + VERIFY_IS_EQUAL(cs1.info(), Success); + VERIFY_IS_EQUAL(cs2.info(), Success); + VERIFY_IS_EQUAL(cs1.matrixT(), cs2.matrixT()); + VERIFY_IS_EQUAL(cs1.matrixU(), cs2.matrixU()); + + // Test computation of only T, not U + ComplexSchur<MatrixType> csOnlyT(A, false); + VERIFY_IS_EQUAL(csOnlyT.info(), Success); + VERIFY_IS_EQUAL(cs1.matrixT(), csOnlyT.matrixT()); + VERIFY_RAISES_ASSERT(csOnlyT.matrixU()); + + if (size > 1) + { + // Test matrix with NaN + A(0,0) = std::numeric_limits<typename MatrixType::RealScalar>::quiet_NaN(); + ComplexSchur<MatrixType> csNaN(A); + VERIFY_IS_EQUAL(csNaN.info(), NoConvergence); + } +} + +void test_schur_complex() +{ + CALL_SUBTEST_1(( schur<Matrix4cd>() )); + CALL_SUBTEST_2(( schur<MatrixXcf>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/4)) )); + CALL_SUBTEST_3(( schur<Matrix<std::complex<float>, 1, 1> >() )); + CALL_SUBTEST_4(( schur<Matrix<float, 3, 3, Eigen::RowMajor> >() )); + + // Test problem size constructors + CALL_SUBTEST_5(ComplexSchur<MatrixXf>(10)); +} diff --git a/test/schur_real.cpp b/test/schur_real.cpp new file mode 100644 index 000000000..e6351d94a --- /dev/null +++ b/test/schur_real.cpp @@ -0,0 +1,93 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2010 Jitse Niesen <jitse@maths.leeds.ac.uk> +// +// 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" +#include <limits> +#include <Eigen/Eigenvalues> + +template<typename MatrixType> void verifyIsQuasiTriangular(const MatrixType& T) +{ + typedef typename MatrixType::Index Index; + + const Index size = T.cols(); + typedef typename MatrixType::Scalar Scalar; + + // Check T is lower Hessenberg + for(int row = 2; row < size; ++row) { + for(int col = 0; col < row - 1; ++col) { + VERIFY(T(row,col) == Scalar(0)); + } + } + + // Check that any non-zero on the subdiagonal is followed by a zero and is + // part of a 2x2 diagonal block with imaginary eigenvalues. + for(int row = 1; row < size; ++row) { + if (T(row,row-1) != Scalar(0)) { + VERIFY(row == size-1 || T(row+1,row) == 0); + Scalar tr = T(row-1,row-1) + T(row,row); + Scalar det = T(row-1,row-1) * T(row,row) - T(row-1,row) * T(row,row-1); + VERIFY(4 * det > tr * tr); + } + } +} + +template<typename MatrixType> void schur(int size = MatrixType::ColsAtCompileTime) +{ + // Test basic functionality: T is quasi-triangular and A = U T U* + for(int counter = 0; counter < g_repeat; ++counter) { + MatrixType A = MatrixType::Random(size, size); + RealSchur<MatrixType> schurOfA(A); + VERIFY_IS_EQUAL(schurOfA.info(), Success); + MatrixType U = schurOfA.matrixU(); + MatrixType T = schurOfA.matrixT(); + verifyIsQuasiTriangular(T); + VERIFY_IS_APPROX(A, U * T * U.transpose()); + } + + // Test asserts when not initialized + RealSchur<MatrixType> rsUninitialized; + VERIFY_RAISES_ASSERT(rsUninitialized.matrixT()); + VERIFY_RAISES_ASSERT(rsUninitialized.matrixU()); + VERIFY_RAISES_ASSERT(rsUninitialized.info()); + + // Test whether compute() and constructor returns same result + MatrixType A = MatrixType::Random(size, size); + RealSchur<MatrixType> rs1; + rs1.compute(A); + RealSchur<MatrixType> rs2(A); + VERIFY_IS_EQUAL(rs1.info(), Success); + VERIFY_IS_EQUAL(rs2.info(), Success); + VERIFY_IS_EQUAL(rs1.matrixT(), rs2.matrixT()); + VERIFY_IS_EQUAL(rs1.matrixU(), rs2.matrixU()); + + // Test computation of only T, not U + RealSchur<MatrixType> rsOnlyT(A, false); + VERIFY_IS_EQUAL(rsOnlyT.info(), Success); + VERIFY_IS_EQUAL(rs1.matrixT(), rsOnlyT.matrixT()); + VERIFY_RAISES_ASSERT(rsOnlyT.matrixU()); + + if (size > 2) + { + // Test matrix with NaN + A(0,0) = std::numeric_limits<typename MatrixType::Scalar>::quiet_NaN(); + RealSchur<MatrixType> rsNaN(A); + VERIFY_IS_EQUAL(rsNaN.info(), NoConvergence); + } +} + +void test_schur_real() +{ + CALL_SUBTEST_1(( schur<Matrix4f>() )); + CALL_SUBTEST_2(( schur<MatrixXd>(internal::random<int>(1,EIGEN_TEST_MAX_SIZE/4)) )); + CALL_SUBTEST_3(( schur<Matrix<float, 1, 1> >() )); + CALL_SUBTEST_4(( schur<Matrix<double, 3, 3, Eigen::RowMajor> >() )); + + // Test problem size constructors + CALL_SUBTEST_5(RealSchur<MatrixXf>(10)); +} diff --git a/test/selfadjoint.cpp b/test/selfadjoint.cpp new file mode 100644 index 000000000..6d3ec6536 --- /dev/null +++ b/test/selfadjoint.cpp @@ -0,0 +1,62 @@ +// This file is triangularView of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2010 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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" + +// This file tests the basic selfadjointView API, +// the related products and decompositions are tested in specific files. + +template<typename MatrixType> void selfadjoint(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m3(rows, cols); + + m1.diagonal() = m1.diagonal().real().template cast<Scalar>(); + + // check selfadjoint to dense + m3 = m1.template selfadjointView<Upper>(); + VERIFY_IS_APPROX(MatrixType(m3.template triangularView<Upper>()), MatrixType(m1.template triangularView<Upper>())); + VERIFY_IS_APPROX(m3, m3.adjoint()); + + + m3 = m1.template selfadjointView<Lower>(); + VERIFY_IS_APPROX(MatrixType(m3.template triangularView<Lower>()), MatrixType(m1.template triangularView<Lower>())); + VERIFY_IS_APPROX(m3, m3.adjoint()); +} + +void bug_159() +{ + Matrix3d m = Matrix3d::Random().selfadjointView<Lower>(); + EIGEN_UNUSED_VARIABLE(m) +} + +void test_selfadjoint() +{ + for(int i = 0; i < g_repeat ; i++) + { + int s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE); EIGEN_UNUSED_VARIABLE(s); + + CALL_SUBTEST_1( selfadjoint(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( selfadjoint(Matrix<float, 2, 2>()) ); + CALL_SUBTEST_3( selfadjoint(Matrix3cf()) ); + CALL_SUBTEST_4( selfadjoint(MatrixXcd(s,s)) ); + CALL_SUBTEST_5( selfadjoint(Matrix<float,Dynamic,Dynamic,RowMajor>(s, s)) ); + + EIGEN_UNUSED_VARIABLE(s) + } + + CALL_SUBTEST_1( bug_159() ); +} diff --git a/test/simplicial_cholesky.cpp b/test/simplicial_cholesky.cpp new file mode 100644 index 000000000..e93a52e9c --- /dev/null +++ b/test/simplicial_cholesky.cpp @@ -0,0 +1,40 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Gael Guennebaud <g.gael@free.fr> +// +// 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 "sparse_solver.h" + +template<typename T> void test_simplicial_cholesky_T() +{ + SimplicialCholesky<SparseMatrix<T>, Lower> chol_colmajor_lower; + SimplicialCholesky<SparseMatrix<T>, Upper> chol_colmajor_upper; + SimplicialLLT<SparseMatrix<T>, Lower> llt_colmajor_lower; + SimplicialLDLT<SparseMatrix<T>, Upper> llt_colmajor_upper; + SimplicialLDLT<SparseMatrix<T>, Lower> ldlt_colmajor_lower; + SimplicialLDLT<SparseMatrix<T>, Upper> ldlt_colmajor_upper; + + check_sparse_spd_solving(chol_colmajor_lower); + check_sparse_spd_solving(chol_colmajor_upper); + check_sparse_spd_solving(llt_colmajor_lower); + check_sparse_spd_solving(llt_colmajor_upper); + check_sparse_spd_solving(ldlt_colmajor_lower); + check_sparse_spd_solving(ldlt_colmajor_upper); + + check_sparse_spd_determinant(chol_colmajor_lower); + check_sparse_spd_determinant(chol_colmajor_upper); + check_sparse_spd_determinant(llt_colmajor_lower); + check_sparse_spd_determinant(llt_colmajor_upper); + check_sparse_spd_determinant(ldlt_colmajor_lower); + check_sparse_spd_determinant(ldlt_colmajor_upper); +} + +void test_simplicial_cholesky() +{ + CALL_SUBTEST_1(test_simplicial_cholesky_T<double>()); + CALL_SUBTEST_2(test_simplicial_cholesky_T<std::complex<double> >()); +} diff --git a/test/sizeof.cpp b/test/sizeof.cpp new file mode 100644 index 000000000..68463c9b6 --- /dev/null +++ b/test/sizeof.cpp @@ -0,0 +1,34 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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<typename MatrixType> void verifySizeOf(const MatrixType&) +{ + typedef typename MatrixType::Scalar Scalar; + if (MatrixType::RowsAtCompileTime!=Dynamic && MatrixType::ColsAtCompileTime!=Dynamic) + VERIFY(sizeof(MatrixType)==sizeof(Scalar)*size_t(MatrixType::SizeAtCompileTime)); + else + VERIFY(sizeof(MatrixType)==sizeof(Scalar*) + 2 * sizeof(typename MatrixType::Index)); +} + +void test_sizeof() +{ + CALL_SUBTEST(verifySizeOf(Matrix<float, 1, 1>()) ); + CALL_SUBTEST(verifySizeOf(Matrix4d()) ); + CALL_SUBTEST(verifySizeOf(Matrix<double, 4, 2>()) ); + CALL_SUBTEST(verifySizeOf(Matrix<bool, 7, 5>()) ); + CALL_SUBTEST(verifySizeOf(MatrixXcf(3, 3)) ); + CALL_SUBTEST(verifySizeOf(MatrixXi(8, 12)) ); + CALL_SUBTEST(verifySizeOf(MatrixXcd(20, 20)) ); + CALL_SUBTEST(verifySizeOf(Matrix<float, 100, 100>()) ); + + VERIFY(sizeof(std::complex<float>) == 2*sizeof(float)); + VERIFY(sizeof(std::complex<double>) == 2*sizeof(double)); +} diff --git a/test/sizeoverflow.cpp b/test/sizeoverflow.cpp new file mode 100644 index 000000000..16d6f8d04 --- /dev/null +++ b/test/sizeoverflow.cpp @@ -0,0 +1,66 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" + +#define VERIFY_THROWS_BADALLOC(a) { \ + bool threw = false; \ + try { \ + a; \ + } \ + catch (std::bad_alloc&) { threw = true; } \ + VERIFY(threw && "should have thrown bad_alloc: " #a); \ + } + +typedef DenseIndex Index; + +template<typename MatrixType> +void triggerMatrixBadAlloc(Index rows, Index cols) +{ + VERIFY_THROWS_BADALLOC( MatrixType m(rows, cols) ); + VERIFY_THROWS_BADALLOC( MatrixType m; m.resize(rows, cols) ); + VERIFY_THROWS_BADALLOC( MatrixType m; m.conservativeResize(rows, cols) ); +} + +template<typename VectorType> +void triggerVectorBadAlloc(Index size) +{ + VERIFY_THROWS_BADALLOC( VectorType v(size) ); + VERIFY_THROWS_BADALLOC( VectorType v; v.resize(size) ); + VERIFY_THROWS_BADALLOC( VectorType v; v.conservativeResize(size) ); +} + +void test_sizeoverflow() +{ + // there are 2 levels of overflow checking. first in PlainObjectBase.h we check for overflow in rows*cols computations. + // this is tested in tests of the form times_itself_gives_0 * times_itself_gives_0 + // Then in Memory.h we check for overflow in size * sizeof(T) computations. + // this is tested in tests of the form times_4_gives_0 * sizeof(float) + + size_t times_itself_gives_0 = size_t(1) << (8 * sizeof(Index) / 2); + VERIFY(times_itself_gives_0 * times_itself_gives_0 == 0); + + size_t times_4_gives_0 = size_t(1) << (8 * sizeof(Index) - 2); + VERIFY(times_4_gives_0 * 4 == 0); + + size_t times_8_gives_0 = size_t(1) << (8 * sizeof(Index) - 3); + VERIFY(times_8_gives_0 * 8 == 0); + + triggerMatrixBadAlloc<MatrixXf>(times_itself_gives_0, times_itself_gives_0); + triggerMatrixBadAlloc<MatrixXf>(times_itself_gives_0 / 4, times_itself_gives_0); + triggerMatrixBadAlloc<MatrixXf>(times_4_gives_0, 1); + + triggerMatrixBadAlloc<MatrixXd>(times_itself_gives_0, times_itself_gives_0); + triggerMatrixBadAlloc<MatrixXd>(times_itself_gives_0 / 8, times_itself_gives_0); + triggerMatrixBadAlloc<MatrixXd>(times_8_gives_0, 1); + + triggerVectorBadAlloc<VectorXf>(times_4_gives_0); + + triggerVectorBadAlloc<VectorXd>(times_8_gives_0); +} diff --git a/test/smallvectors.cpp b/test/smallvectors.cpp new file mode 100644 index 000000000..781511397 --- /dev/null +++ b/test/smallvectors.cpp @@ -0,0 +1,67 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#define EIGEN_NO_STATIC_ASSERT +#include "main.h" + +template<typename Scalar> void smallVectors() +{ + typedef Matrix<Scalar, 1, 2> V2; + typedef Matrix<Scalar, 3, 1> V3; + typedef Matrix<Scalar, 1, 4> V4; + typedef Matrix<Scalar, Dynamic, 1> VX; + Scalar x1 = internal::random<Scalar>(), + x2 = internal::random<Scalar>(), + x3 = internal::random<Scalar>(), + x4 = internal::random<Scalar>(); + V2 v2(x1, x2); + V3 v3(x1, x2, x3); + V4 v4(x1, x2, x3, x4); + VERIFY_IS_APPROX(x1, v2.x()); + VERIFY_IS_APPROX(x1, v3.x()); + VERIFY_IS_APPROX(x1, v4.x()); + VERIFY_IS_APPROX(x2, v2.y()); + VERIFY_IS_APPROX(x2, v3.y()); + VERIFY_IS_APPROX(x2, v4.y()); + VERIFY_IS_APPROX(x3, v3.z()); + VERIFY_IS_APPROX(x3, v4.z()); + VERIFY_IS_APPROX(x4, v4.w()); + + if (!NumTraits<Scalar>::IsInteger) + { + VERIFY_RAISES_ASSERT(V3(2, 1)) + VERIFY_RAISES_ASSERT(V3(3, 2)) + VERIFY_RAISES_ASSERT(V3(Scalar(3), 1)) + VERIFY_RAISES_ASSERT(V3(3, Scalar(1))) + VERIFY_RAISES_ASSERT(V3(Scalar(3), Scalar(1))) + VERIFY_RAISES_ASSERT(V3(Scalar(123), Scalar(123))) + + VERIFY_RAISES_ASSERT(V4(1, 3)) + VERIFY_RAISES_ASSERT(V4(2, 4)) + VERIFY_RAISES_ASSERT(V4(1, Scalar(4))) + VERIFY_RAISES_ASSERT(V4(Scalar(1), 4)) + VERIFY_RAISES_ASSERT(V4(Scalar(1), Scalar(4))) + VERIFY_RAISES_ASSERT(V4(Scalar(123), Scalar(123))) + + VERIFY_RAISES_ASSERT(VX(3, 2)) + VERIFY_RAISES_ASSERT(VX(Scalar(3), 1)) + VERIFY_RAISES_ASSERT(VX(3, Scalar(1))) + VERIFY_RAISES_ASSERT(VX(Scalar(3), Scalar(1))) + VERIFY_RAISES_ASSERT(VX(Scalar(123), Scalar(123))) + } +} + +void test_smallvectors() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST(smallVectors<int>() ); + CALL_SUBTEST(smallVectors<float>() ); + CALL_SUBTEST(smallVectors<double>() ); + } +} diff --git a/test/sparse.h b/test/sparse.h new file mode 100644 index 000000000..4db0004aa --- /dev/null +++ b/test/sparse.h @@ -0,0 +1,182 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2011 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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/. + +#ifndef EIGEN_TESTSPARSE_H +#define EIGEN_TESTSPARSE_H + +#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET + +#include "main.h" + +#if EIGEN_GNUC_AT_LEAST(4,0) && !defined __ICC && !defined(__clang__) + +#ifdef min +#undef min +#endif + +#ifdef max +#undef max +#endif + +#include <tr1/unordered_map> +#define EIGEN_UNORDERED_MAP_SUPPORT +namespace std { + using std::tr1::unordered_map; +} +#endif + +#ifdef EIGEN_GOOGLEHASH_SUPPORT + #include <google/sparse_hash_map> +#endif + +#include <Eigen/Cholesky> +#include <Eigen/LU> +#include <Eigen/Sparse> + +enum { + ForceNonZeroDiag = 1, + MakeLowerTriangular = 2, + MakeUpperTriangular = 4, + ForceRealDiag = 8 +}; + +/* Initializes both a sparse and dense matrix with same random values, + * and a ratio of \a density non zero entries. + * \param flags is a union of ForceNonZeroDiag, MakeLowerTriangular and MakeUpperTriangular + * allowing to control the shape of the matrix. + * \param zeroCoords and nonzeroCoords allows to get the coordinate lists of the non zero, + * and zero coefficients respectively. + */ +template<typename Scalar,int Opt1,int Opt2,typename Index> void +initSparse(double density, + Matrix<Scalar,Dynamic,Dynamic,Opt1>& refMat, + SparseMatrix<Scalar,Opt2,Index>& sparseMat, + int flags = 0, + std::vector<Vector2i>* zeroCoords = 0, + std::vector<Vector2i>* nonzeroCoords = 0) +{ + enum { IsRowMajor = SparseMatrix<Scalar,Opt2,Index>::IsRowMajor }; + sparseMat.setZero(); + //sparseMat.reserve(int(refMat.rows()*refMat.cols()*density)); + sparseMat.reserve(VectorXi::Constant(IsRowMajor ? refMat.rows() : refMat.cols(), int((1.5*density)*(IsRowMajor?refMat.cols():refMat.rows())))); + + for(int j=0; j<sparseMat.outerSize(); j++) + { + //sparseMat.startVec(j); + for(int i=0; i<sparseMat.innerSize(); i++) + { + int ai(i), aj(j); + if(IsRowMajor) + std::swap(ai,aj); + Scalar v = (internal::random<double>(0,1) < density) ? internal::random<Scalar>() : Scalar(0); + if ((flags&ForceNonZeroDiag) && (i==j)) + { + v = internal::random<Scalar>()*Scalar(3.); + v = v*v + Scalar(5.); + } + if ((flags & MakeLowerTriangular) && aj>ai) + v = Scalar(0); + else if ((flags & MakeUpperTriangular) && aj<ai) + v = Scalar(0); + + if ((flags&ForceRealDiag) && (i==j)) + v = internal::real(v); + + if (v!=Scalar(0)) + { + //sparseMat.insertBackByOuterInner(j,i) = v; + sparseMat.insertByOuterInner(j,i) = v; + if (nonzeroCoords) + nonzeroCoords->push_back(Vector2i(ai,aj)); + } + else if (zeroCoords) + { + zeroCoords->push_back(Vector2i(ai,aj)); + } + refMat(ai,aj) = v; + } + } + //sparseMat.finalize(); +} + +template<typename Scalar,int Opt1,int Opt2,typename Index> void +initSparse(double density, + Matrix<Scalar,Dynamic,Dynamic, Opt1>& refMat, + DynamicSparseMatrix<Scalar, Opt2, Index>& sparseMat, + int flags = 0, + std::vector<Vector2i>* zeroCoords = 0, + std::vector<Vector2i>* nonzeroCoords = 0) +{ + enum { IsRowMajor = DynamicSparseMatrix<Scalar,Opt2,Index>::IsRowMajor }; + sparseMat.setZero(); + sparseMat.reserve(int(refMat.rows()*refMat.cols()*density)); + for(int j=0; j<sparseMat.outerSize(); j++) + { + sparseMat.startVec(j); // not needed for DynamicSparseMatrix + for(int i=0; i<sparseMat.innerSize(); i++) + { + int ai(i), aj(j); + if(IsRowMajor) + std::swap(ai,aj); + Scalar v = (internal::random<double>(0,1) < density) ? internal::random<Scalar>() : Scalar(0); + if ((flags&ForceNonZeroDiag) && (i==j)) + { + v = internal::random<Scalar>()*Scalar(3.); + v = v*v + Scalar(5.); + } + if ((flags & MakeLowerTriangular) && aj>ai) + v = Scalar(0); + else if ((flags & MakeUpperTriangular) && aj<ai) + v = Scalar(0); + + if ((flags&ForceRealDiag) && (i==j)) + v = internal::real(v); + + if (v!=Scalar(0)) + { + sparseMat.insertBackByOuterInner(j,i) = v; + if (nonzeroCoords) + nonzeroCoords->push_back(Vector2i(ai,aj)); + } + else if (zeroCoords) + { + zeroCoords->push_back(Vector2i(ai,aj)); + } + refMat(ai,aj) = v; + } + } + sparseMat.finalize(); +} + +template<typename Scalar> void +initSparse(double density, + Matrix<Scalar,Dynamic,1>& refVec, + SparseVector<Scalar>& sparseVec, + std::vector<int>* zeroCoords = 0, + std::vector<int>* nonzeroCoords = 0) +{ + sparseVec.reserve(int(refVec.size()*density)); + sparseVec.setZero(); + for(int i=0; i<refVec.size(); i++) + { + Scalar v = (internal::random<double>(0,1) < density) ? internal::random<Scalar>() : Scalar(0); + if (v!=Scalar(0)) + { + sparseVec.insertBack(i) = v; + if (nonzeroCoords) + nonzeroCoords->push_back(i); + } + else if (zeroCoords) + zeroCoords->push_back(i); + refVec[i] = v; + } +} + +#include <unsupported/Eigen/SparseExtra> +#endif // EIGEN_TESTSPARSE_H diff --git a/test/sparse_basic.cpp b/test/sparse_basic.cpp new file mode 100644 index 000000000..8897a9dca --- /dev/null +++ b/test/sparse_basic.cpp @@ -0,0 +1,395 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2011 Gael Guennebaud <gael.guennebaud@inria.fr> +// Copyright (C) 2008 Daniel Gomez Ferro <dgomezferro@gmail.com> +// +// 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 "sparse.h" + +template<typename SparseMatrixType> void sparse_basic(const SparseMatrixType& ref) +{ + typedef typename SparseMatrixType::Index Index; + + const Index rows = ref.rows(); + const Index cols = ref.cols(); + typedef typename SparseMatrixType::Scalar Scalar; + enum { Flags = SparseMatrixType::Flags }; + + double density = (std::max)(8./(rows*cols), 0.01); + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; + typedef Matrix<Scalar,Dynamic,1> DenseVector; + Scalar eps = 1e-6; + + SparseMatrixType m(rows, cols); + DenseMatrix refMat = DenseMatrix::Zero(rows, cols); + DenseVector vec1 = DenseVector::Random(rows); + Scalar s1 = internal::random<Scalar>(); + + std::vector<Vector2i> zeroCoords; + std::vector<Vector2i> nonzeroCoords; + initSparse<Scalar>(density, refMat, m, 0, &zeroCoords, &nonzeroCoords); + + if (zeroCoords.size()==0 || nonzeroCoords.size()==0) + return; + + // test coeff and coeffRef + for (int i=0; i<(int)zeroCoords.size(); ++i) + { + VERIFY_IS_MUCH_SMALLER_THAN( m.coeff(zeroCoords[i].x(),zeroCoords[i].y()), eps ); + if(internal::is_same<SparseMatrixType,SparseMatrix<Scalar,Flags> >::value) + VERIFY_RAISES_ASSERT( m.coeffRef(zeroCoords[0].x(),zeroCoords[0].y()) = 5 ); + } + VERIFY_IS_APPROX(m, refMat); + + m.coeffRef(nonzeroCoords[0].x(), nonzeroCoords[0].y()) = Scalar(5); + refMat.coeffRef(nonzeroCoords[0].x(), nonzeroCoords[0].y()) = Scalar(5); + + VERIFY_IS_APPROX(m, refMat); + /* + // test InnerIterators and Block expressions + for (int t=0; t<10; ++t) + { + int j = internal::random<int>(0,cols-1); + int i = internal::random<int>(0,rows-1); + int w = internal::random<int>(1,cols-j-1); + int h = internal::random<int>(1,rows-i-1); + +// VERIFY_IS_APPROX(m.block(i,j,h,w), refMat.block(i,j,h,w)); + for(int c=0; c<w; c++) + { + VERIFY_IS_APPROX(m.block(i,j,h,w).col(c), refMat.block(i,j,h,w).col(c)); + for(int r=0; r<h; r++) + { +// VERIFY_IS_APPROX(m.block(i,j,h,w).col(c).coeff(r), refMat.block(i,j,h,w).col(c).coeff(r)); + } + } +// for(int r=0; r<h; r++) +// { +// VERIFY_IS_APPROX(m.block(i,j,h,w).row(r), refMat.block(i,j,h,w).row(r)); +// for(int c=0; c<w; c++) +// { +// VERIFY_IS_APPROX(m.block(i,j,h,w).row(r).coeff(c), refMat.block(i,j,h,w).row(r).coeff(c)); +// } +// } + } + + for(int c=0; c<cols; c++) + { + VERIFY_IS_APPROX(m.col(c) + m.col(c), (m + m).col(c)); + VERIFY_IS_APPROX(m.col(c) + m.col(c), refMat.col(c) + refMat.col(c)); + } + + for(int r=0; r<rows; r++) + { + VERIFY_IS_APPROX(m.row(r) + m.row(r), (m + m).row(r)); + VERIFY_IS_APPROX(m.row(r) + m.row(r), refMat.row(r) + refMat.row(r)); + } + */ + + // test insert (inner random) + { + DenseMatrix m1(rows,cols); + m1.setZero(); + SparseMatrixType m2(rows,cols); + if(internal::random<int>()%2) + m2.reserve(VectorXi::Constant(m2.outerSize(), 2)); + for (int j=0; j<cols; ++j) + { + for (int k=0; k<rows/2; ++k) + { + int i = internal::random<int>(0,rows-1); + if (m1.coeff(i,j)==Scalar(0)) + m2.insert(i,j) = m1(i,j) = internal::random<Scalar>(); + } + } + m2.finalize(); + VERIFY_IS_APPROX(m2,m1); + } + + // test insert (fully random) + { + DenseMatrix m1(rows,cols); + m1.setZero(); + SparseMatrixType m2(rows,cols); + if(internal::random<int>()%2) + m2.reserve(VectorXi::Constant(m2.outerSize(), 2)); + for (int k=0; k<rows*cols; ++k) + { + int i = internal::random<int>(0,rows-1); + int j = internal::random<int>(0,cols-1); + if ((m1.coeff(i,j)==Scalar(0)) && (internal::random<int>()%2)) + m2.insert(i,j) = m1(i,j) = internal::random<Scalar>(); + else + { + Scalar v = internal::random<Scalar>(); + m2.coeffRef(i,j) += v; + m1(i,j) += v; + } + } + VERIFY_IS_APPROX(m2,m1); + } + + // test insert (un-compressed) + for(int mode=0;mode<4;++mode) + { + DenseMatrix m1(rows,cols); + m1.setZero(); + SparseMatrixType m2(rows,cols); + VectorXi r(VectorXi::Constant(m2.outerSize(), ((mode%2)==0) ? m2.innerSize() : std::max<int>(1,m2.innerSize()/8))); + m2.reserve(r); + for (int k=0; k<rows*cols; ++k) + { + int i = internal::random<int>(0,rows-1); + int j = internal::random<int>(0,cols-1); + if (m1.coeff(i,j)==Scalar(0)) + m2.insert(i,j) = m1(i,j) = internal::random<Scalar>(); + if(mode==3) + m2.reserve(r); + } + if(internal::random<int>()%2) + m2.makeCompressed(); + VERIFY_IS_APPROX(m2,m1); + } + + // test basic computations + { + DenseMatrix refM1 = DenseMatrix::Zero(rows, rows); + DenseMatrix refM2 = DenseMatrix::Zero(rows, rows); + DenseMatrix refM3 = DenseMatrix::Zero(rows, rows); + DenseMatrix refM4 = DenseMatrix::Zero(rows, rows); + SparseMatrixType m1(rows, rows); + SparseMatrixType m2(rows, rows); + SparseMatrixType m3(rows, rows); + SparseMatrixType m4(rows, rows); + initSparse<Scalar>(density, refM1, m1); + initSparse<Scalar>(density, refM2, m2); + initSparse<Scalar>(density, refM3, m3); + initSparse<Scalar>(density, refM4, m4); + + VERIFY_IS_APPROX(m1+m2, refM1+refM2); + VERIFY_IS_APPROX(m1+m2+m3, refM1+refM2+refM3); + VERIFY_IS_APPROX(m3.cwiseProduct(m1+m2), refM3.cwiseProduct(refM1+refM2)); + VERIFY_IS_APPROX(m1*s1-m2, refM1*s1-refM2); + + VERIFY_IS_APPROX(m1*=s1, refM1*=s1); + VERIFY_IS_APPROX(m1/=s1, refM1/=s1); + + VERIFY_IS_APPROX(m1+=m2, refM1+=refM2); + VERIFY_IS_APPROX(m1-=m2, refM1-=refM2); + + if(SparseMatrixType::IsRowMajor) + VERIFY_IS_APPROX(m1.innerVector(0).dot(refM2.row(0)), refM1.row(0).dot(refM2.row(0))); + else + VERIFY_IS_APPROX(m1.innerVector(0).dot(refM2.row(0)), refM1.col(0).dot(refM2.row(0))); + + VERIFY_IS_APPROX(m1.conjugate(), refM1.conjugate()); + VERIFY_IS_APPROX(m1.real(), refM1.real()); + + refM4.setRandom(); + // sparse cwise* dense + VERIFY_IS_APPROX(m3.cwiseProduct(refM4), refM3.cwiseProduct(refM4)); +// VERIFY_IS_APPROX(m3.cwise()/refM4, refM3.cwise()/refM4); + } + + // test transpose + { + DenseMatrix refMat2 = DenseMatrix::Zero(rows, rows); + SparseMatrixType m2(rows, rows); + initSparse<Scalar>(density, refMat2, m2); + VERIFY_IS_APPROX(m2.transpose().eval(), refMat2.transpose().eval()); + VERIFY_IS_APPROX(m2.transpose(), refMat2.transpose()); + + VERIFY_IS_APPROX(SparseMatrixType(m2.adjoint()), refMat2.adjoint()); + } + + // test innerVector() + { + DenseMatrix refMat2 = DenseMatrix::Zero(rows, rows); + SparseMatrixType m2(rows, rows); + initSparse<Scalar>(density, refMat2, m2); + int j0 = internal::random<int>(0,rows-1); + int j1 = internal::random<int>(0,rows-1); + if(SparseMatrixType::IsRowMajor) + VERIFY_IS_APPROX(m2.innerVector(j0), refMat2.row(j0)); + else + VERIFY_IS_APPROX(m2.innerVector(j0), refMat2.col(j0)); + + if(SparseMatrixType::IsRowMajor) + VERIFY_IS_APPROX(m2.innerVector(j0)+m2.innerVector(j1), refMat2.row(j0)+refMat2.row(j1)); + else + VERIFY_IS_APPROX(m2.innerVector(j0)+m2.innerVector(j1), refMat2.col(j0)+refMat2.col(j1)); + + SparseMatrixType m3(rows,rows); + m3.reserve(VectorXi::Constant(rows,rows/2)); + for(int j=0; j<rows; ++j) + for(int k=0; k<j; ++k) + m3.insertByOuterInner(j,k) = k+1; + for(int j=0; j<rows; ++j) + { + VERIFY(j==internal::real(m3.innerVector(j).nonZeros())); + if(j>0) + VERIFY(j==internal::real(m3.innerVector(j).lastCoeff())); + } + m3.makeCompressed(); + for(int j=0; j<rows; ++j) + { + VERIFY(j==internal::real(m3.innerVector(j).nonZeros())); + if(j>0) + VERIFY(j==internal::real(m3.innerVector(j).lastCoeff())); + } + + //m2.innerVector(j0) = 2*m2.innerVector(j1); + //refMat2.col(j0) = 2*refMat2.col(j1); + //VERIFY_IS_APPROX(m2, refMat2); + } + + // test innerVectors() + { + DenseMatrix refMat2 = DenseMatrix::Zero(rows, rows); + SparseMatrixType m2(rows, rows); + initSparse<Scalar>(density, refMat2, m2); + int j0 = internal::random<int>(0,rows-2); + int j1 = internal::random<int>(0,rows-2); + int n0 = internal::random<int>(1,rows-(std::max)(j0,j1)); + if(SparseMatrixType::IsRowMajor) + VERIFY_IS_APPROX(m2.innerVectors(j0,n0), refMat2.block(j0,0,n0,cols)); + else + VERIFY_IS_APPROX(m2.innerVectors(j0,n0), refMat2.block(0,j0,rows,n0)); + if(SparseMatrixType::IsRowMajor) + VERIFY_IS_APPROX(m2.innerVectors(j0,n0)+m2.innerVectors(j1,n0), + refMat2.block(j0,0,n0,cols)+refMat2.block(j1,0,n0,cols)); + else + VERIFY_IS_APPROX(m2.innerVectors(j0,n0)+m2.innerVectors(j1,n0), + refMat2.block(0,j0,rows,n0)+refMat2.block(0,j1,rows,n0)); + //m2.innerVectors(j0,n0) = m2.innerVectors(j0,n0) + m2.innerVectors(j1,n0); + //refMat2.block(0,j0,rows,n0) = refMat2.block(0,j0,rows,n0) + refMat2.block(0,j1,rows,n0); + } + + // test prune + { + SparseMatrixType m2(rows, rows); + DenseMatrix refM2(rows, rows); + refM2.setZero(); + int countFalseNonZero = 0; + int countTrueNonZero = 0; + for (int j=0; j<m2.outerSize(); ++j) + { + m2.startVec(j); + for (int i=0; i<m2.innerSize(); ++i) + { + float x = internal::random<float>(0,1); + if (x<0.1) + { + // do nothing + } + else if (x<0.5) + { + countFalseNonZero++; + m2.insertBackByOuterInner(j,i) = Scalar(0); + } + else + { + countTrueNonZero++; + m2.insertBackByOuterInner(j,i) = Scalar(1); + if(SparseMatrixType::IsRowMajor) + refM2(j,i) = Scalar(1); + else + refM2(i,j) = Scalar(1); + } + } + } + m2.finalize(); + VERIFY(countFalseNonZero+countTrueNonZero == m2.nonZeros()); + VERIFY_IS_APPROX(m2, refM2); + m2.prune(Scalar(1)); + VERIFY(countTrueNonZero==m2.nonZeros()); + VERIFY_IS_APPROX(m2, refM2); + } + + // test setFromTriplets + { + typedef Triplet<Scalar,Index> TripletType; + std::vector<TripletType> triplets; + int ntriplets = rows*cols; + triplets.reserve(ntriplets); + DenseMatrix refMat(rows,cols); + refMat.setZero(); + for(int i=0;i<ntriplets;++i) + { + int r = internal::random<int>(0,rows-1); + int c = internal::random<int>(0,cols-1); + Scalar v = internal::random<Scalar>(); + triplets.push_back(TripletType(r,c,v)); + refMat(r,c) += v; + } + SparseMatrixType m(rows,cols); + m.setFromTriplets(triplets.begin(), triplets.end()); + VERIFY_IS_APPROX(m, refMat); + } + + // test triangularView + { + DenseMatrix refMat2(rows, rows), refMat3(rows, rows); + SparseMatrixType m2(rows, rows), m3(rows, rows); + initSparse<Scalar>(density, refMat2, m2); + refMat3 = refMat2.template triangularView<Lower>(); + m3 = m2.template triangularView<Lower>(); + VERIFY_IS_APPROX(m3, refMat3); + + refMat3 = refMat2.template triangularView<Upper>(); + m3 = m2.template triangularView<Upper>(); + VERIFY_IS_APPROX(m3, refMat3); + + refMat3 = refMat2.template triangularView<UnitUpper>(); + m3 = m2.template triangularView<UnitUpper>(); + VERIFY_IS_APPROX(m3, refMat3); + + refMat3 = refMat2.template triangularView<UnitLower>(); + m3 = m2.template triangularView<UnitLower>(); + VERIFY_IS_APPROX(m3, refMat3); + } + + // test selfadjointView + if(!SparseMatrixType::IsRowMajor) + { + DenseMatrix refMat2(rows, rows), refMat3(rows, rows); + SparseMatrixType m2(rows, rows), m3(rows, rows); + initSparse<Scalar>(density, refMat2, m2); + refMat3 = refMat2.template selfadjointView<Lower>(); + m3 = m2.template selfadjointView<Lower>(); + VERIFY_IS_APPROX(m3, refMat3); + } + + // test sparseView + { + DenseMatrix refMat2 = DenseMatrix::Zero(rows, rows); + SparseMatrixType m2(rows, rows); + initSparse<Scalar>(density, refMat2, m2); + VERIFY_IS_APPROX(m2.eval(), refMat2.sparseView().eval()); + } + + // test diagonal + { + DenseMatrix refMat2 = DenseMatrix::Zero(rows, rows); + SparseMatrixType m2(rows, rows); + initSparse<Scalar>(density, refMat2, m2); + VERIFY_IS_APPROX(m2.diagonal(), refMat2.diagonal().eval()); + } +} + +void test_sparse_basic() +{ + for(int i = 0; i < g_repeat; i++) { + int s = Eigen::internal::random<int>(1,50); + CALL_SUBTEST_1(( sparse_basic(SparseMatrix<double>(8, 8)) )); + CALL_SUBTEST_2(( sparse_basic(SparseMatrix<std::complex<double>, ColMajor>(s, s)) )); + CALL_SUBTEST_2(( sparse_basic(SparseMatrix<std::complex<double>, RowMajor>(s, s)) )); + CALL_SUBTEST_1(( sparse_basic(SparseMatrix<double>(s, s)) )); + CALL_SUBTEST_1(( sparse_basic(SparseMatrix<double,ColMajor,long int>(s, s)) )); + CALL_SUBTEST_1(( sparse_basic(SparseMatrix<double,RowMajor,long int>(s, s)) )); + } +} diff --git a/test/sparse_permutations.cpp b/test/sparse_permutations.cpp new file mode 100644 index 000000000..e4ce1d679 --- /dev/null +++ b/test/sparse_permutations.cpp @@ -0,0 +1,187 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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 "sparse.h" + +template<int OtherStorage, typename SparseMatrixType> void sparse_permutations(const SparseMatrixType& ref) +{ + typedef typename SparseMatrixType::Index Index; + + const Index rows = ref.rows(); + const Index cols = ref.cols(); + typedef typename SparseMatrixType::Scalar Scalar; + typedef typename SparseMatrixType::Index Index; + typedef SparseMatrix<Scalar, OtherStorage, Index> OtherSparseMatrixType; + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; + typedef Matrix<Index,Dynamic,1> VectorI; + + double density = (std::max)(8./(rows*cols), 0.01); + + SparseMatrixType mat(rows, cols), up(rows,cols), lo(rows,cols); + OtherSparseMatrixType res; + DenseMatrix mat_d = DenseMatrix::Zero(rows, cols), up_sym_d, lo_sym_d, res_d; + + initSparse<Scalar>(density, mat_d, mat, 0); + + up = mat.template triangularView<Upper>(); + lo = mat.template triangularView<Lower>(); + + up_sym_d = mat_d.template selfadjointView<Upper>(); + lo_sym_d = mat_d.template selfadjointView<Lower>(); + + VERIFY_IS_APPROX(mat, mat_d); + VERIFY_IS_APPROX(up, DenseMatrix(mat_d.template triangularView<Upper>())); + VERIFY_IS_APPROX(lo, DenseMatrix(mat_d.template triangularView<Lower>())); + + PermutationMatrix<Dynamic> p, p_null; + VectorI pi; + randomPermutationVector(pi, cols); + p.indices() = pi; + + res = mat*p; + res_d = mat_d*p; + VERIFY(res.isApprox(res_d) && "mat*p"); + + res = p*mat; + res_d = p*mat_d; + VERIFY(res.isApprox(res_d) && "p*mat"); + + res = mat*p.inverse(); + res_d = mat*p.inverse(); + VERIFY(res.isApprox(res_d) && "mat*inv(p)"); + + res = p.inverse()*mat; + res_d = p.inverse()*mat_d; + VERIFY(res.isApprox(res_d) && "inv(p)*mat"); + + res = mat.twistedBy(p); + res_d = (p * mat_d) * p.inverse(); + VERIFY(res.isApprox(res_d) && "p*mat*inv(p)"); + + + res = mat.template selfadjointView<Upper>().twistedBy(p_null); + res_d = up_sym_d; + VERIFY(res.isApprox(res_d) && "full selfadjoint upper to full"); + + res = mat.template selfadjointView<Lower>().twistedBy(p_null); + res_d = lo_sym_d; + VERIFY(res.isApprox(res_d) && "full selfadjoint lower to full"); + + + res = up.template selfadjointView<Upper>().twistedBy(p_null); + res_d = up_sym_d; + VERIFY(res.isApprox(res_d) && "upper selfadjoint to full"); + + res = lo.template selfadjointView<Lower>().twistedBy(p_null); + res_d = lo_sym_d; + VERIFY(res.isApprox(res_d) && "lower selfadjoint full"); + + + res = mat.template selfadjointView<Upper>(); + res_d = up_sym_d; + VERIFY(res.isApprox(res_d) && "full selfadjoint upper to full"); + + res = mat.template selfadjointView<Lower>(); + res_d = lo_sym_d; + VERIFY(res.isApprox(res_d) && "full selfadjoint lower to full"); + + res = up.template selfadjointView<Upper>(); + res_d = up_sym_d; + VERIFY(res.isApprox(res_d) && "upper selfadjoint to full"); + + res = lo.template selfadjointView<Lower>(); + res_d = lo_sym_d; + VERIFY(res.isApprox(res_d) && "lower selfadjoint full"); + + + res.template selfadjointView<Upper>() = mat.template selfadjointView<Upper>(); + res_d = up_sym_d.template triangularView<Upper>(); + VERIFY(res.isApprox(res_d) && "full selfadjoint upper to upper"); + + res.template selfadjointView<Lower>() = mat.template selfadjointView<Upper>(); + res_d = up_sym_d.template triangularView<Lower>(); + VERIFY(res.isApprox(res_d) && "full selfadjoint upper to lower"); + + res.template selfadjointView<Upper>() = mat.template selfadjointView<Lower>(); + res_d = lo_sym_d.template triangularView<Upper>(); + VERIFY(res.isApprox(res_d) && "full selfadjoint lower to upper"); + + res.template selfadjointView<Lower>() = mat.template selfadjointView<Lower>(); + res_d = lo_sym_d.template triangularView<Lower>(); + VERIFY(res.isApprox(res_d) && "full selfadjoint lower to lower"); + + + + res.template selfadjointView<Upper>() = mat.template selfadjointView<Upper>().twistedBy(p); + res_d = ((p * up_sym_d) * p.inverse()).eval().template triangularView<Upper>(); + VERIFY(res.isApprox(res_d) && "full selfadjoint upper twisted to upper"); + + res.template selfadjointView<Upper>() = mat.template selfadjointView<Lower>().twistedBy(p); + res_d = ((p * lo_sym_d) * p.inverse()).eval().template triangularView<Upper>(); + VERIFY(res.isApprox(res_d) && "full selfadjoint lower twisted to upper"); + + res.template selfadjointView<Lower>() = mat.template selfadjointView<Lower>().twistedBy(p); + res_d = ((p * lo_sym_d) * p.inverse()).eval().template triangularView<Lower>(); + VERIFY(res.isApprox(res_d) && "full selfadjoint lower twisted to lower"); + + res.template selfadjointView<Lower>() = mat.template selfadjointView<Upper>().twistedBy(p); + res_d = ((p * up_sym_d) * p.inverse()).eval().template triangularView<Lower>(); + VERIFY(res.isApprox(res_d) && "full selfadjoint upper twisted to lower"); + + + res.template selfadjointView<Upper>() = up.template selfadjointView<Upper>().twistedBy(p); + res_d = ((p * up_sym_d) * p.inverse()).eval().template triangularView<Upper>(); + VERIFY(res.isApprox(res_d) && "upper selfadjoint twisted to upper"); + + res.template selfadjointView<Upper>() = lo.template selfadjointView<Lower>().twistedBy(p); + res_d = ((p * lo_sym_d) * p.inverse()).eval().template triangularView<Upper>(); + VERIFY(res.isApprox(res_d) && "lower selfadjoint twisted to upper"); + + res.template selfadjointView<Lower>() = lo.template selfadjointView<Lower>().twistedBy(p); + res_d = ((p * lo_sym_d) * p.inverse()).eval().template triangularView<Lower>(); + VERIFY(res.isApprox(res_d) && "lower selfadjoint twisted to lower"); + + res.template selfadjointView<Lower>() = up.template selfadjointView<Upper>().twistedBy(p); + res_d = ((p * up_sym_d) * p.inverse()).eval().template triangularView<Lower>(); + VERIFY(res.isApprox(res_d) && "upper selfadjoint twisted to lower"); + + + res = mat.template selfadjointView<Upper>().twistedBy(p); + res_d = (p * up_sym_d) * p.inverse(); + VERIFY(res.isApprox(res_d) && "full selfadjoint upper twisted to full"); + + res = mat.template selfadjointView<Lower>().twistedBy(p); + res_d = (p * lo_sym_d) * p.inverse(); + VERIFY(res.isApprox(res_d) && "full selfadjoint lower twisted to full"); + + res = up.template selfadjointView<Upper>().twistedBy(p); + res_d = (p * up_sym_d) * p.inverse(); + VERIFY(res.isApprox(res_d) && "upper selfadjoint twisted to full"); + + res = lo.template selfadjointView<Lower>().twistedBy(p); + res_d = (p * lo_sym_d) * p.inverse(); + VERIFY(res.isApprox(res_d) && "lower selfadjoint twisted to full"); +} + +template<typename Scalar> void sparse_permutations_all(int size) +{ + CALL_SUBTEST(( sparse_permutations<ColMajor>(SparseMatrix<Scalar, ColMajor>(size,size)) )); + CALL_SUBTEST(( sparse_permutations<ColMajor>(SparseMatrix<Scalar, RowMajor>(size,size)) )); + CALL_SUBTEST(( sparse_permutations<RowMajor>(SparseMatrix<Scalar, ColMajor>(size,size)) )); + CALL_SUBTEST(( sparse_permutations<RowMajor>(SparseMatrix<Scalar, RowMajor>(size,size)) )); +} + +void test_sparse_permutations() +{ + for(int i = 0; i < g_repeat; i++) { + int s = Eigen::internal::random<int>(1,50); + CALL_SUBTEST_1(( sparse_permutations_all<double>(s) )); + CALL_SUBTEST_2(( sparse_permutations_all<std::complex<double> >(s) )); + } +} diff --git a/test/sparse_product.cpp b/test/sparse_product.cpp new file mode 100644 index 000000000..17a955c9d --- /dev/null +++ b/test/sparse_product.cpp @@ -0,0 +1,204 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2011 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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 "sparse.h" + +template<typename SparseMatrixType, typename DenseMatrix, bool IsRowMajor=SparseMatrixType::IsRowMajor> struct test_outer; + +template<typename SparseMatrixType, typename DenseMatrix> struct test_outer<SparseMatrixType,DenseMatrix,false> { + static void run(SparseMatrixType& m2, SparseMatrixType& m4, DenseMatrix& refMat2, DenseMatrix& refMat4) { + int c = internal::random(0,m2.cols()-1); + int c1 = internal::random(0,m2.cols()-1); + VERIFY_IS_APPROX(m4=m2.col(c)*refMat2.col(c1).transpose(), refMat4=refMat2.col(c)*refMat2.col(c1).transpose()); + VERIFY_IS_APPROX(m4=refMat2.col(c1)*m2.col(c).transpose(), refMat4=refMat2.col(c1)*refMat2.col(c).transpose()); + } +}; + +template<typename SparseMatrixType, typename DenseMatrix> struct test_outer<SparseMatrixType,DenseMatrix,true> { + static void run(SparseMatrixType& m2, SparseMatrixType& m4, DenseMatrix& refMat2, DenseMatrix& refMat4) { + int r = internal::random(0,m2.rows()-1); + int c1 = internal::random(0,m2.cols()-1); + VERIFY_IS_APPROX(m4=m2.row(r).transpose()*refMat2.col(c1).transpose(), refMat4=refMat2.row(r).transpose()*refMat2.col(c1).transpose()); + VERIFY_IS_APPROX(m4=refMat2.col(c1)*m2.row(r), refMat4=refMat2.col(c1)*refMat2.row(r)); + } +}; + +// (m2,m4,refMat2,refMat4,dv1); +// VERIFY_IS_APPROX(m4=m2.innerVector(c)*dv1.transpose(), refMat4=refMat2.colVector(c)*dv1.transpose()); +// VERIFY_IS_APPROX(m4=dv1*mcm.col(c).transpose(), refMat4=dv1*refMat2.col(c).transpose()); + +template<typename SparseMatrixType> void sparse_product() +{ + typedef typename SparseMatrixType::Index Index; + Index n = 100; + const Index rows = internal::random<int>(1,n); + const Index cols = internal::random<int>(1,n); + const Index depth = internal::random<int>(1,n); + typedef typename SparseMatrixType::Scalar Scalar; + enum { Flags = SparseMatrixType::Flags }; + + double density = (std::max)(8./(rows*cols), 0.01); + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; + typedef Matrix<Scalar,Dynamic,1> DenseVector; + + Scalar s1 = internal::random<Scalar>(); + Scalar s2 = internal::random<Scalar>(); + + // test matrix-matrix product + { + DenseMatrix refMat2 = DenseMatrix::Zero(rows, depth); + DenseMatrix refMat2t = DenseMatrix::Zero(depth, rows); + DenseMatrix refMat3 = DenseMatrix::Zero(depth, cols); + DenseMatrix refMat3t = DenseMatrix::Zero(cols, depth); + DenseMatrix refMat4 = DenseMatrix::Zero(rows, cols); + DenseMatrix refMat4t = DenseMatrix::Zero(cols, rows); + DenseMatrix refMat5 = DenseMatrix::Random(depth, cols); + DenseMatrix refMat6 = DenseMatrix::Random(rows, rows); + DenseMatrix dm4 = DenseMatrix::Zero(rows, rows); +// DenseVector dv1 = DenseVector::Random(rows); + SparseMatrixType m2 (rows, depth); + SparseMatrixType m2t(depth, rows); + SparseMatrixType m3 (depth, cols); + SparseMatrixType m3t(cols, depth); + SparseMatrixType m4 (rows, cols); + SparseMatrixType m4t(cols, rows); + SparseMatrixType m6(rows, rows); + initSparse(density, refMat2, m2); + initSparse(density, refMat2t, m2t); + initSparse(density, refMat3, m3); + initSparse(density, refMat3t, m3t); + initSparse(density, refMat4, m4); + initSparse(density, refMat4t, m4t); + initSparse(density, refMat6, m6); + +// int c = internal::random<int>(0,depth-1); + + // sparse * sparse + VERIFY_IS_APPROX(m4=m2*m3, refMat4=refMat2*refMat3); + VERIFY_IS_APPROX(m4=m2t.transpose()*m3, refMat4=refMat2t.transpose()*refMat3); + VERIFY_IS_APPROX(m4=m2t.transpose()*m3t.transpose(), refMat4=refMat2t.transpose()*refMat3t.transpose()); + VERIFY_IS_APPROX(m4=m2*m3t.transpose(), refMat4=refMat2*refMat3t.transpose()); + + VERIFY_IS_APPROX(m4 = m2*m3/s1, refMat4 = refMat2*refMat3/s1); + VERIFY_IS_APPROX(m4 = m2*m3*s1, refMat4 = refMat2*refMat3*s1); + VERIFY_IS_APPROX(m4 = s2*m2*m3*s1, refMat4 = s2*refMat2*refMat3*s1); + + VERIFY_IS_APPROX(m4=(m2*m3).pruned(0), refMat4=refMat2*refMat3); + VERIFY_IS_APPROX(m4=(m2t.transpose()*m3).pruned(0), refMat4=refMat2t.transpose()*refMat3); + VERIFY_IS_APPROX(m4=(m2t.transpose()*m3t.transpose()).pruned(0), refMat4=refMat2t.transpose()*refMat3t.transpose()); + VERIFY_IS_APPROX(m4=(m2*m3t.transpose()).pruned(0), refMat4=refMat2*refMat3t.transpose()); + + // test aliasing + m4 = m2; refMat4 = refMat2; + VERIFY_IS_APPROX(m4=m4*m3, refMat4=refMat4*refMat3); + + // sparse * dense + VERIFY_IS_APPROX(dm4=m2*refMat3, refMat4=refMat2*refMat3); + VERIFY_IS_APPROX(dm4=m2*refMat3t.transpose(), refMat4=refMat2*refMat3t.transpose()); + VERIFY_IS_APPROX(dm4=m2t.transpose()*refMat3, refMat4=refMat2t.transpose()*refMat3); + VERIFY_IS_APPROX(dm4=m2t.transpose()*refMat3t.transpose(), refMat4=refMat2t.transpose()*refMat3t.transpose()); + + VERIFY_IS_APPROX(dm4=m2*(refMat3+refMat3), refMat4=refMat2*(refMat3+refMat3)); + VERIFY_IS_APPROX(dm4=m2t.transpose()*(refMat3+refMat5)*0.5, refMat4=refMat2t.transpose()*(refMat3+refMat5)*0.5); + + // dense * sparse + VERIFY_IS_APPROX(dm4=refMat2*m3, refMat4=refMat2*refMat3); + VERIFY_IS_APPROX(dm4=refMat2*m3t.transpose(), refMat4=refMat2*refMat3t.transpose()); + VERIFY_IS_APPROX(dm4=refMat2t.transpose()*m3, refMat4=refMat2t.transpose()*refMat3); + VERIFY_IS_APPROX(dm4=refMat2t.transpose()*m3t.transpose(), refMat4=refMat2t.transpose()*refMat3t.transpose()); + + // sparse * dense and dense * sparse outer product + test_outer<SparseMatrixType,DenseMatrix>::run(m2,m4,refMat2,refMat4); + + VERIFY_IS_APPROX(m6=m6*m6, refMat6=refMat6*refMat6); + } + + // test matrix - diagonal product + { + DenseMatrix refM2 = DenseMatrix::Zero(rows, rows); + DenseMatrix refM3 = DenseMatrix::Zero(rows, rows); + DiagonalMatrix<Scalar,Dynamic> d1(DenseVector::Random(rows)); + SparseMatrixType m2(rows, rows); + SparseMatrixType m3(rows, rows); + initSparse<Scalar>(density, refM2, m2); + initSparse<Scalar>(density, refM3, m3); + VERIFY_IS_APPROX(m3=m2*d1, refM3=refM2*d1); + VERIFY_IS_APPROX(m3=m2.transpose()*d1, refM3=refM2.transpose()*d1); + VERIFY_IS_APPROX(m3=d1*m2, refM3=d1*refM2); + VERIFY_IS_APPROX(m3=d1*m2.transpose(), refM3=d1 * refM2.transpose()); + } + + // test self adjoint products + { + DenseMatrix b = DenseMatrix::Random(rows, rows); + DenseMatrix x = DenseMatrix::Random(rows, rows); + DenseMatrix refX = DenseMatrix::Random(rows, rows); + DenseMatrix refUp = DenseMatrix::Zero(rows, rows); + DenseMatrix refLo = DenseMatrix::Zero(rows, rows); + DenseMatrix refS = DenseMatrix::Zero(rows, rows); + SparseMatrixType mUp(rows, rows); + SparseMatrixType mLo(rows, rows); + SparseMatrixType mS(rows, rows); + do { + initSparse<Scalar>(density, refUp, mUp, ForceRealDiag|/*ForceNonZeroDiag|*/MakeUpperTriangular); + } while (refUp.isZero()); + refLo = refUp.adjoint(); + mLo = mUp.adjoint(); + refS = refUp + refLo; + refS.diagonal() *= 0.5; + mS = mUp + mLo; + // TODO be able to address the diagonal.... + for (int k=0; k<mS.outerSize(); ++k) + for (typename SparseMatrixType::InnerIterator it(mS,k); it; ++it) + if (it.index() == k) + it.valueRef() *= 0.5; + + VERIFY_IS_APPROX(refS.adjoint(), refS); + VERIFY_IS_APPROX(mS.adjoint(), mS); + VERIFY_IS_APPROX(mS, refS); + VERIFY_IS_APPROX(x=mS*b, refX=refS*b); + + VERIFY_IS_APPROX(x=mUp.template selfadjointView<Upper>()*b, refX=refS*b); + VERIFY_IS_APPROX(x=mLo.template selfadjointView<Lower>()*b, refX=refS*b); + VERIFY_IS_APPROX(x=mS.template selfadjointView<Upper|Lower>()*b, refX=refS*b); + } +} + +// New test for Bug in SparseTimeDenseProduct +template<typename SparseMatrixType, typename DenseMatrixType> void sparse_product_regression_test() +{ + // This code does not compile with afflicted versions of the bug + SparseMatrixType sm1(3,2); + DenseMatrixType m2(2,2); + sm1.setZero(); + m2.setZero(); + + DenseMatrixType m3 = sm1*m2; + + + // This code produces a segfault with afflicted versions of another SparseTimeDenseProduct + // bug + + SparseMatrixType sm2(20000,2); + sm2.setZero(); + DenseMatrixType m4(sm2*m2); + + VERIFY_IS_APPROX( m4(0,0), 0.0 ); +} + +void test_sparse_product() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( (sparse_product<SparseMatrix<double,ColMajor> >()) ); + CALL_SUBTEST_1( (sparse_product<SparseMatrix<double,RowMajor> >()) ); + CALL_SUBTEST_2( (sparse_product<SparseMatrix<std::complex<double>, ColMajor > >()) ); + CALL_SUBTEST_2( (sparse_product<SparseMatrix<std::complex<double>, RowMajor > >()) ); + CALL_SUBTEST_4( (sparse_product_regression_test<SparseMatrix<double,RowMajor>, Matrix<double, Dynamic, Dynamic, RowMajor> >()) ); + } +} diff --git a/test/sparse_solver.h b/test/sparse_solver.h new file mode 100644 index 000000000..75fa85082 --- /dev/null +++ b/test/sparse_solver.h @@ -0,0 +1,309 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Gael Guennebaud <g.gael@free.fr> +// +// 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 "sparse.h" +#include <Eigen/SparseCore> + +template<typename Solver, typename Rhs, typename DenseMat, typename DenseRhs> +void check_sparse_solving(Solver& solver, const typename Solver::MatrixType& A, const Rhs& b, const DenseMat& dA, const DenseRhs& db) +{ + typedef typename Solver::MatrixType Mat; + typedef typename Mat::Scalar Scalar; + + DenseRhs refX = dA.lu().solve(db); + + Rhs x(b.rows(), b.cols()); + Rhs oldb = b; + + solver.compute(A); + if (solver.info() != Success) + { + std::cerr << "sparse solver testing: factorization failed (check_sparse_solving)\n"; + exit(0); + return; + } + x = solver.solve(b); + if (solver.info() != Success) + { + std::cerr << "sparse solver testing: solving failed\n"; + return; + } + VERIFY(oldb.isApprox(b) && "sparse solver testing: the rhs should not be modified!"); + + VERIFY(x.isApprox(refX,test_precision<Scalar>())); + + x.setZero(); + // test the analyze/factorize API + solver.analyzePattern(A); + solver.factorize(A); + if (solver.info() != Success) + { + std::cerr << "sparse solver testing: factorization failed (check_sparse_solving)\n"; + exit(0); + return; + } + x = solver.solve(b); + if (solver.info() != Success) + { + std::cerr << "sparse solver testing: solving failed\n"; + return; + } + VERIFY(oldb.isApprox(b) && "sparse solver testing: the rhs should not be modified!"); + + VERIFY(x.isApprox(refX,test_precision<Scalar>())); + + // test Block as the result and rhs: + { + DenseRhs x(db.rows(), db.cols()); + DenseRhs b(db), oldb(db); + x.setZero(); + x.block(0,0,x.rows(),x.cols()) = solver.solve(b.block(0,0,b.rows(),b.cols())); + VERIFY(oldb.isApprox(b) && "sparse solver testing: the rhs should not be modified!"); + VERIFY(x.isApprox(refX,test_precision<Scalar>())); + } +} + +template<typename Solver, typename Rhs> +void check_sparse_solving_real_cases(Solver& solver, const typename Solver::MatrixType& A, const Rhs& b, const Rhs& refX) +{ + typedef typename Solver::MatrixType Mat; + typedef typename Mat::Scalar Scalar; + typedef typename Mat::RealScalar RealScalar; + + Rhs x(b.rows(), b.cols()); + + solver.compute(A); + if (solver.info() != Success) + { + std::cerr << "sparse solver testing: factorization failed (check_sparse_solving_real_cases)\n"; + exit(0); + return; + } + x = solver.solve(b); + if (solver.info() != Success) + { + std::cerr << "sparse solver testing: solving failed\n"; + return; + } + + RealScalar res_error; + // Compute the norm of the relative error + if(refX.size() != 0) + res_error = (refX - x).norm()/refX.norm(); + else + { + // Compute the relative residual norm + res_error = (b - A * x).norm()/b.norm(); + } + if (res_error > test_precision<Scalar>() ){ + std::cerr << "Test " << g_test_stack.back() << " failed in "EI_PP_MAKE_STRING(__FILE__) + << " (" << EI_PP_MAKE_STRING(__LINE__) << ")" << std::endl << std::endl; + abort(); + } + +} +template<typename Solver, typename DenseMat> +void check_sparse_determinant(Solver& solver, const typename Solver::MatrixType& A, const DenseMat& dA) +{ + typedef typename Solver::MatrixType Mat; + typedef typename Mat::Scalar Scalar; + typedef typename Mat::RealScalar RealScalar; + + solver.compute(A); + if (solver.info() != Success) + { + std::cerr << "sparse solver testing: factorization failed (check_sparse_determinant)\n"; + return; + } + + Scalar refDet = dA.determinant(); + VERIFY_IS_APPROX(refDet,solver.determinant()); +} + + +template<typename Solver, typename DenseMat> +int generate_sparse_spd_problem(Solver& , typename Solver::MatrixType& A, typename Solver::MatrixType& halfA, DenseMat& dA, int maxSize = 300) +{ + typedef typename Solver::MatrixType Mat; + typedef typename Mat::Scalar Scalar; + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; + + int size = internal::random<int>(1,maxSize); + double density = (std::max)(8./(size*size), 0.01); + + Mat M(size, size); + DenseMatrix dM(size, size); + + initSparse<Scalar>(density, dM, M, ForceNonZeroDiag); + + A = M * M.adjoint(); + dA = dM * dM.adjoint(); + + halfA.resize(size,size); + halfA.template selfadjointView<Solver::UpLo>().rankUpdate(M); + + return size; +} + + +#ifdef TEST_REAL_CASES +template<typename Scalar> +inline std::string get_matrixfolder() +{ + std::string mat_folder = TEST_REAL_CASES; + if( internal::is_same<Scalar, std::complex<float> >::value || internal::is_same<Scalar, std::complex<double> >::value ) + mat_folder = mat_folder + static_cast<string>("/complex/"); + else + mat_folder = mat_folder + static_cast<string>("/real/"); + return mat_folder; +} +#endif + +template<typename Solver> void check_sparse_spd_solving(Solver& solver) +{ + typedef typename Solver::MatrixType Mat; + typedef typename Mat::Scalar Scalar; + typedef typename Mat::Index Index; + typedef SparseMatrix<Scalar,ColMajor> SpMat; + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; + typedef Matrix<Scalar,Dynamic,1> DenseVector; + + // generate the problem + Mat A, halfA; + DenseMatrix dA; + int size = generate_sparse_spd_problem(solver, A, halfA, dA); + + // generate the right hand sides + int rhsCols = internal::random<int>(1,16); + double density = (std::max)(8./(size*rhsCols), 0.1); + SpMat B(size,rhsCols); + DenseVector b = DenseVector::Random(size); + DenseMatrix dB(size,rhsCols); + initSparse<Scalar>(density, dB, B, ForceNonZeroDiag); + + for (int i = 0; i < g_repeat; i++) { + check_sparse_solving(solver, A, b, dA, b); + check_sparse_solving(solver, halfA, b, dA, b); + check_sparse_solving(solver, A, dB, dA, dB); + check_sparse_solving(solver, halfA, dB, dA, dB); + check_sparse_solving(solver, A, B, dA, dB); + check_sparse_solving(solver, halfA, B, dA, dB); + } + + // First, get the folder +#ifdef TEST_REAL_CASES + if (internal::is_same<Scalar, float>::value + || internal::is_same<Scalar, std::complex<float> >::value) + return ; + + std::string mat_folder = get_matrixfolder<Scalar>(); + MatrixMarketIterator<Scalar> it(mat_folder); + for (; it; ++it) + { + if (it.sym() == SPD){ + Mat halfA; + PermutationMatrix<Dynamic, Dynamic, Index> pnull; + halfA.template selfadjointView<Solver::UpLo>() = it.matrix().template triangularView<Eigen::Lower>().twistedBy(pnull); + + std::cout<< " ==== SOLVING WITH MATRIX " << it.matname() << " ==== \n"; + check_sparse_solving_real_cases(solver, it.matrix(), it.rhs(), it.refX()); + check_sparse_solving_real_cases(solver, halfA, it.rhs(), it.refX()); + } + } +#endif +} + +template<typename Solver> void check_sparse_spd_determinant(Solver& solver) +{ + typedef typename Solver::MatrixType Mat; + typedef typename Mat::Scalar Scalar; + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; + + // generate the problem + Mat A, halfA; + DenseMatrix dA; + generate_sparse_spd_problem(solver, A, halfA, dA, 30); + + for (int i = 0; i < g_repeat; i++) { + check_sparse_determinant(solver, A, dA); + check_sparse_determinant(solver, halfA, dA ); + } +} + +template<typename Solver, typename DenseMat> +int generate_sparse_square_problem(Solver&, typename Solver::MatrixType& A, DenseMat& dA, int maxSize = 300) +{ + typedef typename Solver::MatrixType Mat; + typedef typename Mat::Scalar Scalar; + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; + + int size = internal::random<int>(1,maxSize); + double density = (std::max)(8./(size*size), 0.01); + + A.resize(size,size); + dA.resize(size,size); + + initSparse<Scalar>(density, dA, A, ForceNonZeroDiag); + + return size; +} + +template<typename Solver> void check_sparse_square_solving(Solver& solver) +{ + typedef typename Solver::MatrixType Mat; + typedef typename Mat::Scalar Scalar; + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; + typedef Matrix<Scalar,Dynamic,1> DenseVector; + + int rhsCols = internal::random<int>(1,16); + + Mat A; + DenseMatrix dA; + int size = generate_sparse_square_problem(solver, A, dA); + + DenseVector b = DenseVector::Random(size); + DenseMatrix dB = DenseMatrix::Random(size,rhsCols); + A.makeCompressed(); + for (int i = 0; i < g_repeat; i++) { + check_sparse_solving(solver, A, b, dA, b); + check_sparse_solving(solver, A, dB, dA, dB); + } + + // First, get the folder +#ifdef TEST_REAL_CASES + if (internal::is_same<Scalar, float>::value + || internal::is_same<Scalar, std::complex<float> >::value) + return ; + + std::string mat_folder = get_matrixfolder<Scalar>(); + MatrixMarketIterator<Scalar> it(mat_folder); + for (; it; ++it) + { + std::cout<< " ==== SOLVING WITH MATRIX " << it.matname() << " ==== \n"; + check_sparse_solving_real_cases(solver, it.matrix(), it.rhs(), it.refX()); + } +#endif + +} + +template<typename Solver> void check_sparse_square_determinant(Solver& solver) +{ + typedef typename Solver::MatrixType Mat; + typedef typename Mat::Scalar Scalar; + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; + + // generate the problem + Mat A; + DenseMatrix dA; + generate_sparse_square_problem(solver, A, dA, 30); + A.makeCompressed(); + for (int i = 0; i < g_repeat; i++) { + check_sparse_determinant(solver, A, dA); + } +} diff --git a/test/sparse_solvers.cpp b/test/sparse_solvers.cpp new file mode 100644 index 000000000..3a8873d43 --- /dev/null +++ b/test/sparse_solvers.cpp @@ -0,0 +1,112 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2010 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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 "sparse.h" + +template<typename Scalar> void +initSPD(double density, + Matrix<Scalar,Dynamic,Dynamic>& refMat, + SparseMatrix<Scalar>& sparseMat) +{ + Matrix<Scalar,Dynamic,Dynamic> aux(refMat.rows(),refMat.cols()); + initSparse(density,refMat,sparseMat); + refMat = refMat * refMat.adjoint(); + for (int k=0; k<2; ++k) + { + initSparse(density,aux,sparseMat,ForceNonZeroDiag); + refMat += aux * aux.adjoint(); + } + sparseMat.setZero(); + for (int j=0 ; j<sparseMat.cols(); ++j) + for (int i=j ; i<sparseMat.rows(); ++i) + if (refMat(i,j)!=Scalar(0)) + sparseMat.insert(i,j) = refMat(i,j); + sparseMat.finalize(); +} + +template<typename Scalar> void sparse_solvers(int rows, int cols) +{ + double density = (std::max)(8./(rows*cols), 0.01); + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; + typedef Matrix<Scalar,Dynamic,1> DenseVector; + // Scalar eps = 1e-6; + + DenseVector vec1 = DenseVector::Random(rows); + + std::vector<Vector2i> zeroCoords; + std::vector<Vector2i> nonzeroCoords; + + // test triangular solver + { + DenseVector vec2 = vec1, vec3 = vec1; + SparseMatrix<Scalar> m2(rows, cols); + DenseMatrix refMat2 = DenseMatrix::Zero(rows, cols); + + // lower - dense + initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeLowerTriangular, &zeroCoords, &nonzeroCoords); + VERIFY_IS_APPROX(refMat2.template triangularView<Lower>().solve(vec2), + m2.template triangularView<Lower>().solve(vec3)); + + // upper - dense + initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeUpperTriangular, &zeroCoords, &nonzeroCoords); + VERIFY_IS_APPROX(refMat2.template triangularView<Upper>().solve(vec2), + m2.template triangularView<Upper>().solve(vec3)); + VERIFY_IS_APPROX(refMat2.conjugate().template triangularView<Upper>().solve(vec2), + m2.conjugate().template triangularView<Upper>().solve(vec3)); + { + SparseMatrix<Scalar> cm2(m2); + //Index rows, Index cols, Index nnz, Index* outerIndexPtr, Index* innerIndexPtr, Scalar* valuePtr + MappedSparseMatrix<Scalar> mm2(rows, cols, cm2.nonZeros(), cm2.outerIndexPtr(), cm2.innerIndexPtr(), cm2.valuePtr()); + VERIFY_IS_APPROX(refMat2.conjugate().template triangularView<Upper>().solve(vec2), + mm2.conjugate().template triangularView<Upper>().solve(vec3)); + } + + // lower - transpose + initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeLowerTriangular, &zeroCoords, &nonzeroCoords); + VERIFY_IS_APPROX(refMat2.transpose().template triangularView<Upper>().solve(vec2), + m2.transpose().template triangularView<Upper>().solve(vec3)); + + // upper - transpose + initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeUpperTriangular, &zeroCoords, &nonzeroCoords); + VERIFY_IS_APPROX(refMat2.transpose().template triangularView<Lower>().solve(vec2), + m2.transpose().template triangularView<Lower>().solve(vec3)); + + SparseMatrix<Scalar> matB(rows, rows); + DenseMatrix refMatB = DenseMatrix::Zero(rows, rows); + + // lower - sparse + initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeLowerTriangular); + initSparse<Scalar>(density, refMatB, matB); + refMat2.template triangularView<Lower>().solveInPlace(refMatB); + m2.template triangularView<Lower>().solveInPlace(matB); + VERIFY_IS_APPROX(matB.toDense(), refMatB); + + // upper - sparse + initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeUpperTriangular); + initSparse<Scalar>(density, refMatB, matB); + refMat2.template triangularView<Upper>().solveInPlace(refMatB); + m2.template triangularView<Upper>().solveInPlace(matB); + VERIFY_IS_APPROX(matB, refMatB); + + // test deprecated API + initSparse<Scalar>(density, refMat2, m2, ForceNonZeroDiag|MakeLowerTriangular, &zeroCoords, &nonzeroCoords); + VERIFY_IS_APPROX(refMat2.template triangularView<Lower>().solve(vec2), + m2.template triangularView<Lower>().solve(vec3)); + } +} + +void test_sparse_solvers() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1(sparse_solvers<double>(8, 8) ); + int s = internal::random<int>(1,300); + CALL_SUBTEST_2(sparse_solvers<std::complex<double> >(s,s) ); + CALL_SUBTEST_1(sparse_solvers<double>(s,s) ); + } +} diff --git a/test/sparse_vector.cpp b/test/sparse_vector.cpp new file mode 100644 index 000000000..7201afe5b --- /dev/null +++ b/test/sparse_vector.cpp @@ -0,0 +1,91 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2011 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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 "sparse.h" + +template<typename Scalar> void sparse_vector(int rows, int cols) +{ + double densityMat = (std::max)(8./(rows*cols), 0.01); + double densityVec = (std::max)(8./float(rows), 0.1); + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; + typedef Matrix<Scalar,Dynamic,1> DenseVector; + typedef SparseVector<Scalar> SparseVectorType; + typedef SparseMatrix<Scalar> SparseMatrixType; + Scalar eps = 1e-6; + + SparseMatrixType m1(rows,rows); + SparseVectorType v1(rows), v2(rows), v3(rows); + DenseMatrix refM1 = DenseMatrix::Zero(rows, rows); + DenseVector refV1 = DenseVector::Random(rows), + refV2 = DenseVector::Random(rows), + refV3 = DenseVector::Random(rows); + + std::vector<int> zerocoords, nonzerocoords; + initSparse<Scalar>(densityVec, refV1, v1, &zerocoords, &nonzerocoords); + initSparse<Scalar>(densityMat, refM1, m1); + + initSparse<Scalar>(densityVec, refV2, v2); + initSparse<Scalar>(densityVec, refV3, v3); + + Scalar s1 = internal::random<Scalar>(); + + // test coeff and coeffRef + for (unsigned int i=0; i<zerocoords.size(); ++i) + { + VERIFY_IS_MUCH_SMALLER_THAN( v1.coeff(zerocoords[i]), eps ); + //VERIFY_RAISES_ASSERT( v1.coeffRef(zerocoords[i]) = 5 ); + } + { + VERIFY(int(nonzerocoords.size()) == v1.nonZeros()); + int j=0; + for (typename SparseVectorType::InnerIterator it(v1); it; ++it,++j) + { + VERIFY(nonzerocoords[j]==it.index()); + VERIFY(it.value()==v1.coeff(it.index())); + VERIFY(it.value()==refV1.coeff(it.index())); + } + } + VERIFY_IS_APPROX(v1, refV1); + + v1.coeffRef(nonzerocoords[0]) = Scalar(5); + refV1.coeffRef(nonzerocoords[0]) = Scalar(5); + VERIFY_IS_APPROX(v1, refV1); + + VERIFY_IS_APPROX(v1+v2, refV1+refV2); + VERIFY_IS_APPROX(v1+v2+v3, refV1+refV2+refV3); + + VERIFY_IS_APPROX(v1*s1-v2, refV1*s1-refV2); + + VERIFY_IS_APPROX(v1*=s1, refV1*=s1); + VERIFY_IS_APPROX(v1/=s1, refV1/=s1); + + VERIFY_IS_APPROX(v1+=v2, refV1+=refV2); + VERIFY_IS_APPROX(v1-=v2, refV1-=refV2); + + VERIFY_IS_APPROX(v1.dot(v2), refV1.dot(refV2)); + VERIFY_IS_APPROX(v1.dot(refV2), refV1.dot(refV2)); + + VERIFY_IS_APPROX(v1.dot(m1*v2), refV1.dot(refM1*refV2)); + int i = internal::random<int>(0,rows-1); + VERIFY_IS_APPROX(v1.dot(m1.col(i)), refV1.dot(refM1.col(i))); + + + VERIFY_IS_APPROX(v1.squaredNorm(), refV1.squaredNorm()); + +} + +void test_sparse_vector() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( sparse_vector<double>(8, 8) ); + CALL_SUBTEST_2( sparse_vector<std::complex<double> >(16, 16) ); + CALL_SUBTEST_1( sparse_vector<double>(299, 535) ); + } +} + diff --git a/test/stable_norm.cpp b/test/stable_norm.cpp new file mode 100644 index 000000000..a25dbf51c --- /dev/null +++ b/test/stable_norm.cpp @@ -0,0 +1,110 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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<typename T> bool isNotNaN(const T& x) +{ + return x==x; +} + +// workaround aggressive optimization in ICC +template<typename T> EIGEN_DONT_INLINE T sub(T a, T b) { return a - b; } + +template<typename T> bool isFinite(const T& x) +{ + return isNotNaN(sub(x,x)); +} + +template<typename T> EIGEN_DONT_INLINE T copy(const T& x) +{ + return x; +} + +template<typename MatrixType> void stable_norm(const MatrixType& m) +{ + /* this test covers the following files: + StableNorm.h + */ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + + // Check the basic machine-dependent constants. + { + int ibeta, it, iemin, iemax; + + ibeta = std::numeric_limits<RealScalar>::radix; // base for floating-point numbers + it = std::numeric_limits<RealScalar>::digits; // number of base-beta digits in mantissa + iemin = std::numeric_limits<RealScalar>::min_exponent; // minimum exponent + iemax = std::numeric_limits<RealScalar>::max_exponent; // maximum exponent + + VERIFY( (!(iemin > 1 - 2*it || 1+it>iemax || (it==2 && ibeta<5) || (it<=4 && ibeta <= 3 ) || it<2)) + && "the stable norm algorithm cannot be guaranteed on this computer"); + } + + + Index rows = m.rows(); + Index cols = m.cols(); + + Scalar big = internal::random<Scalar>() * ((std::numeric_limits<RealScalar>::max)() * RealScalar(1e-4)); + Scalar small = internal::random<Scalar>() * ((std::numeric_limits<RealScalar>::min)() * RealScalar(1e4)); + + MatrixType vzero = MatrixType::Zero(rows, cols), + vrand = MatrixType::Random(rows, cols), + vbig(rows, cols), + vsmall(rows,cols); + + vbig.fill(big); + vsmall.fill(small); + + VERIFY_IS_MUCH_SMALLER_THAN(vzero.norm(), static_cast<RealScalar>(1)); + VERIFY_IS_APPROX(vrand.stableNorm(), vrand.norm()); + VERIFY_IS_APPROX(vrand.blueNorm(), vrand.norm()); + VERIFY_IS_APPROX(vrand.hypotNorm(), vrand.norm()); + + RealScalar size = static_cast<RealScalar>(m.size()); + + // test isFinite + VERIFY(!isFinite( std::numeric_limits<RealScalar>::infinity())); + VERIFY(!isFinite(internal::sqrt(-internal::abs(big)))); + + // test overflow + VERIFY(isFinite(internal::sqrt(size)*internal::abs(big))); + VERIFY_IS_NOT_APPROX(internal::sqrt(copy(vbig.squaredNorm())), internal::abs(internal::sqrt(size)*big)); // here the default norm must fail + VERIFY_IS_APPROX(vbig.stableNorm(), internal::sqrt(size)*internal::abs(big)); + VERIFY_IS_APPROX(vbig.blueNorm(), internal::sqrt(size)*internal::abs(big)); + VERIFY_IS_APPROX(vbig.hypotNorm(), internal::sqrt(size)*internal::abs(big)); + + // test underflow + VERIFY(isFinite(internal::sqrt(size)*internal::abs(small))); + VERIFY_IS_NOT_APPROX(internal::sqrt(copy(vsmall.squaredNorm())), internal::abs(internal::sqrt(size)*small)); // here the default norm must fail + VERIFY_IS_APPROX(vsmall.stableNorm(), internal::sqrt(size)*internal::abs(small)); + VERIFY_IS_APPROX(vsmall.blueNorm(), internal::sqrt(size)*internal::abs(small)); + VERIFY_IS_APPROX(vsmall.hypotNorm(), internal::sqrt(size)*internal::abs(small)); + +// Test compilation of cwise() version + VERIFY_IS_APPROX(vrand.colwise().stableNorm(), vrand.colwise().norm()); + VERIFY_IS_APPROX(vrand.colwise().blueNorm(), vrand.colwise().norm()); + VERIFY_IS_APPROX(vrand.colwise().hypotNorm(), vrand.colwise().norm()); + VERIFY_IS_APPROX(vrand.rowwise().stableNorm(), vrand.rowwise().norm()); + VERIFY_IS_APPROX(vrand.rowwise().blueNorm(), vrand.rowwise().norm()); + VERIFY_IS_APPROX(vrand.rowwise().hypotNorm(), vrand.rowwise().norm()); +} + +void test_stable_norm() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( stable_norm(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( stable_norm(Vector4d()) ); + CALL_SUBTEST_3( stable_norm(VectorXd(internal::random<int>(10,2000))) ); + CALL_SUBTEST_4( stable_norm(VectorXf(internal::random<int>(10,2000))) ); + CALL_SUBTEST_5( stable_norm(VectorXcd(internal::random<int>(10,2000))) ); + } +} diff --git a/test/stddeque.cpp b/test/stddeque.cpp new file mode 100644 index 000000000..bb4b476f3 --- /dev/null +++ b/test/stddeque.cpp @@ -0,0 +1,132 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// Copyright (C) 2010 Hauke Heibel <hauke.heibel@gmail.com> +// +// 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" +#include <Eigen/StdDeque> +#include <Eigen/Geometry> + +template<typename MatrixType> +void check_stddeque_matrix(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + + Index rows = m.rows(); + Index cols = m.cols(); + MatrixType x = MatrixType::Random(rows,cols), y = MatrixType::Random(rows,cols); + std::deque<MatrixType,Eigen::aligned_allocator<MatrixType> > v(10, MatrixType(rows,cols)), w(20, y); + v.front() = x; + w.front() = w.back(); + VERIFY_IS_APPROX(w.front(), w.back()); + v = w; + + typename std::deque<MatrixType,Eigen::aligned_allocator<MatrixType> >::iterator vi = v.begin(); + typename std::deque<MatrixType,Eigen::aligned_allocator<MatrixType> >::iterator wi = w.begin(); + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(*vi, *wi); + ++vi; + ++wi; + } + + v.resize(21); + v.back() = x; + VERIFY_IS_APPROX(v.back(), x); + v.resize(22,y); + VERIFY_IS_APPROX(v.back(), y); + v.push_back(x); + VERIFY_IS_APPROX(v.back(), x); +} + +template<typename TransformType> +void check_stddeque_transform(const TransformType&) +{ + typedef typename TransformType::MatrixType MatrixType; + TransformType x(MatrixType::Random()), y(MatrixType::Random()); + std::deque<TransformType,Eigen::aligned_allocator<TransformType> > v(10), w(20, y); + v.front() = x; + w.front() = w.back(); + VERIFY_IS_APPROX(w.front(), w.back()); + v = w; + + typename std::deque<TransformType,Eigen::aligned_allocator<TransformType> >::iterator vi = v.begin(); + typename std::deque<TransformType,Eigen::aligned_allocator<TransformType> >::iterator wi = w.begin(); + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(*vi, *wi); + ++vi; + ++wi; + } + + v.resize(21); + v.back() = x; + VERIFY_IS_APPROX(v.back(), x); + v.resize(22,y); + VERIFY_IS_APPROX(v.back(), y); + v.push_back(x); + VERIFY_IS_APPROX(v.back(), x); +} + +template<typename QuaternionType> +void check_stddeque_quaternion(const QuaternionType&) +{ + typedef typename QuaternionType::Coefficients Coefficients; + QuaternionType x(Coefficients::Random()), y(Coefficients::Random()); + std::deque<QuaternionType,Eigen::aligned_allocator<QuaternionType> > v(10), w(20, y); + v.front() = x; + w.front() = w.back(); + VERIFY_IS_APPROX(w.front(), w.back()); + v = w; + + typename std::deque<QuaternionType,Eigen::aligned_allocator<QuaternionType> >::iterator vi = v.begin(); + typename std::deque<QuaternionType,Eigen::aligned_allocator<QuaternionType> >::iterator wi = w.begin(); + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(*vi, *wi); + ++vi; + ++wi; + } + + v.resize(21); + v.back() = x; + VERIFY_IS_APPROX(v.back(), x); + v.resize(22,y); + VERIFY_IS_APPROX(v.back(), y); + v.push_back(x); + VERIFY_IS_APPROX(v.back(), x); +} + +void test_stddeque() +{ + // some non vectorizable fixed sizes + CALL_SUBTEST_1(check_stddeque_matrix(Vector2f())); + CALL_SUBTEST_1(check_stddeque_matrix(Matrix3f())); + CALL_SUBTEST_2(check_stddeque_matrix(Matrix3d())); + + // some vectorizable fixed sizes + CALL_SUBTEST_1(check_stddeque_matrix(Matrix2f())); + CALL_SUBTEST_1(check_stddeque_matrix(Vector4f())); + CALL_SUBTEST_1(check_stddeque_matrix(Matrix4f())); + CALL_SUBTEST_2(check_stddeque_matrix(Matrix4d())); + + // some dynamic sizes + CALL_SUBTEST_3(check_stddeque_matrix(MatrixXd(1,1))); + CALL_SUBTEST_3(check_stddeque_matrix(VectorXd(20))); + CALL_SUBTEST_3(check_stddeque_matrix(RowVectorXf(20))); + CALL_SUBTEST_3(check_stddeque_matrix(MatrixXcf(10,10))); + + // some Transform + CALL_SUBTEST_4(check_stddeque_transform(Affine2f())); + CALL_SUBTEST_4(check_stddeque_transform(Affine3f())); + CALL_SUBTEST_4(check_stddeque_transform(Affine3d())); + + // some Quaternion + CALL_SUBTEST_5(check_stddeque_quaternion(Quaternionf())); + CALL_SUBTEST_5(check_stddeque_quaternion(Quaterniond())); +} diff --git a/test/stdlist.cpp b/test/stdlist.cpp new file mode 100644 index 000000000..17cce779a --- /dev/null +++ b/test/stdlist.cpp @@ -0,0 +1,132 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// Copyright (C) 2010 Hauke Heibel <hauke.heibel@gmail.com> +// +// 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" +#include <Eigen/StdList> +#include <Eigen/Geometry> + +template<typename MatrixType> +void check_stdlist_matrix(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + + Index rows = m.rows(); + Index cols = m.cols(); + MatrixType x = MatrixType::Random(rows,cols), y = MatrixType::Random(rows,cols); + std::list<MatrixType,Eigen::aligned_allocator<MatrixType> > v(10, MatrixType(rows,cols)), w(20, y); + v.front() = x; + w.front() = w.back(); + VERIFY_IS_APPROX(w.front(), w.back()); + v = w; + + typename std::list<MatrixType,Eigen::aligned_allocator<MatrixType> >::iterator vi = v.begin(); + typename std::list<MatrixType,Eigen::aligned_allocator<MatrixType> >::iterator wi = w.begin(); + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(*vi, *wi); + ++vi; + ++wi; + } + + v.resize(21); + v.back() = x; + VERIFY_IS_APPROX(v.back(), x); + v.resize(22,y); + VERIFY_IS_APPROX(v.back(), y); + v.push_back(x); + VERIFY_IS_APPROX(v.back(), x); +} + +template<typename TransformType> +void check_stdlist_transform(const TransformType&) +{ + typedef typename TransformType::MatrixType MatrixType; + TransformType x(MatrixType::Random()), y(MatrixType::Random()); + std::list<TransformType,Eigen::aligned_allocator<TransformType> > v(10), w(20, y); + v.front() = x; + w.front() = w.back(); + VERIFY_IS_APPROX(w.front(), w.back()); + v = w; + + typename std::list<TransformType,Eigen::aligned_allocator<TransformType> >::iterator vi = v.begin(); + typename std::list<TransformType,Eigen::aligned_allocator<TransformType> >::iterator wi = w.begin(); + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(*vi, *wi); + ++vi; + ++wi; + } + + v.resize(21); + v.back() = x; + VERIFY_IS_APPROX(v.back(), x); + v.resize(22,y); + VERIFY_IS_APPROX(v.back(), y); + v.push_back(x); + VERIFY_IS_APPROX(v.back(), x); +} + +template<typename QuaternionType> +void check_stdlist_quaternion(const QuaternionType&) +{ + typedef typename QuaternionType::Coefficients Coefficients; + QuaternionType x(Coefficients::Random()), y(Coefficients::Random()); + std::list<QuaternionType,Eigen::aligned_allocator<QuaternionType> > v(10), w(20, y); + v.front() = x; + w.front() = w.back(); + VERIFY_IS_APPROX(w.front(), w.back()); + v = w; + + typename std::list<QuaternionType,Eigen::aligned_allocator<QuaternionType> >::iterator vi = v.begin(); + typename std::list<QuaternionType,Eigen::aligned_allocator<QuaternionType> >::iterator wi = w.begin(); + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(*vi, *wi); + ++vi; + ++wi; + } + + v.resize(21); + v.back() = x; + VERIFY_IS_APPROX(v.back(), x); + v.resize(22,y); + VERIFY_IS_APPROX(v.back(), y); + v.push_back(x); + VERIFY_IS_APPROX(v.back(), x); +} + +void test_stdlist() +{ + // some non vectorizable fixed sizes + CALL_SUBTEST_1(check_stdlist_matrix(Vector2f())); + CALL_SUBTEST_1(check_stdlist_matrix(Matrix3f())); + CALL_SUBTEST_2(check_stdlist_matrix(Matrix3d())); + + // some vectorizable fixed sizes + CALL_SUBTEST_1(check_stdlist_matrix(Matrix2f())); + CALL_SUBTEST_1(check_stdlist_matrix(Vector4f())); + CALL_SUBTEST_1(check_stdlist_matrix(Matrix4f())); + CALL_SUBTEST_2(check_stdlist_matrix(Matrix4d())); + + // some dynamic sizes + CALL_SUBTEST_3(check_stdlist_matrix(MatrixXd(1,1))); + CALL_SUBTEST_3(check_stdlist_matrix(VectorXd(20))); + CALL_SUBTEST_3(check_stdlist_matrix(RowVectorXf(20))); + CALL_SUBTEST_3(check_stdlist_matrix(MatrixXcf(10,10))); + + // some Transform + CALL_SUBTEST_4(check_stdlist_transform(Affine2f())); + CALL_SUBTEST_4(check_stdlist_transform(Affine3f())); + CALL_SUBTEST_4(check_stdlist_transform(Affine3d())); + + // some Quaternion + CALL_SUBTEST_5(check_stdlist_quaternion(Quaternionf())); + CALL_SUBTEST_5(check_stdlist_quaternion(Quaterniond())); +} diff --git a/test/stdvector.cpp b/test/stdvector.cpp new file mode 100644 index 000000000..6e173c678 --- /dev/null +++ b/test/stdvector.cpp @@ -0,0 +1,148 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/StdVector> +#include <Eigen/Geometry> + +template<typename MatrixType> +void check_stdvector_matrix(const MatrixType& m) +{ + typename MatrixType::Index rows = m.rows(); + typename MatrixType::Index cols = m.cols(); + MatrixType x = MatrixType::Random(rows,cols), y = MatrixType::Random(rows,cols); + std::vector<MatrixType,Eigen::aligned_allocator<MatrixType> > v(10, MatrixType(rows,cols)), w(20, y); + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.resize(22,y); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((size_t)&(v[22]) == (size_t)&(v[21]) + sizeof(MatrixType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + MatrixType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(unsigned int i=23; i<v.size(); ++i) + { + VERIFY(v[i]==w[(i-23)%w.size()]); + } +} + +template<typename TransformType> +void check_stdvector_transform(const TransformType&) +{ + typedef typename TransformType::MatrixType MatrixType; + TransformType x(MatrixType::Random()), y(MatrixType::Random()); + std::vector<TransformType,Eigen::aligned_allocator<TransformType> > v(10), w(20, y); + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.resize(22,y); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((size_t)&(v[22]) == (size_t)&(v[21]) + sizeof(TransformType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + TransformType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(unsigned int i=23; i<v.size(); ++i) + { + VERIFY(v[i].matrix()==w[(i-23)%w.size()].matrix()); + } +} + +template<typename QuaternionType> +void check_stdvector_quaternion(const QuaternionType&) +{ + typedef typename QuaternionType::Coefficients Coefficients; + QuaternionType x(Coefficients::Random()), y(Coefficients::Random()); + std::vector<QuaternionType,Eigen::aligned_allocator<QuaternionType> > v(10), w(20, y); + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.resize(22,y); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((size_t)&(v[22]) == (size_t)&(v[21]) + sizeof(QuaternionType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + QuaternionType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(unsigned int i=23; i<v.size(); ++i) + { + VERIFY(v[i].coeffs()==w[(i-23)%w.size()].coeffs()); + } +} + +void test_stdvector() +{ + // some non vectorizable fixed sizes + CALL_SUBTEST_1(check_stdvector_matrix(Vector2f())); + CALL_SUBTEST_1(check_stdvector_matrix(Matrix3f())); + CALL_SUBTEST_2(check_stdvector_matrix(Matrix3d())); + + // some vectorizable fixed sizes + CALL_SUBTEST_1(check_stdvector_matrix(Matrix2f())); + CALL_SUBTEST_1(check_stdvector_matrix(Vector4f())); + CALL_SUBTEST_1(check_stdvector_matrix(Matrix4f())); + CALL_SUBTEST_2(check_stdvector_matrix(Matrix4d())); + + // some dynamic sizes + CALL_SUBTEST_3(check_stdvector_matrix(MatrixXd(1,1))); + CALL_SUBTEST_3(check_stdvector_matrix(VectorXd(20))); + CALL_SUBTEST_3(check_stdvector_matrix(RowVectorXf(20))); + CALL_SUBTEST_3(check_stdvector_matrix(MatrixXcf(10,10))); + + // some Transform + CALL_SUBTEST_4(check_stdvector_transform(Projective2f())); + CALL_SUBTEST_4(check_stdvector_transform(Projective3f())); + CALL_SUBTEST_4(check_stdvector_transform(Projective3d())); + //CALL_SUBTEST(heck_stdvector_transform(Projective4d())); + + // some Quaternion + CALL_SUBTEST_5(check_stdvector_quaternion(Quaternionf())); + CALL_SUBTEST_5(check_stdvector_quaternion(Quaterniond())); +} diff --git a/test/stdvector_overload.cpp b/test/stdvector_overload.cpp new file mode 100644 index 000000000..736ff0ee7 --- /dev/null +++ b/test/stdvector_overload.cpp @@ -0,0 +1,161 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// Copyright (C) 2010 Hauke Heibel <hauke.heibel@gmail.com> +// +// 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" + +#include <Eigen/StdVector> +#include <Eigen/Geometry> + +EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Vector4f) + +EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Matrix2f) +EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Matrix4f) +EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Matrix4d) + +EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Affine3f) +EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Affine3d) + +EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Quaternionf) +EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Quaterniond) + +template<typename MatrixType> +void check_stdvector_matrix(const MatrixType& m) +{ + typename MatrixType::Index rows = m.rows(); + typename MatrixType::Index cols = m.cols(); + MatrixType x = MatrixType::Random(rows,cols), y = MatrixType::Random(rows,cols); + std::vector<MatrixType> v(10, MatrixType(rows,cols)), w(20, y); + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.resize(22,y); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((size_t)&(v[22]) == (size_t)&(v[21]) + sizeof(MatrixType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + MatrixType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(unsigned int i=23; i<v.size(); ++i) + { + VERIFY(v[i]==w[(i-23)%w.size()]); + } +} + +template<typename TransformType> +void check_stdvector_transform(const TransformType&) +{ + typedef typename TransformType::MatrixType MatrixType; + TransformType x(MatrixType::Random()), y(MatrixType::Random()); + std::vector<TransformType> v(10), w(20, y); + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.resize(22,y); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((size_t)&(v[22]) == (size_t)&(v[21]) + sizeof(TransformType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + TransformType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(unsigned int i=23; i<v.size(); ++i) + { + VERIFY(v[i].matrix()==w[(i-23)%w.size()].matrix()); + } +} + +template<typename QuaternionType> +void check_stdvector_quaternion(const QuaternionType&) +{ + typedef typename QuaternionType::Coefficients Coefficients; + QuaternionType x(Coefficients::Random()), y(Coefficients::Random()); + std::vector<QuaternionType> v(10), w(20, y); + v[5] = x; + w[6] = v[5]; + VERIFY_IS_APPROX(w[6], v[5]); + v = w; + for(int i = 0; i < 20; i++) + { + VERIFY_IS_APPROX(w[i], v[i]); + } + + v.resize(21); + v[20] = x; + VERIFY_IS_APPROX(v[20], x); + v.resize(22,y); + VERIFY_IS_APPROX(v[21], y); + v.push_back(x); + VERIFY_IS_APPROX(v[22], x); + VERIFY((size_t)&(v[22]) == (size_t)&(v[21]) + sizeof(QuaternionType)); + + // do a lot of push_back such that the vector gets internally resized + // (with memory reallocation) + QuaternionType* ref = &w[0]; + for(int i=0; i<30 || ((ref==&w[0]) && i<300); ++i) + v.push_back(w[i%w.size()]); + for(unsigned int i=23; i<v.size(); ++i) + { + VERIFY(v[i].coeffs()==w[(i-23)%w.size()].coeffs()); + } +} + +void test_stdvector_overload() +{ + // some non vectorizable fixed sizes + CALL_SUBTEST_1(check_stdvector_matrix(Vector2f())); + CALL_SUBTEST_1(check_stdvector_matrix(Matrix3f())); + CALL_SUBTEST_2(check_stdvector_matrix(Matrix3d())); + + // some vectorizable fixed sizes + CALL_SUBTEST_1(check_stdvector_matrix(Matrix2f())); + CALL_SUBTEST_1(check_stdvector_matrix(Vector4f())); + CALL_SUBTEST_1(check_stdvector_matrix(Matrix4f())); + CALL_SUBTEST_2(check_stdvector_matrix(Matrix4d())); + + // some dynamic sizes + CALL_SUBTEST_3(check_stdvector_matrix(MatrixXd(1,1))); + CALL_SUBTEST_3(check_stdvector_matrix(VectorXd(20))); + CALL_SUBTEST_3(check_stdvector_matrix(RowVectorXf(20))); + CALL_SUBTEST_3(check_stdvector_matrix(MatrixXcf(10,10))); + + // some Transform + CALL_SUBTEST_4(check_stdvector_transform(Affine2f())); // does not need the specialization (2+1)^2 = 9 + CALL_SUBTEST_4(check_stdvector_transform(Affine3f())); + CALL_SUBTEST_4(check_stdvector_transform(Affine3d())); + + // some Quaternion + CALL_SUBTEST_5(check_stdvector_quaternion(Quaternionf())); + CALL_SUBTEST_5(check_stdvector_quaternion(Quaterniond())); +} diff --git a/test/superlu_support.cpp b/test/superlu_support.cpp new file mode 100644 index 000000000..3b16135bc --- /dev/null +++ b/test/superlu_support.cpp @@ -0,0 +1,22 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Gael Guennebaud <g.gael@free.fr> +// +// 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 "sparse_solver.h" + +#include <Eigen/SuperLUSupport> + +void test_superlu_support() +{ + SuperLU<SparseMatrix<double> > superlu_double_colmajor; + SuperLU<SparseMatrix<std::complex<double> > > superlu_cplxdouble_colmajor; + CALL_SUBTEST_1( check_sparse_square_solving(superlu_double_colmajor) ); + CALL_SUBTEST_2( check_sparse_square_solving(superlu_cplxdouble_colmajor) ); + CALL_SUBTEST_1( check_sparse_square_determinant(superlu_double_colmajor) ); + CALL_SUBTEST_2( check_sparse_square_determinant(superlu_cplxdouble_colmajor) ); +} diff --git a/test/swap.cpp b/test/swap.cpp new file mode 100644 index 000000000..36b353148 --- /dev/null +++ b/test/swap.cpp @@ -0,0 +1,83 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#define EIGEN_NO_STATIC_ASSERT +#include "main.h" + +template<typename T> +struct other_matrix_type +{ + typedef int type; +}; + +template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols> +struct other_matrix_type<Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> > +{ + typedef Matrix<_Scalar, _Rows, _Cols, _Options^RowMajor, _MaxRows, _MaxCols> type; +}; + +template<typename MatrixType> void swap(const MatrixType& m) +{ + typedef typename other_matrix_type<MatrixType>::type OtherMatrixType; + typedef typename MatrixType::Scalar Scalar; + + eigen_assert((!internal::is_same<MatrixType,OtherMatrixType>::value)); + typename MatrixType::Index rows = m.rows(); + typename MatrixType::Index cols = m.cols(); + + // construct 3 matrix guaranteed to be distinct + MatrixType m1 = MatrixType::Random(rows,cols); + MatrixType m2 = MatrixType::Random(rows,cols) + Scalar(100) * MatrixType::Identity(rows,cols); + OtherMatrixType m3 = OtherMatrixType::Random(rows,cols) + Scalar(200) * OtherMatrixType::Identity(rows,cols); + + MatrixType m1_copy = m1; + MatrixType m2_copy = m2; + OtherMatrixType m3_copy = m3; + + // test swapping 2 matrices of same type + m1.swap(m2); + VERIFY_IS_APPROX(m1,m2_copy); + VERIFY_IS_APPROX(m2,m1_copy); + m1 = m1_copy; + m2 = m2_copy; + + // test swapping 2 matrices of different types + m1.swap(m3); + VERIFY_IS_APPROX(m1,m3_copy); + VERIFY_IS_APPROX(m3,m1_copy); + m1 = m1_copy; + m3 = m3_copy; + + // test swapping matrix with expression + m1.swap(m2.block(0,0,rows,cols)); + VERIFY_IS_APPROX(m1,m2_copy); + VERIFY_IS_APPROX(m2,m1_copy); + m1 = m1_copy; + m2 = m2_copy; + + // test swapping two expressions of different types + m1.transpose().swap(m3.transpose()); + VERIFY_IS_APPROX(m1,m3_copy); + VERIFY_IS_APPROX(m3,m1_copy); + m1 = m1_copy; + m3 = m3_copy; + + // test assertion on mismatching size -- matrix case + VERIFY_RAISES_ASSERT(m1.swap(m1.row(0))); + // test assertion on mismatching size -- xpr case + VERIFY_RAISES_ASSERT(m1.row(0).swap(m1)); +} + +void test_swap() +{ + CALL_SUBTEST_1( swap(Matrix3f()) ); // fixed size, no vectorization + CALL_SUBTEST_2( swap(Matrix4d()) ); // fixed size, possible vectorization + CALL_SUBTEST_3( swap(MatrixXd(3,3)) ); // dyn size, no vectorization + CALL_SUBTEST_4( swap(MatrixXf(30,30)) ); // dyn size, possible vectorization +} diff --git a/test/testsuite.cmake b/test/testsuite.cmake new file mode 100644 index 000000000..3bec56b3f --- /dev/null +++ b/test/testsuite.cmake @@ -0,0 +1,229 @@ + +#################################################################### +# +# Usage: +# - create a new folder, let's call it cdash +# - in that folder, do: +# ctest -S path/to/eigen/test/testsuite.cmake[,option1=value1[,option2=value2]] +# +# Options: +# - EIGEN_CXX: compiler, eg.: g++-4.2 +# default: default c++ compiler +# - EIGEN_SITE: eg, INRIA-Bdx_pc-gael, or the name of the contributor, etc. +# default: hostname +# - EIGEN_BUILD_STRING: a string which identify the system/compiler. It should be formed like that: +# <OS_name>-<OS_version>-<arch>-<compiler-version> +# with: +# <OS_name> = opensuse, debian, osx, windows, cygwin, freebsd, solaris, etc. +# <OS_version> = 11.1, XP, vista, leopard, etc. +# <arch> = i386, x86_64, ia64, powerpc, etc. +# <compiler-version> = gcc-4.3.2, icc-11.0, MSVC-2008, etc. +# - EIGEN_EXPLICIT_VECTORIZATION: novec, SSE2, Altivec +# default: SSE2 for x86_64 systems, novec otherwise +# Its value is automatically appended to EIGEN_BUILD_STRING +# - EIGEN_CMAKE_DIR: path to cmake executable +# - EIGEN_MODE: dashboard model, can be Experimental, Nightly, or Continuous +# default: Nightly +# - EIGEN_WORK_DIR: directory used to download the source files and make the builds +# default: folder which contains this script +# - EIGEN_CMAKE_ARGS: additional arguments passed to cmake +# - EIGEN_GENERATOR_TYPE: allows to overwrite the generator type +# default: nmake (windows +# See http://www.cmake.org/cmake/help/cmake2.6docs.html#section_Generators for a complete +# list of supported generators. +# - EIGEN_NO_UPDATE: allows to submit dash boards from local repositories +# This might be interesting in case you want to submit dashboards +# including local changes. +# - CTEST_SOURCE_DIRECTORY: path to eigen's src (use a new and empty folder, not the one you are working on) +# default: <EIGEN_WORK_DIR>/src +# - CTEST_BINARY_DIRECTORY: build directory +# default: <EIGEN_WORK_DIR>/nightly-<EIGEN_CXX> +# +# Here is an example running several compilers on a linux system: +# #!/bin/bash +# ARCH=`uname -m` +# SITE=`hostname` +# VERSION=opensuse-11.1 +# WORK_DIR=/home/gael/Coding/eigen/cdash +# # get the last version of the script +# wget http://bitbucket.org/eigen/eigen/raw/tip/test/testsuite.cmake -o $WORK_DIR/testsuite.cmake +# COMMON="ctest -S $WORK_DIR/testsuite.cmake,EIGEN_WORK_DIR=$WORK_DIR,EIGEN_SITE=$SITE,EIGEN_MODE=$1,EIGEN_BUILD_STRING=$OS_VERSION-$ARCH" +# $COMMON-gcc-3.4.6,EIGEN_CXX=g++-3.4 +# $COMMON-gcc-4.0.1,EIGEN_CXX=g++-4.0.1 +# $COMMON-gcc-4.3.2,EIGEN_CXX=g++-4.3,EIGEN_EXPLICIT_VECTORIZATION=novec +# $COMMON-gcc-4.3.2,EIGEN_CXX=g++-4.3,EIGEN_EXPLICIT_VECTORIZATION=SSE2 +# $COMMON-icc-11.0,EIGEN_CXX=icpc +# +#################################################################### + +# process the arguments + +set(ARGLIST ${CTEST_SCRIPT_ARG}) +while(${ARGLIST} MATCHES ".+.*") + + # pick first + string(REGEX MATCH "([^,]*)(,.*)?" DUMMY ${ARGLIST}) + SET(TOP ${CMAKE_MATCH_1}) + + # remove first + string(REGEX MATCHALL "[^,]*,(.*)" DUMMY ${ARGLIST}) + SET(ARGLIST ${CMAKE_MATCH_1}) + + # decompose as a pair key=value + string(REGEX MATCH "([^=]*)(=.*)?" DUMMY ${TOP}) + SET(KEY ${CMAKE_MATCH_1}) + + string(REGEX MATCH "[^=]*=(.*)" DUMMY ${TOP}) + SET(VALUE ${CMAKE_MATCH_1}) + + # set the variable to the specified value + if(VALUE) + SET(${KEY} ${VALUE}) + else(VALUE) + SET(${KEY} ON) + endif(VALUE) + +endwhile(${ARGLIST} MATCHES ".+.*") + +#################################################################### +# Automatically set some user variables if they have not been defined manually +#################################################################### +cmake_minimum_required(VERSION 2.6 FATAL_ERROR) + +if(NOT EIGEN_SITE) + site_name(EIGEN_SITE) +endif(NOT EIGEN_SITE) + +if(NOT EIGEN_CMAKE_DIR) + SET(EIGEN_CMAKE_DIR "") +endif(NOT EIGEN_CMAKE_DIR) + +if(NOT EIGEN_BUILD_STRING) + + # let's try to find all information we need to make the build string ourself + + # OS + build_name(EIGEN_OS_VERSION) + + # arch + set(EIGEN_ARCH ${CMAKE_SYSTEM_PROCESSOR}) + if(WIN32) + set(EIGEN_ARCH $ENV{PROCESSOR_ARCHITECTURE}) + else(WIN32) + execute_process(COMMAND uname -m OUTPUT_VARIABLE EIGEN_ARCH OUTPUT_STRIP_TRAILING_WHITESPACE) + endif(WIN32) + + set(EIGEN_BUILD_STRING ${EIGEN_OS_VERSION}${EIGEN_ARCH}-${EIGEN_CXX}) + +endif(NOT EIGEN_BUILD_STRING) + +if(DEFINED EIGEN_EXPLICIT_VECTORIZATION) + set(EIGEN_BUILD_STRING ${EIGEN_BUILD_STRING}-${EIGEN_EXPLICIT_VECTORIZATION}) +endif(DEFINED EIGEN_EXPLICIT_VECTORIZATION) + +if(NOT EIGEN_WORK_DIR) + set(EIGEN_WORK_DIR ${CTEST_SCRIPT_DIRECTORY}) +endif(NOT EIGEN_WORK_DIR) + +if(NOT CTEST_SOURCE_DIRECTORY) + SET (CTEST_SOURCE_DIRECTORY "${EIGEN_WORK_DIR}/src") +endif(NOT CTEST_SOURCE_DIRECTORY) + +if(NOT CTEST_BINARY_DIRECTORY) + SET (CTEST_BINARY_DIRECTORY "${EIGEN_WORK_DIR}/nightly_${EIGEN_CXX}") +endif(NOT CTEST_BINARY_DIRECTORY) + +if(NOT EIGEN_MODE) + set(EIGEN_MODE Nightly) +endif(NOT EIGEN_MODE) + +## mandatory variables (the default should be ok in most cases): + +if(NOT EIGEN_NO_UPDATE) + SET (CTEST_CVS_COMMAND "hg") + SET (CTEST_CVS_CHECKOUT "${CTEST_CVS_COMMAND} clone http://bitbucket.org/eigen/eigen \"${CTEST_SOURCE_DIRECTORY}\"") + SET(CTEST_BACKUP_AND_RESTORE TRUE) # the backup is CVS related ... +endif(NOT EIGEN_NO_UPDATE) + +# which ctest command to use for running the dashboard +SET (CTEST_COMMAND "${EIGEN_CMAKE_DIR}ctest -D ${EIGEN_MODE} --no-compress-output") +if($ENV{EIGEN_CTEST_ARGS}) +SET (CTEST_COMMAND "${CTEST_COMMAND} $ENV{EIGEN_CTEST_ARGS}") +endif($ENV{EIGEN_CTEST_ARGS}) +# what cmake command to use for configuring this dashboard +SET (CTEST_CMAKE_COMMAND "${EIGEN_CMAKE_DIR}cmake -DEIGEN_LEAVE_TEST_IN_ALL_TARGET=ON") + +#################################################################### +# The values in this section are optional you can either +# have them or leave them commented out +#################################################################### + +# this make sure we get consistent outputs +SET($ENV{LC_MESSAGES} "en_EN") + +# should ctest wipe the binary tree before running +SET(CTEST_START_WITH_EMPTY_BINARY_DIRECTORY TRUE) + +# raise the warning/error limit +set(CTEST_CUSTOM_MAXIMUM_NUMBER_OF_WARNINGS "33331") +set(CTEST_CUSTOM_MAXIMUM_NUMBER_OF_ERRORS "33331") + +# this is the initial cache to use for the binary tree, be careful to escape +# any quotes inside of this string if you use it +if(WIN32 AND NOT UNIX) + #message(SEND_ERROR "win32") + if(EIGEN_GENERATOR_TYPE) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -G \"${EIGEN_GENERATOR_TYPE}\"") + SET (CTEST_INITIAL_CACHE " + CMAKE_BUILD_TYPE:STRING=Release + BUILDNAME:STRING=${EIGEN_BUILD_STRING} + SITE:STRING=${EIGEN_SITE} + ") + else(EIGEN_GENERATOR_TYPE) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -G \"NMake Makefiles\" -DCMAKE_MAKE_PROGRAM=nmake") + SET (CTEST_INITIAL_CACHE " + MAKECOMMAND:STRING=nmake /i + CMAKE_MAKE_PROGRAM:FILEPATH=nmake + CMAKE_GENERATOR:INTERNAL=NMake Makefiles + CMAKE_BUILD_TYPE:STRING=Release + BUILDNAME:STRING=${EIGEN_BUILD_STRING} + SITE:STRING=${EIGEN_SITE} + ") + endif(EIGEN_GENERATOR_TYPE) +else(WIN32 AND NOT UNIX) + SET (CTEST_INITIAL_CACHE " + BUILDNAME:STRING=${EIGEN_BUILD_STRING} + SITE:STRING=${EIGEN_SITE} + ") +endif(WIN32 AND NOT UNIX) + +# set any extra environment variables to use during the execution of the script here: +# setting this variable on windows machines causes trouble ... + +if(EIGEN_CXX AND NOT WIN32) + set(CTEST_ENVIRONMENT "CXX=${EIGEN_CXX}") +endif(EIGEN_CXX AND NOT WIN32) + +if(DEFINED EIGEN_EXPLICIT_VECTORIZATION) + if(EIGEN_EXPLICIT_VECTORIZATION MATCHES SSE2) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -DEIGEN_TEST_SSE2=ON") + elseif(EIGEN_EXPLICIT_VECTORIZATION MATCHES SSE3) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -DEIGEN_TEST_SSE2=ON -DEIGEN_TEST_SSE3=ON") + elseif(EIGEN_EXPLICIT_VECTORIZATION MATCHES SSSE3) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -DEIGEN_TEST_SSE2=ON -DEIGEN_TEST_SSE3=ON -DEIGEN_TEST_SSSE3=ON") + elseif(EIGEN_EXPLICIT_VECTORIZATION MATCHES SSE4_1) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -DEIGEN_TEST_SSE2=ON -DEIGEN_TEST_SSE3=ON -DEIGEN_TEST_SSSE3=ON -DEIGEN_TEST_SSE4_1=ON") + elseif(EIGEN_EXPLICIT_VECTORIZATION MATCHES SSE4_2) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -DEIGEN_TEST_SSE2=ON -DEIGEN_TEST_SSE3=ON -DEIGEN_TEST_SSSE3=ON -DEIGEN_TEST_SSE4_1=ON -DEIGEN_TEST_SSE4_2=ON") + elseif(EIGEN_EXPLICIT_VECTORIZATION MATCHES Altivec) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -DEIGEN_TEST_ALTIVEC=ON") + elseif(EIGEN_EXPLICIT_VECTORIZATION MATCHES novec) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -DEIGEN_TEST_NO_EXPLICIT_VECTORIZATION=ON") + else(EIGEN_EXPLICIT_VECTORIZATION MATCHES SSE2) + message(FATAL_ERROR "Invalid value for EIGEN_EXPLICIT_VECTORIZATION (${EIGEN_EXPLICIT_VECTORIZATION}), must be: novec, SSE2, SSE3, Altivec") + endif(EIGEN_EXPLICIT_VECTORIZATION MATCHES SSE2) +endif(DEFINED EIGEN_EXPLICIT_VECTORIZATION) + +if(DEFINED EIGEN_CMAKE_ARGS) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} ${EIGEN_CMAKE_ARGS}") +endif(DEFINED EIGEN_CMAKE_ARGS) diff --git a/test/triangular.cpp b/test/triangular.cpp new file mode 100644 index 000000000..0e8ee5487 --- /dev/null +++ b/test/triangular.cpp @@ -0,0 +1,235 @@ +// This file is triangularView of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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<typename MatrixType> void triangular_square(const MatrixType& m) +{ + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; + + RealScalar largerEps = 10*test_precision<RealScalar>(); + + typename MatrixType::Index rows = m.rows(); + typename MatrixType::Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols), + m4(rows, cols), + r1(rows, cols), + r2(rows, cols); + VectorType v2 = VectorType::Random(rows); + + MatrixType m1up = m1.template triangularView<Upper>(); + MatrixType m2up = m2.template triangularView<Upper>(); + + if (rows*cols>1) + { + VERIFY(m1up.isUpperTriangular()); + VERIFY(m2up.transpose().isLowerTriangular()); + VERIFY(!m2.isLowerTriangular()); + } + +// VERIFY_IS_APPROX(m1up.transpose() * m2, m1.upper().transpose().lower() * m2); + + // test overloaded operator+= + r1.setZero(); + r2.setZero(); + r1.template triangularView<Upper>() += m1; + r2 += m1up; + VERIFY_IS_APPROX(r1,r2); + + // test overloaded operator= + m1.setZero(); + m1.template triangularView<Upper>() = m2.transpose() + m2; + m3 = m2.transpose() + m2; + VERIFY_IS_APPROX(m3.template triangularView<Lower>().transpose().toDenseMatrix(), m1); + + // test overloaded operator= + m1.setZero(); + m1.template triangularView<Lower>() = m2.transpose() + m2; + VERIFY_IS_APPROX(m3.template triangularView<Lower>().toDenseMatrix(), m1); + + VERIFY_IS_APPROX(m3.template triangularView<Lower>().conjugate().toDenseMatrix(), + m3.conjugate().template triangularView<Lower>().toDenseMatrix()); + + m1 = MatrixType::Random(rows, cols); + for (int i=0; i<rows; ++i) + while (internal::abs2(m1(i,i))<1e-1) m1(i,i) = internal::random<Scalar>(); + + Transpose<MatrixType> trm4(m4); + // test back and forward subsitution with a vector as the rhs + m3 = m1.template triangularView<Upper>(); + VERIFY(v2.isApprox(m3.adjoint() * (m1.adjoint().template triangularView<Lower>().solve(v2)), largerEps)); + m3 = m1.template triangularView<Lower>(); + VERIFY(v2.isApprox(m3.transpose() * (m1.transpose().template triangularView<Upper>().solve(v2)), largerEps)); + m3 = m1.template triangularView<Upper>(); + VERIFY(v2.isApprox(m3 * (m1.template triangularView<Upper>().solve(v2)), largerEps)); + m3 = m1.template triangularView<Lower>(); + VERIFY(v2.isApprox(m3.conjugate() * (m1.conjugate().template triangularView<Lower>().solve(v2)), largerEps)); + + // test back and forward subsitution with a matrix as the rhs + m3 = m1.template triangularView<Upper>(); + VERIFY(m2.isApprox(m3.adjoint() * (m1.adjoint().template triangularView<Lower>().solve(m2)), largerEps)); + m3 = m1.template triangularView<Lower>(); + VERIFY(m2.isApprox(m3.transpose() * (m1.transpose().template triangularView<Upper>().solve(m2)), largerEps)); + m3 = m1.template triangularView<Upper>(); + VERIFY(m2.isApprox(m3 * (m1.template triangularView<Upper>().solve(m2)), largerEps)); + m3 = m1.template triangularView<Lower>(); + VERIFY(m2.isApprox(m3.conjugate() * (m1.conjugate().template triangularView<Lower>().solve(m2)), largerEps)); + + // check M * inv(L) using in place API + m4 = m3; + m1.transpose().template triangularView<Eigen::Upper>().solveInPlace(trm4); + VERIFY_IS_APPROX(m4 * m1.template triangularView<Eigen::Lower>(), m3); + + // check M * inv(U) using in place API + m3 = m1.template triangularView<Upper>(); + m4 = m3; + m3.transpose().template triangularView<Eigen::Lower>().solveInPlace(trm4); + VERIFY_IS_APPROX(m4 * m1.template triangularView<Eigen::Upper>(), m3); + + // check solve with unit diagonal + m3 = m1.template triangularView<UnitUpper>(); + VERIFY(m2.isApprox(m3 * (m1.template triangularView<UnitUpper>().solve(m2)), largerEps)); + +// VERIFY(( m1.template triangularView<Upper>() +// * m2.template triangularView<Upper>()).isUpperTriangular()); + + // test swap + m1.setOnes(); + m2.setZero(); + m2.template triangularView<Upper>().swap(m1); + m3.setZero(); + m3.template triangularView<Upper>().setOnes(); + VERIFY_IS_APPROX(m2,m3); + +} + + +template<typename MatrixType> void triangular_rect(const MatrixType& m) +{ + typedef const typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + enum { Rows = MatrixType::RowsAtCompileTime, Cols = MatrixType::ColsAtCompileTime }; + typedef Matrix<Scalar, Rows, 1> VectorType; + typedef Matrix<Scalar, Rows, Rows> RMatrixType; + + + Index rows = m.rows(); + Index cols = m.cols(); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2 = MatrixType::Random(rows, cols), + m3(rows, cols), + m4(rows, cols), + r1(rows, cols), + r2(rows, cols); + + MatrixType m1up = m1.template triangularView<Upper>(); + MatrixType m2up = m2.template triangularView<Upper>(); + + if (rows>1 && cols>1) + { + VERIFY(m1up.isUpperTriangular()); + VERIFY(m2up.transpose().isLowerTriangular()); + VERIFY(!m2.isLowerTriangular()); + } + + // test overloaded operator+= + r1.setZero(); + r2.setZero(); + r1.template triangularView<Upper>() += m1; + r2 += m1up; + VERIFY_IS_APPROX(r1,r2); + + // test overloaded operator= + m1.setZero(); + m1.template triangularView<Upper>() = 3 * m2; + m3 = 3 * m2; + VERIFY_IS_APPROX(m3.template triangularView<Upper>().toDenseMatrix(), m1); + + + m1.setZero(); + m1.template triangularView<Lower>() = 3 * m2; + VERIFY_IS_APPROX(m3.template triangularView<Lower>().toDenseMatrix(), m1); + + m1.setZero(); + m1.template triangularView<StrictlyUpper>() = 3 * m2; + VERIFY_IS_APPROX(m3.template triangularView<StrictlyUpper>().toDenseMatrix(), m1); + + + m1.setZero(); + m1.template triangularView<StrictlyLower>() = 3 * m2; + VERIFY_IS_APPROX(m3.template triangularView<StrictlyLower>().toDenseMatrix(), m1); + m1.setRandom(); + m2 = m1.template triangularView<Upper>(); + VERIFY(m2.isUpperTriangular()); + VERIFY(!m2.isLowerTriangular()); + m2 = m1.template triangularView<StrictlyUpper>(); + VERIFY(m2.isUpperTriangular()); + VERIFY(m2.diagonal().isMuchSmallerThan(RealScalar(1))); + m2 = m1.template triangularView<UnitUpper>(); + VERIFY(m2.isUpperTriangular()); + m2.diagonal().array() -= Scalar(1); + VERIFY(m2.diagonal().isMuchSmallerThan(RealScalar(1))); + m2 = m1.template triangularView<Lower>(); + VERIFY(m2.isLowerTriangular()); + VERIFY(!m2.isUpperTriangular()); + m2 = m1.template triangularView<StrictlyLower>(); + VERIFY(m2.isLowerTriangular()); + VERIFY(m2.diagonal().isMuchSmallerThan(RealScalar(1))); + m2 = m1.template triangularView<UnitLower>(); + VERIFY(m2.isLowerTriangular()); + m2.diagonal().array() -= Scalar(1); + VERIFY(m2.diagonal().isMuchSmallerThan(RealScalar(1))); + // test swap + m1.setOnes(); + m2.setZero(); + m2.template triangularView<Upper>().swap(m1); + m3.setZero(); + m3.template triangularView<Upper>().setOnes(); + VERIFY_IS_APPROX(m2,m3); +} + +void bug_159() +{ + Matrix3d m = Matrix3d::Random().triangularView<Lower>(); + EIGEN_UNUSED_VARIABLE(m) +} + +void test_triangular() +{ + int maxsize = (std::min)(EIGEN_TEST_MAX_SIZE,20); + for(int i = 0; i < g_repeat ; i++) + { + int r = internal::random<int>(2,maxsize); EIGEN_UNUSED_VARIABLE(r); + int c = internal::random<int>(2,maxsize); EIGEN_UNUSED_VARIABLE(c); + + CALL_SUBTEST_1( triangular_square(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( triangular_square(Matrix<float, 2, 2>()) ); + CALL_SUBTEST_3( triangular_square(Matrix3d()) ); + CALL_SUBTEST_4( triangular_square(Matrix<std::complex<float>,8, 8>()) ); + CALL_SUBTEST_5( triangular_square(MatrixXcd(r,r)) ); + CALL_SUBTEST_6( triangular_square(Matrix<float,Dynamic,Dynamic,RowMajor>(r, r)) ); + + CALL_SUBTEST_7( triangular_rect(Matrix<float, 4, 5>()) ); + CALL_SUBTEST_8( triangular_rect(Matrix<double, 6, 2>()) ); + CALL_SUBTEST_9( triangular_rect(MatrixXcf(r, c)) ); + CALL_SUBTEST_5( triangular_rect(MatrixXcd(r, c)) ); + CALL_SUBTEST_6( triangular_rect(Matrix<float,Dynamic,Dynamic,RowMajor>(r, c)) ); + } + + CALL_SUBTEST_1( bug_159() ); +} diff --git a/test/umeyama.cpp b/test/umeyama.cpp new file mode 100644 index 000000000..b6c9be3a5 --- /dev/null +++ b/test/umeyama.cpp @@ -0,0 +1,183 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Hauke Heibel <hauke.heibel@gmail.com> +// +// 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" + +#include <Eigen/Core> +#include <Eigen/Geometry> + +#include <Eigen/LU> // required for MatrixBase::determinant +#include <Eigen/SVD> // required for SVD + +using namespace Eigen; + +// Constructs a random matrix from the unitary group U(size). +template <typename T> +Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> randMatrixUnitary(int size) +{ + typedef T Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + + typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> MatrixType; + + MatrixType Q; + + int max_tries = 40; + double is_unitary = false; + + while (!is_unitary && max_tries > 0) + { + // initialize random matrix + Q = MatrixType::Random(size, size); + + // orthogonalize columns using the Gram-Schmidt algorithm + for (int col = 0; col < size; ++col) + { + typename MatrixType::ColXpr colVec = Q.col(col); + for (int prevCol = 0; prevCol < col; ++prevCol) + { + typename MatrixType::ColXpr prevColVec = Q.col(prevCol); + colVec -= colVec.dot(prevColVec)*prevColVec; + } + Q.col(col) = colVec.normalized(); + } + + // this additional orthogonalization is not necessary in theory but should enhance + // the numerical orthogonality of the matrix + for (int row = 0; row < size; ++row) + { + typename MatrixType::RowXpr rowVec = Q.row(row); + for (int prevRow = 0; prevRow < row; ++prevRow) + { + typename MatrixType::RowXpr prevRowVec = Q.row(prevRow); + rowVec -= rowVec.dot(prevRowVec)*prevRowVec; + } + Q.row(row) = rowVec.normalized(); + } + + // final check + is_unitary = Q.isUnitary(); + --max_tries; + } + + if (max_tries == 0) + eigen_assert(false && "randMatrixUnitary: Could not construct unitary matrix!"); + + return Q; +} + +// Constructs a random matrix from the special unitary group SU(size). +template <typename T> +Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> randMatrixSpecialUnitary(int size) +{ + typedef T Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + + typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> MatrixType; + + // initialize unitary matrix + MatrixType Q = randMatrixUnitary<Scalar>(size); + + // tweak the first column to make the determinant be 1 + Q.col(0) *= internal::conj(Q.determinant()); + + return Q; +} + +template <typename MatrixType> +void run_test(int dim, int num_elements) +{ + typedef typename internal::traits<MatrixType>::Scalar Scalar; + typedef Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> MatrixX; + typedef Matrix<Scalar, Eigen::Dynamic, 1> VectorX; + + // MUST be positive because in any other case det(cR_t) may become negative for + // odd dimensions! + const Scalar c = internal::abs(internal::random<Scalar>()); + + MatrixX R = randMatrixSpecialUnitary<Scalar>(dim); + VectorX t = Scalar(50)*VectorX::Random(dim,1); + + MatrixX cR_t = MatrixX::Identity(dim+1,dim+1); + cR_t.block(0,0,dim,dim) = c*R; + cR_t.block(0,dim,dim,1) = t; + + MatrixX src = MatrixX::Random(dim+1, num_elements); + src.row(dim) = Matrix<Scalar, 1, Dynamic>::Constant(num_elements, Scalar(1)); + + MatrixX dst = cR_t*src; + + MatrixX cR_t_umeyama = umeyama(src.block(0,0,dim,num_elements), dst.block(0,0,dim,num_elements)); + + const Scalar error = ( cR_t_umeyama*src - dst ).norm() / dst.norm(); + VERIFY(error < Scalar(40)*std::numeric_limits<Scalar>::epsilon()); +} + +template<typename Scalar, int Dimension> +void run_fixed_size_test(int num_elements) +{ + typedef Matrix<Scalar, Dimension+1, Dynamic> MatrixX; + typedef Matrix<Scalar, Dimension+1, Dimension+1> HomMatrix; + typedef Matrix<Scalar, Dimension, Dimension> FixedMatrix; + typedef Matrix<Scalar, Dimension, 1> FixedVector; + + const int dim = Dimension; + + // MUST be positive because in any other case det(cR_t) may become negative for + // odd dimensions! + const Scalar c = internal::abs(internal::random<Scalar>()); + + FixedMatrix R = randMatrixSpecialUnitary<Scalar>(dim); + FixedVector t = Scalar(50)*FixedVector::Random(dim,1); + + HomMatrix cR_t = HomMatrix::Identity(dim+1,dim+1); + cR_t.block(0,0,dim,dim) = c*R; + cR_t.block(0,dim,dim,1) = t; + + MatrixX src = MatrixX::Random(dim+1, num_elements); + src.row(dim) = Matrix<Scalar, 1, Dynamic>::Constant(num_elements, Scalar(1)); + + MatrixX dst = cR_t*src; + + Block<MatrixX, Dimension, Dynamic> src_block(src,0,0,dim,num_elements); + Block<MatrixX, Dimension, Dynamic> dst_block(dst,0,0,dim,num_elements); + + HomMatrix cR_t_umeyama = umeyama(src_block, dst_block); + + const Scalar error = ( cR_t_umeyama*src - dst ).array().square().sum(); + + VERIFY(error < Scalar(10)*std::numeric_limits<Scalar>::epsilon()); +} + +void test_umeyama() +{ + for (int i=0; i<g_repeat; ++i) + { + const int num_elements = internal::random<int>(40,500); + + // works also for dimensions bigger than 3... + for (int dim=2; dim<8; ++dim) + { + CALL_SUBTEST_1(run_test<MatrixXd>(dim, num_elements)); + CALL_SUBTEST_2(run_test<MatrixXf>(dim, num_elements)); + } + + CALL_SUBTEST_3((run_fixed_size_test<float, 2>(num_elements))); + CALL_SUBTEST_4((run_fixed_size_test<float, 3>(num_elements))); + CALL_SUBTEST_5((run_fixed_size_test<float, 4>(num_elements))); + + CALL_SUBTEST_6((run_fixed_size_test<double, 2>(num_elements))); + CALL_SUBTEST_7((run_fixed_size_test<double, 3>(num_elements))); + CALL_SUBTEST_8((run_fixed_size_test<double, 4>(num_elements))); + } + + // Those two calls don't compile and result in meaningful error messages! + // umeyama(MatrixXcf(),MatrixXcf()); + // umeyama(MatrixXcd(),MatrixXcd()); +} diff --git a/test/umfpack_support.cpp b/test/umfpack_support.cpp new file mode 100644 index 000000000..9eb84c14b --- /dev/null +++ b/test/umfpack_support.cpp @@ -0,0 +1,31 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Gael Guennebaud <g.gael@free.fr> +// +// 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 "sparse_solver.h" + +#include <Eigen/UmfPackSupport> + +template<typename T> void test_umfpack_support_T() +{ + UmfPackLU<SparseMatrix<T, ColMajor> > umfpack_colmajor; + UmfPackLU<SparseMatrix<T, RowMajor> > umfpack_rowmajor; + + check_sparse_square_solving(umfpack_colmajor); + check_sparse_square_solving(umfpack_rowmajor); + + check_sparse_square_determinant(umfpack_colmajor); + check_sparse_square_determinant(umfpack_rowmajor); +} + +void test_umfpack_support() +{ + CALL_SUBTEST_1(test_umfpack_support_T<double>()); + CALL_SUBTEST_2(test_umfpack_support_T<std::complex<double> >()); +} + diff --git a/test/unalignedassert.cpp b/test/unalignedassert.cpp new file mode 100644 index 000000000..601dbf214 --- /dev/null +++ b/test/unalignedassert.cpp @@ -0,0 +1,127 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" + +struct TestNew1 +{ + MatrixXd m; // good: m will allocate its own array, taking care of alignment. + TestNew1() : m(20,20) {} +}; + +struct TestNew2 +{ + Matrix3d m; // good: m's size isn't a multiple of 16 bytes, so m doesn't have to be 16-byte aligned, + // 8-byte alignment is good enough here, which we'll get automatically +}; + +struct TestNew3 +{ + Vector2f m; // good: m's size isn't a multiple of 16 bytes, so m doesn't have to be 16-byte aligned +}; + +struct TestNew4 +{ + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + Vector2d m; + float f; // make the struct have sizeof%16!=0 to make it a little more tricky when we allow an array of 2 such objects +}; + +struct TestNew5 +{ + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + float f; // try the f at first -- the EIGEN_ALIGN16 attribute of m should make that still work + Matrix4f m; +}; + +struct TestNew6 +{ + Matrix<float,2,2,DontAlign> m; // good: no alignment requested + float f; +}; + +template<bool Align> struct Depends +{ + EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(Align) + Vector2d m; + float f; +}; + +template<typename T> +void check_unalignedassert_good() +{ + T *x, *y; + x = new T; + delete x; + y = new T[2]; + delete[] y; +} + +#if EIGEN_ALIGN_STATICALLY +template<typename T> +void construct_at_boundary(int boundary) +{ + char buf[sizeof(T)+256]; + size_t _buf = reinterpret_cast<size_t>(buf); + _buf += (16 - (_buf % 16)); // make 16-byte aligned + _buf += boundary; // make exact boundary-aligned + T *x = ::new(reinterpret_cast<void*>(_buf)) T; + x[0].setZero(); // just in order to silence warnings + x->~T(); +} +#endif + +void unalignedassert() +{ + #if EIGEN_ALIGN_STATICALLY + construct_at_boundary<Vector2f>(4); + construct_at_boundary<Vector3f>(4); + construct_at_boundary<Vector4f>(16); + construct_at_boundary<Matrix2f>(16); + construct_at_boundary<Matrix3f>(4); + construct_at_boundary<Matrix4f>(16); + + construct_at_boundary<Vector2d>(16); + construct_at_boundary<Vector3d>(4); + construct_at_boundary<Vector4d>(16); + construct_at_boundary<Matrix2d>(16); + construct_at_boundary<Matrix3d>(4); + construct_at_boundary<Matrix4d>(16); + + construct_at_boundary<Vector2cf>(16); + construct_at_boundary<Vector3cf>(4); + construct_at_boundary<Vector2cd>(16); + construct_at_boundary<Vector3cd>(16); + #endif + + check_unalignedassert_good<TestNew1>(); + check_unalignedassert_good<TestNew2>(); + check_unalignedassert_good<TestNew3>(); + + check_unalignedassert_good<TestNew4>(); + check_unalignedassert_good<TestNew5>(); + check_unalignedassert_good<TestNew6>(); + check_unalignedassert_good<Depends<true> >(); + +#if EIGEN_ALIGN_STATICALLY + VERIFY_RAISES_ASSERT(construct_at_boundary<Vector4f>(8)); + VERIFY_RAISES_ASSERT(construct_at_boundary<Matrix4f>(8)); + VERIFY_RAISES_ASSERT(construct_at_boundary<Vector2d>(8)); + VERIFY_RAISES_ASSERT(construct_at_boundary<Vector4d>(8)); + VERIFY_RAISES_ASSERT(construct_at_boundary<Matrix2d>(8)); + VERIFY_RAISES_ASSERT(construct_at_boundary<Matrix4d>(8)); + VERIFY_RAISES_ASSERT(construct_at_boundary<Vector2cf>(8)); + VERIFY_RAISES_ASSERT(construct_at_boundary<Vector2cd>(8)); +#endif +} + +void test_unalignedassert() +{ + CALL_SUBTEST(unalignedassert()); +} diff --git a/test/unalignedcount.cpp b/test/unalignedcount.cpp new file mode 100644 index 000000000..5451159e6 --- /dev/null +++ b/test/unalignedcount.cpp @@ -0,0 +1,44 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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/. + +static int nb_load; +static int nb_loadu; +static int nb_store; +static int nb_storeu; + +#define EIGEN_DEBUG_ALIGNED_LOAD { nb_load++; } +#define EIGEN_DEBUG_UNALIGNED_LOAD { nb_loadu++; } +#define EIGEN_DEBUG_ALIGNED_STORE { nb_store++; } +#define EIGEN_DEBUG_UNALIGNED_STORE { nb_storeu++; } + +#define VERIFY_ALIGNED_UNALIGNED_COUNT(XPR,AL,UL,AS,US) {\ + nb_load = nb_loadu = nb_store = nb_storeu = 0; \ + XPR; \ + if(!(nb_load==AL && nb_loadu==UL && nb_store==AS && nb_storeu==US)) \ + std::cerr << " >> " << nb_load << ", " << nb_loadu << ", " << nb_store << ", " << nb_storeu << "\n"; \ + VERIFY( (#XPR) && nb_load==AL && nb_loadu==UL && nb_store==AS && nb_storeu==US ); \ + } + + +#include "main.h" + +void test_unalignedcount() +{ + #ifdef EIGEN_VECTORIZE_SSE + VectorXf a(40), b(40); + VERIFY_ALIGNED_UNALIGNED_COUNT(a += b, 20, 0, 10, 0); + VERIFY_ALIGNED_UNALIGNED_COUNT(a.segment(0,40) += b.segment(0,40), 10, 10, 10, 0); + VERIFY_ALIGNED_UNALIGNED_COUNT(a.segment(0,40) -= b.segment(0,40), 10, 10, 10, 0); + VERIFY_ALIGNED_UNALIGNED_COUNT(a.segment(0,40) *= 3.5, 10, 0, 10, 0); + VERIFY_ALIGNED_UNALIGNED_COUNT(a.segment(0,40) /= 3.5, 10, 0, 10, 0); + #else + // The following line is to eliminate "variable not used" warnings + nb_load = nb_loadu = nb_store = nb_storeu = 0; + #endif +} diff --git a/test/upperbidiagonalization.cpp b/test/upperbidiagonalization.cpp new file mode 100644 index 000000000..db6ce383e --- /dev/null +++ b/test/upperbidiagonalization.cpp @@ -0,0 +1,41 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2010 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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" +#include <Eigen/SVD> + +template<typename MatrixType> void upperbidiag(const MatrixType& m) +{ + const typename MatrixType::Index rows = m.rows(); + const typename MatrixType::Index cols = m.cols(); + + typedef typename MatrixType::Scalar Scalar; + typedef Matrix<typename MatrixType::RealScalar, MatrixType::RowsAtCompileTime, MatrixType::ColsAtCompileTime> RealMatrixType; + + MatrixType a = MatrixType::Random(rows,cols); + internal::UpperBidiagonalization<MatrixType> ubd(a); + RealMatrixType b(rows, cols); + b.setZero(); + b.block(0,0,cols,cols) = ubd.bidiagonal(); + MatrixType c = ubd.householderU() * b * ubd.householderV().adjoint(); + VERIFY_IS_APPROX(a,c); +} + +void test_upperbidiagonalization() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( upperbidiag(MatrixXf(3,3)) ); + CALL_SUBTEST_2( upperbidiag(MatrixXd(17,12)) ); + CALL_SUBTEST_3( upperbidiag(MatrixXcf(20,20)) ); + CALL_SUBTEST_4( upperbidiag(MatrixXcd(16,15)) ); + CALL_SUBTEST_5( upperbidiag(Matrix<float,6,4>()) ); + CALL_SUBTEST_6( upperbidiag(Matrix<float,5,5>()) ); + CALL_SUBTEST_7( upperbidiag(Matrix<double,4,3>()) ); + } +} diff --git a/test/vectorization_logic.cpp b/test/vectorization_logic.cpp new file mode 100644 index 000000000..aee68a87f --- /dev/null +++ b/test/vectorization_logic.cpp @@ -0,0 +1,235 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// 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/. + +#define EIGEN_DEBUG_ASSIGN +#include "main.h" +#include <typeinfo> + +std::string demangle_traversal(int t) +{ + if(t==DefaultTraversal) return "DefaultTraversal"; + if(t==LinearTraversal) return "LinearTraversal"; + if(t==InnerVectorizedTraversal) return "InnerVectorizedTraversal"; + if(t==LinearVectorizedTraversal) return "LinearVectorizedTraversal"; + if(t==SliceVectorizedTraversal) return "SliceVectorizedTraversal"; + return "?"; +} +std::string demangle_unrolling(int t) +{ + if(t==NoUnrolling) return "NoUnrolling"; + if(t==InnerUnrolling) return "InnerUnrolling"; + if(t==CompleteUnrolling) return "CompleteUnrolling"; + return "?"; +} + +template<typename Dst, typename Src> +bool test_assign(const Dst&, const Src&, int traversal, int unrolling) +{ + internal::assign_traits<Dst,Src>::debug(); + bool res = internal::assign_traits<Dst,Src>::Traversal==traversal + && internal::assign_traits<Dst,Src>::Unrolling==unrolling; + if(!res) + { + std::cerr << " Expected Traversal == " << demangle_traversal(traversal) + << " got " << demangle_traversal(internal::assign_traits<Dst,Src>::Traversal) << "\n"; + std::cerr << " Expected Unrolling == " << demangle_unrolling(unrolling) + << " got " << demangle_unrolling(internal::assign_traits<Dst,Src>::Unrolling) << "\n"; + } + return res; +} + +template<typename Dst, typename Src> +bool test_assign(int traversal, int unrolling) +{ + internal::assign_traits<Dst,Src>::debug(); + bool res = internal::assign_traits<Dst,Src>::Traversal==traversal + && internal::assign_traits<Dst,Src>::Unrolling==unrolling; + if(!res) + { + std::cerr << " Expected Traversal == " << demangle_traversal(traversal) + << " got " << demangle_traversal(internal::assign_traits<Dst,Src>::Traversal) << "\n"; + std::cerr << " Expected Unrolling == " << demangle_unrolling(unrolling) + << " got " << demangle_unrolling(internal::assign_traits<Dst,Src>::Unrolling) << "\n"; + } + return res; +} + +template<typename Xpr> +bool test_redux(const Xpr&, int traversal, int unrolling) +{ + typedef internal::redux_traits<internal::scalar_sum_op<typename Xpr::Scalar>,Xpr> traits; + bool res = traits::Traversal==traversal && traits::Unrolling==unrolling; + if(!res) + { + std::cerr << " Expected Traversal == " << demangle_traversal(traversal) + << " got " << demangle_traversal(traits::Traversal) << "\n"; + std::cerr << " Expected Unrolling == " << demangle_unrolling(unrolling) + << " got " << demangle_unrolling(traits::Unrolling) << "\n"; + } + return res; +} + +template<typename Scalar, bool Enable = internal::packet_traits<Scalar>::Vectorizable> struct vectorization_logic +{ + enum { + PacketSize = internal::packet_traits<Scalar>::size + }; + static void run() + { + + typedef Matrix<Scalar,PacketSize,1> Vector1; + typedef Matrix<Scalar,Dynamic,1> VectorX; + typedef Matrix<Scalar,Dynamic,Dynamic> MatrixXX; + typedef Matrix<Scalar,PacketSize,PacketSize> Matrix11; + typedef Matrix<Scalar,2*PacketSize,2*PacketSize> Matrix22; + typedef Matrix<Scalar,(Matrix11::Flags&RowMajorBit)?16:4*PacketSize,(Matrix11::Flags&RowMajorBit)?4*PacketSize:16> Matrix44; + typedef Matrix<Scalar,(Matrix11::Flags&RowMajorBit)?16:4*PacketSize,(Matrix11::Flags&RowMajorBit)?4*PacketSize:16,DontAlign|EIGEN_DEFAULT_MATRIX_STORAGE_ORDER_OPTION> Matrix44u; + typedef Matrix<Scalar,4*PacketSize,16,ColMajor> Matrix44c; + typedef Matrix<Scalar,4*PacketSize,16,RowMajor> Matrix44r; + + typedef Matrix<Scalar, + (PacketSize==8 ? 4 : PacketSize==4 ? 2 : PacketSize==2 ? 1 : /*PacketSize==1 ?*/ 1), + (PacketSize==8 ? 2 : PacketSize==4 ? 2 : PacketSize==2 ? 2 : /*PacketSize==1 ?*/ 1) + > Matrix1; + + typedef Matrix<Scalar, + (PacketSize==8 ? 4 : PacketSize==4 ? 2 : PacketSize==2 ? 1 : /*PacketSize==1 ?*/ 1), + (PacketSize==8 ? 2 : PacketSize==4 ? 2 : PacketSize==2 ? 2 : /*PacketSize==1 ?*/ 1), + DontAlign|((Matrix1::Flags&RowMajorBit)?RowMajor:ColMajor)> Matrix1u; + + // this type is made such that it can only be vectorized when viewed as a linear 1D vector + typedef Matrix<Scalar, + (PacketSize==8 ? 4 : PacketSize==4 ? 6 : PacketSize==2 ? ((Matrix11::Flags&RowMajorBit)?2:3) : /*PacketSize==1 ?*/ 1), + (PacketSize==8 ? 6 : PacketSize==4 ? 2 : PacketSize==2 ? ((Matrix11::Flags&RowMajorBit)?3:2) : /*PacketSize==1 ?*/ 3) + > Matrix3; + + #if !EIGEN_GCC_AND_ARCH_DOESNT_WANT_STACK_ALIGNMENT + VERIFY(test_assign(Vector1(),Vector1(), + InnerVectorizedTraversal,CompleteUnrolling)); + VERIFY(test_assign(Vector1(),Vector1()+Vector1(), + InnerVectorizedTraversal,CompleteUnrolling)); + VERIFY(test_assign(Vector1(),Vector1().cwiseProduct(Vector1()), + InnerVectorizedTraversal,CompleteUnrolling)); + VERIFY(test_assign(Vector1(),Vector1().template cast<Scalar>(), + InnerVectorizedTraversal,CompleteUnrolling)); + + + VERIFY(test_assign(Vector1(),Vector1(), + InnerVectorizedTraversal,CompleteUnrolling)); + VERIFY(test_assign(Vector1(),Vector1()+Vector1(), + InnerVectorizedTraversal,CompleteUnrolling)); + VERIFY(test_assign(Vector1(),Vector1().cwiseProduct(Vector1()), + InnerVectorizedTraversal,CompleteUnrolling)); + + VERIFY(test_assign(Matrix44(),Matrix44()+Matrix44(), + InnerVectorizedTraversal,InnerUnrolling)); + + VERIFY(test_assign(Matrix44u(),Matrix44()+Matrix44(), + LinearTraversal,NoUnrolling)); + + VERIFY(test_assign(Matrix1u(),Matrix1()+Matrix1(), + LinearTraversal,CompleteUnrolling)); + + VERIFY(test_assign(Matrix44c().col(1),Matrix44c().col(2)+Matrix44c().col(3), + InnerVectorizedTraversal,CompleteUnrolling)); + + VERIFY(test_assign(Matrix44r().row(2),Matrix44r().row(1)+Matrix44r().row(1), + InnerVectorizedTraversal,CompleteUnrolling)); + + if(PacketSize>1) + { + typedef Matrix<Scalar,3,3,ColMajor> Matrix33c; + VERIFY(test_assign(Matrix33c().row(2),Matrix33c().row(1)+Matrix33c().row(1), + LinearTraversal,CompleteUnrolling)); + VERIFY(test_assign(Matrix33c().col(0),Matrix33c().col(1)+Matrix33c().col(1), + LinearTraversal,CompleteUnrolling)); + + VERIFY(test_assign(Matrix3(),Matrix3().cwiseQuotient(Matrix3()), + LinearVectorizedTraversal,CompleteUnrolling)); + + VERIFY(test_assign(Matrix<Scalar,17,17>(),Matrix<Scalar,17,17>()+Matrix<Scalar,17,17>(), + LinearTraversal,NoUnrolling)); + + VERIFY(test_assign(Matrix11(),Matrix<Scalar,17,17>().template block<PacketSize,PacketSize>(2,3)+Matrix<Scalar,17,17>().template block<PacketSize,PacketSize>(10,4), + DefaultTraversal,CompleteUnrolling)); + } + + VERIFY(test_redux(Matrix3(), + LinearVectorizedTraversal,CompleteUnrolling)); + + VERIFY(test_redux(Matrix44(), + LinearVectorizedTraversal,NoUnrolling)); + + VERIFY(test_redux(Matrix44().template block<(Matrix1::Flags&RowMajorBit)?4:PacketSize,(Matrix1::Flags&RowMajorBit)?PacketSize:4>(1,2), + DefaultTraversal,CompleteUnrolling)); + + VERIFY(test_redux(Matrix44c().template block<2*PacketSize,1>(1,2), + LinearVectorizedTraversal,CompleteUnrolling)); + + VERIFY(test_redux(Matrix44r().template block<1,2*PacketSize>(2,1), + LinearVectorizedTraversal,CompleteUnrolling)); + + VERIFY((test_assign< + Map<Matrix22, Aligned, OuterStride<3*PacketSize> >, + Matrix22 + >(InnerVectorizedTraversal,CompleteUnrolling))); + + VERIFY((test_assign< + Map<Matrix22, Aligned, InnerStride<3*PacketSize> >, + Matrix22 + >(DefaultTraversal,CompleteUnrolling))); + + VERIFY((test_assign(Matrix11(), Matrix11()*Matrix11(), InnerVectorizedTraversal, CompleteUnrolling))); + #endif + + VERIFY(test_assign(MatrixXX(10,10),MatrixXX(20,20).block(10,10,2,3), + SliceVectorizedTraversal,NoUnrolling)); + + VERIFY(test_redux(VectorX(10), + LinearVectorizedTraversal,NoUnrolling)); + + + } +}; + +template<typename Scalar> struct vectorization_logic<Scalar,false> +{ + static void run() {} +}; + +void test_vectorization_logic() +{ + +#ifdef EIGEN_VECTORIZE + + CALL_SUBTEST( vectorization_logic<float>::run() ); + CALL_SUBTEST( vectorization_logic<double>::run() ); + CALL_SUBTEST( vectorization_logic<std::complex<float> >::run() ); + CALL_SUBTEST( vectorization_logic<std::complex<double> >::run() ); + + if(internal::packet_traits<float>::Vectorizable) + { + VERIFY(test_assign(Matrix<float,3,3>(),Matrix<float,3,3>()+Matrix<float,3,3>(), + LinearTraversal,CompleteUnrolling)); + + VERIFY(test_redux(Matrix<float,5,2>(), + DefaultTraversal,CompleteUnrolling)); + } + + if(internal::packet_traits<double>::Vectorizable) + { + VERIFY(test_assign(Matrix<double,3,3>(),Matrix<double,3,3>()+Matrix<double,3,3>(), + LinearTraversal,CompleteUnrolling)); + + VERIFY(test_redux(Matrix<double,7,3>(), + DefaultTraversal,CompleteUnrolling)); + } +#endif // EIGEN_VECTORIZE + +} diff --git a/test/vectorwiseop.cpp b/test/vectorwiseop.cpp new file mode 100644 index 000000000..b938e3957 --- /dev/null +++ b/test/vectorwiseop.cpp @@ -0,0 +1,172 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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/. + +#define EIGEN_NO_STATIC_ASSERT + +#include "main.h" + +template<typename ArrayType> void vectorwiseop_array(const ArrayType& m) +{ + typedef typename ArrayType::Index Index; + typedef typename ArrayType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Array<Scalar, ArrayType::RowsAtCompileTime, 1> ColVectorType; + typedef Array<Scalar, 1, ArrayType::ColsAtCompileTime> RowVectorType; + + Index rows = m.rows(); + Index cols = m.cols(); + Index r = internal::random<Index>(0, rows-1), + c = internal::random<Index>(0, cols-1); + + ArrayType m1 = ArrayType::Random(rows, cols), + m2(rows, cols), + m3(rows, cols); + + ColVectorType colvec = ColVectorType::Random(rows); + RowVectorType rowvec = RowVectorType::Random(cols); + + // test addition + + m2 = m1; + m2.colwise() += colvec; + VERIFY_IS_APPROX(m2, m1.colwise() + colvec); + VERIFY_IS_APPROX(m2.col(c), m1.col(c) + colvec); + + VERIFY_RAISES_ASSERT(m2.colwise() += colvec.transpose()); + VERIFY_RAISES_ASSERT(m1.colwise() + colvec.transpose()); + + m2 = m1; + m2.rowwise() += rowvec; + VERIFY_IS_APPROX(m2, m1.rowwise() + rowvec); + VERIFY_IS_APPROX(m2.row(r), m1.row(r) + rowvec); + + VERIFY_RAISES_ASSERT(m2.rowwise() += rowvec.transpose()); + VERIFY_RAISES_ASSERT(m1.rowwise() + rowvec.transpose()); + + // test substraction + + m2 = m1; + m2.colwise() -= colvec; + VERIFY_IS_APPROX(m2, m1.colwise() - colvec); + VERIFY_IS_APPROX(m2.col(c), m1.col(c) - colvec); + + VERIFY_RAISES_ASSERT(m2.colwise() -= colvec.transpose()); + VERIFY_RAISES_ASSERT(m1.colwise() - colvec.transpose()); + + m2 = m1; + m2.rowwise() -= rowvec; + VERIFY_IS_APPROX(m2, m1.rowwise() - rowvec); + VERIFY_IS_APPROX(m2.row(r), m1.row(r) - rowvec); + + VERIFY_RAISES_ASSERT(m2.rowwise() -= rowvec.transpose()); + VERIFY_RAISES_ASSERT(m1.rowwise() - rowvec.transpose()); + + // test multiplication + + m2 = m1; + m2.colwise() *= colvec; + VERIFY_IS_APPROX(m2, m1.colwise() * colvec); + VERIFY_IS_APPROX(m2.col(c), m1.col(c) * colvec); + + VERIFY_RAISES_ASSERT(m2.colwise() *= colvec.transpose()); + VERIFY_RAISES_ASSERT(m1.colwise() * colvec.transpose()); + + m2 = m1; + m2.rowwise() *= rowvec; + VERIFY_IS_APPROX(m2, m1.rowwise() * rowvec); + VERIFY_IS_APPROX(m2.row(r), m1.row(r) * rowvec); + + VERIFY_RAISES_ASSERT(m2.rowwise() *= rowvec.transpose()); + VERIFY_RAISES_ASSERT(m1.rowwise() * rowvec.transpose()); + + // test quotient + + m2 = m1; + m2.colwise() /= colvec; + VERIFY_IS_APPROX(m2, m1.colwise() / colvec); + VERIFY_IS_APPROX(m2.col(c), m1.col(c) / colvec); + + VERIFY_RAISES_ASSERT(m2.colwise() /= colvec.transpose()); + VERIFY_RAISES_ASSERT(m1.colwise() / colvec.transpose()); + + m2 = m1; + m2.rowwise() /= rowvec; + VERIFY_IS_APPROX(m2, m1.rowwise() / rowvec); + VERIFY_IS_APPROX(m2.row(r), m1.row(r) / rowvec); + + VERIFY_RAISES_ASSERT(m2.rowwise() /= rowvec.transpose()); + VERIFY_RAISES_ASSERT(m1.rowwise() / rowvec.transpose()); +} + +template<typename MatrixType> void vectorwiseop_matrix(const MatrixType& m) +{ + typedef typename MatrixType::Index Index; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits<Scalar>::Real RealScalar; + typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> ColVectorType; + typedef Matrix<Scalar, 1, MatrixType::ColsAtCompileTime> RowVectorType; + + Index rows = m.rows(); + Index cols = m.cols(); + Index r = internal::random<Index>(0, rows-1), + c = internal::random<Index>(0, cols-1); + + MatrixType m1 = MatrixType::Random(rows, cols), + m2(rows, cols), + m3(rows, cols); + + ColVectorType colvec = ColVectorType::Random(rows); + RowVectorType rowvec = RowVectorType::Random(cols); + + // test addition + + m2 = m1; + m2.colwise() += colvec; + VERIFY_IS_APPROX(m2, m1.colwise() + colvec); + VERIFY_IS_APPROX(m2.col(c), m1.col(c) + colvec); + + VERIFY_RAISES_ASSERT(m2.colwise() += colvec.transpose()); + VERIFY_RAISES_ASSERT(m1.colwise() + colvec.transpose()); + + m2 = m1; + m2.rowwise() += rowvec; + VERIFY_IS_APPROX(m2, m1.rowwise() + rowvec); + VERIFY_IS_APPROX(m2.row(r), m1.row(r) + rowvec); + + VERIFY_RAISES_ASSERT(m2.rowwise() += rowvec.transpose()); + VERIFY_RAISES_ASSERT(m1.rowwise() + rowvec.transpose()); + + // test substraction + + m2 = m1; + m2.colwise() -= colvec; + VERIFY_IS_APPROX(m2, m1.colwise() - colvec); + VERIFY_IS_APPROX(m2.col(c), m1.col(c) - colvec); + + VERIFY_RAISES_ASSERT(m2.colwise() -= colvec.transpose()); + VERIFY_RAISES_ASSERT(m1.colwise() - colvec.transpose()); + + m2 = m1; + m2.rowwise() -= rowvec; + VERIFY_IS_APPROX(m2, m1.rowwise() - rowvec); + VERIFY_IS_APPROX(m2.row(r), m1.row(r) - rowvec); + + VERIFY_RAISES_ASSERT(m2.rowwise() -= rowvec.transpose()); + VERIFY_RAISES_ASSERT(m1.rowwise() - rowvec.transpose()); +} + +void test_vectorwiseop() +{ + CALL_SUBTEST_1(vectorwiseop_array(Array22cd())); + CALL_SUBTEST_2(vectorwiseop_array(Array<double, 3, 2>())); + CALL_SUBTEST_3(vectorwiseop_array(ArrayXXf(3, 4))); + CALL_SUBTEST_4(vectorwiseop_matrix(Matrix4cf())); + CALL_SUBTEST_5(vectorwiseop_matrix(Matrix<float,4,5>())); + CALL_SUBTEST_6(vectorwiseop_matrix(MatrixXd(7,2))); +} diff --git a/test/visitor.cpp b/test/visitor.cpp new file mode 100644 index 000000000..087306258 --- /dev/null +++ b/test/visitor.cpp @@ -0,0 +1,118 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename MatrixType> void matrixVisitor(const MatrixType& p) +{ + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::Index Index; + + Index rows = p.rows(); + Index cols = p.cols(); + + // construct a random matrix where all coefficients are different + MatrixType m; + m = MatrixType::Random(rows, cols); + for(Index i = 0; i < m.size(); i++) + for(Index i2 = 0; i2 < i; i2++) + while(m(i) == m(i2)) // yes, == + m(i) = internal::random<Scalar>(); + + Scalar minc = Scalar(1000), maxc = Scalar(-1000); + Index minrow=0,mincol=0,maxrow=0,maxcol=0; + for(Index j = 0; j < cols; j++) + for(Index i = 0; i < rows; i++) + { + if(m(i,j) < minc) + { + minc = m(i,j); + minrow = i; + mincol = j; + } + if(m(i,j) > maxc) + { + maxc = m(i,j); + maxrow = i; + maxcol = j; + } + } + Index eigen_minrow, eigen_mincol, eigen_maxrow, eigen_maxcol; + Scalar eigen_minc, eigen_maxc; + eigen_minc = m.minCoeff(&eigen_minrow,&eigen_mincol); + eigen_maxc = m.maxCoeff(&eigen_maxrow,&eigen_maxcol); + VERIFY(minrow == eigen_minrow); + VERIFY(maxrow == eigen_maxrow); + VERIFY(mincol == eigen_mincol); + VERIFY(maxcol == eigen_maxcol); + VERIFY_IS_APPROX(minc, eigen_minc); + VERIFY_IS_APPROX(maxc, eigen_maxc); + VERIFY_IS_APPROX(minc, m.minCoeff()); + VERIFY_IS_APPROX(maxc, m.maxCoeff()); +} + +template<typename VectorType> void vectorVisitor(const VectorType& w) +{ + typedef typename VectorType::Scalar Scalar; + typedef typename VectorType::Index Index; + + Index size = w.size(); + + // construct a random vector where all coefficients are different + VectorType v; + v = VectorType::Random(size); + for(Index i = 0; i < size; i++) + for(Index i2 = 0; i2 < i; i2++) + while(v(i) == v(i2)) // yes, == + v(i) = internal::random<Scalar>(); + + Scalar minc = Scalar(1000), maxc = Scalar(-1000); + Index minidx=0,maxidx=0; + for(Index i = 0; i < size; i++) + { + if(v(i) < minc) + { + minc = v(i); + minidx = i; + } + if(v(i) > maxc) + { + maxc = v(i); + maxidx = i; + } + } + Index eigen_minidx, eigen_maxidx; + Scalar eigen_minc, eigen_maxc; + eigen_minc = v.minCoeff(&eigen_minidx); + eigen_maxc = v.maxCoeff(&eigen_maxidx); + VERIFY(minidx == eigen_minidx); + VERIFY(maxidx == eigen_maxidx); + VERIFY_IS_APPROX(minc, eigen_minc); + VERIFY_IS_APPROX(maxc, eigen_maxc); + VERIFY_IS_APPROX(minc, v.minCoeff()); + VERIFY_IS_APPROX(maxc, v.maxCoeff()); +} + +void test_visitor() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_1( matrixVisitor(Matrix<float, 1, 1>()) ); + CALL_SUBTEST_2( matrixVisitor(Matrix2f()) ); + CALL_SUBTEST_3( matrixVisitor(Matrix4d()) ); + CALL_SUBTEST_4( matrixVisitor(MatrixXd(8, 12)) ); + CALL_SUBTEST_5( matrixVisitor(Matrix<double,Dynamic,Dynamic,RowMajor>(20, 20)) ); + CALL_SUBTEST_6( matrixVisitor(MatrixXi(8, 12)) ); + } + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST_7( vectorVisitor(Vector4f()) ); + CALL_SUBTEST_8( vectorVisitor(VectorXd(10)) ); + CALL_SUBTEST_9( vectorVisitor(RowVectorXd(10)) ); + CALL_SUBTEST_10( vectorVisitor(VectorXf(33)) ); + } +} diff --git a/test/zerosized.cpp b/test/zerosized.cpp new file mode 100644 index 000000000..6905e584e --- /dev/null +++ b/test/zerosized.cpp @@ -0,0 +1,59 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Benoit Jacob <jacob.benoit.1@gmail.com> +// +// 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<typename MatrixType> void zeroSizedMatrix() +{ + MatrixType t1; + + if (MatrixType::SizeAtCompileTime == Dynamic) + { + if (MatrixType::RowsAtCompileTime == Dynamic) + VERIFY(t1.rows() == 0); + if (MatrixType::ColsAtCompileTime == Dynamic) + VERIFY(t1.cols() == 0); + + if (MatrixType::RowsAtCompileTime == Dynamic && MatrixType::ColsAtCompileTime == Dynamic) + { + MatrixType t2(0, 0); + VERIFY(t2.rows() == 0); + VERIFY(t2.cols() == 0); + } + } +} + +template<typename VectorType> void zeroSizedVector() +{ + VectorType t1; + + if (VectorType::SizeAtCompileTime == Dynamic) + { + VERIFY(t1.size() == 0); + VectorType t2(DenseIndex(0)); // DenseIndex disambiguates with 0-the-null-pointer (error with gcc 4.4 and MSVC8) + VERIFY(t2.size() == 0); + } +} + +void test_zerosized() +{ + zeroSizedMatrix<Matrix2d>(); + zeroSizedMatrix<Matrix3i>(); + zeroSizedMatrix<Matrix<float, 2, Dynamic> >(); + zeroSizedMatrix<MatrixXf>(); + zeroSizedMatrix<Matrix<float, 0, 0> >(); + zeroSizedMatrix<Matrix<float, Dynamic, 0, 0, 0, 0> >(); + zeroSizedMatrix<Matrix<float, 0, Dynamic, 0, 0, 0> >(); + zeroSizedMatrix<Matrix<float, Dynamic, Dynamic, 0, 0, 0> >(); + + zeroSizedVector<Vector2d>(); + zeroSizedVector<Vector3i>(); + zeroSizedVector<VectorXf>(); + zeroSizedVector<Matrix<float, 0, 1> >(); +} |