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) //if samples per stc aren't set -> use full window
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 (t_pMatPhi_s)
379 MatrixXT* t_pMatPhi_s = nullptr; //(m_iNumChannels, m_iN < t_r ? m_iN : t_r);
380 int t_r = calcPhi_s(/*(MatrixXT)*/ p_matMeasurement, t_pMatPhi_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_pMatPhi_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_pMatPhi_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 //garbage collecting
544 delete t_pMatPhi_s;
545
546 return p_SourceEstimate;
547}
548
549//=============================================================================================================
550
551int InvRapMusic::calcPhi_s(const MatrixXT& p_matMeasurement, MatrixXT*& p_pMatPhi_s) const
552{
553 //Calculate p_pMatPhi_s
554 MatrixXT t_matF; //t_matF = makeSquareMat(p_pMatMeasurement); //FF^T -> ToDo Check this
555 if (p_matMeasurement.cols() > p_matMeasurement.rows())
556 t_matF = makeSquareMat(p_matMeasurement); //FF^T
557 else
558 t_matF = MatrixXT(p_matMeasurement);
559
560 Eigen::JacobiSVD<MatrixXT> t_svdF(t_matF, Eigen::ComputeThinU);
561
562 int t_r = getRank(t_svdF.singularValues().asDiagonal());
563
564 int t_iCols = t_r; //t_r < m_iN ? m_iN : t_r;
565
566 delete p_pMatPhi_s;
567
568 //m_iNumChannels has to be equal to t_svdF.matrixU().rows()
569 p_pMatPhi_s = new MatrixXT(m_iNumChannels, t_iCols);
570
571 //assign the signal subspace
572 memcpy(p_pMatPhi_s->data(), t_svdF.matrixU().data(), sizeof(double) * m_iNumChannels * t_iCols);
573
574 return t_r;
575}
576
577//=============================================================================================================
578
579double InvRapMusic::subcorr(MatrixX6T& p_matProj_G, const MatrixXT& p_matU_B)
580{
581 //Orthogonalisierungstest wegen performance weggelassen -> ohne is es viel schneller
582
583 Matrix6T t_matSigma_A(6, 6);
584 Matrix6XT t_matU_A_T(6, p_matProj_G.rows()); //rows and cols are changed, because of CV_SVD_U_T
585
586 Eigen::JacobiSVD<MatrixXT> t_svdProj_G(p_matProj_G, Eigen::ComputeThinU);
587
588 t_matSigma_A = t_svdProj_G.singularValues().asDiagonal();
589 t_matU_A_T = t_svdProj_G.matrixU().transpose();
590
591 //lt. Mosher 1998 ToDo: Only Retain those Components of U_A and U_B that correspond to nonzero singular values
592 //for U_A and U_B the number of columns corresponds to their ranks
593 MatrixXT t_matU_A_T_full;
594 //reduce to rank only when directions aren't calculated, otherwise use the full t_matU_A_T
595
596 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
597
598 MatrixXT t_matCor(t_matU_A_T_full.rows(), p_matU_B.cols());
599
600 //Step 2: compute the subspace correlation
601 t_matCor = t_matU_A_T_full * p_matU_B; //lt. Mosher 1998: C = U_A^T * U_B
602
603 VectorXT t_vecSigma_C;
604
605 if (t_matCor.cols() > t_matCor.rows()) {
606 MatrixXT t_matCor_H = t_matCor.adjoint(); //for complex it has to be adjoint
607
608 Eigen::JacobiSVD<MatrixXT> t_svdCor_H(t_matCor_H);
609
610 t_vecSigma_C = t_svdCor_H.singularValues();
611 } else {
612 Eigen::JacobiSVD<MatrixXT> t_svdCor(t_matCor);
613
614 t_vecSigma_C = t_svdCor.singularValues();
615 }
616
617 //Step 3
618 double t_dRetSigma_C;
619 t_dRetSigma_C = t_vecSigma_C(0); //Take only the correlation of the first principal components
620
621 //garbage collecting
622 //ToDo
623
624 return t_dRetSigma_C;
625}
626
627//=============================================================================================================
628
629double InvRapMusic::subcorr(MatrixX6T& p_matProj_G, const MatrixXT& p_matU_B, Vector6T& p_vec_phi_k_1)
630{
631 //Orthogonalisierungstest wegen performance weggelassen -> ohne is es viel schneller
632
633 Matrix6T sigma_A(6, 6);
634 Matrix6XT U_A_T(6, p_matProj_G.rows()); //rows and cols are changed, because of CV_SVD_U_T
635 Matrix6T V_A(6, 6);
636
637 Eigen::JacobiSVD<MatrixXT> svdOfProj_G(p_matProj_G, Eigen::ComputeThinU | Eigen::ComputeThinV);
638
639 sigma_A = svdOfProj_G.singularValues().asDiagonal();
640 U_A_T = svdOfProj_G.matrixU().transpose();
641 V_A = svdOfProj_G.matrixV();
642
643 //lt. Mosher 1998 ToDo: Only Retain those Components of U_A and U_B that correspond to nonzero singular values
644 //for U_A and U_B the number of columns corresponds to their ranks
645 //-> reduce to rank only when directions aren't calculated, otherwise use the full U_A_T
646
647 Matrix6XT t_matCor(6, p_matU_B.cols());
648
649 //Step 2: compute the subspace correlation
650 t_matCor = U_A_T * p_matU_B; //lt. Mosher 1998: C = U_A^T * U_B
651
652 VectorXT sigma_C;
653
654 //Step 4
655 Matrix6XT U_C;
656
657 if (t_matCor.cols() > t_matCor.rows()) {
658 MatrixX6T Cor_H(t_matCor.cols(), 6);
659 Cor_H = t_matCor.adjoint(); //for complex it has to be adjunct
660
661 // Thin SVDs need dynamic sizes; Eigen asserts on the fixed 6-row/column types
662 Eigen::JacobiSVD<MatrixXT> svdOfCor_H(MatrixXT(Cor_H), Eigen::ComputeThinV);
663
664 U_C = svdOfCor_H.matrixV(); //because t_matCor Hermitesch U and V are exchanged
665 sigma_C = svdOfCor_H.singularValues();
666 } else {
667 Eigen::JacobiSVD<MatrixXT> svdOfCor(MatrixXT(t_matCor), Eigen::ComputeThinU);
668
669 U_C = svdOfCor.matrixU();
670 sigma_C = svdOfCor.singularValues();
671 }
672
673 Matrix6T sigma_a_inv;
674 sigma_a_inv = sigma_A.inverse();
675
676 Matrix6XT X;
677 X = (V_A * sigma_a_inv) * U_C; //X = V_A*Sigma_A^-1*U_C
678
679 Vector6T X_max; //only for the maximum c - so instead of X->cols use 1
680 X_max = X.col(0);
681
682 double norm_X = 1 / (X_max.norm());
683
684 //Multiply a scalar with an Array -> linear transform
685 p_vec_phi_k_1 = X_max * norm_X; //u1 = x1/||x1|| this is the orientation
686
687 //garbage collecting
688 //ToDo
689
690 //Step 3
691 double ret_sigma_C;
692 ret_sigma_C = sigma_C(0); //Take only the correlation of the first principal components
693
694 //garbage collecting
695 //ToDo
696
697 return ret_sigma_C;
698}
699
700//=============================================================================================================
701
702void InvRapMusic::calcA_k_1(const MatrixX6T& p_matG_k_1,
703 const Vector6T& p_matPhi_k_1,
704 const int p_iIdxk_1,
705 MatrixXT& p_matA_k_1)
706{
707 //Calculate A_k_1 = [a_theta_1..a_theta_k_1] matrix for subtraction of found source
708 VectorXT t_vec_a_theta_k_1(p_matG_k_1.rows(), 1);
709
710 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
711
712 p_matA_k_1.block(0, p_iIdxk_1, p_matA_k_1.rows(), 1) = t_vec_a_theta_k_1;
713}
714
715//=============================================================================================================
716
717void InvRapMusic::calcOrthProj(const MatrixXT& p_matA_k_1, MatrixXT& p_matOrthProj) const
718{
719 //Calculate OrthProj=I-A_k_1*(A_k_1'*A_k_1)^-1*A_k_1' //Wetterling -> A_k_1 = Gain
720
721 MatrixXT t_matA_k_1_tmp(p_matA_k_1.cols(), p_matA_k_1.cols());
722 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
723
724 int t_size = t_matA_k_1_tmp.cols();
725
726 while (!t_matA_k_1_tmp.block(0, 0, t_size, t_size).fullPivLu().isInvertible()) {
727 --t_size;
728 }
729
730 MatrixXT t_matA_k_1_tmp_inv(t_matA_k_1_tmp.rows(), t_matA_k_1_tmp.cols());
731 t_matA_k_1_tmp_inv.setZero();
732
733 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
734
735 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
736
737 MatrixXT t_matA_k_1_tmp2(p_matA_k_1.rows(), p_matA_k_1.rows());
738
739 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
740
742 I.setIdentity();
743
744 p_matOrthProj = I - t_matA_k_1_tmp2; //OrthProj=I-A_k_1*(A_k_1'*A_k_1)^-1*A_k_1';
745
746 //garbage collecting
747 //ToDo
748}
749
750//=============================================================================================================
751
752void InvRapMusic::calcPairCombinations(const int p_iNumPoints,
753 const int p_iNumCombinations,
754 std::vector<Pair>& p_pairIdxCombinations) const
755{
756 int idx1 = 0;
757 int idx2 = 0;
758
759//Process Code in {m_max_num_threads} threads -> When compile with Intel Compiler -> probably obsolete
760#ifdef _OPENMP
761#pragma omp parallel num_threads(m_iMaxNumThreads) private(idx1, idx2)
762#endif
763 {
764#ifdef _OPENMP
765#pragma omp for
766#endif
767 for (int i = 0; i < p_iNumCombinations; ++i) {
768 InvRapMusic::getPointPair(p_iNumPoints, i, idx1, idx2);
769
770 Pair t_pairCombination;
771 t_pairCombination.x1 = idx1;
772 t_pairCombination.x2 = idx2;
773
774 p_pairIdxCombinations[i] = t_pairCombination;
775 }
776 }
777}
778
779//=============================================================================================================
780
781void InvRapMusic::getPointPair(const int p_iPoints, const int p_iCurIdx, int& p_iIdx1, int& p_iIdx2)
782{
783 int ii = p_iPoints * (p_iPoints + 1) / 2 - 1 - p_iCurIdx;
784 int K = static_cast<int>(floor((sqrt(static_cast<double>(8 * ii + 1)) - 1) / 2));
785
786 p_iIdx1 = p_iPoints - 1 - K;
787
788 p_iIdx2 = (p_iCurIdx - p_iPoints * (p_iPoints + 1) / 2 + (K + 1) * (K + 2) / 2) + p_iIdx1;
789}
790
791//=============================================================================================================
792//ToDo don't make a real copy
793void InvRapMusic::getGainMatrixPair(const MatrixXT& p_matGainMarix,
794 MatrixX6T& p_matGainMarix_Pair,
795 int p_iIdx1, int p_iIdx2)
796{
797 p_matGainMarix_Pair.block(0, 0, p_matGainMarix.rows(), 3) = p_matGainMarix.block(0, p_iIdx1 * 3, p_matGainMarix.rows(), 3);
798
799 p_matGainMarix_Pair.block(0, 3, p_matGainMarix.rows(), 3) = p_matGainMarix.block(0, p_iIdx2 * 3, p_matGainMarix.rows(), 3);
800}
801
802//=============================================================================================================
803
804void InvRapMusic::insertSource(int p_iDipoleIdx1, int p_iDipoleIdx2,
805 const Vector6T& p_vec_phi_k_1,
806 double p_valCor,
807 QList<InvDipolePair<double>>& p_RapDipoles)
808{
809 InvDipolePair<double> t_pRapDipolePair;
810
811 t_pRapDipolePair.m_iIdx1 = p_iDipoleIdx1; //p_iDipoleIdx1+1 because of MATLAB index
812 t_pRapDipolePair.m_iIdx2 = p_iDipoleIdx2;
813
814 t_pRapDipolePair.m_Dipole1.x() = 0; //m_bGridInitialized ? (*m_pMatGrid)(p_iDipoleIdx1, 0) : 0;
815 t_pRapDipolePair.m_Dipole1.y() = 0; //m_bGridInitialized ? (*m_pMatGrid)(p_iDipoleIdx1, 1) : 0;
816 t_pRapDipolePair.m_Dipole1.z() = 0; //m_bGridInitialized ? (*m_pMatGrid)(p_iDipoleIdx1, 2) : 0;
817
818 t_pRapDipolePair.m_Dipole2.x() = 0; //m_bGridInitialized ? (*m_pMatGrid)(p_iDipoleIdx2, 0) : 0;
819 t_pRapDipolePair.m_Dipole2.y() = 0; //m_bGridInitialized ? (*m_pMatGrid)(p_iDipoleIdx2, 1) : 0;
820 t_pRapDipolePair.m_Dipole2.z() = 0; //m_bGridInitialized ? (*m_pMatGrid)(p_iDipoleIdx2, 2) : 0;
821
822 t_pRapDipolePair.m_Dipole1.phi_x() = p_vec_phi_k_1[0];
823 t_pRapDipolePair.m_Dipole1.phi_y() = p_vec_phi_k_1[1];
824 t_pRapDipolePair.m_Dipole1.phi_z() = p_vec_phi_k_1[2];
825
826 t_pRapDipolePair.m_Dipole2.phi_x() = p_vec_phi_k_1[3];
827 t_pRapDipolePair.m_Dipole2.phi_y() = p_vec_phi_k_1[4];
828 t_pRapDipolePair.m_Dipole2.phi_z() = p_vec_phi_k_1[5];
829
830 t_pRapDipolePair.m_vCorrelation = p_valCor;
831
832 p_RapDipoles.append(t_pRapDipolePair);
833}
834
835//=============================================================================================================
836
837void InvRapMusic::setStcAttr(int p_iSampStcWin, float p_fStcOverlap)
838{
839 m_iSamplesStcWindow = p_iSampStcWin;
840 m_fStcOverlap = p_fStcOverlap;
841}
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: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
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:87
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.