v2.0.0
Loading...
Searching...
No Matches
mne_cov_matrix.cpp
Go to the documentation of this file.
1//=============================================================================================================
16
17//=============================================================================================================
18// INCLUDES
19//=============================================================================================================
20
21#include "mne_cov_matrix.h"
22#include "mne_sss_data.h"
23#include "mne_proj_item.h"
24#include "mne_proj_op.h"
25
26#include <Eigen/Core>
27#include <Eigen/Eigenvalues>
29#include <fiff/fiff_ch_info.h>
30#include <fiff/fiff_constants.h>
31#include <fiff/fiff_stream.h>
32#include <fiff/fiff_tag.h>
33
34#include <QFile>
35#include <QDebug>
36
37//=============================================================================================================
38// USED NAMESPACES
39//=============================================================================================================
40
41using namespace Eigen;
42using namespace FIFFLIB;
43using namespace MNELIB;
44
45constexpr int FAIL = -1;
46constexpr int OK = 0;
47
48//============================= mne_decompose.c =============================
49
50int mne_decompose_eigen (const VectorXd& mat,
51 VectorXd& lambda,
52 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>& vectors,
53 int dim)
54/*
55 * Compute the eigenvalue decomposition of
56 * a symmetric matrix using the LAPACK routines
57 *
58 * 'mat' contains the lower triangle of the matrix
59 */
60{
61 int np = dim*(dim+1)/2;
62 int maxi;
63 double scale;
64
65// idamax workaround begin
66 maxi = 0;
67 for(int i = 0; i < np; ++i)
68 if (std::fabs(mat[i]) > std::fabs(mat[maxi]))
69 maxi = i;
70// idamax workaround end
71
72 scale = 1.0/mat[maxi];
73
74// dspev workaround begin
75 MatrixXd dmat_full = MatrixXd::Zero(dim,dim);
76 int idx = 0;
77 for (int i = 0; i < dim; ++i) {
78 for(int j = 0; j <= i; ++j) {
79 double val = mat[idx]*scale;
80 dmat_full(i,j) = val;
81 dmat_full(j,i) = val;
82 ++idx;
83 }
84 }
85 SelfAdjointEigenSolver<MatrixXd> es;
86 es.compute(dmat_full);
87// dspev workaround end
88
89 scale = 1.0/scale;
90 lambda = es.eigenvalues() * scale;
91 vectors = es.eigenvectors().transpose().cast<float>();
92
93 return 0;
94}
95
96//=============================================================================================================
97// DEFINE MEMBER METHODS
98//=============================================================================================================
99
101 int p_ncov,
102 const QStringList& p_names,
103 const VectorXd& p_cov,
104 const VectorXd& p_cov_diag,
105 FiffSparseMatrix* p_cov_sparse)
106:kind(p_kind)
107,ncov(p_ncov)
108,nfree(1)
109,nproj(0)
110,nzero(0)
111,names(p_names)
112,cov(p_cov)
113,cov_diag(p_cov_diag)
114,cov_sparse(p_cov_sparse)
115,proj(nullptr)
116,sss(nullptr)
117,nbad(0)
118{
119}
120
121//=============================================================================================================
122
126
127//=============================================================================================================
128
129std::unique_ptr<MNECovMatrix> MNECovMatrix::read(const QString& name, int kind)
130{
131 QFile file(name);
132 FiffStream::SPtr stream(new FiffStream(&file));
133
134 FiffTag::UPtr t_pTag;
135 QList<FiffDirNode::SPtr> nodes;
136 FiffDirNode::SPtr covnode;
137
138 QStringList names;
139 int nnames = 0;
140 Eigen::VectorXd cov;
141 Eigen::VectorXd cov_diag;
142 std::unique_ptr<FiffSparseMatrix> cov_sparse_owner;
143 Eigen::VectorXd lambda;
144 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> eigen;
145 MatrixXf tmp_eigen;
146 QStringList bads;
147 int nbad = 0;
148 int ncov = 0;
149 int nfree = 1;
150 std::unique_ptr<MNECovMatrix> res;
151
152 int k,p,nn;
153 const float *f;
154 const double *d;
155 std::unique_ptr<MNEProjOp> op;
156 std::unique_ptr<MNESssData> sss;
157
158 if (!stream->open())
159 return nullptr;
160
161 nodes = stream->dirtree()->dir_tree_find(FIFFB_MNE_COV);
162
163 if (nodes.size() == 0) {
164 qWarning("No covariance matrix available in %s", name.toUtf8().data());
165 stream->close();
166 return nullptr;
167 }
168 /*
169 * Locate the desired matrix
170 */
171 for (k = 0; k < nodes.size(); ++k) {
172 if (!nodes[k]->find_tag(stream, FIFF_MNE_COV_KIND, t_pTag))
173 continue;
174
175 if (*t_pTag->toInt() == kind) {
176 covnode = nodes[k];
177 break;
178 }
179 }
180 if (covnode->isEmpty()) {
181 qWarning("Desired covariance matrix not found from %s", name.toUtf8().data());
182 stream->close();
183 return nullptr;
184 }
185 /*
186 * Read the data
187 */
188 if (!nodes[k]->find_tag(stream, FIFF_MNE_COV_DIM, t_pTag)) {
189 stream->close();
190 return nullptr;
191 }
192 ncov = *t_pTag->toInt();
193
194 if (nodes[k]->find_tag(stream, FIFF_MNE_COV_NFREE, t_pTag)) {
195 nfree = *t_pTag->toInt();
196 }
197 if (covnode->find_tag(stream, FIFF_MNE_ROW_NAMES, t_pTag)) {
198 names = FiffStream::split_name_list(t_pTag->toString());
199 nnames = names.size();
200 if (nnames != ncov) {
201 qCritical("Incorrect number of channel names for a covariance matrix");
202 stream->close();
203 return nullptr;
204 }
205 }
206 if (!nodes[k]->find_tag(stream, FIFF_MNE_COV, t_pTag)) {
207 if (!nodes[k]->find_tag(stream, FIFF_MNE_COV_DIAG, t_pTag)) {
208 stream->close();
209 return nullptr;
210 } else {
211 if (t_pTag->getType() == FIFFT_DOUBLE) {
212 cov_diag.resize(ncov);
213 d = t_pTag->toDouble();
214 for (p = 0; p < ncov; p++)
215 cov_diag[p] = d[p];
216 /*
217 * Check for all-zero data
218 */
219 if (cov_diag.sum() == 0.0) {
220 qCritical("Sum of covariance matrix elements is zero!");
221 stream->close();
222 return nullptr;
223 }
224 } else if (t_pTag->getType() == FIFFT_FLOAT) {
225 cov_diag.resize(ncov);
226 f = t_pTag->toFloat();
227 for (p = 0; p < ncov; p++)
228 cov_diag[p] = f[p];
229 } else {
230 qWarning("Illegal data type for covariance matrix");
231 stream->close();
232 return nullptr;
233 }
234 }
235 } else {
236 nn = ncov * (ncov + 1) / 2;
237 if (t_pTag->getType() == FIFFT_DOUBLE) {
238 cov.resize(nn);
239 d = t_pTag->toDouble();
240 for (p = 0; p < nn; p++)
241 cov[p] = d[p];
242 if (cov.sum() == 0.0) {
243 qCritical("Sum of covariance matrix elements is zero!");
244 stream->close();
245 return nullptr;
246 }
247 } else if (t_pTag->getType() == FIFFT_FLOAT) {
248 cov.resize(nn);
249 f = t_pTag->toFloat();
250 for (p = 0; p < nn; p++)
251 cov[p] = f[p];
252 } else {
253 cov_sparse_owner = FiffSparseMatrix::fiff_get_float_sparse_matrix(t_pTag);
254 if (!cov_sparse_owner) {
255 stream->close();
256 return nullptr;
257 }
258 }
259
260 if (nodes[k]->find_tag(stream, FIFF_MNE_COV_EIGENVALUES, t_pTag)) {
261 const double *lambda_data = static_cast<const double *>(t_pTag->toDouble());
262 lambda = Eigen::Map<const Eigen::VectorXd>(lambda_data, ncov);
263 if (nodes[k]->find_tag(stream, FIFF_MNE_COV_EIGENVECTORS, t_pTag)) {
264 stream->close();
265 return nullptr;
266 }
267
268 tmp_eigen = t_pTag->toFloatMatrix().transpose();
269 eigen.resize(tmp_eigen.rows(), tmp_eigen.cols());
270 for (int r = 0; r < tmp_eigen.rows(); ++r)
271 for (int c = 0; c < tmp_eigen.cols(); ++c)
272 eigen(r, c) = tmp_eigen(r, c);
273 }
274 /*
275 * Read the optional projection operator
276 */
277 op = MNEProjOp::read_from_node(stream, nodes[k]);
278 if (!op) {
279 stream->close();
280 return nullptr;
281 }
282 /*
283 * Read the optional SSS data
284 */
285 sss = MNESssData::read_from_node(stream, nodes[k]);
286 if (!sss) {
287 stream->close();
288 return nullptr;
289 }
290 /*
291 * Read the optional bad channel list
292 */
293 bads = stream->read_bad_channels(nodes[k]);
294 nbad = bads.size();
295 }
296 if (cov_sparse_owner)
297 res = create_sparse(kind, ncov, names, cov_sparse_owner.release());
298 else if (cov.size() > 0)
299 res = create_dense(kind, ncov, names, cov);
300 else if (cov_diag.size() > 0)
302 else {
303 qCritical("MNECovMatrix::read : covariance matrix data is not defined.");
304 stream->close();
305 return nullptr;
306 }
307 res->eigen = std::move(eigen);
308 res->lambda = std::move(lambda);
309 res->nfree = nfree;
310 res->bads = bads;
311 res->nbad = nbad;
312 /*
313 * Count the non-zero eigenvalues
314 */
315 if (res->lambda.size() > 0) {
316 res->nzero = 0;
317 for (k = 0; k < res->ncov; k++, res->nzero++)
318 if (res->lambda[k] > 0)
319 break;
320 }
321
322 if (op && op->nitems > 0) {
323 res->proj = std::move(op);
324 }
325 if (sss && sss->comp_info.size() > 0 && sss->job != FIFFV_SSS_JOB_NOTHING) {
326 res->sss = std::move(sss);
327 }
328
329 stream->close();
330 return res;
331}
332
333//=============================================================================================================
334
335std::unique_ptr<MNECovMatrix> MNECovMatrix::dup() const
336{
337 auto res = cov_diag.size() > 0
338 ? create(kind,ncov,names,VectorXd(),VectorXd(cov_diag))
339 : create(kind,ncov,names,VectorXd(cov),VectorXd());
340 /*
341 * Duplicate additional items
342 */
343 if (ch_class.size() > 0) {
344 res->ch_class = ch_class;
345 }
346 res->bads = bads;
347 res->nbad = nbad;
348 res->proj = proj ? proj->dup() : nullptr;
349 if (sss)
350 res->sss = std::make_unique<MNESssData>(*sss);
351
352 return res;
353}
354
355//=============================================================================================================
356
358{
359 return cov_diag.size() > 0;
360}
361
362//=============================================================================================================
363
365/*
366 * Calculate the inverse square roots for whitening
367 */
368{
369 const VectorXd& src = lambda.size() > 0 ? lambda : cov_diag;
370 int k;
371
372 if (src.size() == 0) {
373 qCritical("Covariance matrix is not diagonal or not decomposed.");
374 return FAIL;
375 }
376 inv_lambda.resize(ncov);
377 for (k = 0; k < ncov; k++) {
378 if (src[k] <= 0.0)
379 inv_lambda[k] = 0.0;
380 else
381 inv_lambda[k] = 1.0/sqrt(src[k]);
382 }
383 return OK;
384}
385
386//=============================================================================================================
387
388int MNECovMatrix::condition(float rank_threshold, int use_rank)
389{
390 VectorXd scale_vec;
391 VectorXd cov_local;
392 VectorXd lambda_local;
393 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> local_eigen;
394 MatrixXd data1;
395 double magscale,gradscale,eegscale;
396 int nmag,ngrad,neeg,nok;
397 int j,k;
398 int res = FAIL;
399
400 if (cov_diag.size() > 0)
401 return OK;
402 if (ch_class.size() == 0) {
403 qCritical("Channels not classified. Rank cannot be determined.");
404 return FAIL;
405 }
406 magscale = gradscale = eegscale = 0.0;
407 nmag = ngrad = neeg = 0;
408 for (k = 0; k < ncov; k++) {
409 if (ch_class[k] == MNE_COV_CH_MEG_MAG) {
410 magscale += this->cov[lt_packed_index(k,k)]; nmag++;
411 }
412 else if (ch_class[k] == MNE_COV_CH_MEG_GRAD) {
413 gradscale += this->cov[lt_packed_index(k,k)]; ngrad++;
414 }
415 else if (ch_class[k] == MNE_COV_CH_EEG) {
416 eegscale += this->cov[lt_packed_index(k,k)]; neeg++;
417 }
418#ifdef DEBUG
419 fprintf(stdout,"%d ",ch_class[k]);
420#endif
421 }
422#ifdef DEBUG
423 fprintf(stdout,"\n");
424#endif
425 if (nmag > 0)
426 magscale = magscale > 0.0 ? sqrt(nmag/magscale) : 0.0;
427 if (ngrad > 0)
428 gradscale = gradscale > 0.0 ? sqrt(ngrad/gradscale) : 0.0;
429 if (neeg > 0)
430 eegscale = eegscale > 0.0 ? sqrt(neeg/eegscale) : 0.0;
431#ifdef DEBUG
432 fprintf(stdout,"%d %g\n",nmag,magscale);
433 fprintf(stdout,"%d %g\n",ngrad,gradscale);
434 fprintf(stdout,"%d %g\n",neeg,eegscale);
435#endif
436 scale_vec.resize(ncov);
437 for (k = 0; k < ncov; k++) {
439 scale_vec[k] = magscale;
440 else if (ch_class[k] == MNE_COV_CH_MEG_GRAD)
441 scale_vec[k] = gradscale;
442 else if (ch_class[k] == MNE_COV_CH_EEG)
443 scale_vec[k] = eegscale;
444 else
445 scale_vec[k] = 1.0;
446 }
447 cov_local.resize(ncov*(ncov+1)/2);
448 lambda_local.resize(ncov);
449 local_eigen.resize(ncov,ncov);
450 for (j = 0; j < ncov; j++)
451 for (k = 0; k <= j; k++)
452 cov_local[lt_packed_index(j,k)] = this->cov[lt_packed_index(j,k)]*scale_vec[j]*scale_vec[k];
453 if (mne_decompose_eigen(cov_local,lambda_local,local_eigen,ncov) == 0) {
454#ifdef DEBUG
455 for (k = 0; k < ncov; k++)
456 fprintf(stdout,"%g ",lambda_local[k]/lambda_local[ncov-1]);
457 fprintf(stdout,"\n");
458#endif
459 nok = 0;
460 for (k = ncov-1; k >= 0; k--) {
461 if (lambda_local[k] >= rank_threshold*lambda_local[ncov-1])
462 nok++;
463 else
464 break;
465 }
466 qInfo("\n\tEstimated covariance matrix rank = %d (%g)\n",nok,lambda_local[ncov-nok]/lambda_local[ncov-1]);
467 if (use_rank > 0 && use_rank < nok) {
468 nok = use_rank;
469 qInfo("\tUser-selected covariance matrix rank = %d (%g)\n",nok,lambda_local[ncov-nok]/lambda_local[ncov-1]);
470 }
471 /*
472 * Put it back together
473 */
474 for (j = 0; j < ncov-nok; j++)
475 lambda_local[j] = 0.0;
476 data1.resize(ncov,ncov);
477 for (j = 0; j < ncov; j++) {
478#ifdef DEBUG
479 mne_print_vector(stdout,nullptr,local_eigen.row(j).data(),ncov);
480#endif
481 for (k = 0; k < ncov; k++)
482 data1(j,k) = sqrt(lambda_local[j])*local_eigen(j,k);
483 }
484 MatrixXd data2 = data1.transpose() * data1;
485#ifdef DEBUG
486 qInfo(">>>\n");
487 for (j = 0; j < ncov; j++)
488 mne_print_dvector(stdout,nullptr,data2.row(j).data(),ncov);
489 qInfo(">>>\n");
490#endif
491 /*
492 * Scale back
493 */
494 for (k = 0; k < ncov; k++)
495 if (scale_vec[k] > 0.0)
496 scale_vec[k] = 1.0/scale_vec[k];
497 for (j = 0; j < ncov; j++)
498 for (k = 0; k <= j; k++)
499 if (this->cov[lt_packed_index(j,k)] != 0.0)
500 this->cov[lt_packed_index(j,k)] = scale_vec[j]*scale_vec[k]*data2(j,k);
501 res = nok;
502 }
503 return res;
504}
505
506//=============================================================================================================
507
508int MNECovMatrix::decompose_eigen_small(float p_small, int use_rank)
509/*
510 * Do the eigenvalue decomposition
511 */
512{
513 int k,p,rank;
514 float rank_threshold = 1e-6;
515
516 if (p_small < 0)
517 p_small = 1.0;
518
519 if (cov_diag.size() > 0)
520 return add_inv();
521 if (lambda.size() > 0 && eigen.size() > 0) {
522 qInfo("\n\tEigenvalue decomposition had been precomputed.\n");
523 nzero = 0;
524 for (k = 0; k < ncov; k++, nzero++)
525 if (lambda[k] > 0)
526 break;
527 }
528 else {
529 lambda.resize(0);
530 eigen.resize(0,0);
531
532 if ((rank = condition(rank_threshold,use_rank)) < 0)
533 return FAIL;
534
535 lambda.resize(ncov);
536 eigen.resize(ncov,ncov);
538 lambda.resize(0);
539 eigen.resize(0,0);
540 return FAIL;
541 }
542 nzero = ncov - rank;
543 for (k = 0; k < nzero; k++)
544 lambda[k] = 0.0;
545 /*
546 * Find which eigenvectors correspond to EEG/MEG
547 */
548 {
549 float meglike,eeglike;
550 int nmeg,neeg;
551
552 nmeg = neeg = 0;
553 for (k = nzero; k < ncov; k++) {
554 meglike = eeglike = 0.0;
555 for (p = 0; p < ncov; p++) {
556 if (ch_class[p] == MNE_COV_CH_EEG)
557 eeglike += std::fabs(eigen(k,p));
559 meglike += std::fabs(eigen(k,p));
560 }
561 if (meglike > eeglike)
562 nmeg++;
563 else
564 neeg++;
565 }
566 qInfo("\t%d MEG and %d EEG-like channels remain in the whitened data\n",nmeg,neeg);
567 }
568 }
569 return add_inv();
570}
571
572//=============================================================================================================
573
575
576{
577 return decompose_eigen_small(-1.0,-1);
578}
579
580//=============================================================================================================
581
583
584{
585 if (j >= k)
586 return k + j*(j+1)/2;
587 else
588 return j + k*(k+1)/2;
589}
590
591//=============================================================================================================
592
593int MNECovMatrix::classify_channels(const QList<FiffChInfo>& chs, int nchan)
594{
595 int k,p;
596 FiffChInfo ch;
597
598 if (chs.isEmpty()) {
599 qCritical("Channel information not available in classify_channels");
600 return FAIL;
601 }
602 ch_class.resize(ncov);
603 for (k = 0; k < ncov; k++) {
605 for (p = 0; p < nchan; p++) {
606 if (QString::compare(chs[p].ch_name,names[k]) == 0) {
607 ch = chs[p];
608 if (ch.kind == FIFFV_MEG_CH) {
609 if (ch.unit == FIFF_UNIT_T)
611 else
613 }
614 else if (ch.kind == FIFFV_EEG_CH)
616 break;
617 }
618 }
619 }
620 return OK;
621}
622
623//=============================================================================================================
624
625int MNECovMatrix::whiten_vector(Eigen::Ref<Eigen::VectorXf> data, Eigen::Ref<Eigen::VectorXf> whitened_data, int nchan) const
626{
627 if (ncov != nchan) {
628 qWarning("Incompatible covariance matrix. Cannot whiten the data.");
629 return FAIL;
630 }
631 const double *inv = inv_lambda.data();
632 if (is_diag()) {
633 for (int k = 0; k < nchan; k++)
634 whitened_data[k] = data[k]*inv[k];
635 }
636 else {
637 Eigen::VectorXf tmp(nchan);
638 for (int k = nzero; k < nchan; k++)
639 tmp[k] = eigen.row(k).dot(data.cast<float>());
640 for (int k = 0; k < nzero; k++)
641 whitened_data[k] = 0.0;
642 for (int k = nzero; k < nchan; k++)
643 whitened_data[k] = tmp[k]*inv[k];
644 }
645 return OK;
646}
647
648//=============================================================================================================
649
650void MNECovMatrix::regularize(const Eigen::Vector3f& regs)
651{
652 int j;
653 float sums[3],nn[3];
654 int nkind = 3;
655
656 if (cov.size() == 0 || ch_class.size() == 0)
657 return;
658
659 for (j = 0; j < nkind; j++) {
660 sums[j] = 0.0;
661 nn[j] = 0;
662 }
663 for (j = 0; j < ncov; j++) {
664 if (ch_class[j] >= 0) {
665 sums[ch_class[j]] += cov[lt_packed_index(j,j)];
666 nn[ch_class[j]]++;
667 }
668 }
669 qInfo("Average noise-covariance matrix diagonals:");
670 for (j = 0; j < nkind; j++) {
671 if (nn[j] > 0) {
672 sums[j] = sums[j]/nn[j];
673 if (j == MNE_COV_CH_MEG_MAG)
674 qInfo("\tMagnetometers : %-7.2f fT reg = %-6.2f",1e15*sqrt(sums[j]),regs[j]);
675 else if (j == MNE_COV_CH_MEG_GRAD)
676 qInfo("\tPlanar gradiometers : %-7.2f fT/cm reg = %-6.2f",1e13*sqrt(sums[j]),regs[j]);
677 else
678 qInfo("\tEEG : %-7.2f uV reg = %-6.2f",1e6*sqrt(sums[j]),regs[j]);
679 sums[j] = regs[j]*sums[j];
680 }
681 }
682 for (j = 0; j < ncov; j++)
683 if (ch_class[j] >= 0)
684 cov[lt_packed_index(j,j)] += sums[ch_class[j]];
685
686 qInfo("Noise-covariance regularized as requested.");
687}
688
689//=============================================================================================================
690
692{
693 int k,p;
694 if (cov.size() == 0)
695 return;
696
697 cov_diag.resize(ncov);
698
699 for (k = p = 0; k < ncov; k++) {
700 cov_diag[k] = cov[p];
701 p = p + k + 2;
702 }
703 cov.resize(0);
704
705 lambda.resize(0);
706 eigen.resize(0,0);
707}
708
709//=============================================================================================================
710
711std::unique_ptr<MNECovMatrix> MNECovMatrix::pick_chs_omit(const QStringList& new_names,
712 int new_ncov,
713 int omit_meg_eeg,
714 const QList<FiffChInfo>& chs) const
715{
716 int j,k;
717 Eigen::VectorXd cov_local;
718 Eigen::VectorXd cov_diag_local;
719 QStringList picked_names;
720 int from,to;
721 std::unique_ptr<MNECovMatrix> res;
722
723 if (new_ncov == 0) {
724 qCritical("No channels specified for picking in pick_chs_omit");
725 return nullptr;
726 }
727 if (names.isEmpty()) {
728 qCritical("No names in covariance matrix. Cannot do picking.");
729 return nullptr;
730 }
731 Eigen::VectorXi pickVec = Eigen::VectorXi::Constant(new_ncov, -1);
732 for (j = 0; j < new_ncov; j++)
733 for (k = 0; k < ncov; k++)
734 if (QString::compare(names[k],new_names[j]) == 0) {
735 pickVec[j] = k;
736 break;
737 }
738 for (j = 0; j < new_ncov; j++) {
739 if (pickVec[j] < 0) {
740 qWarning("All desired channels not found in the covariance matrix (at least missing %s).", new_names[j].toUtf8().constData());
741 return nullptr;
742 }
743 }
744 Eigen::VectorXi isMegVec;
745 if (omit_meg_eeg) {
746 isMegVec.resize(new_ncov);
747 if (!chs.isEmpty()) {
748 for (j = 0; j < new_ncov; j++)
749 if (chs[j].kind == FIFFV_MEG_CH)
750 isMegVec[j] = true;
751 else
752 isMegVec[j] = false;
753 }
754 else {
755 for (j = 0; j < new_ncov; j++)
756 if (new_names[j].startsWith("MEG"))
757 isMegVec[j] = true;
758 else
759 isMegVec[j] = false;
760 }
761 }
762 if (cov_diag.size() > 0) {
763 cov_diag_local.resize(new_ncov);
764 for (j = 0; j < new_ncov; j++) {
765 cov_diag_local[j] = cov_diag[pickVec[j]];
766 picked_names.append(names[pickVec[j]]);
767 }
768 }
769 else {
770 cov_local.resize(new_ncov*(new_ncov+1)/2);
771 for (j = 0; j < new_ncov; j++) {
772 picked_names.append(names[pickVec[j]]);
773 for (k = 0; k <= j; k++) {
774 from = lt_packed_index(pickVec[j],pickVec[k]);
775 to = lt_packed_index(j,k);
776 if (to < 0 || to > new_ncov*(new_ncov+1)/2-1) {
777 qCritical("Wrong destination index in pick_chs_omit : %d %d %d",j,k,to);
778 return nullptr;
779 }
780 if (from < 0 || from > ncov*(ncov+1)/2-1) {
781 qCritical("Wrong source index in pick_chs_omit : %d %d %d",pickVec[j],pickVec[k],from);
782 return nullptr;
783 }
784 cov_local[to] = cov[from];
785 if (omit_meg_eeg)
786 if (isMegVec[j] != isMegVec[k])
787 cov_local[to] = 0.0;
788 }
789 }
790 }
791
792 res = MNECovMatrix::create(kind,new_ncov,picked_names,cov_local,cov_diag_local);
793
794 res->bads = bads;
795 res->nbad = nbad;
796 res->proj = proj ? proj->dup() : nullptr;
797 res->sss = sss ? std::make_unique<MNESssData>(*sss) : nullptr;
798
799 if (ch_class.size() > 0) {
800 res->ch_class.resize(res->ncov);
801 for (k = 0; k < res->ncov; k++)
802 res->ch_class[k] = ch_class[pickVec[k]];
803 }
804 return res;
805}
constexpr int FAIL
constexpr int OK
int mne_decompose_eigen(const VectorXd &mat, VectorXd &lambda, Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > &vectors, int dim)
Legacy MNE-C noise covariance container preserved for cross-toolchain compatibility.
#define MNE_COV_CH_MEG_MAG
#define MNE_COV_CH_EEG
#define MNE_COV_CH_MEG_GRAD
#define MNE_COV_CH_UNKNOWN
Composite SSP projection operator P = I - U U^T assembled from a list of MNELIB::MNEProjItem.
Signal Space Separation (Maxwell filter) basis metadata stored alongside MEG raw data.
Single SSP projection vector with kind/active flag and channel labels.
FIFF sparse matrix: column / row-compressed sparse storage backed by Eigen::SparseMatrix.
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
#define FIFF_MNE_COV_KIND
#define FIFFV_EEG_CH
#define FIFF_MNE_COV
#define FIFF_MNE_COV_DIAG
#define FIFF_MNE_ROW_NAMES
#define FIFF_MNE_COV_EIGENVALUES
#define FIFFV_MEG_CH
#define FIFFB_MNE_COV
#define FIFF_MNE_COV_NFREE
#define FIFF_MNE_COV_EIGENVECTORS
#define FIFF_UNIT_T
#define FIFF_MNE_COV_DIM
FIFF channel descriptor record (FIFF_CH_INFO): per-channel logical/scanner numbers,...
#define FIFFT_DOUBLE
Definition fiff_file.h:226
#define FIFFT_FLOAT
Definition fiff_file.h:225
#define FIFFV_SSS_JOB_NOTHING
Definition fiff_file.h:531
FIFF tag: the 16-byte tag header (kind, type, size, next) plus its decoded payload.
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
Per-channel FIFF descriptor: identifiers, kind, calibration, coil type, channel-frame coil position a...
QSharedPointer< FiffDirNode > SPtr
Sparse FIFF matrix: CCS or RCS storage with the value / index / pointer triple as written by FiffStre...
static FiffSparseMatrix::UPtr fiff_get_float_sparse_matrix(const FIFFLIB::FiffTag::UPtr &tag)
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
QSharedPointer< FiffStream > SPtr
static QStringList split_name_list(QString p_sNameList)
std::unique_ptr< FiffTag > UPtr
Definition fiff_tag.h:164
Eigen::VectorXd inv_lambda
std::unique_ptr< FIFFLIB::FiffSparseMatrix > cov_sparse
Eigen::VectorXd lambda
int condition(float rank_threshold, int use_rank)
static std::unique_ptr< MNECovMatrix > create_sparse(int kind, int ncov, const QStringList &names, FIFFLIB::FiffSparseMatrix *cov_sparse)
void regularize(const Eigen::Vector3f &regs)
int decompose_eigen_small(float p_small, int use_rank)
int classify_channels(const QList< FIFFLIB::FiffChInfo > &chs, int nchan)
std::unique_ptr< MNEProjOp > proj
static std::unique_ptr< MNECovMatrix > create_diag(int kind, int ncov, const QStringList &names, const Eigen::VectorXd &cov_diag)
static std::unique_ptr< MNECovMatrix > create_dense(int kind, int ncov, const QStringList &names, const Eigen::VectorXd &cov)
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 > read(const QString &name, int kind)
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
static int lt_packed_index(int j, int k)
std::unique_ptr< MNECovMatrix > pick_chs_omit(const QStringList &new_names, int new_ncov, int omit_meg_eeg, const QList< FIFFLIB::FiffChInfo > &chs) const
Eigen::VectorXi ch_class
int whiten_vector(Eigen::Ref< Eigen::VectorXf > data, Eigen::Ref< Eigen::VectorXf > whitened_data, int nchan) const
static std::unique_ptr< MNEProjOp > read_from_node(FIFFLIB::FiffStream::SPtr &stream, const FIFFLIB::FiffDirNode::SPtr &start)
Read all linear projection items from a FIFF tree node.
static std::unique_ptr< MNESssData > read_from_node(QSharedPointer< FIFFLIB::FiffStream > &stream, const QSharedPointer< FIFFLIB::FiffDirNode > &start)