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