LCOV - code coverage report
Current view: top level - src/util - RDC.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 60 70 85.7 %
Date: 2025-03-25 01:19:55 Functions: 12 13 92.3 %
Branches: 66 136 48.5 %

           Branch data     Line data    Source code
       1                 :            : #include "RDC.hpp"
       2                 :            : 
       3                 :            : #include <Eigen/Dense>
       4                 :            : #include <mutable/util/macro.hpp>
       5                 :            : #include <vector>
       6                 :            : 
       7                 :            : 
       8                 :            : using namespace m;
       9                 :            : using namespace Eigen;
      10                 :            : 
      11                 :            : 
      12                 :            : /*======================================================================================================================
      13                 :            :  * Helper functions
      14                 :            :  *====================================================================================================================*/
      15                 :            : 
      16                 :        838 : MatrixXf m::create_CDF_matrix(const MatrixXf &data)
      17                 :            : {
      18                 :            :     /* Allocate CDF matrix. */
      19                 :        838 :     MatrixXf CDF_matrix(data.rows(), data.cols() + 1); // +1 for y-intercepts in non-linear transformations in RDC
      20                 :            : 
      21                 :            :     /* Allocate index vector and CDF vector. */
      22                 :        838 :     std::vector<unsigned> indices;
      23         [ +  - ]:        838 :     indices.resize(data.rows());
      24                 :            : 
      25         [ +  + ]:       1676 :     for (unsigned col_id = 0; col_id != data.cols(); ++col_id) {
      26                 :            :         /* Initialize index vector. */
      27         [ +  - ]:        838 :         M_insist(indices.size() == (unsigned long)(data.rows()));
      28         [ +  - ]:        838 :         std::iota(indices.begin(), indices.end(), 0);
      29                 :            : 
      30                 :            :         /* Compute index vector that sorts the column. */
      31   [ +  -  +  - ]:      96148 :         std::sort(indices.begin(), indices.end(), [col=data.col(col_id)](unsigned first, unsigned second) {
      32                 :      95310 :             return col(first) < col(second);
      33                 :            :         });
      34                 :            : 
      35                 :            :         /* Translate index vector to CDF. */
      36                 :        838 :         unsigned last_pos = data.rows() - 1;
      37         [ +  - ]:        838 :         float last_value = data(indices[last_pos], col_id);
      38                 :        839 :         unsigned num_same = 0;
      39         [ +  + ]:      24698 :         for (unsigned i = data.rows(); i-->0;) {
      40   [ +  -  +  + ]:      23860 :             if (data(indices[i], col_id) != last_value) {
      41         [ +  - ]:      21613 :                 last_value = data(indices[i], col_id);
      42                 :      21613 :                 last_pos -= num_same;
      43                 :      21613 :                 num_same = 0;
      44                 :      21613 :             }
      45         [ +  - ]:      23860 :             CDF_matrix(indices[i], col_id) = float(last_pos + 1) / data.rows();
      46                 :      23860 :             ++num_same;
      47                 :            :         }
      48                 :        838 :     }
      49                 :            : 
      50                 :            :     /* Add last, all-ones column for Y-intercept of non-liner transformations, used later for RDC computation. */
      51   [ +  -  +  -  :        838 :     CDF_matrix.col(data.cols()) = VectorXf::Ones(data.rows());
                   +  - ]
      52                 :            : 
      53                 :        838 :     return CDF_matrix;
      54         [ +  - ]:        838 : }
      55                 :            : 
      56                 :            : namespace {
      57                 :          1 : 
      58                 :            : template<typename URBG>
      59                 :        880 : MatrixXf create_w_b(float stddev, unsigned num_rows, unsigned k, URBG &&g)
      60                 :            : {
      61                 :        880 :     std::normal_distribution<float> normal_distribution(0.f, stddev);
      62                 :        880 :     MatrixXf w_b(num_rows, k);
      63         [ +  + ]:       5280 :     for (unsigned col_id = 0; col_id != k; ++col_id) {
      64         [ +  - ]:       4400 :         auto col = w_b.col(col_id);
      65         [ +  + ]:      13200 :         for (unsigned row_id = 0; row_id != num_rows; ++row_id)
      66   [ +  -  +  - ]:       8800 :             col(row_id) = normal_distribution(g);
      67                 :       4400 :     }
      68                 :        880 :     return w_b;
      69         [ +  - ]:        880 : }
      70                 :            : 
      71                 :            : }
      72                 :            : 
      73                 :            : 
      74                 :          1 : /*======================================================================================================================
      75                 :            :  * RDC
      76                 :            :  *====================================================================================================================*/
      77                 :            : 
      78                 :          0 : float m::rdc(const Eigen::MatrixXf &X, const Eigen::MatrixXf &Y, unsigned k, float stddev)
      79                 :            : {
      80                 :          0 :     M_insist(X.rows() == Y.rows());
      81                 :            : 
      82                 :            :     /* Estimate copula transformation by CDFs. */
      83                 :          0 :     MatrixXf CDFs_of_X = create_CDF_matrix(X);
      84         [ #  # ]:          0 :     MatrixXf CDFs_of_Y = create_CDF_matrix(Y);
      85         [ #  # ]:          0 :     M_insist(CDFs_of_X.rows() == X.rows());
      86         [ #  # ]:          0 :     M_insist(CDFs_of_Y.rows() == X.rows());
      87         [ #  # ]:          0 :     M_insist(CDFs_of_X.cols() == X.cols() + 1);
      88         [ #  # ]:          0 :     M_insist(CDFs_of_Y.cols() == Y.cols() + 1);
      89                 :            : 
      90   [ #  #  #  # ]:          0 :     return rdc_precomputed_CDF(CDFs_of_X, CDFs_of_Y, k, stddev);
      91                 :          0 : }
      92                 :            : 
      93                 :            : template<typename URBG>
      94                 :        440 : float m::rdc_precomputed_CDF(MatrixXf &CDFs_of_X, MatrixXf &CDFs_of_Y, unsigned k, float stddev, URBG &&g)
      95                 :            : {
      96                 :        440 :     unsigned num_rows = CDFs_of_X.rows();
      97                 :        440 :     M_insist(num_rows == CDFs_of_Y.rows());
      98                 :            : 
      99                 :            :     /* Generate random non-linear projections. */
     100                 :        440 :     MatrixXf X_w_b = create_w_b(stddev, CDFs_of_X.cols(), k, g);
     101         [ +  - ]:        440 :     MatrixXf Y_w_b = create_w_b(stddev, CDFs_of_Y.cols(), k, g);
     102         [ +  - ]:        440 :     M_insist(X_w_b.rows() == CDFs_of_X.cols());
     103         [ +  - ]:        440 :     M_insist(Y_w_b.rows() == CDFs_of_Y.cols());
     104         [ +  - ]:        440 :     M_insist(X_w_b.cols() == k);
     105         [ +  - ]:        440 :     M_insist(Y_w_b.cols() == k);
     106                 :            : 
     107                 :            :     /* Compute canonical correlations. */
     108         [ +  - ]:        440 :     MatrixXf concat_matrix(CDFs_of_X.rows(), 2 * k);
     109   [ +  -  +  -  :        440 :     concat_matrix << (CDFs_of_X * X_w_b), (CDFs_of_Y * Y_w_b);
             +  -  +  - ]
     110   [ +  -  +  -  :        440 :     concat_matrix.array() = concat_matrix.array().sin();
             +  -  +  - ]
     111                 :            : 
     112                 :            :     /* Center concat matrix. */
     113   [ +  -  +  -  :        440 :     const RowVectorXf means = concat_matrix.colwise().mean();
                   +  - ]
     114   [ +  -  +  - ]:        440 :     concat_matrix.rowwise() -= means;
     115                 :            : 
     116                 :            :     /* Compute covariance matrix. */
     117   [ +  -  +  -  :        440 :     MatrixXf covariance_matrix = concat_matrix.adjoint() * concat_matrix / float(num_rows - 1);
             +  -  +  - ]
     118                 :            : 
     119   [ +  -  +  - ]:        440 :     FullPivLU<MatrixXf> CXX(covariance_matrix.block(0, 0, k, k));
     120   [ +  -  +  - ]:        440 :     FullPivLU<MatrixXf> CYY(covariance_matrix.block(k, k, k, k));
     121   [ +  -  +  - ]:        440 :     MatrixXf CXY = covariance_matrix.block(0, k, k, k);
     122   [ +  -  +  - ]:        440 :     MatrixXf CYX = covariance_matrix.block(k, 0, k, k);
     123                 :            : 
     124   [ +  -  +  -  :        440 :     SelfAdjointEigenSolver<MatrixXf> eigensolver((CXX.inverse() * CXY) * (CYY.inverse() * CYX));
          +  -  +  -  +  
                -  -  + ]
     125                 :            : 
     126   [ +  -  +  - ]:        440 :     return sqrtf(eigensolver.eigenvalues().maxCoeff());
     127                 :        440 : }

Generated by: LCOV version 1.16