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

Integration with qiskit-symb #313

Merged
merged 41 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
1a4dbad
add support for qiskit-symb
gcattan Sep 17, 2024
2c2aa34
temp fix (as per Simone suggestion)
gcattan Sep 18, 2024
55aa1f4
fix feature map
gcattan Sep 25, 2024
5d916e5
solution with threads
gcattan Sep 25, 2024
26d972c
working solution with a dumping statevector
gcattan Sep 25, 2024
443864d
add param_prefix to gen_x_featuremap
gcattan Sep 25, 2024
57e1e37
param prefix for all feature maps
gcattan Sep 25, 2024
f8a378e
clean SymFidelityStatevectorKernel
gcattan Sep 25, 2024
57fe9b1
add documentation
gcattan Sep 26, 2024
086a580
add some common feature maps
gcattan Sep 26, 2024
da13991
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 26, 2024
0600c8b
edge case for pegasos implementation
gcattan Sep 27, 2024
bdb799d
flake8
gcattan Sep 27, 2024
5440d47
fix typo
gcattan Sep 27, 2024
0a6d6ed
add qiskit-symb as an optional dependency
gcattan Sep 29, 2024
44d875a
fix no bare exception
gcattan Sep 29, 2024
ba0877a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 29, 2024
9a649fd
Update deploy_ghpages.yml
gcattan Sep 29, 2024
567c6f1
Update pyriemann_qiskit/utils/hyper_params_factory.py
gcattan Sep 30, 2024
10a385e
Update pyriemann_qiskit/utils/hyper_params_factory.py
gcattan Sep 30, 2024
e3f101b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 30, 2024
783d6e2
Update pyriemann_qiskit/utils/quantum_provider.py
gcattan Sep 30, 2024
25007ee
Update pyriemann_qiskit/utils/quantum_provider.py
gcattan Sep 30, 2024
c83c93b
Update pyriemann_qiskit/utils/hyper_params_factory.py
gcattan Sep 30, 2024
087a490
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 30, 2024
aec2678
correct workflow file
gcattan Sep 30, 2024
3ec4e13
tentative to fix error in workflow
gcattan Sep 30, 2024
9a9103e
fix workflow: wrong directory
gcattan Sep 30, 2024
0475525
simplify workflow file
gcattan Sep 30, 2024
37c876c
revert changes to workflow back.
gcattan Sep 30, 2024
ec94c95
create missing cache `symb_statevectors` inside doc
gcattan Sep 30, 2024
f08a599
second version (not passed as a prebuilt command)
gcattan Sep 30, 2024
1244195
add debug trace
gcattan Sep 30, 2024
de4bd0f
joblib result assignation read-only when started from within another …
gcattan Oct 1, 2024
29a90b9
simplify workflow
gcattan Oct 1, 2024
7b2663a
disable multithreading for financial data too
gcattan Oct 1, 2024
d20b3fb
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 1, 2024
47f1054
fix version of symengine for serialization
gcattan Oct 1, 2024
661a728
improve simulation time for plot_financial_data
gcattan Oct 1, 2024
bf43d2e
Missing flag to deactivate qiskit_symb
gcattan Oct 1, 2024
56227cb
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 1, 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
1 change: 1 addition & 0 deletions doc/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ moabb==1.1.0
pyriemann==0.6
docplex>=2.21.207
firebase_admin==6.5.0
qiskit-symb
7 changes: 4 additions & 3 deletions pyriemann_qiskit/classification.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class QuanticClassifierBase(BaseEstimator, ClassifierMixin):
If true, will output all intermediate results and logs.
shots : int, default=1024
Number of repetitions of each circuit, for sampling.
gen_feature_map : Callable[int, QuantumCircuit | FeatureMap], \
gen_feature_map : Callable[[int, str], QuantumCircuit | FeatureMap], \
default=Callable[int, ZZFeatureMap]
Function generating a feature map to encode data into a quantum state.
seed : int | None, default=None
Expand Down Expand Up @@ -357,7 +357,7 @@ class QuanticSVM(QuanticClassifierBase):
If true, will output all intermediate results and logs.
shots : int, default=1024
Number of repetitions of each circuit, for sampling.
gen_feature_map : Callable[int, QuantumCircuit | FeatureMap], \
gen_feature_map : Callable[[int, str], QuantumCircuit | FeatureMap], \
default=Callable[int, ZZFeatureMap]
Function generating a feature map to encode data into a quantum state.
seed : int | None, default=None
Expand Down Expand Up @@ -420,6 +420,7 @@ def _init_algo(self, n_features):
if self.quantum:
quantum_kernel = get_quantum_kernel(
self._feature_map,
self.gen_feature_map,
self._quantum_instance,
self.use_fidelity_state_vector_kernel,
)
Expand Down Expand Up @@ -496,7 +497,7 @@ class QuanticVQC(QuanticClassifierBase):
If true, will output all intermediate results and logs
shots : int, default=1024
Number of repetitions of each circuit, for sampling
gen_feature_map : Callable[int, QuantumCircuit | FeatureMap], \
gen_feature_map : Callable[[int, str], QuantumCircuit | FeatureMap], \
default=Callable[int, ZZFeatureMap]
Function generating a feature map to encode data into a quantum state.
seed : int | None, default=None
Expand Down
2 changes: 1 addition & 1 deletion pyriemann_qiskit/pipelines.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ class QuantumMDMVotingClassifier(BasePipeline):
If true, will output all intermediate results and logs.
shots : int (default:1024)
Number of repetitions of each circuit, for sampling.
gen_feature_map : Callable[int, QuantumCircuit | FeatureMap] \
gen_feature_map : Callable[[int, str], QuantumCircuit | FeatureMap] \
(default : Callable[int, ZZFeatureMap])
Function generating a feature map to encode data into a quantum state.

Expand Down
44 changes: 33 additions & 11 deletions pyriemann_qiskit/utils/hyper_params_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ def gen_x_feature_map(reps=2):

Returns
-------
ret : XFeatureMap
An instance of XFeatureMap.
ret : Callable[[int, str], XFeatureMap]
A callable that takes into arguments:
- the number of features
gcattan marked this conversation as resolved.
Show resolved Hide resolved
- the prefix string for the parameters
And returns an instance of XFeatureMap.

Raises
------
Expand All @@ -41,16 +44,18 @@ def gen_x_feature_map(reps=2):
Notes
-----
.. versionadded:: 0.2.0
.. versionchanged:: 0.4.0
Possibility to specify parameter prefix.
"""
if reps < 1:
raise ValueError(f"Parameter reps must be superior or equal to 1 (Got {reps})")

return lambda n_features: PauliFeatureMap(
return lambda n_features, param_prefix="x": PauliFeatureMap(
feature_dimension=n_features,
paulis=["X"],
reps=reps,
data_map_func=None,
parameter_prefix="x",
parameter_prefix=param_prefix,
insert_barriers=False,
name="XFeatureMap",
)
Expand All @@ -70,8 +75,11 @@ def gen_z_feature_map(reps=2):

Returns
-------
ret : ZFeatureMap
An instance of ZFeatureMap.
ret : Callable[[int, str], ZFeatureMap]
A callable that takes into arguments:
- the number of features
gcattan marked this conversation as resolved.
Show resolved Hide resolved
- the prefix string for the parameters
And returns an instance of ZFeatureMap.

Raises
------
Expand All @@ -86,11 +94,17 @@ def gen_z_feature_map(reps=2):
Notes
-----
.. versionadded:: 0.2.0
.. versionchanged:: 0.4.0
Possibility to specify parameter prefix.
"""
if reps < 1:
raise ValueError(f"Parameter reps must be superior or equal to 1 (Got {reps})")

return lambda n_features: ZFeatureMap(feature_dimension=n_features, reps=reps)
return lambda n_features, param_prefix="x": ZFeatureMap(
feature_dimension=n_features,
reps=reps,
parameter_prefix=param_prefix,
)


def gen_zz_feature_map(reps=2, entanglement="linear"):
Expand All @@ -114,8 +128,11 @@ def gen_zz_feature_map(reps=2, entanglement="linear"):

Returns
-------
ret : ZZFeatureMap
An instance of ZZFeatureMap.
ret : Callable[[int, str], ZZFeatureMap]
A callable that takes into arguments:
- the number of features
gcattan marked this conversation as resolved.
Show resolved Hide resolved
- the prefix string for the parameters
And returns an instance of ZZFeatureMap.

Raises
------
Expand All @@ -132,12 +149,17 @@ def gen_zz_feature_map(reps=2, entanglement="linear"):
Notes
-----
.. versionadded:: 0.0.1
.. versionchanged:: 0.4.0
Possibility to specify parameter prefix.
"""
if reps < 1:
raise ValueError(f"Parameter reps must be superior or equal to 1 (Got {reps})")

return lambda n_features: ZZFeatureMap(
feature_dimension=n_features, reps=reps, entanglement=entanglement
return lambda n_features, param_prefix="x": ZZFeatureMap(
feature_dimension=n_features,
reps=reps,
entanglement=entanglement,
parameter_prefix=param_prefix,
)


Expand Down
161 changes: 147 additions & 14 deletions pyriemann_qiskit/utils/quantum_provider.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
"""Module containing helpers for IBM quantum backends
providers and simulators."""

import joblib
import logging
import numpy as np
import os
import pickle

from qiskit_aer import AerSimulator
gcattan marked this conversation as resolved.
Show resolved Hide resolved
from qiskit_aer.quantum_info import AerStatevector
Expand All @@ -11,6 +15,117 @@
FidelityStatevectorKernel,
FidelityQuantumKernel,
)
from qiskit_symb.quantum_info import Statevector


class SymbFidelityStatevectorKernel:

"""Symbolic Statevector kernel

An implementation of the quantum kernel for classically simulated
state vectors [1]_ using qiskit-symb for symbolic representation
of statevectors [2]_.

Here, the kernel function is defined as the overlap of two simulated quantum
statevectors produced by a parametrized quantum circuit (called feature map) [1]_.

Notes
-----
.. versionadded:: 0.4.0

Parameters
----------
feature_map: QuantumCircuit | FeatureMap
An instance of a feature map.
gen_feature_map: Callable[[int, str], QuantumCircuit | FeatureMap], \
default=Callable[int, ZZFeatureMap]
Function generating a feature map to encode data into a quantum state.
n_jobs: int
The number of job for fidelity evaluation.
If null or negative, the number of jobs is set to 1
If set to 1, evaluation will run on the main thread.

References
----------
.. [1] \
https://github.com/qiskit-community/qiskit-machine-learning/blob/30dad803e9457f955464220eddc1e55a65452bbc/qiskit_machine_learning/kernels/fidelity_statevector_kernel.py#L31
.. [2] https://github.com/SimoneGasperini/qiskit-symb/issues/6

"""

def __init__(self, feature_map, gen_feature_map, n_jobs=1):
self.n_jobs = n_jobs if n_jobs >= 1 else 1
cached_file = os.path.join(
"symb_statevectors", f"{feature_map.name}-{feature_map.reps}"
)

if os.path.isfile(cached_file):
print("Loading symbolic Statevector from cache")
file = open(cached_file, "rb")
sv = pickle.load(file)
else:
print("Computing symbolic Statevector")
fm2 = gen_feature_map(feature_map.num_qubits, "b")
self.circuit = feature_map.compose(fm2.inverse()).decompose()
sv = Statevector(self.circuit)
print(f"Dumping to {cached_file}")
file = open(cached_file, "wb")
pickle.dump(sv, file)

self.function = sv.to_lambda()

"""Evaluate the quantum kernel.

Returns
-------
kernel : ndarray, shape (len(x_vec), len(y_vec))
The kernel matrix.

Notes
-----
.. versionadded:: 0.4.0
"""

def evaluate(self, x_vec, y_vec=None):
if y_vec is None:
gcattan marked this conversation as resolved.
Show resolved Hide resolved
y_vec = x_vec

x_vec_len = len(x_vec)
y_vec_len = len(y_vec)

is_sim = x_vec_len == y_vec_len and (x_vec == y_vec).all()

kernel_matrix = np.zeros((x_vec_len, y_vec_len))

chunck = x_vec_len // self.n_jobs

def compute_fidelity_partial_matrix(i_thread):
for i in range(i_thread * chunck, (i_thread + 1) * chunck):
x = x_vec[i]
for j in range(i if is_sim else y_vec_len):
y = y_vec[j]
if isinstance(x, np.float64):
# Pegagos implementation
fidelity = abs(self.function(x, y)[0, 0]) ** 2
else:
fidelity = abs(self.function(*x, *y)[0, 0]) ** 2

kernel_matrix[i, j] = fidelity
if is_sim:
kernel_matrix[j, i] = fidelity
return kernel_matrix

if self.n_jobs == 1:
return compute_fidelity_partial_matrix(0)
else:
print("n_jobs greater than 1, parallelizing")
results = joblib.Parallel(n_jobs=self.n_jobs)(
joblib.delayed(compute_fidelity_partial_matrix)(i_thread)
for i_thread in range(self.n_jobs)
)
for result in results:
kernel_matrix += result
return kernel_matrix


def get_provider():
Expand Down Expand Up @@ -93,12 +208,16 @@ def get_device(provider, min_qubits):
)


def get_quantum_kernel(feature_map, quantum_instance, use_fidelity_state_vector_kernel):
def get_quantum_kernel(
feature_map, gen_feature_map, quantum_instance, use_fidelity_state_vector_kernel
):
"""Get a quantum kernel

Return an instance of FidelityQuantumKernel or
FidelityStatevectorKernel (in the case of a simulation).

For simulation with a small number of qubits (< 9), qiskit-symb is used.

Parameters
----------
feature_map: QuantumCircuit | FeatureMap
Expand All @@ -116,26 +235,40 @@ def get_quantum_kernel(feature_map, quantum_instance, use_fidelity_state_vector_
Notes
-----
.. versionadded:: 0.3.0
.. versionchanged:: 0.4.0
Add support for qiskit-symb
"""
if use_fidelity_state_vector_kernel and isinstance(
quantum_instance._backend, AerSimulator
):
logging.log(
logging.WARN,
"""FidelityQuantumKernel skipped because of time.
# For simulation:
if feature_map.num_qubits <= 9:
# With a small number of qubits, let's use qiskit-symb
# See:
# https://medium.com/qiskit/qiskit-symb-a-qiskit-ecosystem-package-for-symbolic-quantum-computation-b6b4407fa705
kernel = SymbFidelityStatevectorKernel(
feature_map, gen_feature_map, n_jobs=4
)
logging.log(
logging.WARN,
"""Using SymbFidelityStatevectorKernel""",
)
else:
# For a larger number of qubits,
# we will not use FidelityQuantumKernel as it is slow. See
# https://github.com/qiskit-community/qiskit-machine-learning/issues/547#issuecomment-1486527297
kernel = FidelityStatevectorKernel(
feature_map=feature_map,
statevector_type=AerStatevector,
shots=quantum_instance.options["shots"],
)
logging.log(
logging.WARN,
"""FidelityQuantumKernel skipped because of time.
Using FidelityStatevectorKernel with AerStatevector.
Seed cannot be set with FidelityStatevectorKernel.
Increase the number of shots to diminish the noise.""",
)

# if this is a simulation,
# we will not use FidelityQuantumKernel as it is slow. See
# https://github.com/qiskit-community/qiskit-machine-learning/issues/547#issuecomment-1486527297
kernel = FidelityStatevectorKernel(
feature_map=feature_map,
statevector_type=AerStatevector,
shots=quantum_instance.options["shots"],
)
)
else:
kernel = FidelityQuantumKernel(
feature_map=feature_map, fidelity=ComputeUncompute(quantum_instance)
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ docplex>=2.21.207
firebase_admin==6.5.0
tqdm
pandas
qiskit-symb
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
'firebase_admin==6.5.0',
'scikit-learn==1.5.1',
'tqdm',
'pandas'
'pandas',
'qiskit-symb'
],
extras_require={'docs': [
'sphinx-gallery',
Expand Down
Binary file added symb_statevectors/XFeatureMap-2
Binary file not shown.
Binary file added symb_statevectors/XFeatureMap-3
Binary file not shown.
Binary file added symb_statevectors/ZFeatureMap-2
Binary file not shown.
Binary file added symb_statevectors/ZFeatureMap-3
Binary file not shown.
Binary file added symb_statevectors/ZZFeatureMap-2
Binary file not shown.
Binary file added symb_statevectors/ZZFeatureMap-3
Binary file not shown.
Loading