78static constexpr double SSS_PI =
M_PI;
91MatrixXd regPinv(
const MatrixXd& A,
double reg = 1e-5)
93 JacobiSVD<MatrixXd> svd(A, ComputeThinU | ComputeThinV);
94 const VectorXd& sv = svd.singularValues();
95 double threshold = reg * sv(0);
97 for (
int i = 0; i < invSv.size(); ++i) {
98 invSv(i) = (sv(i) > threshold) ? 1.0 / sv(i) : 0.0;
100 return svd.matrixV() * invSv.asDiagonal() * svd.matrixU().transpose();
107MatrixXd orthonormalCols(
const MatrixXd& A)
110 return MatrixXd(A.rows(), 0);
113 ColPivHouseholderQR<MatrixXd> qr(A);
114 qr.setThreshold(1e-12);
115 const int rank = qr.rank();
117 return MatrixXd(A.rows(), 0);
120 return qr.householderQ() * MatrixXd::Identity(A.rows(), rank);
129void SSS::computeNormALP(
int lmax,
double cosTheta,
double sinTheta,
130 MatrixXd& P, MatrixXd& dP)
133 if (sinTheta < 1e-12) {
137 const int sz = lmax + 2;
152 MatrixXd Praw(sz, sz);
156 Praw(1, 0) = cosTheta;
157 Praw(1, 1) = -sinTheta;
159 for (
int l = 2; l <= lmax + 1; ++l) {
160 Praw(l, l) = -(2 * l - 1) * sinTheta * Praw(l - 1, l - 1);
161 Praw(l, l - 1) = (2 * l - 1) * cosTheta * Praw(l - 1, l - 1);
162 for (
int m = 0; m <= l - 2; ++m) {
163 Praw(l, m) = ((2 * l - 1) * cosTheta * Praw(l - 1, m)
164 - (l - 1 + m) * Praw(l - 2, m))
165 /
static_cast<double>(l - m);
170 for (
int l = 1; l <= lmax; ++l) {
171 for (
int m = 0; m <= l; ++m) {
175 for (
int k = l - m + 1; k <= l + m; ++k) {
176 fac /=
static_cast<double>(k);
178 double norm = (m == 0)
179 ? std::sqrt((2.0 * l + 1.0) / (4.0 * SSS_PI) * fac)
180 : std::sqrt(2.0 * (2.0 * l + 1.0) / (4.0 * SSS_PI) * fac);
182 P(l, m) = norm * Praw(l, m);
187 double sinThetaDeriv =
static_cast<double>(l) * cosTheta * Praw(l, m);
189 sinThetaDeriv -=
static_cast<double>(l + m) * Praw(l - 1, m);
191 dP(l, m) = norm * sinThetaDeriv / sinTheta;
198Vector3d SSS::basisGradCart(
int l,
int m,
bool bInternal,
199 const Vector3d& rPos,
200 const MatrixXd& P,
const MatrixXd& dP,
201 double cosTheta,
double sinTheta,
202 double cosPhi,
double sinPhi)
205 double r = rPos.norm();
207 return Vector3d::Zero();
210 const int absM = std::abs(m);
223 double Plm = P(l, absM);
224 double dPlm = dP(l, absM);
226 double angFactor, dAngFactor_theta, dAngFactor_phi;
229 dAngFactor_theta = 0.0;
230 dAngFactor_phi = 0.0;
232 double cosmPhi = std::cos(
static_cast<double>(m) * std::atan2(sinPhi, cosPhi));
233 double sinmPhi = std::sin(
static_cast<double>(m) * std::atan2(sinPhi, cosPhi));
235 dAngFactor_theta = 0.0;
236 dAngFactor_phi = -
static_cast<double>(m) * sinmPhi;
239 double cosmPhi = std::cos(
static_cast<double>(absM) * std::atan2(sinPhi, cosPhi));
240 double sinmPhi = std::sin(
static_cast<double>(absM) * std::atan2(sinPhi, cosPhi));
242 dAngFactor_theta = 0.0;
243 dAngFactor_phi =
static_cast<double>(absM) * cosmPhi;
247 double Ylm = Plm * angFactor;
250 double dYdTheta = dPlm * angFactor;
253 double dYdPhi = Plm * dAngFactor_phi;
266 double radPow, Gr_coeff, Gtu_coeff;
268 radPow = std::pow(r, l - 1);
269 Gr_coeff =
static_cast<double>(l) * radPow;
272 radPow = std::pow(r, -(l + 2));
273 Gr_coeff = -
static_cast<double>(l + 1) * radPow;
277 double Gr = Gr_coeff * Ylm;
278 double Gtheta = Gtu_coeff * dYdTheta;
279 double GphiTimesSin = Gtu_coeff * dYdPhi;
290 double Gphi = (sinTheta > 1e-12) ? GphiTimesSin / sinTheta : 0.0;
292 double gx = Gr * sinTheta * cosPhi + Gtheta * cosTheta * cosPhi - Gphi * sinPhi;
293 double gy = Gr * sinTheta * sinPhi + Gtheta * cosTheta * sinPhi + Gphi * cosPhi;
294 double gz = Gr * cosTheta - Gtheta * sinTheta;
296 return Vector3d(gx, gy, gz);
310 for (
int i = 0; i < fiffInfo.
nchan; ++i) {
311 int kind = fiffInfo.
chs[i].kind;
319 qWarning() <<
"SSS::computeBasis: no MEG channels found in FiffInfo.";
331 for (
int si = 0; si < nMeg; ++si) {
335 Vector3d rPos(
static_cast<double>(ch.
chpos.
r0(0)) - params.
origin(0),
340 Vector3d normal(
static_cast<double>(ch.
chpos.
ez(0)),
341 static_cast<double>(ch.
chpos.
ez(1)),
342 static_cast<double>(ch.
chpos.
ez(2)));
345 double nNorm = normal.norm();
352 double r = rPos.norm();
356 double cosTheta = rPos(2) / r;
357 double sinTheta = std::sqrt(rPos(0) * rPos(0) + rPos(1) * rPos(1)) / r;
358 if (sinTheta < 1e-12) sinTheta = 1e-12;
359 double phi = std::atan2(rPos(1), rPos(0));
360 double cosPhi = std::cos(phi);
361 double sinPhi = std::sin(phi);
365 computeNormALP(lmax, cosTheta, sinTheta, P, dP);
369 for (
int l = 1; l <= params.
iOrderIn; ++l) {
370 for (
int m = -l; m <= l; ++m) {
371 Vector3d grad = basisGradCart(l, m,
true,
373 cosTheta, sinTheta, cosPhi, sinPhi);
374 basis.
matSin(si, colIn) = normal.dot(grad);
381 for (
int l = 1; l <= params.
iOrderOut; ++l) {
382 for (
int m = -l; m <= l; ++m) {
383 Vector3d grad = basisGradCart(l, m,
false,
385 cosTheta, sinTheta, cosPhi, sinPhi);
386 basis.
matSout(si, colOut) = normal.dot(grad);
397 MatrixXd S(nMeg, basis.
iNin + basis.
iNout);
406 const MatrixXd Qin = orthonormalCols(basis.
matSin);
407 const MatrixXd Qout = orthonormalCols(basis.
matSout);
409 if (Qin.cols() == 0) {
410 basis.
matProjIn = MatrixXd::Zero(nMeg, nMeg);
414 const MatrixXd I = MatrixXd::Identity(nMeg, nMeg);
415 const MatrixXd QoutPerp = Qout.cols() > 0 ? (I - Qout * Qout.transpose()) : I;
416 const MatrixXd gram = Qin.transpose() * QoutPerp * Qin;
417 basis.
matProjIn = Qin * regPinv(gram, params.
dRegIn) * Qin.transpose() * QoutPerp;
431 MatrixXd matOut = matData;
434 MatrixXd megData(nMeg, matData.cols());
435 for (
int i = 0; i < nMeg; ++i) {
440 MatrixXd megSss = basis.
matProjIn * megData;
443 for (
int i = 0; i < nMeg; ++i) {
462 const int nSamp =
static_cast<int>(matData.cols());
463 const int bufLen = std::min(iBufferLength, nSamp);
465 MatrixXd matOut = matData;
468 MatrixXd megData(nMeg, nSamp);
469 for (
int i = 0; i < nMeg; ++i) {
481 while (offset < nSamp) {
482 int winLen = std::min(bufLen, nSamp - offset);
485 MatrixXd cInWin = cIn.middleCols(offset, winLen);
486 MatrixXd cOutWin = cOut.middleCols(offset, winLen);
492 JacobiSVD<MatrixXd> svd(cOutWin, ComputeThinU | ComputeThinV);
493 const VectorXd& sv = svd.singularValues();
494 const MatrixXd& V = svd.matrixV();
497 double svMax = (sv.size() > 0) ? sv(0) : 0.0;
508 for (
int k = 0; k < sv.size(); ++k) {
509 if (sv(k) / svMax > dCorrLimit) {
517 Vr = V.leftCols(nRemove);
519 MatrixXd cInWinClean = cInWin - cInWin * (Vr * Vr.transpose());
520 cIn.middleCols(offset, winLen) = cInWinClean;
527 MatrixXd megTsss = basis.
matSin * cIn;
530 for (
int i = 0; i < nMeg; ++i) {
FiffChInfo class declaration.
Declaration of the SSS class implementing Signal Space Separation (SSS) and temporal Signal Space Sep...
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
static Basis computeBasis(const FIFFLIB::FiffInfo &fiffInfo, const Params ¶ms=Params())
static Eigen::MatrixXd apply(const Eigen::MatrixXd &matData, const Basis &basis)
static Eigen::MatrixXd applyTemporal(const Eigen::MatrixXd &matData, const Basis &basis, int iBufferLength=10000, double dCorrLimit=0.98)
Precomputed SSS basis and projectors for a given sensor array.
Eigen::MatrixXd matPinvAll
QVector< int > megChannelIdx
Eigen::MatrixXd matProjIn
FIFF measurement file information.