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