v2.0.0
Loading...
Searching...
No Matches
phaselagindex.cpp
Go to the documentation of this file.
1//=============================================================================================================
15
16//=============================================================================================================
17// INCLUDES
18//=============================================================================================================
19
20#include "phaselagindex.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{
64// QElapsedTimer timer;
65// qint64 iTime = 0;
66// timer.start();
67
68 Network finalNetwork("PLI");
69
70 if(connectivitySettings.isEmpty()) {
71 qDebug() << "PhaseLagIndex::calculate - Input data is empty";
72 return finalNetwork;
73 }
74
76 connectivitySettings.clearIntermediateData();
77 }
78
79 finalNetwork.setSamplingFrequency(connectivitySettings.getSamplingFrequency());
80
81 #ifdef EIGEN_FFTW_DEFAULT
82 fftw_make_planner_thread_safe();
83 #endif
84
85 //Create nodes
86 int iNRows = connectivitySettings.at(0).matData.rows();
87 RowVectorXf rowVert = RowVectorXf::Zero(3);
88
89 for(int i = 0; i < iNRows; ++i) {
90 rowVert = RowVectorXf::Zero(3);
91 if(connectivitySettings.getNodePositions().rows() != 0 && i < connectivitySettings.getNodePositions().rows()) {
92 rowVert(0) = connectivitySettings.getNodePositions().row(i)(0);
93 rowVert(1) = connectivitySettings.getNodePositions().row(i)(1);
94 rowVert(2) = connectivitySettings.getNodePositions().row(i)(2);
95 }
96
97 finalNetwork.append(NetworkNode::SPtr(new NetworkNode(i, rowVert)));
98 }
99
100 // Check that iNfft >= signal length
101 int iSignalLength = connectivitySettings.at(0).matData.cols();
102 int iNfft = connectivitySettings.getFFTSize();
103
104 // Generate tapers
105 QPair<MatrixXd, VectorXd> tapers = Spectral::generateTapers(iSignalLength, connectivitySettings.getWindowType());
106
107 // Initialize
108 int iNFreqs = int(floor(iNfft / 2.0)) + 1;
109
110 // Check if start and bin amount need to be reset to full spectrum
111 if(m_iNumberBinStart == -1 ||
112 m_iNumberBinAmount == -1 ||
113 m_iNumberBinStart > iNFreqs ||
114 m_iNumberBinAmount > iNFreqs ||
116 qDebug() << "PhaseLagIndex::calculate - Resetting to full spectrum";
119 }
120
121 // Pass information about the FFT length. Use iNFreqs because we only use the half spectrum
122 finalNetwork.setFFTSize(iNFreqs);
124
125 QMutex mutex;
126
127 std::function<void(ConnectivitySettings::IntermediateTrialData&)> computeLambda = [&](ConnectivitySettings::IntermediateTrialData& inputData) {
128 compute(inputData,
129 connectivitySettings.getIntermediateSumData().vecPairCsdSum,
130 connectivitySettings.getIntermediateSumData().vecPairCsdImagSignSum,
131 mutex,
132 iNRows,
133 iNFreqs,
134 iNfft,
135 tapers);
136 };
137
138// iTime = timer.elapsed();
139// qWarning() << "Preparation" << iTime;
140// timer.restart();
141
142 // Compute DSWPLV in parallel for all trials
143 QFuture<void> result = QtConcurrent::map(connectivitySettings.getTrialData(),
144 computeLambda);
145 result.waitForFinished();
146
147// iTime = timer.elapsed();
148// qWarning() << "ComputeSpectraPSDCSD" << iTime;
149// timer.restart();
150
151 // Compute PLI
152 computePLI(connectivitySettings,
153 finalNetwork);
154
155// iTime = timer.elapsed();
156// qWarning() << "Compute" << iTime;
157// timer.restart();
158
159 return finalNetwork;
160}
161
162//=============================================================================================================
163
165 QVector<QPair<int,MatrixXcd> >& vecPairCsdSum,
166 QVector<QPair<int,MatrixXd> >& vecPairCsdImagSignSum,
167 QMutex& mutex,
168 int iNRows,
169 int iNFreqs,
170 int iNfft,
171 const QPair<MatrixXd, VectorXd>& tapers)
172{
173 if(inputData.vecPairCsdImagSign.size() == iNRows) {
174 //qDebug() << "PhaseLagIndex::compute - vecPairCsdImagSign was already computed for this trial.";
175 return;
176 }
177
178 int i,j;
179
180 // Calculate tapered spectra if not available already
181 // This code was copied and changed modified Utils/Spectra since we do not want to call the function due to time loss.
182 if(inputData.vecTapSpectra.isEmpty()) {
183 RowVectorXd vecInputFFT, rowData;
184 RowVectorXcd vecTmpFreq;
185
186 MatrixXcd matTapSpectrum(tapers.first.rows(), iNFreqs);
187
188 FFT<double> fft;
189 fft.SetFlag(fft.HalfSpectrum);
190
191 for (i = 0; i < iNRows; ++i) {
192 // Substract mean
193 rowData.array() = inputData.matData.row(i).array() - inputData.matData.row(i).mean();
194
195 // Calculate tapered spectra
196 for(j = 0; j < tapers.first.rows(); j++) {
197 // Zero padd if necessary. The zero padding in Eigen's FFT is only working for column vectors.
198 if (rowData.cols() < iNfft) {
199 vecInputFFT.setZero(iNfft);
200 vecInputFFT.block(0,0,1,rowData.cols()) = rowData.cwiseProduct(tapers.first.row(j));;
201 } else {
202 vecInputFFT = rowData.cwiseProduct(tapers.first.row(j));
203 }
204
205 // FFT for freq domain returning the half spectrum and multiply taper weights
206 fft.fwd(vecTmpFreq, vecInputFFT, iNfft);
207 matTapSpectrum.row(j) = vecTmpFreq * tapers.second(j);
208 }
209
210 inputData.vecTapSpectra.append(matTapSpectrum);
211 }
212 }
213
214 // Compute CSD
215 if(inputData.vecPairCsd.isEmpty()) {
216 MatrixXcd matCsd = MatrixXcd(iNRows, m_iNumberBinAmount);
217
218 double denomCSD = sqrt(tapers.second.cwiseAbs2().sum()) * sqrt(tapers.second.cwiseAbs2().sum()) / 2.0;
219
220 bool bNfftEven = false;
221 if (iNfft % 2 == 0){
222 bNfftEven = true;
223 }
224
225 for (i = 0; i < iNRows; ++i) {
226 for (j = i; j < iNRows; ++j) {
227 // Compute CSD (average over tapers if necessary)
228 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;
229
230 // Divide first and last element by 2 due to half spectrum
231 if(m_iNumberBinStart == 0) {
232 matCsd.row(j)(0) /= 2.0;
233 }
234
235 if(bNfftEven && m_iNumberBinStart + m_iNumberBinAmount >= iNFreqs) {
236 matCsd.row(j).tail(1) /= 2.0;
237 }
238 }
239
240 inputData.vecPairCsd.append(QPair<int,MatrixXcd>(i,matCsd));
241 inputData.vecPairCsdImagSign.append(QPair<int,MatrixXd>(i,matCsd.imag().cwiseSign()));
242 }
243
244 mutex.lock();
245
246 if(vecPairCsdSum.isEmpty()) {
247 vecPairCsdSum = inputData.vecPairCsd;
248 vecPairCsdImagSignSum = inputData.vecPairCsdImagSign;
249 } else {
250 for (int j = 0; j < vecPairCsdSum.size(); ++j) {
251 vecPairCsdSum[j].second += inputData.vecPairCsd.at(j).second;
252 vecPairCsdImagSignSum[j].second += inputData.vecPairCsdImagSign.at(j).second;
253 }
254 }
255
256 mutex.unlock();
257 } else {
258 if(inputData.vecPairCsdImagSign.isEmpty()) {
259 for (i = 0; i < inputData.vecPairCsd.size(); ++i) {
260 inputData.vecPairCsdImagSign.append(QPair<int,MatrixXd>(i,inputData.vecPairCsd.at(i).second.imag().cwiseSign()));
261 }
262
263 mutex.lock();
264
265 if(vecPairCsdImagSignSum.isEmpty()) {
266 vecPairCsdImagSignSum = inputData.vecPairCsdImagSign;
267 } else {
268 for (int j = 0; j < vecPairCsdImagSignSum.size(); ++j) {
269 vecPairCsdImagSignSum[j].second += inputData.vecPairCsdImagSign.at(j).second;
270 }
271 }
272
273 mutex.unlock();
274 }
275 }
276
278 inputData.vecPairCsd.clear();
279 inputData.vecTapSpectra.clear();
280 inputData.vecPairCsdImagSign.clear();
281 }
282}
283
284//=============================================================================================================
285
287 Network& finalNetwork)
288{
289 // Compute final PLI and create Network
290 MatrixXd matNom;
291 MatrixXd matWeight;
292 QSharedPointer<NetworkEdge> pEdge;
293 int j;
294
295 for (int i = 0; i < connectivitySettings.getIntermediateSumData().vecPairCsdImagSignSum.size(); ++i) {
296 matNom = connectivitySettings.getIntermediateSumData().vecPairCsdImagSignSum.at(i).second.cwiseAbs() / connectivitySettings.size();
297
298 for(j = i; j < matNom.rows(); ++j) {
299 matWeight = matNom.row(j).transpose();
300
301 pEdge = QSharedPointer<NetworkEdge>(new NetworkEdge(i, j, matWeight));
302
303 finalNetwork.getNodeAt(i)->append(pEdge);
304 finalNetwork.getNodeAt(j)->append(pEdge);
305 finalNetwork.append(pEdge);
306 }
307 }
308}
309
Phase Lag Index (Stam, Nolte & Daffertshofer 2007) between every channel pair.
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
const Eigen::MatrixX3f & getNodePositions() const
Per-trial intermediate frequency-domain data used during connectivity computation.
QVector< QPair< int, Eigen::MatrixXd > > vecPairCsdImagSignSum
QVector< QPair< int, Eigen::MatrixXcd > > vecPairCsdSum
static Network calculate(ConnectivitySettings &connectivitySettings)
static void compute(ConnectivitySettings::IntermediateTrialData &inputData, QVector< QPair< int, Eigen::MatrixXcd > > &vecPairCsdSum, QVector< QPair< int, Eigen::MatrixXd > > &vecPairCsdImagSignSum, QMutex &mutex, int iNRows, int iNFreqs, int iNfft, const QPair< Eigen::MatrixXd, Eigen::VectorXd > &tapers)
static void computePLI(ConnectivitySettings &connectivitySettings, Network &finalNetwork)
Graph container for one connectivity metric; nodes + weighted edges + threshold/visualisation state.
Definition network.h:98
void setUsedFreqBins(int iNumberFreqBins)
Definition network.cpp:493
void append(QSharedPointer< NetworkEdge > newEdge)
void setFFTSize(int iFFTSize)
Definition network.cpp:500
void setSamplingFrequency(float fSFreq)
Definition network.cpp:479
QSharedPointer< NetworkNode > getNodeAt(int i)
Definition network.cpp:143
Weighted, directional edge in a Network; carries per-frequency weights plus a band-averaged scalar.
Definition networkedge.h:82
Graph node carrying a 3D position and its incident in/out, full/thresholded edge lists.
Definition networknode.h:80
QSharedPointer< NetworkNode > SPtr
Definition networknode.h:83
static QPair< Eigen::MatrixXd, Eigen::VectorXd > generateTapers(int iSignalLength, const QString &sWindowType="hanning")
Definition spectral.cpp:270