v2.0.0
Loading...
Searching...
No Matches
inv_minimum_norm.cpp
Go to the documentation of this file.
1//=============================================================================================================
19
20//=============================================================================================================
21// INCLUDES
22//=============================================================================================================
23
24#include "inv_minimum_norm.h"
25
27#include <fiff/fiff_evoked.h>
28#include <fiff/fiff_proj.h>
29#include <math/linalg.h>
30
31#include <iostream>
32#include <cmath>
33#include <algorithm>
34
35//=============================================================================================================
36// EIGEN INCLUDES
37//=============================================================================================================
38
39#include <Eigen/Core>
40#include <Eigen/Dense>
41#include <Eigen/Eigenvalues>
42#include <Eigen/SVD>
43
44//=============================================================================================================
45// USED NAMESPACES
46//=============================================================================================================
47
48using namespace Eigen;
49using namespace MNELIB;
50using namespace INVLIB;
51using namespace UTILSLIB;
52using namespace FIFFLIB;
53
54//=============================================================================================================
55// DEFINE MEMBER METHODS
56//=============================================================================================================
57
58InvMinimumNorm::InvMinimumNorm(const MNEInverseOperator& p_inverseOperator, float lambda, const QString method)
59: m_inverseOperator(p_inverseOperator)
60, m_beLoreta(false)
61, m_iELoretaMaxIter(20)
62, m_dELoretaEps(1e-6)
63, m_bELoretaForceEqual(false)
64, inverseSetup(false)
65{
66 this->setRegularization(lambda);
67 this->setMethod(method);
68}
69
70//=============================================================================================================
71
72InvMinimumNorm::InvMinimumNorm(const MNEInverseOperator& p_inverseOperator, float lambda, bool dSPM, bool sLORETA)
73: m_inverseOperator(p_inverseOperator)
74, m_beLoreta(false)
75, m_iELoretaMaxIter(20)
76, m_dELoretaEps(1e-6)
77, m_bELoretaForceEqual(false)
78, inverseSetup(false)
79{
80 this->setRegularization(lambda);
81 this->setMethod(dSPM, sLORETA);
82}
83
84//=============================================================================================================
85
87{
88 //
89 // Set up the inverse according to the parameters
90 //
91 qint32 nave = p_fiffEvoked.nave;
92
93 if (!m_inverseOperator.check_ch_names(p_fiffEvoked.info)) {
94 qWarning("Channel name check failed.");
95 return InvSourceEstimate();
96 }
97
98 doInverseSetup(nave, pick_normal);
99
100 //
101 // Pick the correct channels from the data
102 //
103 FiffEvoked t_fiffEvoked = p_fiffEvoked.pick_channels(inv.noise_cov->names);
104
105 qInfo("Picked %d channels from the data", t_fiffEvoked.info.nchan);
106
107 //Results
108 float tmin = p_fiffEvoked.times[0];
109 float tstep = 1 / t_fiffEvoked.info.sfreq;
110
111 return calculateInverse(t_fiffEvoked.data, tmin, tstep, pick_normal);
112}
113
114//=============================================================================================================
115
116InvSourceEstimate InvMinimumNorm::calculateInverse(const MatrixXd& data, float tmin, float tstep, bool pick_normal) const
117{
118 if (!inverseSetup) {
119 qWarning("InvMinimumNorm::calculateInverse - Inverse not setup -> call doInverseSetup first!");
120 return InvSourceEstimate();
121 }
122
123 if (K.cols() != data.rows()) {
124 qWarning() << "InvMinimumNorm::calculateInverse - Dimension mismatch between K.cols() and data.rows() -" << K.cols() << "and" << data.rows();
125 return InvSourceEstimate();
126 }
127
128 MatrixXd sol = K * data; //apply imaging kernel
129
130 if (inv.source_ori == FIFFV_MNE_FREE_ORI && pick_normal == false) {
131 qInfo("combining the current components...");
132
133 MatrixXd sol1(sol.rows() / 3, sol.cols());
134 for (qint32 i = 0; i < sol.cols(); ++i) {
135 VectorXd tmp = Linalg::combine_xyz(sol.col(i));
136 sol1.block(0, i, sol.rows() / 3, 1) = tmp.cwiseSqrt();
137 }
138 sol.resize(sol1.rows(), sol1.cols());
139 sol = sol1;
140 }
141
142 if (m_bdSPM) {
143 qInfo("(dSPM)...");
144 sol = inv.noisenorm * sol;
145 } else if (m_bsLORETA) {
146 qInfo("(sLORETA)...");
147 sol = inv.noisenorm * sol;
148 } else if (m_beLoreta) {
149 qInfo("(eLORETA)...");
150 sol = inv.noisenorm * sol;
151 }
152 qInfo("[done]");
153
154 //Results
155 VectorXi p_vecVertices(inv.src[0].vertno.size() + inv.src[1].vertno.size());
156 p_vecVertices << inv.src[0].vertno, inv.src[1].vertno;
157
158 // VectorXi p_vecVertices();
159 // for(qint32 h = 0; h < inv.src.size(); ++h)
160 // t_qListVertices.push_back(inv.src[h].vertno);
161
162 InvSourceEstimate stc(sol, p_vecVertices, tmin, tstep);
163
164 // Record where the left hemisphere ends so the estimate can be written as
165 // an MNE-C / MNE-Python compatible -lh.stc / -rh.stc pair.
166 stc.nVerticesLh = static_cast<int>(inv.src[0].vertno.size());
167
168 return stc;
169}
170
171//=============================================================================================================
172
173MNEMneData InvMinimumNorm::mneData(const FiffEvoked& p_fiffEvoked, double snr)
174{
175 if (!m_inverseOperator.check_ch_names(p_fiffEvoked.info)) {
176 qWarning("Channel name check failed.");
177 return MNEMneData();
178 }
179 doInverseSetup(p_fiffEvoked.nave, false);
180 return MNEMneData::compute(inv, p_fiffEvoked.pick_channels(inv.noise_cov->names).data, snr);
181}
182
183//=============================================================================================================
184
185void InvMinimumNorm::doInverseSetup(qint32 nave, bool pick_normal)
186{
187 //
188 // Set up the inverse according to the parameters
189 //
190 inv = m_inverseOperator.prepare_inverse_operator(nave, m_fLambda, m_bdSPM || m_beLoreta, m_bsLORETA);
191
192 // For eLORETA: recompute source covariance weights before assembling kernel
193 if (m_beLoreta) {
194 computeELoreta();
195 }
196
197 qInfo("Computing inverse...");
198 inv.assemble_kernel(label, m_sMethod, pick_normal, K, noise_norm, vertno);
199
200 std::cout << "K " << K.rows() << " x " << K.cols() << std::endl;
201
202 inverseSetup = true;
203}
204
205//=============================================================================================================
206
207const char* InvMinimumNorm::getName() const
208{
209 return "Minimum Norm Estimate";
210}
211
212//=============================================================================================================
213
215{
216 return m_inverseOperator.src;
217}
218
219//=============================================================================================================
220
221void InvMinimumNorm::setMethod(QString method)
222{
223 if (method.compare("MNE") == 0)
224 setMethod(false, false);
225 else if (method.compare("dSPM") == 0)
226 setMethod(true, false);
227 else if (method.compare("sLORETA") == 0)
228 setMethod(false, true);
229 else if (method.compare("eLORETA") == 0)
230 setMethod(false, false, true);
231 else {
232 qWarning("Method not recognized!");
233 method = "dSPM";
234 setMethod(true, false);
235 }
236
237 qInfo("\tSet minimum norm method to %s.", method.toUtf8().constData());
238}
239
240//=============================================================================================================
241
242void InvMinimumNorm::setMethod(bool dSPM, bool sLORETA, bool eLoreta)
243{
244 int nActive = (dSPM ? 1 : 0) + (sLORETA ? 1 : 0) + (eLoreta ? 1 : 0);
245 if (nActive > 1) {
246 qWarning("Only one method can be active at a time! - Activating dSPM");
247 dSPM = true;
248 sLORETA = false;
249 eLoreta = false;
250 }
251
252 m_bdSPM = dSPM;
253 m_bsLORETA = sLORETA;
254 m_beLoreta = eLoreta;
255
256 if (dSPM)
257 m_sMethod = QString("dSPM");
258 else if (sLORETA)
259 m_sMethod = QString("sLORETA");
260 else if (eLoreta)
261 m_sMethod = QString("eLORETA");
262 else
263 m_sMethod = QString("MNE");
264}
265
266//=============================================================================================================
267
269{
270 m_fLambda = lambda;
271}
272
273//=============================================================================================================
274
275void InvMinimumNorm::setELoretaOptions(int maxIter, double eps, bool forceEqual)
276{
277 m_iELoretaMaxIter = maxIter;
278 m_dELoretaEps = eps;
279 m_bELoretaForceEqual = forceEqual;
280}
281
282//=============================================================================================================
283
284void InvMinimumNorm::computeELoreta()
285{
286 //
287 // eLORETA: Iteratively compute optimized source covariance weights R
288 // so that lambda2 acts consistently across depth.
289 //
290 // Reference: Pascual-Marqui (2007), Discrete, 3D distributed, linear imaging
291 // methods of electric neuronal activity.
292 // Adapted from MNE-Python: mne.minimum_norm._eloreta._compute_eloreta()
293 //
294
295 qInfo("Computing eLORETA source weights...");
296
297 // Reassemble the whitened gain matrix G = eigen_fields^T * diag(sing) * eigen_leads^T,
298 // with eigen_fields (n_comp, n_channels) and eigen_leads (n_sources*n_orient, n_comp).
299 if (!inv.eigen_fields || !inv.eigen_leads) {
300 qWarning("InvMinimumNorm::computeELoreta - Inverse operator missing eigen structures!");
301 return;
302 }
303
304 const MatrixXd& eigenFields = inv.eigen_fields->data;
305 const MatrixXd& eigenLeads = inv.eigen_leads->data;
306 const VectorXd& sing = inv.sing;
307
308 MatrixXd G = eigenFields.transpose() * sing.asDiagonal() * eigenLeads.transpose();
309
310 const int nChan = static_cast<int>(G.rows());
311 const int nSrc = inv.nsource;
312 const int nOrient = static_cast<int>(G.cols()) / nSrc;
313
314 if (nOrient != 1 && nOrient != 3) {
315 qWarning("InvMinimumNorm::computeELoreta - Unexpected n_orient: %d", nOrient);
316 return;
317 }
318
319 // Divide by sqrt(source_cov) to undo source covariance weighting
320 if (inv.source_cov && inv.source_cov->data.size() > 0) {
321 for (int i = 0; i < G.cols(); ++i) {
322 double sc = inv.source_cov->data(i, 0);
323 if (sc > 0)
324 G.col(i) /= std::sqrt(sc);
325 }
326 }
327
328 // Restore orientation prior
329 VectorXd sourceStd = VectorXd::Ones(G.cols());
330 if (inv.orient_prior && inv.orient_prior->data.size() > 0) {
331 for (int i = 0; i < G.cols() && i < inv.orient_prior->data.rows(); ++i) {
332 double op = inv.orient_prior->data(i, 0);
333 if (op > 0)
334 sourceStd(i) *= std::sqrt(op);
335 }
336 }
337 for (int i = 0; i < G.cols(); ++i) {
338 G.col(i) *= sourceStd(i);
339 }
340
341 // Rank of the inverse as mne-python compute_rank_inverse: from the noise covariance, not from sing.
342 int nNonZero = 0;
343 if (inv.noise_cov && !inv.noise_cov->diag) {
344 for (int i = 0; i < inv.noise_cov->eig.size(); ++i) {
345 if (inv.noise_cov->eig(i) > 0)
346 ++nNonZero;
347 }
348 } else if (inv.noise_cov) {
349 MatrixXd proj;
350 nNonZero = inv.noise_cov->dim - FiffProj::make_projector(inv.projs, inv.noise_cov->names, proj);
351 }
352 nNonZero = std::min(nNonZero, nChan);
353
354 double lambda2 = static_cast<double>(m_fLambda);
355
356 // Initialize weight matrix R
357 // For fixed orientation (or force_equal): R is a diagonal vector (nSrc * nOrient)
358 // For free orientation: R is block-diagonal (nSrc x 3 x 3)
359 const bool useScalar = (nOrient == 1 || m_bELoretaForceEqual);
360
361 VectorXd R_vec; // For scalar mode: (nSrc * nOrient)
362 std::vector<Matrix3d> R_mat; // For matrix mode: nSrc x (3x3)
363
364 if (useScalar) {
365 R_vec = VectorXd::Ones(static_cast<Eigen::Index>(nSrc) * nOrient);
366 // Apply prior: R *= sourceStd^2
367 for (int i = 0; i < R_vec.size(); ++i) {
368 R_vec(i) *= sourceStd(i) * sourceStd(i);
369 }
370 } else {
371 R_mat.resize(nSrc);
372 for (int s = 0; s < nSrc; ++s) {
373 R_mat[s] = Matrix3d::Identity();
374 // Apply prior as outer product
375 for (int a = 0; a < 3; ++a) {
376 for (int b = 0; b < 3; ++b) {
377 R_mat[s](a, b) *= sourceStd(s * 3 + a) * sourceStd(s * 3 + b);
378 }
379 }
380 }
381 }
382
383 qInfo(" Fitting up to %d iterations (n_orient=%d, force_equal=%s)...",
384 m_iELoretaMaxIter, nOrient, m_bELoretaForceEqual ? "true" : "false");
385
386 // Lambda for computing G * R * G^T and normalizing
387 auto computeGRGt = [&]() -> MatrixXd {
388 MatrixXd GRGt;
389 if (useScalar) {
390 // G_R = G * diag(R)
391 MatrixXd GR = G;
392 for (int i = 0; i < G.cols(); ++i) {
393 GR.col(i) *= R_vec(i);
394 }
395 GRGt = GR * G.transpose();
396 } else {
397 // Block multiplication
398 MatrixXd RGt = MatrixXd::Zero(nSrc * 3, nChan);
399 for (int s = 0; s < nSrc; ++s) {
400 // G_s: (nChan, 3)
401 MatrixXd Gs = G.middleCols(s * 3, 3);
402 // R_s * G_s^T: (3, nChan)
403 RGt.middleRows(s * 3, 3) = R_mat[s] * Gs.transpose();
404 }
405 GRGt = G * RGt;
406 }
407 // Normalize so trace(GRGt) / nNonZero = 1
408 double trace = GRGt.trace();
409 double norm = trace / static_cast<double>(nNonZero);
410 if (norm > 1e-30) {
411 GRGt /= norm;
412 if (useScalar)
413 R_vec /= norm;
414 else
415 for (auto& Rm : R_mat)
416 Rm /= norm;
417 }
418 return GRGt;
419 };
420
421 MatrixXd GRGt = computeGRGt();
422
423 for (int kk = 0; kk < m_iELoretaMaxIter; ++kk) {
424 // 1. Compute inverse of GRGt (stabilized eigendecomposition)
425 SelfAdjointEigenSolver<MatrixXd> eig(GRGt);
426 VectorXd s = eig.eigenvalues().cwiseAbs();
427 MatrixXd u = eig.eigenvectors();
428
429 // Keep top nNonZero eigenvalues
430 // Sort descending
431 std::vector<int> idx(s.size());
432 std::iota(idx.begin(), idx.end(), 0);
433 std::sort(idx.begin(), idx.end(), [&s](int a, int b) { return s(a) > s(b); });
434
435 MatrixXd uKeep(nChan, nNonZero);
436 VectorXd sKeep(nNonZero);
437 for (int i = 0; i < nNonZero && i < static_cast<int>(idx.size()); ++i) {
438 uKeep.col(i) = u.col(idx[i]);
439 sKeep(i) = s(idx[i]);
440 }
441
442 // N = u * diag(1/(s + lambda2)) * u^T
443 VectorXd sInv(nNonZero);
444 for (int i = 0; i < nNonZero; ++i) {
445 sInv(i) = (sKeep(i) > 0) ? 1.0 / (sKeep(i) + lambda2) : 0.0;
446 }
447 MatrixXd N = uKeep * sInv.asDiagonal() * uKeep.transpose();
448
449 // Save old R for convergence check
450 VectorXd R_old_vec;
451 std::vector<Matrix3d> R_old_mat;
452 if (useScalar)
453 R_old_vec = R_vec;
454 else
455 R_old_mat = R_mat;
456
457 // 2. Update R
458 if (nOrient == 1) {
459 // R_i = 1 / sqrt(sum_j(N * G)_ij * G_ij)
460 MatrixXd NG = N * G; // (nChan, nDipoles)
461 for (int i = 0; i < nSrc; ++i) {
462 double val = (NG.col(i).array() * G.col(i).array()).sum();
463 R_vec(i) = (val > 1e-30) ? 1.0 / std::sqrt(val) : 1.0;
464 }
465 } else if (m_bELoretaForceEqual) {
466 // For force_equal: compute M_s = G_s^T N G_s, then average eigenvalues of M_s^{-1/2}
467 for (int s_idx = 0; s_idx < nSrc; ++s_idx) {
468 MatrixXd Gs = G.middleCols(s_idx * 3, 3); // (nChan, 3)
469 Matrix3d M = Gs.transpose() * N * Gs;
470
471 // mne-python: R = 1 / mean(sqrt(eig)); an eigenvalue <= 1e-7 * max counts as infinite, giving R = 0.
472 SelfAdjointEigenSolver<Matrix3d> eigM(M);
473 Vector3d mEig = eigM.eigenvalues();
474 const double limit = mEig(2) * 1e-7;
475 double meanSqrt = 0;
476 bool degenerate = false;
477 for (int d = 0; d < 3; ++d) {
478 if (mEig(d) > limit)
479 meanSqrt += std::sqrt(mEig(d));
480 else
481 degenerate = true;
482 }
483 meanSqrt /= 3.0;
484 for (int d = 0; d < 3; ++d) {
485 R_vec(s_idx * 3 + d) = (degenerate || meanSqrt <= 0) ? 0.0 : 1.0 / meanSqrt;
486 }
487 }
488 } else {
489 // Free orientation, independent: R_s = sqrtm(G_s^T N G_s)^{-1/2}
490 for (int s_idx = 0; s_idx < nSrc; ++s_idx) {
491 MatrixXd Gs = G.middleCols(s_idx * 3, 3);
492 Matrix3d M = Gs.transpose() * N * Gs;
493
494 // Symmetric matrix power: M^{-1/2}
495 SelfAdjointEigenSolver<Matrix3d> eigM(M);
496 Vector3d mEig = eigM.eigenvalues();
497 Matrix3d mVec = eigM.eigenvectors();
498 Vector3d mPow;
499 for (int d = 0; d < 3; ++d) {
500 mPow(d) = (mEig(d) > 1e-7 * mEig(2)) ? std::pow(mEig(d), -0.5) : 0.0; // mne _sym_mat_pow rcond
501 }
502 R_mat[s_idx] = mVec * mPow.asDiagonal() * mVec.transpose();
503 }
504 }
505
506 // Reapply prior
507 if (useScalar) {
508 for (int i = 0; i < R_vec.size(); ++i) {
509 R_vec(i) *= sourceStd(i) * sourceStd(i);
510 }
511 } else {
512 for (int s_idx = 0; s_idx < nSrc; ++s_idx) {
513 for (int a = 0; a < 3; ++a) {
514 for (int b = 0; b < 3; ++b) {
515 R_mat[s_idx](a, b) *= sourceStd(s_idx * 3 + a) * sourceStd(s_idx * 3 + b);
516 }
517 }
518 }
519 }
520
521 GRGt = computeGRGt();
522
523 // 3. Check convergence
524 double deltaNum = 0.0, deltaDen = 0.0;
525 if (useScalar) {
526 deltaNum = (R_vec - R_old_vec).norm();
527 deltaDen = R_old_vec.norm();
528 } else {
529 for (int s_idx = 0; s_idx < nSrc; ++s_idx) {
530 Matrix3d diff = R_mat[s_idx] - R_old_mat[s_idx];
531 deltaNum += diff.squaredNorm();
532 deltaDen += R_old_mat[s_idx].squaredNorm();
533 }
534 deltaNum = std::sqrt(deltaNum);
535 deltaDen = std::sqrt(deltaDen);
536 }
537 double delta = (deltaDen > 1e-30) ? deltaNum / deltaDen : 0.0;
538
539 if (delta < m_dELoretaEps) {
540 qInfo(" eLORETA converged on iteration %d (delta=%.2e < eps=%.2e)", kk + 1, delta, m_dELoretaEps);
541 break;
542 }
543 if (kk == m_iELoretaMaxIter - 1) {
544 qWarning(" eLORETA weight fitting did not converge after %d iterations (delta=%.2e)", m_iELoretaMaxIter, delta);
545 }
546 }
547
548 // Undo source_std weighting on G and renormalize R against the unbiased gain, as mne-python does
549 for (int i = 0; i < G.cols(); ++i) {
550 G.col(i) /= sourceStd(i);
551 }
552 computeGRGt();
553
554 // Compute R^{1/2}
555 VectorXd R_sqrt_vec;
556 std::vector<Matrix3d> R_sqrt_mat;
557 if (useScalar) {
558 R_sqrt_vec = R_vec.cwiseSqrt();
559 } else {
560 R_sqrt_mat.resize(nSrc);
561 for (int s_idx = 0; s_idx < nSrc; ++s_idx) {
562 SelfAdjointEigenSolver<Matrix3d> eigR(R_mat[s_idx]);
563 Vector3d rEig = eigR.eigenvalues();
564 Matrix3d rVec = eigR.eigenvectors();
565 Vector3d rSqrt;
566 for (int d = 0; d < 3; ++d) {
567 rSqrt(d) = (rEig(d) > 1e-7 * rEig(2)) ? std::sqrt(rEig(d)) : 0.0;
568 }
569 R_sqrt_mat[s_idx] = rVec * rSqrt.asDiagonal() * rVec.transpose();
570 }
571 }
572
573 // Compute weighted gain: A = G * R^{1/2}
574 MatrixXd A = G;
575 if (useScalar) {
576 for (int i = 0; i < A.cols(); ++i) {
577 A.col(i) *= R_sqrt_vec(i);
578 }
579 } else {
580 for (int s_idx = 0; s_idx < nSrc; ++s_idx) {
581 MatrixXd Gs = G.middleCols(s_idx * 3, 3);
582 A.middleCols(s_idx * 3, 3) = Gs * R_sqrt_mat[s_idx];
583 }
584 }
585
586 // SVD of A = G * R^{1/2}, shape (nChan, nSrc*nOrient)
587 // A = U * Σ * V^T → U: (nChan, ncomp), V: (nSrc*nOrient, ncomp)
588 JacobiSVD<MatrixXd> svd(A, ComputeThinU | ComputeThinV);
589 const VectorXd newSing = svd.singularValues(); // (ncomp,)
590 const MatrixXd newU = svd.matrixU(); // (nChan, ncomp)
591 const MatrixXd newV = svd.matrixV(); // (nSrc*nOrient, ncomp)
592
593 // Build R^{1/2}-weighted eigen_leads: weightedLeads[i,:] = R_sqrt[i] * V[i,:]
594 MatrixXd weightedLeads = newV; // (nSrc*nOrient, ncomp)
595 if (useScalar) {
596 for (int i = 0; i < weightedLeads.rows(); ++i) {
597 weightedLeads.row(i) *= R_sqrt_vec(i);
598 }
599 } else {
600 // For each source s: rows [s*3, s*3+3) = R_sqrt_mat[s] * V[s*3, s*3+3)
601 for (int s_idx = 0; s_idx < nSrc; ++s_idx) {
602 MatrixXd Vs = newV.middleRows(s_idx * 3, 3); // (3, ncomp)
603 weightedLeads.middleRows(s_idx * 3, 3) = R_sqrt_mat[s_idx] * Vs;
604 }
605 }
606
607 // eigen_fields is stored (ncomp, nChan); weighted eigen leads make assemble_kernel use them directly.
608 inv.sing = newSing;
609 inv.eigen_fields->data = newU.transpose();
610 inv.eigen_fields->nrow = static_cast<int>(newU.cols());
611 inv.eigen_fields->ncol = static_cast<int>(newU.rows());
612 inv.eigen_leads->ncol = static_cast<int>(weightedLeads.cols());
613 inv.eigen_leads->data = weightedLeads;
614 inv.eigen_leads_weighted = true;
615
616 // reginv = sing / (sing^2 + lambda2) for the first nNonZero components, zero beyond (mne-python _compute_reginv).
617 VectorXd reginv = VectorXd::Zero(newSing.size());
618 for (int i = 0; i < std::min<int>(nNonZero, static_cast<int>(newSing.size())); ++i) {
619 if (newSing(i) > 0)
620 reginv(i) = newSing(i) / (newSing(i) * newSing(i) + lambda2);
621 }
622 inv.reginv = reginv;
623
624 qInfo(" eLORETA inverse operator updated. [done]");
625}
#define FIFFV_MNE_FREE_ORI
Single averaged evoked response: time axis, samples, baseline, channel info and processing history.
Eigen::JacobiSVD< Eigen::Matrix3f > svd(S, Eigen::ComputeFullU|Eigen::ComputeFullV)
SSP projection item: a named projection vector set with active/desired flags, parsed from FIFFB_PROJ_...
InvSourceEstimate value type — central source-space data container produced by every INVLIB inverse s...
Linear minimum-norm inverse solver — MNE, dSPM, sLORETA and eLORETA from a precomputed MNEInverseOper...
Static linear-algebra helpers: SVD-based conditioning, block-diagonal assembly, sorted index pairs.
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Inverse source estimation (MNE, dSPM, sLORETA, dipole fitting).
Single averaged evoked response: time axis, data, baseline, channel info and averaging metadata.
Definition fiff_evoked.h:77
Eigen::RowVectorXf times
Eigen::MatrixXd data
FiffEvoked pick_channels(const QStringList &include=defaultQStringList, const QStringList &exclude=defaultQStringList) const
static fiff_int_t make_projector(const QList< FiffProj > &projs, const QStringList &ch_names, Eigen::MatrixXd &proj, const QStringList &bads=defaultQStringList, Eigen::MatrixXd &U=defaultMatrixXd)
Source-space inverse-solution container with dense grid plus optional focal-dipole,...
virtual const MNELIB::MNESourceSpaces & getSourceSpace() const
void setELoretaOptions(int maxIter=20, double eps=1e-6, bool forceEqual=false)
MNELIB::MNEMneData mneData(const FIFFLIB::FiffEvoked &p_fiffEvoked, double snr=0.0)
virtual InvSourceEstimate calculateInverse(const FIFFLIB::FiffEvoked &p_fiffEvoked, bool pick_normal=false)
virtual void doInverseSetup(qint32 nave, bool pick_normal=false)
void setMethod(QString method)
void setRegularization(float lambda)
virtual const char * getName() const
InvMinimumNorm(const MNELIB::MNEInverseOperator &p_inverseOperator, float lambda, const QString method)
static Eigen::VectorXd combine_xyz(const Eigen::VectorXd &vec)
Definition linalg.cpp:57
MNE-style inverse operator.
FIFFLIB::FiffNamedMatrix::SDPtr eigen_leads
FIFFLIB::FiffNamedMatrix::SDPtr eigen_fields
Data associated with MNE computations for each mneMeasDataSet.
static MNEMneData compute(const MNEInverseOperator &inv, const Eigen::MatrixXd &data, double snr)
List of MNESourceSpace objects forming a subject source space.