blob: 533b74ba3531091735058a334106c65cf9b662e4 [file]
// 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/.
// SPDX-License-Identifier: MPL-2.0
#define EIGEN_NO_DEBUG_SMALL_PRODUCT_BLOCKS
#include "sparse_solver.h"
#include <Eigen/PaStiXSupport>
#include <contrib/Eigen/SparseExtra>
template <typename Solver>
void test_pastix_reuse() {
using Scalar = typename Solver::Scalar;
using Real = typename NumTraits<Scalar>::Real;
using Sparse = typename Solver::MatrixType;
using Dense = Matrix<Scalar, Dynamic, Dynamic, RowMajor>;
Sparse matrix(5, 5);
const Scalar offDiagonal = Scalar(-1) + internal::random<Scalar>() * Scalar(0.25);
for (Index i = 0; i < matrix.rows(); ++i) {
matrix.insert(i, i) = Scalar(4);
if (i > 0) matrix.insert(i, i - 1) = offDiagonal;
if (i + 1 < matrix.rows()) matrix.insert(i, i + 1) = numext::conj(offDiagonal);
}
Dense expected(5, 2);
for (Index i = 0; i < expected.rows(); ++i) {
expected(i, 0) = Scalar(i + 1);
expected(i, 1) = Scalar(2 - i);
}
Solver solver;
solver.iparm(IPARM_THREAD_NBR) = 1;
solver.dparm(DPARM_EPSILON_REFINEMENT) = NumTraits<Real>::epsilon();
solver.analyzePattern(matrix);
VERIFY_IS_EQUAL(solver.info(), Success);
for (int iteration = 0; iteration < 2; ++iteration) {
for (Index i = 0; i < matrix.rows(); ++i) matrix.coeffRef(i, i) += Scalar(1);
solver.factorize(matrix);
VERIFY_IS_EQUAL(solver.info(), Success);
const Dense rhs = matrix * expected;
const Dense result = solver.solve(rhs);
VERIFY_IS_EQUAL(solver.info(), Success);
const Real errorBound = Real(128) * NumTraits<Real>::epsilon() * expected.norm();
VERIFY((result - expected).norm() <= errorBound);
}
solver.compute(matrix);
VERIFY_IS_EQUAL(solver.info(), Success);
const Dense rhs = matrix * expected;
const Dense result = solver.solve(rhs);
VERIFY_IS_EQUAL(solver.info(), Success);
VERIFY_IS_APPROX(result, expected);
Sparse diagonal(2, 2);
diagonal.insert(0, 0) = Scalar(2);
diagonal.insert(1, 1) = Scalar(3);
solver.compute(diagonal);
VERIFY_IS_EQUAL(solver.info(), Success);
const Matrix<Scalar, 2, 1> diagonalRhs(Scalar(2), Scalar(3));
const Matrix<Scalar, 2, 1> diagonalResult = solver.solve(diagonalRhs);
VERIFY_IS_EQUAL(solver.info(), Success);
VERIFY_IS_APPROX(diagonalResult, (Matrix<Scalar, 2, 1>::Ones()));
Sparse empty(0, 0);
solver.compute(empty);
VERIFY_IS_EQUAL(solver.info(), Success);
const Dense emptyRhs(0, 2);
const Dense emptyResult = solver.solve(emptyRhs);
VERIFY_IS_EQUAL(solver.info(), Success);
VERIFY_IS_EQUAL(emptyResult.rows(), 0);
VERIFY_IS_EQUAL(emptyResult.cols(), 2);
solver.analyzePattern(empty);
solver.factorize(empty);
VERIFY_IS_EQUAL(solver.info(), Success);
solver.compute(matrix);
const Dense noRhs(matrix.rows(), 0);
const Dense noResult = solver.solve(noRhs);
VERIFY_IS_EQUAL(solver.info(), Success);
VERIFY_IS_EQUAL(noResult.cols(), 0);
}
template <typename Scalar>
void test_pastix_special_cases() {
using Sparse = SparseMatrix<Scalar, RowMajor, long long>;
test_pastix_reuse<PastixLU<Sparse>>();
test_pastix_reuse<PastixLLT<Sparse, Lower>>();
test_pastix_reuse<PastixLDLT<Sparse, Upper>>();
for (int emptyRow = 0; emptyRow < 2; ++emptyRow) {
Sparse singular(2, 2);
singular.insert(0, 0) = Scalar(1);
if (!emptyRow) {
singular.insert(0, 1) = Scalar(1);
singular.insert(1, 0) = Scalar(1);
singular.insert(1, 1) = Scalar(1);
}
PastixLU<Sparse> solver;
solver.iparm(IPARM_THREAD_NBR) = 1;
// A fixed tiny perturbation makes these inconsistent systems overflow during solve.
solver.dparm(DPARM_EPSILON_MAGN_CTRL) = -(std::numeric_limits<double>::min)();
solver.compute(singular);
if (solver.info() == Success) {
const Matrix<Scalar, 2, 1> rhs(Scalar(1), Scalar(8));
const Matrix<Scalar, 2, 1> result = solver.solve(rhs);
EIGEN_UNUSED_VARIABLE(result);
VERIFY_IS_EQUAL(solver.info(), NumericalIssue);
} else {
VERIFY_IS_EQUAL(solver.info(), NumericalIssue);
}
}
}
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;
pastix_llt_lower.iparm(IPARM_THREAD_NBR) = 1;
pastix_ldlt_lower.iparm(IPARM_THREAD_NBR) = 1;
pastix_llt_upper.iparm(IPARM_THREAD_NBR) = 1;
pastix_ldlt_upper.iparm(IPARM_THREAD_NBR) = 1;
pastix_lu.iparm(IPARM_THREAD_NBR) = 1;
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);
// Some compilation check:
pastix_llt_lower.iparm();
pastix_llt_lower.dparm();
pastix_ldlt_lower.iparm();
pastix_ldlt_lower.dparm();
pastix_lu.iparm();
pastix_lu.dparm();
}
EIGEN_DECLARE_TEST(pastix_support) {
CALL_SUBTEST_1(test_pastix_T<float>());
CALL_SUBTEST_2(test_pastix_T<double>());
CALL_SUBTEST_3((test_pastix_T<std::complex<float>>()));
CALL_SUBTEST_4(test_pastix_T<std::complex<double>>());
CALL_SUBTEST_1(test_pastix_special_cases<float>());
CALL_SUBTEST_2(test_pastix_special_cases<double>());
CALL_SUBTEST_3(test_pastix_special_cases<std::complex<float>>());
CALL_SUBTEST_4(test_pastix_special_cases<std::complex<double>>());
}