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 } else {
257 vecInputFFT = rowData.cwiseProduct(tapers.first.row(j));
258 }
259
260 // FFT for freq domain returning the half spectrum and multiply taper weights
261 fft.fwd(vecTmpFreq, vecInputFFT, iNfft);
262 matTapSpectrum.row(j) = vecTmpFreq * tapers.second(j);
263 }
264
265 inputData.vecTapSpectra.append(matTapSpectrum);
266 }
267
268 // Compute PSD (average over tapers if necessary).
269 inputData.matPsd.row(i) = inputData.vecTapSpectra.at(i).block(0,m_iNumberBinStart,inputData.vecTapSpectra.at(i).rows(),m_iNumberBinAmount).cwiseAbs2().colwise().sum() / denomPSD;
270
271 // Divide first and last element by 2 due to half spectrum
272 if(m_iNumberBinStart == 0) {
273 inputData.matPsd.row(i)(0) /= 2.0;
274 }
275
276 if(bNfftEven && m_iNumberBinStart + m_iNumberBinAmount >= iNFreqs) {
277 inputData.matPsd.row(i).tail(1) /= 2.0;
278 }
279 }
280
281 mutex.lock();
282
283 if(matPsdSum.rows() == 0 || matPsdSum.cols() == 0) {
284 matPsdSum = inputData.matPsd;
285 } else {
286 matPsdSum += inputData.matPsd;
287 }
288
289 mutex.unlock();
290
291// iTime = timer.elapsed();
292// qWarning() << QThread::currentThreadId() << "Coherency::compute timer - compute - Tapered spectra and PSD (summing):" << iTime;
293// timer.restart();
294
295 // Compute CSD
296 if(inputData.vecPairCsd.size() != iNRows) {
297 inputData.vecPairCsd.clear();
298
299 //MatrixXcd matCsd = MatrixXcd(iNRows, iNFreqs);
300 MatrixXcd matCsd = MatrixXcd(iNRows, m_iNumberBinAmount);
301
302 double denomCSD = sqrt(tapers.second.cwiseAbs2().sum()) * sqrt(tapers.second.cwiseAbs2().sum()) / 2.0;
303
304 for (i = 0; i < iNRows; ++i) {
305 for (j = i; j < iNRows; ++j) {
306 // Compute CSD (average over tapers if necessary)
307 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;
308
309 // Divide first and last element by 2 due to half spectrum
310 if(m_iNumberBinStart == 0) {
311 matCsd.row(j)(0) /= 2.0;
312 }
313
314 if(bNfftEven && m_iNumberBinStart + m_iNumberBinAmount >= iNFreqs) {
315 matCsd.row(j).tail(1) /= 2.0;
316 }
317 }
318
319 inputData.vecPairCsd.append(QPair<int,MatrixXcd>(i,matCsd));
320 }
321
322 mutex.lock();
323
324 if(vecPairCsdSum.isEmpty()) {
325 vecPairCsdSum = inputData.vecPairCsd;
326 } else {
327 for (j = 0; j < vecPairCsdSum.size(); ++j) {
328 vecPairCsdSum[j].second += inputData.vecPairCsd.at(j).second;
329 }
330 }
331
332 mutex.unlock();
333 }
334
335// iTime = timer.elapsed();
336// qWarning() << QThread::currentThreadId() << "Coherency::compute timer - compute - CSD summing:" << iTime;
337// timer.restart();
338
339 //Do not store data to save memory
341 inputData.vecPairCsd.clear();
342 inputData.vecTapSpectra.clear();
343 }
344
345// iTime = timer.elapsed();
346// qWarning() << QThread::currentThreadId() << "Coherency::compute timer - compute - Deleting data:" << iTime;
347// timer.restart();
348}
349
350//=============================================================================================================
351
352void Coherency::computePSDCSDAbs(QMutex& mutex,
353 Network& finalNetwork,
354 const QPair<int,MatrixXcd>& pairInput,
355 const MatrixXd& matPsdSum)
356{
357 MatrixXd matPSDtmp(matPsdSum.rows(), matPsdSum.cols());
358 RowVectorXd rowPsdSum = matPsdSum.row(pairInput.first);
359
360 for(int j = 0; j < matPSDtmp.rows(); ++j) {
361 matPSDtmp.row(j) = rowPsdSum.cwiseProduct(matPsdSum.row(j));
362 }
363
364 // Average. Note that the number of trials cancel each other out.
365 MatrixXcd matCohy = pairInput.second.cwiseQuotient(matPSDtmp.cwiseSqrt());
366
367 QSharedPointer<NetworkEdge> pEdge;
368 MatrixXd matWeight;
369 int j;
370 int i = pairInput.first;
371
372 for(j = i; j < matCohy.rows(); ++j) {
373 matWeight = matCohy.row(j).cwiseAbs().transpose();
374 pEdge = QSharedPointer<NetworkEdge>(new NetworkEdge(i, j, matWeight));
375
376 mutex.lock();
377 finalNetwork.getNodeAt(i)->append(pEdge);
378 finalNetwork.getNodeAt(j)->append(pEdge);
379 finalNetwork.append(pEdge);
380 mutex.unlock();
381 }
382}
383
384//=============================================================================================================
385
386void Coherency::computePSDCSDImag(QMutex& mutex,
387 Network& finalNetwork,
388 const QPair<int,MatrixXcd>& pairInput,
389 const MatrixXd& matPsdSum)
390{
391 MatrixXd matPSDtmp(matPsdSum.rows(), matPsdSum.cols());
392 RowVectorXd rowPsdSum = matPsdSum.row(pairInput.first);
393
394 for(int j = 0; j < matPSDtmp.rows(); ++j) {
395 matPSDtmp.row(j) = rowPsdSum.cwiseProduct(matPsdSum.row(j));
396 }
397
398 MatrixXcd matCohy = pairInput.second.cwiseQuotient(matPSDtmp.cwiseSqrt());
399
400 QSharedPointer<NetworkEdge> pEdge;
401 MatrixXd matWeight;
402 int j;
403 int i = pairInput.first;
404
405 for(j = i; j < matCohy.rows(); ++j) {
406 matWeight = matCohy.row(j).imag().transpose();
407 pEdge = QSharedPointer<NetworkEdge>(new NetworkEdge(i, j, matWeight));
408
409 mutex.lock();
410 finalNetwork.getNodeAt(i)->append(pEdge);
411 finalNetwork.getNodeAt(j)->append(pEdge);
412 finalNetwork.append(pEdge);
413 mutex.unlock();
414 }
415}
Complex coherency between every channel pair and its two reductions: magnitude (coherence) and imagin...
Weighted edge between two NetworkNode instances; stores the full per-frequency weight matrix and the ...
Node of a connectivity Network; carries a 3D position and the lists of incident (in / out,...
Graph container that stores the result of one functional-connectivity metric as nodes (sources/sensor...
Multi-taper spectral estimation: tapered FFT, power and cross-spectral density, DPSS weighting.
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:98
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