metabci.brainda.algorithms.manifold.riemann module

Riemannian Geometry for BCI.

class metabci.brainda.algorithms.manifold.riemann.Alignment(align_method: str = 'euclid', cov_method: str = 'lwf', n_jobs: int | None = None)[source]

Bases: BaseEstimator, TransformerMixin

Characteristics and uses of classes Alignment

Authors: Swolf <swolfforever@gmail.com>

Date: 2021-1-23

update log:

2023-12-18 by Yuwei Liu<liuyuwei20010905@163.com> add code annotation

Riemannian Alignment (RA) uses the Riemannian mean of the covariance matrix of all trials as the reference matrix, so that the center point of the whitened covariance matrix is located in the identity matrix. By performing RA processing on each subject’s data, the center point of the covariance matrix for all individuals can be aligned. Euclidean Alignment (EA) replaces the Riemann mean covariance matrix with the Euclidean mean covariance matrix.

Parameters:
  • n_jobs (int) – the default is None

  • align_method (str) – choose the alignment method:’riemann’ or ‘euclid’

  • cov_method (str) – covariance estimators:’lwf’

n_jobs

the default is None

Type:

int

align_method

choose the alignment method:’riemann’ or ‘euclid’

Type:

str

cov_method

covariance estimators:’lwf’

Type:

str

iC12_

aligned Riemann/Euclidean center

Type:

ndarray,shape(int,int)

References

Tip

An example using Alignment
1 from metabci.brainda.algorithms.manifold import Alignment
2 estimator = Alignment(align_method='riemann')
3 filterX = estimator.fit(X).transform(X)
fit(X: ndarray, y: ndarray | None = None)[source]

Train the model,calculate the aligned center

Parameters:

X (ndarray,shape(n_trails,n_channels,n_samples)) – train data: EEG signals

Returns:

Return type:

self

transform(X)[source]

Obtain the aligned individual data

Parameters:

X (ndarray,shape(n_trails,n_channels,n_samples)) – EEG data

Returns:

X – aligned EEG data

Return type:

ndarray,shape(n_trails,n_channels,n_samples)

class metabci.brainda.algorithms.manifold.riemann.FGDA(n_jobs=1)[source]

Bases: BaseEstimator, TransformerMixin

Characteristics and uses of classes FGDA

Authors: Swolf <swolfforever@gmail.com>

Created on: 2021-1-23

update log:

2023-12-18 by Yuwei Liu<liuyuwei20010905@163.com> add code annotation

Fisher Geodesic Discriminate Analysis(FGDA) is the application of Fisher Linear Discriminate Analysis in the Riemannian tangent space.FGDA first calculates the projection vectors of the sample covariance matrix of EEG signals in the Riemannian tangent space.Then, leveraging the properties of Riemannian tangent space as a Euclidean space, it performs discriminant feature extraction on the projected vectors in the tangent space based on the Fisher Linear Discriminant Analysis criterion.

Parameters:

n_jobs (int) – the default of n_jobs is None,meaning it will utilize all available CPUs.

lda_

LDA

Type:

discriminate_analysis.Linear Discriminate Analysis

P_

the average covariance matrix calculates from the Riemann matrix

Type:

ndarray:shape(int,int)

W_

the weight of LDA

Type:

ndarray,shape(int,int)

References

fit(X, y)[source]

Train the model.

Parameters:
  • X (ndarray:shape(n_trails,n_channels,n_samples)) – train data: EEG signals

  • y (ndarray:shape(n_trails)) – the labels of train data

transform(X)[source]

Calculate the FGDA from the parameters stored in self

Parameters:

X (ndarray:shape(n_trails,n_channels,n_samples)) – train data: EEG signals

Returns:

Pi – the projection matrix

Return type:

ndarray

class metabci.brainda.algorithms.manifold.riemann.FgMDRM(n_jobs: int | None = None)[source]

Bases: BaseEstimator, TransformerMixin, ClassifierMixin

Characteristics and uses of classes FgMDRM

Authors: Swolf <swolfforever@gmail.com>

Date: 2021-1-23

update log:

2023-12-18 by Yuwei Liu<liuyuwei20010905@163.com> add code annotation

The Fisher Geodesic Minimum Distance to Riemannian Mean(FGMDRM) algorithm is a fusion of MDRM and FGDA.The algorithm first employs FGDA in the tangent space to filter the data,extracting key discriminative features,removing irrelevant noise components. Subsequently, the extracted discriminative features are remapped back to the manifold space. The covariance matrix of the filtered sample space is then calculated based on MDRM to determine the Riemannian centroids for each class. The classification of test data is performed based on the minimum distance principle.

Parameters:

n_jobs (int) – the default of n_jobs is None,meaning it will utilize all available CPUs.

n_jobs

the default of n_jobs is None,meaning it will utilize all available CPUs.

Type:

int

classes_

the class of samples

Type:

ndarray,shape(int)

centroids_

Riemannian centroid of two classes

Type:

ndarray,shape(int,float,float)

fgda_

Fisher Geodesic Discriminate Analysis(FGDA)

Type:

algorithms.mainfold.riemann.FGDA

References

Tip

An example using FgMDRM
1from metabci.brainda.algorithms.mainfold import FgMDRM
2estimator = FgMDRM()
3p_labels = estimator.fit(X[train_ind],y[train_ind]).predict(X[test_ind])
fit(X: ndarray, y: ndarray, sample_weight: ndarray | None = None)[source]

Train the model.

Parameters:
  • X (ndarray,shape(n_trails,n_channels,n_samples)) – train data: EEG signals

  • y (ndarray,shape(n_trails)) – the labels if train data

  • sample_weight (ndarray) – the weight of the model

predict(X: ndarray)[source]

Predict the labels

Parameters:

X (ndarray,shape(n_trails,n_channels,n_samples)) – train data: EEG signals

Returns:

self.classes_[np.argmin(dist, axis=1)] – predicted labels

Return type:

ndarray,shape(n_trails)

set_fit_request(*, sample_weight: bool | None | str = '$UNCHANGED$') FgMDRM

Request metadata passed to the fit method.

Note that this method is only relevant if enable_metadata_routing=True (see sklearn.set_config()). Please see User Guide on how the routing mechanism works.

The options for each parameter are:

  • True: metadata is requested, and passed to fit if provided. The request is ignored if metadata is not provided.

  • False: metadata is not requested and the meta-estimator will not pass it to fit.

  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.

New in version 1.3.

Note

This method is only relevant if this estimator is used as a sub-estimator of a meta-estimator, e.g. used inside a Pipeline. Otherwise it has no effect.

Parameters:

sample_weight (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for sample_weight parameter in fit.

Returns:

self – The updated object.

Return type:

object

set_score_request(*, sample_weight: bool | None | str = '$UNCHANGED$') FgMDRM

Request metadata passed to the score method.

Note that this method is only relevant if enable_metadata_routing=True (see sklearn.set_config()). Please see User Guide on how the routing mechanism works.

The options for each parameter are:

  • True: metadata is requested, and passed to score if provided. The request is ignored if metadata is not provided.

  • False: metadata is not requested and the meta-estimator will not pass it to score.

  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.

New in version 1.3.

Note

This method is only relevant if this estimator is used as a sub-estimator of a meta-estimator, e.g. used inside a Pipeline. Otherwise it has no effect.

Parameters:

sample_weight (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for sample_weight parameter in score.

Returns:

self – The updated object.

Return type:

object

transform(X: ndarray)[source]

Calculate the Riemann distance of each class from the parameters stored in self

Parameters:

X (ndarray,shape(n_trails,n_channels,n_samples)) – train data: EEG signals

Returns:

the Riemann distance of each class

Return type:

Self._transform_distance(X)

class metabci.brainda.algorithms.manifold.riemann.MDRM(n_jobs: int = 1)[source]

Bases: BaseEstimator, TransformerMixin, ClassifierMixin

Characteristics and uses of classes MDRM

Authors: Swolf <swolfforever@gmail.com>

Date: 2021-1-23

update log:

2023-12-18 by Yuwei Liu<liuyuwei20010905@163.com> add code annotation

Minimum Distance to Riemannian Mean(MDRM) is a decoding algorithm based on Riemann distance metric. MDRM calculates the covariance matrix of EEG signals, estimates the Riemannian centroids for each class, then determines the class of a test sample by computing the minimum distance between the test data’s covariance matrix and the mean point.

Parameters:

n_jobs (int) – n_jobs the default is None,meaning it will utilize all available CPUs.

classes_

class labels

Type:

ndarray,shape(int)

centroids_

Riemannian centroid of two classes

Type:

ndarray,shape(int,float,float)

References

Tip

An example using MDRM
1from metabci.brainda.algorithms.mainfold import MDRM
2estimator = MDRM()
3p_labels = estimator.fit(X[train_ind],y[train_ind]).predict(X[test_ind])
fit(X: ndarray, y: ndarray, sample_weight: ndarray | None = None)[source]

Train the model

Parameters:
  • X (ndarray,shape(n_trails,n_channels,n_samples)) – train data: EEG signals

  • y (ndarray,shape(n_trails)) – label of train data

  • sample_weight (ndarray) – the weight of the samples which is optional,the default is None

Returns:

self

Return type:

the model

predict(X: ndarray)[source]

Predict the label

Parameters:

X (ndarray, shape(n_trials, n_channels, n_samples)) – train data: EEG signals

Returns:

self.classes_[np.argmin(dist, axis=1)] – predicted labels

Return type:

ndarray,shape(n_trails)

predict_proba(X: ndarray)[source]

Predict label probabilities

Parameters:

X (ndarray, shape(n_trials, n_channels, n_samples)) – train data: EEG signals

Returns:

softmax(-1 * self._transform_distance(X)) – the probabilities of the predicted labels

Return type:

ndarray,shape(n_trails)

set_fit_request(*, sample_weight: bool | None | str = '$UNCHANGED$') MDRM

Request metadata passed to the fit method.

Note that this method is only relevant if enable_metadata_routing=True (see sklearn.set_config()). Please see User Guide on how the routing mechanism works.

The options for each parameter are:

  • True: metadata is requested, and passed to fit if provided. The request is ignored if metadata is not provided.

  • False: metadata is not requested and the meta-estimator will not pass it to fit.

  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.

New in version 1.3.

Note

This method is only relevant if this estimator is used as a sub-estimator of a meta-estimator, e.g. used inside a Pipeline. Otherwise it has no effect.

Parameters:

sample_weight (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for sample_weight parameter in fit.

Returns:

self – The updated object.

Return type:

object

set_score_request(*, sample_weight: bool | None | str = '$UNCHANGED$') MDRM

Request metadata passed to the score method.

Note that this method is only relevant if enable_metadata_routing=True (see sklearn.set_config()). Please see User Guide on how the routing mechanism works.

The options for each parameter are:

  • True: metadata is requested, and passed to score if provided. The request is ignored if metadata is not provided.

  • False: metadata is not requested and the meta-estimator will not pass it to score.

  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.

New in version 1.3.

Note

This method is only relevant if this estimator is used as a sub-estimator of a meta-estimator, e.g. used inside a Pipeline. Otherwise it has no effect.

Parameters:

sample_weight (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for sample_weight parameter in score.

Returns:

self – The updated object.

Return type:

object

transform(X: ndarray)[source]

Calculate the Riemann distance of each class using the parameters from self.

Parameters:

X (ndarray,shape(n_trails,n_channels,n_samples)) – train data: EEG signals

Returns:

the Riemann distance of each class

Return type:

self._transform_distance(X)

class metabci.brainda.algorithms.manifold.riemann.RecursiveAlignment(align_method: str = 'euclid', cov_method: str = 'lwf', n_jobs: int | None = None)[source]

Bases: BaseEstimator, TransformerMixin

Characteristics and uses of classes RecursiveAlignment

Authors: Swolf <swolfforever@gmail.com>

Date: 2021-1-23

update log:

2023-12-18 by Yuwei Liu<liuyuwei20010905@163.com> add code annotation

In order to overcome the problem that the trial data gradually appear in chronological order under the online experiment, there is no initial sample size estimation center, and the calculation process of the Riemann center is complex, and it takes a lot of time to recalculate the Riemannian center in the feedback stage, the Recursive Riemannian Alignment (rRA) and Recursive Euclidean Alignment (rEA) suitable for the online stage were proposed.

Parameters:
  • n_jobs (int) – the default is None

  • align_method (str) – choose the alignment method:’riemann’ or ‘euclid’

  • cov_method (str) – covariance estimators:’lwf’

n_jobs

the default is None

Type:

int

align_method

choose the alignment method:’riemann’ or ‘euclid’

Type:

str

cov_method

covariance estimators:’lwf’

Type:

str

iC12_

aligned Riemann/Euclidean center

Type:

ndarray,shape(int,int)

n_tracked

the number of iterations

Type:

int

C_

the Euclid or Riemann center after iteration

Type:

ndarray

References

Tip

An example using RecursiveAlignment
1from metabci.brainda.algorithms.manifold import RecursiveAlignment
2estimator = RecursiveAlignment(align_method='riemann')
3filterX = estimator.fit(X).transform(X)
fit(X, y=None)[source]

recursive alignment

Parameters:

X (ndarray,shape(n_trails,n_channels.n_samples)) – EEG data

Returns:

Return type:

self

transform(X)[source]

obtain the subject’s data after recursive alignment

Parameters:

X (ndarray,shape(n_trails,n_channels.n_samples)) – EEG data

Returns:

X – the individual data after recursive alignment

Return type:

ndarray,shape(n_trails,n_channels.n_samples)

class metabci.brainda.algorithms.manifold.riemann.TSClassifier(clf=LogisticRegression(), n_jobs=None)[source]

Bases: BaseEstimator, ClassifierMixin

Characteristics and uses of classes TSClassifier

Authors: Swolf <swolfforever@gmail.com>

Date: 2021-1-23

update log:

2023-12-18 by Yuwei Liu<liuyuwei20010905@163.com> add code annotation

The Tangent Space Classifier (TSClassifier) is a general term for classifiers constructed in the Riemannian tangent space,which is treated as a Euclidean space. Methods such as LDA (Linear Discriminant Analysis), SVM (Support Vector Machine),Logistic Regression, and others are employed to build classifiers in this Riemannian tangent space.

Parameters:
  • n_jobs (int) – the default of n_jobs is None,meaning it will utilize all available CPUs.

  • clf (linear_model._logistic.LogisticRegression) – Logistic Regression

n_jobs

the default of n_jobs is None,meaning it will utilize all available CPUs.

Type:

int

clf

Logistic Regression

Type:

linear_model._logistic.LogisticRegression

P_

The average covariance matrix returned according to the Riemann matrix

Type:

ndarray,shape(int,int)

References

Tip

An example using TSClassifier
1from metabci.brainda.algorithms.manifold import TSClassifier
2estimator = TSClassifier()
3p_labels = estimator.fit(X[train_ind], y[train_ind]).predict(X[test_ind])
fit(X: ndarray, y: ndarray)[source]

Train the model

Parameters:
  • X (ndarray,shape(n_trails,n_channels,n_samples)) – train data: EEG signals

  • y (ndarray,shape(n_trails)) – the labels if train data

predict(X: ndarray)[source]

Predict labels

Parameters:

X (ndarray,shape(n_trails,n_channels,n_samples)) – train data: EEG signals

Returns:

self.clf.predict(vSi)

Return type:

ndarray,shape(n_trails) predicted labels

predict_proba(X: ndarray)[source]

Predict label probabilities

Parameters:

X (ndarray,shape(n_trails,n_channels,n_samples)) – train data: EEG signals

Returns:

self.clf.predict_proba(vSi) – predicted label probabilities

Return type:

ndarray,shape(n_trails)

set_score_request(*, sample_weight: bool | None | str = '$UNCHANGED$') TSClassifier

Request metadata passed to the score method.

Note that this method is only relevant if enable_metadata_routing=True (see sklearn.set_config()). Please see User Guide on how the routing mechanism works.

The options for each parameter are:

  • True: metadata is requested, and passed to score if provided. The request is ignored if metadata is not provided.

  • False: metadata is not requested and the meta-estimator will not pass it to score.

  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.

New in version 1.3.

Note

This method is only relevant if this estimator is used as a sub-estimator of a meta-estimator, e.g. used inside a Pipeline. Otherwise it has no effect.

Parameters:

sample_weight (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for sample_weight parameter in score.

Returns:

self – The updated object.

Return type:

object

metabci.brainda.algorithms.manifold.riemann.distance_riemann(A: ndarray, B: ndarray, n_jobs: int | None = None)[source]

Riemannian distance between two covariance matrices A and B.

\[d = {\left( \sum_i \log(\lambda_i)^2 \right)}^{-1/2}\]

where \(\lambda_i\) are the joint eigenvalues of A and B.

Parameters:
  • A (ndarray) – First positive-definite matrix, shape (n_trials, n_channels, n_channels) or (n_channels, n_channels).

  • B (ndarray) – Second positive-definite matrix.

Returns:

Riemannian distance between A and B.

Return type:

ndarray | float

metabci.brainda.algorithms.manifold.riemann.expmap(Si: ndarray, P: ndarray, n_jobs: int | None = None)[source]

Exponential map from the tangent space to the positive-definite space.

Exponential map projects \(\mathbf{S}_i \in \mathcal{T}_{\mathbf{P}} \mathcal{M}\) bach to the manifold \(\mathcal{M}\).

Parameters:
  • Si (ndarray) – Tangent space point (in matrix form).

  • P (ndarray) – Reference point.

  • n_jobs (int, optional) – the number of jobs to use.

Returns:

Pi – SPD matrix.

Return type:

ndarray

metabci.brainda.algorithms.manifold.riemann.geodesic(P1: ndarray, P2: ndarray, t: float, n_jobs: int | None = None)[source]

Geodesic.

The geodesic curve between any two SPD matrices \(\mathbf{P}_1,\mathbf{P}_2 \in \mathcal{M}\).

Parameters:
  • P1 (ndarray) – SPD matrix.

  • P2 (ndarray) – SPD matrix, the same shape of P1.

  • t (float) – \(0 \leq t \leq 1\).

  • n_jobs (int, optional) – the number of jobs to use.

Returns:

phi – SPD matrix on the geodesic curve between P1 and P2.

Return type:

ndarray

metabci.brainda.algorithms.manifold.riemann.logmap(Pi: ndarray, P: ndarray, n_jobs: int | None = None)[source]

Logarithm map from the positive-definite space to the tangent space.

Logarithm map projects \(\mathbf{P}_i \in \mathcal{M}\) to the tangent space point \(\mathbf{S}_i \in \mathcal{T}_{\mathbf{P}} \mathcal{M}\) at \(\mathbf{P} \in \mathcal{M}\).

Parameters:
  • Pi (ndarray) – SPD matrix.

  • P (ndarray) – Reference point.

  • n_jobs (int, optional) – the number of jobs to use.

Returns:

Si – Tangent space point (in matrix form).

Return type:

ndarray

metabci.brainda.algorithms.manifold.riemann.mdrm_kernel(X: ndarray, y: ndarray, sample_weight: ndarray | None = None, n_jobs: int = 1)[source]

Minimum Distance to Riemannian Mean.

Parameters:
  • X (ndarray) – eeg data, shape (n_trials, n_channels, n_samples)

  • y (ndarray) – labels, shape (n_trials)

  • sample_weight (Optional[ndarray], optional) – sample weights, by default None

  • n_jobs (int) – the number of jobs to use, by default 1

Returns:

centroids of each class, shape (n_class, n_channels, n_channels).

Return type:

ndarray

metabci.brainda.algorithms.manifold.riemann.mean_riemann(covmats, tol=1e-11, maxiter=300, init=None, sample_weight=None, n_jobs=None)[source]

Return the mean covariance matrix according to the Riemannian metric.

The procedure is similar to a gradient descent minimizing the sum of riemannian distance to the mean.

\[\mathbf{C} = \arg \min{(\sum_i \delta_R ( \mathbf{C} , \mathbf{C}_i)^2)}\]

where \(\delta_R\) is riemann distance.

Parameters:
  • covmats (ndarray) – Covariance matrices set, shape (n_trials, n_channels, n_channels).

  • tol (float, optional) – The tolerance to stop the gradient descent (default 1e-8).

  • maxiter (int, optional) – The maximum number of iteration (default 50).

  • init (None|ndarray, optional) – A covariance matrix used to initialize the gradient descent (default None), if None the arithmetic mean is used.

  • sample_weight (None|ndarray, optional) – The weight of each sample (efault None), if None weights are 1 otherwise weights are normalized.

Returns:

C – The Riemannian mean covariance matrix.

Return type:

ndarray

metabci.brainda.algorithms.manifold.riemann.tangent_space(Pi: ndarray, P: ndarray, n_jobs: int | None = None)[source]

Logarithm map projects SPD matrices to the tangent vectors.

Parameters:
  • Pi (ndarray) – SPD matrices, shape (n_trials, n_channels, n_channels).

  • P (ndarray) – Reference point.

Returns:

vSi – Tangent vectors, shape (n_trials, n_channels*(n_channels+1)/2).

Return type:

ndarray

metabci.brainda.algorithms.manifold.riemann.untangent_space(vSi: ndarray, P: ndarray, n_jobs: int | None = None)[source]

Logarithm map projects SPD matrices to the tangent vectors.

Parameters:
  • vSi (ndarray) – Tangent vectors, shape (n_trials, n_channels*(n_channels+1)/2).

  • P (ndarray) – Reference point.

Returns:

Pi – SPD matrices, shape (n_trials, n_channels, n_channels).

Return type:

ndarray

metabci.brainda.algorithms.manifold.riemann.unvectorize(vSi: ndarray)[source]

unvectorize tangent space points.

Parameters:

vSi (ndarray) – vectorized version of Si, shape (n_trials, n_channels*(n_channels+1)/2)

Returns:

points in the tangent space, shape (n_trials, n_channels, n_channels)

Return type:

ndarray

metabci.brainda.algorithms.manifold.riemann.vectorize(Si: ndarray)[source]

vectorize tangent space points.

Parameters:

Si (ndarray) – points in the tangent space, shape (n_trials, n_channels, n_channels)

Returns:

vectorized version of Si, shape (n_trials, n_channels*(n_channels+1)/2)

Return type:

ndarray