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