v2.0.0
Loading...
Searching...
No Matches
simulate.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "simulate.h"
18
20#include <inv/inv_convenience.h>
21#include <fiff/fiff_evoked.h>
22#include <fiff/fiff_info.h>
23#include <fiff/fiff_cov.h>
25
26//=============================================================================================================
27// QT INCLUDES
28//=============================================================================================================
29
30#include <QDebug>
31
32//=============================================================================================================
33// EIGEN INCLUDES
34//=============================================================================================================
35
36#include <Eigen/Dense>
37
38//=============================================================================================================
39// STL INCLUDES
40//=============================================================================================================
41
42#include <cmath>
43#include <random>
44
45//=============================================================================================================
46// USED NAMESPACES
47//=============================================================================================================
48
49using namespace UTILSLIB;
50using namespace INVLIB;
51using namespace FIFFLIB;
52using namespace MNELIB;
53using namespace Eigen;
54
55//=============================================================================================================
56// DEFINE FUNCTIONS
57//=============================================================================================================
58
60 const VectorXi& activeVertices,
61 const VectorXi& allVertices,
62 const SimulateStcParams& params)
63{
64 const int nAll = static_cast<int>(allVertices.size());
65 const int nActive = static_cast<int>(activeVertices.size());
66 const float tstep = 1.0f / params.sfreq;
67 const int nTimes = static_cast<int>(params.duration * params.sfreq);
68
69 if (nTimes <= 0 || nAll <= 0) {
70 qWarning() << "[simulateStc] Invalid parameters.";
71 return InvSourceEstimate();
72 }
73
74 // Build mapping: active vertex -> row in allVertices
75 QList<int> activeRows;
76 for (int a = 0; a < nActive; ++a) {
77 bool found = false;
78 for (int v = 0; v < nAll; ++v) {
79 if (allVertices(v) == activeVertices(a)) {
80 activeRows.append(v);
81 found = true;
82 break;
83 }
84 }
85 if (!found) {
86 qWarning() << "[simulateStc] Active vertex" << activeVertices(a) << "not in allVertices.";
87 return InvSourceEstimate();
88 }
89 }
90
91 // Generate Gaussian-envelope waveforms for each active source
92 MatrixXd data = MatrixXd::Zero(nAll, nTimes);
93
94 std::mt19937 gen(params.seed);
95 std::uniform_real_distribution<double> timeDist(0.2, 0.8);
96
97 for (int a = 0; a < nActive; ++a) {
98 // Center the Gaussian at a random fraction of the duration
99 double centerFrac = timeDist(gen);
100 double centerSample = centerFrac * nTimes;
101 double sigma = nTimes * 0.1; // 10% of duration
102
103 for (int t = 0; t < nTimes; ++t) {
104 double exponent = -0.5 * std::pow((t - centerSample) / sigma, 2.0);
105 data(activeRows[a], t) = std::exp(exponent);
106 }
107 }
108
109 return InvSourceEstimate(data, allVertices, params.tmin, tstep);
110}
111
112//=============================================================================================================
113
115 const MatrixXd& waveforms,
116 const VectorXi& activeVertices,
117 const VectorXi& allVertices,
118 float tmin,
119 float tstep)
120{
121 const int nAll = static_cast<int>(allVertices.size());
122 const int nActive = static_cast<int>(activeVertices.size());
123 const int nTimes = static_cast<int>(waveforms.cols());
124
125 if (waveforms.rows() != nActive) {
126 qWarning() << "[simulateStcFromWaveforms] waveforms.rows() != activeVertices.size()";
127 return InvSourceEstimate();
128 }
129
130 MatrixXd data = MatrixXd::Zero(nAll, nTimes);
131
132 for (int a = 0; a < nActive; ++a) {
133 for (int v = 0; v < nAll; ++v) {
134 if (allVertices(v) == activeVertices(a)) {
135 data.row(v) = waveforms.row(a);
136 break;
137 }
138 }
139 }
140
141 return InvSourceEstimate(data, allVertices, tmin, tstep);
142}
143
144//=============================================================================================================
145
147 const MNEForwardSolution& fwd,
148 const InvSourceEstimate& stc,
149 const FiffInfo& info,
150 const FiffCov& noiseCov,
151 int nave,
152 int seed)
153{
154 // First generate noiseless data
155 FiffEvoked evoked = simulateEvokedNoiseless(fwd, stc, info);
156
157 if (evoked.data.size() == 0)
158 return evoked;
159
160 const int nChan = static_cast<int>(evoked.data.rows());
161 const int nTimes = static_cast<int>(evoked.data.cols());
162
163 // Add noise from covariance
164 if (noiseCov.data.size() > 0) {
165 // Coloured like mne.simulation.simulate_evoked: the colourer of the prepared covariance (SSP applied,
166 // MEG and EEG decomposed separately), so the noise has no MEG-EEG covariance; divided by sqrt(nave).
167 const FiffCov prepared = noiseCov.prepare_noise_cov(evoked.info, evoked.info.ch_names);
168 if (prepared.eig.size() != nChan) {
169 qWarning() << "[simulateEvoked] Noise covariance does not cover the forward channels.";
170 return evoked;
171 }
172 const MatrixXd sqrtCov = prepared.eigvec.transpose() * prepared.eig.cwiseMax(0.0).cwiseSqrt().asDiagonal();
173
174 std::mt19937 gen(seed);
175 std::normal_distribution<double> dist(0.0, 1.0);
176
177 MatrixXd noise(nChan, nTimes);
178 for (int i = 0; i < nChan; ++i) {
179 for (int j = 0; j < nTimes; ++j) {
180 noise(i, j) = dist(gen);
181 }
182 }
183
184 double scaleFactor = 1.0 / std::sqrt(static_cast<double>(nave));
185 evoked.data += sqrtCov * noise * scaleFactor;
186 }
187
188 evoked.nave = nave;
189 return evoked;
190}
191
192//=============================================================================================================
193
195 const MNEForwardSolution& fwd,
196 const InvSourceEstimate& stc,
197 const FiffInfo& info)
198{
199 return INVLIB::applyForward(fwd, stc, info);
200}
Single averaged evoked response: time axis, samples, baseline, channel info and processing history.
Full FIFF measurement metadata: everything from FIFFB_MEAS / FIFFB_MEAS_INFO needed to interpret a re...
Noise / data covariance matrix as stored under FIFFB_MNE_COV, with channel names, kind,...
Top-level convenience entry points that mirror MNE-Python's apply_inverse_* / compute_source_psd help...
InvSourceEstimate value type — central source-space data container produced by every INVLIB inverse s...
Simulation utilities for source estimates and evoked data.
Forward solution (gain matrix mapping source dipoles to sensor measurements).
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).
DSPSHARED_EXPORT INVLIB::InvSourceEstimate simulateStc(const Eigen::VectorXi &activeVertices, const Eigen::VectorXi &allVertices, const SimulateStcParams &params=SimulateStcParams())
Create a synthetic source time course.
DSPSHARED_EXPORT INVLIB::InvSourceEstimate simulateStcFromWaveforms(const Eigen::MatrixXd &waveforms, const Eigen::VectorXi &activeVertices, const Eigen::VectorXi &allVertices, float tmin=0.0f, float tstep=0.001f)
Create a synthetic source time course from custom waveforms.
DSPSHARED_EXPORT FIFFLIB::FiffEvoked simulateEvokedNoiseless(const MNELIB::MNEForwardSolution &fwd, const INVLIB::InvSourceEstimate &stc, const FIFFLIB::FiffInfo &info)
Simulate evoked data without noise.
Definition simulate.cpp:194
DSPSHARED_EXPORT FIFFLIB::FiffEvoked simulateEvoked(const MNELIB::MNEForwardSolution &fwd, const INVLIB::InvSourceEstimate &stc, const FIFFLIB::FiffInfo &info, const FIFFLIB::FiffCov &noiseCov, int nave=1, int seed=42)
Simulate evoked data from a source estimate and forward model.
Definition simulate.cpp:146
Inverse source estimation (MNE, dSPM, sLORETA, dipole fitting).
INVSHARED_EXPORT FIFFLIB::FiffEvoked applyForward(const MNELIB::MNEForwardSolution &fwd, const InvSourceEstimate &stc, const FIFFLIB::FiffInfo &info)
Project a source estimate to the sensors, as mne.apply_forward.
Parameters for source time course simulation.
Definition simulate.h:73
FIFF noise / data covariance: matrix, channel names, kind, applied projectors, bads,...
Definition fiff_cov.h:82
Eigen::MatrixXd eigvec
Definition fiff_cov.h:283
Eigen::VectorXd eig
Definition fiff_cov.h:282
Eigen::MatrixXd data
Definition fiff_cov.h:278
FiffCov prepare_noise_cov(const FiffInfo &p_info, const QStringList &p_chNames) const
Definition fiff_cov.cpp:176
Single averaged evoked response: time axis, data, baseline, channel info and averaging metadata.
Definition fiff_evoked.h:77
Eigen::MatrixXd data
Full FIFF measurement info: per-channel descriptors, sampling and filter setup, projectors,...
Definition fiff_info.h:90
Source-space inverse-solution container with dense grid plus optional focal-dipole,...
In-memory representation of an -fwd.fif forward solution.