+
+.. toctree::
+ :maxdepth: 1
+
+ Introduction
+ Release notes
+ Installing
+ Example gallery
+ API reference
+
+.. raw:: html
+
+
+
+
+
+
+
diff --git a/doc/installing.rst b/doc/installing.rst
new file mode 100644
index 00000000..3a915900
--- /dev/null
+++ b/doc/installing.rst
@@ -0,0 +1,46 @@
+.. _installing:
+
+Installing pyRiemann-qiskit
+===========================
+
+There is no yet stable version of pyRiemann-qiskit.
+
+Therefore, it is recommanded to clone the source code on `github `__ and install directly the package from source.
+
+``pip install -e .``
+
+The install script will install the required dependencies. If you want also to build the documentation and to run the test locally, you could install all development dependencies with
+
+``pip install -e .[docs,tests]``
+
+If you use a zsh shell, you need to write `pip install -e .\[docs,tests\]`. If you do not know what zsh is, you could use the above command.
+
+You may check that the package was correctly installed by starting a python shell and writing:
+
+``import pyriemann_qiskit``
+
+Dependencies
+~~~~~~~~~~~~
+
+- Python (>= 3.6)
+
+Mandatory dependencies
+^^^^^^^^^^^^^^^^^^^^^^
+
+- `cython `__
+
+- `pyriemann `__
+
+- `qiskit==0.20.0 `__
+
+- `cvxpy=1.1.12 `__
+
+Recommended dependencies
+^^^^^^^^^^^^^^^^^^^^^^^^
+These dependencies are recommanded to use the plotting functions of pyriemann or to run examples and tutorials, but they are not mandatory:
+
+- `matplotlib>=2.2 `__
+
+- `mne-python `__
+
+- `seaborn `__
diff --git a/doc/introduction.rst b/doc/introduction.rst
new file mode 100644
index 00000000..386bb1ad
--- /dev/null
+++ b/doc/introduction.rst
@@ -0,0 +1,48 @@
+.. _introduction:
+
+Introduction to pyRiemann-qiskit
+================================
+
+Litterature on quantum computing suggests it may offer an advantage as compared
+with classical computing in terms of computational time and outcomes, such as
+for pattern recognition or when using limited training sets [1, 2].
+
+A ubiquitous library on quantum computing is Qiskit [3].
+Qiskit is an IBM library distributed under Apache 2.0 which provides both
+quantum algorithms and backends. A backend can be either your local machine
+or a remote machine, which one can emulates or be a quantum machine.
+Qiskit abstraction over the type of machine you want to use, make designing
+quantum algorithm seamless.
+
+Qiskit implements a quantum version of support vector
+-like classifiers, known as quantum-enhanced support vector classifier (QSVC)
+and varitional quantum classifier (VQC) [4]. These classifiers likely offer
+an advantage over classical SVM in situations where the classification task
+is complex. Task complexity is raised by the encoding of the data into a
+quantum state, the number of available data and the quality of the data.
+Although there is no study on this topic at the time of writting,
+this could be an interesting research direction to investigate illiteracy
+in the domain of brain computer interfaces.
+
+pyRiemann-qiskit implements a wrapper around QSVC and VQC, to use quantum
+classification with riemanian geometry. A use case would be to use vectorized
+covariance matrices in the TangentSpace as an input for these classifiers,
+enabling a possible sandbox for researchers and engineers in the field.
+
+[1] A. Blance and M. Spannowsky,
+ ‘Quantum machine learning for particle physics using a variational quantum classifier’,
+ J. High Energ. Phys., vol. 2021, no. 2, p. 212, Feb. 2021,
+ doi: 10.1007/JHEP02(2021)212.
+
+[2] P. Rebentrost, M. Mohseni, and S. Lloyd,
+ ‘Quantum Support Vector Machine for Big Data Classification’,
+ Phys. Rev. Lett., vol. 113, no. 13, p. 130503, Sep. 2014,
+ doi: 10.1103/PhysRevLett.113.130503.
+
+[3] H. Abraham et al., Qiskit: An Open-source Framework for Quantum Computing.
+ Zenodo, 2019. doi: 10.5281/zenodo.2562110.
+
+[4] V. Havlíček et al.,
+ ‘Supervised learning with quantum-enhanced feature spaces’,
+ Nature, vol. 567, no. 7747, pp. 209–212, Mar. 2019,
+ doi: 10.1038/s41586-019-0980-2.
diff --git a/doc/requirements.txt b/doc/requirements.txt
new file mode 100644
index 00000000..7b11425e
--- /dev/null
+++ b/doc/requirements.txt
@@ -0,0 +1,11 @@
+sphinx-gallery
+sphinx-bootstrap_theme
+numpydoc
+cython
+mne
+seaborn
+scikit-learn
+joblib
+pandas
+qiskit
+pyriemann
diff --git a/doc/whatsnew.rst b/doc/whatsnew.rst
new file mode 100644
index 00000000..99d1f856
--- /dev/null
+++ b/doc/whatsnew.rst
@@ -0,0 +1,11 @@
+.. _whatsnew:
+
+.. currentmodule:: pyriemann-qiskit
+
+What's new in the package
+=========================
+
+A catalog of new features, improvements, and bug-fixes in each release.
+
+v0.0.1.dev
+----------
diff --git a/examples/README.txt b/examples/README.txt
new file mode 100644
index 00000000..0db9ef0e
--- /dev/null
+++ b/examples/README.txt
@@ -0,0 +1,6 @@
+Examples Gallery
+================
+
+.. contents:: Contents
+ :local:
+ :depth: 2
diff --git a/pyriemann_qiskit/__init__.py b/pyriemann_qiskit/__init__.py
new file mode 100644
index 00000000..84fedf2e
--- /dev/null
+++ b/pyriemann_qiskit/__init__.py
@@ -0,0 +1,5 @@
+from ._version import __version__
+
+__all__ = [
+ '__version__',
+]
diff --git a/pyriemann_qiskit/_version.py b/pyriemann_qiskit/_version.py
new file mode 100644
index 00000000..511a3465
--- /dev/null
+++ b/pyriemann_qiskit/_version.py
@@ -0,0 +1 @@
+__version__ = '0.0.1.dev'
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 00000000..c6b42198
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+cython
+pyriemann
+qiskit==0.20.0
+cvxpy==1.1.12
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 00000000..3f7d987b
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,13 @@
+[bdist_wheel]
+# This flag says that the code is written to work on both Python 2 and Python
+# 3. If at all possible, it is good practice to do this. If you cannot, you
+# will need to generate wheels for each Python version that you support.
+universal=1
+
+[build_sphinx]
+source-dir = doc/
+build-dir = doc/build
+all_files = 1
+
+[upload_sphinx]
+upload-dir = doc/build/html
diff --git a/setup.py b/setup.py
new file mode 100644
index 00000000..72d37791
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,40 @@
+import os.path as op
+
+from setuptools import setup, find_packages
+
+
+# get the version (don't import mne here, so dependencies are not needed)
+version = None
+with open(op.join('pyriemann_qiskit', '_version.py'), 'r') as fid:
+ for line in (line.strip() for line in fid):
+ if line.startswith('__version__'):
+ version = line.split('=')[1].strip().strip('\'')
+ break
+if version is None:
+ raise RuntimeError('Could not determine version')
+
+with open('README.md', 'r', encoding="utf8") as fid:
+ long_description = fid.read()
+
+setup(name='pyriemann-qiskit',
+ version=version,
+ description='Qiskit wrapper for pyRiemann',
+ url='https://pyriemann-qiskit.readthedocs.io',
+ author='Gregoire Cattan',
+ author_email='gcattan@hotmail.com',
+ license='BSD (3-clause)',
+ packages=find_packages(),
+ long_description=long_description,
+ long_description_content_type='text/markdown',
+ project_urls={
+ 'Documentation': 'https://pyriemann.readthedocs.io',
+ 'Source': 'https://github.com/pyRiemann/pyRiemann-qiskit',
+ 'Tracker': 'https://github.com/pyRiemann/pyRiemann-qiskit/issues/',
+ },
+ platforms='any',
+ python_requires=">=3.6",
+ install_requires=['cython', 'pyriemann', 'qiskit==0.20.0', 'cvxpy==1.1.12'],
+ extras_require={'docs': ['sphinx-gallery', 'sphinx-bootstrap_theme', 'numpydoc', 'mne', 'seaborn'],
+ 'tests': ['pytest', 'seaborn', 'flake8']},
+ zip_safe=False,
+)
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 00000000..7ecb2226
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,200 @@
+import pytest
+from pytest import approx
+import numpy as np
+from functools import partial
+
+
+def requires_module(function, name, call=None):
+ """Skip a test if package is not available (decorator)."""
+ call = ("import %s" % name) if call is None else call
+ reason = "Test %s skipped, requires %s." % (function.__name__, name)
+ try:
+ exec(call) in globals(), locals()
+ except Exception as exc:
+ if len(str(exc)) > 0 and str(exc) != "No module named %s" % name:
+ reason += " Got exception (%s)" % (exc,)
+ skip = True
+ else:
+ skip = False
+ return pytest.mark.skipif(skip, reason=reason)(function)
+
+
+requires_matplotlib = partial(requires_module, name="matplotlib")
+requires_seaborn = partial(requires_module, name="seaborn")
+
+
+def generate_cov(n_trials, n_channels, rs, return_params=False):
+ """Generate a set of covariances matrices for test purpose"""
+ diags = 2.0 + 0.1 * rs.randn(n_trials, n_channels)
+ A = 2 * rs.rand(n_channels, n_channels) - 1
+ A /= np.linalg.norm(A, axis=1)[:, np.newaxis]
+ covmats = np.empty((n_trials, n_channels, n_channels))
+ for i in range(n_trials):
+ covmats[i] = A @ np.diag(diags[i]) @ A.T
+ if return_params:
+ return covmats, diags, A
+ else:
+ return covmats
+
+
+@pytest.fixture
+def rndstate():
+ return np.random.RandomState(1234)
+
+
+@pytest.fixture
+def get_covmats(rndstate):
+ def _gen_cov(n_trials, n_chan):
+ return generate_cov(n_trials, n_chan, rndstate, return_params=False)
+
+ return _gen_cov
+
+
+@pytest.fixture
+def get_covmats_params(rndstate):
+ def _gen_cov_params(n_trials, n_chan):
+ return generate_cov(n_trials, n_chan, rndstate, return_params=True)
+
+ return _gen_cov_params
+
+
+@pytest.fixture
+def get_labels():
+ def _get_labels(n_trials, n_classes):
+ return np.arange(n_classes).repeat(n_trials // n_classes)
+
+ return _get_labels
+
+
+def is_positive_semi_definite(X):
+ """Check if all matrices are positive semi-definite.
+
+ Parameters
+ ----------
+ X : ndarray, shape (..., n, n)
+ The set of square matrices, at least 2D ndarray.
+
+ Returns
+ -------
+ ret : boolean
+ True if all matrices are positive semi-definite.
+ """
+ cs = X.shape[-1]
+ return np.all(np.linalg.eigvals(X.reshape((-1, cs, cs))) >= 0.0)
+
+
+def is_positive_definite(X):
+ """Check if all matrices are positive definite.
+
+ Parameters
+ ----------
+ X : ndarray, shape (..., n, n)
+ The set of square matrices, at least 2D ndarray.
+
+ Returns
+ -------
+ ret : boolean
+ True if all matrices are positive definite.
+ """
+ cs = X.shape[-1]
+ return np.all(np.linalg.eigvals(X.reshape((-1, cs, cs))) > 0.0)
+
+
+def is_symmetric(X):
+ """Check if all matrices are symmetric.
+
+ Parameters
+ ----------
+ X : ndarray, shape (..., n, n)
+ The set of square matrices, at least 2D ndarray.
+
+ Returns
+ -------
+ ret : boolean
+ True if all matrices are symmetric.
+ """
+ return X == approx(np.swapaxes(X, -2, -1))
+
+
+@pytest.fixture
+def is_spd():
+ """Check if all matrices are symmetric positive-definite.
+
+ Parameters
+ ----------
+ X : ndarray, shape (..., n, n)
+ The set of square matrices, at least 2D ndarray.
+
+ Returns
+ -------
+ ret : boolean
+ True if all matrices are symmetric positive-definite.
+ """
+
+ def _is_spd(X):
+ return is_symmetric(X) and is_positive_definite(X)
+
+ return _is_spd
+
+
+@pytest.fixture
+def is_spsd():
+ """Check if all matrices are symmetric positive semi-definite.
+
+ Parameters
+ ----------
+ X : ndarray, shape (..., n, n)
+ The set of square matrices, at least 2D ndarray.
+
+ Returns
+ -------
+ ret : boolean
+ True if all matrices are symmetric positive semi-definite.
+ """
+
+ def _is_spsd(X):
+ return is_symmetric(X) and is_positive_semi_definite(X)
+
+ return _is_spsd
+
+
+def get_distances():
+ distances = [
+ "riemann",
+ "logeuclid",
+ "euclid",
+ "logdet",
+ "kullback",
+ "kullback_right",
+ "kullback_sym",
+ ]
+ for dist in distances:
+ yield dist
+
+
+def get_means():
+ means = [
+ "riemann",
+ "logeuclid",
+ "euclid",
+ "logdet",
+ "identity",
+ "wasserstein",
+ "ale",
+ "harmonic",
+ "kullback_sym",
+ ]
+ for mean in means:
+ yield mean
+
+
+def get_metrics():
+ metrics = [
+ "riemann",
+ "logeuclid",
+ "euclid",
+ "logdet",
+ "kullback_sym",
+ ]
+ for met in metrics:
+ yield met