v2.0.0
Loading...
Searching...
No Matches
mne_proj_op.cpp
Go to the documentation of this file.
1//=============================================================================================================
16
17//=============================================================================================================
18// INCLUDES
19//=============================================================================================================
20
21#include <fiff/fiff_constants.h>
22#include <fiff/fiff_tag.h>
23
24#include "mne_proj_op.h"
25#include "mne_proj_item.h"
26#include "mne_cov_matrix.h"
27#include "mne_named_vector.h"
28#include "mne_named_matrix.h"
29
30#include <QFile>
31#include <QTextStream>
32#include <QDebug>
33
34#include <Eigen/Core>
35#include <Eigen/SVD>
36
37#include <vector>
38
39constexpr int FAIL = -1;
40constexpr int OK = 0;
41
42
43
44//=============================================================================================================
45// USED NAMESPACES
46//=============================================================================================================
47
48using namespace Eigen;
49using namespace FIFFLIB;
50using namespace MNELIB;
51
52//=============================================================================================================
53// DEFINE MEMBER METHODS
54//=============================================================================================================
55
57: nitems (0)
58, nch (0)
59, nvec (0)
60{
61}
62
63//=============================================================================================================
64
68
69//=============================================================================================================
70
72{
73 proj_data.resize(0, 0);
74
75 names.clear();
76 nch = 0;
77 nvec = 0;
78
79 return;
80}
81
82//=============================================================================================================
83
85/*
86 * Copy items from 'from' operator to this operator
87 */
88{
89 if (from) {
90 for (int k = 0; k < from->nitems; k++) {
91 const auto& it = from->items[k];
92 add_item(it.vecs.get(),it.kind,it.desc);
93 items[nitems-1].active_file = it.active_file;
94 }
95 }
96 return this;
97}
98
99//=============================================================================================================
100
101void MNEProjOp::add_item_active(const MNENamedMatrix *vecs, int kind, const QString& desc, bool is_active)
102/*
103 * Add a new item to an existing projection operator
104 */
105{
106 items.append(MNEProjItem());
107 auto& new_item = items.back();
108
109 new_item.active = is_active;
110 new_item.vecs = std::make_unique<MNENamedMatrix>(*vecs);
111
112 if (kind == FIFFV_MNE_PROJ_ITEM_EEG_AVREF) {
113 new_item.has_meg = false;
114 new_item.has_eeg = true;
115 }
116 else {
117 for (int k = 0; k < vecs->ncol; k++) {
118 if (vecs->collist[k].contains("EEG"))//strstr(vecs->collist[k],"EEG") == vecs->collist[k])
119 new_item.has_eeg = true;
120 if (vecs->collist[k].contains("MEG"))//strstr(vecs->collist[k],"MEG") == vecs->collist[k])
121 new_item.has_meg = true;
122 }
123 if (!new_item.has_meg && !new_item.has_eeg) {
124 new_item.has_meg = true;
125 new_item.has_eeg = false;
126 }
127 else if (new_item.has_meg && new_item.has_eeg) {
128 new_item.has_meg = true;
129 new_item.has_eeg = false;
130 }
131 }
132 if (!desc.isEmpty())
133 new_item.desc = desc;
134 new_item.kind = kind;
135 new_item.nvec = new_item.vecs->nrow;
136
137 nitems++;
138
139 free_proj(); /* These data are not valid any more */
140 return;
141}
142
143//=============================================================================================================
144
145void MNEProjOp::add_item(const MNENamedMatrix *vecs, int kind, const QString& desc)
146{
147 add_item_active(vecs, kind, desc, true);
148}
149
150//=============================================================================================================
151
152std::unique_ptr<MNEProjOp> MNEProjOp::dup() const
153/*
154 * Provide a duplicate (item data only)
155 */
156{
157 auto res = std::make_unique<MNEProjOp>();
158
159 for (int k = 0; k < nitems; k++) {
160 const auto& it = items[k];
161 res->add_item_active(it.vecs.get(),it.kind,it.desc,it.active);
162 res->items[k].active_file = it.active_file;
163 }
164 return res;
165}
166
167//=============================================================================================================
168
169std::unique_ptr<MNEProjOp> MNEProjOp::create_average_eeg_ref(const QList<FiffChInfo>& chs, int nch)
170/*
171 * Make the projection operator for average electrode reference
172 */
173{
174 int eegcount = 0;
175 int k;
176 QStringList names;
177
178 for (k = 0; k < nch; k++)
179 if (chs.at(k).kind == FIFFV_EEG_CH)
180 eegcount++;
181 if (eegcount == 0) {
182 qCritical("No EEG channels specified for average reference.");
183 return nullptr;
184 }
185
186 for (k = 0; k < nch; k++)
187 if (chs.at(k).kind == FIFFV_EEG_CH)
188 names.append(chs.at(k).ch_name);
189
190 Eigen::MatrixXf vec_data = Eigen::MatrixXf::Constant(1, eegcount, 1.0f/sqrt(static_cast<double>(eegcount)));
191
192 QStringList emptyList;
193 auto vecs = MNENamedMatrix::build(1,eegcount,emptyList,names,vec_data);
194
195 auto op = std::make_unique<MNEProjOp>();
196 op->add_item(vecs.get(),FIFFV_MNE_PROJ_ITEM_EEG_AVREF,"Average EEG reference");
197
198 return op;
199}
200
201//=============================================================================================================
202
203int MNEProjOp::affect(const QStringList& list, int nlist)
204{
205 int k;
206 int naff;
207
208 for (k = 0, naff = 0; k < nitems; k++)
209 if (items[k].active && items[k].affect(list,nlist))
210 naff += items[k].nvec;
211
212 return naff;
213}
214
215//=============================================================================================================
216
217int MNEProjOp::affect_chs(const QList<FiffChInfo>& chs, int nch)
218{
219 if (nch == 0)
220 return false;
221 QStringList list;
222 list.reserve(nch);
223 for (int k = 0; k < nch; k++)
224 list.append(chs.at(k).ch_name);
225 return affect(list, nch);
226}
227
228//=============================================================================================================
229
230int MNEProjOp::project_vector(Eigen::Ref<Eigen::VectorXf> vec, bool do_complement)
231/*
232 * Apply projection operator to a vector (floats)
233 * Assume that all dimension checking etc. has been done before
234 */
235{
237 return OK;
238
239 if (nch != static_cast<int>(vec.size())) {
240 qCritical("Data vector size does not match projection operator");
241 return FAIL;
242 }
243
244 Eigen::VectorXf proj = Eigen::VectorXf::Zero(nch);
245
246 for (int p = 0; p < this->nvec; p++) {
247 auto row = proj_data.row(p);
248 float w = row.dot(vec);
249 proj += w * row.transpose();
250 }
251
252 if (do_complement)
253 vec -= proj;
254 else
255 vec = proj;
256
257 return OK;
258}
259
260//=============================================================================================================
261
262std::unique_ptr<MNEProjOp> MNEProjOp::read_from_node(FiffStream::SPtr &stream, const FiffDirNode::SPtr &start)
263/*
264 * Load all the linear projection data
265 */
266{
267 QList<FiffDirNode::SPtr> proj;
268 FiffDirNode::SPtr start_node;
269 QList<FiffDirNode::SPtr> items;
271 int k;
272 QString item_desc,desc_tag;
273 int global_nchan,item_nchan;
274 QStringList item_names;
275 int item_kind;
276 int item_nvec;
277 int item_active;
278 FiffTag::UPtr t_pTag;
279
280 if (!stream) {
281 qCritical("File not open read_from_node");
282 return nullptr;
283 }
284
285 if (!start || start->isEmpty())
286 start_node = stream->dirtree();
287 else
288 start_node = start;
289
290 auto op = std::make_unique<MNEProjOp>();
291 proj = start_node->dir_tree_find(FIFFB_PROJ);
292 if (proj.size() == 0 || proj[0]->isEmpty()) /* The caller must recognize an empty projection */
293 return op;
294 /*
295 * Only the first projection block is recognized
296 */
297 items = proj[0]->dir_tree_find(FIFFB_PROJ_ITEM);
298 if (items.size() == 0 || items[0]->isEmpty()) /* The caller must recognize an empty projection */
299 return op;
300 /*
301 * Get a common number of channels
302 */
303 node = proj[0];
304 if(!node->find_tag(stream, FIFF_NCHAN, t_pTag))
305 global_nchan = 0;
306 else {
307 global_nchan = *t_pTag->toInt();
308 // TAG_FREE(tag);
309 }
310 /*
311 * Proceess each item
312 */
313 for (k = 0; k < items.size(); k++) {
314 node = items[k];
315 /*
316 * Complicated procedure for getting the description
317 */
318 item_desc.clear();
319
320 if (node->find_tag(stream, FIFF_NAME, t_pTag)) {
321 item_desc += t_pTag->toString();
322 }
323
324 /*
325 * Take the first line of description if it exists
326 */
327 if (node->find_tag(stream, FIFF_DESCRIPTION, t_pTag)) {
328 desc_tag = t_pTag->toString();
329 int pos;
330 if((pos = desc_tag.indexOf("\n")) >= 0)
331 desc_tag.truncate(pos);
332 if (!item_desc.isEmpty())
333 item_desc += " ";
334 item_desc += desc_tag;
335 }
336 /*
337 * Possibility to override number of channels here
338 */
339 if (!node->find_tag(stream, FIFF_NCHAN, t_pTag)) {
340 item_nchan = global_nchan;
341 }
342 else {
343 item_nchan = *t_pTag->toInt();
344 }
345 if (item_nchan <= 0) {
346 qCritical("Number of channels incorrectly specified for one of the projection items.");
347 return nullptr;
348 }
349 /*
350 * Take care of the channel names
351 */
352 if (!node->find_tag(stream, FIFF_PROJ_ITEM_CH_NAME_LIST, t_pTag)) {
353 return nullptr;
354 }
355
356 item_names = FiffStream::split_name_list(t_pTag->toString());
357
358 if (item_names.size() != item_nchan) {
359 qCritical("Channel name list incorrectly specified for proj item # %d",k+1);
360 item_names.clear();
361 return nullptr;
362 }
363 /*
364 * Kind of item
365 */
366 if (!node->find_tag(stream, FIFF_PROJ_ITEM_KIND, t_pTag)) {
367 return nullptr;
368 }
369 item_kind = *t_pTag->toInt();
370 /*
371 * How many vectors
372 */
373 if (!node->find_tag(stream,FIFF_PROJ_ITEM_NVEC, t_pTag)) {
374 return nullptr;
375 }
376 item_nvec = *t_pTag->toInt();
377 /*
378 * The projection data
379 */
380 if (!node->find_tag(stream,FIFF_PROJ_ITEM_VECTORS, t_pTag)) {
381 return nullptr;
382 }
383
384 MatrixXf item_vectors = t_pTag->toFloatMatrix().transpose();
385
386 /*
387 * Is this item active?
388 */
389 if (node->find_tag(stream, FIFF_MNE_PROJ_ITEM_ACTIVE, t_pTag)) {
390 item_active = *t_pTag->toInt();
391 }
392 else
393 item_active = false;
394 /*
395 * Ready to add
396 */
397 QStringList emptyList;
398 auto item = MNENamedMatrix::build(item_nvec,item_nchan,emptyList,item_names,item_vectors);
399 op->add_item_active(item.get(),item_kind,item_desc,item_active);
400 op->items[op->nitems-1].active_file = item_active;
401 }
402
403 return op;
404}
405
406//=============================================================================================================
407
408std::unique_ptr<MNEProjOp> MNEProjOp::read(const QString &name)
409{
410 QFile file(name);
411 FiffStream::SPtr stream(new FiffStream(&file));
412
413 if(!stream->open())
414 return nullptr;
415
416 FiffDirNode::SPtr t_default;
417 auto res = read_from_node(stream,t_default);
418
419 stream->close();
420
421 return res;
422}
423
424//=============================================================================================================
425
426void MNEProjOp::report_data(QTextStream &out, const QString &tag, bool list_data, const QStringList &exclude)
427/*
428 * Output info about the projection operator
429 */
430{
431 int j,p,q;
432 MNENamedMatrix* vecs;
433 bool found;
434
435 if (nitems <= 0) {
436 out << "Empty operator\n";
437 return;
438 }
439
440 for (int k = 0; k < nitems; k++) {
441 const auto& it = items[k];
442 if (list_data && !tag.isEmpty())
443 out << tag << "\n";
444 if (!tag.isEmpty())
445 out << tag;
446 out << "# " << (k+1) << " : " << it.desc << " : " << it.nvec << " vecs : " << it.vecs->ncol << " chs "
447 << (it.has_meg ? "MEG" : "EEG") << " "
448 << (it.active ? "active" : "idle") << "\n";
449 if (list_data && !tag.isEmpty())
450 out << tag << "\n";
451 if (list_data) {
452 vecs = items[k].vecs.get();
453
454 for (q = 0; q < vecs->ncol; q++) {
455 out << qSetFieldWidth(10) << Qt::left << vecs->collist[q] << qSetFieldWidth(0);
456 out << (q < vecs->ncol-1 ? " " : "\n");
457 }
458 for (p = 0; p < vecs->nrow; p++)
459 for (q = 0; q < vecs->ncol; q++) {
460 found = exclude.contains(vecs->collist[q]);
461 out << qSetFieldWidth(10) << qSetRealNumberPrecision(5) << Qt::forcepoint
462 << (found ? 0.0 : vecs->data(p, q)) << qSetFieldWidth(0) << " ";
463 out << (q < vecs->ncol-1 ? " " : "\n");
464 }
465 if (list_data && !tag.isEmpty())
466 out << tag << "\n";
467 }
468 }
469 return;
470}
471
472//=============================================================================================================
473
474void MNEProjOp::report(QTextStream &out, const QString &tag)
475{
476 report_data(out, tag, false, QStringList());
477}
478
479//=============================================================================================================
480
481int MNEProjOp::assign_channels(const QStringList& list, int nlist)
482{
483 free_proj(); /* Compiled data is no longer valid */
484
485 if (nlist == 0)
486 return OK;
487
488 names = list;
489 nch = nlist;
490
491 return OK;
492}
493
494//=============================================================================================================
495
496namespace {
497
498void clear_channel_group(Eigen::Ref<Eigen::VectorXf> data, const QStringList& ch_names, int nnames, const QString& prefix)
499{
500 for (int k = 0; k < nnames; k++)
501 if (ch_names[k].contains(prefix))
502 data[k] = 0.0;
503}
504
505constexpr float USE_LIMIT = 1e-5f;
506constexpr float SMALL_VALUE = 1e-4f;
507
508} // anonymous namespace
509
510int MNEProjOp::make_proj_bad(const QStringList& bad)
511{
512 using RowMatrixXf = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
513
514 int k,p,q,r,nvec_total;
515 RowMatrixXf vv_meg_mat;
516 Eigen::VectorXf sing_meg_vec;
517 RowMatrixXf vv_eeg_mat;
518 Eigen::VectorXf sing_eeg_vec;
519 int nvec_meg;
520 int nvec_eeg;
521 MNENamedVector vec;
522 float size;
523 int nzero;
524
525 proj_data.resize(0, 0);
526 nvec = 0;
527
528 if (nch <= 0)
529 return OK;
530 if (nitems <= 0)
531 return OK;
532
533 nvec_total = affect(names,nch);
534 if (nvec_total == 0)
535 return OK;
536
537 RowMatrixXf mat_meg_mat = RowMatrixXf::Zero(nvec_total, nch);
538 RowMatrixXf mat_eeg_mat = RowMatrixXf::Zero(nvec_total, nch);
539
540 for (k = 0, nvec_meg = nvec_eeg = 0; k < nitems; k++) {
541 if (items[k].active && items[k].affect(names,nch)) {
542 vec.nvec = items[k].vecs->ncol;
543 vec.names = items[k].vecs->collist;
544 if (items[k].has_meg) {
545 for (p = 0; p < items[k].nvec; p++, nvec_meg++) {
546 vec.data = items[k].vecs->data.row(p);
547 Eigen::Map<Eigen::VectorXf> res_meg(mat_meg_mat.row(nvec_meg).data(), nch);
548 if (vec.pick(names,nch,false,res_meg) == FAIL)
549 return FAIL;
550 }
551 }
552 else if (items[k].has_eeg) {
553 for (p = 0; p < items[k].nvec; p++, nvec_eeg++) {
554 vec.data = items[k].vecs->data.row(p);
555 Eigen::Map<Eigen::VectorXf> res_eeg(mat_eeg_mat.row(nvec_eeg).data(), nch);
556 if (vec.pick(names,nch,false,res_eeg) == FAIL)
557 return FAIL;
558 }
559 }
560 }
561 }
562 /*
563 * Replace bad channel entries with zeroes
564 */
565 for (q = 0; q < bad.size(); q++)
566 for (r = 0; r < nch; r++)
567 if (names[r] == bad[q]) {
568 for (p = 0; p < nvec_meg; p++)
569 mat_meg_mat(p,r) = 0.0;
570 for (p = 0; p < nvec_eeg; p++)
571 mat_eeg_mat(p,r) = 0.0;
572 }
573 /*
574 * Scale the rows so that detection of linear dependence becomes easy
575 */
576 for (p = 0, nzero = 0; p < nvec_meg; p++) {
577 size = mat_meg_mat.row(p).norm();
578 if (size > 0) {
579 mat_meg_mat.row(p) /= size;
580 }
581 else
582 nzero++;
583 }
584 if (nzero == nvec_meg) {
585 mat_meg_mat.resize(0, 0); nvec_meg = 0;
586 }
587 for (p = 0, nzero = 0; p < nvec_eeg; p++) {
588 size = mat_eeg_mat.row(p).norm();
589 if (size > 0) {
590 mat_eeg_mat.row(p) /= size;
591 }
592 else
593 nzero++;
594 }
595 if (nzero == nvec_eeg) {
596 mat_eeg_mat.resize(0, 0); nvec_eeg = 0;
597 }
598 if (nvec_meg + nvec_eeg == 0) {
599 qWarning("No projection remains after excluding bad channels. Omitting projection.");
600 return OK;
601 }
602 /*
603 * Proceed to SVD
604 */
605 if (nvec_meg > 0) {
606 Eigen::JacobiSVD<Eigen::MatrixXf> svd(mat_meg_mat.topRows(nvec_meg), Eigen::ComputeFullV);
607 sing_meg_vec = svd.singularValues();
608 vv_meg_mat = svd.matrixV().transpose().topRows(nvec_meg);
609 }
610 if (nvec_eeg > 0) {
611 Eigen::JacobiSVD<Eigen::MatrixXf> svd(mat_eeg_mat.topRows(nvec_eeg), Eigen::ComputeFullV);
612 sing_eeg_vec = svd.singularValues();
613 vv_eeg_mat = svd.matrixV().transpose().topRows(nvec_eeg);
614 }
615 /*
616 * Check for linearly dependent vectors
617 */
618 for (p = 0, nvec = 0; p < nvec_meg; p++, nvec++)
619 if (sing_meg_vec[p]/sing_meg_vec[0] < USE_LIMIT)
620 break;
621 for (p = 0; p < nvec_eeg; p++, nvec++)
622 if (sing_eeg_vec[p]/sing_eeg_vec[0] < USE_LIMIT)
623 break;
624 proj_data = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>::Zero(nvec,nch);
625 for (p = 0, nvec = 0; p < nvec_meg; p++, nvec++) {
626 if (sing_meg_vec[p]/sing_meg_vec[0] < USE_LIMIT)
627 break;
628 for (k = 0; k < nch; k++) {
629 if (std::fabs(vv_meg_mat(p,k)) < SMALL_VALUE)
630 proj_data(nvec,k) = 0.0;
631 else
632 proj_data(nvec,k) = vv_meg_mat(p,k);
633 clear_channel_group(Eigen::Map<Eigen::VectorXf>(proj_data.row(nvec).data(), nch),names,nch,"EEG");
634 }
635 }
636 for (p = 0; p < nvec_eeg; p++, nvec++) {
637 if (sing_eeg_vec[p]/sing_eeg_vec[0] < USE_LIMIT)
638 break;
639 for (k = 0; k < nch; k++) {
640 if (std::fabs(vv_eeg_mat(p,k)) < SMALL_VALUE)
641 proj_data(nvec,k) = 0.0;
642 else
643 proj_data(nvec,k) = vv_eeg_mat(p,k);
644 clear_channel_group(Eigen::Map<Eigen::VectorXf>(proj_data.row(nvec).data(), nch),names,nch,"MEG");
645 }
646 }
647 /*
648 * Make sure that the stimulus channels are not modified
649 */
650 for (k = 0; k < nch; k++)
651 if (names[k].contains("STI")) {
652 for (p = 0; p < nvec; p++)
653 proj_data(p,k) = 0.0;
654 }
655
656 return OK;
657}
658
659//=============================================================================================================
660
662{
663 return make_proj_bad(QStringList());
664}
665
666//=============================================================================================================
667
668int MNEProjOp::project_dvector(Eigen::Ref<Eigen::VectorXd> vec, bool do_complement)
669{
670 if (nvec <= 0)
671 return OK;
672
673 if (nch != static_cast<int>(vec.size())) {
674 qCritical("Data vector size does not match projection operator");
675 return FAIL;
676 }
677
678 Eigen::VectorXd proj = Eigen::VectorXd::Zero(vec.size());
679
680 for (int p = 0; p < nvec; p++) {
681 double w = vec.dot(proj_data.row(p).cast<double>());
682 proj += w * proj_data.row(p).cast<double>().transpose();
683 }
684
685 if (do_complement)
686 vec -= proj;
687 else
688 vec = proj;
689
690 return OK;
691}
692
693//=============================================================================================================
694
695bool MNEProjOp::makeProjection(const QList<QString>& projnames,
696 const QList<FiffChInfo>& chs,
697 int nch,
698 std::unique_ptr<MNEProjOp>& result)
699{
700 result.reset();
701 int neeg = 0;
702
703 for (int k = 0; k < nch; k++)
704 if (chs[k].kind == FIFFV_EEG_CH)
705 neeg++;
706
707 if (projnames.size() == 0 && neeg == 0)
708 return true;
709
710 std::unique_ptr<MNEProjOp> all;
711
712 for (int k = 0; k < projnames.size(); k++) {
713 std::unique_ptr<MNEProjOp> one(MNEProjOp::read(projnames[k]));
714 if (!one) {
715 qCritical("Failed to read projection from %s.", projnames[k].toUtf8().data());
716 return false;
717 }
718 if (one->nitems == 0) {
719 qInfo("No linear projection information in %s.", projnames[k].toUtf8().data());
720 }
721 else {
722 qInfo("Loaded projection from %s:", projnames[k].toUtf8().data());
723 { QTextStream errStream(stderr); one->report(errStream, QStringLiteral("\t")); }
724 if (!all)
725 all = std::make_unique<MNEProjOp>();
726 all->combine(one.get());
727 }
728 }
729
730 if (neeg > 0) {
731 bool found = false;
732 if (all) {
733 for (int k = 0; k < all->nitems; k++)
734 if (all->items[k].kind == FIFFV_MNE_PROJ_ITEM_EEG_AVREF) {
735 found = true;
736 break;
737 }
738 }
739 if (!found) {
740 std::unique_ptr<MNEProjOp> one(MNEProjOp::create_average_eeg_ref(chs, nch));
741 if (one) {
742 qInfo("Average EEG reference projection added:");
743 { QTextStream errStream(stderr); one->report(errStream, QStringLiteral("\t")); }
744 if (!all)
745 all = std::make_unique<MNEProjOp>();
746 all->combine(one.get());
747 }
748 }
749 }
750 if (all && all->affect_chs(chs, nch) == 0) {
751 qInfo("Projection will not have any effect on selected channels. Projection omitted.");
752 all.reset();
753 }
754 result = std::move(all);
755 return true;
756}
757
758//=============================================================================================================
759
761{
762 using RowMatrixXd = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
763
764 int j,k,p;
765 bool do_complement = true;
766
767 if (nitems == 0)
768 return OK;
769
770 if (nch != c->ncov || names != c->names) {
771 qCritical("Incompatible data in apply_cov");
772 return FAIL;
773 }
774
775 RowMatrixXd dcovMat = RowMatrixXd::Zero(c->ncov,c->ncov);
776
777 if (c->cov_diag.size() > 0) {
778 for (j = 0, p = 0; j < c->ncov; j++)
779 for (k = 0; k < c->ncov; k++)
780 dcovMat(j,k) = (j == k) ? c->cov_diag[j] : 0;
781 }
782 else {
783 for (j = 0, p = 0; j < c->ncov; j++)
784 for (k = 0; k <= j; k++)
785 dcovMat(j,k) = c->cov[p++];
786 for (j = 0; j < c->ncov; j++)
787 for (k = j+1; k < c->ncov; k++)
788 dcovMat(j,k) = dcovMat(k,j);
789 }
790
791 for (k = 0; k < c->ncov; k++) {
792 Eigen::Map<Eigen::VectorXd> row_k(dcovMat.row(k).data(), c->ncov);
793 if (project_dvector(row_k,do_complement) != OK)
794 return FAIL;
795 }
796
797 dcovMat.transposeInPlace();
798
799 for (k = 0; k < c->ncov; k++) {
800 Eigen::Map<Eigen::VectorXd> row_k(dcovMat.row(k).data(), c->ncov);
801 if (project_dvector(row_k,do_complement) != OK)
802 return FAIL;
803 }
804
805 if (c->cov_diag.size() > 0) {
806 for (j = 0; j < c->ncov; j++) {
807 c->cov_diag[j] = dcovMat(j,j);
808 }
809 c->cov.resize(0);
810 }
811 else {
812 for (j = 0, p = 0; j < c->ncov; j++)
813 for (k = 0; k <= j; k++)
814 c->cov[p++] = dcovMat(j,k);
815 }
816
817 c->nproj = affect(c->names,c->ncov);
818 return OK;
819}
constexpr int FAIL
constexpr int OK
Legacy MNE-C noise covariance container preserved for cross-toolchain compatibility.
Named one-dimensional counterpart of MNELIB::MNENamedMatrix.
Composite SSP projection operator P = I - U U^T assembled from a list of MNELIB::MNEProjItem.
Single SSP projection vector with kind/active flag and channel labels.
Row/column-labelled dense matrix used wherever FIFF stores per-channel data.
Eigen::JacobiSVD< Eigen::Matrix3f > svd(S, Eigen::ComputeFullU|Eigen::ComputeFullV)
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
#define FIFFV_EEG_CH
#define FIFF_MNE_PROJ_ITEM_ACTIVE
#define FIFFV_MNE_PROJ_ITEM_EEG_AVREF
#define FIFFB_PROJ
Definition fiff_file.h:402
#define FIFF_NCHAN
Definition fiff_file.h:446
#define FIFF_NAME
Definition fiff_file.h:478
#define FIFF_DESCRIPTION
Definition fiff_file.h:479
#define FIFFB_PROJ_ITEM
Definition fiff_file.h:403
#define FIFF_PROJ_ITEM_VECTORS
Definition fiff_file.h:798
#define FIFF_PROJ_ITEM_KIND
Definition fiff_file.h:793
#define FIFF_PROJ_ITEM_CH_NAME_LIST
Definition fiff_file.h:802
#define FIFF_PROJ_ITEM_NVEC
Definition fiff_file.h:797
FIFF tag: the 16-byte tag header (kind, type, size, next) plus its decoded payload.
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
QSharedPointer< FiffDirNode > SPtr
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
Covariance matrix storage.
Eigen::VectorXd cov
Eigen::VectorXd cov_diag
A dense matrix with named rows and columns.
static std::unique_ptr< MNENamedMatrix > build(int nrow, int ncol, const QStringList &rowlist, const QStringList &collist, const Eigen::MatrixXf &data)
Factory: build a named matrix from its constituent parts.
int pick(const QStringList &names, int nnames, bool require_all, Eigen::Ref< Eigen::VectorXf > res) const
A single SSP (Signal-Space Projection) item.
Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > proj_data
QStringList names
void report_data(QTextStream &out, const QString &tag, bool list_data, const QStringList &exclude)
void free_proj()
Release the compiled projector data.
static std::unique_ptr< MNEProjOp > read(const QString &name)
void add_item_active(const MNENamedMatrix *vecs, int kind, const QString &desc, bool is_active)
Add a projection item with an explicit active/inactive state.
static bool makeProjection(const QList< QString > &projnames, const QList< FIFFLIB::FiffChInfo > &chs, int nch, std::unique_ptr< MNEProjOp > &result)
Load and combine SSP projection operators from files for the selected channels.
int apply_cov(MNECovMatrix *c)
~MNEProjOp()
Destructor.
int affect(const QStringList &list, int nlist)
MNEProjOp()
Default constructor.
int make_proj_bad(const QStringList &bad)
MNEProjOp * combine(MNEProjOp *from)
Append all projection items from another operator.
int project_vector(Eigen::Ref< Eigen::VectorXf > vec, bool do_complement)
int project_dvector(Eigen::Ref< Eigen::VectorXd > vec, bool do_complement)
void add_item(const MNENamedMatrix *vecs, int kind, const QString &desc)
Add a projection item that is active by default.
int affect_chs(const QList< FIFFLIB::FiffChInfo > &chs, int nch)
std::unique_ptr< MNEProjOp > dup() const
Create a deep copy of this projection operator.
int assign_channels(const QStringList &list, int nlist)
static std::unique_ptr< MNEProjOp > create_average_eeg_ref(const QList< FIFFLIB::FiffChInfo > &chs, int nch)
Create an average EEG reference projector.
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.
QList< MNELIB::MNEProjItem > items
void report(QTextStream &out, const QString &tag)