27#include <Eigen/Eigenvalues>
53 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>& vectors,
62 int np = dim * (dim + 1) / 2;
68 for (
int i = 0; i < np; ++i)
69 if (std::fabs(mat[i]) > std::fabs(mat[maxi]))
73 scale = 1.0 / mat[maxi];
76 MatrixXd dmat_full = MatrixXd::Zero(dim, dim);
78 for (
int i = 0; i < dim; ++i) {
79 for (
int j = 0; j <= i; ++j) {
80 double val = mat[idx] * scale;
81 dmat_full(i, j) = val;
82 dmat_full(j, i) = val;
86 SelfAdjointEigenSolver<MatrixXd> es;
87 es.compute(dmat_full);
91 lambda = es.eigenvalues() * scale;
92 vectors = es.eigenvectors().transpose().cast<
float>();
103 const QStringList& p_names,
104 const VectorXd& p_cov,
105 const VectorXd& p_cov_diag,
136 QList<FiffDirNode::SPtr> nodes;
143 std::unique_ptr<FiffSparseMatrix> cov_sparse_owner;
145 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
eigen;
151 std::unique_ptr<MNECovMatrix> res;
156 std::unique_ptr<MNEProjOp> op;
157 std::unique_ptr<MNESssData>
sss;
164 if (nodes.size() == 0) {
165 qWarning(
"No covariance matrix available in %s", name.toUtf8().data());
172 for (k = 0; k < nodes.size(); ++k) {
176 if (*t_pTag->toInt() ==
kind) {
181 if (!covnode || covnode->isEmpty()) {
182 qWarning(
"Desired covariance matrix not found from %s", name.toUtf8().data());
193 ncov = *t_pTag->toInt();
196 nfree = *t_pTag->toInt();
200 nnames =
names.size();
201 if (nnames !=
ncov) {
202 qCritical(
"Incorrect number of channel names for a covariance matrix");
207 if (!nodes[k]->find_tag(stream,
FIFF_MNE_COV, t_pTag)) {
214 d = t_pTag->toDouble();
215 for (p = 0; p <
ncov; p++)
221 qCritical(
"Sum of covariance matrix elements is zero!");
227 f = t_pTag->toFloat();
228 for (p = 0; p <
ncov; p++)
231 qWarning(
"Illegal data type for covariance matrix");
240 d = t_pTag->toDouble();
241 for (p = 0; p < nn; p++)
243 if (
cov.sum() == 0.0) {
244 qCritical(
"Sum of covariance matrix elements is zero!");
250 f = t_pTag->toFloat();
251 for (p = 0; p < nn; p++)
255 if (!cov_sparse_owner) {
262 const double* lambda_data =
static_cast<const double*
>(t_pTag->toDouble());
263 lambda = Eigen::Map<const Eigen::VectorXd>(lambda_data,
ncov);
269 tmp_eigen = t_pTag->toFloatMatrix().transpose();
270 eigen.resize(tmp_eigen.rows(), tmp_eigen.cols());
271 for (
int r = 0; r < tmp_eigen.rows(); ++r)
272 for (
int c = 0; c < tmp_eigen.cols(); ++c)
273 eigen(r, c) = tmp_eigen(r, c);
294 bads = stream->read_bad_channels(nodes[k]);
297 if (cov_sparse_owner)
299 else if (
cov.size() > 0)
304 qCritical(
"MNECovMatrix::read : covariance matrix data is not defined.");
308 res->eigen = std::move(
eigen);
309 res->lambda = std::move(
lambda);
316 if (res->lambda.size() > 0) {
318 for (k = 0; k < res->ncov; k++, res->nzero++)
319 if (res->lambda[k] > 0)
323 if (op && op->nitems > 0) {
324 res->proj = std::move(op);
327 res->sss = std::move(
sss);
349 res->proj =
proj ?
proj->dup() :
nullptr;
351 res->sss = std::make_unique<MNESssData>(*
sss);
373 if (src.size() == 0) {
374 qCritical(
"Covariance matrix is not diagonal or not decomposed.");
378 for (k = 0; k <
ncov; k++) {
393 VectorXd lambda_local;
394 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> local_eigen;
396 double magscale, gradscale, eegscale;
397 int nmag, ngrad, neeg, nok;
404 qCritical(
"Channels not classified. Rank cannot be determined.");
407 magscale = gradscale = eegscale = 0.0;
408 nmag = ngrad = neeg = 0;
409 for (k = 0; k <
ncov; k++) {
424 for (k = 0; k <
ncov; k++)
425 classes << QString::number(
ch_class[k]);
426 qDebug(
"%s", qPrintable(classes.join(
' ')));
430 magscale = magscale > 0.0 ? sqrt(nmag / magscale) : 0.0;
432 gradscale = gradscale > 0.0 ? sqrt(ngrad / gradscale) : 0.0;
434 eegscale = eegscale > 0.0 ? sqrt(neeg / eegscale) : 0.0;
436 qInfo(
"%d %g", nmag, magscale);
437 qInfo(
"%d %g", ngrad, gradscale);
438 qInfo(
"%d %g", neeg, eegscale);
440 scale_vec.resize(
ncov);
441 for (k = 0; k <
ncov; k++) {
443 scale_vec[k] = magscale;
445 scale_vec[k] = gradscale;
447 scale_vec[k] = eegscale;
451 cov_local.resize(
ncov * (
ncov + 1) / 2);
452 lambda_local.resize(
ncov);
454 for (j = 0; j <
ncov; j++)
455 for (k = 0; k <= j; k++)
460 for (k = 0; k <
ncov; k++)
461 ratios << QString::number(lambda_local[k] / lambda_local[
ncov - 1]);
462 qDebug(
"%s", qPrintable(ratios.join(
' ')));
465 for (k =
ncov - 1; k >= 0; k--) {
466 if (lambda_local[k] >= rank_threshold * lambda_local[
ncov - 1])
471 qInfo(
"\n\tEstimated covariance matrix rank = %d (%g)\n", nok, lambda_local[
ncov - nok] / lambda_local[
ncov - 1]);
472 if (use_rank > 0 && use_rank < nok) {
474 qInfo(
"\tUser-selected covariance matrix rank = %d (%g)\n", nok, lambda_local[
ncov - nok] / lambda_local[
ncov - 1]);
479 for (j = 0; j <
ncov - nok; j++)
480 lambda_local[j] = 0.0;
482 for (j = 0; j <
ncov; j++) {
484 mne_print_vector(stdout,
nullptr, local_eigen.row(j).data(),
ncov);
486 for (k = 0; k <
ncov; k++)
487 data1(j, k) = sqrt(lambda_local[j]) * local_eigen(j, k);
489 MatrixXd data2 = data1.transpose() * data1;
492 for (j = 0; j <
ncov; j++)
493 mne_print_dvector(stdout,
nullptr, data2.row(j).data(),
ncov);
499 for (k = 0; k <
ncov; k++)
500 if (scale_vec[k] > 0.0)
501 scale_vec[k] = 1.0 / scale_vec[k];
502 for (j = 0; j <
ncov; j++)
503 for (k = 0; k <= j; k++)
519 float rank_threshold = 1e-6f;
527 qInfo(
"\n\tEigenvalue decomposition had been precomputed.\n");
536 if ((rank =
condition(rank_threshold, use_rank)) < 0)
547 for (k = 0; k <
nzero; k++)
553 float meglike, eeglike;
558 meglike = eeglike = 0.0;
559 for (p = 0; p <
ncov; p++) {
561 eeglike += std::fabs(
eigen(k, p));
563 meglike += std::fabs(
eigen(k, p));
565 if (meglike > eeglike)
570 qInfo(
"\t%d MEG and %d EEG-like channels remain in the whitened data\n", nmeg, neeg);
590 return k + j * (j + 1) / 2;
592 return j + k * (k + 1) / 2;
603 qCritical(
"Channel information not available in classify_channels");
607 for (k = 0; k <
ncov; k++) {
609 for (p = 0; p < nchan; p++) {
610 if (QString::compare(chs[p].ch_name,
names[k]) == 0) {
631 qWarning(
"Incompatible covariance matrix. Cannot whiten the data.");
636 for (
int k = 0; k < nchan; k++)
637 whitened_data[k] = data[k] * inv[k];
639 Eigen::VectorXf tmp(nchan);
640 for (
int k =
nzero; k < nchan; k++)
641 tmp[k] =
eigen.row(k).dot(data.cast<
float>());
642 for (
int k = 0; k <
nzero; k++)
643 whitened_data[k] = 0.0;
644 for (
int k =
nzero; k < nchan; k++)
645 whitened_data[k] = tmp[k] * inv[k];
655 float sums[3], nn[3];
661 for (j = 0; j < nkind; j++) {
665 for (j = 0; j <
ncov; j++) {
671 qInfo(
"Average noise-covariance matrix diagonals:");
672 for (j = 0; j < nkind; j++) {
674 sums[j] = sums[j] / nn[j];
676 qInfo(
"\tMagnetometers : %-7.2f fT reg = %-6.2f", 1e15 * sqrt(sums[j]), regs[j]);
678 qInfo(
"\tPlanar gradiometers : %-7.2f fT/cm reg = %-6.2f", 1e13 * sqrt(sums[j]), regs[j]);
680 qInfo(
"\tEEG : %-7.2f uV reg = %-6.2f", 1e6 * sqrt(sums[j]), regs[j]);
681 sums[j] = regs[j] * sums[j];
684 for (j = 0; j <
ncov; j++)
688 qInfo(
"Noise-covariance regularized as requested.");
701 for (k = p = 0; k <
ncov; k++) {
716 const QList<FiffChInfo>& chs)
const
719 Eigen::VectorXd cov_local;
720 Eigen::VectorXd cov_diag_local;
721 QStringList picked_names;
723 std::unique_ptr<MNECovMatrix> res;
726 qCritical(
"No channels specified for picking in pick_chs_omit");
729 if (
names.isEmpty()) {
730 qCritical(
"No names in covariance matrix. Cannot do picking.");
733 Eigen::VectorXi pickVec = Eigen::VectorXi::Constant(new_ncov, -1);
734 for (j = 0; j < new_ncov; j++)
735 for (k = 0; k <
ncov; k++)
736 if (QString::compare(
names[k], new_names[j]) == 0) {
740 for (j = 0; j < new_ncov; j++) {
741 if (pickVec[j] < 0) {
742 qWarning(
"All desired channels not found in the covariance matrix (at least missing %s).", new_names[j].toUtf8().constData());
746 Eigen::VectorXi isMegVec;
748 isMegVec.resize(new_ncov);
749 if (!chs.isEmpty()) {
750 for (j = 0; j < new_ncov; j++)
756 for (j = 0; j < new_ncov; j++)
757 if (new_names[j].startsWith(
"MEG"))
764 cov_diag_local.resize(new_ncov);
765 for (j = 0; j < new_ncov; j++) {
766 cov_diag_local[j] =
cov_diag[pickVec[j]];
767 picked_names.append(
names[pickVec[j]]);
770 cov_local.resize(new_ncov * (new_ncov + 1) / 2);
771 for (j = 0; j < new_ncov; j++) {
772 picked_names.append(
names[pickVec[j]]);
773 for (k = 0; k <= j; k++) {
776 if (to < 0 || to > new_ncov * (new_ncov + 1) / 2 - 1) {
777 qCritical(
"Wrong destination index in pick_chs_omit : %d %d %d", j, k, to);
780 if (from < 0 || from >
ncov * (
ncov + 1) / 2 - 1) {
781 qCritical(
"Wrong source index in pick_chs_omit : %d %d %d", pickVec[j], pickVec[k], from);
784 cov_local[to] =
cov[from];
786 if (isMegVec[j] != isMegVec[k])
796 res->proj =
proj ?
proj->dup() :
nullptr;
797 res->sss =
sss ? std::make_unique<MNESssData>(*
sss) :
nullptr;
800 res->ch_class.resize(res->ncov);
801 for (k = 0; k < res->ncov; k++)
802 res->ch_class[k] =
ch_class[pickVec[k]];
FIFF channel descriptor record (FIFF_CH_INFO): per-channel logical/scanner numbers,...
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
constexpr auto FIFF_MNE_ROW_NAMES
constexpr auto FIFF_MNE_COV_EIGENVALUES
constexpr auto FIFFB_MNE_COV
constexpr auto FIFF_MNE_COV_EIGENVECTORS
constexpr auto FIFF_MNE_COV_DIAG
constexpr auto FIFF_UNIT_T
constexpr auto FIFF_MNE_COV_DIM
constexpr auto FIFF_MNE_COV_NFREE
constexpr auto FIFF_MNE_COV
constexpr auto FIFF_MNE_COV_KIND
FIFF tag: the 16-byte tag header (kind, type, size, next) plus its decoded payload.
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
constexpr auto FIFFT_DOUBLE
constexpr auto FIFFV_SSS_JOB_NOTHING
constexpr auto FIFFT_FLOAT
FIFF sparse matrix: column / row-compressed sparse storage backed by Eigen::SparseMatrix.
Single SSP projection vector with kind/active flag and channel labels.
Composite SSP projection operator P = I - U U^T assembled from a list of MNELIB::MNEProjItem.
int mne_decompose_eigen(const VectorXd &mat, VectorXd &lambda, Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > &vectors, int dim)
Signal Space Separation (Maxwell filter) basis metadata stored alongside MEG raw data.
Legacy MNE-C noise covariance container preserved for cross-toolchain compatibility.
constexpr auto MNE_COV_CH_EEG
constexpr auto MNE_COV_CH_MEG_GRAD
constexpr auto MNE_COV_CH_UNKNOWN
constexpr auto MNE_COV_CH_MEG_MAG
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
Per-channel FIFF descriptor: identifiers, kind, calibration, coil type, channel-frame coil position a...
QSharedPointer< FiffDirNode > SPtr
Sparse FIFF matrix: CCS or RCS storage with the value / index / pointer triple as written by FiffStre...
static FiffSparseMatrix::UPtr fiff_get_float_sparse_matrix(const FIFFLIB::FiffTag::UPtr &tag)
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
QSharedPointer< FiffStream > SPtr
static QStringList split_name_list(QString p_sNameList)
std::unique_ptr< FiffTag > UPtr
Eigen::VectorXd inv_lambda
std::unique_ptr< FIFFLIB::FiffSparseMatrix > cov_sparse
int condition(float rank_threshold, int use_rank)
static std::unique_ptr< MNECovMatrix > create_sparse(int kind, int ncov, const QStringList &names, FIFFLIB::FiffSparseMatrix *cov_sparse)
void regularize(const Eigen::Vector3f ®s)
int decompose_eigen_small(float p_small, int use_rank)
int classify_channels(const QList< FIFFLIB::FiffChInfo > &chs, int nchan)
std::unique_ptr< MNEProjOp > proj
static std::unique_ptr< MNECovMatrix > create_diag(int kind, int ncov, const QStringList &names, const Eigen::VectorXd &cov_diag)
static std::unique_ptr< MNECovMatrix > create_dense(int kind, int ncov, const QStringList &names, const Eigen::VectorXd &cov)
std::unique_ptr< MNECovMatrix > dup() const
MNECovMatrix(int p_kind, int p_ncov, const QStringList &p_names, const Eigen::VectorXd &p_cov, const Eigen::VectorXd &p_cov_diag, FIFFLIB::FiffSparseMatrix *p_cov_sparse)
std::unique_ptr< MNESssData > sss
static std::unique_ptr< MNECovMatrix > read(const QString &name, int kind)
static std::unique_ptr< MNECovMatrix > create(int kind, int ncov, const QStringList &names, const Eigen::VectorXd &cov, const Eigen::VectorXd &cov_diag)
Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > eigen
static int lt_packed_index(int j, int k)
std::unique_ptr< MNECovMatrix > pick_chs_omit(const QStringList &new_names, int new_ncov, int omit_meg_eeg, const QList< FIFFLIB::FiffChInfo > &chs) const
int whiten_vector(Eigen::Ref< Eigen::VectorXf > data, Eigen::Ref< Eigen::VectorXf > whitened_data, int nchan) const
static std::unique_ptr< MNEProjOp > read_from_node(FIFFLIB::FiffStream::SPtr &stream, const FIFFLIB::FiffDirNode::SPtr &start)
Read all linear projection items from a FIFF tree node.
static std::unique_ptr< MNESssData > read_from_node(QSharedPointer< FIFFLIB::FiffStream > &stream, const QSharedPointer< FIFFLIB::FiffDirNode > &start)