v2.0.0
Loading...
Searching...
No Matches
inv_gamma_map.cpp
Go to the documentation of this file.
1//=============================================================================================================
21
22//=============================================================================================================
23// INCLUDES
24//=============================================================================================================
25
26#include "inv_gamma_map.h"
27
28//=============================================================================================================
29// STL INCLUDES
30//=============================================================================================================
31
32#include <cmath>
33#include <algorithm>
34
35//=============================================================================================================
36// USED NAMESPACES
37//=============================================================================================================
38
39using namespace INVLIB;
40using namespace Eigen;
41
42//=============================================================================================================
43// DEFINE MEMBER METHODS
44//=============================================================================================================
45
47 const MatrixXd& matGain,
48 const MatrixXd& matData,
49 const MatrixXd& matNoiseCov,
50 int nIterations,
51 double tolerance,
52 double gammaThreshold)
53{
54 const int nChannels = static_cast<int>(matGain.rows());
55 const int nSources = static_cast<int>(matGain.cols());
56 const int nTimes = static_cast<int>(matData.cols());
57
58 // Compute noise covariance inverse once
59 MatrixXd matNoiseCovInv = matNoiseCov.ldlt().solve(MatrixXd::Identity(nChannels, nChannels));
60
61 // Initialize gamma (source variance hyperparameters)
62 VectorXd vecGamma = VectorXd::Ones(nSources);
63 VectorXd vecGammaOld = vecGamma;
64
65 // Active set: all sources initially active
66 std::vector<int> activeIdx(nSources);
67 std::iota(activeIdx.begin(), activeIdx.end(), 0);
68
69 // Full source solution
70 MatrixXd matX = MatrixXd::Zero(nSources, nTimes);
71
72 int actualIterations = 0;
73
74 for (int iter = 0; iter < nIterations; ++iter) {
75 actualIterations = iter + 1;
76
77 const int nActive = static_cast<int>(activeIdx.size());
78 if (nActive == 0)
79 break;
80
81 // Extract active columns of G
82 MatrixXd matG_active(nChannels, nActive);
83 VectorXd vecGamma_active(nActive);
84 for (int i = 0; i < nActive; ++i) {
85 matG_active.col(i) = matGain.col(activeIdx[i]);
86 vecGamma_active(i) = vecGamma(activeIdx[i]);
87 }
88
89 // Gamma as diagonal matrix: Gamma_active = diag(gamma_active)
90 // Data covariance model: C_M = G_active * Gamma_active * G_active^T + NoiseCov
91 MatrixXd matCm = matG_active * vecGamma_active.asDiagonal() * matG_active.transpose() + matNoiseCov;
92
93 // Solve C_M^{-1} * M
94 MatrixXd matCmInvM = matCm.ldlt().solve(matData);
95
96 // Posterior mean: X_active = Gamma_active * G_active^T * C_M^{-1} * M
97 MatrixXd matX_active = vecGamma_active.asDiagonal() * matG_active.transpose() * matCmInvM;
98
99 // Write back to full solution
100 matX.setZero();
101 for (int i = 0; i < nActive; ++i) {
102 matX.row(activeIdx[i]) = matX_active.row(i);
103 }
104
105 // Update gamma: gamma_i = ||X_i||^2_2 / T
106 vecGammaOld = vecGamma;
107 for (int i = 0; i < nActive; ++i) {
108 int srcIdx = activeIdx[i];
109 vecGamma(srcIdx) = matX_active.row(i).squaredNorm() / static_cast<double>(nTimes);
110 }
111
112 // Prune sources with gamma below threshold
113 std::vector<int> newActive;
114 newActive.reserve(nActive);
115 for (int i = 0; i < nActive; ++i) {
116 int srcIdx = activeIdx[i];
117 if (vecGamma(srcIdx) >= gammaThreshold) {
118 newActive.push_back(srcIdx);
119 } else {
120 vecGamma(srcIdx) = 0.0;
121 }
122 }
123 activeIdx = newActive;
124
125 // Check convergence: max|gamma_new - gamma_old| / max(max|gamma_old|, 1e-10) < tolerance
126 double maxGammaOld = std::max(vecGammaOld.cwiseAbs().maxCoeff(), 1e-10);
127 double maxRelChange = 0.0;
128 for (int idx : activeIdx) {
129 double relChange = std::abs(vecGamma(idx) - vecGammaOld(idx)) / maxGammaOld;
130 maxRelChange = std::max(maxRelChange, relChange);
131 }
132 if (maxRelChange < tolerance)
133 break;
134 }
135
136 // Build result
137 InvGammaMapResult result;
138 result.nIterations = actualIterations;
139 result.vecGamma = vecGamma;
140
141 // Collect active vertices
142 QVector<int> finalActive;
143 for (int i = 0; i < nSources; ++i) {
144 if (vecGamma(i) >= gammaThreshold) {
145 finalActive.append(i);
146 }
147 }
148 result.activeVertices = finalActive;
149
150 // Build source estimate with active rows only
151 const int nActiveFinal = finalActive.size();
152 MatrixXd matActiveSol(nActiveFinal, nTimes);
153 VectorXi vecActiveVerts(nActiveFinal);
154 for (int i = 0; i < nActiveFinal; ++i) {
155 matActiveSol.row(i) = matX.row(finalActive[i]);
156 vecActiveVerts(i) = finalActive[i];
157 }
158
159 result.stc = InvSourceEstimate(matActiveSol, vecActiveVerts, 0.0f, 1.0f);
161
162 // Compute residual norm ||M - G*X||_F
163 MatrixXd matResidual = matData - matGain * matX;
164 result.residualNorm = matResidual.norm();
165
166 return result;
167}
Gamma-MAP sparse Bayesian inverse solver — automatic-relevance-determination prior on per-source vari...
Inverse source estimation (MNE, dSPM, sLORETA, dipole fitting).
Source-space inverse-solution container with dense grid plus optional focal-dipole,...
InvSourceEstimate stc
QVector< int > activeVertices
static InvGammaMapResult compute(const Eigen::MatrixXd &matGain, const Eigen::MatrixXd &matData, const Eigen::MatrixXd &matNoiseCov, int nIterations=100, double tolerance=1e-6, double gammaThreshold=1e-10)