29#ifndef math_eigenLapack_hpp
30#define math_eigenLapack_hpp
32#pragma GCC system_header
62template <
typename floatT>
63struct eigenLapackTestHooks
66 using allocatorT =
void *(*)( std::size_t );
69 using solverT = MXLAPACK_INT ( * )( char,
91 static inline allocatorT allocator{
nullptr };
94 static inline solverT solver{
nullptr };
98template <
typename floatT,
typename itemT>
99bool resizeSyevrBuffer( itemT *&buffer,
100 MXLAPACK_INT &capacity,
101 MXLAPACK_INT requestedItems )
103 if( capacity >= requestedItems )
108 void *rawBuffer{
nullptr };
109 if( eigenLapackTestHooks<floatT>::allocator )
111 rawBuffer = eigenLapackTestHooks<floatT>::allocator( requestedItems *
sizeof( itemT ) );
115 rawBuffer = ::malloc( requestedItems *
sizeof( itemT ) );
118 if( rawBuffer ==
nullptr )
128 buffer =
static_cast<itemT *
>( rawBuffer );
129 capacity = requestedItems;
134template <
typename itemT>
135void releaseSyevrBuffer( itemT *&buffer,
136 MXLAPACK_INT &capacity )
148template <
typename floatT>
149MXLAPACK_INT callSyevr(
char JOBZ,
164 MXLAPACK_INT *ISUPPZ,
168 MXLAPACK_INT LIWORK )
170 if( eigenLapackTestHooks<floatT>::solver )
172 return eigenLapackTestHooks<floatT>::solver( JOBZ,
229template <
typename eigenT1,
typename eigenT2>
235 cv.resize( ims.cols(), ims.cols() );
253template <
typename floatT>
269 MXLAPACK_INT
IL{ 0 };
272 MXLAPACK_INT
IU{ 0 };
305 Eigen::Array<floatT, Eigen::Dynamic, Eigen::Dynamic>
cvd;
308 Eigen::Array<floatT, Eigen::Dynamic, Eigen::Dynamic>
evecsd;
311 Eigen::Array<floatT, Eigen::Dynamic, Eigen::Dynamic>
evalsd;
359template <
typename arrT>
376 typedef typename arrT::Scalar calcT;
378 MXLAPACK_INT numeig{ 0 };
382 MXLAPACK_INT n = X.rows();
383 if( n <= 0 || X.cols() != n )
388 if( ev1 < -1 || ( ev1 != -1 && ( ev0 < 0 || ev0 >= ev1 || ev1 > n ) ) )
393 MXLAPACK_INT localMem = 0;
410 eigvec.resize( n, IU - IL + 1 );
411 eigval.resize( n, 1 );
413 if( UPLO != mem->UPLO || n != mem->n || RANGE != mem->RANGE || mem->IL != IL || mem->IU != IU )
415 if( !detail::resizeSyevrBuffer<calcT>( mem->iSuppZ, mem->sizeISuppZ, 2 * n ) ||
416 !detail::resizeSyevrBuffer<calcT>( mem->minWork, mem->sizeMinWork, 26 * n ) ||
417 !detail::resizeSyevrBuffer<calcT>( mem->minIWork, mem->sizeMinIWork, 10 * n ) )
428 info = detail::callSyevr<calcT>(
'V',
460 const calcT queriedWork = mem->minWork[0];
461 const MXLAPACK_INT requestedIWork = mem->minIWork[0];
463 static_cast<long double>( queriedWork ) >
464 static_cast<long double>( std::numeric_limits<MXLAPACK_INT>::max() ) ||
475 const MXLAPACK_INT requestedWork =
static_cast<MXLAPACK_INT
>( queriedWork );
476 if( !detail::resizeSyevrBuffer<calcT>( mem->work, mem->sizeWork, requestedWork ) ||
477 !detail::resizeSyevrBuffer<calcT>( mem->iWork, mem->sizeIWork, requestedIWork ) )
490 mem->numeig = numeig;
496 info = detail::callSyevr<calcT>(
'V',
536template <
typename _evCalcT =
double,
typename eigenT>
543 bool normalize =
false,
550 double *t_eigenv =
nullptr
554 typedef _evCalcT evCalcT;
555 typedef typename eigenT::Scalar realT;
557 bool localMem =
false;
565 if( cv.rows() <= 0 || cv.rows() != cv.cols() )
567 std::cerr <<
"calcEigenVecs: covariance matrix must be non-empty and square\n";
575 MXLAPACK_INT tNims = cv.rows();
577 if( nVecs <= 0 || nVecs > tNims )
587 mem->cvd = cv.template cast<evCalcT>();
592 MXLAPACK_INT info =
eigenSYEVR( mem->evecsd, mem->evalsd, mem->cvd, tNims - nVecs, tNims,
'L', mem );
596 std::cerr <<
"calcEigenVecs: eigenSYEVR returned an error (info = " << info <<
")\n";
605 evecs = mem->evecsd.template cast<realT>();
606 evals = mem->evalsd.template cast<realT>();
613 for( MXLAPACK_INT i = 0; i < nVecs; ++i )
615 evecs.col( i ) = evecs.col( i ) / sqrt( evals( i ) );
620 for( MXLAPACK_INT i = 0; i < nVecs; ++i )
624 std::cerr <<
"got non-finite eigenvalue (# " << i <<
")\n";
625 evecs.col( i ).setZero();
627 else if( evals( i ) == 0 )
629 std::cerr <<
"got 0 eigenvalue (# " << i <<
")\n";
630 evecs.col( i ).setZero();
632 else if( evals( i ) < 0 )
634 std::cerr <<
"got < 0 eigenvalue (# " << i <<
")\n";
635 evecs.col( i ).setZero();
639 evecs.col( i ) = evecs.col( i ) / sqrt( evals( i ) );
642 for(
int r = 0; r < evecs.rows(); ++r )
646 std::cerr <<
"got non-finite eigenvector entry (# " << i <<
"," << r <<
")\n";
647 evecs.col( i ).setZero();
678template <
typename _evCalcT =
double,
typename eigenT,
typename eigenT1>
687 double *t_eigenv =
nullptr,
689 double *t_klim =
nullptr
692 typedef _evCalcT evCalcT;
693 typedef typename eigenT::Scalar realT;
695 bool localMem =
false;
703 if( cv.rows() != Rims.cols() )
705 std::cerr <<
"Covariance matrix - reference image size mismatch in calcKLModes\n";
715 MXLAPACK_INT tNims = cv.rows();
716 MXLAPACK_INT tNpix = Rims.rows();
718 if( n_modes <= 0 || n_modes > tNims )
724 mem->cvd = cv.template cast<evCalcT>();
725 MXLAPACK_INT info =
calcEigenVecs( mem->evecsd, mem->evalsd, mem->cvd, n_modes,
true,
true, mem, t_eigenv );
729 std::cerr <<
"calckKLModes: eigenSYEVR returned an error (info = " << info <<
")\n";
737 evecs = mem->evecsd.template cast<realT>();
738 evals = mem->evalsd.template cast<realT>();
740 klModes.resize( n_modes, tNpix );
751 constexpr realT alpha{ 1 };
752 constexpr realT beta{ 0 };
793template <
typename dataT>
795 Eigen::Array<dataT, -1, -1> &S,
796 Eigen::Array<dataT, -1, -1> &VT,
798 Eigen::Array<dataT, -1, -1> &A
802 MXLAPACK_INT M = A.rows();
803 MXLAPACK_INT N = A.cols();
804 MXLAPACK_INT LDA = M;
807 MXLAPACK_INT LDU = M;
809 MXLAPACK_INT LDVT = N;
812 MXLAPACK_INT LWORK = -1;
814 MXLAPACK_INT *IWORK =
new MXLAPACK_INT[8 * M];
818 math::gesdd<dataT>( JOBZ, M, N, A.data(), LDA, S.data(), U.data(), LDU, VT.data(), LDVT, &wkOpt, LWORK, IWORK );
822 dataT *WORK =
new dataT[LWORK];
825 math::gesdd<dataT>( JOBZ, M, N, A.data(), LDA, S.data(), U.data(), LDU, VT.data(), LDVT, WORK, LWORK, IWORK );
833#define MX_PINV_NO_INTERACT 0
834#define MX_PINV_PLOT 1
836#define MX_PINV_ASK_NMODES 4
853template <
typename dataT>
857 Eigen::Array<dataT, -1, -1> &U,
858 Eigen::Array<dataT, -1, -1> &S,
859 Eigen::Array<dataT, -1, -1> &VT,
873 int interact = MX_PINV_NO_INTERACT
878 dataT Smax = S.maxCoeff();
882 for( MXLAPACK_INT i = 0; i < S.rows(); ++i )
884 S( i ) = ( pow( S( i ), 2 ) + pow( alpha * Smax, 2 ) ) / S( i );
890 for( MXLAPACK_INT i = 0; i < S.rows(); ++i )
892 S( i ) = S( i ) + -alpha * Smax;
896 int modesToReject = 0;
897 if( maxCondition < 0 )
899 modesToReject = -maxCondition;
901 if( modesToReject - 1 < S.rows() )
903 maxCondition = Smax / S( modesToReject - 1, 0 );
907 if( interact & MX_PINV_PLOT )
910 gp.
command(
"set title \"SVD Singular Values\"" );
912 gp.
plot( S.data(), S.rows(),
" w lp",
"singular values" );
915 if( interact & MX_PINV_ASK && !( interact & MX_PINV_ASK_NMODES ) )
918 std::cout <<
"Maximum singular value: " << Smax <<
"\n";
919 std::cout <<
"Minimum singular value: " << S.minCoeff() <<
"\n";
920 std::cout <<
"Enter singular value threshold: ";
925 maxCondition = Smax / mine;
929 maxCondition = Smax / S( S.rows() - 1, 0 );
932 else if( interact & MX_PINV_ASK_NMODES )
935 std::cout <<
"Maximum singular value: " << Smax <<
"\n";
936 std::cout <<
"Minimum singular value: " << S.minCoeff() <<
"\n";
937 std::cout <<
"Enter number of modes to keep: ";
939 modesToReject = S.rows() - mine;
941 if( modesToReject <= 0 || modesToReject > S.rows() )
946 maxCondition = -modesToReject;
949 Eigen::Array<dataT, -1, -1> sigma;
950 sigma.resize( S.rows(), S.rows() );
955 if( maxCondition > 0 )
959 if( maxCondition > 0 )
961 threshold = Smax / maxCondition;
965 for( MXLAPACK_INT i = 0; i < S.rows(); ++i )
967 if( S( i ) >= threshold )
969 sigma( i, i ) = 1. / S( i );
970 if( Smax / S( i ) > condition )
972 condition = Smax / S( i );
985 std::cerr <<
"rejecting based on modes\n";
986 std::cerr <<
" modes to reject: " << modesToReject <<
"\n";
987 for( MXLAPACK_INT i = 0; i < S.rows(); ++i )
989 if( i < S.rows() - modesToReject )
991 sigma( i, i ) = 1. / S( i );
992 if( Smax / S( i ) > condition )
993 condition = Smax / S( i );
1003 if( interact & MX_PINV_PLOT )
1005 std::vector<dataT> vsig( sigma.rows() );
1006 for(
int rr = 0; rr < sigma.rows(); ++rr )
1008 vsig[rr] = sigma( rr, rr );
1012 gp.
command(
"set title \"Inverted Singular Values\"" );
1014 gp.
plot( vsig.data(), vsig.size(),
" w lp",
"inverted singular values" );
1017 if( interact & MX_PINV_ASK || interact & MX_PINV_ASK_NMODES )
1020 std::cout <<
"Modes Rejected: " << nRejected <<
"\n";
1021 std::cout <<
"Condition Number: " << condition <<
"\n";
1024 PInv = ( VT.matrix().transpose() * sigma.matrix().transpose() ) *
1025 U.block( 0, 0, U.rows(), minMN ).matrix().transpose();
1045template <
typename dataT>
1049 Eigen::Array<dataT, -1, -1> &U,
1050 Eigen::Array<dataT, -1, -1> &S,
1051 Eigen::Array<dataT, -1, -1> &VT,
1053 Eigen::Array<dataT, -1, -1> &A,
1054 dataT &maxCondition,
1065 int interact = MX_PINV_NO_INTERACT
1070 int minMN = std::min( A.rows(), A.cols() );
1077 std::cerr <<
"eigenPseudoInverse: eigenGESDD failed with info = " << info <<
"\n";
1081 return eigenPseudoInverse( PInv, condition, nRejected, U, S, VT, minMN, maxCondition, alpha, interact );
1101template <
typename dataT>
1106 Eigen::Array<dataT, -1, -1> &A,
1108 dataT &maxCondition,
1119 int interact = MX_PINV_NO_INTERACT
1123 Eigen::Array<dataT, -1, -1> S, U, VT;
1125 return eigenPseudoInverse( PInv, condition, nRejected, U, S, VT, A, maxCondition, alpha, interact );
An interactive c++ interface to gnuplot.
int plot(const std::string &fname, const std::string &modifiers, const std::string &title, const std::string &name)
Plot from a file specifying all curve components.
int logy()
Set the y axis to log scale.
int command(const std::string &com, bool flush=true)
Send a command to gnuplot.
Floating-point classification utilities that remain reliable under fast-math optimization.
Declaration and definition of an interface to the gnuplot program.
MXLAPACK_INT calcKLModes(eigenT &klModes, eigenT &cv, const eigenT1 &Rims, int n_modes=0, syevrMem< _evCalcT > *mem=0, double *t_eigenv=nullptr, double *t_klim=nullptr)
Calculate the K-L modes, or principle components, given a covariance matrix.
MXLAPACK_INT calcEigenVecs(eigenT &evecs, eigenT &evals, eigenT &cv, int nVecs=0, bool normalize=false, bool check=false, syevrMem< _evCalcT > *mem=0, double *t_eigenv=nullptr)
Calculate the eigenvectors and eigenvalues given a triangular matrix.
void eigenSYRK(eigenT1 &cv, const eigenT2 &ims)
Calculates the lower triangular part of the covariance matrix of ims.
int eigenPseudoInverse(Eigen::Array< dataT, -1, -1 > &PInv, dataT &condition, int &nRejected, Eigen::Array< dataT, -1, -1 > &U, Eigen::Array< dataT, -1, -1 > &S, Eigen::Array< dataT, -1, -1 > &VT, int minMN, dataT &maxCondition, dataT alpha=0, int interact=MX_PINV_NO_INTERACT)
Calculate the pseudo-inverse of a patrix given its SVD.
MXLAPACK_INT eigenSYEVR(arrT &eigvec, arrT &eigval, arrT &X, int ev0=0, int ev1=-1, char UPLO='L', syevrMem< typename arrT::Scalar > *mem=0)
Calculate select eigenvalues and eigenvectors of an Eigen Array.
MXLAPACK_INT eigenGESDD(Eigen::Array< dataT, -1, -1 > &U, Eigen::Array< dataT, -1, -1 > &S, Eigen::Array< dataT, -1, -1 > &VT, Eigen::Array< dataT, -1, -1 > &A)
Compute the SVD of an Eigen::Array using LAPACK's xgesdd.
@ lapackerr
An error was returned by Lapack.
@ allocerr
An error occurred during memory allocation.
error_t mxlib_error_report(const error_t &code, const std::string &expl, const std::source_location &loc=std::source_location::current())
Print a report to stderr given an mxlib error_t code and explanation and return the code.
bool isFinite(realT value)
Test whether a floating-point value is finite, including under finite-math-only optimization.
void syrk(const CBLAS_ORDER Order, const CBLAS_UPLO Uplo, const CBLAS_TRANSPOSE Trans, const int N, const int K, const dataT &alpha, const dataT *A, const int lda, const dataT &beta, dataT *C, const int ldc)
Template Wrapper for cblas xSYRK.
void gemm(const CBLAS_ORDER Order, const CBLAS_TRANSPOSE TransA, const CBLAS_TRANSPOSE TransB, const int M, const int N, const int K, const dataT &alpha, const dataT *A, const int lda, const dataT *B, const int ldb, const dataT &beta, dataT *C, const int ldc)
Template Wrapper for cblas xGEMM.
MXLAPACK_INT gesdd(char JOBZ, MXLAPACK_INT M, MXLAPACK_INT N, dataT *A, MXLAPACK_INT LDA, dataT *S, dataT *U, MXLAPACK_INT LDU, dataT *VT, MXLAPACK_INT LDVT, dataT *WORK, MXLAPACK_INT LWORK, MXLAPACK_INT *IWORK)
Compute the singular value decomposition (SVD) of a real matrix with GESDD.
MXLAPACK_INT syevr(char JOBZ, char RANGE, char UPLO, MXLAPACK_INT N, dataT *A, MXLAPACK_INT LDA, dataT VL, dataT VU, MXLAPACK_INT IL, MXLAPACK_INT IU, dataT ABSTOL, MXLAPACK_INT *M, dataT *W, dataT *Z, MXLAPACK_INT LDZ, MXLAPACK_INT *ISUPPZ, dataT *WORK, MXLAPACK_INT LWORK, MXLAPACK_INT *IWORK, MXLAPACK_INT LIWORK)
Compute selected eigenvalues and, optionally, eigenvectors of a real symmetric matrix.
dataT lamch(char CMACH)
Determine machine parameters.
typeT get_curr_time()
Get the current system time in seconds.
A struct to hold the working memory for eigenSYEVR and maintain it between calls if desired.
MXLAPACK_INT IU
One-based upper eigenvalue index associated with the cached workspace query.
MXLAPACK_INT * iSuppZ
LAPACK eigenvector-support workspace owned by this object.
syevrMem()
Construct an empty reusable SYEVR workspace.
floatT * work
Optimized floating-point LAPACK workspace owned by this object.
MXLAPACK_INT sizeISuppZ
Capacity of the eigenvector-support workspace.
void free()
Release all workspace allocations and reset the cached LAPACK configuration.
MXLAPACK_INT n
Matrix order associated with the cached workspace query.
~syevrMem()
Release every allocation owned by this workspace.
MXLAPACK_INT sizeMinWork
Capacity of the minimum floating-point query workspace.
MXLAPACK_INT IL
One-based lower eigenvalue index associated with the cached workspace query.
Eigen::Array< floatT, Eigen::Dynamic, Eigen::Dynamic > evalsd
Calculation-type eigenvalue storage used by higher-level eigensolver helpers.
char UPLO
Triangle selection associated with the cached workspace query.
MXLAPACK_INT sizeMinIWork
Capacity of the minimum integer query workspace.
MXLAPACK_INT * iWork
Optimized integer LAPACK workspace owned by this object.
syevrMem(const syevrMem &other)=delete
Copying an owning SYEVR workspace is prohibited.
Eigen::Array< floatT, Eigen::Dynamic, Eigen::Dynamic > cvd
Calculation-type covariance storage used by higher-level eigensolver helpers.
MXLAPACK_INT * minIWork
Minimum integer workspace used for LAPACK size queries.
syevrMem & operator=(const syevrMem &other)=delete
Copy assignment of an owning SYEVR workspace is prohibited.
MXLAPACK_INT sizeWork
Capacity of the optimized floating-point workspace.
MXLAPACK_INT numeig
Number of eigenvalues reported by the most recent workspace query.
floatT * minWork
Minimum floating-point workspace used for LAPACK size queries.
Eigen::Array< floatT, Eigen::Dynamic, Eigen::Dynamic > evecsd
Calculation-type eigenvector storage used by higher-level eigensolver helpers.
char RANGE
Eigenvalue-range selection associated with the cached workspace query.
MXLAPACK_INT sizeIWork
Capacity of the optimized integer workspace.
Declares and defines templatized wrappers for the BLAS.
Declares and defines templatized wrappers for the Lapack library.
Utilities for working with time.