Skip to content
Merged
Show file tree
Hide file tree
Changes from 77 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
379f528
add first draft of qulacs implementation
David-Kreplin Jan 15, 2025
2fe213b
wip
David-Kreplin Jan 16, 2025
c0d8b73
add parametrized gates with values
David-Kreplin Jan 22, 2025
7d0b05f
fix sign in parametrized gates
David-Kreplin Jan 22, 2025
62fb226
functionized parameters working
David-Kreplin Jan 27, 2025
bdec057
first draft of mixed parameter expressions
David-Kreplin Jan 27, 2025
d06d91c
circuit eval working, obs todo
David-Kreplin Jan 27, 2025
55e6ce3
circuit eval working, obs todo
David-Kreplin Jan 27, 2025
415978d
first part of observations owkring
David-Kreplin Jan 27, 2025
3cf80c4
first implementation of parametrized observables
David-Kreplin Jan 27, 2025
3421ef3
wip: starting on low level qnn with qulacs
David-Kreplin Jan 27, 2025
a3f71d5
draft but not working
David-Kreplin Jan 27, 2025
171e856
fix bug in sorting
David-Kreplin Jan 27, 2025
3274e8e
first run of pqk is working
David-Kreplin Jan 27, 2025
414ebb6
added CausalConeSimulator
David-Kreplin Jan 29, 2025
19c2d92
simple gradients of a single variable are working
David-Kreplin Jan 29, 2025
22d8185
more complex terms with single parameters, mixed parameters not working
David-Kreplin Jan 29, 2025
e1c08d9
mixed derivatives working
David-Kreplin Jan 29, 2025
34de866
orbital grad with different observables working
David-Kreplin Jan 29, 2025
19adbab
orbital grad with different observables working
David-Kreplin Jan 29, 2025
61f2e50
first draft of gradients in llqnn working
David-Kreplin Jan 30, 2025
8c7726d
cleaning and efficiency boost
David-Kreplin Jan 30, 2025
3696f69
complete arbitrary operators are workgin
David-Kreplin Jan 30, 2025
0230dae
wip
David-Kreplin Feb 4, 2025
658d495
comment out qulacs_execution.py
David-Kreplin Feb 6, 2025
6129748
add statevector evaluation
David-Kreplin Feb 6, 2025
a9e0689
remove qulacs cp gate
MoritzWillmann Feb 7, 2025
aa29653
hotfix used parameters for non-parametrized gates
rothma Feb 19, 2025
c6399d0
add same fix for two qubit gate
MoritzWillmann Feb 20, 2025
21c8c57
clean statement
MoritzWillmann Feb 20, 2025
eb560ce
allow circuits without features and parameters
MoritzWillmann Feb 20, 2025
5a71af0
Merge branch 'develop' into qulacs
MoritzWillmann Feb 21, 2025
bb18b4c
move qulacs qnn to folder
MoritzWillmann Feb 21, 2025
44b080c
black
MoritzWillmann Feb 21, 2025
5eb47d3
fix is_parameterized check
MoritzWillmann Feb 24, 2025
29dd239
wip
David-Kreplin Feb 27, 2025
05b8cb2
black
David-Kreplin Feb 27, 2025
a6af213
merge
David-Kreplin Feb 27, 2025
7ef1b90
use kwargs for evaluate functions
MoritzWillmann Feb 28, 2025
673cecf
rename qualcs to qulacs
MoritzWillmann Feb 28, 2025
e21e1de
use public properties
MoritzWillmann Feb 28, 2025
bc2fb66
fix test notebooks
MoritzWillmann Feb 28, 2025
4ac7c70
black
MoritzWillmann Feb 28, 2025
a8c57b0
add fidelity kernel implementation
David-Kreplin Feb 28, 2025
cf0fdde
Merge branch 'qulacs' of github.com:David-Kreplin/squlearn into qulacs
David-Kreplin Feb 28, 2025
8de4981
black
David-Kreplin Feb 28, 2025
12d4302
variance calculation in qulacs now working
David-Kreplin Feb 28, 2025
c57da74
refactoring of evaluation code
David-Kreplin Feb 28, 2025
9c42c59
fix single parameter derivatives for lowlevelqnn
David-Kreplin Feb 28, 2025
f824215
checkin of untracked evaluation_classes.py
David-Kreplin Mar 3, 2025
a27b6d1
use all parameters in symbol tuple
MoritzWillmann Mar 21, 2025
c5a6e16
fix sorting
MoritzWillmann Mar 26, 2025
2950689
Merge branch 'develop' into qulacs
MoritzWillmann Apr 25, 2025
77f2038
fix in fidelity kernels
David-Kreplin May 16, 2025
ab318d1
Merge branch 'develop' into qulacs
MoritzWillmann Jul 4, 2025
91c4d5a
Merge branch 'develop' into qulacs
David-Kreplin Aug 1, 2025
3f8f6ea
add dependency and fix shots
David-Kreplin Aug 1, 2025
33184d2
small fixes
David-Kreplin Aug 1, 2025
a975bb0
reset to pennylane default
David-Kreplin Aug 1, 2025
cc6093c
Merge branch 'develop' into qulacs
David-Kreplin Aug 18, 2025
faf2906
add caching for qulacs circuits
David-Kreplin Aug 18, 2025
88ee68a
black
David-Kreplin Aug 18, 2025
a703511
removed unnecessary examples
David-Kreplin Aug 18, 2025
f7b66e3
reset of examples
David-Kreplin Aug 19, 2025
3d33945
changed some examples to qulacs
David-Kreplin Aug 19, 2025
98a7640
black
David-Kreplin Aug 19, 2025
66bf7b9
started cleaning
David-Kreplin Aug 19, 2025
bf8ff66
cleaning
David-Kreplin Aug 19, 2025
d5f6e02
cleaning
David-Kreplin Aug 19, 2025
bd5d0df
cleaning of circuit done
David-Kreplin Aug 19, 2025
ffedbbb
cleaning of qulacs_circuit.py
David-Kreplin Aug 19, 2025
b39ee62
refactored fidelity kernels for qulacs and pennylane
David-Kreplin Aug 19, 2025
a14e548
black
David-Kreplin Aug 19, 2025
5c9267e
Merge branch 'develop' into qulacs
MoritzWillmann Aug 20, 2025
57e50af
adapt LowLevelQNNQulacs to accept num_features
MoritzWillmann Aug 21, 2025
1d73219
Merge branch 'develop' into qulacs
MoritzWillmann Aug 21, 2025
67f9b00
add qulacs to docs
MoritzWillmann Aug 21, 2025
c21c262
fix QulacsCircuit path in classes.rst
MoritzWillmann Aug 21, 2025
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
22 changes: 14 additions & 8 deletions docs/modules/classes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,9 @@
:template: class.rst

qnn.lowlevel_qnn.LowLevelQNN
qnn.lowlevel_qnn.lowlevel_qnn_qiskit.LowLevelQNNQiskit
qnn.lowlevel_qnn.lowlevel_qnn_qiskit.Expec
qnn.lowlevel_qnn.lowlevel_qnn_pennylane.LowLevelQNNPennyLane
qnn.lowlevel_qnn.lowlevel_qnn_qiskit.LowLevelQNNQiskit
qnn.lowlevel_qnn.lowlevel_qnn_qulacs.LowLevelQNNQulacs
qnn.lowlevel_qnn.lowlevel_qnn_base.LowLevelQNNBase
qnn.loss.SquaredLoss
qnn.loss.VarianceLoss
Expand Down Expand Up @@ -274,6 +274,18 @@

util.pennylane.PennyLaneCircuit

Qulacs interface
------------------------------------

.. currentmodule:: squlearn

.. autosummary::

Check warning on line 282 in docs/modules/classes.rst

View workflow job for this annotation

GitHub Actions / doc_checks

autosummary: failed to import util.pennylane.QulacsCircuit.
:nosignatures:
:toctree: generated/
:template: class.rst

util.pennylane.QulacsCircuit


Base Classes
------------------------------------
Expand All @@ -293,9 +305,3 @@
qnn.base_qnn.BaseQNN
qnn.loss.qnn_loss_base.QNNLossBase
qrc.base_qrc.BaseQRC






8 changes: 6 additions & 2 deletions docs/user_guide/executor.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The :class:`Executor <squlearn.Executor>` class is the central component of sQUl
for running all quantum jobs.
Both high- and low-level methods utilize the :class:`Executor <squlearn.Executor>` class to execute circuits or to run other
quantum jobs.
The class provides a high-level interface to the simulators of PennyLane and Qiskit,
The class provides a high-level interface to the simulators of PennyLane, Qiskit and Qulacs,
as well as manages access to real QC hardware as for example IBM Quantum.
It features a variety of comfort features, such as result caching,
automatic restarts of failed jobs, logging of all actions, and Qiskit Session handling.
Expand Down Expand Up @@ -93,7 +93,8 @@ execution environment:

- A string specifying the local simulator backend: Qiskit's :class:`AerSimulator <qiskit_aer.AerSimulator>` is available by providing ``"qiskit"``,
``"statevector_simulator"`` and ``"qasm_simulator"``; PennyLane's :class:`DefaultQubit <pennylane.devices.default_qubit.DefaultQubit>` simulator can be initialized by
``"pennylane"`` or ``"default.qubit"``. Note that with Qiskit version 1.2, the sampler routines will always performe shots for the ``"statevector_simulator"`` keyword.
``"pennylane"`` or ``"default.qubit"``. The Qulacs statevector simulator can be used with the keyword ``"qulacs"``.
Note that with Qiskit version 1.2, the sampler routines will always performe shots for the ``"statevector_simulator"`` keyword.

.. jupyter-execute::

Expand All @@ -113,6 +114,9 @@ execution environment:
# Initialize the Executor with the PennyLane shot-based simulator
executor = Executor("default.qubit", shots=1234)

# Initialize the Executor with the Qulacs statevector simulator
executor = Executor("qulacs")

- A Qiskit :class:`Backend <qiskit.providers.Backend>`, e.g. a :class:`AerProvider <qiskit_aer.AerProvider>` backend or a :mod:`fake_provider <qiskit_ibm_runtime.fake_provider>` backend. This allows also the utilization
of other quantum computing backends, as long as they provide a Qiskit :class:`Backend <qiskit.providers.Backend>` class.

Expand Down
2 changes: 1 addition & 1 deletion examples/qnn/classification_example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"metadata": {},
"outputs": [],
"source": [
"executor = Executor(\"pennylane\")"
"executor = Executor(\"qulacs\")"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion examples/qnn/example_minibatch.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"metadata": {},
"outputs": [],
"source": [
"executor = Executor(\"pennylane\")"
"executor = Executor(\"qulacs\")"
]
},
{
Expand Down
4 changes: 2 additions & 2 deletions examples/qrc/qrc_classification.ipynb

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions examples/qrc/qrc_regression.ipynb

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dependencies = [
"scipy>=1.8.0",
"scikit-learn>=1.2.0",
"tqdm>=4.1.0",
"qulacs>=0.6.0",
]
requires-python = ">=3.9,<3.13"
dynamic = ["version", "description"]
Expand Down
6 changes: 3 additions & 3 deletions src/squlearn/kernel/lowlevel_kernel/fidelity_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
from ...util.executor import BaseSamplerV2, Executor
from ...util.data_preprocessing import convert_to_float64, extract_num_features

from .fidelity_kernel_pennylane import FidelityKernelPennyLane
from .fidelity_kernel_expectation_value import FidelityKernelExpectationValue
from .fidelity_kernel_statevector import FidelityKernelStatevector


class FidelityKernel(KernelMatrixBase):
Expand Down Expand Up @@ -347,9 +347,9 @@ def _initialize_kernel(self, num_features: int) -> None:
caching=self._caching,
)
else:
if self._executor.quantum_framework == "pennylane":
if self._executor.quantum_framework in ["pennylane", "qulacs"]:

self._quantum_kernel = FidelityKernelPennyLane(
self._quantum_kernel = FidelityKernelStatevector(
encoding_circuit=self._encoding_circuit,
executor=self._executor,
evaluate_duplicates=self._evaluate_duplicates,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""Fidelity Quantum Kernel class"""

from typing import Union

from functools import lru_cache
import numpy as np

from qiskit.circuit import ParameterVector
from qiskit.compiler import transpile
from qiskit_algorithms.utils import algorithm_globals
Expand All @@ -12,12 +13,14 @@

from ...util.pennylane.pennylane_gates import qiskit_pennylane_gate_dict
from ...util.pennylane.pennylane_circuit import PennyLaneCircuit
from ...util.data_preprocessing import to_tuple, adjust_features

from functools import lru_cache
from ...util.qulacs.qulacs_circuit import QulacsCircuit
from ...util.qulacs.qulacs_execution import qulacs_evaluate_statevector

from ...util.data_preprocessing import to_tuple, adjust_features


class FidelityKernelPennyLane:
class FidelityKernelStatevector:
"""
Fidelity Quantum Kernel implementation based on PennyLane.

Expand Down Expand Up @@ -48,8 +51,11 @@ def __init__(
self._num_features = num_features
self._parameters = None

if self._executor.quantum_framework != "pennylane":
raise RuntimeError("FidelityKernelPennyLane is only supported for PennyLane.")
if self._executor.quantum_framework not in ["pennylane", "qulacs"]:
raise NotImplementedError(
"FidelityKernelStatevector is not supported for this quantum framework:",
self._executor.quantum_framework,
)

if self._executor.is_statevector:

Expand All @@ -63,38 +69,84 @@ def __init__(
self._parameter_vector = None

enc_circ = self._encoding_circuit.get_circuit(x, self._parameter_vector)
circuit = transpile(
enc_circ, basis_gates=qiskit_pennylane_gate_dict.keys(), optimization_level=0
)
self._pennylane_circuit = PennyLaneCircuit(circuit, "state")

@lru_cache(maxsize=self._cache_size)
def pennylane_circuit_executor(*args, **kwargs):
args_numpy = [np.array(arg) for arg in args]
return self._executor.pennylane_execute(
self._pennylane_circuit, *args_numpy, **kwargs
if self._executor.quantum_framework == "pennylane":
circuit = transpile(
enc_circ, basis_gates=qiskit_pennylane_gate_dict.keys(), optimization_level=0
)
self._pennylane_circuit = PennyLaneCircuit(circuit, "state")

@lru_cache(maxsize=self._cache_size)
def pennylane_circuit_executor(*args, **kwargs):
args_numpy = [np.array(arg) for arg in args]
return self._executor.pennylane_execute(
self._pennylane_circuit, *args_numpy, **kwargs
)

self._cached_execution = pennylane_circuit_executor

elif self._executor.quantum_framework == "qulacs":

enc_circ = self._encoding_circuit.get_circuit(x, self._parameter_vector)
self._qulacs_circuit = QulacsCircuit(enc_circ, None)

@lru_cache(maxsize=self._cache_size)
def qulacs_circuit_executor(*args):
args_numpy = [np.array(arg) for arg in args]
if len(args_numpy) == 0:
return self._executor.qulacs_execute(
qulacs_evaluate_statevector, self._qulacs_circuit
)
elif len(args_numpy) == 1:
return self._executor.qulacs_execute(
qulacs_evaluate_statevector, self._qulacs_circuit, x=args_numpy[0]
)
elif len(args_numpy) == 2:
return self._executor.qulacs_execute(
qulacs_evaluate_statevector,
self._qulacs_circuit,
p=args_numpy[0],
x=args_numpy[1],
)

self._cached_execution = qulacs_circuit_executor

self._pennylane_circuit_cached = pennylane_circuit_executor
else:
raise RuntimeError(
"Quantum framework not supported for FidelityKernelStatevector: "
f"{self._executor.quantum_framework}"
)

else:

# Mode 2 for qasm: calculate the |0> probabilities of the quantum circuit U(x)U(x)'
x1 = ParameterVector("x1", self._num_features)
x2 = ParameterVector("x2", self._num_features)
if self.num_parameters > 0:
self._parameter_vector = ParameterVector("p", self.num_parameters)
else:
self._parameter_vector = None
# Mode 2 for shot based: calculate the |0> probabilities
# of the quantum circuit U(x)U(x)'

enc_circ1 = self._encoding_circuit.get_circuit(x1, self._parameter_vector)
enc_circ2 = self._encoding_circuit.get_circuit(x2, self._parameter_vector)
if self._executor.quantum_framework == "pennylane":
x1 = ParameterVector("x1", self._num_features)
x2 = ParameterVector("x2", self._num_features)
if self.num_parameters > 0:
self._parameter_vector = ParameterVector("p", self.num_parameters)
else:
self._parameter_vector = None

circuit = enc_circ1.compose(enc_circ2.inverse())
circuit = transpile(
circuit, basis_gates=qiskit_pennylane_gate_dict.keys(), optimization_level=0
)
self._pennylane_circuit = PennyLaneCircuit(circuit, "probs")
enc_circ1 = self._encoding_circuit.get_circuit(x1, self._parameter_vector)
enc_circ2 = self._encoding_circuit.get_circuit(x2, self._parameter_vector)

circuit = enc_circ1.compose(enc_circ2.inverse())
circuit = transpile(
circuit, basis_gates=qiskit_pennylane_gate_dict.keys(), optimization_level=0
)
self._pennylane_circuit = PennyLaneCircuit(circuit, "probs")
elif self._executor.quantum_framework == "qulacs":
raise NotImplementedError(
"Shot based fidelity kernel is not implemented for Qulacs yet."
)
else:
raise RuntimeError(
"Quantum framework not supported for FidelityKernelStatevector: "
f"{self._executor.quantum_framework}"
)

@property
def num_parameters(self) -> int:
Expand Down Expand Up @@ -133,13 +185,13 @@ def evaluate(self, x: np.ndarray, y: Union[np.ndarray, None] = None) -> np.ndarr
kernel_matrix = np.ones((x.shape[0], y.shape[0]))

if self._executor.is_statevector:
kernel_matrix = self._pennylane_evaluate_kernel_sv(x, y)
kernel_matrix = self.evaluate_kernel_sv(x, y)
else:
kernel_matrix = self._pennylane_evaluate_kernel(x, y)
kernel_matrix = self.evaluate_kernel_shots(x, y)

return kernel_matrix

def _pennylane_evaluate_kernel(self, x, y):
def evaluate_kernel_shots(self, x, y):
"""Function to evaluate the kernel matrix using PennyLane based on fidelity test.

Args:
Expand All @@ -151,6 +203,12 @@ def _pennylane_evaluate_kernel(self, x, y):
np.ndarray: Quantum kernel matrix as 2D numpy array.
"""

if self._executor.quantum_framework != "pennylane":
raise RuntimeError(
"Quantum framework not supported for FidelityKernelStatevector: "
f"{self._executor.quantum_framework}"
)

def not_needed(i: int, j: int, x_i: np.ndarray, y_j: np.ndarray, symmetric: bool) -> bool:
"""Verifies if the kernel entry is trivial (to be set to `1.0`) or not.

Expand Down Expand Up @@ -227,7 +285,7 @@ def not_needed(i: int, j: int, x_i: np.ndarray, y_j: np.ndarray, symmetric: bool

return kernel_matrix

def _pennylane_evaluate_kernel_sv(self, x, y):
def evaluate_kernel_sv(self, x, y):
"""
Function to evaluate the kernel matrix with statevector simulator using PennyLane.

Expand Down Expand Up @@ -261,16 +319,35 @@ def get_kernel_entry(x: np.ndarray, y: np.ndarray) -> float:
y_inp, _ = adjust_features(y, self._num_features)
y_inpT = to_tuple(np.transpose(y_inp), flatten=False)

if self._parameter_vector is not None:
if self._parameters is None:
raise ValueError(
"Parameters have to been set with assign_parameters or as initial parameters!"
if self._executor.quantum_framework == "pennylane":

if self._parameter_vector is not None:
if self._parameters is None:
raise ValueError(
"Parameters have to been set with assign_parameters or as initial parameters!"
)
x_sv = np.array(self._cached_execution(tuple(self._parameters), x_inpT))
y_sv = np.array(self._cached_execution(tuple(self._parameters), y_inpT))
else:
x_sv = np.array(self._cached_execution(x_inpT))
y_sv = np.array(self._cached_execution(y_inpT))

elif self._executor.quantum_framework == "qulacs":

if self._parameter_vector is not None:
if self._parameters is None:
raise ValueError(
"Parameters have to been set with assign_parameters or as initial parameters!"
)
x_sv = np.array(
[self._cached_execution(tuple(self._parameters), tuple(x_)) for x_ in x_inp]
)
x_sv = np.array(self._pennylane_circuit_cached(tuple(self._parameters), x_inpT))
y_sv = np.array(self._pennylane_circuit_cached(tuple(self._parameters), y_inpT))
else:
x_sv = np.array(self._pennylane_circuit_cached(x_inpT))
y_sv = np.array(self._pennylane_circuit_cached(y_inpT))
y_sv = np.array(
[self._cached_execution(tuple(self._parameters), tuple(y_)) for y_ in y_inp]
)
else:
x_sv = np.array([self._cached_execution(tuple(x_)) for x_ in x_inp])
y_sv = np.array([self._cached_execution(tuple(y_)) for y_ in y_inp])

if len(x_sv.shape) == 1:
x_sv = np.array([x_sv])
Expand Down
Loading