From a36c9ba94e7b582eb196049165432ce9ba828c00 Mon Sep 17 00:00:00 2001 From: Eric Arellano <14852634+Eric-Arellano@users.noreply.github.com> Date: Wed, 8 Nov 2023 14:10:06 -0500 Subject: [PATCH] Finish converting migration guides to MDX (#304) Closes https://github.com/Qiskit/documentation/issues/213. These changes are all manual. I looked over the rendered site and MDX files to see issues like `[My link]` without the corresponding `()` for the link itself. But for tables, I used the script from https://github.com/Qiskit/documentation/pull/296. --- docs/api/migration-guides/_toc.json | 17 + docs/api/migration-guides/index.mdx | 4 + .../qiskit-algorithms-module.mdx | 42 +- .../migration-guides/qiskit-opflow-module.mdx | 99 +-- .../qiskit-quantum-instance.mdx | 792 +++++++++--------- 5 files changed, 465 insertions(+), 489 deletions(-) diff --git a/docs/api/migration-guides/_toc.json b/docs/api/migration-guides/_toc.json index 402058a2c19..905c3d74fcb 100644 --- a/docs/api/migration-guides/_toc.json +++ b/docs/api/migration-guides/_toc.json @@ -17,6 +17,23 @@ "url": "/api/migration-guides/qiskit-runtime-examples" } ] + }, + { + "title": "Qiskit 0.44 changes", + "children": [ + { + "title": "QuantumInstance deprecation", + "url": "/api/migration-guides/qiskit-quantum-instance" + }, + { + "title": "qiskit.algorithms new interface", + "url": "/api/migration-guides/qiskit-algorithms-module" + }, + { + "title": "qiskit.opflow deprecation", + "url": "/api/migration-guides/qiskit-opflow-module" + } + ] } ] } diff --git a/docs/api/migration-guides/index.mdx b/docs/api/migration-guides/index.mdx index f3472bb6cbb..de5356ec958 100644 --- a/docs/api/migration-guides/index.mdx +++ b/docs/api/migration-guides/index.mdx @@ -11,3 +11,7 @@ We've prepared various migration guides to help you more effectively use Qiskit * Migrate to Qiskit Runtime * [How to migrate](./qiskit-runtime) * [Examples](./qiskit-runtime-examples) +* Qiskit 0.44 changes + * [`qiskit.algorithms` new interface](./qiskit-algorithms-module) + * [`qiskit.opflow` deprecation](./qiskit-opflow-module) + * [`QuantumIntance` deprecation](./qiskit-quantum-instance) diff --git a/docs/api/migration-guides/qiskit-algorithms-module.mdx b/docs/api/migration-guides/qiskit-algorithms-module.mdx index a1ef36810b4..2af39c926fb 100644 --- a/docs/api/migration-guides/qiskit-algorithms-module.mdx +++ b/docs/api/migration-guides/qiskit-algorithms-module.mdx @@ -5,6 +5,22 @@ description: Use the new interface for `qiskit.algorithms` # Algorithms Migration Guide + + The `qiskit.algorithms` module has been superseded by a new standalone library, `qiskit_algorithms`, + available on [GitHub](https://github.com/qiskit-community/qiskit-algorithms) and + [PyPi](https://pypi.org/project/qiskit-algorithms). + + As of Qiskit's 0.44 release, active development of algorithm features has migrated to this new package. + If your code used `qiskit.algorithms`, you should: + 1. Check that you are not using any legacy algorithm implementation. If you are, follow this guide to + migrate to the primitives-based implementation. + 2. Once your code is updated, run `pip install qiskit-algorithms` and update your imports from + `qiskit.algorithms` to `qiskit_algorithms`. + + The decision to migrate the `qiskit.algorithms` module to a + separate package was made to clarify the purpose of Qiskit and make a distinction between the tools and libraries built on top of it. + + ## TL;DR The [`qiskit.algorithms`](../qiskit/algorithms) module has been fully refactored to use the [`qiskit.primitives`](../qiskit/primitives), for circuit execution, instead of the [`qiskit.utils.QuantumInstance`](../qiskit/qiskit.utils.QuantumInstance), which is now deprecated. @@ -21,16 +37,16 @@ There have been **3 types of refactoring**: > **specify the full import path** (e.g., `from qiskit.algorithms.eigensolvers import VQD`) > > - > - [Minimum Eigensolvers] - > - [Eigensolvers] - > - [Time Evolvers] + > - [Minimum Eigensolvers](#minimum-eigensolvers) + > - [Eigensolvers](#eigensolvers) + > - [Time Evolvers](#time-evolvers) 2. Algorithms refactored in-place (same namespace) to support both [`qiskit.utils.QuantumInstance`](../qiskit/qiskit.utils.QuantumInstance) and [`qiskit.primitives`](../qiskit/primitives). In the future, the use of [`qiskit.utils.QuantumInstance`](../qiskit/qiskit.utils.QuantumInstance) will be removed. - > - [Amplitude Amplifiers] - > - [Amplitude Estimators] - > - [Phase Estimators] + > - [Amplitude Amplifiers](#amplitude-amplifiers) + > - [Amplitude Estimators](#amplitude-estimators) + > - [Phase Estimators](#phase-estimators) 3. Algorithms that were deprecated and are now removed entirely from [`qiskit.algorithms`](../qiskit/algorithms). These are algorithms that do not currently serve as building blocks for applications. Their main value is educational. You can consult the below tutorials: @@ -93,13 +109,13 @@ In this guide, we will cover 3 different common configurations for algorithms th 1. Running an algorithm with a statevector simulator (i.e., using [`qiskit.opflow`](../qiskit/opflow)'s legacy [`qiskit.opflow.expectations.MatrixExpectation`](../qiskit/qiskit.opflow.expectations.MatrixExpectation)), when you want the ideal outcome without shot noise: - > - Reference Primitives with default configuration (see [QAOA] example): + > - Reference Primitives with default configuration (see [QAOA](https://github.com/Qiskit/qiskit-tutorials/blob/master/tutorials/algorithms/05_qaoa.ipynb) example): > > ```python > from qiskit.primitives import Sampler, Estimator > ``` > - > - Aer Primitives **with statevector simulator** (see [QAOA] example): + > - Aer Primitives **with statevector simulator** (see [QAOA](https://github.com/Qiskit/qiskit-tutorials/blob/master/tutorials/algorithms/05_qaoa.ipynb) example): > > ```python > from qiskit_aer.primitives import Sampler, Estimator @@ -111,7 +127,7 @@ In this guide, we will cover 3 different common configurations for algorithms th 2. Running an algorithm using a simulator/device with shot noise (i.e., using [`qiskit.opflow`](../qiskit/opflow)'s legacy [`qiskit.opflow.expectations.PauliExpectation`](../qiskit/qiskit.opflow.expectations.PauliExpectation)): - > - Reference Primitives **with shots** (see [VQE] examples): + > - Reference Primitives **with shots** (see [VQE](#vqe) examples): > > ```python > from qiskit.primitives import Sampler, Estimator @@ -127,22 +143,22 @@ In this guide, we will cover 3 different common configurations for algorithms th > job = estimator.run(circuits, observables, shots=100) > ``` > - > - Aer Primitives with default configuration (see [VQE] examples): + > - Aer Primitives with default configuration (see [VQE](#vqe) examples): > > ```python > from qiskit_aer.primitives import Sampler, Estimator > ``` > - > - IBM's Qiskit Runtime Primitives with default configuration (see [VQD] example): + > - IBM's Qiskit Runtime Primitives with default configuration (see [VQD](https://github.com/Qiskit/qiskit-tutorials/blob/master/tutorials/algorithms/04_vqd.ipynb) example): > > ```python > from qiskit_ibm_runtime import Sampler, Estimator > ``` -3\. Running an algorithm on an Aer simulator using a custom instruction (i.e., using [`qiskit.opflow`](../qiskit/opflow)'s legacy +3. Running an algorithm on an Aer simulator using a custom instruction (i.e., using [`qiskit.opflow`](../qiskit/opflow)'s legacy [`qiskit.opflow.expectations.AerPauliExpectation`](../qiskit/qiskit.opflow.expectations.AerPauliExpectation)): -> - Aer Primitives with `shots=None`, `approximation=True` (see [TrotterQRTE] example): +> - Aer Primitives with `shots=None`, `approximation=True` (see [TrotterQRTE](#trotterqrte) example): > > ```python > from qiskit_aer.primitives import Sampler, Estimator diff --git a/docs/api/migration-guides/qiskit-opflow-module.mdx b/docs/api/migration-guides/qiskit-opflow-module.mdx index 0d0c1e599f9..d41b0f69e89 100644 --- a/docs/api/migration-guides/qiskit-opflow-module.mdx +++ b/docs/api/migration-guides/qiskit-opflow-module.mdx @@ -357,22 +357,11 @@ to initialize it. Interpreting [`qiskit.opflow.primitive_ops.PrimitiveOp`](../qiskit/qiskit.opflow.primitive_ops.PrimitiveOp) as a factory class: - ```{eval-rst} - .. list-table:: - :header-rows: 1 - - * - Class passed to [`qiskit.opflow.primitive_ops.PrimitiveOp`](../qiskit/qiskit.opflow.primitive_ops.PrimitiveOp) - - Subclass returned - - * - [`qiskit.quantum_info.Pauli`](../qiskit/qiskit.quantum_info.Pauli) - - [`qiskit.opflow.primitive_ops.PauliOp`](../qiskit/qiskit.opflow.primitive_ops.PauliOp) - - * - [`qiskit.circuit.Instruction`](../qiskit/qiskit.circuit.Instruction), [`qiskit.circuit.QuantumCircuit`](../qiskit/qiskit.circuit.QuantumCircuit) - - [`qiskit.opflow.primitive_ops.CircuitOp`](../qiskit/qiskit.opflow.primitive_ops.CircuitOp) - - * - ``list``, ``np.ndarray``, ``scipy.sparse.spmatrix``, [`qiskit.quantum_info.Operator`](../qiskit/qiskit.quantum_info.Operator) - - [`qiskit.opflow.primitive_ops.MatrixOp`](../qiskit/qiskit.opflow.primitive_ops.MatrixOp) - ``` + | Class passed to [`qiskit.opflow.primitive_ops.PrimitiveOp`](../qiskit/qiskit.opflow.primitive_ops.PrimitiveOp) | Subclass returned | + | --- | --- | + | [`qiskit.quantum_info.Pauli`](../qiskit/qiskit.quantum_info.Pauli) | [`qiskit.opflow.primitive_ops.PauliOp`](../qiskit/qiskit.opflow.primitive_ops.PauliOp) | + | [`qiskit.circuit.Instruction`](../qiskit/qiskit.circuit.Instruction), [`qiskit.circuit.QuantumCircuit`](../qiskit/qiskit.circuit.QuantumCircuit) | [`qiskit.opflow.primitive_ops.CircuitOp`](../qiskit/qiskit.opflow.primitive_ops.CircuitOp) | + | `list`, `np.ndarray`, `scipy.sparse.spmatrix`, [`qiskit.quantum_info.Operator`](../qiskit/qiskit.quantum_info.Operator) | [`qiskit.opflow.primitive_ops.MatrixOp`](../qiskit/qiskit.opflow.primitive_ops.MatrixOp) | Thus, when migrating opflow code, it is important to look for alternatives to replace the specific subclasses that @@ -390,7 +379,7 @@ are used "under the hood" in the original code: -#### Example 1: ``PauliSumOp`` +#### Example 1: `PauliSumOp` Opflow: @@ -423,7 +412,7 @@ SparsePauliOp(['XYZY'], -#### Example 2: ``Z2Symmetries`` and ``TaperedPauliSumOp`` +#### Example 2: `Z2Symmetries` and `TaperedPauliSumOp` Opflow: @@ -545,25 +534,12 @@ acts as a factory to create the corresponding subclass depending on the computat Interpreting [`qiskit.opflow.state_fns.StateFn`](../qiskit/qiskit.opflow.state_fns.StateFn) as a factory class: - ```{eval-rst} - .. list-table:: - :header-rows: 1 - - * - Class passed to [`qiskit.opflow.state_fns.StateFn`](../qiskit/qiskit.opflow.state_fns.StateFn) - - Sub-class returned - - * - ``str``, ``dict``, [`qiskit.result.Result`](../qiskit/qiskit.result.Result) - - [`qiskit.opflow.state_fns.DictStateFn`](../qiskit/qiskit.opflow.state_fns.DictStateFn) - - * - ``list``, ``np.ndarray``, [`qiskit.quantum_info.Statevector`](../qiskit/qiskit.quantum_info.Statevector) - - [`qiskit.opflow.state_fns.VectorStateFn`](../qiskit/qiskit.opflow.state_fns.VectorStateFn) - - * - [`qiskit.circuit.QuantumCircuit`](../qiskit/qiskit.circuit.QuantumCircuit), [`qiskit.circuit.Instruction`](../qiskit/qiskit.circuit.Instruction) - - [`qiskit.opflow.state_fns.CircuitStateFn`](../qiskit/qiskit.opflow.state_fns.CircuitStateFn) - - * - [`qiskit.opflow.OperatorBase`](../qiskit/qiskit.opflow.OperatorBase) - - [`qiskit.opflow.state_fns.OperatorStateFn`](../qiskit/qiskit.opflow.state_fns.OperatorStateFn) - ``` +| Class passed to [`qiskit.opflow.state_fns.StateFn`](../qiskit/qiskit.opflow.state_fns.StateFn) | Subclass returned | +| --- | --- | +| `str`, `dict`, [`qiskit.result.Result`](../qiskit/qiskit.result.Result) | [`qiskit.opflow.state_fns.DictStateFn`](../qiskit/qiskit.opflow.state_fns.DictStateFn) | +| `list`, `np.ndarray`, [`qiskit.quantum_info.Statevector`](../qiskit/qiskit.quantum_info.Statevector) | [`qiskit.opflow.state_fns.VectorStateFn`](../qiskit/qiskit.opflow.state_fns.VectorStateFn) | +| [`qiskit.circuit.QuantumCircuit`](../qiskit/qiskit.circuit.QuantumCircuit), [`qiskit.circuit.Instruction`](../qiskit/qiskit.circuit.Instruction) | [`qiskit.opflow.state_fns.CircuitStateFn`](../qiskit/qiskit.opflow.state_fns.CircuitStateFn) | +| [`qiskit.opflow.OperatorBase`](../qiskit/qiskit.opflow.OperatorBase) | [`qiskit.opflow.state_fns.OperatorStateFn`](../qiskit/qiskit.opflow.state_fns.OperatorStateFn) | This means that references to [`qiskit.opflow.state_fns.StateFn`](../qiskit/qiskit.opflow.state_fns.StateFn) in opflow code should be examined to @@ -668,7 +644,7 @@ Notably, this functionality has been replaced by the [`qiskit.primitives`](../qi -### Example 1: ``CircuitSampler`` for sampling parametrized circuits +### Example 1: `CircuitSampler` for sampling parametrized circuits Opflow: @@ -733,7 +709,7 @@ print(sampled) [{0: 1.0}, {0: 1.0}, {0: 1.0}] ``` -### Example 2: ``CircuitSampler`` for computing expectation values +### Example 2: `CircuitSampler` for computing expectation values Opflow: @@ -783,7 +759,7 @@ print(expectation_value) -### Example 3: ``AbelianGrouper`` for grouping operators +### Example 3: `AbelianGrouper` for grouping operators Opflow: @@ -1260,42 +1236,23 @@ qfi = QFI(qgt) Here is a quick guide for migrating the most common gradient settings. Please note that all new gradient imports follow the format: - > ```python - > from qiskit.algorithms.gradients import MethodPrimitiveGradient, QFI - > ``` - - ```{eval-rst} - .. dropdown:: Gradients - :animate: fade-in-slide-down - - .. list-table:: - :header-rows: 1 - - * - Opflow - - Alternative - - * - ``Gradient(method="lin_comb")`` - - ``LinCombEstimatorGradient(estimator=estimator)`` or ``LinCombSamplerGradient(sampler=sampler)`` - * - ``Gradient(method="param_shift")`` - - ``ParamShiftEstimatorGradient(estimator=estimator)`` or ``ParamShiftSamplerGradient(sampler=sampler)`` - * - ``Gradient(method="fin_diff")`` - - ``FiniteDiffEstimatorGradient(estimator=estimator)`` or ``ParamShiftSamplerGradient(sampler=sampler)`` + ```python + from qiskit.algorithms.gradients import MethodPrimitiveGradient, QFI ``` - ```{eval-rst} - .. dropdown:: QFI/QGT - :animate: fade-in-slide-down + Gradients: - .. list-table:: - :header-rows: 1 + | Opflow | Alternative | + | --- | --- | + | `Gradient(method="lin_comb")` | `LinCombEstimatorGradient(estimator=estimator)` or `LinCombSamplerGradient(sampler=sampler)` | + | `Gradient(method="param_shift")` | `ParamShiftEstimatorGradient(estimator=estimator)` or `ParamShiftSamplerGradient(sampler=sampler)` | + | `Gradient(method="fin_diff")` | `FiniteDiffEstimatorGradient(estimator=estimator)` or `ParamShiftSamplerGradient(sampler=sampler)` | - * - Opflow - - Alternative + QFI/QGT: - * - ``QFI(method="lin_comb_full")`` - - ``qgt=LinCombQGT(Estimator())`` - ``QFI(qgt=qgt)`` - ``` + | Opflow | Alternative | + | --- | --- | + | `QFI(method="lin_comb_full")` | `qgt=LinCombQGT(Estimator())` | Other auxiliary classes in the legacy gradient framework have now been deprecated. Here is the complete migration diff --git a/docs/api/migration-guides/qiskit-quantum-instance.mdx b/docs/api/migration-guides/qiskit-quantum-instance.mdx index 9453312815c..2e96bec9520 100644 --- a/docs/api/migration-guides/qiskit-quantum-instance.mdx +++ b/docs/api/migration-guides/qiskit-quantum-instance.mdx @@ -34,9 +34,9 @@ The remainder of this guide will focus on the [`qiskit.utils.QuantumInstance.exe ## Contents -- [Choosing the right primitive for your task] -- [Choosing the right primitive for your settings] -- [Code examples] +- [Choosing the right primitive for your task](#choosing-the-right-primitive-for-your-task) +- [Choosing the right primitive for your settings](#choosing-the-right-primitive-for-your-settings) +- [Code examples](#code-examples) **Background on the Qiskit Primitives** @@ -144,481 +144,463 @@ primitives **expose a similar setting through their interface**: ## Code examples -```{eval-rst} -.. dropdown:: Example 1: Circuit Sampling with Local Simulation - :animate: fade-in-slide-down +### Example 1: Circuit Sampling with Local Simulation - **Using Quantum Instance** +**Using Quantum Instance** - The only alternative for local simulations using the quantum instance was using an Aer simulator backend. - If no simulation method is specified, the Aer simulator will default to an exact simulation - (statevector/stabilizer), if shots are specified, it will add shot noise. - Please note that ``QuantumInstance.execute()`` returned the counts in hexadecimal format. +The only alternative for local simulations using the quantum instance was using an Aer simulator backend. +If no simulation method is specified, the Aer simulator will default to an exact simulation +(statevector/stabilizer), if shots are specified, it will add shot noise. +Please note that `QuantumInstance.execute()` returned the counts in hexadecimal format. - .. code-block:: python +```python +from qiskit import QuantumCircuit +from qiskit_aer import AerSimulator +from qiskit.utils import QuantumInstance - from qiskit import QuantumCircuit - from qiskit_aer import AerSimulator - from qiskit.utils import QuantumInstance +circuit = QuantumCircuit(2) +circuit.x(0) +circuit.x(1) +circuit.measure_all() - circuit = QuantumCircuit(2) - circuit.x(0) - circuit.x(1) - circuit.measure_all() +simulator = AerSimulator() +qi = QuantumInstance(backend=simulator, shots=200) +result = qi.execute(circuit).results[0] +data = result.data +counts = data.counts - simulator = AerSimulator() - qi = QuantumInstance(backend=simulator, shots=200) - result = qi.execute(circuit).results[0] - data = result.data - counts = data.counts - - print("Counts: ", counts) - print("Data: ", data) - print("Result: ", result) - - .. code-block:: text - - Counts: {'0x3': 200} - Data: ExperimentResultData(counts={'0x3': 200}) - Result: ExperimentResult(shots=200, success=True, meas_level=2, data=ExperimentResultData(counts={'0x3': 200}), header=QobjExperimentHeader(clbit_labels=[['meas', 0], ['meas', 1]], creg_sizes=[['meas', 2]], global_phase=0.0, memory_slots=2, metadata={}, n_qubits=2, name='circuit-99', qreg_sizes=[['q', 2]], qubit_labels=[['q', 0], ['q', 1]]), status=DONE, seed_simulator=2846213898, metadata={'parallel_state_update': 16, 'parallel_shots': 1, 'sample_measure_time': 0.00025145, 'noise': 'ideal', 'batched_shots_optimization': False, 'remapped_qubits': False, 'device': 'CPU', 'active_input_qubits': [0, 1], 'measure_sampling': True, 'num_clbits': 2, 'input_qubit_map': [[1, 1], [0, 0]], 'num_qubits': 2, 'method': 'stabilizer', 'fusion': {'enabled': False}}, time_taken=0.000672166) - - **Using Primitives** - - The primitives offer two alternatives for local simulation, one with the Reference primitives - and one with the Aer primitives. As mentioned above the closest alternative to ``QuantumInstance.execute()`` - for sampling is the ``Sampler`` primitive. - - **a. Using the Reference Primitives** - - Basic simulation implemented using the [`qiskit.quantum_info`](../qiskit/quantum_info) module. If shots are - specified, the results will include shot noise. Please note that - the resulting quasi-probability distribution does not use bitstrings but **integers** to identify the states. - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.primitives import Sampler - - circuit = QuantumCircuit(2) - circuit.x(0) - circuit.x(1) - circuit.measure_all() - - sampler = Sampler() - result = sampler.run(circuit, shots=200).result() - quasi_dists = result.quasi_dists - - print("Quasi-dists: ", quasi_dists) - print("Result: ", result) - - .. code-block:: text - - Quasi-dists: [{3: 1.0}] - Result: SamplerResult(quasi_dists=[{3: 1.0}], metadata=[{'shots': 200}]) - - **b. Using the Aer Primitives** - - Aer simulation following the statevector method. This would be the closer replacement of the - [`qiskit.utils.QuantumInstance`](../qiskit/qiskit.utils.QuantumInstance) - example, as they are both accessing the same simulator. For this reason, the output metadata is - closer to the Quantum Instance's output. Please note that - the resulting quasi-probability distribution does not use bitstrings but **integers** to identify the states. - - .. note:: - - The [`qiskit.result.QuasiDistribution`](../qiskit/qiskit.result.QuasiDistribution) class returned as part of the [`qiskit.primitives.SamplerResult`](../qiskit/qiskit.primitives.SamplerResult) - exposes two methods to convert the result keys from integer to binary strings/hexadecimal: +print("Counts: ", counts) +print("Data: ", data) +print("Result: ", result) +``` - - [`qiskit.result.QuasiDistribution.binary_probabilities`](../qiskit/qiskit.result.QuasiDistribution#binary_probabilities) - - [`qiskit.result.QuasiDistribution.hex_probabilities`](../qiskit/qiskit.result.QuasiDistribution#hex_probabilities) +```text +Counts: {'0x3': 200} +Data: ExperimentResultData(counts={'0x3': 200}) +Result: ExperimentResult(shots=200, success=True, meas_level=2, data=ExperimentResultData(counts={'0x3': 200}), header=QobjExperimentHeader(clbit_labels=[['meas', 0], ['meas', 1]], creg_sizes=[['meas', 2]], global_phase=0.0, memory_slots=2, metadata={}, n_qubits=2, name='circuit-99', qreg_sizes=[['q', 2]], qubit_labels=[['q', 0], ['q', 1]]), status=DONE, seed_simulator=2846213898, metadata={'parallel_state_update': 16, 'parallel_shots': 1, 'sample_measure_time': 0.00025145, 'noise': 'ideal', 'batched_shots_optimization': False, 'remapped_qubits': False, 'device': 'CPU', 'active_input_qubits': [0, 1], 'measure_sampling': True, 'num_clbits': 2, 'input_qubit_map': [[1, 1], [0, 0]], 'num_qubits': 2, 'method': 'stabilizer', 'fusion': {'enabled': False}}, time_taken=0.000672166) +``` +**Using Primitives** - .. code-block:: python +The primitives offer two alternatives for local simulation, one with the Reference primitives +and one with the Aer primitives. As mentioned above the closest alternative to `QuantumInstance.execute()` +for sampling is the `Sampler` primitive. - from qiskit import QuantumCircuit - from qiskit_aer.primitives import Sampler +**a. Using the Reference Primitives** - circuit = QuantumCircuit(2) - circuit.x(0) - circuit.x(1) - circuit.measure_all() +Basic simulation implemented using the [`qiskit.quantum_info`](../qiskit/quantum_info) module. If shots are +specified, the results will include shot noise. Please note that +the resulting quasi-probability distribution does not use bitstrings but **integers** to identify the states. - # if no Noise Model provided, the aer primitives - # perform an exact (statevector) simulation - sampler = Sampler() - result = sampler.run(circuit, shots=200).result() - quasi_dists = result.quasi_dists - # convert keys to binary bitstrings - binary_dist = quasi_dists[0].binary_probabilities() +```python +from qiskit import QuantumCircuit +from qiskit.primitives import Sampler - print("Quasi-dists: ", quasi_dists) - print("Result: ", result) - print("Binary quasi-dist: ", binary_dist) +circuit = QuantumCircuit(2) +circuit.x(0) +circuit.x(1) +circuit.measure_all() - .. code-block:: text +sampler = Sampler() +result = sampler.run(circuit, shots=200).result() +quasi_dists = result.quasi_dists - Quasi-dists: [{3: 1.0}] - Result: SamplerResult(quasi_dists=[{3: 1.0}], metadata=[{'shots': 200, 'simulator_metadata': {'parallel_state_update': 16, 'parallel_shots': 1, 'sample_measure_time': 9.016e-05, 'noise': 'ideal', 'batched_shots_optimization': False, 'remapped_qubits': False, 'device': 'CPU', 'active_input_qubits': [0, 1], 'measure_sampling': True, 'num_clbits': 2, 'input_qubit_map': [[1, 1], [0, 0]], 'num_qubits': 2, 'method': 'statevector', 'fusion': {'applied': False, 'max_fused_qubits': 5, 'threshold': 14, 'enabled': True}}}]) - Binary quasi-dist: {'11': 1.0} +print("Quasi-dists: ", quasi_dists) +print("Result: ", result) ``` -```{eval-rst} -.. dropdown:: Example 2: Expectation Value Calculation with Local Noisy Simulation - :animate: fade-in-slide-down - - While this example does not include a direct call to ``QuantumInstance.execute()``, it shows - how to migrate from a [`qiskit.utils.QuantumInstance`](../qiskit/qiskit.utils.QuantumInstance)-based to a [`qiskit.primitives`](../qiskit/primitives)-based - workflow. - - **Using Quantum Instance** - - The most common use case for computing expectation values with the Quantum Instance was as in combination with the - [`qiskit.opflow`](../qiskit/opflow) library. You can see more information in the [opflow migration guide](./qiskit-opflow-module). - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.opflow import StateFn, PauliSumOp, PauliExpectation, CircuitSampler - from qiskit.utils import QuantumInstance - from qiskit_aer import AerSimulator - from qiskit_aer.noise import NoiseModel - from qiskit_ibm_provider import IBMProvider - - # Define problem using opflow - op = PauliSumOp.from_list([("XY",1)]) - qc = QuantumCircuit(2) - qc.x(0) - qc.x(1) - - state = StateFn(qc) - measurable_expression = StateFn(op, is_measurement=True).compose(state) - expectation = PauliExpectation().convert(measurable_expression) +```text +Quasi-dists: [{3: 1.0}] +Result: SamplerResult(quasi_dists=[{3: 1.0}], metadata=[{'shots': 200}]) +``` - # Define Quantum Instance with noisy simulator - provider = IBMProvider() - device = provider.get_backend("ibmq_manila") - noise_model = NoiseModel.from_backend(device) - coupling_map = device.configuration().coupling_map +**b. Using the Aer Primitives** - backend = AerSimulator() - qi = QuantumInstance(backend=backend, shots=1024, - seed_simulator=42, seed_transpiler=42, - coupling_map=coupling_map, noise_model=noise_model) +Aer simulation following the statevector method. This would be the closer replacement of the +[`qiskit.utils.QuantumInstance`](../qiskit/qiskit.utils.QuantumInstance) +example, as they are both accessing the same simulator. For this reason, the output metadata is +closer to the Quantum Instance's output. Please note that +the resulting quasi-probability distribution does not use bitstrings but **integers** to identify the states. - # Run - sampler = CircuitSampler(qi).convert(expectation) - expectation_value = sampler.eval().real + + The [`qiskit.result.QuasiDistribution`](../qiskit/qiskit.result.QuasiDistribution) class returned as part of the [`qiskit.primitives.SamplerResult`](../qiskit/qiskit.primitives.SamplerResult) + exposes two methods to convert the result keys from integer to binary strings/hexadecimal: - print(expectation_value) + - [`qiskit.result.QuasiDistribution.binary_probabilities`](../qiskit/qiskit.result.QuasiDistribution#binary_probabilities) + - [`qiskit.result.QuasiDistribution.hex_probabilities`](../qiskit/qiskit.result.QuasiDistribution#hex_probabilities) + - .. code-block:: text +```python +from qiskit import QuantumCircuit +from qiskit_aer.primitives import Sampler + +circuit = QuantumCircuit(2) +circuit.x(0) +circuit.x(1) +circuit.measure_all() + +# if no Noise Model provided, the aer primitives +# perform an exact (statevector) simulation +sampler = Sampler() +result = sampler.run(circuit, shots=200).result() +quasi_dists = result.quasi_dists +# convert keys to binary bitstrings +binary_dist = quasi_dists[0].binary_probabilities() + +print("Quasi-dists: ", quasi_dists) +print("Result: ", result) +print("Binary quasi-dist: ", binary_dist) +``` - -0.04687500000000008 +```text +Quasi-dists: [{3: 1.0}] +Result: SamplerResult(quasi_dists=[{3: 1.0}], metadata=[{'shots': 200, 'simulator_metadata': {'parallel_state_update': 16, 'parallel_shots': 1, 'sample_measure_time': 9.016e-05, 'noise': 'ideal', 'batched_shots_optimization': False, 'remapped_qubits': False, 'device': 'CPU', 'active_input_qubits': [0, 1], 'measure_sampling': True, 'num_clbits': 2, 'input_qubit_map': [[1, 1], [0, 0]], 'num_qubits': 2, 'method': 'statevector', 'fusion': {'applied': False, 'max_fused_qubits': 5, 'threshold': 14, 'enabled': True}}}]) +Binary quasi-dist: {'11': 1.0} +``` - **Using Primitives** +### Example 2: Expectation Value Calculation with Local Noisy Simulation - The primitives now allow the combination of the opflow and quantum instance functionality in a single ``Estimator``. - In this case, for local noisy simulation, this will be the Aer Estimator. +While this example does not include a direct call to `QuantumInstance.execute()`, it shows +how to migrate from a [`qiskit.utils.QuantumInstance`](../qiskit/qiskit.utils.QuantumInstance)-based to a [`qiskit.primitives`](../qiskit/primitives)-based +workflow. - .. code-block:: python +**Using Quantum Instance** - from qiskit import QuantumCircuit - from qiskit.quantum_info import SparsePauliOp - from qiskit_aer.noise import NoiseModel - from qiskit_aer.primitives import Estimator - from qiskit_ibm_provider import IBMProvider +The most common use case for computing expectation values with the Quantum Instance was as in combination with the +[`qiskit.opflow`](../qiskit/opflow) library. You can see more information in the [opflow migration guide](./qiskit-opflow-module). - # Define problem - op = SparsePauliOp("XY") - qc = QuantumCircuit(2) - qc.x(0) - qc.x(1) +```python +from qiskit import QuantumCircuit +from qiskit.opflow import StateFn, PauliSumOp, PauliExpectation, CircuitSampler +from qiskit.utils import QuantumInstance +from qiskit_aer import AerSimulator +from qiskit_aer.noise import NoiseModel +from qiskit_ibm_provider import IBMProvider - # Define Aer Estimator with noisy simulator - device = provider.get_backend("ibmq_manila") - noise_model = NoiseModel.from_backend(device) - coupling_map = device.configuration().coupling_map +# Define problem using opflow +op = PauliSumOp.from_list([("XY",1)]) +qc = QuantumCircuit(2) +qc.x(0) +qc.x(1) - # if Noise Model provided, the aer primitives - # perform a "qasm" simulation - estimator = Estimator( - backend_options={ # method chosen automatically to match options - "coupling_map": coupling_map, - "noise_model": noise_model, - }, - run_options={"seed": 42, "shots": 1024}, - transpile_options={"seed_transpiler": 42}, - ) +state = StateFn(qc) +measurable_expression = StateFn(op, is_measurement=True).compose(state) +expectation = PauliExpectation().convert(measurable_expression) - # Run - expectation_value = estimator.run(qc, op).result().values +# Define Quantum Instance with noisy simulator +provider = IBMProvider() +device = provider.get_backend("ibmq_manila") +noise_model = NoiseModel.from_backend(device) +coupling_map = device.configuration().coupling_map - print(expectation_value) +backend = AerSimulator() +qi = QuantumInstance(backend=backend, shots=1024, + seed_simulator=42, seed_transpiler=42, + coupling_map=coupling_map, noise_model=noise_model) - .. code-block:: text +# Run +sampler = CircuitSampler(qi).convert(expectation) +expectation_value = sampler.eval().real - [-0.04101562] +print(expectation_value) ``` -```{eval-rst} -.. dropdown:: Example 3: Circuit Sampling on IBM Backend with Error Mitigation - :animate: fade-in-slide-down - - **Using Quantum Instance** +```text +-0.04687500000000008 +``` - The ``QuantumInstance`` interface allowed the configuration of measurement error mitigation settings such as the method, the - matrix refresh period or the mitigation pattern. This configuration is no longer available in the primitives - interface. +**Using Primitives** + +The primitives now allow the combination of the opflow and quantum instance functionality in a single `Estimator`. +In this case, for local noisy simulation, this will be the Aer Estimator. + +```python +from qiskit import QuantumCircuit +from qiskit.quantum_info import SparsePauliOp +from qiskit_aer.noise import NoiseModel +from qiskit_aer.primitives import Estimator +from qiskit_ibm_provider import IBMProvider + +# Define problem +op = SparsePauliOp("XY") +qc = QuantumCircuit(2) +qc.x(0) +qc.x(1) + +# Define Aer Estimator with noisy simulator +device = provider.get_backend("ibmq_manila") +noise_model = NoiseModel.from_backend(device) +coupling_map = device.configuration().coupling_map + +# if Noise Model provided, the aer primitives +# perform a "qasm" simulation +estimator = Estimator( + backend_options={ # method chosen automatically to match options + "coupling_map": coupling_map, + "noise_model": noise_model, + }, + run_options={"seed": 42, "shots": 1024}, + transpile_options={"seed_transpiler": 42}, + ) - .. code-block:: python +# Run +expectation_value = estimator.run(qc, op).result().values - from qiskit import QuantumCircuit - from qiskit.utils import QuantumInstance - from qiskit.utils.mitigation import CompleteMeasFitter - from qiskit_ibm_provider import IBMProvider +print(expectation_value) +``` - circuit = QuantumCircuit(2) - circuit.x(0) - circuit.x(1) - circuit.measure_all() +```python +[-0.04101562] +``` - provider = IBMProvider() - backend = provider.get_backend("ibmq_montreal") +### Example 3: Circuit Sampling on IBM Backend with Error Mitigation - qi = QuantumInstance( - backend=backend, - shots=4000, - measurement_error_mitigation_cls=CompleteMeasFitter, - cals_matrix_refresh_period=0, - ) +**Using Quantum Instance** - result = qi.execute(circuit).results[0].data - print(result) +The `QuantumInstance` interface allowed the configuration of measurement error mitigation settings such as the method, the +matrix refresh period or the mitigation pattern. This configuration is no longer available in the primitives +interface. - .. code-block:: text +```python +from qiskit import QuantumCircuit +from qiskit.utils import QuantumInstance +from qiskit.utils.mitigation import CompleteMeasFitter +from qiskit_ibm_provider import IBMProvider - ExperimentResultData(counts={'11': 4000}) +circuit = QuantumCircuit(2) +circuit.x(0) +circuit.x(1) +circuit.measure_all() +provider = IBMProvider() +backend = provider.get_backend("ibmq_montreal") - **Using Primitives** +qi = QuantumInstance( + backend=backend, + shots=4000, + measurement_error_mitigation_cls=CompleteMeasFitter, + cals_matrix_refresh_period=0, +) - The Qiskit Runtime Primitives offer a suite of error mitigation methods that can be easily turned on with the - ``resilience_level`` option. These are, however, not configurable. The sampler's ``resilience_level=1`` - is the closest alternative to the Quantum Instance's measurement error mitigation implementation, but this - is not a 1-1 replacement. +result = qi.execute(circuit).results[0].data +print(result) +``` - For more information on the error mitigation options in the Qiskit Runtime Primitives, you can check out [Configure Error Mitigation](../../run/configure-error-mitigation). +```python +ExperimentResultData(counts={'11': 4000}) +``` - .. code-block:: python +**Using Primitives** - from qiskit import QuantumCircuit - from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Options +The Qiskit Runtime Primitives offer a suite of error mitigation methods that can be easily turned on with the +`resilience_level` option. These are, however, not configurable. The sampler's `resilience_level=1` +is the closest alternative to the Quantum Instance's measurement error mitigation implementation, but this +is not a 1-1 replacement. - circuit = QuantumCircuit(2) - circuit.x(0) - circuit.x(1) - circuit.measure_all() +For more information on the error mitigation options in the Qiskit Runtime Primitives, you can check out [Configure Error Mitigation](../../run/configure-error-mitigation). - service = QiskitRuntimeService(channel="ibm_quantum") - backend = service.backend("ibmq_montreal") +```python +from qiskit import QuantumCircuit +from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Options - options = Options(resilience_level = 1) # 1 = measurement error mitigation - sampler = Sampler(session=backend, options=options) +circuit = QuantumCircuit(2) +circuit.x(0) +circuit.x(1) +circuit.measure_all() - # Run - result = sampler.run(circuit, shots=4000).result() - quasi_dists = result.quasi_dists +service = QiskitRuntimeService(channel="ibm_quantum") +backend = service.backend("ibmq_montreal") - print("Quasi dists: ", quasi_dists) +options = Options(resilience_level = 1) # 1 = measurement error mitigation +sampler = Sampler(session=backend, options=options) - .. code-block:: text +# Run +result = sampler.run(circuit, shots=4000).result() +quasi_dists = result.quasi_dists - Quasi dists: [{2: 0.0008492371522941081, 3: 0.9968874384378738, 0: -0.0003921227905920063, - 1: 0.002655447200424097}] +print("Quasi dists: ", quasi_dists) ``` -```{eval-rst} -.. dropdown:: Example 4: Circuit Sampling with Custom Bound and Unbound Pass Managers - :animate: fade-in-slide-down +```text +Quasi dists: [{2: 0.0008492371522941081, 3: 0.9968874384378738, 0: -0.0003921227905920063, + 1: 0.002655447200424097}] +``` - The management of transpilation is different between the ``QuantumInstance`` and the Primitives. +### Example 4: Circuit Sampling with Custom Bound and Unbound Pass Managers - The Quantum Instance allowed you to: +The management of transpilation is different between the `QuantumInstance` and the Primitives. - * Define bound and unbound pass managers that will be called during ``.execute()``. - * Explicitly call its ``.transpile()`` method with a specific pass manager. +The Quantum Instance allowed you to: - However: +* Define bound and unbound pass managers that will be called during `.execute()`. +* Explicitly call its `.transpile()` method with a specific pass manager. - * The Quantum Instance **did not** manage parameter bindings on parametrized quantum circuits. This would - mean that if a ``bound_pass_manager`` was set, the circuit sent to ``QuantumInstance.execute()`` could - not have any free parameters. +However: - On the other hand, when using the primitives: +* The Quantum Instance **did not** manage parameter bindings on parametrized quantum circuits. This would + mean that if a `bound_pass_manager` was set, the circuit sent to `QuantumInstance.execute()` could + not have any free parameters. - * You cannot explicitly access their transpilation routine. - * The mechanism to apply custom transpilation passes to the Aer, Runtime and Backend primitives is to pre-transpile - locally and set ``skip_transpilation=True`` in the corresponding primitive. - * The only primitives that currently accept a custom **bound** transpiler pass manager are instances of [`qiskit.primitives.BackendSampler`](../qiskit/qiskit.primitives.BackendSampler) or [`qiskit.primitives.BackendEstimator`](../qiskit/qiskit.primitives.BackendEstimator). - If a ``bound_pass_manager`` is defined, the ``skip_transpilation=True`` option will **not** skip this bound pass. +On the other hand, when using the primitives: - .. attention:: +* You cannot explicitly access their transpilation routine. +* The mechanism to apply custom transpilation passes to the Aer, Runtime and Backend primitives is to pre-transpile + locally and set `skip_transpilation=True` in the corresponding primitive. +* The only primitives that currently accept a custom **bound** transpiler pass manager are instances of [`qiskit.primitives.BackendSampler`](../qiskit/qiskit.primitives.BackendSampler) or [`qiskit.primitives.BackendEstimator`](../qiskit/qiskit.primitives.BackendEstimator). + If a `bound_pass_manager` is defined, the `skip_transpilation=True` option will **not** skip this bound pass. - Care is needed when setting ``skip_transpilation=True`` with the ``Estimator`` primitive. + + Care is needed when setting `skip_transpilation=True` with the `Estimator` primitive. Since operator and circuit size need to match for the Estimator, should the custom transpilation change the circuit size, then the operator must be adapted before sending it to the Estimator, as there is no currently no mechanism to identify the active qubits it should consider. + + +Note that the primitives **do** handle parameter bindings, meaning that even if a `bound_pass_manager` is defined in a +[`qiskit.primitives.BackendSampler`](../qiskit/qiskit.primitives.BackendSampler) or [`qiskit.primitives.BackendEstimator`](../qiskit/qiskit.primitives.BackendEstimator), you do not have to manually assign parameters as expected in the Quantum Instance workflow. + +The use-case that motivated the addition of the two-stage transpilation to the `QuantumInstance` was to allow +running pulse-efficient transpilation passes with the [`qiskit.opflow.converters.CircuitSampler`](../qiskit/qiskit.opflow.converters.CircuitSampler) class. The following +example shows to migrate this particular use-case, where the `QuantumInstance.execute()` method is called +under the hood by the [`qiskit.opflow.converters.CircuitSampler`](../qiskit/qiskit.opflow.converters.CircuitSampler). + +**Using Quantum Instance** + +```python +from qiskit.circuit.library.standard_gates.equivalence_library import StandardEquivalenceLibrary as std_eqlib +from qiskit.circuit.library import RealAmplitudes +from qiskit.opflow import CircuitSampler, StateFn +from qiskit.providers.fake_provider import FakeBelem +from qiskit.transpiler import PassManager, PassManagerConfig, CouplingMap +from qiskit.transpiler.preset_passmanagers import level_1_pass_manager +from qiskit.transpiler.passes import ( + Collect2qBlocks, ConsolidateBlocks, Optimize1qGatesDecomposition, + RZXCalibrationBuilderNoEcho, UnrollCustomDefinitions, BasisTranslator +) +from qiskit.transpiler.passes.optimization.echo_rzx_weyl_decomposition import EchoRZXWeylDecomposition +from qiskit.utils import QuantumInstance + +# Define backend +backend = FakeBelem() + +# Build the pass manager for the parameterized circuit +rzx_basis = ['rzx', 'rz', 'x', 'sx'] +coupling_map = CouplingMap(backend.configuration().coupling_map) +config = PassManagerConfig(basis_gates=rzx_basis, coupling_map=coupling_map) +pre = level_1_pass_manager(config) +inst_map = backend.defaults().instruction_schedule_map + +# Build a pass manager for the CX decomposition (works only on bound circuits) +post = PassManager([ + # Consolidate consecutive two-qubit operations. + Collect2qBlocks(), + ConsolidateBlocks(basis_gates=['rz', 'sx', 'x', 'rxx']), + + # Rewrite circuit in terms of Weyl-decomposed echoed RZX gates. + EchoRZXWeylDecomposition(inst_map), + + # Attach scaled CR pulse schedules to the RZX gates. + RZXCalibrationBuilderNoEcho(inst_map), + + # Simplify single-qubit gates. + UnrollCustomDefinitions(std_eqlib, rzx_basis), + BasisTranslator(std_eqlib, rzx_basis), + Optimize1qGatesDecomposition(rzx_basis), +]) + +# Instantiate qi +quantum_instance = QuantumInstance(backend, pass_manager=pre, bound_pass_manager=post) + +# Define parametrized circuit and parameter values +qc = RealAmplitudes(2) +print(qc.decompose()) +param_dict = {p: 0.5 for p in qc.parameters} + +# Instantiate CircuitSampler +sampler = CircuitSampler(quantum_instance) + +# Run +quasi_dists = sampler.convert(StateFn(qc), params=param_dict).sample() +print("Quasi-dists: ", quasi_dists) +``` - .. - In opflow, the ansatz would always have the basis change and measurement gates added before transpilation, - so if the circuit ended up on more qubits it did not matter. - - Note that the primitives **do** handle parameter bindings, meaning that even if a ``bound_pass_manager`` is defined in a - [`qiskit.primitives.BackendSampler`](../qiskit/qiskit.primitives.BackendSampler) or [`qiskit.primitives.BackendEstimator`](../qiskit/qiskit.primitives.BackendEstimator), you do not have to manually assign parameters as expected in the Quantum Instance workflow. - - The use-case that motivated the addition of the two-stage transpilation to the ``QuantumInstance`` was to allow - running pulse-efficient transpilation passes with the [`qiskit.opflow.converters.CircuitSampler`](../qiskit/qiskit.opflow.converters.CircuitSampler) class. The following - example shows to migrate this particular use-case, where the ``QuantumInstance.execute()`` method is called - under the hood by the [`qiskit.opflow.converters.CircuitSampler`](../qiskit/qiskit.opflow.converters.CircuitSampler). - - **Using Quantum Instance** +```text + ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ +q_0: ┤ Ry(θ[0]) ├──■──┤ Ry(θ[2]) ├──■──┤ Ry(θ[4]) ├──■──┤ Ry(θ[6]) ├ + ├──────────┤┌─┴─┐├──────────┤┌─┴─┐├──────────┤┌─┴─┐├──────────┤ +q_1: ┤ Ry(θ[1]) ├┤ X ├┤ Ry(θ[3]) ├┤ X ├┤ Ry(θ[5]) ├┤ X ├┤ Ry(θ[7]) ├ + └──────────┘└───┘└──────────┘└───┘└──────────┘└───┘└──────────┘ +Quasi-dists: {'11': 0.443359375, '10': 0.21875, '01': 0.189453125, '00': 0.1484375} +``` - .. code-block:: python +**Using Primitives** + +Let's see how the workflow changes with the Backend Sampler: + +```python +from qiskit.circuit.library.standard_gates.equivalence_library import StandardEquivalenceLibrary as std_eqlib +from qiskit.circuit.library import RealAmplitudes +from qiskit.primitives import BackendSampler +from qiskit.providers.fake_provider import FakeBelem +from qiskit.transpiler import PassManager, PassManagerConfig, CouplingMap +from qiskit.transpiler.preset_passmanagers import level_1_pass_manager +from qiskit.transpiler.passes import ( + Collect2qBlocks, ConsolidateBlocks, Optimize1qGatesDecomposition, + RZXCalibrationBuilderNoEcho, UnrollCustomDefinitions, BasisTranslator +) +from qiskit.transpiler.passes.optimization.echo_rzx_weyl_decomposition import EchoRZXWeylDecomposition + +# Define backend +backend = FakeBelem() + +# Build the pass manager for the parameterized circuit +rzx_basis = ['rzx', 'rz', 'x', 'sx'] +coupling_map = CouplingMap(backend.configuration().coupling_map) +config = PassManagerConfig(basis_gates=rzx_basis, coupling_map=coupling_map) +pre = level_1_pass_manager(config) + +# Build a pass manager for the CX decomposition (works only on bound circuits) +inst_map = backend.defaults().instruction_schedule_map +post = PassManager([ + # Consolidate consecutive two-qubit operations. + Collect2qBlocks(), + ConsolidateBlocks(basis_gates=['rz', 'sx', 'x', 'rxx']), + + # Rewrite circuit in terms of Weyl-decomposed echoed RZX gates. + EchoRZXWeylDecomposition(inst_map), + + # Attach scaled CR pulse schedules to the RZX gates. + RZXCalibrationBuilderNoEcho(inst_map), + + # Simplify single-qubit gates. + UnrollCustomDefinitions(std_eqlib, rzx_basis), + BasisTranslator(std_eqlib, rzx_basis), + Optimize1qGatesDecomposition(rzx_basis), +]) + +# Define parametrized circuit and parameter values +qc = RealAmplitudes(2) +qc.measure_all() # add measurements! +print(qc.decompose()) + +# Instantiate backend sampler with skip_transpilation +sampler = BackendSampler(backend=backend, skip_transpilation=True, bound_pass_manager=post) + +# Run unbound transpiler pass +transpiled_circuit = pre.run(qc) + +# Run sampler +quasi_dists = sampler.run(transpiled_circuit, [[0.5] * len(qc.parameters)]).result().quasi_dists +print("Quasi-dists: ", quasi_dists) +``` - from qiskit.circuit.library.standard_gates.equivalence_library import StandardEquivalenceLibrary as std_eqlib - from qiskit.circuit.library import RealAmplitudes - from qiskit.opflow import CircuitSampler, StateFn - from qiskit.providers.fake_provider import FakeBelem - from qiskit.transpiler import PassManager, PassManagerConfig, CouplingMap - from qiskit.transpiler.preset_passmanagers import level_1_pass_manager - from qiskit.transpiler.passes import ( - Collect2qBlocks, ConsolidateBlocks, Optimize1qGatesDecomposition, - RZXCalibrationBuilderNoEcho, UnrollCustomDefinitions, BasisTranslator - ) - from qiskit.transpiler.passes.optimization.echo_rzx_weyl_decomposition import EchoRZXWeylDecomposition - from qiskit.utils import QuantumInstance - - # Define backend - backend = FakeBelem() - - # Build the pass manager for the parameterized circuit - rzx_basis = ['rzx', 'rz', 'x', 'sx'] - coupling_map = CouplingMap(backend.configuration().coupling_map) - config = PassManagerConfig(basis_gates=rzx_basis, coupling_map=coupling_map) - pre = level_1_pass_manager(config) - inst_map = backend.defaults().instruction_schedule_map - - # Build a pass manager for the CX decomposition (works only on bound circuits) - post = PassManager([ - # Consolidate consecutive two-qubit operations. - Collect2qBlocks(), - ConsolidateBlocks(basis_gates=['rz', 'sx', 'x', 'rxx']), - - # Rewrite circuit in terms of Weyl-decomposed echoed RZX gates. - EchoRZXWeylDecomposition(inst_map), - - # Attach scaled CR pulse schedules to the RZX gates. - RZXCalibrationBuilderNoEcho(inst_map), - - # Simplify single-qubit gates. - UnrollCustomDefinitions(std_eqlib, rzx_basis), - BasisTranslator(std_eqlib, rzx_basis), - Optimize1qGatesDecomposition(rzx_basis), - ]) - - # Instantiate qi - quantum_instance = QuantumInstance(backend, pass_manager=pre, bound_pass_manager=post) - - # Define parametrized circuit and parameter values - qc = RealAmplitudes(2) - print(qc.decompose()) - param_dict = {p: 0.5 for p in qc.parameters} - - # Instantiate CircuitSampler - sampler = CircuitSampler(quantum_instance) - - # Run - quasi_dists = sampler.convert(StateFn(qc), params=param_dict).sample() - print("Quasi-dists: ", quasi_dists) - - .. code-block:: text - - ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ - q_0: ┤ Ry(θ[0]) ├──■──┤ Ry(θ[2]) ├──■──┤ Ry(θ[4]) ├──■──┤ Ry(θ[6]) ├ - ├──────────┤┌─┴─┐├──────────┤┌─┴─┐├──────────┤┌─┴─┐├──────────┤ - q_1: ┤ Ry(θ[1]) ├┤ X ├┤ Ry(θ[3]) ├┤ X ├┤ Ry(θ[5]) ├┤ X ├┤ Ry(θ[7]) ├ - └──────────┘└───┘└──────────┘└───┘└──────────┘└───┘└──────────┘ - Quasi-dists: {'11': 0.443359375, '10': 0.21875, '01': 0.189453125, '00': 0.1484375} - - **Using Primitives** - - Let's see how the workflow changes with the Backend Sampler: - - .. code-block:: python - - from qiskit.circuit.library.standard_gates.equivalence_library import StandardEquivalenceLibrary as std_eqlib - from qiskit.circuit.library import RealAmplitudes - from qiskit.primitives import BackendSampler - from qiskit.providers.fake_provider import FakeBelem - from qiskit.transpiler import PassManager, PassManagerConfig, CouplingMap - from qiskit.transpiler.preset_passmanagers import level_1_pass_manager - from qiskit.transpiler.passes import ( - Collect2qBlocks, ConsolidateBlocks, Optimize1qGatesDecomposition, - RZXCalibrationBuilderNoEcho, UnrollCustomDefinitions, BasisTranslator - ) - from qiskit.transpiler.passes.optimization.echo_rzx_weyl_decomposition import EchoRZXWeylDecomposition - - # Define backend - backend = FakeBelem() - - # Build the pass manager for the parameterized circuit - rzx_basis = ['rzx', 'rz', 'x', 'sx'] - coupling_map = CouplingMap(backend.configuration().coupling_map) - config = PassManagerConfig(basis_gates=rzx_basis, coupling_map=coupling_map) - pre = level_1_pass_manager(config) - - # Build a pass manager for the CX decomposition (works only on bound circuits) - inst_map = backend.defaults().instruction_schedule_map - post = PassManager([ - # Consolidate consecutive two-qubit operations. - Collect2qBlocks(), - ConsolidateBlocks(basis_gates=['rz', 'sx', 'x', 'rxx']), - - # Rewrite circuit in terms of Weyl-decomposed echoed RZX gates. - EchoRZXWeylDecomposition(inst_map), - - # Attach scaled CR pulse schedules to the RZX gates. - RZXCalibrationBuilderNoEcho(inst_map), - - # Simplify single-qubit gates. - UnrollCustomDefinitions(std_eqlib, rzx_basis), - BasisTranslator(std_eqlib, rzx_basis), - Optimize1qGatesDecomposition(rzx_basis), - ]) - - # Define parametrized circuit and parameter values - qc = RealAmplitudes(2) - qc.measure_all() # add measurements! - print(qc.decompose()) - - # Instantiate backend sampler with skip_transpilation - sampler = BackendSampler(backend=backend, skip_transpilation=True, bound_pass_manager=post) - - # Run unbound transpiler pass - transpiled_circuit = pre.run(qc) - - # Run sampler - quasi_dists = sampler.run(transpiled_circuit, [[0.5] * len(qc.parameters)]).result().quasi_dists - print("Quasi-dists: ", quasi_dists) - - .. code-block:: text - - ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ░ ┌─┐ - q_0: ┤ Ry(θ[0]) ├──■──┤ Ry(θ[2]) ├──■──┤ Ry(θ[4]) ├──■──┤ Ry(θ[6]) ├─░─┤M├─── - ├──────────┤┌─┴─┐├──────────┤┌─┴─┐├──────────┤┌─┴─┐├──────────┤ ░ └╥┘┌─┐ - q_1: ┤ Ry(θ[1]) ├┤ X ├┤ Ry(θ[3]) ├┤ X ├┤ Ry(θ[5]) ├┤ X ├┤ Ry(θ[7]) ├─░──╫─┤M├ - └──────────┘└───┘└──────────┘└───┘└──────────┘└───┘└──────────┘ ░ ║ └╥┘ - meas: 2/═══════════════════════════════════════════════════════════════════╩══╩═ - 0 1 - Quasi-dists: [{1: 0.18359375, 2: 0.2333984375, 0: 0.1748046875, 3: 0.408203125}] +```text + ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ░ ┌─┐ + q_0: ┤ Ry(θ[0]) ├──■──┤ Ry(θ[2]) ├──■──┤ Ry(θ[4]) ├──■──┤ Ry(θ[6]) ├─░─┤M├─── + ├──────────┤┌─┴─┐├──────────┤┌─┴─┐├──────────┤┌─┴─┐├──────────┤ ░ └╥┘┌─┐ + q_1: ┤ Ry(θ[1]) ├┤ X ├┤ Ry(θ[3]) ├┤ X ├┤ Ry(θ[5]) ├┤ X ├┤ Ry(θ[7]) ├─░──╫─┤M├ + └──────────┘└───┘└──────────┘└───┘└──────────┘└───┘└──────────┘ ░ ║ └╥┘ +meas: 2/═══════════════════════════════════════════════════════════════════╩══╩═ + 0 1 +Quasi-dists: [{1: 0.18359375, 2: 0.2333984375, 0: 0.1748046875, 3: 0.408203125}] ```