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