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