Learning Curve Plus Plus (LCPP)
functionalpca.h
Go to the documentation of this file.
1 /**
2  * @file functionalpca.h
3  * @author Ozgur Taylan Turan
4  *
5  * Functional PCA
6  *
7  *
8  *
9  */
10 #ifndef FUNCTIONALPCA_H
11 #define FUNCTIONALPCA_H
12 
13 namespace algo::dimred {
14 
15 //-----------------------------------------------------------------------------
16 // ufpca
17 // * Univariate functional pca implementation
18 // @param inputs : inputs (DxN)
19 // @param labels : labels (MxN)
20 //-----------------------------------------------------------------------------
21 template<class T=DTYPE>
22 std::tuple<arma::Col<T>, arma::Mat<T>> ufpca ( const arma::Mat<T>& inputs,
23  const arma::Mat<T>& labels,
24  const bool mean_add = false )
25 {
26 
27  arma::Col<T> eigenvalues;
28  arma::Mat<T> eigenvectors, eigenfunctions;
29 
30  size_t D = inputs.n_rows;
31  size_t N = inputs.n_cols;
32  size_t M = labels.n_rows;
33 
34  assert ( D == 1 && "FunctionalData input dim. != 1!");
35 
36 
37  arma::Mat<T> weights(1, N);
38 
39  weights(0,0) = inputs(0,1)-inputs(0,0);
40  weights(0,N-1) = inputs(0,inputs.n_cols-1)-inputs(0,inputs.n_cols-2);
41  weights(0,arma::span(1,N-2)) =
42  inputs(0,arma::span(2,N-1)) - inputs(0,arma::span(0,N-3));
43 
44  weights *= 0.5;
45 
46  arma::Mat<T> sqrt_W = arma::diagmat(arma::sqrt(weights));
47  arma::Mat<T> inv_sqrt_W = arma::diagmat(arma::sqrt(1./weights));
48  arma::Mat<T> mean = arma::mean(labels,0);
49  arma::Mat<T> adj_labels(M, N);
50 
51  for(size_t i=0; i < M; i++)
52  adj_labels.row(i) = labels.row(i) - mean;
53 
54  arma::Mat<T> covariance = (adj_labels.t() * adj_labels)/(M-1);
55 
56  covariance += covariance.t();
57  covariance *= 0.5;
58  arma::Mat<T> variance = sqrt_W * covariance * sqrt_W;
59 
60  arma::eig_sym(eigenvalues, eigenvectors, variance);
61  eigenvalues = eigenvalues.clamp(0., arma::datum::inf);
62  eigenvalues = arma::reverse(eigenvalues);
63  eigenvectors = arma::reverse(eigenvectors,1);
64 
65  eigenfunctions = (inv_sqrt_W*eigenvectors).t();
66  if ( mean_add )
67  eigenfunctions.each_row() += mean;
68  return std::make_tuple(eigenvalues, eigenfunctions);
69 }
70 
71 //-----------------------------------------------------------------------------
72 // ufpca
73 // * Univariate functional pca implementation
74 // @param inputs : inputs (DxN)
75 // @param labels : labels (MxN)
76 // @param ppc : percentage of principle component
77 //-----------------------------------------------------------------------------
78 template<class T=DTYPE>
79 std::tuple<arma::Col<T>, arma::Mat<T>> ufpca ( const arma::Mat<T>& inputs,
80  const arma::Mat<T>& labels,
81  const double ppc )
82 {
83  arma::Col<T> eigenvalues, eigvals;
84  arma::Mat<T> eigenfunctions, eigfuncs;
85  int npc;
86 
87  size_t N = inputs.n_cols;
88 
89  auto res = ufpca(inputs, labels);
90  eigenvalues = std::get<0>(res);
91  eigenfunctions = std::get<1>(res);
92  arma::vec cum_ppc= arma::cumsum(eigenvalues) / arma::sum(eigenvalues);
93 
94  arma::uvec cum_ppc_index = arma::find( cum_ppc > ppc, 1);
95  npc = cum_ppc_index[0];
96  // Just a tiny hack for getting all the values if the first pc is able to
97  // represent the whole function
98  if (npc == 0)
99  npc = 1;
100 
101  eigvals = eigenvalues.subvec(arma::span(0,npc-1));
102  eigfuncs = eigenfunctions(arma::span(0,npc-1),arma::span(0,N-1));
103 
104  return std::make_tuple(eigvals, eigfuncs);
105 }
106 
107 //-----------------------------------------------------------------------------
108 // ufpca
109 // * Functional PCA mmplementation for 1D problems
110 // @param inputs : inputs (DxN)
111 // @param labels : labels (MxN)
112 // @param npc : number of principle component
113 //-----------------------------------------------------------------------------
114 template<class T=DTYPE>
115 std::tuple<arma::Col<T>, arma::Mat<T>> ufpca ( const arma::Mat<T>& inputs,
116  const arma::Mat<T>& labels,
117  const size_t npc )
118 {
119 
120  arma::Col<T> eigenvalues, eigvals;
121  arma::Mat<T> eigenfunctions, eigfuncs;
122 
123  size_t N = inputs.n_cols;
124 
125  auto res = ufpca(inputs, labels);
126 
127  eigenvalues = std::get<0>(res);
128  eigenfunctions = std::get<1>(res);
129 
130  eigvals = eigenvalues.subvec(arma::span(0,npc-1));
131  eigfuncs = eigenfunctions(arma::span(0,npc-1),arma::span(0,N-1));
132 
133  return std::make_tuple(eigvals, eigfuncs);
134 }
135 
136 } // namespace algo::dimred
137 
138 
139 #endif