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