From 779649c94cfc02512f83bc8abf1fc579943ed3e9 Mon Sep 17 00:00:00 2001 From: Ville Bergholm <1044345+smite@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:38:29 +0300 Subject: [PATCH] More user guide updates. (#120) * Fix contact link. * Use transpiled_circuits in the inspection example of the guide * Fix the batch execution example in the guide * User guide improvements. * "Fix" stochastically failing test. --------- Co-authored-by: Ville Bergholm Co-authored-by: Rakhim Davletkaliyev --- CHANGELOG.rst | 5 ++ docs/user_guide.rst | 149 ++++++++++++++++---------------- tests/test_iqm_transpilation.py | 5 +- 3 files changed, 82 insertions(+), 77 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 115266512..72fdaab84 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog ========= +Version 13.15 +============= + +* User guide updated. `#120 `_ + Version 13.14 ============= diff --git a/docs/user_guide.rst b/docs/user_guide.rst index 28bd8c183..280d66e6e 100644 --- a/docs/user_guide.rst +++ b/docs/user_guide.rst @@ -9,7 +9,7 @@ code snippets and check the output yourself. .. note:: At the moment IQM does not provide a quantum computing service open to the general public. - Please contact our `sales team `_ to set up your access to an IQM quantum + Please contact our `sales team `_ to set up your access to an IQM quantum computer. @@ -78,13 +78,13 @@ Let's consider the following quantum circuit which prepares and measures a GHZ s from qiskit import QuantumCircuit - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, 1) - qc.cx(0, 2) - qc.measure_all() + circuit = QuantumCircuit(3) + circuit.h(0) + circuit.cx(0, 1) + circuit.cx(0, 2) + circuit.measure_all() - print(qc.draw(output='text')) + print(circuit.draw(output='text')) :: @@ -114,7 +114,7 @@ circuit(s) are sampled: provider = IQMProvider(iqm_server_url) backend = provider.get_backend() - transpiled_circuit = transpile(qc, backend=backend) + transpiled_circuit = transpile(circuit, backend=backend) job = backend.run(transpiled_circuit, shots=1000) @@ -140,7 +140,7 @@ to use, you can provide it as follows: .. code-block:: python - job = backend.run(qc, shots=1000, calibration_set_id="f7d9642e-b0ca-4f2d-af2a-30195bd7a76d") + job = backend.run(transpiled_circuit, shots=1000, calibration_set_id="f7d9642e-b0ca-4f2d-af2a-30195bd7a76d") Alternatively, you can update the values of the options directly in the backend instance using :meth:`.IQMBackend.set_options` and then call :meth:`.IQMBackend.run` without specifying additional keyword arguments. @@ -244,11 +244,11 @@ parameters as :meth:`.IQMBackend.run`. .. code-block:: python # inspect the run_request without submitting it for execution - run_request = backend.create_run_request(circuit, shots=10) + run_request = backend.create_run_request(transpiled_circuit, shots=10) print(run_request) # the following two calls submit exactly the same run request for execution on the server - backend.run(circuit, shots=10) + backend.run(transpiled_circuit, shots=10) backend.client.submit_run_request(run_request) It is also possible to print a run request when it is actually submitted by setting the environment variable @@ -271,9 +271,8 @@ On IQM quantum computers without computational resonators from qiskit.compiler import transpile - qc_transpiled = transpile(qc, backend=backend, layout_method='sabre', optimization_level=3) - - print(qc_transpiled.draw(output='text', idle_wires=False)) + transpiled_circuit = transpile(circuit, backend=backend, layout_method='sabre', optimization_level=3) + print(transpiled_circuit.draw(output='text', idle_wires=False)) :: @@ -296,9 +295,8 @@ of single-qubit gates. This optimization expects an already transpiled circuit. from iqm.qiskit_iqm.iqm_transpilation import optimize_single_qubit_gates - qc_optimized = optimize_single_qubit_gates(qc_transpiled) - - print(qc_optimized.draw(output='text', idle_wires=False)) + optimized_circuit = optimize_single_qubit_gates(transpiled_circuit) + print(optimized_circuit.draw(output='text', idle_wires=False)) :: @@ -382,6 +380,63 @@ for the user to provide :func:`.transpile_to_IQM` with an ``optimization_level`` as the Qiskit :func:`transpile` function. +Batch execution of circuits +--------------------------- + +It is possible to submit multiple circuits to be executed, as a batch. In many cases this is more +time efficient than running the circuits one by one. Batch execution has some restrictions: all the +circuits must measure the same qubits, and be executed for the same number of shots. For starters, +let's construct two circuits preparing and measuring different Bell states: + +.. code-block:: python + + qc_1 = QuantumCircuit(2) + qc_1.h(0) + qc_1.cx(0, 1) + qc_1.measure_all() + + qc_2 = QuantumCircuit(2) + qc_2.h(0) + qc_2.x(1) + qc_2.cx(0, 1) + qc_2.measure_all() + +Now, we can run them together in a batch: + +.. code-block:: python + + transpiled_qcs = transpile([qc_1, qc_2], backend=backend, initial_layout=[0, 2]) + job = backend.run(transpiled_qcs, shots=1000) + print(job.result().get_counts()) + +The batch execution functionality can be used to run a parameterized circuit for various concrete values of parameters: + +.. code-block:: python + + import numpy as np + from qiskit.circuit import Parameter + + circuit = QuantumCircuit(2) + theta = Parameter('theta') + theta_range = np.linspace(0, np.pi / 2, 3) + + circuit.h(0) + circuit.cx(0, 1) + circuit.rz(theta, [0, 1]) + circuit.cx(0, 1) + circuit.h(0) + circuit.measure_all() + + + transpiled_circuit = transpile(circuit, backend=backend, layout_method='sabre', optimization_level=3) + circuits = [transpiled_circuit.assign_parameters({theta: n}) for n in theta_range] + job = backend.run(circuits, shots=1000) + print(job.result().get_counts()) + +Note that it is important to transpile the parameterized circuit before binding the values to ensure a consistent qubit +measurements across circuits in the batch. + + Simulation ---------- @@ -414,7 +469,7 @@ that represent specific IQM quantum architectures with pre-defined, representati backend = IQMFakeAdonis() transpiled_circuit = transpile(circuit, backend=backend) job = backend.run(transpiled_circuit, shots=1000) - job.result().get_counts() + print(job.result().get_counts()) Above, we use an :func:`.IQMFakeAdonis` instance to run a noisy simulation of ``circuit`` on a simulated 5-qubit Adonis chip. @@ -467,62 +522,4 @@ connectivity, and the native gateset should match the 5-qubit Adonis architectur are any. -Batch execution of circuits ---------------------------- - -It is possible to submit multiple circuits to be executed, as a batch. In many cases this is more -time efficient than running the circuits one by one. Batch execution has some restrictions: all the -circuits must measure the same qubits, and be executed for the same number of shots. For starters, -let's construct two circuits preparing and measuring different Bell states: - -.. code-block:: python - - qc_1 = QuantumCircuit(2) - qc_1.h(0) - qc_1.cx(0, 1) - qc_1.measure_all() - - qc_2 = QuantumCircuit(2) - qc_2.h(0) - qc_2.x(1) - qc_2.cx(0, 1) - qc_2.measure_all() - -Now, we can run them together in a batch: - -.. code-block:: python - - transpiled_qcs = transpile([qc_1, qc_2], backend=backend) - job = backend.run(transpiled_qcs, backend, initial_layout=[0, 2], shots=1000) - print(job.result().get_counts()) - -The batch execution functionality can be used to run a parameterized circuit for various concrete values of parameters: - -.. code-block:: python - - import numpy as np - from qiskit.circuit import Parameter - - qc = QuantumCircuit(2) - theta = Parameter('theta') - theta_range = np.linspace(0, 2*np.pi, 3) - - qc.h(0) - qc.cx(0, 1) - qc.rz(theta, [0, 1]) - qc.cx(0, 1) - qc.h(0) - qc.measure_all() - - - qc_transpiled = transpile(qc, backend=backend, layout_method='sabre', optimization_level=3) - - circuits = [qc_transpiled.assign_parameters({theta: n}) for n in theta_range] - job = backend.run(circuits, shots=1000) - - print(job.result().get_counts()) - -Note that it is important to transpile the parameterized circuit before binding the values to ensure a consistent qubit -measurements across circuits in the batch. - .. include:: ../CONTRIBUTING.rst diff --git a/tests/test_iqm_transpilation.py b/tests/test_iqm_transpilation.py index 95d5e00a2..8e9f5ba7f 100644 --- a/tests/test_iqm_transpilation.py +++ b/tests/test_iqm_transpilation.py @@ -60,13 +60,16 @@ def test_optimize_single_qubit_gates_drops_final_rz(): optimized_circuit = optimize_single_qubit_gates(transpiled_circuit, drop_final_rz=False) simulator = AerSimulator(method='statevector') - shots = 1000 + shots = 100000 + transpiled_counts = simulator.run(transpiled_circuit, shots=shots).result().get_counts() optimized_counts = simulator.run(optimized_circuit, shots=shots).result().get_counts() optimized_dropped_rz_counts = simulator.run(optimized_circuit_dropped_rz, shots=shots).result().get_counts() for counts in [transpiled_counts, optimized_counts, optimized_dropped_rz_counts]: for key in counts: + # rounding to one decimal to make stochastic failures unlikely + # TODO should think of a better test counts[key] = np.round(counts[key] / shots, 1) assert transpiled_counts == optimized_counts == optimized_dropped_rz_counts