Open-source project
pyRiemann/pyRiemann avatar
pyRiemann/pyRiemann

pyRiemann: Riemannian geometry for SPD matrices behind a scikit-learn API

Machine learning for multivariate data through the Riemannian geometry of positive definite matrices in Python

777 stars189 forksPythonBSD-3-Clause

At a glance

What is it?
pyRiemann estimates covariance matrices from multichannel time series and classifies them on the manifold of symmetric positive definite matrices. It is a specialist library for EEG, MEG, EMG and radar imagery, and its value depends on whether your data actually produces well-conditioned SPD matrices.
Who is it for?
Adopt pyRiemann if your pipeline already produces covariance matrices from multichannel time series and you want minimum distance to mean, tangent space projection or Riemannian alignment without writing the manifold math yourself. Do not adopt it if your features are ordinary tabular vectors, or if you cannot get enough trials per class to estimate a stable covariance.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem pyRiemann solves is the geometry of covariance matrices

A multichannel recording gives you a matrix of channels by time samples. The usual next step is to compute a covariance matrix, which is symmetric positive definite if the signal is not degenerate. The trouble is that SPD matrices do not live in a vector space. Averaging two of them elementwise can produce something that is not a valid covariance, and Euclidean distance between two covariance matrices is not the distance that matters for classification. pyRiemann handles that by treating the set of SPD matrices as a Riemannian manifold and providing the operations that go with it. The README describes the package as a scikit-learn based machine learning package that gives a high-level interface for processing and classification of real and complex-valued multivariate data through the Riemannian geometry of symmetric and Hermitian positive definite matrices. The intended audience is narrow and identifiable: researchers working on brain-computer interfaces with motor imagery, event-related potentials or steady-state visually evoked potentials, and remote sensing practitioners working with hyperspectral or synthetic-aperture radar imagery. If you are doing ordinary tabular machine learning, this package has nothing for you.

Covariances, MDM and tangent space: the three moving parts

The pipeline has a consistent shape. Raw epochs in the format n_epochs by n_channels by n_times go into an estimator that produces one SPD matrix per epoch. pyriemann.estimation.Covariances is the entry point shown in the README. From there the two documented routes diverge. The first is pyriemann.classification.MDM, minimum distance to mean, which classifies a covariance by how close it sits to each class mean under the Riemannian metric. The second is pyriemann.tangentspace.TangentSpace, which maps each covariance to a tangent vector at a reference point and hands the result to an ordinary Euclidean classifier such as a linear SVM. That second route is what makes the library composable: after the tangent space projection you are back in flat vector space and the whole scikit-learn ecosystem is available again. The README also mentions extended labels that allow multisource transfer learning between sessions or subjects, which matters because covariance distributions drift between recording sessions and reusing a classifier across them is normally where BCI pipelines break. For remote sensing the README describes the same machinery applied differently: covariance matrices are estimated over spatial coordinates of radar images using a sliding window, then processed with SPD geometry for hyperspectral images and HPD geometry for SAR images. The backend story is worth noting. The README states that core utility functions support NumPy and PyTorch transparently through the Python Array API, so passing PyTorch tensors enables optional GPU acceleration and autograd. That is a statement about the utility functions, not a blanket claim about every estimator in the package.

Installing pyRiemann and running the two README pipelines

Installation is unremarkable, which is a compliment. From PyPI it is pip install pyriemann. From conda-forge it is conda install -c conda-forge pyriemann. For the latest code the README gives pip install git+https://github.com/pyRiemann/pyRiemann, and from a source checkout either pip install . or pip install -e . for editable mode. The Python version badge lists 3.11, 3.12 and 3.13, so older interpreters are outside the supported set. The first documented pipeline builds an MDM classifier directly on covariances: import pyriemann, call pyriemann.estimation.Covariances().fit_transform(X) on data shaped n_epochs by n_channels by n_times, construct pyriemann.classification.MDM(), and score it with sklearn.model_selection.cross_val_score. The second uses sklearn.pipeline.make_pipeline to chain Covariances, TangentSpace and SVC(kernel="linear"), then cross-validates the whole chain on the raw epochs. The second form is the one to copy, because the scaler and the tangent space reference point are then fitted inside each cross-validation fold rather than on the full dataset. That distinction is not spelled out in the README, but it follows from the fact that TangentSpace is a fitted transformer. Contributors run pytest before opening a pull request, per the testing section.

Where the design constrains you

The main constraint is sample support. A covariance matrix over n_channels has n_channels times (n_channels + 1) / 2 free parameters, so short epochs with many channels produce noisy or singular estimates, and the Riemannian machinery assumes positive definiteness rather than tolerating its absence. The README does not document shrinkage estimators or regularization options, so if your epochs are short and your montage is dense, you need to solve that upstream of pyRiemann. A second constraint is that the library is built around biosignals and radar. The README says it aims at being a generic package for multivariate data analysis but was designed around biosignal manipulation, and that ordering is honest: the examples, the paradigms named, and the transfer learning support all assume this domain. Applying it to, say, financial correlation matrices is possible in principle but you are on your own for validation. Third, the PyTorch and Array API support is described for core utility functions, so do not assume every estimator accepts a tensor and returns gradients. If you need end-to-end differentiable Riemannian layers you should verify that specific estimator against the documentation before designing around it.

pyRiemann against MNE-Python and plain scikit-learn

The natural comparison is MNE-Python, which many of the same users already have installed. MNE handles reading, filtering, epoching and visualization of electrophysiology data, and it does include some decoding utilities, but its center of gravity is data handling rather than manifold-valued classification. The difference in approach is that MNE gives you clean epochs and pyRiemann gives you the geometry to classify their covariances. They are complements, and the README's contribution guidelines explicitly point contributors at MNE-Python conventions, which suggests the two projects expect to be used together. The other comparison is plain scikit-learn. You could compute covariances yourself with numpy.cov and feed the flattened upper triangle to a logistic regression. That works, and for some datasets it works well enough. What you lose is the metric: flattening a covariance matrix and treating its entries as independent features discards the constraint that the matrix must stay positive definite, and it makes the classifier sensitive to how the entries are scaled. TangentSpace exists precisely to produce a Euclidean representation that respects the manifold structure, and MDM exists to classify without leaving it. If your accuracy is already adequate with flattened covariances, pyRiemann adds a dependency and a conceptual layer for no gain.

Release cadence, licence and what maintenance costs you

The release history shows v0.10 in January 2026, v0.11 in April 2026 and v0.12 in July 2026, with the last push to master in September 2026. That is a roughly quarterly cadence on a pre-1.0 version number, which means minor releases can carry API changes and you should pin the version in your requirements file rather than tracking master. The package is BSD-3-Clause, which is permissive and compatible with commercial use and with redistribution in closed products, subject to the usual attribution requirements. That is a description of the licence identifier, not legal advice; read the LICENSE file in the repository if the terms matter to your organization. The contributor list in the citation block is long, which is a reasonable signal that the project is not a single-maintainer effort, though the README does not state a governance model or a support commitment. Documentation lives at pyriemann.readthedocs.io and the README points to an example folder for further pipelines. Upgrade cost is mostly the cost of re-validating your cross-validation numbers, since the estimators are fitted and small changes to a tangent space reference point or a covariance estimator can shift accuracy without any error being raised.

Who this is for, and the check to run first

pyRiemann fits teams and labs that already have a working epoching pipeline and want to add a Riemannian classifier without implementing the manifold operations. The two README pipelines are short enough to evaluate in an afternoon. The check to run first is dimensional: take one subject or one scene from your own data, compute covariances, and confirm the matrices are well conditioned before you build anything on top. If they are not, fix the epoching or add regularization upstream, because no amount of manifold geometry rescues a singular covariance. After that, compare the tangent space plus linear SVM pipeline against a flattened-covariance baseline on the same folds. If the Riemannian route does not beat the baseline on your data, the extra dependency is not earning its place.

Editorial conclusion

Adopt pyRiemann if your pipeline already produces covariance matrices from multichannel time series and you want minimum distance to mean, tangent space projection or Riemannian alignment without writing the manifold math yourself. Do not adopt it if your features are ordinary tabular vectors, or if you cannot get enough trials per class to estimate a stable covariance. Before committing, verify three things: that your channel count times your sample count gives an invertible covariance, that the Python version in your environment is 3.11, 3.12 or 3.13 as the README badge states, and that you can run the two README pipelines end to end on your own data.

Official sources

  1. License: BSD-3-Clause
  2. Project website
  3. pyRiemann/pyRiemann on GitHub
  4. README
  5. Releases
Community notes

Community notes