27#include <Eigen/Eigenvalues>
52 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>& vectors,
61 int np = dim*(dim+1)/2;
67 for(
int i = 0; i < np; ++i)
68 if (std::fabs(mat[i]) > std::fabs(mat[maxi]))
72 scale = 1.0/mat[maxi];
75 MatrixXd dmat_full = MatrixXd::Zero(dim,dim);
77 for (
int i = 0; i < dim; ++i) {
78 for(
int j = 0; j <= i; ++j) {
79 double val = mat[idx]*scale;
85 SelfAdjointEigenSolver<MatrixXd> es;
86 es.compute(dmat_full);
90 lambda = es.eigenvalues() * scale;
91 vectors = es.eigenvectors().transpose().cast<
float>();
102 const QStringList& p_names,
103 const VectorXd& p_cov,
104 const VectorXd& p_cov_diag,
135 QList<FiffDirNode::SPtr> nodes;
142 std::unique_ptr<FiffSparseMatrix> cov_sparse_owner;
144 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
eigen;
150 std::unique_ptr<MNECovMatrix> res;
155 std::unique_ptr<MNEProjOp> op;
156 std::unique_ptr<MNESssData>
sss;
163 if (nodes.size() == 0) {
164 qWarning(
"No covariance matrix available in %s", name.toUtf8().data());
171 for (k = 0; k < nodes.size(); ++k) {
175 if (*t_pTag->toInt() ==
kind) {
180 if (covnode->isEmpty()) {
181 qWarning(
"Desired covariance matrix not found from %s", name.toUtf8().data());
192 ncov = *t_pTag->toInt();
195 nfree = *t_pTag->toInt();
199 nnames =
names.size();
200 if (nnames !=
ncov) {
201 qCritical(
"Incorrect number of channel names for a covariance matrix");
206 if (!nodes[k]->find_tag(stream,
FIFF_MNE_COV, t_pTag)) {
213 d = t_pTag->toDouble();
214 for (p = 0; p <
ncov; p++)
220 qCritical(
"Sum of covariance matrix elements is zero!");
226 f = t_pTag->toFloat();
227 for (p = 0; p <
ncov; p++)
230 qWarning(
"Illegal data type for covariance matrix");
239 d = t_pTag->toDouble();
240 for (p = 0; p < nn; p++)
242 if (
cov.sum() == 0.0) {
243 qCritical(
"Sum of covariance matrix elements is zero!");
249 f = t_pTag->toFloat();
250 for (p = 0; p < nn; p++)
254 if (!cov_sparse_owner) {
261 const double *lambda_data =
static_cast<const double *
>(t_pTag->toDouble());
262 lambda = Eigen::Map<const Eigen::VectorXd>(lambda_data,
ncov);
268 tmp_eigen = t_pTag->toFloatMatrix().transpose();
269 eigen.resize(tmp_eigen.rows(), tmp_eigen.cols());
270 for (
int r = 0; r < tmp_eigen.rows(); ++r)
271 for (
int c = 0; c < tmp_eigen.cols(); ++c)
272 eigen(r, c) = tmp_eigen(r, c);
293 bads = stream->read_bad_channels(nodes[k]);
296 if (cov_sparse_owner)
298 else if (
cov.size() > 0)
303 qCritical(
"MNECovMatrix::read : covariance matrix data is not defined.");
307 res->eigen = std::move(
eigen);
308 res->lambda = std::move(
lambda);
315 if (res->lambda.size() > 0) {
317 for (k = 0; k < res->ncov; k++, res->nzero++)
318 if (res->lambda[k] > 0)
322 if (op && op->nitems > 0) {
323 res->proj = std::move(op);
326 res->sss = std::move(
sss);
348 res->proj =
proj ?
proj->dup() :
nullptr;
350 res->sss = std::make_unique<MNESssData>(*
sss);
372 if (src.size() == 0) {
373 qCritical(
"Covariance matrix is not diagonal or not decomposed.");
377 for (k = 0; k <
ncov; k++) {
392 VectorXd lambda_local;
393 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> local_eigen;
395 double magscale,gradscale,eegscale;
396 int nmag,ngrad,neeg,nok;
403 qCritical(
"Channels not classified. Rank cannot be determined.");
406 magscale = gradscale = eegscale = 0.0;
407 nmag = ngrad = neeg = 0;
408 for (k = 0; k <
ncov; k++) {
423 fprintf(stdout,
"\n");
426 magscale = magscale > 0.0 ? sqrt(nmag/magscale) : 0.0;
428 gradscale = gradscale > 0.0 ? sqrt(ngrad/gradscale) : 0.0;
430 eegscale = eegscale > 0.0 ? sqrt(neeg/eegscale) : 0.0;
432 fprintf(stdout,
"%d %g\n",nmag,magscale);
433 fprintf(stdout,
"%d %g\n",ngrad,gradscale);
434 fprintf(stdout,
"%d %g\n",neeg,eegscale);
436 scale_vec.resize(
ncov);
437 for (k = 0; k <
ncov; k++) {
439 scale_vec[k] = magscale;
441 scale_vec[k] = gradscale;
443 scale_vec[k] = eegscale;
448 lambda_local.resize(
ncov);
450 for (j = 0; j <
ncov; j++)
451 for (k = 0; k <= j; k++)
455 for (k = 0; k <
ncov; k++)
456 fprintf(stdout,
"%g ",lambda_local[k]/lambda_local[
ncov-1]);
457 fprintf(stdout,
"\n");
460 for (k =
ncov-1; k >= 0; k--) {
461 if (lambda_local[k] >= rank_threshold*lambda_local[
ncov-1])
466 qInfo(
"\n\tEstimated covariance matrix rank = %d (%g)\n",nok,lambda_local[
ncov-nok]/lambda_local[
ncov-1]);
467 if (use_rank > 0 && use_rank < nok) {
469 qInfo(
"\tUser-selected covariance matrix rank = %d (%g)\n",nok,lambda_local[
ncov-nok]/lambda_local[
ncov-1]);
474 for (j = 0; j <
ncov-nok; j++)
475 lambda_local[j] = 0.0;
477 for (j = 0; j <
ncov; j++) {
479 mne_print_vector(stdout,
nullptr,local_eigen.row(j).data(),
ncov);
481 for (k = 0; k <
ncov; k++)
482 data1(j,k) = sqrt(lambda_local[j])*local_eigen(j,k);
484 MatrixXd data2 = data1.transpose() * data1;
487 for (j = 0; j <
ncov; j++)
488 mne_print_dvector(stdout,
nullptr,data2.row(j).data(),
ncov);
494 for (k = 0; k <
ncov; k++)
495 if (scale_vec[k] > 0.0)
496 scale_vec[k] = 1.0/scale_vec[k];
497 for (j = 0; j <
ncov; j++)
498 for (k = 0; k <= j; k++)
514 float rank_threshold = 1e-6;
522 qInfo(
"\n\tEigenvalue decomposition had been precomputed.\n");
532 if ((rank =
condition(rank_threshold,use_rank)) < 0)
543 for (k = 0; k <
nzero; k++)
549 float meglike,eeglike;
554 meglike = eeglike = 0.0;
555 for (p = 0; p <
ncov; p++) {
557 eeglike += std::fabs(
eigen(k,p));
559 meglike += std::fabs(
eigen(k,p));
561 if (meglike > eeglike)
566 qInfo(
"\t%d MEG and %d EEG-like channels remain in the whitened data\n",nmeg,neeg);
586 return k + j*(j+1)/2;
588 return j + k*(k+1)/2;
599 qCritical(
"Channel information not available in classify_channels");
603 for (k = 0; k <
ncov; k++) {
605 for (p = 0; p < nchan; p++) {
606 if (QString::compare(chs[p].ch_name,
names[k]) == 0) {
628 qWarning(
"Incompatible covariance matrix. Cannot whiten the data.");
633 for (
int k = 0; k < nchan; k++)
634 whitened_data[k] = data[k]*inv[k];
637 Eigen::VectorXf tmp(nchan);
638 for (
int k =
nzero; k < nchan; k++)
639 tmp[k] =
eigen.row(k).dot(data.cast<
float>());
640 for (
int k = 0; k <
nzero; k++)
641 whitened_data[k] = 0.0;
642 for (
int k =
nzero; k < nchan; k++)
643 whitened_data[k] = tmp[k]*inv[k];
659 for (j = 0; j < nkind; j++) {
663 for (j = 0; j <
ncov; j++) {
669 qInfo(
"Average noise-covariance matrix diagonals:");
670 for (j = 0; j < nkind; j++) {
672 sums[j] = sums[j]/nn[j];
674 qInfo(
"\tMagnetometers : %-7.2f fT reg = %-6.2f",1e15*sqrt(sums[j]),regs[j]);
676 qInfo(
"\tPlanar gradiometers : %-7.2f fT/cm reg = %-6.2f",1e13*sqrt(sums[j]),regs[j]);
678 qInfo(
"\tEEG : %-7.2f uV reg = %-6.2f",1e6*sqrt(sums[j]),regs[j]);
679 sums[j] = regs[j]*sums[j];
682 for (j = 0; j <
ncov; j++)
686 qInfo(
"Noise-covariance regularized as requested.");
699 for (k = p = 0; k <
ncov; k++) {
714 const QList<FiffChInfo>& chs)
const
717 Eigen::VectorXd cov_local;
718 Eigen::VectorXd cov_diag_local;
719 QStringList picked_names;
721 std::unique_ptr<MNECovMatrix> res;
724 qCritical(
"No channels specified for picking in pick_chs_omit");
727 if (
names.isEmpty()) {
728 qCritical(
"No names in covariance matrix. Cannot do picking.");
731 Eigen::VectorXi pickVec = Eigen::VectorXi::Constant(new_ncov, -1);
732 for (j = 0; j < new_ncov; j++)
733 for (k = 0; k <
ncov; k++)
734 if (QString::compare(
names[k],new_names[j]) == 0) {
738 for (j = 0; j < new_ncov; j++) {
739 if (pickVec[j] < 0) {
740 qWarning(
"All desired channels not found in the covariance matrix (at least missing %s).", new_names[j].toUtf8().constData());
744 Eigen::VectorXi isMegVec;
746 isMegVec.resize(new_ncov);
747 if (!chs.isEmpty()) {
748 for (j = 0; j < new_ncov; j++)
755 for (j = 0; j < new_ncov; j++)
756 if (new_names[j].startsWith(
"MEG"))
763 cov_diag_local.resize(new_ncov);
764 for (j = 0; j < new_ncov; j++) {
765 cov_diag_local[j] =
cov_diag[pickVec[j]];
766 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]];
int mne_decompose_eigen(const VectorXd &mat, VectorXd &lambda, Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > &vectors, int dim)
Legacy MNE-C noise covariance container preserved for cross-toolchain compatibility.
#define MNE_COV_CH_MEG_MAG
#define MNE_COV_CH_MEG_GRAD
#define MNE_COV_CH_UNKNOWN
Composite SSP projection operator P = I - U U^T assembled from a list of MNELIB::MNEProjItem.
Signal Space Separation (Maxwell filter) basis metadata stored alongside MEG raw data.
Single SSP projection vector with kind/active flag and channel labels.
FIFF sparse matrix: column / row-compressed sparse storage backed by Eigen::SparseMatrix.
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
#define FIFF_MNE_COV_KIND
#define FIFF_MNE_COV_DIAG
#define FIFF_MNE_ROW_NAMES
#define FIFF_MNE_COV_EIGENVALUES
#define FIFF_MNE_COV_NFREE
#define FIFF_MNE_COV_EIGENVECTORS
FIFF channel descriptor record (FIFF_CH_INFO): per-channel logical/scanner numbers,...
#define FIFFV_SSS_JOB_NOTHING
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,...
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)