v2.0.0
Loading...
Searching...
No Matches
inv_rap_music.cpp
Go to the documentation of this file.
1//=============================================================================================================
19
20//=============================================================================================================
21// INCLUDES
22//=============================================================================================================
23
24#include "inv_rap_music.h"
25
26#include <math/numerics.h>
27
28#include <QDebug>
29
30#ifdef _OPENMP
31#include <omp.h>
32#endif
33
34#include <stdexcept>
35//=============================================================================================================
36// USED NAMESPACES
37//=============================================================================================================
38
39using namespace INVLIB;
40using namespace MNELIB;
41using namespace FIFFLIB;
42using namespace UTILSLIB;
43
44//=============================================================================================================
45// DEFINE MEMBER METHODS
46//=============================================================================================================
47
60
61//=============================================================================================================
62
63InvRapMusic::InvRapMusic(MNEForwardSolution& p_pFwd, bool p_bSparsed, int p_iN, double p_dThr)
64: m_iN(0)
65, m_dThreshold(0)
70, m_bIsInit(false)
72, m_fStcOverlap(-1)
73{
74 //Init
75 init(p_pFwd, p_bSparsed, p_iN, p_dThr);
76}
77
78//=============================================================================================================
79
83
84//=============================================================================================================
85
86bool InvRapMusic::init(MNEForwardSolution& p_pFwd, bool p_bSparsed, int p_iN, double p_dThr)
87{
88//Get available thread number
89#ifdef _OPENMP
90 qDebug() << "OpenMP enabled";
91 m_iMaxNumThreads = omp_get_max_threads();
92#else
93 qDebug() << "OpenMP disabled (to enable it: VS2010->Project Properties->C/C++->Language, then modify OpenMP Support)";
95#endif
96 qDebug() << "Available Threats: " << m_iMaxNumThreads;
97
98 //Initialize RAP MUSIC
99 qDebug() << "##### Initialization RAP MUSIC started ######\n\n";
100
101 m_iN = p_iN;
102 m_dThreshold = p_dThr;
103
104 // //Grid check
105 // if(p_pMatGrid != nullptr)
106 // {
107 // if ( p_pMatGrid->rows() != p_pMatLeadField->cols() / 3 )
108 // {
109 // qDebug() << "Grid does not fit to given Lead Field!\n";
110 // return false;
111 // }
112 // }
113
114 // m_pMatGrid = p_pMatGrid;
115
116 //Lead Field check
117 if (p_pFwd.sol->data.cols() % 3 != 0) {
118 qDebug() << "Gain matrix is not associated with a 3D grid!\n";
119 return false;
120 }
121
122 m_iNumGridPoints = p_pFwd.sol->data.cols() / 3;
123
124 m_iNumChannels = p_pFwd.sol->data.rows();
125
126 // m_pMappedMatLeadField = new Eigen::Map<MatrixXT>
127 // ( p_pMatLeadField->data(),
128 // p_pMatLeadField->rows(),
129 // p_pMatLeadField->cols() );
130
131 m_ForwardSolution = p_pFwd;
132
133 //##### Calc lead field combination #####
134
135 qDebug() << "Calculate gain matrix combinations. \n";
136
138
140
142
143 qDebug() << "Gain matrix combinations calculated. \n\n";
144
145 //##### Calc lead field combination end #####
146
147 qDebug() << "Number of grid points: " << m_iNumGridPoints << "\n\n";
148
149 qDebug() << "Number of combinated points: " << m_iNumLeadFieldCombinations << "\n\n";
150
151 qDebug() << "Number of sources to find: " << m_iN << "\n\n";
152
153 qDebug() << "Threshold: " << m_dThreshold << "\n\n";
154
155 //Init end
156
157 qDebug() << "##### Initialization RAP MUSIC completed ######\n\n\n";
158
159 Q_UNUSED(p_bSparsed);
160
161 m_bIsInit = true;
162
163 return m_bIsInit;
164}
165
166//=============================================================================================================
167
168const char* InvRapMusic::getName() const
169{
170 return "RAP MUSIC";
171}
172
173//=============================================================================================================
174
176{
177 return m_ForwardSolution.src;
178}
179
180//=============================================================================================================
181
182InvSourceEstimate InvRapMusic::calculateInverse(const FiffEvoked& p_fiffEvoked, bool pick_normal)
183{
184 Q_UNUSED(pick_normal);
185
186 InvSourceEstimate p_sourceEstimate;
187
188 if (p_fiffEvoked.data.rows() != m_iNumChannels) {
189 qDebug() << "Number of FiffEvoked channels (" << p_fiffEvoked.data.rows() << ") doesn't match the number of channels (" << m_iNumChannels << ") of the forward solution.";
190 return p_sourceEstimate;
191 }
192 // else
193 // qDebug() << "Number of FiffEvoked channels (" << p_fiffEvoked.data.rows() << ") matchs the number of channels (" << m_iNumChannels << ") of the forward solution.";
194
195 //
196 // Rap MUSIC Source estimate
197 //
198 p_sourceEstimate.data = MatrixXd::Zero(m_ForwardSolution.nsource, p_fiffEvoked.data.cols());
199
200 //Results
201 p_sourceEstimate.vertices = VectorXi(m_ForwardSolution.src[0].vertno.size() + m_ForwardSolution.src[1].vertno.size());
202 p_sourceEstimate.vertices << m_ForwardSolution.src[0].vertno, m_ForwardSolution.src[1].vertno;
203
204 p_sourceEstimate.times = p_fiffEvoked.times;
205 p_sourceEstimate.tmin = p_fiffEvoked.times[0];
206 p_sourceEstimate.tstep = p_fiffEvoked.times[1] - p_fiffEvoked.times[0];
207
208 if (m_iSamplesStcWindow <= 3 || m_iSamplesStcWindow >= p_fiffEvoked.data.cols()) // no window set, or it spans the data
209 {
210 QList<InvDipolePair<double>> t_RapDipoles;
211 calculateInverse(p_fiffEvoked.data, t_RapDipoles);
212
213 for (qint32 i = 0; i < t_RapDipoles.size(); ++i) {
214 double dip1 = sqrt(pow(t_RapDipoles[i].m_Dipole1.phi_x(), 2) +
215 pow(t_RapDipoles[i].m_Dipole1.phi_y(), 2) +
216 pow(t_RapDipoles[i].m_Dipole1.phi_z(), 2)) *
217 t_RapDipoles[i].m_vCorrelation;
218
219 double dip2 = sqrt(pow(t_RapDipoles[i].m_Dipole2.phi_x(), 2) +
220 pow(t_RapDipoles[i].m_Dipole2.phi_y(), 2) +
221 pow(t_RapDipoles[i].m_Dipole2.phi_z(), 2)) *
222 t_RapDipoles[i].m_vCorrelation;
223
224 RowVectorXd dip1Time = RowVectorXd::Constant(p_fiffEvoked.data.cols(), dip1);
225 RowVectorXd dip2Time = RowVectorXd::Constant(p_fiffEvoked.data.cols(), dip2);
226
227 p_sourceEstimate.data.block(t_RapDipoles[i].m_iIdx1, 0, 1, p_fiffEvoked.data.cols()) = dip1Time;
228 p_sourceEstimate.data.block(t_RapDipoles[i].m_iIdx2, 0, 1, p_fiffEvoked.data.cols()) = dip2Time;
229 }
230 } else {
231 bool first = true;
232 bool last = false;
233
234 qint32 t_iNumSensors = p_fiffEvoked.data.rows();
235 qint32 t_iNumSteps = p_fiffEvoked.data.cols();
236
237 qint32 t_iSamplesOverlap = static_cast<qint32>(floor(static_cast<float>(m_iSamplesStcWindow) * m_fStcOverlap));
238 qint32 t_iSamplesDiscard = t_iSamplesOverlap / 2;
239
240 MatrixXd data = MatrixXd::Zero(t_iNumSensors, m_iSamplesStcWindow);
241
242 qint32 curSample = 0;
243 qint32 curResultSample = 0;
244 qint32 stcWindowSize = m_iSamplesStcWindow - 2 * t_iSamplesDiscard;
245
246 while (!last) {
247 QList<InvDipolePair<double>> t_RapDipoles;
248
249 //Data
250 if (curSample + m_iSamplesStcWindow >= t_iNumSteps) //last
251 {
252 last = true;
253 data = p_fiffEvoked.data.block(0, p_fiffEvoked.data.cols() - m_iSamplesStcWindow, t_iNumSensors, m_iSamplesStcWindow);
254 } else
255 data = p_fiffEvoked.data.block(0, curSample, t_iNumSensors, m_iSamplesStcWindow);
256
257 curSample += (m_iSamplesStcWindow - t_iSamplesOverlap);
258 if (first)
259 curSample -= t_iSamplesDiscard; //shift on start t_iSamplesDiscard backwards
260
261 //Calculate
262 calculateInverse(data, t_RapDipoles);
263
264 //Assign Result
265 if (last)
266 stcWindowSize = p_sourceEstimate.data.cols() - curResultSample;
267
268 for (qint32 i = 0; i < t_RapDipoles.size(); ++i) {
269 double dip1 = sqrt(pow(t_RapDipoles[i].m_Dipole1.phi_x(), 2) +
270 pow(t_RapDipoles[i].m_Dipole1.phi_y(), 2) +
271 pow(t_RapDipoles[i].m_Dipole1.phi_z(), 2)) *
272 t_RapDipoles[i].m_vCorrelation;
273
274 double dip2 = sqrt(pow(t_RapDipoles[i].m_Dipole2.phi_x(), 2) +
275 pow(t_RapDipoles[i].m_Dipole2.phi_y(), 2) +
276 pow(t_RapDipoles[i].m_Dipole2.phi_z(), 2)) *
277 t_RapDipoles[i].m_vCorrelation;
278
279 RowVectorXd dip1Time = RowVectorXd::Constant(stcWindowSize, dip1);
280 RowVectorXd dip2Time = RowVectorXd::Constant(stcWindowSize, dip2);
281
282 p_sourceEstimate.data.block(t_RapDipoles[i].m_iIdx1, curResultSample, 1, stcWindowSize) = dip1Time;
283 p_sourceEstimate.data.block(t_RapDipoles[i].m_iIdx2, curResultSample, 1, stcWindowSize) = dip2Time;
284 }
285
286 curResultSample += stcWindowSize;
287
288 if (first)
289 first = false;
290 }
291 }
292
293 return p_sourceEstimate;
294}
295
296//=============================================================================================================
297
298InvSourceEstimate InvRapMusic::calculateInverse(const MatrixXd& data, float tmin, float tstep, bool pick_normal) const
299{
300 Q_UNUSED(pick_normal);
301
302 InvSourceEstimate p_sourceEstimate;
303
304 if (data.rows() != m_iNumChannels) {
305 qDebug() << "Number of FiffEvoked channels (" << data.rows() << ") doesn't match the number of channels (" << m_iNumChannels << ") of the forward solution.";
306 return p_sourceEstimate;
307 }
308 // else
309 // qDebug() << "Number of FiffEvoked channels (" << data.rows() << ") matchs the number of channels (" << m_iNumChannels << ") of the forward solution.";
310
311 //
312 // Rap MUSIC Source estimate
313 //
314 p_sourceEstimate.data = MatrixXd::Zero(m_ForwardSolution.nsource, data.cols());
315
316 //Results
317 p_sourceEstimate.vertices = VectorXi(m_ForwardSolution.src[0].vertno.size() + m_ForwardSolution.src[1].vertno.size());
318 p_sourceEstimate.vertices << m_ForwardSolution.src[0].vertno, m_ForwardSolution.src[1].vertno;
319
320 p_sourceEstimate.times = RowVectorXf::Zero(data.cols());
321 p_sourceEstimate.times[0] = tmin;
322 for (qint32 i = 1; i < p_sourceEstimate.times.size(); ++i)
323 p_sourceEstimate.times[i] = p_sourceEstimate.times[i - 1] + tstep;
324 p_sourceEstimate.tmin = tmin;
325 p_sourceEstimate.tstep = tstep;
326
327 QList<InvDipolePair<double>> t_RapDipoles;
328 calculateInverse(data, t_RapDipoles);
329
330 for (qint32 i = 0; i < t_RapDipoles.size(); ++i) {
331 double dip1 = sqrt(pow(t_RapDipoles[i].m_Dipole1.phi_x(), 2) +
332 pow(t_RapDipoles[i].m_Dipole1.phi_y(), 2) +
333 pow(t_RapDipoles[i].m_Dipole1.phi_z(), 2)) *
334 t_RapDipoles[i].m_vCorrelation;
335
336 double dip2 = sqrt(pow(t_RapDipoles[i].m_Dipole2.phi_x(), 2) +
337 pow(t_RapDipoles[i].m_Dipole2.phi_y(), 2) +
338 pow(t_RapDipoles[i].m_Dipole2.phi_z(), 2)) *
339 t_RapDipoles[i].m_vCorrelation;
340
341 RowVectorXd dip1Time = RowVectorXd::Constant(data.cols(), dip1);
342 RowVectorXd dip2Time = RowVectorXd::Constant(data.cols(), dip2);
343
344 p_sourceEstimate.data.block(t_RapDipoles[i].m_iIdx1, 0, 1, data.cols()) = dip1Time;
345 p_sourceEstimate.data.block(t_RapDipoles[i].m_iIdx2, 0, 1, data.cols()) = dip2Time;
346 }
347
348 return p_sourceEstimate;
349}
350
351//=============================================================================================================
352
353InvSourceEstimate InvRapMusic::calculateInverse(const MatrixXd& p_matMeasurement, QList<InvDipolePair<double>>& p_RapDipoles) const
354{
355 InvSourceEstimate p_SourceEstimate;
356
357 //if not initialized -> break
358 if (!m_bIsInit) {
359 throw std::logic_error("RAP MUSIC was not initialized");
360 }
361
362 //Test if data are correct
363 if (p_matMeasurement.rows() != m_iNumChannels) {
364 throw std::invalid_argument("Lead field channels do not match number of measurement channels");
365 }
366
367 //Inits
368 //Stop the time for benchmark purpose
369 clock_t start, end;
370 start = clock();
371
372 // //Map HPCMatrix to Eigen Matrix
373 // Eigen::Map<MatrixXT>
374 // t_MappedMatMeasurement( p_pMatMeasurement->data(),
375 // p_pMatMeasurement->rows(),
376 // p_pMatMeasurement->cols() );
377
378 //Calculate the signal subspace
379 MatrixXT t_matPhi_s;
380 int t_r = calcPhi_s(p_matMeasurement, t_matPhi_s);
381
382 int t_iMaxSearch = m_iN < t_r ? m_iN : t_r; //The smallest of Rank and Iterations
383
384 if (t_r < m_iN) {
385 qDebug() << "Warning: Rank " << t_r << " of the measurement data is smaller than the " << m_iN;
386 qDebug() << " sources to find.";
387 qDebug() << " Searching now for " << t_iMaxSearch << " correlated sources.";
388 qDebug();
389 }
390
391 //Create Orthogonal Projector
392 //OrthProj
393 MatrixXT t_matOrthProj(m_iNumChannels, m_iNumChannels);
394 t_matOrthProj.setIdentity();
395
396 //A_k_1
397 MatrixXT t_matA_k_1(m_iNumChannels, t_iMaxSearch);
398 t_matA_k_1.setZero();
399
400 // if (m_pMatGrid != nullptr)
401 // {
402 // if(p_pRapDipoles != nullptr)
403 // p_pRapDipoles->initRapDipoles(m_pMatGrid);
404 // else
405 // p_pRapDipoles = new RapDipoles<T>(m_pMatGrid);
406 // }
407 // else
408 // {
409 // if(p_pRapDipoles != nullptr)
410 // delete p_pRapDipoles;
411
412 // p_pRapDipoles = new RapDipoles<T>();
413 // }
414 p_RapDipoles.clear();
415
416 qDebug() << "##### Calculation of RAP MUSIC started ######\n\n";
417
418 MatrixXT t_matProj_Phi_s(t_matOrthProj.rows(), t_matPhi_s.cols());
419 //new Version: Calculate projection before
420 MatrixXT t_matProj_LeadField(m_ForwardSolution.sol->data.rows(), m_ForwardSolution.sol->data.cols());
421
422 for (int r = 0; r < t_iMaxSearch; ++r) {
423 t_matProj_Phi_s = t_matOrthProj * t_matPhi_s;
424
425 //new Version: Calculating Projection before
426 t_matProj_LeadField = t_matOrthProj * m_ForwardSolution.sol->data; //Subtract the found sources from the current found source
427
428 //###First Option###
429 //Step 1: lt. Mosher 1998 -> Maybe tmp_Proj_Phi_S is already orthogonal -> so no SVD needed -> U_B = tmp_Proj_Phi_S;
430 Eigen::JacobiSVD<MatrixXT> t_svdProj_Phi_S(t_matProj_Phi_s, Eigen::ComputeThinU);
431 MatrixXT t_matU_B;
432 useFullRank(t_svdProj_Phi_S.matrixU(), t_svdProj_Phi_S.singularValues().asDiagonal(), t_matU_B);
433
434 //Inits
436 t_vecRoh.setZero();
437
438 //subcorr benchmark
439 //Stop the time
440 clock_t start_subcorr, end_subcorr;
441 start_subcorr = clock();
442
443//Multithreading correlation calculation
444#ifdef _OPENMP
445#pragma omp parallel num_threads(m_iMaxNumThreads)
446#endif
447 {
448#ifdef _OPENMP
449#pragma omp for
450#endif
451 for (int i = 0; i < m_iNumLeadFieldCombinations; i++) {
452 //new Version: calculate matrix multiplication before
453 //Create Lead Field combinations -> It would be better to use a pointer construction, to increase performance
454 MatrixX6T t_matProj_G(t_matProj_LeadField.rows(), 6);
455
456 int idx1 = m_ppPairIdxCombinations[i].x1;
457 int idx2 = m_ppPairIdxCombinations[i].x2;
458
459 InvRapMusic::getGainMatrixPair(t_matProj_LeadField, t_matProj_G, idx1, idx2);
460
461 t_vecRoh(i) = InvRapMusic::subcorr(t_matProj_G, t_matU_B); //t_vecRoh holds the correlations roh_k
462 }
463 }
464
465 // if(r==0)
466 // {
467 // std::fstream filestr;
468 // std::stringstream filename;
469 // filename << "Roh_gold.txt";
470 //
471 // filestr.open ( filename.str().c_str(), std::fstream::out);
472 // for(int i = 0; i < m_iNumLeadFieldCombinations; ++i)
473 // {
474 // filestr << t_vecRoh(i) << "\n";
475 // }
476 // filestr.close();
477 //
478 // //exit(0);
479 // }
480
481 //subcorr benchmark
482 end_subcorr = clock();
483
484 float t_fSubcorrElapsedTime = (static_cast<float>(end_subcorr - start_subcorr) / static_cast<float>(CLOCKS_PER_SEC)) * 1000.0f;
485 qDebug() << "Time Elapsed: " << t_fSubcorrElapsedTime << " ms";
486
487 //Find the maximum of correlation - can't put this in the for loop because it's running in different threads.
488 double t_val_roh_k;
489
490 VectorXT::Index t_iMaxIdx;
491
492 t_val_roh_k = t_vecRoh.maxCoeff(&t_iMaxIdx); //p_vecCor = ^roh_k
493
494 //get positions in sparsed leadfield from index combinations;
495 int t_iIdx1 = m_ppPairIdxCombinations[t_iMaxIdx].x1;
496 int t_iIdx2 = m_ppPairIdxCombinations[t_iMaxIdx].x2;
497
498 // (Idx+1) because of MATLAB positions -> starting with 1 not with 0
499 qDebug() << "Iteration: " << r + 1 << " of " << t_iMaxSearch
500 << "; Correlation: " << t_val_roh_k << "; Position (Idx+1): " << t_iIdx1 + 1 << " - " << t_iIdx2 + 1 << "\n\n";
501
502 //Calculations with the max correlated dipole pair G_k_1 -> ToDo Obsolet when taking direkt Projected Lead Field
503 MatrixX6T t_matG_k_1(m_ForwardSolution.sol->data.rows(), 6);
504 InvRapMusic::getGainMatrixPair(m_ForwardSolution.sol->data, t_matG_k_1, t_iIdx1, t_iIdx2);
505
506 MatrixX6T t_matProj_G_k_1(t_matOrthProj.rows(), t_matG_k_1.cols());
507 t_matProj_G_k_1 = t_matOrthProj * t_matG_k_1; //Subtract the found sources from the current found source
508 // MatrixX6T t_matProj_G_k_1(t_matProj_LeadField.rows(), 6);
509 // getLeadFieldPair(t_matProj_LeadField, t_matProj_G_k_1, t_iIdx1, t_iIdx2);
510
511 //Calculate source direction
512 //source direction (p_pMatPhi) for current source r (phi_k_1)
513 Vector6T t_vec_phi_k_1(6);
514 InvRapMusic::subcorr(t_matProj_G_k_1, t_matU_B, t_vec_phi_k_1); //Correlate the current source to calculate the direction
515
516 //Set return values
517 InvRapMusic::insertSource(t_iIdx1, t_iIdx2, t_vec_phi_k_1, t_val_roh_k, p_RapDipoles);
518
519 //Stop Searching when Correlation is smaller then the Threshold
520 if (t_val_roh_k < m_dThreshold) {
521 qDebug() << "Searching stopped, last correlation " << t_val_roh_k;
522 qDebug() << " is smaller then the given threshold " << m_dThreshold;
523 break;
524 }
525
526 //Calculate A_k_1 = [a_theta_1..a_theta_k_1] matrix for subtraction of found source
527 InvRapMusic::calcA_k_1(t_matG_k_1, t_vec_phi_k_1, r, t_matA_k_1);
528
529 //Calculate new orthogonal Projector (Pi_k_1)
530 calcOrthProj(t_matA_k_1, t_matOrthProj);
531
532 //garbage collecting
533 //ToDo
534 }
535
536 qDebug() << "##### Calculation of RAP MUSIC completed ######";
537
538 end = clock();
539
540 float t_fElapsedTime = (static_cast<float>(end - start) / static_cast<float>(CLOCKS_PER_SEC)) * 1000.0f;
541 qDebug() << "Total Time Elapsed: " << t_fElapsedTime << " ms";
542
543 return p_SourceEstimate;
544}
545
546//=============================================================================================================
547
548int InvRapMusic::calcPhi_s(const MatrixXT& p_matMeasurement, MatrixXT& p_matPhi_s) const
549{
550 MatrixXT t_matF; //t_matF = makeSquareMat(p_pMatMeasurement); //FF^T -> ToDo Check this
551 if (p_matMeasurement.cols() > p_matMeasurement.rows())
552 t_matF = makeSquareMat(p_matMeasurement); //FF^T
553 else
554 t_matF = MatrixXT(p_matMeasurement);
555
556 Eigen::JacobiSVD<MatrixXT> t_svdF(t_matF, Eigen::ComputeThinU);
557
558 int t_r = getRank(t_svdF.singularValues().asDiagonal());
559
560 int t_iCols = t_r; //t_r < m_iN ? m_iN : t_r;
561
562 //m_iNumChannels has to be equal to t_svdF.matrixU().rows()
563 p_matPhi_s = t_svdF.matrixU().topLeftCorner(m_iNumChannels, t_iCols);
564
565 return t_r;
566}
567
568//=============================================================================================================
569
570double InvRapMusic::subcorr(MatrixX6T& p_matProj_G, const MatrixXT& p_matU_B)
571{
572 //Orthogonalisierungstest wegen performance weggelassen -> ohne is es viel schneller
573
574 Matrix6T t_matSigma_A(6, 6);
575 Matrix6XT t_matU_A_T(6, p_matProj_G.rows()); //rows and cols are changed, because of CV_SVD_U_T
576
577 Eigen::JacobiSVD<MatrixXT> t_svdProj_G(p_matProj_G, Eigen::ComputeThinU);
578
579 t_matSigma_A = t_svdProj_G.singularValues().asDiagonal();
580 t_matU_A_T = t_svdProj_G.matrixU().transpose();
581
582 //lt. Mosher 1998 ToDo: Only Retain those Components of U_A and U_B that correspond to nonzero singular values
583 //for U_A and U_B the number of columns corresponds to their ranks
584 MatrixXT t_matU_A_T_full;
585 //reduce to rank only when directions aren't calculated, otherwise use the full t_matU_A_T
586
587 useFullRank(t_matU_A_T, t_matSigma_A, t_matU_A_T_full, IS_TRANSPOSED); //lt. Mosher 1998: Only Retain those Components of U_A that correspond to nonzero singular values -> for U_A the number of columns corresponds to their ranks
588
589 MatrixXT t_matCor(t_matU_A_T_full.rows(), p_matU_B.cols());
590
591 //Step 2: compute the subspace correlation
592 t_matCor = t_matU_A_T_full * p_matU_B; //lt. Mosher 1998: C = U_A^T * U_B
593
594 VectorXT t_vecSigma_C;
595
596 if (t_matCor.cols() > t_matCor.rows()) {
597 MatrixXT t_matCor_H = t_matCor.adjoint(); //for complex it has to be adjoint
598
599 Eigen::JacobiSVD<MatrixXT> t_svdCor_H(t_matCor_H);
600
601 t_vecSigma_C = t_svdCor_H.singularValues();
602 } else {
603 Eigen::JacobiSVD<MatrixXT> t_svdCor(t_matCor);
604
605 t_vecSigma_C = t_svdCor.singularValues();
606 }
607
608 //Step 3
609 double t_dRetSigma_C;
610 t_dRetSigma_C = t_vecSigma_C(0); //Take only the correlation of the first principal components
611
612 //garbage collecting
613 //ToDo
614
615 return t_dRetSigma_C;
616}
617
618//=============================================================================================================
619
620double InvRapMusic::subcorr(MatrixX6T& p_matProj_G, const MatrixXT& p_matU_B, Vector6T& p_vec_phi_k_1)
621{
622 //Orthogonalisierungstest wegen performance weggelassen -> ohne is es viel schneller
623
624 Matrix6T sigma_A(6, 6);
625 Matrix6XT U_A_T(6, p_matProj_G.rows()); //rows and cols are changed, because of CV_SVD_U_T
626 Matrix6T V_A(6, 6);
627
628 Eigen::JacobiSVD<MatrixXT> svdOfProj_G(p_matProj_G, Eigen::ComputeThinU | Eigen::ComputeThinV);
629
630 sigma_A = svdOfProj_G.singularValues().asDiagonal();
631 U_A_T = svdOfProj_G.matrixU().transpose();
632 V_A = svdOfProj_G.matrixV();
633
634 //lt. Mosher 1998 ToDo: Only Retain those Components of U_A and U_B that correspond to nonzero singular values
635 //for U_A and U_B the number of columns corresponds to their ranks
636 //-> reduce to rank only when directions aren't calculated, otherwise use the full U_A_T
637
638 Matrix6XT t_matCor(6, p_matU_B.cols());
639
640 //Step 2: compute the subspace correlation
641 t_matCor = U_A_T * p_matU_B; //lt. Mosher 1998: C = U_A^T * U_B
642
643 VectorXT sigma_C;
644
645 //Step 4
646 Matrix6XT U_C;
647
648 if (t_matCor.cols() > t_matCor.rows()) {
649 MatrixX6T Cor_H(t_matCor.cols(), 6);
650 Cor_H = t_matCor.adjoint(); //for complex it has to be adjunct
651
652 // Thin SVDs need dynamic sizes; Eigen asserts on the fixed 6-row/column types
653 Eigen::JacobiSVD<MatrixXT> svdOfCor_H(MatrixXT(Cor_H), Eigen::ComputeThinV);
654
655 U_C = svdOfCor_H.matrixV(); //because t_matCor Hermitesch U and V are exchanged
656 sigma_C = svdOfCor_H.singularValues();
657 } else {
658 Eigen::JacobiSVD<MatrixXT> svdOfCor(MatrixXT(t_matCor), Eigen::ComputeThinU);
659
660 U_C = svdOfCor.matrixU();
661 sigma_C = svdOfCor.singularValues();
662 }
663
664 Matrix6T sigma_a_inv;
665 sigma_a_inv = sigma_A.inverse();
666
667 Matrix6XT X;
668 X = (V_A * sigma_a_inv) * U_C; //X = V_A*Sigma_A^-1*U_C
669
670 Vector6T X_max; //only for the maximum c - so instead of X->cols use 1
671 X_max = X.col(0);
672
673 double norm_X = 1 / (X_max.norm());
674
675 //Multiply a scalar with an Array -> linear transform
676 p_vec_phi_k_1 = X_max * norm_X; //u1 = x1/||x1|| this is the orientation
677
678 //garbage collecting
679 //ToDo
680
681 //Step 3
682 double ret_sigma_C;
683 ret_sigma_C = sigma_C(0); //Take only the correlation of the first principal components
684
685 //garbage collecting
686 //ToDo
687
688 return ret_sigma_C;
689}
690
691//=============================================================================================================
692
693void InvRapMusic::calcA_k_1(const MatrixX6T& p_matG_k_1,
694 const Vector6T& p_matPhi_k_1,
695 const int p_iIdxk_1,
696 MatrixXT& p_matA_k_1)
697{
698 //Calculate A_k_1 = [a_theta_1..a_theta_k_1] matrix for subtraction of found source
699 VectorXT t_vec_a_theta_k_1(p_matG_k_1.rows(), 1);
700
701 t_vec_a_theta_k_1 = p_matG_k_1 * p_matPhi_k_1; // a_theta_k_1 = G_k_1*phi_k_1 this corresponds to the normalized signal component in subspace r
702
703 p_matA_k_1.block(0, p_iIdxk_1, p_matA_k_1.rows(), 1) = t_vec_a_theta_k_1;
704}
705
706//=============================================================================================================
707
708void InvRapMusic::calcOrthProj(const MatrixXT& p_matA_k_1, MatrixXT& p_matOrthProj) const
709{
710 //Calculate OrthProj=I-A_k_1*(A_k_1'*A_k_1)^-1*A_k_1' //Wetterling -> A_k_1 = Gain
711
712 MatrixXT t_matA_k_1_tmp(p_matA_k_1.cols(), p_matA_k_1.cols());
713 t_matA_k_1_tmp = p_matA_k_1.adjoint() * p_matA_k_1; //A_k_1'*A_k_1 = A_k_1_tmp -> A_k_1' has to be adjoint for complex
714
715 int t_size = t_matA_k_1_tmp.cols();
716
717 while (!t_matA_k_1_tmp.block(0, 0, t_size, t_size).fullPivLu().isInvertible()) {
718 --t_size;
719 }
720
721 MatrixXT t_matA_k_1_tmp_inv(t_matA_k_1_tmp.rows(), t_matA_k_1_tmp.cols());
722 t_matA_k_1_tmp_inv.setZero();
723
724 t_matA_k_1_tmp_inv.block(0, 0, t_size, t_size) = t_matA_k_1_tmp.block(0, 0, t_size, t_size).inverse(); //(A_k_1_tmp)^-1 = A_k_1_tmp_inv
725
726 t_matA_k_1_tmp = p_matA_k_1 * t_matA_k_1_tmp_inv; //(A_k_1*A_k_1_tmp_inv) = A_k_1_tmp
727
728 MatrixXT t_matA_k_1_tmp2(p_matA_k_1.rows(), p_matA_k_1.rows());
729
730 t_matA_k_1_tmp2 = t_matA_k_1_tmp * p_matA_k_1.adjoint(); //(A_k_1_tmp)*A_k_1' -> here A_k_1' is only transposed - it has to be adjoint
731
733 I.setIdentity();
734
735 p_matOrthProj = I - t_matA_k_1_tmp2; //OrthProj=I-A_k_1*(A_k_1'*A_k_1)^-1*A_k_1';
736
737 //garbage collecting
738 //ToDo
739}
740
741//=============================================================================================================
742
743void InvRapMusic::calcPairCombinations(const int p_iNumPoints,
744 const int p_iNumCombinations,
745 std::vector<Pair>& p_pairIdxCombinations) const
746{
747 int idx1 = 0;
748 int idx2 = 0;
749
750//Process Code in {m_max_num_threads} threads -> When compile with Intel Compiler -> probably obsolete
751#ifdef _OPENMP
752#pragma omp parallel num_threads(m_iMaxNumThreads) private(idx1, idx2)
753#endif
754 {
755#ifdef _OPENMP
756#pragma omp for
757#endif
758 for (int i = 0; i < p_iNumCombinations; ++i) {
759 InvRapMusic::getPointPair(p_iNumPoints, i, idx1, idx2);
760
761 Pair t_pairCombination;
762 t_pairCombination.x1 = idx1;
763 t_pairCombination.x2 = idx2;
764
765 p_pairIdxCombinations[i] = t_pairCombination;
766 }
767 }
768}
769
770//=============================================================================================================
771
772void InvRapMusic::getPointPair(const int p_iPoints, const int p_iCurIdx, int& p_iIdx1, int& p_iIdx2)
773{
774 int ii = p_iPoints * (p_iPoints + 1) / 2 - 1 - p_iCurIdx;
775 int K = static_cast<int>(floor((sqrt(static_cast<double>(8 * ii + 1)) - 1) / 2));
776
777 p_iIdx1 = p_iPoints - 1 - K;
778
779 p_iIdx2 = (p_iCurIdx - p_iPoints * (p_iPoints + 1) / 2 + (K + 1) * (K + 2) / 2) + p_iIdx1;
780}
781
782//=============================================================================================================
783//ToDo don't make a real copy
784void InvRapMusic::getGainMatrixPair(const MatrixXT& p_matGainMarix,
785 MatrixX6T& p_matGainMarix_Pair,
786 int p_iIdx1, int p_iIdx2)
787{
788 p_matGainMarix_Pair.block(0, 0, p_matGainMarix.rows(), 3) = p_matGainMarix.block(0, p_iIdx1 * 3, p_matGainMarix.rows(), 3);
789
790 p_matGainMarix_Pair.block(0, 3, p_matGainMarix.rows(), 3) = p_matGainMarix.block(0, p_iIdx2 * 3, p_matGainMarix.rows(), 3);
791}
792
793//=============================================================================================================
794
795void InvRapMusic::insertSource(int p_iDipoleIdx1, int p_iDipoleIdx2,
796 const Vector6T& p_vec_phi_k_1,
797 double p_valCor,
798 QList<InvDipolePair<double>>& p_RapDipoles)
799{
800 InvDipolePair<double> t_pRapDipolePair;
801
802 t_pRapDipolePair.m_iIdx1 = p_iDipoleIdx1; //p_iDipoleIdx1+1 because of MATLAB index
803 t_pRapDipolePair.m_iIdx2 = p_iDipoleIdx2;
804
805 t_pRapDipolePair.m_Dipole1.x() = 0; //m_bGridInitialized ? (*m_pMatGrid)(p_iDipoleIdx1, 0) : 0;
806 t_pRapDipolePair.m_Dipole1.y() = 0; //m_bGridInitialized ? (*m_pMatGrid)(p_iDipoleIdx1, 1) : 0;
807 t_pRapDipolePair.m_Dipole1.z() = 0; //m_bGridInitialized ? (*m_pMatGrid)(p_iDipoleIdx1, 2) : 0;
808
809 t_pRapDipolePair.m_Dipole2.x() = 0; //m_bGridInitialized ? (*m_pMatGrid)(p_iDipoleIdx2, 0) : 0;
810 t_pRapDipolePair.m_Dipole2.y() = 0; //m_bGridInitialized ? (*m_pMatGrid)(p_iDipoleIdx2, 1) : 0;
811 t_pRapDipolePair.m_Dipole2.z() = 0; //m_bGridInitialized ? (*m_pMatGrid)(p_iDipoleIdx2, 2) : 0;
812
813 t_pRapDipolePair.m_Dipole1.phi_x() = p_vec_phi_k_1[0];
814 t_pRapDipolePair.m_Dipole1.phi_y() = p_vec_phi_k_1[1];
815 t_pRapDipolePair.m_Dipole1.phi_z() = p_vec_phi_k_1[2];
816
817 t_pRapDipolePair.m_Dipole2.phi_x() = p_vec_phi_k_1[3];
818 t_pRapDipolePair.m_Dipole2.phi_y() = p_vec_phi_k_1[4];
819 t_pRapDipolePair.m_Dipole2.phi_z() = p_vec_phi_k_1[5];
820
821 t_pRapDipolePair.m_vCorrelation = p_valCor;
822
823 p_RapDipoles.append(t_pRapDipolePair);
824}
825
826//=============================================================================================================
827
828void InvRapMusic::setStcAttr(int p_iSampStcWin, float p_fStcOverlap)
829{
830 m_iSamplesStcWindow = p_iSampStcWin;
831 m_fStcOverlap = p_fStcOverlap;
832}
constexpr int X
Recursively Applied and Projected MUSIC (RAP-MUSIC) source-localisation algorithm.
General numerical helpers: GCD, log2, histogram binning, baseline rescaling, sparsity tests.
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).
Inverse source estimation (MNE, dSPM, sLORETA, dipole fitting).
constexpr auto IS_TRANSPOSED
Single averaged evoked response: time axis, data, baseline, channel info and averaging metadata.
Definition fiff_evoked.h:77
Eigen::RowVectorXf times
Eigen::MatrixXd data
Source-space inverse-solution container with dense grid plus optional focal-dipole,...
Pair of correlated dipole indices and orientations found by the RAP MUSIC scanning step.
Definition inv_dipole.h:62
InvDipole< T > m_Dipole1
Definition inv_dipole.h:64
InvDipole< T > m_Dipole2
Definition inv_dipole.h:67
Index pair representing two grid points evaluated together in the RAP MUSIC subspace scan.
Eigen::Matrix< double, 6, 1 > Vector6T
void calcPairCombinations(const int p_iNumPoints, const int p_iNumCombinations, std::vector< Pair > &p_pairIdxCombinations) const
static void calcA_k_1(const MatrixX6T &p_matG_k_1, const Vector6T &p_matPhi_k_1, const int p_iIdxk_1, MatrixXT &p_matA_k_1)
static void getPointPair(const int p_iPoints, const int p_iCurIdx, int &p_iIdx1, int &p_iIdx2)
virtual InvSourceEstimate calculateInverse(const FIFFLIB::FiffEvoked &p_fiffEvoked, bool pick_normal=false)
static int getRank(const MatrixXT &p_matSigma)
virtual const char * getName() const
static MatrixXT makeSquareMat(const MatrixXT &p_matF)
static void insertSource(int p_iDipoleIdx1, int p_iDipoleIdx2, const Vector6T &p_vec_phi_k_1, double p_valCor, QList< InvDipolePair< double > > &p_RapDipoles)
void setStcAttr(int p_iSampStcWin, float p_fStcOverlap)
bool init(MNELIB::MNEForwardSolution &p_pFwd, bool p_bSparsed=false, int p_iN=2, double p_dThr=0.5)
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic > MatrixXT
Eigen::Matrix< double, 6, 6 > Matrix6T
Eigen::Matrix< double, Eigen::Dynamic, 1 > VectorXT
void calcOrthProj(const MatrixXT &p_matA_k_1, MatrixXT &p_matOrthProj) const
static int useFullRank(const MatrixXT &p_Mat, const MatrixXT &p_matSigma_src, MatrixXT &p_matFull_Rank, int type=NOT_TRANSPOSED)
int calcPhi_s(const MatrixXT &p_matMeasurement, MatrixXT &p_matPhi_s) const
Eigen::Matrix< double, Eigen::Dynamic, 6 > MatrixX6T
MNELIB::MNEForwardSolution m_ForwardSolution
virtual const MNELIB::MNESourceSpaces & getSourceSpace() const
std::vector< Pair > m_ppPairIdxCombinations
Eigen::Matrix< double, 6, Eigen::Dynamic > Matrix6XT
static void getGainMatrixPair(const MatrixXT &p_matGainMarix, MatrixX6T &p_matGainMarix_Pair, int p_iIdx1, int p_iIdx2)
static double subcorr(MatrixX6T &p_matProj_G, const MatrixXT &p_pMatU_B)
static int nchoose2(int n)
Definition numerics.cpp:86
In-memory representation of an -fwd.fif forward solution.
MNELIB::MNESourceSpaces src
FIFFLIB::FiffNamedMatrix::SDPtr sol
List of MNESourceSpace objects forming a subject source space.