Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to use QAOA-CV optimizer inside classification module #312

Merged
merged 57 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
4b1045c
qaoa cv optimizer for QuanticNCH and MDM
gcattan Sep 17, 2024
c362311
add QAOA-CV to classification module (MDM and NCH)
gcattan Sep 17, 2024
5a5a1fa
Merge branch 'main' into feat/qaoa-cv-classification
gcattan Sep 17, 2024
be2965b
complete tests
gcattan Sep 17, 2024
7cfdbd1
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 17, 2024
8074813
default create_mixer and n_reps for QAOA-CV
gcattan Sep 17, 2024
337a5ff
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 17, 2024
1e29fa2
flake8
gcattan Sep 17, 2024
124d538
add not implemented method for QAOA-CV
gcattan Sep 18, 2024
a0b1a72
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 18, 2024
9052177
reshape solution
gcattan Sep 18, 2024
03b8b4d
add inequalitytoequality converter
gcattan Sep 19, 2024
40bd87a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 19, 2024
f403482
encode not available in new version of the inegality converter.
gcattan Sep 19, 2024
01fd831
use LinearEqualityToPenalty converter instead
gcattan Sep 19, 2024
884b503
- bound variables (even if type is binary -> fix bug with LinearEqual…
gcattan Sep 19, 2024
63d04c9
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 19, 2024
c2115f6
fix bug with quantum=False (private attribute _quantum_instance not a…
gcattan Sep 19, 2024
478c597
add use_params to all mixers
gcattan Sep 19, 2024
1e1a055
flake8
gcattan Sep 19, 2024
9fc6cff
simplify condition for checking whether there is quantum_instance
gcattan Sep 19, 2024
b78e689
missing condition on "cost operator has no parameters"
gcattan Sep 19, 2024
7f52058
missing file
gcattan Sep 19, 2024
fcad45b
export is_pauli_identity to math module
gcattan Sep 19, 2024
0de128c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 19, 2024
18c7132
add QAOACV test to light benchmark
gcattan Sep 20, 2024
dddb7fc
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 20, 2024
2b90ffa
flake8
gcattan Sep 20, 2024
366f22e
change default optimizer for QAOA-CV
gcattan Sep 20, 2024
1a6d0d0
remove use_params misplaced
gcattan Sep 20, 2024
fe3d066
Update pyriemann_qiskit/classification.py
gcattan Sep 27, 2024
8f6cd11
Update pyriemann_qiskit/classification.py
gcattan Sep 27, 2024
0023e67
Update pyriemann_qiskit/utils/math.py
gcattan Sep 27, 2024
3c1d8b9
rename imports
gcattan Sep 27, 2024
bfda31e
correct location of docstrings
gcattan Sep 27, 2024
a98456f
correct import of numpy
gcattan Sep 27, 2024
f19312b
Update pyriemann_qiskit/utils/docplex.py
gcattan Sep 29, 2024
29baec0
Update pyriemann_qiskit/utils/docplex.py
gcattan Sep 29, 2024
e3b462e
replace all "our"
gcattan Sep 29, 2024
c3848f1
replace reference to covariance in docplex module
gcattan Sep 29, 2024
31b8361
rename covmat to X
gcattan Sep 29, 2024
3af3154
check dostrings in docplex.py
gcattan Sep 29, 2024
5e7032a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 29, 2024
5d650ea
Update pyriemann_qiskit/utils/docplex.py
gcattan Sep 30, 2024
8be7a6a
Update pyriemann_qiskit/utils/docplex.py
gcattan Sep 30, 2024
311d8d5
Update pyriemann_qiskit/utils/docplex.py
gcattan Sep 30, 2024
df951a6
Update pyriemann_qiskit/utils/docplex.py
gcattan Sep 30, 2024
9cb772c
Update pyriemann_qiskit/utils/docplex.py
gcattan Sep 30, 2024
52f4d8e
Update pyriemann_qiskit/utils/docplex.py
gcattan Sep 30, 2024
8ddc937
Update pyriemann_qiskit/utils/docplex.py
gcattan Sep 30, 2024
1b10376
Update pyriemann_qiskit/utils/docplex.py
gcattan Sep 30, 2024
cdeafb1
Update pyriemann_qiskit/utils/hyper_params_factory.py
gcattan Sep 30, 2024
1cbdba1
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 30, 2024
34e55de
Improve description of `docplex_spdmat`
gcattan Sep 30, 2024
d94f1bb
Update pyriemann_qiskit/utils/docplex.py
gcattan Sep 30, 2024
c34ee41
fix import of light_benchmark
gcattan Sep 30, 2024
43b9952
Update light_benchmark.py
gcattan Sep 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion benchmarks/light_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
from moabb import set_log_level
qbarthelemy marked this conversation as resolved.
Show resolved Hide resolved
from moabb.datasets import bi2012
from moabb.paradigms import P300
from qiskit_algorithms.optimizers import SPSA
from pyriemann_qiskit.utils import distance, mean # noqa
from pyriemann_qiskit.utils.hyper_params_factory import create_mixer_rotational_X_gates
from pyriemann_qiskit.pipelines import (
QuantumClassifierWithDefaultRiemannianPipeline,
QuantumMDMWithRiemannianPipeline,
Expand Down Expand Up @@ -114,7 +116,6 @@
pipelines["NCH_MIN_HULL"] = make_pipeline(
XdawnCovariances(
nfilter=3,
# classes=[labels_dict["Target"]],
estimator="lwf",
xdawn_estimator="scm",
),
Expand All @@ -128,6 +129,26 @@
),
)


pipelines["NCH_MIN_HULL_QAOACV"] = make_pipeline(
XdawnCovariances(
nfilter=3,
estimator="lwf",
xdawn_estimator="scm",
),
QuanticNCH(
n_hulls_per_class=1,
n_samples_per_hull=3,
n_jobs=12,
subsampling="min",
quantum=True,
# Provide create_mixer to force QAOA-CV optimization
create_mixer=create_mixer_rotational_X_gates(0),
shots=100,
qaoa_optimizer=SPSA(maxiter=500),
),
)

##############################################################################
# Compute score
# --------------
Expand Down
2 changes: 2 additions & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ Hyper-parameters generation
create_mixer_rotational_X_gates
create_mixer_rotational_XY_gates
create_mixer_rotational_XZ_gates
create_mixer_qiskit_default

Preprocessing
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -169,6 +170,7 @@ Math
cov_to_corr_matrix
union_of_diff
to_xyz
is_pauli_identity

Firebase
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
20 changes: 10 additions & 10 deletions examples/toys_dataset/plot_qaoa_cv.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@

from docplex.mp.model import Model
import matplotlib.pyplot as plt
from qiskit.primitives import BackendSampler
from qiskit_aer import AerSimulator
from qiskit_algorithms.optimizers import COBYLA, SPSA

from pyriemann_qiskit.utils.docplex import QAOACVOptimizer
from pyriemann_qiskit.utils.hyper_params_factory import (
create_mixer_qiskit_default,
create_mixer_rotational_X_gates,
create_mixer_rotational_XY_gates,
create_mixer_rotational_XZ_gates,
Expand All @@ -46,16 +45,16 @@ def run_qaoa_cv(n_reps, optimizer, create_mixer):
# objective function to minimize
mdl.minimize((x - 0.83 + y + 2 * z) ** 2)

# Define the BackendSampler (previously QuantumInstance)
backend = AerSimulator(method="statevector", cuStateVec_enable=True)
quantum_instance = BackendSampler(
backend, options={"shots": 200, "seed_simulator": 42}
# Instanciate the QAOA-CV
# Note: if quantum_instance is None, it will be created inside the optimizer.
qaoa_cv = QAOACVOptimizer(
create_mixer, n_reps, quantum_instance=None, optimizer=optimizer
)
quantum_instance.transpile_options["seed_transpiler"] = 42

# Instanciate the QAOA-CV
qaoa_cv = QAOACVOptimizer(create_mixer, n_reps, quantum_instance, optimizer)
solution = qaoa_cv.solve(mdl)
# reshape is when working with covariance matrices
# So the vector of solution is reshaped into a matrix
# (this is not the case here)
solution = qaoa_cv.solve(mdl, reshape=False)

# print the time, the solution (that it the value for our three variable)
# and the minimum of the objective function
Expand Down Expand Up @@ -96,6 +95,7 @@ def run_qaoa_cv(n_reps, optimizer, create_mixer):
for angle in range(n_angles):
angle = math.pi * angle / n_angles
mixers = [
create_mixer_qiskit_default(angle),
create_mixer_rotational_X_gates(angle),
create_mixer_rotational_XY_gates(angle),
create_mixer_rotational_XZ_gates(angle),
Expand Down
111 changes: 88 additions & 23 deletions pyriemann_qiskit/classification.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@
from .datasets import get_feature_dimension
from .utils.docplex import (
set_global_optimizer,
get_global_optimizer,
ClassicalOptimizer,
NaiveQAOAOptimizer,
QAOACVOptimizer,
)
from .utils.distance import (
distance_functions,
Expand Down Expand Up @@ -619,6 +621,37 @@ def parameter_count(self):
return 0


def _get_docplex_optimizer_from_params_bag(
logger,
quantum,
quantum_instance,
upper_bound,
qaoa_optimizer,
classical_optimizer,
create_mixer,
n_reps,
):
if quantum:
if create_mixer:
logger._log("Using QAOACVOptimizer")
return QAOACVOptimizer(
create_mixer=create_mixer,
n_reps=n_reps,
quantum_instance=quantum_instance,
optimizer=qaoa_optimizer,
)
else:
logger._log("Using NaiveQAOAOptimizer")
return NaiveQAOAOptimizer(
quantum_instance=quantum_instance,
upper_bound=upper_bound,
optimizer=qaoa_optimizer,
)
else:
logger._log("Using ClassicalOptimizer (COBYLA)")
return ClassicalOptimizer(classical_optimizer)


class QuanticMDM(QuanticClassifierBase):

"""Quantum-enhanced MDM classifier
Expand All @@ -639,6 +672,8 @@ class QuanticMDM(QuanticClassifierBase):
Add classical_optimizer parameter.
.. versionchanged:: 0.3.0
Add qaoa_optimizer parameter.
.. versionchanged:: 0.4.0
Add QAOA-CV optimization.

Parameters
----------
Expand Down Expand Up @@ -675,6 +710,13 @@ class QuanticMDM(QuanticClassifierBase):
qaoa_optimizer : SciPyOptimizer, default=SLSQP()
An instance of a scipy optimizer to find the optimal weights for the
parametric circuit (ansatz).
create_mixer : None | Callable[int, QuantumCircuit], default=None
A delegate that takes into input an angle and returns a QuantumCircuit.
This circuit is the mixer operatior for the QAOA-CV algorithm.
If None and quantum, the NaiveQAOAOptimizer will be used instead.
n_reps : int, default=3
The number of time the mixer and cost operator are repeated in the QAOA-CV
circuit.

See Also
--------
Expand Down Expand Up @@ -709,6 +751,8 @@ def __init__(
regularization=None,
classical_optimizer=CobylaOptimizer(rhobeg=2.1, rhoend=0.000001),
qaoa_optimizer=SLSQP(),
create_mixer=None,
n_reps=3,
):
QuanticClassifierBase.__init__(
self, quantum, q_account_token, verbose, shots, None, seed
Expand All @@ -718,6 +762,8 @@ def __init__(
self.regularization = regularization
self.classical_optimizer = classical_optimizer
self.qaoa_optimizer = qaoa_optimizer
self.create_mixer = create_mixer
self.n_reps = n_reps

@staticmethod
def _override_predict_distance(mdm):
Expand Down Expand Up @@ -753,16 +799,16 @@ def _init_algo(self, n_features):
classifier._predict_distances = QuanticMDM._override_predict_distance(
classifier
)
if self.quantum:
self._log("Using NaiveQAOAOptimizer")
self._optimizer = NaiveQAOAOptimizer(
quantum_instance=self._quantum_instance,
upper_bound=self.upper_bound,
optimizer=self.qaoa_optimizer,
)
else:
self._log("Using ClassicalOptimizer (COBYLA)")
self._optimizer = ClassicalOptimizer(self.classical_optimizer)
self._optimizer = _get_docplex_optimizer_from_params_bag(
self,
self.quantum,
self._quantum_instance if self.quantum else None,
self.upper_bound,
self.qaoa_optimizer,
self.classical_optimizer,
self.create_mixer,
self.n_reps,
)
set_global_optimizer(self._optimizer)
return classifier

Expand Down Expand Up @@ -953,9 +999,15 @@ def _predict_distances(self, X):
print("Not running in parallel")

if parallel:
dists = Parallel(n_jobs=self.n_jobs)(
delayed(self._process_sample)(x) for x in X
)
# Get global optimizer in this process
optimizer = get_global_optimizer(default=None)

def job(x):
# Set the global optimizer inside the new process
set_global_optimizer(optimizer)
return self._process_sample(x)

dists = Parallel(n_jobs=self.n_jobs)(delayed(job)(x) for x in X)

else:
for x in X:
Expand Down Expand Up @@ -1018,6 +1070,8 @@ class QuanticNCH(QuanticClassifierBase):
.. versionadded:: 0.2.0
.. versionchanged:: 0.3.0
Add qaoa_optimizer parameter.
.. versionchanged:: 0.4.0
Add QAOA-CV optimization.

Parameters
----------
Expand Down Expand Up @@ -1059,6 +1113,13 @@ class QuanticNCH(QuanticClassifierBase):
qaoa_optimizer : SciPyOptimizer, default=SLSQP()
An instance of a scipy optimizer to find the optimal weights for the
parametric circuit (ansatz).
create_mixer : None | Callable[int, QuantumCircuit], default=None
A delegate that takes into input an angle and returns a QuantumCircuit.
This circuit is the mixer operatior for the QAOA-CV algorithm.
If None and quantum, the NaiveQAOAOptimizer will be used instead.
n_reps : int, default=3
The number of time the mixer and cost operator are repeated in the QAOA-CV
circuit.

References
----------
Expand All @@ -1081,6 +1142,8 @@ def __init__(
n_samples_per_hull=10,
subsampling="min",
qaoa_optimizer=SLSQP(),
create_mixer=None,
n_reps=3,
):
QuanticClassifierBase.__init__(
self, quantum, q_account_token, verbose, shots, None, seed
Expand All @@ -1093,6 +1156,8 @@ def __init__(
self.n_jobs = n_jobs
self.subsampling = subsampling
self.qaoa_optimizer = qaoa_optimizer
self.create_mixer = create_mixer
self.n_reps = n_reps

def _init_algo(self, n_features):
self._log("Nearest Convex Hull Classifier initiating algorithm")
Expand All @@ -1104,16 +1169,16 @@ def _init_algo(self, n_features):
subsampling=self.subsampling,
)

if self.quantum:
self._log("Using NaiveQAOAOptimizer")
self._optimizer = NaiveQAOAOptimizer(
quantum_instance=self._quantum_instance,
upper_bound=self.upper_bound,
optimizer=self.qaoa_optimizer,
)
else:
self._log("Using ClassicalOptimizer")
self._optimizer = ClassicalOptimizer(self.classical_optimizer)
self._optimizer = _get_docplex_optimizer_from_params_bag(
self,
self.quantum,
self._quantum_instance if self.quantum else None,
self.upper_bound,
self.qaoa_optimizer,
self.classical_optimizer,
self.create_mixer,
self.n_reps,
)

# sets the optimizer for the distance functions
# used in NearestConvexHull class
Expand Down
Loading
Loading