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