v2.0.0
Loading...
Searching...
No Matches
coherency.cpp
Go to the documentation of this file.
1//=============================================================================================================
15
16//=============================================================================================================
17// INCLUDES
18//=============================================================================================================
19
20#include "coherency.h"
23#include "../network/network.h"
24
25#include <math/spectral.h>
26
27//=============================================================================================================
28// QT INCLUDES
29//=============================================================================================================
30
31#include <QDebug>
32#include <QtConcurrent>
33
34//=============================================================================================================
35// EIGEN INCLUDES
36//=============================================================================================================
37
38#include <unsupported/Eigen/FFT>
39
40//=============================================================================================================
41// USED NAMESPACES
42//=============================================================================================================
43
44using namespace CONNECTIVITYLIB;
45using namespace Eigen;
46using namespace UTILSLIB;
47
48//=============================================================================================================
49// DEFINE GLOBAL METHODS
50//=============================================================================================================
51
52//=============================================================================================================
53// DEFINE MEMBER METHODS
54//=============================================================================================================
55
59
60//=============================================================================================================
61
63 ConnectivitySettings& connectivitySettings)
64{
65 // QElapsedTimer timer;
66 // qint64 iTime = 0;
67 // timer.start();
68
69 if (connectivitySettings.isEmpty()) {
70 qDebug() << "Coherency::calculateReal - Input data is empty";
71 return;
72 }
73
74#ifdef EIGEN_FFTW_DEFAULT
75 fftw_make_planner_thread_safe();
76#endif
77
78 int iSignalLength = connectivitySettings.at(0).matData.cols();
79 int iNfft = connectivitySettings.getFFTSize();
80
81 // Generate tapers
82 QPair<MatrixXd, VectorXd> tapers = Spectral::generateTapers(iSignalLength, connectivitySettings.getWindowType());
83
84 // Initialize vecPsdAvg and vecCsdAvg
85 int iNRows = connectivitySettings.at(0).matData.rows();
86 int iNFreqs = int(floor(iNfft / 2.0)) + 1;
87
88 // Compute PSD/CSD for each trial
89 QMutex mutex;
90
91 std::function<void(ConnectivitySettings::IntermediateTrialData&)> computeLambda = [&](ConnectivitySettings::IntermediateTrialData& inputData) {
92 compute(inputData,
93 connectivitySettings.getIntermediateSumData().matPsdSum,
94 connectivitySettings.getIntermediateSumData().vecPairCsdSum,
95 mutex,
96 iNRows,
97 iNFreqs,
98 iNfft,
99 tapers);
100 };
101
102 // iTime = timer.elapsed();
103 // qWarning() << "Preparation" << iTime;
104 // timer.restart();
105
106 QFuture<void> result = QtConcurrent::map(connectivitySettings.getTrialData(),
107 computeLambda);
108 result.waitForFinished();
109
110 // iTime = timer.elapsed();
111 // qWarning() << "ComputeSpectraPSDCSD" << iTime;
112 // timer.restart();
113
114 // Compute CSD/sqrt(PSD_X * PSD_Y)
115 std::function<void(QPair<int, MatrixXcd>&)> computePSDCSDLambda = [&](QPair<int, MatrixXcd>& pairInput) {
116 computePSDCSDAbs(mutex,
117 finalNetwork,
118 pairInput,
119 connectivitySettings.getIntermediateSumData().matPsdSum);
120 };
121
122 QFuture<void> resultCSDPSD = QtConcurrent::map(connectivitySettings.getIntermediateSumData().vecPairCsdSum,
123 computePSDCSDLambda);
124 resultCSDPSD.waitForFinished();
125
126 // iTime = timer.elapsed();
127 // qWarning() << "Compute" << iTime;
128 // timer.restart();
129}
130
131//=============================================================================================================
132
134 ConnectivitySettings& connectivitySettings)
135{
136 // QElapsedTimer timer;
137 // qint64 iTime = 0;
138 // timer.start();
139
140 if (connectivitySettings.isEmpty()) {
141 qDebug() << "Coherency::calculateImag - Input data is empty";
142 return;
143 }
144
145#ifdef EIGEN_FFTW_DEFAULT
146 fftw_make_planner_thread_safe();
147#endif
148
149 int iSignalLength = connectivitySettings.at(0).matData.cols();
150 int iNfft = connectivitySettings.getFFTSize();
151
152 // Generate tapers
153 QPair<MatrixXd, VectorXd> tapers = Spectral::generateTapers(iSignalLength, connectivitySettings.getWindowType());
154
155 // Initialize vecPsdAvg and vecCsdAvg
156 int iNRows = connectivitySettings.at(0).matData.rows();
157 int iNFreqs = int(floor(iNfft / 2.0)) + 1;
158
159 // Compute PSD/CSD for each trial
160 QMutex mutex;
161
162 std::function<void(ConnectivitySettings::IntermediateTrialData&)> computeLambda = [&](ConnectivitySettings::IntermediateTrialData& inputData) {
163 compute(inputData,
164 connectivitySettings.getIntermediateSumData().matPsdSum,
165 connectivitySettings.getIntermediateSumData().vecPairCsdSum,
166 mutex,
167 iNRows,
168 iNFreqs,
169 iNfft,
170 tapers);
171 };
172
173 // iTime = timer.elapsed();
174 // qWarning() << "Preparation" << iTime;
175 // timer.restart();
176
177 QFuture<void> result = QtConcurrent::map(connectivitySettings.getTrialData(),
178 computeLambda);
179 result.waitForFinished();
180
181 // iTime = timer.elapsed();
182 // qWarning() << "ComputeSpectraPSDCSD" << iTime;
183 // timer.restart();
184
185 // Compute CSD/sqrt(PSD_X * PSD_Y)
186 std::function<void(QPair<int, MatrixXcd>&)> computePSDCSDLambda = [&](QPair<int, MatrixXcd>& pairInput) {
187 computePSDCSDImag(mutex,
188 finalNetwork,
189 pairInput,
190 connectivitySettings.getIntermediateSumData().matPsdSum);
191 };
192
193 QFuture<void> resultCSDPSD = QtConcurrent::map(connectivitySettings.getIntermediateSumData().vecPairCsdSum,
194 computePSDCSDLambda);
195 resultCSDPSD.waitForFinished();
196
197 // iTime = timer.elapsed();
198 // qWarning() << "Compute" << iTime;
199 // timer.restart();
200}
201
202//=============================================================================================================
203
204void Coherency::compute(ConnectivitySettings::IntermediateTrialData& inputData,
205 MatrixXd& matPsdSum,
206 QVector<QPair<int, MatrixXcd>>& vecPairCsdSum,
207 QMutex& mutex,
208 int iNRows,
209 int iNFreqs,
210 int iNfft,
211 const QPair<MatrixXd, VectorXd>& tapers)
212{
213 // QElapsedTimer timer;
214 // qint64 iTime = 0;
215 // timer.start();
216
217 if (inputData.vecPairCsd.size() == iNRows) {
218 //qDebug() << "Coherency::compute - vecPairCsd were already computed for this trial.";
219 return;
220 }
221
222 //qDebug() << "Coherency::compute - vecPairCsdSum and matPsdSum are computed for this trial.";
223
224 // Substract mean, compute tapered spectra and PSD
225 // This code was copied and changed modified Utils/Spectra since we do not want to call the function due to time loss.
226 bool bNfftEven = false;
227 if (iNfft % 2 == 0) {
228 bNfftEven = true;
229 }
230
231 FFT<double> fft;
232 fft.SetFlag(fft.HalfSpectrum);
233
234 double denomPSD = tapers.second.cwiseAbs2().sum() / 2.0;
235
236 RowVectorXd vecInputFFT, rowData;
237 RowVectorXcd vecTmpFreq;
238
239 MatrixXcd matTapSpectrum(tapers.first.rows(), iNFreqs);
240
241 int i, j;
242
243 inputData.matPsd = MatrixXd(iNRows, m_iNumberBinAmount);
244
245 for (i = 0; i < iNRows; ++i) {
246 // Substract mean
247 rowData.array() = inputData.matData.row(i).array() - inputData.matData.row(i).mean();
248
249 // Calculate tapered spectra if not available already
250 if (inputData.vecTapSpectra.size() != iNRows) {
251 for (j = 0; j < tapers.first.rows(); j++) {
252 // Zero padd if necessary. The zero padding in Eigen's FFT is only working for column vectors.
253 if (rowData.cols() < iNfft) {
254 vecInputFFT.setZero(iNfft);
255 vecInputFFT.block(0, 0, 1, rowData.cols()) = rowData.cwiseProduct(tapers.first.row(j));
256 ;
257 } else {
258 vecInputFFT = rowData.cwiseProduct(tapers.first.row(j));
259 }
260
261 // FFT for freq domain returning the half spectrum and multiply taper weights
262 fft.fwd(vecTmpFreq, vecInputFFT, iNfft);
263 matTapSpectrum.row(j) = vecTmpFreq * tapers.second(j);
264 }
265
266 inputData.vecTapSpectra.append(matTapSpectrum);
267 }
268
269 // Compute PSD (average over tapers if necessary).
270 inputData.matPsd.row(i) = inputData.vecTapSpectra.at(i).block(0, m_iNumberBinStart, inputData.vecTapSpectra.at(i).rows(), m_iNumberBinAmount).cwiseAbs2().colwise().sum() / denomPSD;
271
272 // Divide first and last element by 2 due to half spectrum
273 if (m_iNumberBinStart == 0) {
274 inputData.matPsd.row(i)(0) /= 2.0;
275 }
276
277 if (bNfftEven && m_iNumberBinStart + m_iNumberBinAmount >= iNFreqs) {
278 inputData.matPsd.row(i).tail(1) /= 2.0;
279 }
280 }
281
282 mutex.lock();
283
284 if (matPsdSum.rows() == 0 || matPsdSum.cols() == 0) {
285 matPsdSum = inputData.matPsd;
286 } else {
287 matPsdSum += inputData.matPsd;
288 }
289
290 mutex.unlock();
291
292 // iTime = timer.elapsed();
293 // qWarning() << QThread::currentThreadId() << "Coherency::compute timer - compute - Tapered spectra and PSD (summing):" << iTime;
294 // timer.restart();
295
296 // Compute CSD
297 if (inputData.vecPairCsd.size() != iNRows) {
298 inputData.vecPairCsd.clear();
299
300 //MatrixXcd matCsd = MatrixXcd(iNRows, iNFreqs);
301 MatrixXcd matCsd = MatrixXcd(iNRows, m_iNumberBinAmount);
302
303 double denomCSD = sqrt(tapers.second.cwiseAbs2().sum()) * sqrt(tapers.second.cwiseAbs2().sum()) / 2.0;
304
305 for (i = 0; i < iNRows; ++i) {
306 for (j = i; j < iNRows; ++j) {
307 // Compute CSD (average over tapers if necessary)
308 matCsd.row(j) = inputData.vecTapSpectra.at(i).block(0, m_iNumberBinStart, inputData.vecTapSpectra.at(i).rows(), m_iNumberBinAmount).cwiseProduct(inputData.vecTapSpectra.at(j).block(0, m_iNumberBinStart, inputData.vecTapSpectra.at(j).rows(), m_iNumberBinAmount).conjugate()).colwise().sum() / denomCSD;
309
310 // Divide first and last element by 2 due to half spectrum
311 if (m_iNumberBinStart == 0) {
312 matCsd.row(j)(0) /= 2.0;
313 }
314
315 if (bNfftEven && m_iNumberBinStart + m_iNumberBinAmount >= iNFreqs) {
316 matCsd.row(j).tail(1) /= 2.0;
317 }
318 }
319
320 inputData.vecPairCsd.append(QPair<int, MatrixXcd>(i, matCsd));
321 }
322
323 mutex.lock();
324
325 if (vecPairCsdSum.isEmpty()) {
326 vecPairCsdSum = inputData.vecPairCsd;
327 } else {
328 for (j = 0; j < vecPairCsdSum.size(); ++j) {
329 vecPairCsdSum[j].second += inputData.vecPairCsd.at(j).second;
330 }
331 }
332
333 mutex.unlock();
334 }
335
336 // iTime = timer.elapsed();
337 // qWarning() << QThread::currentThreadId() << "Coherency::compute timer - compute - CSD summing:" << iTime;
338 // timer.restart();
339
340 //Do not store data to save memory
342 inputData.vecPairCsd.clear();
343 inputData.vecTapSpectra.clear();
344 }
345
346 // iTime = timer.elapsed();
347 // qWarning() << QThread::currentThreadId() << "Coherency::compute timer - compute - Deleting data:" << iTime;
348 // timer.restart();
349}
350
351//=============================================================================================================
352
353void Coherency::computePSDCSDAbs(QMutex& mutex,
354 Network& finalNetwork,
355 const QPair<int, MatrixXcd>& pairInput,
356 const MatrixXd& matPsdSum)
357{
358 MatrixXd matPSDtmp(matPsdSum.rows(), matPsdSum.cols());
359 RowVectorXd rowPsdSum = matPsdSum.row(pairInput.first);
360
361 for (int j = 0; j < matPSDtmp.rows(); ++j) {
362 matPSDtmp.row(j) = rowPsdSum.cwiseProduct(matPsdSum.row(j));
363 }
364
365 // Average. Note that the number of trials cancel each other out.
366 MatrixXcd matCohy = pairInput.second.cwiseQuotient(matPSDtmp.cwiseSqrt());
367
368 QSharedPointer<NetworkEdge> pEdge;
369 MatrixXd matWeight;
370 int j;
371 int i = pairInput.first;
372
373 for (j = i; j < matCohy.rows(); ++j) {
374 matWeight = matCohy.row(j).cwiseAbs().transpose();
375 pEdge = QSharedPointer<NetworkEdge>(new NetworkEdge(i, j, matWeight));
376
377 mutex.lock();
378 finalNetwork.getNodeAt(i)->append(pEdge);
379 finalNetwork.getNodeAt(j)->append(pEdge);
380 finalNetwork.append(pEdge);
381 mutex.unlock();
382 }
383}
384
385//=============================================================================================================
386
387void Coherency::computePSDCSDImag(QMutex& mutex,
388 Network& finalNetwork,
389 const QPair<int, MatrixXcd>& pairInput,
390 const MatrixXd& matPsdSum)
391{
392 MatrixXd matPSDtmp(matPsdSum.rows(), matPsdSum.cols());
393 RowVectorXd rowPsdSum = matPsdSum.row(pairInput.first);
394
395 for (int j = 0; j < matPSDtmp.rows(); ++j) {
396 matPSDtmp.row(j) = rowPsdSum.cwiseProduct(matPsdSum.row(j));
397 }
398
399 MatrixXcd matCohy = pairInput.second.cwiseQuotient(matPSDtmp.cwiseSqrt());
400
401 QSharedPointer<NetworkEdge> pEdge;
402 MatrixXd matWeight;
403 int j;
404 int i = pairInput.first;
405
406 for (j = i; j < matCohy.rows(); ++j) {
407 matWeight = matCohy.row(j).imag().transpose();
408 pEdge = QSharedPointer<NetworkEdge>(new NetworkEdge(i, j, matWeight));
409
410 mutex.lock();
411 finalNetwork.getNodeAt(i)->append(pEdge);
412 finalNetwork.getNodeAt(j)->append(pEdge);
413 finalNetwork.append(pEdge);
414 mutex.unlock();
415 }
416}
Multi-taper spectral estimation: tapered FFT, power and cross-spectral density, DPSS weighting.
Node of a connectivity CONNECTIVITYLIB::Network; carries a 3D position and the lists of incident (in ...
Weighted edge between two CONNECTIVITYLIB::NetworkNode instances; stores the full per-frequency weigh...
Graph container that stores the result of one functional-connectivity metric as nodes (sources/sensor...
Complex coherency between every channel pair and its two reductions: magnitude (coherence) and imagin...
Functional connectivity metrics (coherence, PLV, cross-correlation, etc.).
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Aggregates trial data, spectral cache and node geometry shared by all CONNECTIVITYLIB metrics.
QList< IntermediateTrialData > & getTrialData()
const IntermediateTrialData & at(int i) const
Per-trial intermediate frequency-domain data used during connectivity computation.
QVector< QPair< int, Eigen::MatrixXcd > > vecPairCsdSum
static void calculateImag(Network &finalNetwork, ConnectivitySettings &connectivitySettings)
static void calculateAbs(Network &finalNetwork, ConnectivitySettings &connectivitySettings)
Definition coherency.cpp:62
Graph container for one connectivity metric; nodes + weighted edges + threshold/visualisation state.
Definition network.h:106
void append(QSharedPointer< NetworkEdge > newEdge)
QSharedPointer< NetworkNode > getNodeAt(int i)
Definition network.cpp:143
static QPair< Eigen::MatrixXd, Eigen::VectorXd > generateTapers(int iSignalLength, const QString &sWindowType="hanning")
Definition spectral.cpp:270