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
53#include <Eigen/Core>
54#include <Eigen/SVD>
55
56#include <vector>
57
58#ifndef TRUE
59#define TRUE 1
60#endif
61
62#ifndef FALSE
63#define FALSE 0
64#endif
65
66#ifndef FAIL
67#define FAIL -1
68#endif
69
70#ifndef OK
71#define OK 0
72#endif
73
74#define MALLOC_23(x,t) (t *)malloc((x)*sizeof(t))
75
76#define REALLOC_23(x,y,t) (t *)((x == NULL) ? malloc((y)*sizeof(t)) : realloc((x),(y)*sizeof(t)))
77
78#define FREE_23(x) if ((char *)(x) != NULL) free((char *)(x))
79
80#define FREE_CMATRIX_23(m) mne_free_cmatrix_23((m))
81
82void mne_free_cmatrix_23 (float **m)
83{
84 if (m) {
85 FREE_23(*m);
86 FREE_23(m);
87 }
88}
89
90#define ALLOC_CMATRIX_23(x,y) mne_cmatrix_23((x),(y))
91
92static void matrix_error_23(int kind, int nr, int nc)
93
94{
95 if (kind == 1)
96 printf("Failed to allocate memory pointers for a %d x %d matrix\n",nr,nc);
97 else if (kind == 2)
98 printf("Failed to allocate memory for a %d x %d matrix\n",nr,nc);
99 else
100 printf("Allocation error for a %d x %d matrix\n",nr,nc);
101 if (sizeof(void *) == 4) {
102 printf("This is probably because you seem to be using a computer with 32-bit architecture.\n");
103 printf("Please consider moving to a 64-bit platform.");
104 }
105 printf("Cannot continue. Sorry.\n");
106 exit(1);
107}
108
109float **mne_cmatrix_23(int nr,int nc)
110
111{
112 int i;
113 float **m;
114 float *whole;
115
116 m = MALLOC_23(nr,float *);
117 if (!m) matrix_error_23(1,nr,nc);
118 whole = MALLOC_23(nr*nc,float);
119 if (!whole) matrix_error_23(2,nr,nc);
120
121 for(i=0;i<nr;i++)
122 m[i] = whole + i*nc;
123 return m;
124}
125
126float mne_dot_vectors_23 (float *v1,
127 float *v2,
128 int nn)
129
130{
131#ifdef BLAS
132 int one = 1;
133 float res = sdot(&nn,v1,&one,v2,&one);
134 return res;
135#else
136 float res = 0.0;
137 int k;
138
139 for (k = 0; k < nn; k++)
140 res = res + v1[k]*v2[k];
141 return res;
142#endif
143}
144
145//============================= mne_named_matrix.c =============================
146
147void mne_string_to_name_list_23(const QString& s, QStringList& listp,int &nlistp)
148/*
149 * Convert a colon-separated list into a string array
150 */
151{
152 QStringList list;
153
154 if (!s.isEmpty() && s.size() > 0) {
156 //list = s.split(":");
157 }
158 listp = list;
159 nlistp = list.size();
160 return;
161}
162
163void fromFloatEigenMatrix_23(const Eigen::MatrixXf& from_mat, float **& to_mat, const int m, const int n)
164{
165 for ( int i = 0; i < m; ++i)
166 for ( int j = 0; j < n; ++j)
167 to_mat[i][j] = from_mat(i,j);
168}
169
170void fromFloatEigenMatrix_23(const Eigen::MatrixXf& from_mat, float **& to_mat)
171{
172 fromFloatEigenMatrix_23(from_mat, to_mat, from_mat.rows(), from_mat.cols());
173}
174
175QString mne_name_list_to_string_23(const QStringList& list)
176/*
177 * Convert a string array to a colon-separated string
178 */
179{
180 int nlist = list.size();
181 QString res;
182 if (nlist == 0 || list.isEmpty())
183 return res;
184// res[0] = '\0';
185 for (int k = 0; k < nlist-1; k++) {
186 res += list[k];
187 res += ":";
188 }
189 res += list[nlist-1];
190 return res;
191}
192
193QString mne_channel_names_to_string_23(const QList<FIFFLIB::FiffChInfo>& chs, int nch)
194/*
195 * Make a colon-separated string out of channel names
196 */
197{
198 QStringList names;
199 QString res;
200 if (nch <= 0)
201 return res;
202 for (int k = 0; k < nch; k++)
203 names.append(chs.at(k).ch_name);
204 res = mne_name_list_to_string_23(names);
205 return res;
206}
207
208//=============================================================================================================
209// USED NAMESPACES
210//=============================================================================================================
211
212using namespace Eigen;
213using namespace FIFFLIB;
214using namespace MNELIB;
215
216//=============================================================================================================
217// DEFINE MEMBER METHODS
218//=============================================================================================================
219
221: nitems (0)
222, nch (0)
223, nvec (0)
224{
225}
226
227//=============================================================================================================
228
232
233//=============================================================================================================
234
236{
237 proj_data.resize(0, 0);
238
239 names.clear();
240 nch = 0;
241 nvec = 0;
242
243 return;
244}
245
246//=============================================================================================================
247
249/*
250 * Copy items from 'from' operator to this operator
251 */
252{
253 if (from) {
254 for (int k = 0; k < from->nitems; k++) {
255 const auto& it = from->items[k];
256 add_item(it.vecs.get(),it.kind,it.desc);
257 items[nitems-1].active_file = it.active_file;
258 }
259 }
260 return this;
261}
262
263//=============================================================================================================
264
265void MNEProjOp::add_item_active(const MNENamedMatrix *vecs, int kind, const QString& desc, int is_active)
266/*
267 * Add a new item to an existing projection operator
268 */
269{
270 items.append(MNEProjItem());
271 auto& new_item = items.back();
272
273 new_item.active = is_active;
274 new_item.vecs = std::make_unique<MNENamedMatrix>(*vecs);
275
276 if (kind == FIFFV_MNE_PROJ_ITEM_EEG_AVREF) {
277 new_item.has_meg = FALSE;
278 new_item.has_eeg = TRUE;
279 }
280 else {
281 for (int k = 0; k < vecs->ncol; k++) {
282 if (vecs->collist[k].contains("EEG"))//strstr(vecs->collist[k],"EEG") == vecs->collist[k])
283 new_item.has_eeg = TRUE;
284 if (vecs->collist[k].contains("MEG"))//strstr(vecs->collist[k],"MEG") == vecs->collist[k])
285 new_item.has_meg = TRUE;
286 }
287 if (!new_item.has_meg && !new_item.has_eeg) {
288 new_item.has_meg = TRUE;
289 new_item.has_eeg = FALSE;
290 }
291 else if (new_item.has_meg && new_item.has_eeg) {
292 new_item.has_meg = TRUE;
293 new_item.has_eeg = FALSE;
294 }
295 }
296 if (!desc.isEmpty())
297 new_item.desc = desc;
298 new_item.kind = kind;
299 new_item.nvec = new_item.vecs->nrow;
300
301 nitems++;
302
303 free_proj(); /* These data are not valid any more */
304 return;
305}
306
307//=============================================================================================================
308
309void MNEProjOp::add_item(const MNENamedMatrix *vecs, int kind, const QString& desc)
310{
311 add_item_active(vecs, kind, desc, TRUE);
312}
313
314//=============================================================================================================
315
317/*
318 * Provide a duplicate (item data only)
319 */
320{
321 MNEProjOp* res = new MNEProjOp();
322
323 for (int k = 0; k < nitems; k++) {
324 const auto& it = items[k];
325 res->add_item_active(it.vecs.get(),it.kind,it.desc,it.active);
326 res->items[k].active_file = it.active_file;
327 }
328 return res;
329}
330
331//=============================================================================================================
332
333MNEProjOp *MNEProjOp::create_average_eeg_ref(const QList<FiffChInfo>& chs, int nch)
334/*
335 * Make the projection operator for average electrode reference
336 */
337{
338 int eegcount = 0;
339 int k;
340 QStringList names;
341 MNEProjOp* op;
342
343 for (k = 0; k < nch; k++)
344 if (chs.at(k).kind == FIFFV_EEG_CH)
345 eegcount++;
346 if (eegcount == 0) {
347 qCritical("No EEG channels specified for average reference.");
348 return NULL;
349 }
350
351 for (k = 0; k < nch; k++)
352 if (chs.at(k).kind == FIFFV_EEG_CH)
353 names.append(chs.at(k).ch_name);
354
355 Eigen::MatrixXf vec_data = Eigen::MatrixXf::Constant(1, eegcount, 1.0f/sqrt((double)eegcount));
356
357 QStringList emptyList;
358 auto vecs = MNENamedMatrix::build(1,eegcount,emptyList,names,vec_data);
359
360 op = new MNEProjOp();
361 op->add_item(vecs.get(),FIFFV_MNE_PROJ_ITEM_EEG_AVREF,"Average EEG reference");
362
363 return op;
364}
365
366//=============================================================================================================
367
368int MNEProjOp::affect(const QStringList& list, int nlist)
369{
370 int k;
371 int naff;
372
373 for (k = 0, naff = 0; k < nitems; k++)
374 if (items[k].active && items[k].affect(list,nlist))
375 naff += items[k].nvec;
376
377 return naff;
378}
379
380//=============================================================================================================
381
382int MNEProjOp::affect_chs(const QList<FiffChInfo>& chs, int nch)
383{
384 QString ch_string;
385 int res;
386 QStringList list;
387 int nlist;
388
389 if (nch == 0)
390 return FALSE;
391 ch_string = mne_channel_names_to_string_23(chs,nch);
392 mne_string_to_name_list_23(ch_string,list,nlist);
393 res = affect(list,nlist);
394 list.clear();
395 return res;
396}
397
398//=============================================================================================================
399
400int MNEProjOp::project_vector(float *vec, int nvec, int do_complement)
401/*
402 * Apply projection operator to a vector (floats)
403 * Assume that all dimension checking etc. has been done before
404 */
405{
406 thread_local std::vector<float> res;
407 float *pvec;
408 float w;
409 int k,p;
410
412 return OK;
413
414 if (nch != nvec) {
415 printf("Data vector size does not match projection operator");
416 return FAIL;
417 }
418
419 if (nch > static_cast<int>(res.size())) {
420 res.resize(nch);
421 }
422
423 for (k = 0; k < nch; k++)
424 res[k] = 0.0;
425
426 for (p = 0; p < this->nvec; p++) {
427 pvec = proj_data.row(p).data();
428 w = mne_dot_vectors_23(pvec,vec,nch);
429 for (k = 0; k < nch; k++)
430 res[k] = res[k] + w*pvec[k];
431 }
432 if (do_complement) {
433 for (k = 0; k < nch; k++)
434 vec[k] = vec[k] - res[k];
435 }
436 else {
437 for (k = 0; k < nch; k++)
438 vec[k] = res[k];
439 }
440 return OK;
441}
442
443//=============================================================================================================
444
446/*
447 * Load all the linear projection data
448 */
449{
450 MNEProjOp* op = NULL;
451 QList<FiffDirNode::SPtr> proj;
452 FiffDirNode::SPtr start_node;
453 QList<FiffDirNode::SPtr> items;
455 int k;
456 QString item_desc,desc_tag;
457 int global_nchan,item_nchan;
458 QStringList item_names;
459 int item_kind;
460 int item_nvec;
461 int item_active;
462 FiffTag::UPtr t_pTag;
463
464 if (!stream) {
465 qCritical("File not open read_from_node");
466 goto bad;
467 }
468
469 if (!start || start->isEmpty())
470 start_node = stream->dirtree();
471 else
472 start_node = start;
473
474 op = new MNEProjOp();
475 proj = start_node->dir_tree_find(FIFFB_PROJ);
476 if (proj.size() == 0 || proj[0]->isEmpty()) /* The caller must recognize an empty projection */
477 goto out;
478 /*
479 * Only the first projection block is recognized
480 */
481 items = proj[0]->dir_tree_find(FIFFB_PROJ_ITEM);
482 if (items.size() == 0 || items[0]->isEmpty()) /* The caller must recognize an empty projection */
483 goto out;
484 /*
485 * Get a common number of channels
486 */
487 node = proj[0];
488 if(!node->find_tag(stream, FIFF_NCHAN, t_pTag))
489 global_nchan = 0;
490 else {
491 global_nchan = *t_pTag->toInt();
492 // TAG_FREE(tag);
493 }
494 /*
495 * Proceess each item
496 */
497 for (k = 0; k < items.size(); k++) {
498 node = items[k];
499 /*
500 * Complicated procedure for getting the description
501 */
502 item_desc.clear();
503
504 if (node->find_tag(stream, FIFF_NAME, t_pTag)) {
505 item_desc += t_pTag->toString();
506 }
507
508 /*
509 * Take the first line of description if it exists
510 */
511 if (node->find_tag(stream, FIFF_DESCRIPTION, t_pTag)) {
512 desc_tag = t_pTag->toString();
513 int pos;
514 if((pos = desc_tag.indexOf("\n")) >= 0)
515 desc_tag.truncate(pos);
516 if (!item_desc.isEmpty())
517 item_desc += " ";
518 item_desc += desc_tag;
519 }
520 /*
521 * Possibility to override number of channels here
522 */
523 if (!node->find_tag(stream, FIFF_NCHAN, t_pTag)) {
524 item_nchan = global_nchan;
525 }
526 else {
527 item_nchan = *t_pTag->toInt();
528 }
529 if (item_nchan <= 0) {
530 qCritical("Number of channels incorrectly specified for one of the projection items.");
531 goto bad;
532 }
533 /*
534 * Take care of the channel names
535 */
536 if (!node->find_tag(stream, FIFF_PROJ_ITEM_CH_NAME_LIST, t_pTag))
537 goto bad;
538
539 item_names = FiffStream::split_name_list(t_pTag->toString());
540
541 if (item_names.size() != item_nchan) {
542 printf("Channel name list incorrectly specified for proj item # %d",k+1);
543 item_names.clear();
544 goto bad;
545 }
546 /*
547 * Kind of item
548 */
549 if (!node->find_tag(stream, FIFF_PROJ_ITEM_KIND, t_pTag))
550 goto bad;
551 item_kind = *t_pTag->toInt();
552 /*
553 * How many vectors
554 */
555 if (!node->find_tag(stream,FIFF_PROJ_ITEM_NVEC, t_pTag))
556 goto bad;
557 item_nvec = *t_pTag->toInt();
558 /*
559 * The projection data
560 */
561 if (!node->find_tag(stream,FIFF_PROJ_ITEM_VECTORS, t_pTag))
562 goto bad;
563
564 MatrixXf item_vectors = t_pTag->toFloatMatrix().transpose();
565
566 /*
567 * Is this item active?
568 */
569 if (node->find_tag(stream, FIFF_MNE_PROJ_ITEM_ACTIVE, t_pTag)) {
570 item_active = *t_pTag->toInt();
571 }
572 else
573 item_active = FALSE;
574 /*
575 * Ready to add
576 */
577 QStringList emptyList;
578 auto item = MNENamedMatrix::build(item_nvec,item_nchan,emptyList,item_names,item_vectors);
579 op->add_item_active(item.get(),item_kind,item_desc,item_active);
580 op->items[op->nitems-1].active_file = item_active;
581 }
582
583out :
584 return op;
585
586bad : {
587 if(op)
588 delete op;
589 return NULL;
590 }
591}
592
593//=============================================================================================================
594
595MNEProjOp *MNEProjOp::read(const QString &name)
596{
597 QFile file(name);
598 FiffStream::SPtr stream(new FiffStream(&file));
599
600 if(!stream->open())
601 return NULL;
602
603 MNEProjOp* res = NULL;
604
605 FiffDirNode::SPtr t_default;
606 res = read_from_node(stream,t_default);
607
608 stream->close();
609
610 return res;
611}
612
613//=============================================================================================================
614
615void MNEProjOp::report_data(QTextStream &out, const char *tag, int list_data, char **exclude, int nexclude)
616/*
617 * Output info about the projection operator
618 */
619{
620 int j,p,q;
621 MNENamedMatrix* vecs;
622 int found;
623
624 if (nitems <= 0) {
625 out << "Empty operator\n";
626 return;
627 }
628
629 for (int k = 0; k < nitems; k++) {
630 const auto& it = items[k];
631 if (list_data && tag)
632 out << tag << "\n";
633 if (tag)
634 out << tag;
635 out << "# " << (k+1) << " : " << it.desc << " : " << it.nvec << " vecs : " << it.vecs->ncol << " chs "
636 << (it.has_meg ? "MEG" : "EEG") << " "
637 << (it.active ? "active" : "idle") << "\n";
638 if (list_data && tag)
639 out << tag << "\n";
640 if (list_data) {
641 vecs = items[k].vecs.get();
642
643 for (q = 0; q < vecs->ncol; q++) {
644 out << qSetFieldWidth(10) << Qt::left << vecs->collist[q] << qSetFieldWidth(0);
645 out << (q < vecs->ncol-1 ? " " : "\n");
646 }
647 for (p = 0; p < vecs->nrow; p++)
648 for (q = 0; q < vecs->ncol; q++) {
649 for (j = 0, found = 0; j < nexclude; j++) {
650 if (QString::compare(exclude[j],vecs->collist[q]) == 0) {
651 found = 1;
652 break;
653 }
654 }
655 out << qSetFieldWidth(10) << qSetRealNumberPrecision(5) << Qt::forcepoint
656 << (found ? 0.0 : vecs->data(p, q)) << qSetFieldWidth(0) << " ";
657 out << (q < vecs->ncol-1 ? " " : "\n");
658 }
659 if (list_data && tag)
660 out << tag << "\n";
661 }
662 }
663 return;
664}
665
666//=============================================================================================================
667
668void MNEProjOp::report(QTextStream &out, const char *tag)
669{
670 report_data(out,tag, FALSE, NULL, 0);
671}
672
673//=============================================================================================================
674
675int MNEProjOp::assign_channels(const QStringList& list, int nlist)
676{
677 free_proj(); /* Compiled data is no longer valid */
678
679 if (nlist == 0)
680 return OK;
681
682 names = list;
683 nch = nlist;
684
685 return OK;
686}
687
688//=============================================================================================================
689
690namespace {
691
692void clear_channel_group(Eigen::Ref<Eigen::VectorXf> data, const QStringList& ch_names, int nnames, const QString& prefix)
693{
694 for (int k = 0; k < nnames; k++)
695 if (ch_names[k].contains(prefix))
696 data[k] = 0.0;
697}
698
699constexpr float USE_LIMIT = 1e-5f;
700constexpr float SMALL_VALUE = 1e-4f;
701
702} // anonymous namespace
703
704int MNEProjOp::make_proj_bad(char **bad, int nbad)
705{
706 using RowMatrixXf = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
707
708 int k,p,q,r,nvec_total;
709 RowMatrixXf vv_meg_mat;
710 Eigen::VectorXf sing_meg_vec;
711 RowMatrixXf vv_eeg_mat;
712 Eigen::VectorXf sing_eeg_vec;
713 int nvec_meg;
714 int nvec_eeg;
715 MNENamedVector vec;
716 float size;
717 int nzero;
718
719 proj_data.resize(0, 0);
720 nvec = 0;
721
722 if (nch <= 0)
723 return OK;
724 if (nitems <= 0)
725 return OK;
726
727 nvec_total = affect(names,nch);
728 if (nvec_total == 0)
729 return OK;
730
731 RowMatrixXf mat_meg_mat = RowMatrixXf::Zero(nvec_total, nch);
732 RowMatrixXf mat_eeg_mat = RowMatrixXf::Zero(nvec_total, nch);
733
734 for (k = 0, nvec_meg = nvec_eeg = 0; k < nitems; k++) {
735 if (items[k].active && items[k].affect(names,nch)) {
736 vec.nvec = items[k].vecs->ncol;
737 vec.names = items[k].vecs->collist;
738 if (items[k].has_meg) {
739 for (p = 0; p < items[k].nvec; p++, nvec_meg++) {
740 vec.data = items[k].vecs->data.row(p);
741 Eigen::Map<Eigen::VectorXf> res_meg(mat_meg_mat.row(nvec_meg).data(), nch);
742 if (vec.pick(names,nch,false,res_meg) == FAIL)
743 return FAIL;
744 }
745 }
746 else if (items[k].has_eeg) {
747 for (p = 0; p < items[k].nvec; p++, nvec_eeg++) {
748 vec.data = items[k].vecs->data.row(p);
749 Eigen::Map<Eigen::VectorXf> res_eeg(mat_eeg_mat.row(nvec_eeg).data(), nch);
750 if (vec.pick(names,nch,false,res_eeg) == FAIL)
751 return FAIL;
752 }
753 }
754 }
755 }
756 /*
757 * Replace bad channel entries with zeroes
758 */
759 for (q = 0; q < nbad; q++)
760 for (r = 0; r < nch; r++)
761 if (QString::compare(names[r],bad[q]) == 0) {
762 for (p = 0; p < nvec_meg; p++)
763 mat_meg_mat(p,r) = 0.0;
764 for (p = 0; p < nvec_eeg; p++)
765 mat_eeg_mat(p,r) = 0.0;
766 }
767 /*
768 * Scale the rows so that detection of linear dependence becomes easy
769 */
770 for (p = 0, nzero = 0; p < nvec_meg; p++) {
771 size = mat_meg_mat.row(p).norm();
772 if (size > 0) {
773 mat_meg_mat.row(p) /= size;
774 }
775 else
776 nzero++;
777 }
778 if (nzero == nvec_meg) {
779 mat_meg_mat.resize(0, 0); nvec_meg = 0;
780 }
781 for (p = 0, nzero = 0; p < nvec_eeg; p++) {
782 size = mat_eeg_mat.row(p).norm();
783 if (size > 0) {
784 mat_eeg_mat.row(p) /= size;
785 }
786 else
787 nzero++;
788 }
789 if (nzero == nvec_eeg) {
790 mat_eeg_mat.resize(0, 0); nvec_eeg = 0;
791 }
792 if (nvec_meg + nvec_eeg == 0) {
793 qWarning("No projection remains after excluding bad channels. Omitting projection.");
794 return OK;
795 }
796 /*
797 * Proceed to SVD
798 */
799 if (nvec_meg > 0) {
800 Eigen::JacobiSVD<Eigen::MatrixXf> svd(mat_meg_mat.topRows(nvec_meg), Eigen::ComputeFullV);
801 sing_meg_vec = svd.singularValues();
802 vv_meg_mat = svd.matrixV().transpose().topRows(nvec_meg);
803 }
804 if (nvec_eeg > 0) {
805 Eigen::JacobiSVD<Eigen::MatrixXf> svd(mat_eeg_mat.topRows(nvec_eeg), Eigen::ComputeFullV);
806 sing_eeg_vec = svd.singularValues();
807 vv_eeg_mat = svd.matrixV().transpose().topRows(nvec_eeg);
808 }
809 /*
810 * Check for linearly dependent vectors
811 */
812 for (p = 0, nvec = 0; p < nvec_meg; p++, nvec++)
813 if (sing_meg_vec[p]/sing_meg_vec[0] < USE_LIMIT)
814 break;
815 for (p = 0; p < nvec_eeg; p++, nvec++)
816 if (sing_eeg_vec[p]/sing_eeg_vec[0] < USE_LIMIT)
817 break;
818 proj_data = RowMajorMatrixXf::Zero(nvec,nch);
819 for (p = 0, nvec = 0; p < nvec_meg; p++, nvec++) {
820 if (sing_meg_vec[p]/sing_meg_vec[0] < USE_LIMIT)
821 break;
822 for (k = 0; k < nch; k++) {
823 if (std::fabs(vv_meg_mat(p,k)) < SMALL_VALUE)
824 proj_data(nvec,k) = 0.0;
825 else
826 proj_data(nvec,k) = vv_meg_mat(p,k);
827 clear_channel_group(Eigen::Map<Eigen::VectorXf>(proj_data.row(nvec).data(), nch),names,nch,"EEG");
828 }
829 }
830 for (p = 0; p < nvec_eeg; p++, nvec++) {
831 if (sing_eeg_vec[p]/sing_eeg_vec[0] < USE_LIMIT)
832 break;
833 for (k = 0; k < nch; k++) {
834 if (std::fabs(vv_eeg_mat(p,k)) < SMALL_VALUE)
835 proj_data(nvec,k) = 0.0;
836 else
837 proj_data(nvec,k) = vv_eeg_mat(p,k);
838 clear_channel_group(Eigen::Map<Eigen::VectorXf>(proj_data.row(nvec).data(), nch),names,nch,"MEG");
839 }
840 }
841 /*
842 * Make sure that the stimulus channels are not modified
843 */
844 for (k = 0; k < nch; k++)
845 if (names[k].contains("STI")) {
846 for (p = 0; p < nvec; p++)
847 proj_data(p,k) = 0.0;
848 }
849
850 return OK;
851}
852
853//=============================================================================================================
854
856{
857 return make_proj_bad(nullptr,0);
858}
859
860//=============================================================================================================
861
862int MNEProjOp::project_dvector(Eigen::Ref<Eigen::VectorXd> vec, int vec_nch, int do_complement)
863{
864 if (nvec <= 0)
865 return OK;
866
867 if (nch != vec_nch) {
868 qCritical("Data vector size does not match projection operator");
869 return FAIL;
870 }
871
872 Eigen::VectorXd proj = Eigen::VectorXd::Zero(vec_nch);
873
874 for (int p = 0; p < nvec; p++) {
875 double w = vec.dot(proj_data.row(p).cast<double>());
876 proj += w * proj_data.row(p).cast<double>().transpose();
877 }
878
879 if (do_complement)
880 vec -= proj;
881 else
882 vec = proj;
883
884 return OK;
885}
886
887//=============================================================================================================
888
889bool MNEProjOp::makeProjection(const QList<QString>& projnames,
890 const QList<FiffChInfo>& chs,
891 int nch,
892 std::unique_ptr<MNEProjOp>& result)
893{
894 result.reset();
895 int neeg = 0;
896
897 for (int k = 0; k < nch; k++)
898 if (chs[k].kind == FIFFV_EEG_CH)
899 neeg++;
900
901 if (projnames.size() == 0 && neeg == 0)
902 return true;
903
904 std::unique_ptr<MNEProjOp> all;
905
906 for (int k = 0; k < projnames.size(); k++) {
907 std::unique_ptr<MNEProjOp> one(MNEProjOp::read(projnames[k]));
908 if (!one) {
909 qCritical("Failed to read projection from %s.", projnames[k].toUtf8().data());
910 return false;
911 }
912 if (one->nitems == 0) {
913 qInfo("No linear projection information in %s.", projnames[k].toUtf8().data());
914 }
915 else {
916 qInfo("Loaded projection from %s:", projnames[k].toUtf8().data());
917 { QTextStream errStream(stderr); one->report(errStream, "\t"); }
918 if (!all)
919 all = std::make_unique<MNEProjOp>();
920 all->combine(one.get());
921 }
922 }
923
924 if (neeg > 0) {
925 bool found = false;
926 if (all) {
927 for (int k = 0; k < all->nitems; k++)
928 if (all->items[k].kind == FIFFV_MNE_PROJ_ITEM_EEG_AVREF) {
929 found = true;
930 break;
931 }
932 }
933 if (!found) {
934 std::unique_ptr<MNEProjOp> one(MNEProjOp::create_average_eeg_ref(chs, nch));
935 if (one) {
936 qInfo("Average EEG reference projection added:");
937 { QTextStream errStream(stderr); one->report(errStream, "\t"); }
938 if (!all)
939 all = std::make_unique<MNEProjOp>();
940 all->combine(one.get());
941 }
942 }
943 }
944 if (all && all->affect_chs(chs, nch) == 0) {
945 qInfo("Projection will not have any effect on selected channels. Projection omitted.");
946 all.reset();
947 }
948 result = std::move(all);
949 return true;
950}
951
952//=============================================================================================================
953
955{
956 using RowMatrixXd = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
957
958 int j,k,p;
959 int do_complement = true;
960
961 if (nitems == 0)
962 return OK;
963
964 if (nch != c->ncov || names != c->names) {
965 qCritical("Incompatible data in apply_cov");
966 return FAIL;
967 }
968
969 RowMatrixXd dcovMat = RowMatrixXd::Zero(c->ncov,c->ncov);
970
971 if (c->cov_diag.size() > 0) {
972 for (j = 0, p = 0; j < c->ncov; j++)
973 for (k = 0; k < c->ncov; k++)
974 dcovMat(j,k) = (j == k) ? c->cov_diag[j] : 0;
975 }
976 else {
977 for (j = 0, p = 0; j < c->ncov; j++)
978 for (k = 0; k <= j; k++)
979 dcovMat(j,k) = c->cov[p++];
980 for (j = 0; j < c->ncov; j++)
981 for (k = j+1; k < c->ncov; k++)
982 dcovMat(j,k) = dcovMat(k,j);
983 }
984
985 for (k = 0; k < c->ncov; k++) {
986 Eigen::Map<Eigen::VectorXd> row_k(dcovMat.row(k).data(), c->ncov);
987 if (project_dvector(row_k,c->ncov,do_complement) != OK)
988 return FAIL;
989 }
990
991 dcovMat.transposeInPlace();
992
993 for (k = 0; k < c->ncov; k++) {
994 Eigen::Map<Eigen::VectorXd> row_k(dcovMat.row(k).data(), c->ncov);
995 if (project_dvector(row_k,c->ncov,do_complement) != OK)
996 return FAIL;
997 }
998
999 if (c->cov_diag.size() > 0) {
1000 for (j = 0; j < c->ncov; j++) {
1001 c->cov_diag[j] = dcovMat(j,j);
1002 }
1003 c->cov.resize(0);
1004 }
1005 else {
1006 for (j = 0, p = 0; j < c->ncov; j++)
1007 for (k = 0; k <= j; k++)
1008 c->cov[p++] = dcovMat(j,k);
1009 }
1010
1011 c->nproj = affect(c->names,c->ncov);
1012 return OK;
1013}
#define TRUE
#define FALSE
#define OK
#define FAIL
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
#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
MNECovMatrix class declaration.
MNEProjOp class declaration.
MNEProjItem class declaration.
void mne_string_to_name_list_23(const QString &s, QStringList &listp, int &nlistp)
#define MALLOC_23(x, t)
void mne_free_cmatrix_23(float **m)
QString mne_name_list_to_string_23(const QStringList &list)
QString mne_channel_names_to_string_23(const QList< FIFFLIB::FiffChInfo > &chs, int nch)
float mne_dot_vectors_23(float *v1, float *v2, int nn)
void fromFloatEigenMatrix_23(const Eigen::MatrixXf &from_mat, float **&to_mat, const int m, const int n)
#define FREE_23(x)
float ** mne_cmatrix_23(int nr, int nc)
MNENamedMatrix 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.
QStringList names
void free_proj()
Release the compiled projector data.
static MNEProjOp * read_from_node(FIFFLIB::FiffStream::SPtr &stream, const FIFFLIB::FiffDirNode::SPtr &start)
Read all linear projection items from a FIFF tree node.
RowMajorMatrixXf proj_data
int project_vector(float *vec, int nvec, int do_complement)
static MNEProjOp * read(const QString &name)
static MNEProjOp * create_average_eeg_ref(const QList< FIFFLIB::FiffChInfo > &chs, int nch)
Create an average EEG reference projector.
void report_data(QTextStream &out, const char *tag, int list_data, char **exclude, int nexclude)
int project_dvector(Eigen::Ref< Eigen::VectorXd > vec, int nch, int do_complement)
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)
void report(QTextStream &out, const char *tag)
MNEProjOp()
Default constructor.
MNEProjOp * combine(MNEProjOp *from)
Append all projection items from another operator.
MNEProjOp * dup() const
Create a deep copy of this projection operator.
int make_proj_bad(char **bad, int nbad)
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)
int assign_channels(const QStringList &list, int nlist)
QList< MNELIB::MNEProjItem > items
void add_item_active(const MNENamedMatrix *vecs, int kind, const QString &desc, int is_active)
Add a projection item with an explicit active/inactive state.