LCOV - code coverage report
Current view: top level - include/math - eigenLapack.hpp (source / functions) Coverage Total Hit
Test: mxlib Lines: 65.4 % 312 204
Test Date: 2026-08-30 22:23:37 Functions: 71.4 % 14 10

            Line data    Source code
       1              : /** \file eigenLapack.hpp
       2              :  * \brief Interfaces to Lapack and BLAS for Eigen-like arrays.
       3              :  *
       4              :  * \author Jared R. Males (jaredmales@gmail.com)
       5              :  *
       6              :  * \ingroup gen_math_files
       7              :  *
       8              :  */
       9              : 
      10              : //***********************************************************************//
      11              : // Copyright 2015, 2016, 2017 Jared R. Males (jaredmales@gmail.com)
      12              : //
      13              : // This file is part of mxlib.
      14              : //
      15              : // mxlib is free software: you can redistribute it and/or modify
      16              : // it under the terms of the GNU General Public License as published by
      17              : // the Free Software Foundation, either version 3 of the License, or
      18              : // (at your option) any later version.
      19              : //
      20              : // mxlib is distributed in the hope that it will be useful,
      21              : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      22              : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      23              : // GNU General Public License for more details.
      24              : //
      25              : // You should have received a copy of the GNU General Public License
      26              : // along with mxlib.  If not, see <http://www.gnu.org/licenses/>.
      27              : //***********************************************************************//
      28              : 
      29              : #ifndef math_eigenLapack_hpp
      30              : #define math_eigenLapack_hpp
      31              : 
      32              : #pragma GCC system_header
      33              : #include <Eigen/Dense>
      34              : 
      35              : #include <cstddef>
      36              : #include <cmath>
      37              : #include <cstdlib>
      38              : #include <limits>
      39              : 
      40              : #include "floatUtils.hpp"
      41              : #include "templateBLAS.hpp"
      42              : #include "templateLapack.hpp"
      43              : 
      44              : #include "../sys/timeUtils.hpp"
      45              : 
      46              : // #include "vectorUtils.hpp"
      47              : 
      48              : #include "../math/plot/gnuPlot.hpp"
      49              : 
      50              : namespace mx
      51              : {
      52              : namespace math
      53              : {
      54              : 
      55              : /// \cond eigenLapack_test_detail
      56              : namespace detail
      57              : {
      58              : 
      59              : /// Test-only injection points for deterministic eigensolver failure coverage.
      60              : /** Production code must leave both hooks null so the normal allocator and LAPACK wrapper are used.
      61              :  */
      62              : template <typename floatT>
      63              : struct eigenLapackTestHooks
      64              : {
      65              :     /// Malloc-compatible allocation function used by workspace-growth tests.
      66              :     using allocatorT = void *(*)( std::size_t );
      67              : 
      68              :     /// Function signature of the LAPACK SYEVR wrapper.
      69              :     using solverT = MXLAPACK_INT ( * )( char,
      70              :                                         char,
      71              :                                         char,
      72              :                                         MXLAPACK_INT,
      73              :                                         floatT *,
      74              :                                         MXLAPACK_INT,
      75              :                                         floatT,
      76              :                                         floatT,
      77              :                                         MXLAPACK_INT,
      78              :                                         MXLAPACK_INT,
      79              :                                         floatT,
      80              :                                         MXLAPACK_INT *,
      81              :                                         floatT *,
      82              :                                         floatT *,
      83              :                                         MXLAPACK_INT,
      84              :                                         MXLAPACK_INT *,
      85              :                                         floatT *,
      86              :                                         MXLAPACK_INT,
      87              :                                         MXLAPACK_INT *,
      88              :                                         MXLAPACK_INT );
      89              : 
      90              :     /// Optional malloc-compatible test allocator; null selects `::malloc`.
      91              :     static inline allocatorT allocator{ nullptr };
      92              : 
      93              :     /// Optional test eigensolver; null selects `math::syevr<floatT>`.
      94              :     static inline solverT solver{ nullptr };
      95              : };
      96              : 
      97              : /// Allocate or grow a SYEVR workspace buffer without discarding a usable buffer on failure.
      98              : template <typename floatT, typename itemT>
      99          113 : bool resizeSyevrBuffer( itemT *&buffer,         /**< [in,out] workspace buffer */
     100              :                         MXLAPACK_INT &capacity, /**< [in,out] current item capacity */
     101              :                         MXLAPACK_INT requestedItems /**< [in] required item capacity */ )
     102              : {
     103          113 :     if( capacity >= requestedItems )
     104              :     {
     105           10 :         return true;
     106              :     }
     107              : 
     108          103 :     void *rawBuffer{ nullptr };
     109          103 :     if( eigenLapackTestHooks<floatT>::allocator )
     110              :     {
     111           21 :         rawBuffer = eigenLapackTestHooks<floatT>::allocator( requestedItems * sizeof( itemT ) );
     112              :     }
     113              :     else
     114              :     {
     115           82 :         rawBuffer = ::malloc( requestedItems * sizeof( itemT ) );
     116              :     }
     117              : 
     118          103 :     if( rawBuffer == nullptr )
     119              :     {
     120            7 :         return false;
     121              :     }
     122              : 
     123           96 :     if( buffer )
     124              :     {
     125            5 :         ::free( buffer );
     126              :     }
     127              : 
     128           96 :     buffer = static_cast<itemT *>( rawBuffer );
     129           96 :     capacity = requestedItems;
     130           96 :     return true;
     131              : }
     132              : 
     133              : /// Release a SYEVR workspace buffer and reset its ownership state.
     134              : template <typename itemT>
     135          155 : void releaseSyevrBuffer( itemT *&buffer, /**< [in,out] workspace buffer */
     136              :                          MXLAPACK_INT &capacity /**< [out] reset item capacity */ )
     137              : {
     138          155 :     if( buffer )
     139              :     {
     140           91 :         ::free( buffer );
     141              :     }
     142              : 
     143          155 :     buffer = nullptr;
     144          155 :     capacity = 0;
     145          155 : }
     146              : 
     147              : /// Invoke the injected test eigensolver or the production LAPACK wrapper.
     148              : template <typename floatT>
     149           42 : MXLAPACK_INT callSyevr( char JOBZ,            /**< [in] eigenvector request */
     150              :                         char RANGE,           /**< [in] eigenvalue selection mode */
     151              :                         char UPLO,            /**< [in] populated input triangle */
     152              :                         MXLAPACK_INT N,       /**< [in] matrix order */
     153              :                         floatT *A,            /**< [in,out] input matrix */
     154              :                         MXLAPACK_INT LDA,     /**< [in] input leading dimension */
     155              :                         floatT VL,            /**< [in] lower value bound */
     156              :                         floatT VU,            /**< [in] upper value bound */
     157              :                         MXLAPACK_INT IL,      /**< [in] one-based lower index */
     158              :                         MXLAPACK_INT IU,      /**< [in] one-based upper index */
     159              :                         floatT ABSTOL,        /**< [in] convergence tolerance */
     160              :                         MXLAPACK_INT *M,      /**< [out] selected eigenvalue count */
     161              :                         floatT *W,            /**< [out] eigenvalues */
     162              :                         floatT *Z,            /**< [out] eigenvectors */
     163              :                         MXLAPACK_INT LDZ,     /**< [in] eigenvector leading dimension */
     164              :                         MXLAPACK_INT *ISUPPZ, /**< [out] eigenvector support */
     165              :                         floatT *WORK,         /**< [in,out] floating workspace */
     166              :                         MXLAPACK_INT LWORK,   /**< [in] floating workspace size */
     167              :                         MXLAPACK_INT *IWORK,  /**< [in,out] integer workspace */
     168              :                         MXLAPACK_INT LIWORK /**< [in] integer workspace size */ )
     169              : {
     170           42 :     if( eigenLapackTestHooks<floatT>::solver )
     171              :     {
     172           15 :         return eigenLapackTestHooks<floatT>::solver( JOBZ,
     173              :                                                      RANGE,
     174              :                                                      UPLO,
     175              :                                                      N,
     176              :                                                      A,
     177              :                                                      LDA,
     178              :                                                      VL,
     179              :                                                      VU,
     180              :                                                      IL,
     181              :                                                      IU,
     182              :                                                      ABSTOL,
     183              :                                                      M,
     184              :                                                      W,
     185              :                                                      Z,
     186              :                                                      LDZ,
     187              :                                                      ISUPPZ,
     188              :                                                      WORK,
     189              :                                                      LWORK,
     190              :                                                      IWORK,
     191           15 :                                                      LIWORK );
     192              :     }
     193              : 
     194           27 :     return math::syevr<floatT>( JOBZ,
     195              :                                 RANGE,
     196              :                                 UPLO,
     197              :                                 N,
     198              :                                 A,
     199              :                                 LDA,
     200              :                                 VL,
     201              :                                 VU,
     202              :                                 IL,
     203              :                                 IU,
     204              :                                 ABSTOL,
     205              :                                 M,
     206              :                                 W,
     207              :                                 Z,
     208              :                                 LDZ,
     209              :                                 ISUPPZ,
     210              :                                 WORK,
     211              :                                 LWORK,
     212              :                                 IWORK,
     213           27 :                                 LIWORK );
     214              : }
     215              : 
     216              : } // namespace detail
     217              : /// \endcond
     218              : 
     219              : /// Calculates the lower triangular part of the covariance matrix of ims.
     220              : /** Uses cblas_ssyrk.  cv is resized to ims.cols() X ims.cols().
     221              :  * Calculates \f$ cv = A^T*A \f$.
     222              :  *
     223              :  *
     224              :  * \tparam eigenT1 is the eigen matrix/array type of cv.
     225              :  * \tparam eigenT2 is the eigen matrix/array type of ims
     226              :  *
     227              :  * \ingroup eigen_lapack
     228              :  */
     229              : template <typename eigenT1, typename eigenT2>
     230            1 : void eigenSYRK( eigenT1 &cv,       ///< [out] is the eigen matrix/array where to store the result
     231              :                 const eigenT2 &ims ///< [in] is the eigen matrix/array (images as columns) to
     232              :                                    ///< calculate the covariance of
     233              : )
     234              : {
     235            1 :     cv.resize( ims.cols(), ims.cols() );
     236              : 
     237            3 :     math::syrk<typename eigenT1::Scalar>( /*const enum CBLAS_ORDER Order*/ CblasColMajor,
     238              :                                           /*const enum CBLAS_UPLO Uplo*/ CblasLower,
     239              :                                           /*const enum CBLAS_TRANSPOSE Trans*/ CblasTrans,
     240            1 :                                           /*const MXLAPACK_INT N*/ ims.cols(),
     241            1 :                                           /*const MXLAPACK_INT K*/ ims.rows(),
     242            1 :                                           /*const float alpha*/ 1.0,
     243              :                                           /*const float *A*/ ims.data(),
     244            1 :                                           /*const MXLAPACK_INT lda*/ ims.rows(),
     245            1 :                                           /*const float beta*/ 0.,
     246              :                                           /*float *C*/ cv.data(),
     247            1 :                                           /*const MXLAPACK_INT ldc*/ cv.rows() );
     248            1 : }
     249              : 
     250              : /// A struct to hold the working memory for eigenSYEVR and maintain it between calls if desired.
     251              : /** \todo this should have the working memory for the first exploratory call to ?syevr as well.
     252              :  */
     253              : template <typename floatT>
     254              : struct syevrMem
     255              : {
     256              :     /// Triangle selection associated with the cached workspace query.
     257              :     char UPLO{ 'L' };
     258              : 
     259              :     /// Matrix order associated with the cached workspace query.
     260              :     MXLAPACK_INT n{ 0 };
     261              : 
     262              :     /// Eigenvalue-range selection associated with the cached workspace query.
     263              :     char RANGE{ 'A' };
     264              : 
     265              :     /// Number of eigenvalues reported by the most recent workspace query.
     266              :     MXLAPACK_INT numeig{ 0 };
     267              : 
     268              :     /// One-based lower eigenvalue index associated with the cached workspace query.
     269              :     MXLAPACK_INT IL{ 0 };
     270              : 
     271              :     /// One-based upper eigenvalue index associated with the cached workspace query.
     272              :     MXLAPACK_INT IU{ 0 };
     273              : 
     274              :     /// Capacity of the eigenvector-support workspace.
     275              :     MXLAPACK_INT sizeISuppZ{ 0 };
     276              : 
     277              :     /// Capacity of the minimum floating-point query workspace.
     278              :     MXLAPACK_INT sizeMinWork{ 0 };
     279              : 
     280              :     /// Capacity of the optimized floating-point workspace.
     281              :     MXLAPACK_INT sizeWork{ 0 };
     282              : 
     283              :     /// Capacity of the minimum integer query workspace.
     284              :     MXLAPACK_INT sizeMinIWork{ 0 };
     285              : 
     286              :     /// Capacity of the optimized integer workspace.
     287              :     MXLAPACK_INT sizeIWork{ 0 };
     288              : 
     289              :     /// LAPACK eigenvector-support workspace owned by this object.
     290              :     MXLAPACK_INT *iSuppZ{ nullptr };
     291              : 
     292              :     /// Minimum floating-point workspace used for LAPACK size queries.
     293              :     floatT *minWork{ nullptr };
     294              : 
     295              :     /// Optimized floating-point LAPACK workspace owned by this object.
     296              :     floatT *work{ nullptr };
     297              : 
     298              :     /// Minimum integer workspace used for LAPACK size queries.
     299              :     MXLAPACK_INT *minIWork{ nullptr };
     300              : 
     301              :     /// Optimized integer LAPACK workspace owned by this object.
     302              :     MXLAPACK_INT *iWork{ nullptr };
     303              : 
     304              :     /// Calculation-type covariance storage used by higher-level eigensolver helpers.
     305              :     Eigen::Array<floatT, Eigen::Dynamic, Eigen::Dynamic> cvd;
     306              : 
     307              :     /// Calculation-type eigenvector storage used by higher-level eigensolver helpers.
     308              :     Eigen::Array<floatT, Eigen::Dynamic, Eigen::Dynamic> evecsd;
     309              : 
     310              :     /// Calculation-type eigenvalue storage used by higher-level eigensolver helpers.
     311              :     Eigen::Array<floatT, Eigen::Dynamic, Eigen::Dynamic> evalsd;
     312              : 
     313              :     /// Construct an empty reusable SYEVR workspace.
     314           28 :     syevrMem()
     315           28 :     {
     316           28 :     }
     317              : 
     318              :     /// Copying an owning SYEVR workspace is prohibited.
     319              :     syevrMem( const syevrMem &other /**< [in] workspace that cannot be copied */ ) = delete;
     320              : 
     321              :     /// Copy assignment of an owning SYEVR workspace is prohibited.
     322              :     syevrMem &operator=( const syevrMem &other /**< [in] workspace that cannot be copied */ ) = delete;
     323              : 
     324              :     /// Release every allocation owned by this workspace.
     325           28 :     ~syevrMem()
     326              :     {
     327           28 :         free();
     328           28 :     }
     329              : 
     330              :     /// Release all workspace allocations and reset the cached LAPACK configuration.
     331           31 :     void free()
     332              :     {
     333           31 :         detail::releaseSyevrBuffer( iSuppZ, sizeISuppZ );
     334           31 :         detail::releaseSyevrBuffer( minWork, sizeMinWork );
     335           31 :         detail::releaseSyevrBuffer( work, sizeWork );
     336           31 :         detail::releaseSyevrBuffer( minIWork, sizeMinIWork );
     337           31 :         detail::releaseSyevrBuffer( iWork, sizeIWork );
     338              : 
     339           31 :         UPLO = 'L';
     340           31 :         n = 0;
     341           31 :         RANGE = 'A';
     342           31 :         numeig = 0;
     343           31 :         IL = 0;
     344           31 :         IU = 0;
     345           31 :     }
     346              : };
     347              : 
     348              : /// Calculate select eigenvalues and eigenvectors of an Eigen Array
     349              : /** Uses the templateLapack wrapper for syevr.
     350              :  *
     351              :  * \tparam arrT is the eigen-like type containing the data
     352              :  *
     353              :  * \returns -1 for invalid matrix/range geometry or invalid workspace-query results.
     354              :  * \returns -1000 on an malloc allocation error.
     355              :  * \returns the return code from syevr (info) otherwise.
     356              :  *
     357              :  * \ingroup eigen_lapack
     358              :  */
     359              : template <typename arrT>
     360           39 : MXLAPACK_INT eigenSYEVR( arrT &eigvec,    /**< [out] will contain the eigenvectors as columns*/
     361              :                          arrT &eigval,    /**< [out] will contain the eigenvalues*/
     362              :                          arrT &X,         /**< [in]  is a square matrix which is either upper
     363              :                                                      or lower (default) triangular*/
     364              :                          int ev0 = 0,     /**< [in] [opt] is the first desired eigenvalue
     365              :                                                           (default = 0)*/
     366              :                          int ev1 = -1,    /**< [in] [opt] exclusive upper bound of the desired eigenvalue indices.
     367              :                                                           If -1 all eigenvalues are returned.*/
     368              :                          char UPLO = 'L', /**< [in] [opt] specifies whether X is upper ('U')
     369              :                                                           or lower ('L') triangular.
     370              :                                                           Default is ('L').*/
     371              :                          syevrMem<typename arrT::Scalar> *mem = 0 /**< [in] [opt] holds the working memory arrays,
     372              :                                                                                   can be re-passed to avoid unnecessary
     373              :                                                                                   re-allocations*/
     374              : )
     375              : {
     376              :     typedef typename arrT::Scalar calcT;
     377              : 
     378           39 :     MXLAPACK_INT numeig{ 0 };
     379              :     MXLAPACK_INT info;
     380           39 :     char RANGE = 'A';
     381              : 
     382           39 :     MXLAPACK_INT n = X.rows();
     383           39 :     if( n <= 0 || X.cols() != n )
     384              :     {
     385            2 :         return -1;
     386              :     }
     387              : 
     388           37 :     if( ev1 < -1 || ( ev1 != -1 && ( ev0 < 0 || ev0 >= ev1 || ev1 > n ) ) )
     389              :     {
     390            4 :         return -1;
     391              :     }
     392              : 
     393           33 :     MXLAPACK_INT localMem = 0;
     394              : 
     395           33 :     if( mem == 0 )
     396              :     {
     397            9 :         mem = new syevrMem<calcT>;
     398            9 :         localMem = 1;
     399              :     }
     400              : 
     401           33 :     MXLAPACK_INT IL = 1;
     402           33 :     MXLAPACK_INT IU = n;
     403           33 :     if( ev1 != -1 )
     404              :     {
     405           13 :         RANGE = 'I';
     406           13 :         IL = ev0 + 1; // This is FORTRAN, after all
     407           13 :         IU = ev1;
     408              :     }
     409              : 
     410           33 :     eigvec.resize( n, IU - IL + 1 );
     411           33 :     eigval.resize( n, 1 );
     412              : 
     413           33 :     if( UPLO != mem->UPLO || n != mem->n || RANGE != mem->RANGE || mem->IL != IL || mem->IU != IU )
     414              :     {
     415           29 :         if( !detail::resizeSyevrBuffer<calcT>( mem->iSuppZ, mem->sizeISuppZ, 2 * n ) ||
     416           55 :             !detail::resizeSyevrBuffer<calcT>( mem->minWork, mem->sizeMinWork, 26 * n ) ||
     417           26 :             !detail::resizeSyevrBuffer<calcT>( mem->minIWork, mem->sizeMinIWork, 10 * n ) )
     418              :         {
     419            8 :             internal::mxlib_error_report( error_t::allocerr, "malloc failed in eigenSYEVR." );
     420            4 :             if( localMem )
     421              :             {
     422            1 :                 delete mem;
     423              :             }
     424            4 :             return -1000;
     425              :         }
     426              : 
     427              :         //  Query for optimum sizes for workspace
     428           25 :         info = detail::callSyevr<calcT>( 'V',
     429              :                                          RANGE,
     430              :                                          UPLO,
     431              :                                          n,
     432              :                                          X.data(),
     433              :                                          n,
     434              :                                          0,
     435              :                                          0,
     436              :                                          IL,
     437              :                                          IU,
     438              :                                          math::lamch<calcT>( 'S' ),
     439              :                                          &numeig,
     440              :                                          eigval.data(),
     441              :                                          eigvec.data(),
     442              :                                          n,
     443              :                                          mem->iSuppZ,
     444              :                                          mem->minWork,
     445              :                                          -1,
     446              :                                          mem->minIWork,
     447              :                                          -1 );
     448              : 
     449           25 :         if( info != 0 )
     450              :         {
     451            4 :             internal::mxlib_error_report( error_t::lapackerr, "error from SYEVR" );
     452            2 :             if( localMem )
     453              :             {
     454            1 :                 delete mem;
     455              :             }
     456              : 
     457            2 :             return info;
     458              :         }
     459              : 
     460           23 :         const calcT queriedWork = mem->minWork[0];
     461           23 :         const MXLAPACK_INT requestedIWork = mem->minIWork[0];
     462           44 :         if( !math::isFinite( queriedWork ) || queriedWork < 1 ||
     463           19 :             static_cast<long double>( queriedWork ) >
     464           44 :                 static_cast<long double>( std::numeric_limits<MXLAPACK_INT>::max() ) ||
     465              :             requestedIWork < 1 )
     466              :         {
     467           14 :             internal::mxlib_error_report( error_t::lapackerr, "invalid workspace sizes returned by SYEVR query" );
     468            7 :             if( localMem )
     469              :             {
     470            5 :                 delete mem;
     471              :             }
     472            7 :             return -1;
     473              :         }
     474              : 
     475           16 :         const MXLAPACK_INT requestedWork = static_cast<MXLAPACK_INT>( queriedWork );
     476           31 :         if( !detail::resizeSyevrBuffer<calcT>( mem->work, mem->sizeWork, requestedWork ) ||
     477           15 :             !detail::resizeSyevrBuffer<calcT>( mem->iWork, mem->sizeIWork, requestedIWork ) )
     478              :         {
     479            6 :             internal::mxlib_error_report( error_t::allocerr, "malloc failed in eigenSYEVR." );
     480            3 :             if( localMem )
     481              :             {
     482            1 :                 delete mem;
     483              :             }
     484            3 :             return -1000;
     485              :         }
     486              : 
     487           13 :         mem->UPLO = UPLO;
     488           13 :         mem->n = n;
     489           13 :         mem->RANGE = RANGE;
     490           13 :         mem->numeig = numeig;
     491           13 :         mem->IL = IL;
     492           13 :         mem->IU = IU;
     493              :     }
     494              : 
     495              :     // Now actually do the calculation
     496           17 :     info = detail::callSyevr<calcT>( 'V',
     497              :                                      RANGE,
     498              :                                      UPLO,
     499              :                                      n,
     500              :                                      X.data(),
     501              :                                      n,
     502              :                                      0,
     503              :                                      0,
     504              :                                      IL,
     505              :                                      IU,
     506              :                                      math::lamch<calcT>( 'S' ),
     507              :                                      &numeig,
     508              :                                      eigval.data(),
     509              :                                      eigvec.data(),
     510              :                                      n,
     511              :                                      mem->iSuppZ,
     512              :                                      mem->work,
     513              :                                      mem->sizeWork,
     514              :                                      mem->iWork,
     515              :                                      mem->sizeIWork );
     516              : 
     517              :     /*  Cleanup and exit  */
     518              : 
     519           17 :     if( localMem )
     520              :     {
     521            1 :         delete mem;
     522              :     }
     523              : 
     524           17 :     return info;
     525              : }
     526              : 
     527              : /// Calculate the eigenvectors and eigenvalues given a triangular matrix
     528              : /** Eigen-decomposition of the matrix is performed using \ref eigenSYEVR().
     529              :  *
     530              :  * \tparam evCalcT is the type in which to perform eigen-decomposition, which may be different
     531              :  *         from the input array and output arrays (which must be the same).
     532              :  * \tparam eigenT is a 2D Eigen-like type
     533              :  *
     534              :  * \ingroup eigen_lapack
     535              :  */
     536              : template <typename _evCalcT = double, typename eigenT>
     537           14 : MXLAPACK_INT calcEigenVecs( eigenT &evecs,               /**< [out] on exit contains the eigen vectors*/
     538              :                             eigenT &evals,               /**< [out] on exit contains the eigen vectors*/
     539              :                             eigenT &cv,                  /**< [in] a lower-triangle (in the Lapack sense) square
     540              :                                                                    covariance matrix.*/
     541              :                             int nVecs = 0,               /**< [in] [opt] The maximum number of modes to solve
     542              :                                                                               for. If 0 all modes are solved for.*/
     543              :                             bool normalize = false,      /**< [in] [opt] flag specifying whether or not to
     544              :                                                                               normalize the eigenvectors.*/
     545              :                             bool check = false,          /**< [in] [opt] flag specifying whether or not to
     546              :                                                                               check the eigenvalues/vectors for
     547              :                                                                               validity. Requires normalize=true.*/
     548              :                             syevrMem<_evCalcT> *mem = 0, /**< [in] [opt] A memory structure which can be
     549              :                                                                               re-used by SYEVR for efficiency.*/
     550              :                             double *t_eigenv = nullptr   /**< [out] [opt] if not null, will be filled in
     551              :                                                                                with the time taken to calculate
     552              :                                                                                eigenvalues.*/ )
     553              : {
     554              :     typedef _evCalcT evCalcT;
     555              :     typedef typename eigenT::Scalar realT;
     556              : 
     557           14 :     bool localMem = false;
     558              : 
     559           14 :     if( mem == 0 )
     560              :     {
     561            4 :         mem = new syevrMem<evCalcT>;
     562            4 :         localMem = true;
     563              :     }
     564              : 
     565           14 :     if( cv.rows() <= 0 || cv.rows() != cv.cols() )
     566              :     {
     567            3 :         std::cerr << "calcEigenVecs: covariance matrix must be non-empty and square\n";
     568            3 :         if( localMem )
     569              :         {
     570            1 :             delete mem;
     571              :         }
     572            3 :         return -1;
     573              :     }
     574              : 
     575           11 :     MXLAPACK_INT tNims = cv.rows();
     576              : 
     577           11 :     if( nVecs <= 0 || nVecs > tNims )
     578              :     {
     579            6 :         nVecs = tNims;
     580              :     }
     581              : 
     582           11 :     if( t_eigenv )
     583              :     {
     584            2 :         *t_eigenv = sys::get_curr_time();
     585              :     }
     586              : 
     587           11 :     mem->cvd = cv.template cast<evCalcT>();
     588              : 
     589              :     // Calculate eigenvectors and eigenvalues
     590              :     /* SYEVR sorts eigenvalues in ascending order, so we specifiy the top n_modes
     591              :      */
     592           11 :     MXLAPACK_INT info = eigenSYEVR( mem->evecsd, mem->evalsd, mem->cvd, tNims - nVecs, tNims, 'L', mem );
     593              : 
     594           11 :     if( info != 0 )
     595              :     {
     596            2 :         std::cerr << "calcEigenVecs: eigenSYEVR returned an error (info = " << info << ")\n";
     597            2 :         if( localMem )
     598              :         {
     599            1 :             delete mem;
     600              :         }
     601              : 
     602            2 :         return -1;
     603              :     }
     604              : 
     605            9 :     evecs = mem->evecsd.template cast<realT>();
     606            9 :     evals = mem->evalsd.template cast<realT>();
     607              : 
     608            9 :     if( normalize )
     609              :     {
     610              :         // Normalize the eigenvectors
     611            5 :         if( !check )
     612              :         {
     613            3 :             for( MXLAPACK_INT i = 0; i < nVecs; ++i )
     614              :             {
     615            2 :                 evecs.col( i ) = evecs.col( i ) / sqrt( evals( i ) );
     616              :             }
     617              :         }
     618              :         else // here we check for invalid results and 0 things out
     619              :         {
     620           13 :             for( MXLAPACK_INT i = 0; i < nVecs; ++i )
     621              :             {
     622            9 :                 if( !math::isFinite( evals( i ) ) )
     623              :                 {
     624            1 :                     std::cerr << "got non-finite eigenvalue (# " << i << ")\n";
     625            1 :                     evecs.col( i ).setZero();
     626              :                 }
     627            8 :                 else if( evals( i ) == 0 )
     628              :                 {
     629            1 :                     std::cerr << "got 0 eigenvalue (# " << i << ")\n";
     630            1 :                     evecs.col( i ).setZero();
     631              :                 }
     632            7 :                 else if( evals( i ) < 0 )
     633              :                 {
     634            1 :                     std::cerr << "got < 0 eigenvalue (# " << i << ")\n";
     635            1 :                     evecs.col( i ).setZero();
     636              :                 }
     637              :                 else
     638              :                 {
     639            6 :                     evecs.col( i ) = evecs.col( i ) / sqrt( evals( i ) );
     640              :                 }
     641              : 
     642           37 :                 for( int r = 0; r < evecs.rows(); ++r )
     643              :                 {
     644           29 :                     if( !math::isFinite( evecs.col( i )( r ) ) )
     645              :                     {
     646            1 :                         std::cerr << "got non-finite eigenvector entry (# " << i << "," << r << ")\n";
     647            1 :                         evecs.col( i ).setZero();
     648            1 :                         break;
     649              :                     }
     650              :                 }
     651              :             }
     652              :         }
     653              :     }
     654              : 
     655            9 :     if( t_eigenv )
     656              :     {
     657            2 :         *t_eigenv = sys::get_curr_time() - *t_eigenv;
     658              :     }
     659              : 
     660            9 :     if( localMem )
     661              :     {
     662            2 :         delete mem;
     663              :     }
     664              : 
     665            9 :     return 0;
     666              : 
     667              : } // calcKLModes
     668              : 
     669              : /// Calculate the K-L modes, or principle components, given a covariance matrix.
     670              : /** Eigen-decomposition of the covariance matrix is performed using \ref eigenSYEVR().
     671              :  *
     672              :  * \tparam evCalcT is the type in which to perform eigen-decomposition.
     673              :  * \tparam eigenT is a 2D Eigen-like type
     674              :  * \tparam eigenT1 is a 2D Eigen-like type.
     675              :  *
     676              :  * \ingroup eigen_lapack
     677              :  */
     678              : template <typename _evCalcT = double, typename eigenT, typename eigenT1>
     679            5 : MXLAPACK_INT calcKLModes( eigenT &klModes,             /**< [out] on exit contains the K-L modes (or P.C.s) */
     680              :                           eigenT &cv,                  /**< [in] a lower-triangle (in the Lapack sense) square
     681              :                                                                  covariance matrix.*/
     682              :                           const eigenT1 &Rims,         /**< [in] The reference data.  cv.rows() == Rims.cols().*/
     683              :                           int n_modes = 0,             /**< [in] [opt] Tbe maximum number of modes to solve for.
     684              :                                                                        If 0 all modes are solved for.*/
     685              :                           syevrMem<_evCalcT> *mem = 0, /**< [in] [opt] A memory structure which can be re-used
     686              :                                                                        by SYEVR for efficiency.*/
     687              :                           double *t_eigenv = nullptr,  /**< [out] [opt] if not null, will be filled in with the time
     688              :                                                                         taken to calculate eigenvalues.*/
     689              :                           double *t_klim = nullptr     /**< [out] [opt] if not null, will be filled in with the time
     690              :                                                                         taken to calculate the KL modes.*/)
     691              : {
     692              :     typedef _evCalcT evCalcT;
     693              :     typedef typename eigenT::Scalar realT;
     694              : 
     695            5 :     bool localMem = false;
     696              : 
     697            5 :     if( mem == 0 )
     698              :     {
     699            3 :         mem = new syevrMem<evCalcT>;
     700            3 :         localMem = true;
     701              :     }
     702              : 
     703            5 :     if( cv.rows() != Rims.cols() )
     704              :     {
     705            1 :         std::cerr << "Covariance matrix - reference image size mismatch in calcKLModes\n";
     706            1 :         if( localMem )
     707              :         {
     708            1 :             delete mem;
     709              :         }
     710            1 :         return -1;
     711              :     }
     712              : 
     713            4 :     eigenT evecs, evals;
     714              : 
     715            4 :     MXLAPACK_INT tNims = cv.rows();
     716            4 :     MXLAPACK_INT tNpix = Rims.rows();
     717              : 
     718            4 :     if( n_modes <= 0 || n_modes > tNims )
     719              :     {
     720            2 :         n_modes = tNims;
     721              :     }
     722              : 
     723              :     // Eigen::Array<evCalcT, Eigen::Dynamic, Eigen::Dynamic> evecsd, evalsd;
     724            4 :     mem->cvd = cv.template cast<evCalcT>();
     725            4 :     MXLAPACK_INT info = calcEigenVecs( mem->evecsd, mem->evalsd, mem->cvd, n_modes, true, true, mem, t_eigenv );
     726              : 
     727            4 :     if( info != 0 )
     728              :     {
     729            1 :         std::cerr << "calckKLModes: eigenSYEVR returned an error (info = " << info << ")\n";
     730            1 :         if( localMem )
     731              :         {
     732            1 :             delete mem;
     733              :         }
     734            1 :         return -1;
     735              :     }
     736              : 
     737            3 :     evecs = mem->evecsd.template cast<realT>();
     738            3 :     evals = mem->evalsd.template cast<realT>();
     739              : 
     740            3 :     klModes.resize( n_modes, tNpix );
     741              : 
     742            3 :     if( t_klim )
     743              :     {
     744            1 :         *t_klim = sys::get_curr_time();
     745              :     }
     746              : 
     747              :     // Now calculate KL images
     748              :     /*
     749              :      *  KL = E^T * R  ==> C = A^T * B
     750              :      */
     751            3 :     constexpr realT alpha{ 1 };
     752            3 :     constexpr realT beta{ 0 };
     753            6 :     gemm<realT>( CblasColMajor,
     754              :                  CblasTrans,
     755              :                  CblasTrans,
     756              :                  n_modes,
     757              :                  tNpix,
     758              :                  tNims,
     759              :                  alpha,
     760            3 :                  evecs.data(),
     761            3 :                  cv.rows(),
     762              :                  Rims.data(),
     763            3 :                  Rims.rows(),
     764              :                  beta,
     765              :                  klModes.data(),
     766            3 :                  klModes.rows() );
     767              : 
     768            3 :     if( t_klim )
     769              :     {
     770            1 :         *t_klim = sys::get_curr_time() - *t_klim;
     771              :     }
     772              : 
     773            3 :     if( localMem )
     774              :     {
     775            1 :         delete mem;
     776              :     }
     777              : 
     778            3 :     return 0;
     779              : 
     780            4 : } // calcKLModes
     781              : 
     782              : /// Compute the SVD of an Eigen::Array using LAPACK's xgesdd
     783              : /** Computes the SVD of A, \f$ A = U S V^T \f$.
     784              :  *
     785              :  * \returns 0 on success
     786              :  * \returns -i on error in ith parameter (from LAPACK xgesdd)
     787              :  * \returns >0 did not converge (from LAPACK xgesdd)
     788              :  *
     789              :  * \tparam dataT is either float or double.
     790              :  *
     791              :  * \ingroup eigen_lapack
     792              :  */
     793              : template <typename dataT>
     794            0 : MXLAPACK_INT eigenGESDD( Eigen::Array<dataT, -1, -1> &U,  ///< [out] the A.rows() x A.rows() left matrix
     795              :                          Eigen::Array<dataT, -1, -1> &S,  ///< [out] the A.cols() x 1 matrix of singular values
     796              :                          Eigen::Array<dataT, -1, -1> &VT, /**< [out] the A.cols() x A.cols() right matrix, note this
     797              :                                                                      is the transpose. */
     798              :                          Eigen::Array<dataT, -1, -1> &A   ///< [in]  the input matrix to be decomposed
     799              : )
     800              : {
     801            0 :     char JOBZ = 'A';
     802            0 :     MXLAPACK_INT M = A.rows();
     803            0 :     MXLAPACK_INT N = A.cols();
     804            0 :     MXLAPACK_INT LDA = M;
     805            0 :     S.resize( N, 1 );
     806            0 :     U.resize( M, M );
     807            0 :     MXLAPACK_INT LDU = M;
     808            0 :     VT.resize( N, N );
     809            0 :     MXLAPACK_INT LDVT = N;
     810              : 
     811              :     dataT wkOpt;
     812            0 :     MXLAPACK_INT LWORK = -1;
     813              : 
     814            0 :     MXLAPACK_INT *IWORK = new MXLAPACK_INT[8 * M];
     815              :     MXLAPACK_INT INFO;
     816              : 
     817              :     INFO =
     818            0 :         math::gesdd<dataT>( JOBZ, M, N, A.data(), LDA, S.data(), U.data(), LDU, VT.data(), LDVT, &wkOpt, LWORK, IWORK );
     819              : 
     820            0 :     LWORK = wkOpt;
     821              :     // delete WORK;
     822            0 :     dataT *WORK = new dataT[LWORK];
     823              : 
     824              :     INFO =
     825            0 :         math::gesdd<dataT>( JOBZ, M, N, A.data(), LDA, S.data(), U.data(), LDU, VT.data(), LDVT, WORK, LWORK, IWORK );
     826              : 
     827            0 :     delete[] WORK;
     828            0 :     delete[] IWORK;
     829              : 
     830            0 :     return INFO;
     831              : }
     832              : 
     833              : #define MX_PINV_NO_INTERACT 0
     834              : #define MX_PINV_PLOT 1
     835              : #define MX_PINV_ASK 2
     836              : #define MX_PINV_ASK_NMODES 4
     837              : 
     838              : /// Calculate the pseudo-inverse of a patrix given its SVD
     839              : /** Given the SVD of A, \f$ A = U S V^T \f$, as calculated by eigenGESDD the psuedo-inverse is
     840              :  * calculated as \f$ A^+ = V S^+ U^T\f$.
     841              :  *
     842              :  * The parameter \p interact is intepreted as a bitmask.  The values can be
     843              :  * - `MX_PINV_PLOT`, which will cause a plot to be displayed of the singular values
     844              :  * - `MX_PINV_ASK`, which will ask the user for a max. condition number using stdin
     845              :  * - `MX_PINV_ASK_NMODES`, which will ask the user for a max number of modes to include using
     846              :  * stdin.  Overrides MX_PINV_ASK. If \p interact is 0 then no interaction is used and \p
     847              :  * maxCondition controls the inversion.
     848              :  *
     849              :  * \tparam dataT is either float or double.
     850              :  *
     851              :  * \ingroup eigen_lapack
     852              :  */
     853              : template <typename dataT>
     854            0 : int eigenPseudoInverse( Eigen::Array<dataT, -1, -1> &PInv, ///< [out] The pseudo-inverse of A
     855              :                         dataT &condition,                  ///< [out] The final condition number.
     856              :                         int &nRejected,                    ///< [out] The number of eigenvectors rejected
     857              :                         Eigen::Array<dataT, -1, -1> &U,    ///< [in] the A.rows() x A.rows() left matrix
     858              :                         Eigen::Array<dataT, -1, -1> &S,    ///< [in] the A.cols() x 1 matrix of singular values
     859              :                         Eigen::Array<dataT, -1, -1> &VT,   /**< [in] the A.cols() x A.cols() right matrix, note this
     860              :                                                                       is the transpose. */
     861              :                         int minMN,                         ///< [in]  The minimum size of the matrix to invert.
     862              :                         dataT &maxCondition,               /**< [in]  If \> 0, the maximum condition number desired.
     863              :                                                                       If \<0 the number of modes to keep. Used to
     864              :                                                                       threshold the singular values.  Set to 0 to
     865              :                                                                       include all eigenvalues/vectors. Ignored if
     866              :                                                                       interactive. */
     867              :                         dataT alpha = 0,                   /**< [in] [opt] the Tikhonov regularization value, as
     868              :                                                                      a fraction of the highest singular value.
     869              :                                                                      If alpha < 0, then it is treated as a (positive)
     870              :                                                                      floor (as a fraction of highest singular value)
     871              :                                                                      for the singular values, which is not the same as
     872              :                                                                     Tikhonov (alpha > 0).*/
     873              :                         int interact = MX_PINV_NO_INTERACT /**< [in] [opt] a bitmask controlling interaction.
     874              :                                                                      See above.*/
     875              : )
     876              : {
     877              : 
     878            0 :     dataT Smax = S.maxCoeff();
     879              : 
     880            0 :     if( alpha > 0 )
     881              :     {
     882            0 :         for( MXLAPACK_INT i = 0; i < S.rows(); ++i )
     883              :         {
     884            0 :             S( i ) = ( pow( S( i ), 2 ) + pow( alpha * Smax, 2 ) ) / S( i );
     885              :         }
     886              :     }
     887              : 
     888            0 :     if( alpha < 0 )
     889              :     {
     890            0 :         for( MXLAPACK_INT i = 0; i < S.rows(); ++i )
     891              :         {
     892            0 :             S( i ) = S( i ) + -alpha * Smax;
     893              :         }
     894              :     }
     895              : 
     896            0 :     int modesToReject = 0;
     897            0 :     if( maxCondition < 0 ) // Rejecting mode numbers
     898              :     {
     899            0 :         modesToReject = -maxCondition;
     900              : 
     901            0 :         if( modesToReject - 1 < S.rows() )
     902              :         {
     903            0 :             maxCondition = Smax / S( modesToReject - 1, 0 );
     904              :         }
     905              :     }
     906              : 
     907            0 :     if( interact & MX_PINV_PLOT )
     908              :     {
     909            0 :         gnuPlot gp;
     910            0 :         gp.command( "set title \"SVD Singular Values\"" );
     911            0 :         gp.logy();
     912            0 :         gp.plot( S.data(), S.rows(), " w lp", "singular values" );
     913            0 :     }
     914              : 
     915            0 :     if( interact & MX_PINV_ASK && !( interact & MX_PINV_ASK_NMODES ) )
     916              :     {
     917              :         dataT mine;
     918            0 :         std::cout << "Maximum singular value: " << Smax << "\n";
     919            0 :         std::cout << "Minimum singular value: " << S.minCoeff() << "\n";
     920            0 :         std::cout << "Enter singular value threshold: ";
     921            0 :         std::cin >> mine;
     922              : 
     923            0 :         if( mine > 0 )
     924              :         {
     925            0 :             maxCondition = Smax / mine;
     926              :         }
     927              :         else
     928              :         {
     929            0 :             maxCondition = Smax / S( S.rows() - 1, 0 );
     930              :         }
     931            0 :     }
     932            0 :     else if( interact & MX_PINV_ASK_NMODES )
     933              :     {
     934              :         unsigned mine;
     935            0 :         std::cout << "Maximum singular value: " << Smax << "\n";
     936            0 :         std::cout << "Minimum singular value: " << S.minCoeff() << "\n";
     937            0 :         std::cout << "Enter number of modes to keep: ";
     938            0 :         std::cin >> mine;
     939            0 :         modesToReject = S.rows() - mine;
     940              : 
     941            0 :         if( modesToReject <= 0 || modesToReject > S.rows() )
     942              :         {
     943            0 :             modesToReject = 0;
     944              :         }
     945              : 
     946            0 :         maxCondition = -modesToReject;
     947              :     }
     948              : 
     949            0 :     Eigen::Array<dataT, -1, -1> sigma;
     950            0 :     sigma.resize( S.rows(), S.rows() );
     951            0 :     sigma.setZero();
     952              : 
     953            0 :     nRejected = 0;
     954              : 
     955            0 :     if( maxCondition > 0 )
     956              :     {
     957            0 :         dataT threshold = 0;
     958              : 
     959            0 :         if( maxCondition > 0 )
     960              :         {
     961            0 :             threshold = Smax / maxCondition;
     962              : 
     963            0 :             condition = 1;
     964              : 
     965            0 :             for( MXLAPACK_INT i = 0; i < S.rows(); ++i )
     966              :             {
     967            0 :                 if( S( i ) >= threshold )
     968              :                 {
     969            0 :                     sigma( i, i ) = 1. / S( i );
     970            0 :                     if( Smax / S( i ) > condition )
     971              :                     {
     972            0 :                         condition = Smax / S( i );
     973              :                     }
     974              :                 }
     975              :                 else
     976              :                 {
     977            0 :                     sigma( i, i ) = 0;
     978            0 :                     ++nRejected;
     979              :                 }
     980              :             }
     981              :         }
     982              :     }
     983              :     else // rejecting modes
     984              :     {
     985            0 :         std::cerr << "rejecting based on modes\n";
     986            0 :         std::cerr << " modes to reject: " << modesToReject << "\n";
     987            0 :         for( MXLAPACK_INT i = 0; i < S.rows(); ++i )
     988              :         {
     989            0 :             if( i < S.rows() - modesToReject )
     990              :             {
     991            0 :                 sigma( i, i ) = 1. / S( i );
     992            0 :                 if( Smax / S( i ) > condition )
     993            0 :                     condition = Smax / S( i );
     994              :             }
     995              :             else
     996              :             {
     997            0 :                 sigma( i, i ) = 0;
     998            0 :                 ++nRejected;
     999              :             }
    1000              :         }
    1001              :     }
    1002              : 
    1003            0 :     if( interact & MX_PINV_PLOT )
    1004              :     {
    1005            0 :         std::vector<dataT> vsig( sigma.rows() );
    1006            0 :         for( int rr = 0; rr < sigma.rows(); ++rr )
    1007              :         {
    1008            0 :             vsig[rr] = sigma( rr, rr );
    1009              :         }
    1010              : 
    1011            0 :         gnuPlot gp;
    1012            0 :         gp.command( "set title \"Inverted Singular Values\"" );
    1013            0 :         gp.logy();
    1014            0 :         gp.plot( vsig.data(), vsig.size(), " w lp", "inverted singular values" );
    1015            0 :     }
    1016              : 
    1017            0 :     if( interact & MX_PINV_ASK || interact & MX_PINV_ASK_NMODES )
    1018              :     {
    1019              :         dataT mine;
    1020            0 :         std::cout << "Modes Rejected: " << nRejected << "\n";
    1021            0 :         std::cout << "Condition Number: " << condition << "\n";
    1022              :     }
    1023              : 
    1024            0 :     PInv = ( VT.matrix().transpose() * sigma.matrix().transpose() ) *
    1025            0 :            U.block( 0, 0, U.rows(), minMN ).matrix().transpose();
    1026              : 
    1027            0 :     return 0;
    1028            0 : }
    1029              : 
    1030              : /// Calculate the pseudo-inverse of a patrix using the SVD
    1031              : /** First computes the SVD of A, \f$ A = U S V^T \f$, using eigenGESDD.  Then the psuedo-inverse is
    1032              :  * calculated as \f$ A^+ = V S^+ U^T\f$.
    1033              :  *
    1034              :  * The parameter \p interact is intepreted as a bitmask.  The values can be
    1035              :  * - `MX_PINV_PLOT`, which will cause a plot to be displayed of the singular values
    1036              :  * - `MX_PINV_ASK`, which will ask the user for a max. condition number using stdin
    1037              :  * - `MX_PINV_ASK_NMODES`, which will ask the user for a max number of modes to include using
    1038              :  * stdin.  Overrides MX_PINV_ASK. If \p interact is 0 then no interaction is used and \p
    1039              :  * maxCondition controls the inversion.
    1040              :  *
    1041              :  * \tparam dataT is either float or double.
    1042              :  *
    1043              :  * \ingroup eigen_lapack
    1044              :  */
    1045              : template <typename dataT>
    1046            0 : int eigenPseudoInverse( Eigen::Array<dataT, -1, -1> &PInv, ///< [out] The pseudo-inverse of A
    1047              :                         dataT &condition,                  ///< [out] The final condition number.
    1048              :                         int &nRejected,                    ///< [out] The number of eigenvectors rejected
    1049              :                         Eigen::Array<dataT, -1, -1> &U,    ///< [out] the A.rows() x A.rows() left matrix
    1050              :                         Eigen::Array<dataT, -1, -1> &S,    ///< [out] the A.cols() x 1 matrix of singular values
    1051              :                         Eigen::Array<dataT, -1, -1> &VT,   /**< [out] the A.cols() x A.cols() right matrix, note this
    1052              :                                                                       is the transpose. */
    1053              :                         Eigen::Array<dataT, -1, -1> &A,    ///< [in]  The matrix to invert.  This will be modified!
    1054              :                         dataT &maxCondition,               /**< [in]  If \> 0, the maximum condition number desired.
    1055              :                                                                       If \<0 the number of modes to keep. Used to
    1056              :                                                                       threshold the singular values.  Set to 0 to
    1057              :                                                                       include all eigenvalues/vectors. Ignored if
    1058              :                                                                       interactive. */
    1059              :                         dataT alpha = 0,                   /**< [in] [opt] the Tikhonov regularization value, as
    1060              :                                                                      a fraction of the highest singular value.
    1061              :                                                                      If alpha < 0, then it is treated as a (positive)
    1062              :                                                                      floor (as a fraction of highest singular value)
    1063              :                                                                      for the singular values, which is not the same as
    1064              :                                                                     Tikhonov (alpha > 0).*/
    1065              :                         int interact = MX_PINV_NO_INTERACT /**< [in] [opt] a bitmask controlling interaction.
    1066              :                                                                      See above.*/
    1067              : )
    1068              : {
    1069              : 
    1070            0 :     int minMN = std::min( A.rows(), A.cols() );
    1071              : 
    1072              :     MXLAPACK_INT info;
    1073            0 :     info = eigenGESDD( U, S, VT, A );
    1074              : 
    1075            0 :     if( info != 0 )
    1076              :     {
    1077            0 :         std::cerr << "eigenPseudoInverse: eigenGESDD failed with info = " << info << "\n";
    1078            0 :         return info;
    1079              :     }
    1080              : 
    1081            0 :     return eigenPseudoInverse( PInv, condition, nRejected, U, S, VT, minMN, maxCondition, alpha, interact );
    1082              : }
    1083              : 
    1084              : /// Calculate the pseudo-inverse of a matrix using the SVD
    1085              : /** First computes the SVD of A, \f$ A = U S V^T \f$, using eigenGESDD.  Then the psuedo-inverse is
    1086              :  * calculated as \f$ A^+ = V S^+ U^T\f$. This interface does not provide access to U, S and VT.
    1087              :  *
    1088              :  * The parameter \p interact is intepreted as a bitmask.  The values can be
    1089              :  * - `MX_PINV_PLOT`, which will cause a plot to be displayed of the singular values
    1090              :  * - `MX_PINV_ASK`, which will ask the user for a max. condition number using stdin
    1091              :  * - `MX_PINV_ASK_NMODES`, which will ask the user for a max number of modes to include using
    1092              :  * stdin.  Overrides MX_PINV_ASK. If \p interact is 0 then no interaction is used and maxCondition
    1093              :  * controls the inversion.
    1094              :  *   *
    1095              :  * \tparam dataT is either float or double.
    1096              :  *
    1097              :  * \overload
    1098              :  *
    1099              :  * \ingroup eigen_lapack
    1100              :  */
    1101              : template <typename dataT>
    1102            0 : int eigenPseudoInverse( Eigen::Array<dataT, -1, -1> &PInv, ///< [out] The pseudo-inverse of A
    1103              :                         dataT &condition,                  ///< [out] The final condition number.
    1104              :                         int &nRejected,                    /**< [out] The number of eigenvectors
    1105              :                                                                       rejected*/
    1106              :                         Eigen::Array<dataT, -1, -1> &A,    /**< [in]  The matrix to invert, will be
    1107              :                                                                        altered!*/
    1108              :                         dataT &maxCondition,               /**< [in]  If \> 0, the maximum condition number desired.
    1109              :                                                                       If \<0 the number of modes to keep. Used to
    1110              :                                                                       threshold the singular values. Set to 0 to
    1111              :                                                                       include all eigenvalues/vectors. Ignored if
    1112              :                                                                       interactive.*/
    1113              :                         dataT alpha = 0,                   /**< [in] [opt] the Tikhonov regularization value,
    1114              :                                                                      as a fraction of the highest singular value. If
    1115              :                                                                      alpha < 0, then it is treated as a (positive)
    1116              :                                                                      floor (as a fraction of highest singular value)
    1117              :                                                                      for the singular values, which is not the same
    1118              :                                                                      as Tikhonov (alpha > 0).*/
    1119              :                         int interact = MX_PINV_NO_INTERACT /**< [in] [opt] a bitmask controlling interaction.
    1120              :                                                                      See above.*/
    1121              : )
    1122              : {
    1123            0 :     Eigen::Array<dataT, -1, -1> S, U, VT;
    1124              : 
    1125            0 :     return eigenPseudoInverse( PInv, condition, nRejected, U, S, VT, A, maxCondition, alpha, interact );
    1126            0 : }
    1127              : 
    1128              : } // namespace math
    1129              : } // namespace mx
    1130              : 
    1131              : #endif // math_eigenLapack_hpp
        

Generated by: LCOV version 2.0-1