Skip to content

Deprecate BlueprintCircuit based circuit usage #945

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

Merged
merged 4 commits into from
Jun 6, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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 .pylintdict
Original file line number Diff line number Diff line change
Expand Up @@ -632,3 +632,4 @@ zz
φ_i
φ_ij
Δ
π
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ Learning module. Let's try an experiment using VQC (Variational Quantum Classifi
train and test samples from a data set to see how accurately the test set can be classified.

```python
from qiskit.circuit.library import TwoLocal, ZZFeatureMap
from qiskit.circuit.library import n_local, zz_feature_map
from qiskit_machine_learning.optimizers import COBYLA
from qiskit_machine_learning.utils import algorithm_globals

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

feature_map = ZZFeatureMap(feature_dimension=feature_dim, reps=2, entanglement="linear")
ansatz = TwoLocal(feature_map.num_qubits, ["ry", "rz"], "cz", reps=3)
feature_map = zz_feature_map(feature_dimension=feature_dim, reps=2, entanglement="linear")
ansatz = n_local(feature_map.num_qubits, ["ry", "rz"], "cz", reps=3)
vqc = VQC(
feature_map=feature_map,
ansatz=ansatz,
Expand Down
12 changes: 6 additions & 6 deletions qiskit_machine_learning/algorithms/classifiers/vqc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of a Qiskit project.
#
# (C) Copyright IBM 2021, 2024.
# (C) Copyright IBM 2021, 2025.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
Expand Down Expand Up @@ -69,14 +69,14 @@ def __init__(
If ``None`` is given, the number of qubits is derived from the
feature map or ansatz. If neither of those is given, raises an exception.
The number of qubits in the feature map and ansatz are adjusted to this
number if required.
number if required and possible (such adjustment is deprecated).
feature_map: The (parametrized) circuit to be used as a feature map for the underlying
QNN. If ``None`` is given, the :class:`~qiskit.circuit.library.ZZFeatureMap`
QNN. If ``None`` is given, the :meth:`~qiskit.circuit.library.zz_feature_map`
is used if the number of qubits is larger than 1. For a single qubit
classification problem the :class:`~qiskit.circuit.library.ZFeatureMap`
classification problem the :meth:`~qiskit.circuit.library.z_feature_map`
is used by default.
ansatz: The (parametrized) circuit to be used as an ansatz for the underlying QNN.
If ``None`` is given then the :class:`~qiskit.circuit.library.RealAmplitudes`
If ``None`` is given then the :meth:`~qiskit.circuit.library.real_amplitudes`
circuit is used.
loss: A target loss function to be used in training. Default value is ``cross_entropy``.
optimizer: An instance of an optimizer or a callable to be used in training.
Expand Down Expand Up @@ -108,7 +108,7 @@ def __init__(
"""

num_qubits, feature_map, ansatz = derive_num_qubits_feature_map_ansatz(
num_qubits, feature_map, ansatz
num_qubits, feature_map, ansatz, use_methods=True
)

if output_shape is None:
Expand Down
10 changes: 5 additions & 5 deletions qiskit_machine_learning/algorithms/inference/qbayesian.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of a Qiskit project.
#
# (C) Copyright IBM 2023, 2024.
# (C) Copyright IBM 2023, 2025.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
Expand All @@ -19,7 +19,7 @@
from qiskit import QuantumCircuit, ClassicalRegister
from qiskit.quantum_info import Statevector
from qiskit.circuit import Qubit
from qiskit.circuit.library import GroverOperator
from qiskit.circuit.library import grover_operator
from qiskit.primitives import BaseSampler, Sampler, BaseSamplerV2, BaseSamplerV1
from qiskit.transpiler.passmanager import BasePassManager
from qiskit.result import QuasiDistribution
Expand Down Expand Up @@ -129,7 +129,7 @@ def __init__(
# True if rejection sampling converged after limit
self._converged = bool()

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

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

def __power_grover(
self, grover_op: GroverOperator, evidence: Dict[str, int], k: int
self, grover_op: QuantumCircuit, evidence: Dict[str, int], k: int
) -> Tuple[QuantumCircuit, Set[Tuple[Qubit, int]]]:
"""
Applies the Grover operator to the quantum circuit 2^k times, measures the evidence qubits,
Expand Down
12 changes: 6 additions & 6 deletions qiskit_machine_learning/algorithms/regressors/vqr.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of a Qiskit project.
#
# (C) Copyright IBM 2021, 2024.
# (C) Copyright IBM 2021, 2025.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
Expand Down Expand Up @@ -52,13 +52,13 @@ def __init__(
If ``None`` then the number of qubits is derived from the
feature map or ansatz, but if neither of these are given an error is raised.
The number of qubits in the feature map and ansatz are adjusted to this
number if required.
number if required and possible (such adjustment is deprecated).
feature_map: The (parametrized) circuit to be used as a feature map for the underlying
QNN. If ``None`` the :class:`~qiskit.circuit.library.ZZFeatureMap`
QNN. If ``None`` the :meth:`~qiskit.circuit.library.zz_feature_map`
is used if the number of qubits is larger than 1. For a single qubit regression
problem the :class:`~qiskit.circuit.library.ZFeatureMap` is used by default.
problem the :meth:`~qiskit.circuit.library.z_feature_map` is used by default.
ansatz: The (parametrized) circuit to be used as an ansatz for the underlying
QNN. If ``None`` then the :class:`~qiskit.circuit.library.RealAmplitudes`
QNN. If ``None`` then the :meth:`~qiskit.circuit.library.real_amplitudes`
circuit is used.
observable: The observable to be measured in the underlying QNN. If ``None``,
use the default :math:`Z^{\otimes num\_qubits}` observable.
Expand Down Expand Up @@ -94,7 +94,7 @@ def __init__(
self._estimator = estimator

num_qubits, feature_map, ansatz = derive_num_qubits_feature_map_ansatz(
num_qubits, feature_map, ansatz
num_qubits, feature_map, ansatz, use_methods=True
)

# construct circuit
Expand Down
13 changes: 6 additions & 7 deletions qiskit_machine_learning/circuit/library/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of a Qiskit project.
#
# (C) Copyright IBM 2020, 2024.
# (C) Copyright IBM 2020, 2025.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
Expand Down Expand Up @@ -28,6 +28,7 @@
:template: autosummary/class_no_inherited_members.rst

RawFeatureVector
raw_feature_vector

Helper circuits
---------------
Expand All @@ -38,12 +39,10 @@
:template: autosummary/class_no_inherited_members.rst

QNNCircuit
qnn_circuit
"""

from .raw_feature_vector import RawFeatureVector
from .qnn_circuit import QNNCircuit
from .raw_feature_vector import RawFeatureVector, raw_feature_vector
from .qnn_circuit import QNNCircuit, qnn_circuit

__all__ = [
"RawFeatureVector",
"QNNCircuit",
]
__all__ = ["RawFeatureVector", "raw_feature_vector", "QNNCircuit", "qnn_circuit"]
118 changes: 116 additions & 2 deletions qiskit_machine_learning/circuit/library/qnn_circuit.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of a Qiskit project.
#
# (C) Copyright IBM 2023, 2024.
# (C) Copyright IBM 2023, 2025.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
Expand All @@ -21,11 +21,115 @@
from qiskit_machine_learning import QiskitMachineLearningError

from ...utils import derive_num_qubits_feature_map_ansatz
from ...utils.deprecation import issue_deprecation_msg


def qnn_circuit(
num_qubits: int | None = None,
feature_map: QuantumCircuit | None = None,
ansatz: QuantumCircuit | None = None,
):
"""
The qnn_circuit creates a QuantumCircuit that is a composition of a feature map
and an ansatz circuit. Also returned are the feature map and ansatz parameters for
use for inputs and weights as needed for a neural network, such as
:class:`~qiskit-machine-learning.neural_networks.SamplerQNN`.

If only the number of qubits is provided the :meth:`~qiskit.circuit.library.real_amplitudes`
ansatz and the :meth:`~qiskit.circuit.library.zz_feature_map` feature map are used. If the
number of qubits is 1 the :meth:`~qiskit.circuit.library.z_feature_map` is used. If only a
feature map is provided, the :meth:`~qiskit.circuit.library.real_amplitudes` ansatz with the
corresponding number of qubits is used. If only an ansatz is provided the
:meth:`~qiskit.circuit.library.zz_feature_map` with the corresponding number of qubits is used.

At least one parameter has to be provided. If a feature map and an ansatz is provided, the
number of qubits must be the same.

Example:

.. code-block:: python

from qiskit_machine_learning.circuit.library import qnn_circuit
qnn_qc, fm_params, anz_params = qnn_circuit(2)
qnn_qc.draw(fold=60)
# ┌───┐┌─────────────┐ »
# q_0: ┤ H ├┤ P(2.0*x[0]) ├──■──»
# ├───┤├─────────────┤┌─┴─┐»
# q_1: ┤ H ├┤ P(2.0*x[1]) ├┤ X ├»
# └───┘└─────────────┘└───┘»
# « ┌───┐»
# «q_0: ──────────────────────────────────■──┤ H ├»
# « ┌──────────────────────────────┐┌─┴─┐├───┤»
# «q_1: ┤ P(2.0*(x[0] - π)*(x[1] - π)) ├┤ X ├┤ H ├»
# « └──────────────────────────────┘└───┘└───┘»
# « ┌─────────────┐ »
# «q_0: ┤ P(2.0*x[0]) ├──■──────────────────────────────────»
# « ├─────────────┤┌─┴─┐┌──────────────────────────────┐»
# «q_1: ┤ P(2.0*x[1]) ├┤ X ├┤ P(2.0*(x[0] - π)*(x[1] - π)) ├»
# « └─────────────┘└───┘└──────────────────────────────┘»
# « ┌──────────┐ ┌──────────┐ ┌──────────┐»
# «q_0: ──■──┤ Ry(θ[0]) ├──■──┤ Ry(θ[2]) ├──■──┤ Ry(θ[4]) ├»
# « ┌─┴─┐├──────────┤┌─┴─┐├──────────┤┌─┴─┐├──────────┤»
# «q_1: ┤ X ├┤ Ry(θ[1]) ├┤ X ├┤ Ry(θ[3]) ├┤ X ├┤ Ry(θ[5]) ├»
# « └───┘└──────────┘└───┘└──────────┘└───┘└──────────┘»
# « ┌──────────┐
# «q_0: ──■──┤ Ry(θ[6]) ├
# « ┌─┴─┐├──────────┤
# «q_1: ┤ X ├┤ Ry(θ[7]) ├
# « └───┘└──────────┘
print(fm_params)
# ParameterView([ParameterVectorElement(x[0]), ParameterVectorElement(x[1])])
print(anz_params)
# ParameterView([ParameterVectorElement(θ[0]), ParameterVectorElement(θ[1]),
# ParameterVectorElement(θ[2]), ParameterVectorElement(θ[3]),
# ParameterVectorElement(θ[4]), ParameterVectorElement(θ[5]),
# ParameterVectorElement(θ[6]), ParameterVectorElement(θ[7])])

Although all arguments to qnn_circuit default to None at least one must be provided,
to determine the number of qubits from.

If more than one is passed:

1) If num_qubits is provided the feature map and/or ansatz circuits supplied must be the
same number of qubits.

2) If num_qubits is not provided the feature_map and ansatz must be set to the same number
of qubits.

Args:
num_qubits: Number of qubits, a positive integer. Optional if feature_map or ansatz is
provided, otherwise required. If not provided num_qubits defaults from the
sizes of feature_map and/or ansatz.
feature_map: A feature map. Optional if num_qubits or ansatz is provided, otherwise
required. If not provided defaults to
:meth:`~qiskit.circuit.library.zz_feature_map` or
:meth:`~qiskit.circuit.library.z_feature_map` if num_qubits is determined
to be 1.
ansatz: An ansatz. Optional if num_qubits or feature_map is provided, otherwise
required. If not provided defaults to
:meth:`~qiskit.circuit.library.real_amplitudes`.

Returns:
The composed feature map and ansatz circuit, the feature map parameters and the
ansatz parameters.

Raises:
QiskitMachineLearningError: If a valid number of qubits cannot be derived from the \
provided input arguments.
"""
# Check if circuit is constructed with valid configuration and set properties accordingly.
num_qubits, feature_map, ansatz = derive_num_qubits_feature_map_ansatz(
num_qubits, feature_map, ansatz, use_methods=True
)
qc = QuantumCircuit(num_qubits)
qc.compose(feature_map, inplace=True)
qc.compose(ansatz, inplace=True)
return qc, feature_map.parameters, ansatz.parameters


class QNNCircuit(BlueprintCircuit):
"""
The QNN circuit is a blueprint circuit that wraps feature map and ansatz circuits.
(DEPRECATED) The QNN circuit is a blueprint circuit that wraps feature map and ansatz circuits.
It can be used to simplify the composition of these two.

If only the number of qubits is provided the :class:`~qiskit.circuit.library.RealAmplitudes`
Expand Down Expand Up @@ -119,6 +223,16 @@ def __init__(
"""

super().__init__()

issue_deprecation_msg(
msg="QNNCircuit, a BlueprintCircuit based class, is deprecated",
version="0.9.0",
remedy="Use qnn_circuit instead of QNNCircuit but note "
"that later adjustment of the number of qubits, or updating "
"the feature map and/or ansatz is not possible anymore.",
period="4 months",
)

self._feature_map = feature_map
self._ansatz = ansatz
# Check if circuit is constructed with valid configuration and set properties accordingly.
Expand Down
Loading