47#include <Eigen/Eigenvalues>
76 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>& vectors,
85 int np = dim*(dim+1)/2;
91 for(
int i = 0; i < np; ++i)
92 if (std::fabs(mat[i]) > std::fabs(mat[maxi]))
96 scale = 1.0/mat[maxi];
99 MatrixXd dmat_full = MatrixXd::Zero(dim,dim);
101 for (
int i = 0; i < dim; ++i) {
102 for(
int j = 0; j <= i; ++j) {
103 double val = mat[idx]*scale;
104 dmat_full(i,j) = val;
105 dmat_full(j,i) = val;
109 SelfAdjointEigenSolver<MatrixXd> es;
110 es.compute(dmat_full);
114 lambda = es.eigenvalues() * scale;
115 vectors = es.eigenvectors().transpose().cast<
float>();
126 const QStringList& p_names,
127 const VectorXd& p_cov,
128 const VectorXd& p_cov_diag,
159 QList<FiffDirNode::SPtr> nodes;
168 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
eigen;
174 std::unique_ptr<MNECovMatrix> res;
179 std::unique_ptr<MNEProjOp> op;
180 std::unique_ptr<MNESssData>
sss;
187 if (nodes.size() == 0) {
188 qWarning(
"No covariance matrix available in %s", name.toUtf8().data());
195 for (k = 0; k < nodes.size(); ++k) {
199 if (*t_pTag->toInt() ==
kind) {
204 if (covnode->isEmpty()) {
205 qWarning(
"Desired covariance matrix not found from %s", name.toUtf8().data());
216 ncov = *t_pTag->toInt();
219 nfree = *t_pTag->toInt();
223 nnames =
names.size();
224 if (nnames !=
ncov) {
225 qCritical(
"Incorrect number of channel names for a covariance matrix");
230 if (!nodes[k]->find_tag(stream,
FIFF_MNE_COV, t_pTag)) {
237 d = t_pTag->toDouble();
238 for (p = 0; p <
ncov; p++)
244 qCritical(
"Sum of covariance matrix elements is zero!");
250 f = t_pTag->toFloat();
251 for (p = 0; p <
ncov; p++)
254 qWarning(
"Illegal data type for covariance matrix");
263 d = t_pTag->toDouble();
264 for (p = 0; p < nn; p++)
266 if (
cov.sum() == 0.0) {
267 qCritical(
"Sum of covariance matrix elements is zero!");
273 f = t_pTag->toFloat();
274 for (p = 0; p < nn; p++)
278 if (!cov_sparse_uptr) {
286 const double *lambda_data =
static_cast<const double *
>(t_pTag->toDouble());
287 lambda = Eigen::Map<const Eigen::VectorXd>(lambda_data,
ncov);
294 tmp_eigen = t_pTag->toFloatMatrix().transpose();
295 eigen.resize(tmp_eigen.rows(), tmp_eigen.cols());
296 for (
int r = 0; r < tmp_eigen.rows(); ++r)
297 for (
int c = 0; c < tmp_eigen.cols(); ++c)
298 eigen(r, c) = tmp_eigen(r, c);
321 bads = stream->read_bad_channels(nodes[k]);
326 else if (
cov.size() > 0)
331 qCritical(
"MNECovMatrix::read : covariance matrix data is not defined.");
336 res->eigen = std::move(
eigen);
337 res->lambda = std::move(
lambda);
344 if (res->lambda.size() > 0) {
346 for (k = 0; k < res->ncov; k++, res->nzero++)
347 if (res->lambda[k] > 0)
351 if (op && op->nitems > 0) {
352 res->proj = std::move(op);
355 res->sss = std::move(
sss);
377 res->proj.reset(
proj ?
proj->dup() :
nullptr);
379 res->sss = std::make_unique<MNESssData>(*
sss);
401 if (src.size() == 0) {
402 qCritical(
"Covariance matrix is not diagonal or not decomposed.");
406 for (k = 0; k <
ncov; k++) {
421 VectorXd lambda_local;
422 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> local_eigen;
424 double magscale,gradscale,eegscale;
425 int nmag,ngrad,neeg,nok;
432 qCritical(
"Channels not classified. Rank cannot be determined.");
435 magscale = gradscale = eegscale = 0.0;
436 nmag = ngrad = neeg = 0;
437 for (k = 0; k <
ncov; k++) {
452 fprintf(stdout,
"\n");
455 magscale = magscale > 0.0 ? sqrt(nmag/magscale) : 0.0;
457 gradscale = gradscale > 0.0 ? sqrt(ngrad/gradscale) : 0.0;
459 eegscale = eegscale > 0.0 ? sqrt(neeg/eegscale) : 0.0;
461 fprintf(stdout,
"%d %g\n",nmag,magscale);
462 fprintf(stdout,
"%d %g\n",ngrad,gradscale);
463 fprintf(stdout,
"%d %g\n",neeg,eegscale);
465 scale_vec.resize(
ncov);
466 for (k = 0; k <
ncov; k++) {
468 scale_vec[k] = magscale;
470 scale_vec[k] = gradscale;
472 scale_vec[k] = eegscale;
477 lambda_local.resize(
ncov);
479 for (j = 0; j <
ncov; j++)
480 for (k = 0; k <= j; k++)
484 for (k = 0; k <
ncov; k++)
485 fprintf(stdout,
"%g ",lambda_local[k]/lambda_local[
ncov-1]);
486 fprintf(stdout,
"\n");
489 for (k =
ncov-1; k >= 0; k--) {
490 if (lambda_local[k] >= rank_threshold*lambda_local[
ncov-1])
495 printf(
"\n\tEstimated covariance matrix rank = %d (%g)\n",nok,lambda_local[
ncov-nok]/lambda_local[
ncov-1]);
496 if (use_rank > 0 && use_rank < nok) {
498 printf(
"\tUser-selected covariance matrix rank = %d (%g)\n",nok,lambda_local[
ncov-nok]/lambda_local[
ncov-1]);
503 for (j = 0; j <
ncov-nok; j++)
504 lambda_local[j] = 0.0;
506 for (j = 0; j <
ncov; j++) {
508 mne_print_vector(stdout,NULL,local_eigen.row(j).data(),
ncov);
510 for (k = 0; k <
ncov; k++)
511 data1(j,k) = sqrt(lambda_local[j])*local_eigen(j,k);
513 MatrixXd data2 = data1.transpose() * data1;
516 for (j = 0; j <
ncov; j++)
517 mne_print_dvector(stdout,NULL,data2.row(j).data(),
ncov);
523 for (k = 0; k <
ncov; k++)
524 if (scale_vec[k] > 0.0)
525 scale_vec[k] = 1.0/scale_vec[k];
526 for (j = 0; j <
ncov; j++)
527 for (k = 0; k <= j; k++)
543 float rank_threshold = 1e-6;
551 printf(
"\n\tEigenvalue decomposition had been precomputed.\n");
561 if ((rank =
condition(rank_threshold,use_rank)) < 0)
569 for (k = 0; k <
nzero; k++)
575 float meglike,eeglike;
580 meglike = eeglike = 0.0;
581 for (p = 0; p <
ncov; p++) {
583 eeglike += std::fabs(
eigen(k,p));
585 meglike += std::fabs(
eigen(k,p));
587 if (meglike > eeglike)
592 printf(
"\t%d MEG and %d EEG-like channels remain in the whitened data\n",nmeg,neeg);
618 return k + j*(j+1)/2;
620 return j + k*(k+1)/2;
631 qCritical(
"Channel information not available in classify_channels");
635 for (k = 0; k <
ncov; k++) {
637 for (p = 0; p < nchan; p++) {
638 if (QString::compare(chs[p].ch_name,
names[k]) == 0) {
660 qWarning(
"Incompatible covariance matrix. Cannot whiten the data.");
665 for (
int k = 0; k < nchan; k++)
666 whitened_data[k] = data[k]*inv[k];
669 std::vector<float> tmp(nchan);
670 for (
int k =
nzero; k < nchan; k++)
671 tmp[k] =
eigen.row(k).dot(data.cast<
float>());
672 for (
int k = 0; k <
nzero; k++)
673 whitened_data[k] = 0.0;
674 for (
int k =
nzero; k < nchan; k++)
675 whitened_data[k] = tmp[k]*inv[k];
691 for (j = 0; j < nkind; j++) {
695 for (j = 0; j <
ncov; j++) {
701 qInfo(
"Average noise-covariance matrix diagonals:");
702 for (j = 0; j < nkind; j++) {
704 sums[j] = sums[j]/nn[j];
706 qInfo(
"\tMagnetometers : %-7.2f fT reg = %-6.2f",1e15*sqrt(sums[j]),regs[j]);
708 qInfo(
"\tPlanar gradiometers : %-7.2f fT/cm reg = %-6.2f",1e13*sqrt(sums[j]),regs[j]);
710 qInfo(
"\tEEG : %-7.2f uV reg = %-6.2f",1e6*sqrt(sums[j]),regs[j]);
711 sums[j] = regs[j]*sums[j];
714 for (j = 0; j <
ncov; j++)
718 qInfo(
"Noise-covariance regularized as requested.");
731 for (k = p = 0; k <
ncov; k++) {
746 const QList<FiffChInfo>& chs)
const
750 Eigen::VectorXd cov_local;
751 Eigen::VectorXd cov_diag_local;
752 QStringList picked_names;
753 int *is_meg =
nullptr;
755 std::unique_ptr<MNECovMatrix> res;
758 qCritical(
"No channels specified for picking in pick_chs_omit");
761 if (
names.isEmpty()) {
762 qCritical(
"No names in covariance matrix. Cannot do picking.");
765 std::vector<int> pickVec(new_ncov, -1);
766 pick = pickVec.data();
767 for (j = 0; j < new_ncov; j++)
768 for (k = 0; k <
ncov; k++)
769 if (QString::compare(
names[k],new_names[j]) == 0) {
773 for (j = 0; j < new_ncov; j++) {
775 qWarning(
"All desired channels not found in the covariance matrix (at least missing %s).", new_names[j].toUtf8().constData());
779 std::vector<int> isMegVec;
781 isMegVec.resize(new_ncov);
782 is_meg = isMegVec.data();
783 if (!chs.isEmpty()) {
784 for (j = 0; j < new_ncov; j++)
791 for (j = 0; j < new_ncov; j++)
792 if (new_names[j].startsWith(
"MEG"))
799 cov_diag_local.resize(new_ncov);
800 for (j = 0; j < new_ncov; j++) {
801 cov_diag_local[j] =
cov_diag[pick[j]];
802 picked_names.append(
names[pick[j]]);
806 cov_local.resize(new_ncov*(new_ncov+1)/2);
807 for (j = 0; j < new_ncov; j++) {
808 picked_names.append(
names[pick[j]]);
809 for (k = 0; k <= j; k++) {
812 if (to < 0 || to > new_ncov*(new_ncov+1)/2-1) {
813 qCritical(
"Wrong destination index in pick_chs_omit : %d %d %d",j,k,to);
816 if (from < 0 || from >
ncov*(
ncov+1)/2-1) {
817 qCritical(
"Wrong source index in pick_chs_omit : %d %d %d",pick[j],pick[k],from);
820 cov_local[to] =
cov[from];
822 if (is_meg[j] != is_meg[k])
832 res->proj.reset(
proj ?
proj->dup() :
nullptr);
836 res->ch_class.resize(res->ncov);
837 for (k = 0; k < res->ncov; k++)
838 res->ch_class[k] =
ch_class[pick[k]];
FiffTag class declaration, which provides fiff tag I/O and processing methods.
#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
FiffStream class declaration.
FiffChInfo class declaration.
#define FIFFV_SSS_JOB_NOTHING
FiffSparseMatrix class declaration.
MNECovMatrix class declaration.
#define MNE_COV_CH_MEG_MAG
#define MNE_COV_CH_MEG_GRAD
#define MNE_COV_CH_UNKNOWN
MNEProjOp class declaration.
MNEProjItem class declaration.
int mne_decompose_eigen(const VectorXd &mat, VectorXd &lambda, Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > &vectors, int dim)
MNE SSS Data (MNESssData) class declaration.
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
QSharedPointer< FiffDirNode > SPtr
FIFF sparse matrix storage.
static FiffSparseMatrix::UPtr fiff_get_float_sparse_matrix(const FIFFLIB::FiffTag::UPtr &tag)
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 MNEProjOp * read_from_node(FIFFLIB::FiffStream::SPtr &stream, const FIFFLIB::FiffDirNode::SPtr &start)
Read all linear projection items from a FIFF tree node.
Container for Signal Space Separation (SSS/Maxwell filtering) expansion coefficients and metadata.
static MNESssData * read_from_node(QSharedPointer< FIFFLIB::FiffStream > &stream, const QSharedPointer< FIFFLIB::FiffDirNode > &start)