v2.0.0
Loading...
Searching...
No Matches
mne_cov_matrix.cpp
Go to the documentation of this file.
1//=============================================================================================================
36
37//=============================================================================================================
38// INCLUDES
39//=============================================================================================================
40
41#include "mne_cov_matrix.h"
42#include "mne_sss_data.h"
43#include "mne_proj_item.h"
44#include "mne_proj_op.h"
45
46#include <Eigen/Core>
47#include <Eigen/Eigenvalues>
49
50//=============================================================================================================
51// USED NAMESPACES
52//=============================================================================================================
53
54using namespace Eigen;
55using namespace FIFFLIB;
56using namespace MNELIB;
57
58#ifndef FAIL
59#define FAIL -1
60#endif
61
62#ifndef OK
63#define OK 0
64#endif
65
66//============================= mne_decompose.c =============================
67
68int mne_decompose_eigen (const VectorXd& mat,
69 VectorXd& lambda,
70 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>& vectors,
71 int dim)
72/*
73 * Compute the eigenvalue decomposition of
74 * a symmetric matrix using the LAPACK routines
75 *
76 * 'mat' contains the lower triangle of the matrix
77 */
78{
79 int np = dim*(dim+1)/2;
80 int maxi;
81 double scale;
82
83// idamax workaround begin
84 maxi = 0;
85 for(int i = 0; i < np; ++i)
86 if (std::fabs(mat[i]) > std::fabs(mat[maxi]))
87 maxi = i;
88// idamax workaround end
89
90 scale = 1.0/mat[maxi];
91
92// dspev workaround begin
93 MatrixXd dmat_full = MatrixXd::Zero(dim,dim);
94 int idx = 0;
95 for (int i = 0; i < dim; ++i) {
96 for(int j = 0; j <= i; ++j) {
97 double val = mat[idx]*scale;
98 dmat_full(i,j) = val;
99 dmat_full(j,i) = val;
100 ++idx;
101 }
102 }
103 SelfAdjointEigenSolver<MatrixXd> es;
104 es.compute(dmat_full);
105// dspev workaround end
106
107 scale = 1.0/scale;
108 lambda = es.eigenvalues() * scale;
109 vectors = es.eigenvectors().transpose().cast<float>();
110
111 return 0;
112}
113
114//=============================================================================================================
115// DEFINE MEMBER METHODS
116//=============================================================================================================
117
119 int p_ncov,
120 const QStringList& p_names,
121 const VectorXd& p_cov,
122 const VectorXd& p_cov_diag,
123 FiffSparseMatrix* p_cov_sparse)
124:kind(p_kind)
125,ncov(p_ncov)
126,nfree(1)
127,nproj(0)
128,nzero(0)
129,names(p_names)
130,cov(p_cov)
131,cov_diag(p_cov_diag)
132,cov_sparse(p_cov_sparse)
133,proj(nullptr)
134,sss(nullptr)
135,nbad(0)
136{
137}
138
139//=============================================================================================================
140
144
145//=============================================================================================================
146
147std::unique_ptr<MNECovMatrix> MNECovMatrix::dup() const
148{
149 auto res = cov_diag.size() > 0
150 ? create(kind,ncov,names,VectorXd(),VectorXd(cov_diag))
151 : create(kind,ncov,names,VectorXd(cov),VectorXd());
152 /*
153 * Duplicate additional items
154 */
155 if (ch_class.size() > 0) {
156 res->ch_class = ch_class;
157 }
158 res->bads = bads;
159 res->nbad = nbad;
160 res->proj.reset(proj ? proj->dup() : nullptr);
161 if (sss)
162 res->sss = std::make_unique<MNESssData>(*sss);
163
164 return res;
165}
166
167//=============================================================================================================
168
170{
171 return cov_diag.size() > 0;
172}
173
174//=============================================================================================================
175
177/*
178 * Calculate the inverse square roots for whitening
179 */
180{
181 const VectorXd& src = lambda.size() > 0 ? lambda : cov_diag;
182 int k;
183
184 if (src.size() == 0) {
185 qCritical("Covariance matrix is not diagonal or not decomposed.");
186 return FAIL;
187 }
188 inv_lambda.resize(ncov);
189 for (k = 0; k < ncov; k++) {
190 if (src[k] <= 0.0)
191 inv_lambda[k] = 0.0;
192 else
193 inv_lambda[k] = 1.0/sqrt(src[k]);
194 }
195 return OK;
196}
197
198//=============================================================================================================
199
200int MNECovMatrix::condition(float rank_threshold, int use_rank)
201{
202 VectorXd scale_vec;
203 VectorXd cov_local;
204 VectorXd lambda_local;
205 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> local_eigen;
206 MatrixXd data1;
207 double magscale,gradscale,eegscale;
208 int nmag,ngrad,neeg,nok;
209 int j,k;
210 int res = FAIL;
211
212 if (cov_diag.size() > 0)
213 return OK;
214 if (ch_class.size() == 0) {
215 qCritical("Channels not classified. Rank cannot be determined.");
216 return FAIL;
217 }
218 magscale = gradscale = eegscale = 0.0;
219 nmag = ngrad = neeg = 0;
220 for (k = 0; k < ncov; k++) {
221 if (ch_class[k] == MNE_COV_CH_MEG_MAG) {
222 magscale += this->cov[lt_packed_index(k,k)]; nmag++;
223 }
224 else if (ch_class[k] == MNE_COV_CH_MEG_GRAD) {
225 gradscale += this->cov[lt_packed_index(k,k)]; ngrad++;
226 }
227 else if (ch_class[k] == MNE_COV_CH_EEG) {
228 eegscale += this->cov[lt_packed_index(k,k)]; neeg++;
229 }
230#ifdef DEBUG
231 fprintf(stdout,"%d ",ch_class[k]);
232#endif
233 }
234#ifdef DEBUG
235 fprintf(stdout,"\n");
236#endif
237 if (nmag > 0)
238 magscale = magscale > 0.0 ? sqrt(nmag/magscale) : 0.0;
239 if (ngrad > 0)
240 gradscale = gradscale > 0.0 ? sqrt(ngrad/gradscale) : 0.0;
241 if (neeg > 0)
242 eegscale = eegscale > 0.0 ? sqrt(neeg/eegscale) : 0.0;
243#ifdef DEBUG
244 fprintf(stdout,"%d %g\n",nmag,magscale);
245 fprintf(stdout,"%d %g\n",ngrad,gradscale);
246 fprintf(stdout,"%d %g\n",neeg,eegscale);
247#endif
248 scale_vec.resize(ncov);
249 for (k = 0; k < ncov; k++) {
251 scale_vec[k] = magscale;
252 else if (ch_class[k] == MNE_COV_CH_MEG_GRAD)
253 scale_vec[k] = gradscale;
254 else if (ch_class[k] == MNE_COV_CH_EEG)
255 scale_vec[k] = eegscale;
256 else
257 scale_vec[k] = 1.0;
258 }
259 cov_local.resize(ncov*(ncov+1)/2);
260 lambda_local.resize(ncov);
261 local_eigen.resize(ncov,ncov);
262 for (j = 0; j < ncov; j++)
263 for (k = 0; k <= j; k++)
264 cov_local[lt_packed_index(j,k)] = this->cov[lt_packed_index(j,k)]*scale_vec[j]*scale_vec[k];
265 if (mne_decompose_eigen(cov_local,lambda_local,local_eigen,ncov) == 0) {
266#ifdef DEBUG
267 for (k = 0; k < ncov; k++)
268 fprintf(stdout,"%g ",lambda_local[k]/lambda_local[ncov-1]);
269 fprintf(stdout,"\n");
270#endif
271 nok = 0;
272 for (k = ncov-1; k >= 0; k--) {
273 if (lambda_local[k] >= rank_threshold*lambda_local[ncov-1])
274 nok++;
275 else
276 break;
277 }
278 printf("\n\tEstimated covariance matrix rank = %d (%g)\n",nok,lambda_local[ncov-nok]/lambda_local[ncov-1]);
279 if (use_rank > 0 && use_rank < nok) {
280 nok = use_rank;
281 printf("\tUser-selected covariance matrix rank = %d (%g)\n",nok,lambda_local[ncov-nok]/lambda_local[ncov-1]);
282 }
283 /*
284 * Put it back together
285 */
286 for (j = 0; j < ncov-nok; j++)
287 lambda_local[j] = 0.0;
288 data1.resize(ncov,ncov);
289 for (j = 0; j < ncov; j++) {
290#ifdef DEBUG
291 mne_print_vector(stdout,NULL,local_eigen.row(j).data(),ncov);
292#endif
293 for (k = 0; k < ncov; k++)
294 data1(j,k) = sqrt(lambda_local[j])*local_eigen(j,k);
295 }
296 MatrixXd data2 = data1.transpose() * data1;
297#ifdef DEBUG
298 printf(">>>\n");
299 for (j = 0; j < ncov; j++)
300 mne_print_dvector(stdout,NULL,data2.row(j).data(),ncov);
301 printf(">>>\n");
302#endif
303 /*
304 * Scale back
305 */
306 for (k = 0; k < ncov; k++)
307 if (scale_vec[k] > 0.0)
308 scale_vec[k] = 1.0/scale_vec[k];
309 for (j = 0; j < ncov; j++)
310 for (k = 0; k <= j; k++)
311 if (this->cov[lt_packed_index(j,k)] != 0.0)
312 this->cov[lt_packed_index(j,k)] = scale_vec[j]*scale_vec[k]*data2(j,k);
313 res = nok;
314 }
315 return res;
316}
317
318//=============================================================================================================
319
320int MNECovMatrix::decompose_eigen_small(float p_small, int use_rank)
321/*
322 * Do the eigenvalue decomposition
323 */
324{
325 int k,p,rank;
326 float rank_threshold = 1e-6;
327
328 if (p_small < 0)
329 p_small = 1.0;
330
331 if (cov_diag.size() > 0)
332 return add_inv();
333 if (lambda.size() > 0 && eigen.size() > 0) {
334 printf("\n\tEigenvalue decomposition had been precomputed.\n");
335 nzero = 0;
336 for (k = 0; k < ncov; k++, nzero++)
337 if (lambda[k] > 0)
338 break;
339 }
340 else {
341 lambda.resize(0);
342 eigen.resize(0,0);
343
344 if ((rank = condition(rank_threshold,use_rank)) < 0)
345 return FAIL;
346
347 lambda.resize(ncov);
348 eigen.resize(ncov,ncov);
350 goto bad;
351 nzero = ncov - rank;
352 for (k = 0; k < nzero; k++)
353 lambda[k] = 0.0;
354 /*
355 * Find which eigenvectors correspond to EEG/MEG
356 */
357 {
358 float meglike,eeglike;
359 int nmeg,neeg;
360
361 nmeg = neeg = 0;
362 for (k = nzero; k < ncov; k++) {
363 meglike = eeglike = 0.0;
364 for (p = 0; p < ncov; p++) {
365 if (ch_class[p] == MNE_COV_CH_EEG)
366 eeglike += std::fabs(eigen(k,p));
368 meglike += std::fabs(eigen(k,p));
369 }
370 if (meglike > eeglike)
371 nmeg++;
372 else
373 neeg++;
374 }
375 printf("\t%d MEG and %d EEG-like channels remain in the whitened data\n",nmeg,neeg);
376 }
377 }
378 return add_inv();
379
380bad : {
381 lambda.resize(0);
382 eigen.resize(0,0);
383 return FAIL;
384 }
385}
386
387//=============================================================================================================
388
390
391{
392 return decompose_eigen_small(-1.0,-1);
393}
394
395//=============================================================================================================
396
397int MNECovMatrix::lt_packed_index(int j, int k)
398
399{
400 if (j >= k)
401 return k + j*(j+1)/2;
402 else
403 return j + k*(k+1)/2;
404}
FiffSparseMatrix class declaration.
MNECovMatrix class declaration.
#define MNE_COV_CH_MEG_MAG
#define MNE_COV_CH_EEG
#define MNE_COV_CH_MEG_GRAD
MNEProjOp class declaration.
MNEProjItem class declaration.
int mne_decompose_eigen(const VectorXd &mat, VectorXd &lambda, Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > &vectors, int dim)
MNE SSS Data (MNESssData) class declaration.
#define OK
#define FAIL
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
FIFF sparse matrix storage.
Eigen::VectorXd inv_lambda
std::unique_ptr< FIFFLIB::FiffSparseMatrix > cov_sparse
Eigen::VectorXd lambda
int condition(float rank_threshold, int use_rank)
int decompose_eigen_small(float p_small, int use_rank)
std::unique_ptr< MNEProjOp > proj
std::unique_ptr< MNECovMatrix > dup() const
MNECovMatrix(int p_kind, int p_ncov, const QStringList &p_names, const Eigen::VectorXd &p_cov, const Eigen::VectorXd &p_cov_diag, FIFFLIB::FiffSparseMatrix *p_cov_sparse)
std::unique_ptr< MNESssData > sss
Eigen::VectorXd cov
Eigen::VectorXd cov_diag
static std::unique_ptr< MNECovMatrix > create(int kind, int ncov, const QStringList &names, const Eigen::VectorXd &cov, const Eigen::VectorXd &cov_diag)
Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > eigen
Eigen::VectorXi ch_class