v2.0.0
Loading...
Searching...
No Matches
inv_hpi_fit_data.cpp
Go to the documentation of this file.
1//=============================================================================================================
18
19//=============================================================================================================
20// INCLUDES
21//=============================================================================================================
22
23#include "inv_hpi_fit_data.h"
24#include "inv_hpi_fit.h"
25#include "inv_sensor_set.h"
26
27#include <math/linalg.h>
28
29#include <iostream>
30#include <algorithm>
31
32//=============================================================================================================
33// EIGEN INCLUDES
34//=============================================================================================================
35
36//=============================================================================================================
37// QT INCLUDES
38//=============================================================================================================
39
40#include <qmath.h>
41
42//=============================================================================================================
43// USED NAMESPACES
44//=============================================================================================================
45
46using namespace INVLIB;
47
48//=============================================================================================================
49// DEFINE GLOBAL METHODS
50//=============================================================================================================
51
52//=============================================================================================================
53// DEFINE MEMBER METHODS
54//=============================================================================================================
55
60
61//=============================================================================================================
62
64{
65 // Initialize variables
66 Eigen::RowVectorXd vecCurrentCoil = this->m_coilPos;
67 Eigen::VectorXd vecCurrentData = this->m_sensorData;
68 InvSensorSet currentSensors = this->m_sensors;
69
70 int iDisplay = 0;
71 int iMaxiter = m_iMaxIterations;
72 int iSimplexNumitr = 0;
73
74 this->m_coilPos = fminsearch(vecCurrentCoil,
75 iMaxiter,
76 2 * iMaxiter * vecCurrentCoil.cols(),
77 iDisplay,
78 vecCurrentData,
79 this->m_matProjector,
80 currentSensors,
81 iSimplexNumitr);
82
83 this->m_errorInfo = dipfitError(vecCurrentCoil,
84 vecCurrentData,
85 currentSensors,
86 this->m_matProjector);
87
88 this->m_errorInfo.numIterations = iSimplexNumitr;
89}
90
91//=============================================================================================================
92
93Eigen::MatrixXd InvHpiFitData::magnetic_dipole(Eigen::MatrixXd matPos,
94 Eigen::MatrixXd matPnt,
95 Eigen::MatrixXd matOri)
96{
97 double u0 = 1e-7;
98 int iNchan;
99 Eigen::MatrixXd r, r2, r5, x, y, z, mx, my, mz, Tx, Ty, Tz, lf;
100
101 iNchan = matPnt.rows();
102
103 // Shift the magnetometers so that the dipole is in the origin
104 matPnt.array().col(0) -=matPos(0);
105 matPnt.array().col(1) -=matPos(1);
106 matPnt.array().col(2) -=matPos(2);
107
108 r = matPnt.array().square().rowwise().sum().sqrt();
109
110 r2 = r5 = x = y = z = mx = my = mz = Tx = Ty = Tz = lf = Eigen::MatrixXd::Zero(iNchan,3);
111
112 for(int i = 0;i < iNchan;i++) {
113 r2.row(i).array().fill(pow(r(i),2));
114 r5.row(i).array().fill(pow(r(i),5));
115 }
116
117 for(int i = 0;i < iNchan;i++) {
118 x.row(i).array().fill(matPnt(i,0));
119 y.row(i).array().fill(matPnt(i,1));
120 z.row(i).array().fill(matPnt(i,2));
121 }
122
123 mx.col(0).array().fill(1);
124 my.col(1).array().fill(1);
125 mz.col(2).array().fill(1);
126
127 Tx = 3 * x.cwiseProduct(matPnt) - mx.cwiseProduct(r2);
128 Ty = 3 * y.cwiseProduct(matPnt) - my.cwiseProduct(r2);
129 Tz = 3 * z.cwiseProduct(matPnt) - mz.cwiseProduct(r2);
130
131 for(int i = 0;i < iNchan;i++) {
132 lf(i,0) = Tx.row(i).dot(matOri.row(i));
133 lf(i,1) = Ty.row(i).dot(matOri.row(i));
134 lf(i,2) = Tz.row(i).dot(matOri.row(i));
135 }
136
137 for(int i = 0;i < iNchan;i++) {
138 for(int j = 0;j < 3;j++) {
139 lf(i,j) = u0 * lf(i,j)/(4 * M_PI * r5(i,j));
140 }
141 }
142
143 return lf;
144}
145
146//=============================================================================================================
147
148Eigen::MatrixXd InvHpiFitData::compute_leadfield(const Eigen::MatrixXd& matPos, const InvSensorSet& sensors)
149{
150
151 Eigen::MatrixXd matPnt, matOri, matLf;
152 matPnt = sensors.rmag(); // position of each integrationpoint
153 matOri = sensors.cosmag(); // mOrientation of each coil
154
155 matLf = magnetic_dipole(matPos, matPnt, matOri);
156
157 return matLf;
158}
159
160//=============================================================================================================
161
162DipFitError InvHpiFitData::dipfitError(const Eigen::MatrixXd& matPos,
163 const Eigen::MatrixXd& matData,
164 const InvSensorSet& sensors,
165 const Eigen::MatrixXd& matProjectors)
166{
167 // Variable Declaration
168 struct DipFitError e;
169 Eigen::MatrixXd matLfSensor, matDif;
170 Eigen::MatrixXd matLf(matData.size(),3);
171 int iNp = sensors.np();
172
173 // calculate lf for all sensorpoints
174 matLfSensor = compute_leadfield(matPos, sensors);
175
176 // apply averaging per coil
177 for(int i = 0; i < sensors.ncoils(); i++){
178 matLf.row(i) = sensors.w(i) * matLfSensor.block(i*iNp,0,iNp,matLfSensor.cols());
179 }
180 //matLf = sensors.tra * matLf;
181
182 // Compute lead field for a magnetic dipole in infinite vacuum
183 e.moment = UTILSLIB::Linalg::pinv(matLf) * matData;
184
185 //matDif = matData - matLf * e.moment;
186 matDif = matData - matProjectors * matLf * e.moment;
187
188 e.error = matDif.array().square().sum()/matData.array().square().sum();
189
190 e.numIterations = 0;
191
192 return e;
193}
194
195//=============================================================================================================
196
198{
199 return (a.base_arr < b.base_arr);
200}
201
202//=============================================================================================================
203
204Eigen::MatrixXd InvHpiFitData::fminsearch(const Eigen::MatrixXd& matPos,
205 int iMaxiter,
206 int iMaxfun,
207 int iDisplay,
208 const Eigen::MatrixXd& matData,
209 const Eigen::MatrixXd& matProjectors,
210 const InvSensorSet& sensors,
211 int &iSimplexNumitr)
212{
213 double tolx, tolf, rho, chi, psi, sigma, func_evals, usual_delta, zero_term_delta, temp1, temp2;
214 std::string header, how;
215 int n, itercount, prnt;
216 Eigen::MatrixXd onesn, two2np1, one2n, v, y, v1, tempX1, tempX2, xbar, xr, x, xe, xc, xcc, xin,posCopy;
217 std::vector <double> fv, fv1;
218 std::vector <int> idx;
219
220 DipFitError tempdip, fxr, fxe, fxc, fxcc;
221
222 tolx = tolf = m_fAbortError;
223
224 switch(iDisplay) {
225 case 0:
226 prnt = 0;
227 break;
228 default:
229 prnt = 1;
230 }
231
232 header = " Iteration Func-count min f(x) Procedure";
233
234 posCopy = matPos;
235
236 n = posCopy.cols();
237
238 // Initialize parameters
239 rho = 1; chi = 2; psi = 0.5; sigma = 0.5;
240 onesn = Eigen::MatrixXd::Ones(1,n);
241 two2np1 = one2n = Eigen::MatrixXd::Zero(1,n);
242
243 for(int i = 0;i < n;i++) {
244 two2np1(i) = 1 + i;
245 one2n(i) = i;
246 }
247
248 v = v1 = Eigen::MatrixXd::Zero(n, n+1);
249 fv.resize(n+1);
250 idx.resize(n+1);
251 fv1.resize(n+1);
252
253 for(int i = 0;i < n; i++) {
254 v(i,0) = posCopy(i);
255 }
256
257 tempdip = dipfitError(posCopy, matData, sensors, matProjectors);
258 fv[0] = tempdip.error;
259
260 func_evals = 1;
261 itercount = 0;
262 how = "";
263
264 // Continue setting up the initial simplex.
265 // Following improvement suggested by L.Pfeffer at Stanford
266 usual_delta = 0.05; // 5 percent deltas for non-zero terms
267 zero_term_delta = 0.00025; // Even smaller delta for zero elements of x
268 xin = posCopy.transpose();
269
270 for(int j = 0;j < n;j++) {
271 y = xin;
272
273 if(y(j) != 0) {
274 y(j) = (1 + usual_delta) * y(j);
275 } else {
276 y(j) = zero_term_delta;
277 }
278
279 v.col(j+1).array() = y;
280 posCopy = y.transpose();
281 tempdip = dipfitError(posCopy, matData, sensors, matProjectors);
282 fv[j+1] = tempdip.error;
283 }
284
285 // Sort elements of fv
286 std::vector<HPISortStruct> vecSortStruct;
287
288 for (int i = 0; i < fv.size(); i++) {
289 HPISortStruct structTemp;
290 structTemp.base_arr = fv[i];
291 structTemp.idx = i;
292 vecSortStruct.push_back(structTemp);
293 }
294
295 std::sort(vecSortStruct.begin(), vecSortStruct.end(), compare);
296
297 for (int i = 0; i < vecSortStruct.size(); i++) {
298 idx[i] = vecSortStruct[i].idx;
299 }
300
301 for (int i = 0;i < n+1;i++) {
302 v1.col(i) = v.col(idx[i]);
303 fv1[i] = fv[idx[i]];
304 }
305
306 v = v1;fv = fv1;
307
308 how = "initial simplex";
309 itercount = itercount + 1;
310 func_evals = n + 1;
311
312 tempX1 = Eigen::MatrixXd::Zero(1,n);
313
314 while ((func_evals < iMaxfun) && (itercount < iMaxiter)) {
315
316 for (int i = 0;i < n;i++) {
317 tempX1(i) = std::fabs(fv[0] - fv[i+1]);
318 }
319
320 temp1 = tempX1.maxCoeff();
321
322 tempX2 = Eigen::MatrixXd::Zero(n,n);
323
324 for(int i = 0;i < n;i++) {
325 tempX2.col(i) = v.col(i+1) - v.col(0);
326 }
327
328 tempX2 = tempX2.array().abs();
329
330 temp2 = tempX2.maxCoeff();
331
332 if((temp1 <= tolf) && (temp2 <= tolx)) {
333 break;
334 }
335
336 xbar = v.block(0,0,n,n).rowwise().sum();
337 xbar /= n;
338
339 xr = (1+rho) * xbar - rho * v.block(0,n,v.rows(),1);
340
341 x = xr.transpose();
342 //std::cout << "Iteration Count: " << itercount << ":" << x << std::endl;
343
344 fxr = dipfitError(x, matData, sensors, matProjectors);
345
346 func_evals = func_evals+1;
347
348 if (fxr.error < fv[0]) {
349 // Calculate the expansion point
350 xe = (1 + rho * chi) * xbar - rho * chi * v.col(v.cols()-1);
351 x = xe.transpose();
352 fxe = dipfitError(x, matData, sensors, matProjectors);
353 func_evals = func_evals+1;
354
355 if(fxe.error < fxr.error) {
356 v.col(v.cols()-1) = xe;
357 fv[n] = fxe.error;
358 how = "expand";
359 } else {
360 v.col(v.cols()-1) = xr;
361 fv[n] = fxr.error;
362 how = "reflect";
363 }
364 }
365 else {
366 if(fxr.error < fv[n-1]) {
367 v.col(v.cols()-1) = xr;
368 fv[n] = fxr.error;
369 how = "reflect";
370 } else { // fxr.error >= fv[:,n-1]
371 // Perform contraction
372 if(fxr.error < fv[n]) {
373 // Perform an outside contraction
374 xc = (1 + psi * rho) * xbar - psi * rho * v.col(v.cols()-1);
375 x = xc.transpose();
376 fxc = dipfitError(x, matData, sensors, matProjectors);
377 func_evals = func_evals + 1;
378
379 if(fxc.error <= fxr.error) {
380 v.col(v.cols()-1) = xc;
381 fv[n] = fxc.error;
382 how = "contract outside";
383 } else {
384 // perform a shrink
385 how = "shrink";
386 }
387 } else {
388 xcc = (1 - psi) * xbar + psi * v.col(v.cols()-1);
389 x = xcc.transpose();
390 fxcc = dipfitError(x, matData, sensors, matProjectors);
391 func_evals = func_evals+1;
392 if(fxcc.error < fv[n]) {
393 v.col(v.cols()-1) = xcc;
394 fv[n] = fxcc.error;
395 how = "contract inside";
396 } else {
397 // perform a shrink
398 how = "shrink";
399 }
400 }
401
402 if(how.compare("shrink") == 0) {
403 for(int j = 1;j < n+1;j++) {
404 v.col(j).array() = v.col(0).array() + sigma * (v.col(j).array() - v.col(0).array());
405 x = v.col(j).array().transpose();
406 tempdip = dipfitError(x,matData, sensors, matProjectors);
407 fv[j] = tempdip.error;
408 }
409 }
410 }
411 }
412
413 // Sort elements of fv
414 vecSortStruct.clear();
415
416 for (int i = 0; i < fv.size(); i++) {
417 HPISortStruct structTemp;
418 structTemp.base_arr = fv[i];
419 structTemp.idx = i;
420 vecSortStruct.push_back(structTemp);
421 }
422
423 std::sort(vecSortStruct.begin(), vecSortStruct.end(), compare);
424 for (int i = 0; i < vecSortStruct.size(); i++) {
425 idx[i] = vecSortStruct[i].idx;
426 }
427
428 for (int i = 0;i < n+1;i++) {
429 v1.col(i) = v.col(idx[i]);
430 fv1[i] = fv[idx[i]];
431 }
432
433 v = v1;
434 fv = fv1;
435 itercount = itercount + 1;
436 }
437
438 x = v.col(0).transpose();
439
440 // Seok
441 iSimplexNumitr = itercount;
442
443 return x;
444}
445
#define M_PI
HPI (Head Position Indicator) fitting — estimates the MEG dewar-to-head transform from coil-current s...
Per-coil magnetic-dipole fitting workspace — Nelder-Mead optimiser plus leadfield computation for HPI...
Compact MEG sensor-geometry container (positions, orientations, integration weights) used by the HPI ...
Static linear-algebra helpers: SVD-based conditioning, block-diagonal assembly, sorted index pairs.
Inverse source estimation (MNE, dSPM, sLORETA, dipole fitting).
Residual error and moment vector from a single magnetic dipole fit iteration.
Eigen::MatrixXd moment
Helper for sorting HPI coil dipole fits by matching each fit to the nearest expected coil position.
Eigen::RowVectorXd m_sensorData
DipFitError dipfitError(const Eigen::MatrixXd &matPos, const Eigen::MatrixXd &matData, const InvSensorSet &sensors, const Eigen::MatrixXd &matProjectors)
Eigen::MatrixXd magnetic_dipole(Eigen::MatrixXd matPos, Eigen::MatrixXd matPnt, Eigen::MatrixXd matOri)
Eigen::MatrixXd m_matProjector
static bool compare(HPISortStruct a, HPISortStruct b)
Eigen::MatrixXd m_coilPos
Eigen::MatrixXd fminsearch(const Eigen::MatrixXd &matPos, int iMaxiter, int iMaxfun, int iDisplay, const Eigen::MatrixXd &matData, const Eigen::MatrixXd &matProjectors, const InvSensorSet &sensors, int &iSimplexNumitr)
Eigen::MatrixXd compute_leadfield(const Eigen::MatrixXd &matPos, const InvSensorSet &sensors)
Stores MEG sensor geometry (positions, orientations, weights, coil count) for a single sensor type.
Eigen::MatrixXd rmag(int iSensor) const
Eigen::RowVectorXd w(int iSensor) const
Eigen::MatrixXd cosmag(int iSensor) const
static Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > pinv(const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > &a)
Definition linalg.h:384