1#ifndef LINEARALGEBRA_SRC_MOLPRO_LINALG_ITERATIVESOLVER_HELPER_IMPLEMENTATION_H_
2#define LINEARALGEBRA_SRC_MOLPRO_LINALG_ITERATIVESOLVER_HELPER_IMPLEMENTATION_H_
5#include <molpro/Profiler.h>
6#include <molpro/lapacke.h>
7#include <molpro/linalg/itsolv/helper.h>
10#include "subspace/Matrix.h"
25template <
typename value_type>
28 auto mat = Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(m.
data(), nrows, ncols);
29#if EIGEN_VERSION_AT_LEAST(3, 4, 90)
32 auto svd = Eigen::JacobiSVD<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(
33 mat,
static_cast<unsigned int>(Eigen::ComputeThinV) |
34 static_cast<unsigned int>(Eigen::NoQRPreconditioner));
36 auto svd = Eigen::JacobiSVD<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>, Eigen::NoQRPreconditioner>(
37 mat, Eigen::ComputeThinV);
39 auto svd_system = std::list<SVD<value_type>>{};
40 auto sv = svd.singularValues();
41 for (
int i =
int(ncols) - 1; i >= 0; --i) {
42 if (std::abs(sv(i)) < threshold) {
46 for (
size_t j = 0; j < ncols; ++j) {
47 t.v.emplace_back(svd.matrixV()(j, i));
55template <
typename value_type>
58 auto mat = Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(m.
data(), nrows, ncols);
59 auto svd = Eigen::BDCSVD<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(mat, Eigen::ComputeThinV);
60 auto svd_system = std::list<SVD<value_type>>{};
61 auto sv = svd.singularValues();
62 for (
int i =
int(ncols) - 1; i >= 0; --i) {
63 if (std::abs(sv(i)) < threshold) {
67 for (
size_t j = 0; j < ncols; ++j) {
68 t.v.emplace_back(svd.matrixV()(j, i));
77template <
typename value_type>
83 int sdim = std::min(m, n);
84 std::vector<double> sv(sdim), u(nrows * nrows), v(ncols * ncols);
85 info = LAPACKE_dgesdd(LAPACK_ROW_MAJOR,
'A',
int(nrows),
int(ncols),
const_cast<double*
>(mat.
data()),
int(ncols),
86 sv.data(), u.data(),
int(nrows), v.data(),
int(ncols));
87 auto svd_system = std::list<SVD<value_type>>{};
88 for (
int i =
int(ncols) - 1; i >= 0; --i) {
89 if (std::abs(sv[i]) < threshold) {
90 auto t = SVD<value_type>{};
93 for (
size_t j = 0; j < ncols; ++j) {
94 t.v.emplace_back(v[i * ncols + j]);
102template <
typename value_type>
103std::list<SVD<value_type>> svd_lapacke_dgesvd(
size_t nrows,
size_t ncols,
const array::Span<value_type>& mat,
108 int sdim = std::min(m, n);
109 std::vector<double> sv(sdim), u(nrows * nrows), v(ncols * ncols);
110 std::vector<double> superb(sdim - 1);
111 info = LAPACKE_dgesvd(LAPACK_ROW_MAJOR,
'N',
'A',
int(nrows),
int(ncols),
const_cast<double*
>(mat.data()),
int(ncols),
112 sv.data(), u.data(),
int(nrows), v.data(),
int(ncols), superb.data());
113 auto svd_system = std::list<SVD<value_type>>{};
114 for (
int i =
int(ncols) - 1; i >= 0; --i) {
115 if (std::abs(sv[i]) < threshold) {
116 auto t = SVD<value_type>{};
119 for (
size_t j = 0; j < ncols; ++j) {
120 t.v.emplace_back(v[i * ncols + j]);
131extern "C" int dsyev_c(
char,
char,
int,
double*,
int,
double*);
144 std::span<double> eigenvalues,
const size_t dimension) {
147 if (eigenvectors.size() != matrix.size()) {
148 throw std::runtime_error(
"Matrix of eigenvectors and input matrix are not the same size!");
151 if (eigenvectors.size() != dimension * dimension || eigenvalues.size() != dimension) {
152 throw std::runtime_error(
"Size of eigenvectors/eigenvlaues do not match dimension!");
157 static const char compute_eigenvalues_eigenvectors =
'V';
159 static const char store_lower_triangle =
'L';
162 std::copy(matrix.begin(), matrix.end(), eigenvectors.begin());
166 lapack_int leading_dimension = dimension;
167 lapack_int order = dimension;
171 status = dsyev_c(compute_eigenvalues_eigenvectors, store_lower_triangle, order, eigenvectors.data(),
172 leading_dimension, eigenvalues.data());
174 status = LAPACKE_dsyev(LAPACK_COL_MAJOR, compute_eigenvalues_eigenvectors, store_lower_triangle, order,
175 eigenvectors.data(), leading_dimension, eigenvalues.data());
189 std::vector<double> eigvecs(dimension * dimension);
190 std::vector<double> eigvals(dimension);
195 throw std::invalid_argument(
"Invalid argument of eigensolver_lapacke_dsyev: ");
198 throw std::runtime_error(
"Lapacke_dsyev (eigensolver) failed to converge. "
199 " elements of an intermediate tridiagonal form did not converge to zero.");
202 auto eigensystem = std::list<SVD<double>>{};
205 for (
int i = dimension - 1; i >= 0;
208 temp_eigenproblem.
value = eigvals[i];
209 for (
size_t j = 0; j < dimension; j++) {
210 temp_eigenproblem.v.emplace_back(eigvecs[j + (dimension * i)]);
212 eigensystem.emplace_back(temp_eigenproblem);
225template <
typename value_type>
226size_t get_rank(std::span<value_type> eigenvalues, value_type threshold) {
227 if (eigenvalues.size() == 0) {
230 value_type max = *max_element(eigenvalues.begin(), eigenvalues.end());
231 value_type threshold_scaled = threshold * max;
233 std::count_if(eigenvalues.begin(), eigenvalues.end(), [&](
auto const& val) { return val >= threshold_scaled; });
244template <
typename value_type>
247 value_type max_value = 0;
248 std::list<SVD<double>>::iterator it;
250 if (it->value > max_value) {
251 max_value = it->value;
255 value_type threshold_scaled = threshold * max_value;
260 if (it->value > threshold_scaled) {
267template <
typename value_type,
typename std::enable_if_t<!is_complex<value_type>{}, std::
nullptr_t>>
269 bool hermitian,
bool reduce_to_rank) {
270 std::list<SVD<value_type>> svds;
271 assert(m.
size() == nrows * ncols);
275 assert(nrows == ncols);
277 for (
auto s = svds.begin(); s != svds.end();)
278 if (s->value > threshold)
288 svds = svd_eigen_jacobi<value_type>(nrows, ncols, m, threshold);
293 if (reduce_to_rank) {
294 int rank =
get_rank(svds, threshold);
295 for (
int i = ncols; i > rank; i--) {
302template <
typename value_type,
typename std::enable_if_t<is_complex<value_type>{},
int>>
304 bool hermitian,
bool reduce_to_rank) {
309template <
typename value_type>
310void printMatrix(
const std::vector<value_type>& m,
size_t rows,
size_t cols, std::string title, std::ostream& s) {
312 << Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(m.data(), rows, cols) << std::endl;
315template <
typename value_type,
typename std::enable_if_t<is_complex<value_type>{},
int>>
316void eigenproblem(std::vector<value_type>& eigenvectors, std::vector<value_type>& eigenvalues,
317 const std::vector<value_type>& matrix,
const std::vector<value_type>& metric,
size_t dimension,
318 bool hermitian,
double svdThreshold,
int verbosity,
bool condone_complex) {
322template <
typename value_type,
typename std::enable_if_t<!is_complex<value_type>{}, std::
nullptr_t>>
323void eigenproblem(std::vector<value_type>& eigenvectors, std::vector<value_type>& eigenvalues,
324 const std::vector<value_type>& matrix,
const std::vector<value_type>& metric,
size_t dimension,
325 bool hermitian,
double svdThreshold,
int verbosity,
bool condone_complex) {
326 using MatrixT = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>;
327 using ComplexMatrixT = Eigen::Matrix<std::complex<value_type>, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>;
328 using MatrixRowMajT = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
329 using VectorT = Eigen::Vector<value_type, Eigen::Dynamic>;
330 using ComplexVectorT = Eigen::Vector<std::complex<value_type>, Eigen::Dynamic>;
333 prof->start(
"itsolv::eigenproblem");
334 Eigen::Map<const MatrixRowMajT> HrowMajor(
335 matrix.data(), dimension, dimension);
336 MatrixT H(dimension, dimension);
338 Eigen::Map<const MatrixT> S(metric.data(), dimension, dimension);
339 ComplexMatrixT subspaceEigenvectors;
340 ComplexVectorT subspaceEigenvalues;
343 VectorT metricEvals(dimension);
344 MatrixT metricEvecs(dimension, dimension);
351 { metricEvals.data(), dimension }, dimension);
353 throw std::runtime_error(
"Eigensolver did not converge");
355 rank =
get_rank(std::span(metricEvals.data(), dimension), svdThreshold);
357 if (verbosity > 1 && rank <
S.cols())
358 molpro::cout <<
"SVD rank " << rank <<
" in subspace of dimension " <<
S.cols() << std::endl;
359 if (verbosity > 2 && rank <
S.cols())
360 molpro::cout <<
"singular values " << metricEvals.transpose() << std::endl;
366 auto svmh = metricEvals.head(rank);
367 for (
auto k = 0; k < rank; k++) {
368 assert(std::abs(svmh(k)) <= 1e-14 || svmh(k) >= 0);
369 svmh(k) = svmh(k) > 1e-14 ? 1 / std::sqrt(svmh(k)) : 0;
372 svmh.asDiagonal() * metricEvecs.leftCols(rank).adjoint() *
H * metricEvecs.leftCols(rank) * svmh.asDiagonal();
375 Eigen::EigenSolver<MatrixT> s(Hbar);
376 subspaceEigenvalues = s.eigenvalues();
377 if (s.eigenvalues().imag().norm() < 1e-10) {
379 subspaceEigenvalues = subspaceEigenvalues.real();
380 subspaceEigenvectors = s.eigenvectors();
383 for (
int i = 0; i < subspaceEigenvectors.cols() - 1; i++) {
384 if (subspaceEigenvectors.col(i).imag().norm() <= 1e-10) {
389 if (std::abs(subspaceEigenvalues(i) - subspaceEigenvalues(j)) >= 1e-10 or
390 subspaceEigenvectors.col(j).imag().norm() <= 1e-10) {
398 subspaceEigenvectors.col(j) = subspaceEigenvectors.col(i).imag() / subspaceEigenvectors.col(i).imag().norm();
399 subspaceEigenvectors.col(i) = subspaceEigenvectors.col(i).real() / subspaceEigenvectors.col(i).real().norm();
403 subspaceEigenvectors = metricEvecs.leftCols(rank) * svmh.asDiagonal() * subspaceEigenvectors;
406#ifdef __INTEL_COMPILER
407 molpro::cout <<
"Hbar\n" << Hbar << std::endl;
408 molpro::cout <<
"Eigenvalues\n" << s.eigenvalues() << std::endl;
409 molpro::cout <<
"Eigenvectors\n" << s.eigenvectors() << std::endl;
410 throw std::runtime_error(
"Intel compiler does not support working with complex eigen3 entities properly");
414 subspaceEigenvectors = metricEvecs.leftCols(rank) * svmh.asDiagonal() * s.eigenvectors();
418 Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> perm(subspaceEigenvalues.size());
420 std::ranges::sort(perm.indices(), std::less<>{}, [&subspaceEigenvalues](
auto idx) { return subspaceEigenvalues[idx].real(); });
423 subspaceEigenvectors = subspaceEigenvectors * perm;
424 subspaceEigenvalues = perm.transpose() * subspaceEigenvalues;
431 for (
auto repeat = 0; repeat < 1; ++repeat)
432 for (Eigen::Index k = 0; k < subspaceEigenvectors.cols(); k++) {
433 if (std::abs(subspaceEigenvalues(k)) < 1e-12) {
435 subspaceEigenvectors.col(k).real() += double(0.3256897) * subspaceEigenvectors.col(k).imag();
436 subspaceEigenvectors.col(k).imag().setZero();
439 auto ovl = subspaceEigenvectors.col(k).dot(S * subspaceEigenvectors.col(k));
441 assert(std::abs(ovl.imag()) < 1e-10);
442 assert(ovl.real() >= 0);
443 subspaceEigenvectors.col(k) /= std::sqrt(ovl.real());
448 for (std::size_t i = 0; i < subspaceEigenvectors.cols(); ++i) {
449 const auto &col = subspaceEigenvectors.col(i);
450 auto it = std::ranges::max_element(col, std::less<>{}, [](
auto val) {
return std::abs(val); });
451 auto idx = std::distance(col.begin(), it);
452 if (subspaceEigenvectors.col(i)[idx].real() < 0) {
453 subspaceEigenvectors.col(i) *= -1;
457 if (condone_complex) {
458 for (Eigen::Index root = 0; root < Hbar.cols(); ++root) {
459 if (subspaceEigenvalues(root).imag() != 0) {
461 subspaceEigenvalues(root) = subspaceEigenvalues(root + 1) = subspaceEigenvalues(root).real();
462 subspaceEigenvectors.col(root) = subspaceEigenvectors.col(root).real();
463 subspaceEigenvectors.col(root + 1) = subspaceEigenvectors.col(root + 1).imag();
469 if ((subspaceEigenvectors - subspaceEigenvectors.real()).norm() > 1e-10 or
470 (subspaceEigenvalues - subspaceEigenvalues.real()).norm() > 1e-10) {
471 throw std::runtime_error(
"unexpected complex solution found");
474 eigenvectors.resize(dimension * Hbar.cols());
475 eigenvalues.resize(Hbar.cols());
477 Eigen::Map<MatrixT>(eigenvectors.data(), dimension, Hbar.cols()) =
478 subspaceEigenvectors.real();
479 Eigen::Map<VectorT> ev(eigenvalues.data(), Hbar.cols());
480 ev = subspaceEigenvalues.real();
485template <
typename value_type,
typename std::enable_if_t<is_complex<value_type>{},
int>>
487 const std::vector<value_type>& matrix,
const std::vector<value_type>& metric,
488 const std::vector<value_type>& rhs,
const size_t dimension,
size_t nroot,
489 double augmented_hessian,
double svdThreshold,
int verbosity) {
493template <
typename value_type,
typename std::enable_if_t<!is_complex<value_type>{}, std::
nullptr_t>>
495 const std::vector<value_type>& matrix,
const std::vector<value_type>& metric,
496 const std::vector<value_type>& rhs,
const size_t dimension,
size_t nroot,
497 double augmented_hessian,
double svdThreshold,
int verbosity) {
498 const Eigen::Index nX = dimension;
499 solution.resize(nX * nroot);
501 if (augmented_hessian > 0) {
502 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> subspaceMatrix;
503 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> subspaceOverlap;
504 subspaceMatrix.conservativeResize(nX + 1, nX + 1);
505 subspaceOverlap.conservativeResize(nX + 1, nX + 1);
506 subspaceMatrix.block(0, 0, nX, nX) =
507 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(matrix.data(), nX, nX);
508 subspaceOverlap.block(0, 0, nX, nX) =
509 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(metric.data(), nX, nX);
510 eigenvalues.resize(nroot);
511 for (
size_t root = 0; root < nroot; root++) {
512 for (Eigen::Index i = 0; i < nX; i++) {
513 subspaceMatrix(i, nX) = subspaceMatrix(nX, i) = -augmented_hessian * rhs[i + nX * root];
514 subspaceOverlap(i, nX) = subspaceOverlap(nX, i) = 0;
516 subspaceMatrix(nX, nX) = 0;
517 subspaceOverlap(nX, nX) = 1;
521 Eigen::GeneralizedEigenSolver<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>> s(subspaceMatrix,
523 auto eval = s.eigenvalues();
524 auto evec = s.eigenvectors();
525 Eigen::Index imax = 0;
526 for (Eigen::Index i = 0; i < nX + 1; i++)
527 if (eval(i).real() < eval(imax).real())
529 eigenvalues[root] = eval(imax).real();
530 auto Solution = evec.col(imax).real().head(nX) / (augmented_hessian * evec.real()(nX, imax));
531 for (
auto k = 0; k < nX; k++)
532 solution[k + nX * root] = Solution(k);
536 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> subspaceMatrixR(
537 matrix.data(), nX, nX);
538 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> RHS_R(rhs.data(), nX,
540 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> subspaceMatrix = subspaceMatrixR;
541 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> RHS = RHS_R;
542 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> Solution;
551 Solution = subspaceMatrix.householderQr().solve(RHS);
553 for (
size_t root = 0; root < nroot; root++)
554 for (
auto k = 0; k < nX; k++)
555 solution[k + nX * root] = Solution(k, root);
559template <
typename value_type,
typename std::enable_if_t<!is_complex<value_type>{}, std::
nullptr_t>>
560void solve_DIIS(std::vector<value_type>& solution,
const std::vector<value_type>& matrix,
const size_t dimension,
561 double svdThreshold,
int verbosity) {
562 auto nAug = dimension + 1;
564 solution.resize(dimension);
566 Eigen::VectorXd Rhs(nAug), Coeffs(nAug);
567 Eigen::MatrixXd BAug(nAug, nAug);
571 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>> subspaceMatrix(matrix.data(), dimension,
573 BAug.block(0, 0, dimension, dimension) = subspaceMatrix;
574 for (
size_t i = 0; i < dimension; ++i) {
575 BAug(dimension, i) = BAug(i, dimension) = -1;
578 BAug(dimension, dimension) = 0;
585 Eigen::JacobiSVD<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>> svd(BAug, Eigen::ComputeThinU |
586 Eigen::ComputeThinV);
590 svd.setThreshold(svdThreshold * svd.singularValues().maxCoeff() * 0);
595 Coeffs = svd.solve(Rhs).head(dimension);
599 molpro::cout <<
"Combination of iteration vectors: " << Coeffs.transpose() << std::endl;
600 for (
size_t k = 0; k < (size_t)Coeffs.rows(); k++) {
601 if (std::isnan(std::abs(Coeffs(k)))) {
602 molpro::cout <<
"B:" << std::endl << BAug << std::endl;
603 molpro::cout <<
"Rhs:" << std::endl << Rhs << std::endl;
604 molpro::cout <<
"Combination of iteration vectors: " << Coeffs.transpose() << std::endl;
605 throw std::overflow_error(
"NaN detected in DIIS submatrix solution");
607 solution[k] = Coeffs(k);
628template <
typename value_type,
typename value_type_abs>
630 const value_type_abs svd_thresh,
Logger& logger) {
632 prof->start(
"itsolv::svd_system");
633 logger.
trace(
"redundant_parameters()");
634 auto redundant_params = std::vector<int>{};
635 auto rspace_indices = std::vector<int>(nR);
636 std::iota(std::begin(rspace_indices), std::end(rspace_indices), 0);
637 auto svd =
svd_system(overlap.rows(), overlap.cols(),
638 array::Span(
const_cast<value_type*
>(overlap.data().data()), overlap.size()), svd_thresh,
true);
640 prof->start(
"find redundant parameters");
641 for (
const auto& singular_system : svd) {
642 if (!rspace_indices.empty()) {
643 auto rspace_contribution = std::vector<value_type_abs>{};
644 for (
auto i : rspace_indices)
645 rspace_contribution.push_back(std::abs(singular_system.v.at(oR + i)));
646 auto it_min = std::max_element(std::begin(rspace_contribution), std::end(rspace_contribution));
647 auto imin = std::distance(std::begin(rspace_contribution), it_min);
648 redundant_params.push_back(rspace_indices[imin]);
649 rspace_indices.erase(std::begin(rspace_indices) + imin);
650 std::stringstream ss;
651 ss << std::setprecision(3) <<
"redundant parameter found, i = " << redundant_params.back()
652 <<
", svd.value = " << singular_system.value
653 <<
", svd.v[i] = " << singular_system.v[oR + redundant_params.back()];
654 logger.
info(ss.str());
658 return redundant_params;
Non-owning container taking a pointer to the data buffer and its size and exposing routines for itera...
Definition: Span.h:31
bool empty() const
Definition: Span.h:79
size_type size() const
Definition: Span.h:77
iterator data()
Definition: Span.h:66
void info(std::string_view message, Ts &&...args) const
Definition: Logger.h:491
void trace(std::string_view message, Ts &&...args) const
Definition: Logger.h:481
static std::shared_ptr< Profiler > single()
Definition: helper-implementation.h:613
auto redundant_parameters(const subspace::Matrix< value_type > &overlap, const size_t oR, const size_t nR, const value_type_abs svd_thresh, Logger &logger)
Deduces a set of parameters that are redundant due to linear dependencies.
Definition: helper-implementation.h:629
4-parameter interpolation of a 1-dimensional function given two points for which function values and ...
Definition: helper.h:11
std::list< SVD< value_type > > svd_eigen_bdcsvd(size_t nrows, size_t ncols, const array::Span< value_type > &m, double threshold)
Definition: helper-implementation.h:56
int eigensolver_lapacke_dsyev(std::span< const double > matrix, std::span< double > eigenvectors, std::span< double > eigenvalues, const size_t dimension)
Definition: helper-implementation.h:143
std::list< SVD< value_type > > svd_system(size_t nrows, size_t ncols, const array::Span< value_type > &m, double threshold, bool hermitian=false, bool reduce_to_rank=false)
Performs singular value decomposition and returns SVD objects for singular values less than threshold...
Definition: helper-implementation.h:268
void solve_LinearEquations(std::vector< value_type > &solution, std::vector< value_type > &eigenvalues, const std::vector< value_type > &matrix, const std::vector< value_type > &metric, const std::vector< value_type > &rhs, size_t dimension, size_t nroot, double augmented_hessian, double svdThreshold, int verbosity)
Definition: helper-implementation.h:486
size_t get_rank(std::vector< value_type > eigenvalues, value_type threshold)
void solve_DIIS(std::vector< value_type > &solution, const std::vector< value_type > &matrix, size_t dimension, double svdThreshold, int verbosity=0)
Definition: helper-implementation.h:560
void eigenproblem(std::vector< value_type > &eigenvectors, std::vector< value_type > &eigenvalues, const std::vector< value_type > &matrix, const std::vector< value_type > &metric, size_t dimension, bool hermitian, double svdThreshold, int verbosity, bool condone_complex)
Definition: helper-implementation.h:316
void printMatrix(const std::vector< value_type > &, size_t rows, size_t cols, std::string title="", std::ostream &s=molpro::cout)
Definition: helper-implementation.h:310
std::list< SVD< value_type > > svd_eigen_jacobi(size_t nrows, size_t ncols, const array::Span< value_type > &m, double threshold)
Definition: helper-implementation.h:26
Stores a singular value and corresponding left and right singular vectors.
Definition: helper.h:20
value_type value
Definition: helper.h:22