Skip to content

Commit 901feff

Browse files
toncho11gcattanqbarthelemy
authored
Added an example that uses datasets from MOABB and the RG+QuantumSVM pipeline (#39)
* Create a new workflow. * This script uses the new version of MOABB that now includes several Brain Invaders datasets. It classifies using a Quantum Classifier and Riemannian geometry. * Small improvement. * Fixed a bug which forced classical SVM instead of Quantum. * force classical SVM * Improved code that compares 3 pipelines. * Now it prints the result in the console. * Comments are improved. * comment * Added moabb 0.4.6 as a requirement. * Delete python-package.yml * Some updates to comply with flake 8. * update * update * description update * Added version of pure SVM on the raw data. * Improvements. The number of pipelines has been reduced to two. This way it will be more simple to compare results. * add example to index.rst add `print(__doc__)` to example * flake8 * ignore python version (3.6 not supported in moabb) * drop support for 3.6 and add support for 3.9 * drop support for 3.9 * add comments to Vectorizer * try installing moabb from the source Co-authored-by: Quentin Barthélemy <[email protected]> * re-test python 3.6 * Revert "re-test python 3.6" This reverts commit f6b20b3. * Revert "try installing moabb from the source" This reverts commit f5ecbac. * fix comment * add moabb to `docs` dependency. * re-enable python 3.6 * Trigger pipeline * comments updated. * fixing flake8 ... * remove support for 3.6 * Improved description and applied one suggestion. * Improved comments. * flake update * Fixed typo. * add_javascript deprecated in Sphinx * Update classify_P300_bi.py Change the way `q_account_token` changed (updated in last PR) Co-authored-by: gcattan <[email protected]> Co-authored-by: gcattan <[email protected]> Co-authored-by: Quentin Barthélemy <[email protected]>
1 parent 4e73938 commit 901feff

File tree

7 files changed

+210
-4
lines changed

7 files changed

+210
-4
lines changed

.github/workflows/testing.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
strategy:
1717
fail-fast: false
1818
matrix:
19-
python-version: [3.6, 3.7, 3.8]
19+
python-version: [3.7, 3.8]
2020
os: [ubuntu-latest, macOS-latest, windows-latest]
2121

2222
steps:
@@ -37,3 +37,4 @@ jobs:
3737
- name: Test with pytest
3838
run: |
3939
pytest
40+

doc/conf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -331,5 +331,5 @@
331331

332332

333333
def setup(app):
334-
app.add_javascript('copybutton.js')
334+
app.add_js_file('copybutton.js')
335335
app.add_stylesheet('style.css')

doc/index.rst

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ pyRiemann-qiskit: Qiskit wrapper for pyRiemann
3232
<img src="_images/sphx_glr_plot_single_thumb.png">
3333
</div>
3434
</a>
35+
<a href="auto_examples/ERP/classify_P300_bi.html">
36+
<div class="col-md-3 thumbnail">
37+
<img src="_images/sphx_glr_plot_single_thumb.png">
38+
</div>
39+
</a>
3540
<a href="auto_examples/ERP/compare_dim_red.html">
3641
<div class="col-md-3 thumbnail">
3742
<img src="_images/sphx_glr_plot_single_thumb.png">

doc/requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ pandas
1010
cvxpy==1.1.12
1111
qiskit==0.20.0
1212
scipy==1.7.3
13+
moabb>=0.4.6
1314
git+https://github.com/pyRiemann/pyRiemann#egg=pyriemann

examples/ERP/classify_P300_bi.py

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
"""
2+
====================================================================
3+
Classification of P300 datasets from MOABB
4+
====================================================================
5+
6+
It demonstrates the QuantumClassifierWithDefaultRiemannianPipeline(). This
7+
pipeline uses Riemannian Geometry, Tangent Space and a quantum SVM
8+
classifier. MOABB is used to access many EEG datasets and also for the
9+
evaluation and comparison with other classifiers.
10+
11+
In QuantumClassifierWithDefaultRiemannianPipeline():
12+
If parameter "shots" is None then a classical SVM is used similar to the one
13+
in scikit learn.
14+
If "shots" is not None and IBM Qunatum token is provided with "q_account_token"
15+
then a real Quantum computer will be used.
16+
You also need to adjust the "n_components" in the PCA procedure to the number
17+
of qubits supported by the real quantum computer you are going to use.
18+
A list of real quantum computers is available in your IBM quantum account.
19+
20+
"""
21+
# Author: Anton Andreev
22+
# Modified from plot_classify_EEG_tangentspace.py of pyRiemann
23+
# License: BSD (3-clause)
24+
25+
from pyriemann.estimation import XdawnCovariances
26+
from pyriemann.tangentspace import TangentSpace
27+
from sklearn.pipeline import make_pipeline
28+
from matplotlib import pyplot as plt
29+
import warnings
30+
import seaborn as sns
31+
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
32+
from moabb import set_log_level
33+
from moabb.datasets import bi2012
34+
from moabb.evaluations import WithinSessionEvaluation
35+
from moabb.paradigms import P300
36+
from pyriemann_qiskit.classification import \
37+
QuantumClassifierWithDefaultRiemannianPipeline
38+
from sklearn.decomposition import PCA
39+
40+
print(__doc__)
41+
42+
##############################################################################
43+
# getting rid of the warnings about the future
44+
warnings.simplefilter(action="ignore", category=FutureWarning)
45+
warnings.simplefilter(action="ignore", category=RuntimeWarning)
46+
47+
warnings.filterwarnings("ignore")
48+
49+
set_log_level("info")
50+
51+
##############################################################################
52+
# Create Pipelines
53+
# ----------------
54+
#
55+
# Pipelines must be a dict of sklearn pipeline transformer.
56+
57+
##############################################################################
58+
# We have to do this because the classes are called 'Target' and 'NonTarget'
59+
# but the evaluation function uses a LabelEncoder, transforming them
60+
# to 0 and 1
61+
labels_dict = {"Target": 1, "NonTarget": 0}
62+
63+
paradigm = P300(resample=128)
64+
65+
datasets = [bi2012()] # MOABB provides several other P300 datasets
66+
67+
# reduce the number of subjects, the Quantum pipeline takes a lot of time
68+
# if executed on the entire dataset
69+
n_subjects = 5
70+
for dataset in datasets:
71+
dataset.subject_list = dataset.subject_list[0:n_subjects]
72+
73+
overwrite = True # set to True if we want to overwrite cached results
74+
75+
pipelines = {}
76+
77+
# A Riemannian Quantum pipeline provided by pyRiemann-qiskit
78+
# You can choose between classical SVM and Quantum SVM.
79+
pipelines["RG+QuantumSVM"] = QuantumClassifierWithDefaultRiemannianPipeline(
80+
shots=None, # 'None' forces classic SVM
81+
nfilter=2, # default 2
82+
# default n_components=10, a higher value renders better performance with
83+
# the non-qunatum SVM version used in qiskit
84+
# On a real Quantum computer (n_components = qubits)
85+
dim_red=PCA(n_components=5),
86+
# params={'q_account_token': '<IBM Quantum TOKEN>'}
87+
)
88+
89+
# Here we provide a pipeline for comparison:
90+
91+
# This is a standard pipeline similar to
92+
# QuantumClassifierWithDefaultRiemannianPipeline, but with LDA classifier
93+
# instead.
94+
pipelines["RG+LDA"] = make_pipeline(
95+
# applies XDawn and calculates the covariance matrix, output it matrices
96+
XdawnCovariances(
97+
nfilter=2,
98+
classes=[labels_dict["Target"]],
99+
estimator="lwf",
100+
xdawn_estimator="scm"
101+
),
102+
TangentSpace(),
103+
PCA(n_components=10),
104+
LDA(solver="lsqr", shrinkage="auto"), # you can use other classifiers
105+
)
106+
107+
print("Total pipelines to evaluate: ", len(pipelines))
108+
109+
evaluation = WithinSessionEvaluation(
110+
paradigm=paradigm,
111+
datasets=datasets,
112+
suffix="examples",
113+
overwrite=overwrite
114+
)
115+
116+
results = evaluation.process(pipelines)
117+
118+
print("Averaging the session performance:")
119+
print(results.groupby('pipeline').mean('score')[['score', 'time']])
120+
121+
##############################################################################
122+
# Plot Results
123+
# ----------------
124+
#
125+
# Here we plot the results to compare the two pipelines
126+
127+
fig, ax = plt.subplots(facecolor="white", figsize=[8, 4])
128+
129+
sns.stripplot(
130+
data=results,
131+
y="score",
132+
x="pipeline",
133+
ax=ax,
134+
jitter=True,
135+
alpha=0.5,
136+
zorder=1,
137+
palette="Set1",
138+
)
139+
sns.pointplot(data=results,
140+
y="score",
141+
x="pipeline",
142+
ax=ax, zorder=1,
143+
palette="Set1")
144+
145+
ax.set_ylabel("ROC AUC")
146+
ax.set_ylim(0.3, 1)
147+
148+
plt.show()

pyriemann_qiskit/utils/filtering.py

+52-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from sklearn.base import TransformerMixin
1+
from sklearn.base import BaseEstimator, TransformerMixin
2+
import numpy as np
23

34

45
class NoDimRed(TransformerMixin):
@@ -112,3 +113,53 @@ def transform(self, X):
112113
"""
113114
offset = 0 if self.is_even else 1
114115
return X[:, offset::2]
116+
117+
118+
class Vectorizer(BaseEstimator, TransformerMixin):
119+
"""This is an auxiliary transformer that allows one to vectorize data
120+
structures in a pipeline For instance, in the case of an X with
121+
dimensions (n_samples, n_features, n_channels),
122+
one might be interested in a new data structure with dimensions
123+
(n_samples, n_features x n_channels)
124+
125+
Notes
126+
-----
127+
.. versionadded:: 0.0.1
128+
129+
"""
130+
def __init__(self):
131+
pass
132+
133+
def fit(self, X, y):
134+
"""Fit the training data.
135+
136+
Parameters
137+
----------
138+
X : ndarray, shape (n_samples, n_features)
139+
Training vector, where `n_samples` is the number of samples and
140+
`n_features` is the number of features.
141+
y : ndarray, shape (n_samples,) (default: None)
142+
Target vector relative to X.
143+
In practice, never used.
144+
145+
Returns
146+
-------
147+
self : Vectorizer instance
148+
The Vectorizer instance.
149+
"""
150+
return self
151+
152+
def transform(self, X):
153+
"""Vectorize matrices.
154+
155+
Parameters
156+
----------
157+
X : ndarray, shape (n_samples, n_features, n_channels)
158+
ndarray of feature vectors.
159+
160+
Returns
161+
-------
162+
X : ndarray, shape (n_samples, n_features x n_channels)
163+
The vectorized matrices.
164+
"""
165+
return np.reshape(X, (X.shape[0], -1))

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
'docplex',
4141
'tqdm'
4242
],
43-
extras_require={'docs': ['sphinx-gallery', 'sphinx-bootstrap_theme', 'numpydoc', 'mne', 'seaborn'],
43+
extras_require={'docs': ['sphinx-gallery', 'sphinx-bootstrap_theme', 'numpydoc', 'mne', 'seaborn', 'moabb>=0.4.6'],
4444
'tests': ['pytest', 'seaborn', 'flake8', 'mne', 'pooch', 'tqdm']},
4545
zip_safe=False,
4646
)

0 commit comments

Comments
 (0)