Skip to content

Commit b16d0b4

Browse files
Deprecate BlueprintCircuit based circuit usage (#945)
* Deprecate BlueprintCircuit based circuit usage * Add greek letter pi (π) * Update qiskit_machine_learning/circuit/library/raw_feature_vector.py Co-authored-by: Edoardo Altamura <38359901+edoaltamura@users.noreply.github.com> * Update qiskit_machine_learning/kernels/base_kernel.py Co-authored-by: Edoardo Altamura <38359901+edoaltamura@users.noreply.github.com> --------- Co-authored-by: Edoardo Altamura <38359901+edoaltamura@users.noreply.github.com>
1 parent 40fc9ab commit b16d0b4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+642
-203
lines changed

.pylintdict

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,3 +632,4 @@ zz
632632
φ_i
633633
φ_ij
634634
Δ
635+
π

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ Learning module. Let's try an experiment using VQC (Variational Quantum Classifi
117117
train and test samples from a data set to see how accurately the test set can be classified.
118118

119119
```python
120-
from qiskit.circuit.library import TwoLocal, ZZFeatureMap
120+
from qiskit.circuit.library import n_local, zz_feature_map
121121
from qiskit_machine_learning.optimizers import COBYLA
122122
from qiskit_machine_learning.utils import algorithm_globals
123123

@@ -138,8 +138,8 @@ training_features, training_labels, test_features, test_labels = ad_hoc_data(
138138
training_size=training_size, test_size=test_size, n=feature_dim, gap=0.3
139139
)
140140

141-
feature_map = ZZFeatureMap(feature_dimension=feature_dim, reps=2, entanglement="linear")
142-
ansatz = TwoLocal(feature_map.num_qubits, ["ry", "rz"], "cz", reps=3)
141+
feature_map = zz_feature_map(feature_dimension=feature_dim, reps=2, entanglement="linear")
142+
ansatz = n_local(feature_map.num_qubits, ["ry", "rz"], "cz", reps=3)
143143
vqc = VQC(
144144
feature_map=feature_map,
145145
ansatz=ansatz,

qiskit_machine_learning/algorithms/classifiers/vqc.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This code is part of a Qiskit project.
22
#
3-
# (C) Copyright IBM 2021, 2024.
3+
# (C) Copyright IBM 2021, 2025.
44
#
55
# This code is licensed under the Apache License, Version 2.0. You may
66
# obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -69,14 +69,14 @@ def __init__(
6969
If ``None`` is given, the number of qubits is derived from the
7070
feature map or ansatz. If neither of those is given, raises an exception.
7171
The number of qubits in the feature map and ansatz are adjusted to this
72-
number if required.
72+
number if required and possible (such adjustment is deprecated).
7373
feature_map: The (parametrized) circuit to be used as a feature map for the underlying
74-
QNN. If ``None`` is given, the :class:`~qiskit.circuit.library.ZZFeatureMap`
74+
QNN. If ``None`` is given, the :meth:`~qiskit.circuit.library.zz_feature_map`
7575
is used if the number of qubits is larger than 1. For a single qubit
76-
classification problem the :class:`~qiskit.circuit.library.ZFeatureMap`
76+
classification problem the :meth:`~qiskit.circuit.library.z_feature_map`
7777
is used by default.
7878
ansatz: The (parametrized) circuit to be used as an ansatz for the underlying QNN.
79-
If ``None`` is given then the :class:`~qiskit.circuit.library.RealAmplitudes`
79+
If ``None`` is given then the :meth:`~qiskit.circuit.library.real_amplitudes`
8080
circuit is used.
8181
loss: A target loss function to be used in training. Default value is ``cross_entropy``.
8282
optimizer: An instance of an optimizer or a callable to be used in training.
@@ -108,7 +108,7 @@ def __init__(
108108
"""
109109

110110
num_qubits, feature_map, ansatz = derive_num_qubits_feature_map_ansatz(
111-
num_qubits, feature_map, ansatz
111+
num_qubits, feature_map, ansatz, use_methods=True
112112
)
113113

114114
if output_shape is None:

qiskit_machine_learning/algorithms/inference/qbayesian.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This code is part of a Qiskit project.
22
#
3-
# (C) Copyright IBM 2023, 2024.
3+
# (C) Copyright IBM 2023, 2025.
44
#
55
# This code is licensed under the Apache License, Version 2.0. You may
66
# obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -19,7 +19,7 @@
1919
from qiskit import QuantumCircuit, ClassicalRegister
2020
from qiskit.quantum_info import Statevector
2121
from qiskit.circuit import Qubit
22-
from qiskit.circuit.library import GroverOperator
22+
from qiskit.circuit.library import grover_operator
2323
from qiskit.primitives import BaseSampler, Sampler, BaseSamplerV2, BaseSamplerV1
2424
from qiskit.transpiler.passmanager import BasePassManager
2525
from qiskit.result import QuasiDistribution
@@ -129,7 +129,7 @@ def __init__(
129129
# True if rejection sampling converged after limit
130130
self._converged = bool()
131131

132-
def _get_grover_op(self, evidence: Dict[str, int]) -> GroverOperator:
132+
def _get_grover_op(self, evidence: Dict[str, int]) -> QuantumCircuit:
133133
"""
134134
Constructs a Grover operator based on the provided evidence. The evidence is used to
135135
determine the "good states" that the Grover operator will amplify.
@@ -161,7 +161,7 @@ def _get_grover_op(self, evidence: Dict[str, int]) -> GroverOperator:
161161
oracle = Statevector(
162162
[int(format(i, f"0{num_qubits}b") in good_states) for i in range(2**num_qubits)]
163163
)
164-
return GroverOperator(oracle, state_preparation=self._circ)
164+
return grover_operator(oracle, state_preparation=self._circ)
165165

166166
def _run_circuit(self, circuit: QuantumCircuit) -> Dict[str, float]:
167167
"""Run the quantum circuit with the sampler."""
@@ -199,7 +199,7 @@ def _run_circuit(self, circuit: QuantumCircuit) -> Dict[str, float]:
199199
return counts
200200

201201
def __power_grover(
202-
self, grover_op: GroverOperator, evidence: Dict[str, int], k: int
202+
self, grover_op: QuantumCircuit, evidence: Dict[str, int], k: int
203203
) -> Tuple[QuantumCircuit, Set[Tuple[Qubit, int]]]:
204204
"""
205205
Applies the Grover operator to the quantum circuit 2^k times, measures the evidence qubits,

qiskit_machine_learning/algorithms/regressors/vqr.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This code is part of a Qiskit project.
22
#
3-
# (C) Copyright IBM 2021, 2024.
3+
# (C) Copyright IBM 2021, 2025.
44
#
55
# This code is licensed under the Apache License, Version 2.0. You may
66
# obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -52,13 +52,13 @@ def __init__(
5252
If ``None`` then the number of qubits is derived from the
5353
feature map or ansatz, but if neither of these are given an error is raised.
5454
The number of qubits in the feature map and ansatz are adjusted to this
55-
number if required.
55+
number if required and possible (such adjustment is deprecated).
5656
feature_map: The (parametrized) circuit to be used as a feature map for the underlying
57-
QNN. If ``None`` the :class:`~qiskit.circuit.library.ZZFeatureMap`
57+
QNN. If ``None`` the :meth:`~qiskit.circuit.library.zz_feature_map`
5858
is used if the number of qubits is larger than 1. For a single qubit regression
59-
problem the :class:`~qiskit.circuit.library.ZFeatureMap` is used by default.
59+
problem the :meth:`~qiskit.circuit.library.z_feature_map` is used by default.
6060
ansatz: The (parametrized) circuit to be used as an ansatz for the underlying
61-
QNN. If ``None`` then the :class:`~qiskit.circuit.library.RealAmplitudes`
61+
QNN. If ``None`` then the :meth:`~qiskit.circuit.library.real_amplitudes`
6262
circuit is used.
6363
observable: The observable to be measured in the underlying QNN. If ``None``,
6464
use the default :math:`Z^{\otimes num\_qubits}` observable.
@@ -94,7 +94,7 @@ def __init__(
9494
self._estimator = estimator
9595

9696
num_qubits, feature_map, ansatz = derive_num_qubits_feature_map_ansatz(
97-
num_qubits, feature_map, ansatz
97+
num_qubits, feature_map, ansatz, use_methods=True
9898
)
9999

100100
# construct circuit

qiskit_machine_learning/circuit/library/__init__.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This code is part of a Qiskit project.
22
#
3-
# (C) Copyright IBM 2020, 2024.
3+
# (C) Copyright IBM 2020, 2025.
44
#
55
# This code is licensed under the Apache License, Version 2.0. You may
66
# obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -28,6 +28,7 @@
2828
:template: autosummary/class_no_inherited_members.rst
2929
3030
RawFeatureVector
31+
raw_feature_vector
3132
3233
Helper circuits
3334
---------------
@@ -38,12 +39,10 @@
3839
:template: autosummary/class_no_inherited_members.rst
3940
4041
QNNCircuit
42+
qnn_circuit
4143
"""
4244

43-
from .raw_feature_vector import RawFeatureVector
44-
from .qnn_circuit import QNNCircuit
45+
from .raw_feature_vector import RawFeatureVector, raw_feature_vector
46+
from .qnn_circuit import QNNCircuit, qnn_circuit
4547

46-
__all__ = [
47-
"RawFeatureVector",
48-
"QNNCircuit",
49-
]
48+
__all__ = ["RawFeatureVector", "raw_feature_vector", "QNNCircuit", "qnn_circuit"]

qiskit_machine_learning/circuit/library/qnn_circuit.py

Lines changed: 116 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This code is part of a Qiskit project.
22
#
3-
# (C) Copyright IBM 2023, 2024.
3+
# (C) Copyright IBM 2023, 2025.
44
#
55
# This code is licensed under the Apache License, Version 2.0. You may
66
# obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -21,11 +21,115 @@
2121
from qiskit_machine_learning import QiskitMachineLearningError
2222

2323
from ...utils import derive_num_qubits_feature_map_ansatz
24+
from ...utils.deprecation import issue_deprecation_msg
25+
26+
27+
def qnn_circuit(
28+
num_qubits: int | None = None,
29+
feature_map: QuantumCircuit | None = None,
30+
ansatz: QuantumCircuit | None = None,
31+
):
32+
"""
33+
The qnn_circuit creates a QuantumCircuit that is a composition of a feature map
34+
and an ansatz circuit. Also returned are the feature map and ansatz parameters for
35+
use for inputs and weights as needed for a neural network, such as
36+
:class:`~qiskit-machine-learning.neural_networks.SamplerQNN`.
37+
38+
If only the number of qubits is provided the :meth:`~qiskit.circuit.library.real_amplitudes`
39+
ansatz and the :meth:`~qiskit.circuit.library.zz_feature_map` feature map are used. If the
40+
number of qubits is 1 the :meth:`~qiskit.circuit.library.z_feature_map` is used. If only a
41+
feature map is provided, the :meth:`~qiskit.circuit.library.real_amplitudes` ansatz with the
42+
corresponding number of qubits is used. If only an ansatz is provided the
43+
:meth:`~qiskit.circuit.library.zz_feature_map` with the corresponding number of qubits is used.
44+
45+
At least one parameter has to be provided. If a feature map and an ansatz is provided, the
46+
number of qubits must be the same.
47+
48+
Example:
49+
50+
.. code-block:: python
51+
52+
from qiskit_machine_learning.circuit.library import qnn_circuit
53+
qnn_qc, fm_params, anz_params = qnn_circuit(2)
54+
qnn_qc.draw(fold=60)
55+
# ┌───┐┌─────────────┐ »
56+
# q_0: ┤ H ├┤ P(2.0*x[0]) ├──■──»
57+
# ├───┤├─────────────┤┌─┴─┐»
58+
# q_1: ┤ H ├┤ P(2.0*x[1]) ├┤ X ├»
59+
# └───┘└─────────────┘└───┘»
60+
# « ┌───┐»
61+
# «q_0: ──────────────────────────────────■──┤ H ├»
62+
# « ┌──────────────────────────────┐┌─┴─┐├───┤»
63+
# «q_1: ┤ P(2.0*(x[0] - π)*(x[1] - π)) ├┤ X ├┤ H ├»
64+
# « └──────────────────────────────┘└───┘└───┘»
65+
# « ┌─────────────┐ »
66+
# «q_0: ┤ P(2.0*x[0]) ├──■──────────────────────────────────»
67+
# « ├─────────────┤┌─┴─┐┌──────────────────────────────┐»
68+
# «q_1: ┤ P(2.0*x[1]) ├┤ X ├┤ P(2.0*(x[0] - π)*(x[1] - π)) ├»
69+
# « └─────────────┘└───┘└──────────────────────────────┘»
70+
# « ┌──────────┐ ┌──────────┐ ┌──────────┐»
71+
# «q_0: ──■──┤ Ry(θ[0]) ├──■──┤ Ry(θ[2]) ├──■──┤ Ry(θ[4]) ├»
72+
# « ┌─┴─┐├──────────┤┌─┴─┐├──────────┤┌─┴─┐├──────────┤»
73+
# «q_1: ┤ X ├┤ Ry(θ[1]) ├┤ X ├┤ Ry(θ[3]) ├┤ X ├┤ Ry(θ[5]) ├»
74+
# « └───┘└──────────┘└───┘└──────────┘└───┘└──────────┘»
75+
# « ┌──────────┐
76+
# «q_0: ──■──┤ Ry(θ[6]) ├
77+
# « ┌─┴─┐├──────────┤
78+
# «q_1: ┤ X ├┤ Ry(θ[7]) ├
79+
# « └───┘└──────────┘
80+
print(fm_params)
81+
# ParameterView([ParameterVectorElement(x[0]), ParameterVectorElement(x[1])])
82+
print(anz_params)
83+
# ParameterView([ParameterVectorElement(θ[0]), ParameterVectorElement(θ[1]),
84+
# ParameterVectorElement(θ[2]), ParameterVectorElement(θ[3]),
85+
# ParameterVectorElement(θ[4]), ParameterVectorElement(θ[5]),
86+
# ParameterVectorElement(θ[6]), ParameterVectorElement(θ[7])])
87+
88+
Although all arguments to qnn_circuit default to None at least one must be provided,
89+
to determine the number of qubits from.
90+
91+
If more than one is passed:
92+
93+
1) If num_qubits is provided the feature map and/or ansatz circuits supplied must be the
94+
same number of qubits.
95+
96+
2) If num_qubits is not provided the feature_map and ansatz must be set to the same number
97+
of qubits.
98+
99+
Args:
100+
num_qubits: Number of qubits, a positive integer. Optional if feature_map or ansatz is
101+
provided, otherwise required. If not provided num_qubits defaults from the
102+
sizes of feature_map and/or ansatz.
103+
feature_map: A feature map. Optional if num_qubits or ansatz is provided, otherwise
104+
required. If not provided defaults to
105+
:meth:`~qiskit.circuit.library.zz_feature_map` or
106+
:meth:`~qiskit.circuit.library.z_feature_map` if num_qubits is determined
107+
to be 1.
108+
ansatz: An ansatz. Optional if num_qubits or feature_map is provided, otherwise
109+
required. If not provided defaults to
110+
:meth:`~qiskit.circuit.library.real_amplitudes`.
111+
112+
Returns:
113+
The composed feature map and ansatz circuit, the feature map parameters and the
114+
ansatz parameters.
115+
116+
Raises:
117+
QiskitMachineLearningError: If a valid number of qubits cannot be derived from the \
118+
provided input arguments.
119+
"""
120+
# Check if circuit is constructed with valid configuration and set properties accordingly.
121+
num_qubits, feature_map, ansatz = derive_num_qubits_feature_map_ansatz(
122+
num_qubits, feature_map, ansatz, use_methods=True
123+
)
124+
qc = QuantumCircuit(num_qubits)
125+
qc.compose(feature_map, inplace=True)
126+
qc.compose(ansatz, inplace=True)
127+
return qc, feature_map.parameters, ansatz.parameters
24128

25129

26130
class QNNCircuit(BlueprintCircuit):
27131
"""
28-
The QNN circuit is a blueprint circuit that wraps feature map and ansatz circuits.
132+
(DEPRECATED) The QNN circuit is a blueprint circuit that wraps feature map and ansatz circuits.
29133
It can be used to simplify the composition of these two.
30134
31135
If only the number of qubits is provided the :class:`~qiskit.circuit.library.RealAmplitudes`
@@ -119,6 +223,16 @@ def __init__(
119223
"""
120224

121225
super().__init__()
226+
227+
issue_deprecation_msg(
228+
msg="QNNCircuit, a BlueprintCircuit based class, is deprecated",
229+
version="0.9.0",
230+
remedy="Use qnn_circuit instead of QNNCircuit but note "
231+
"that later adjustment of the number of qubits, or updating "
232+
"the feature map and/or ansatz is not possible anymore.",
233+
period="4 months",
234+
)
235+
122236
self._feature_map = feature_map
123237
self._ansatz = ansatz
124238
# Check if circuit is constructed with valid configuration and set properties accordingly.

0 commit comments

Comments
 (0)