Skip to content
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

Added a How-To guide for transpiler optimization level #10303

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ccd25b0
Merge pull request #1 from Qiskit/main
MaldoAlberto Jun 17, 2023
7f34465
tutorial optimization level
MaldoAlberto Jun 17, 2023
8f17159
solve the path
MaldoAlberto Jun 19, 2023
a4d850b
rename file
MaldoAlberto Jun 20, 2023
51b4d93
solve typo
MaldoAlberto Jun 21, 2023
39da540
new structure
MaldoAlberto Jun 22, 2023
cfc6c3e
new format
MaldoAlberto Jun 24, 2023
975476b
new version of optimization_level tutorial
MaldoAlberto Jun 29, 2023
537ff17
add new explanation
MaldoAlberto Jun 30, 2023
1cfb832
solve typo
MaldoAlberto Jun 30, 2023
33cdd34
add text
MaldoAlberto Jun 30, 2023
7693d2d
typo fixed
MaldoAlberto Jul 1, 2023
f464c0e
Merge pull request #2 from Qiskit/main
MaldoAlberto Jul 1, 2023
d06e6bc
Merge pull request #3 from MaldoAlberto/main
MaldoAlberto Jul 1, 2023
8f63672
Merge pull request #4 from Qiskit/main
MaldoAlberto Jul 3, 2023
53eaf9c
Merge pull request #5 from MaldoAlberto/main
MaldoAlberto Jul 3, 2023
7ddc83b
new version
MaldoAlberto Jul 5, 2023
d420a01
new format of the code
MaldoAlberto Jul 6, 2023
ff58bb6
solved typos
MaldoAlberto Jul 10, 2023
d5f0c11
remove empty spaces
MaldoAlberto Jul 10, 2023
c3b0ecf
add explanation about num of gates
MaldoAlberto Jul 11, 2023
e622229
solved typo
MaldoAlberto Jul 11, 2023
e29c5e3
Merge pull request #6 from Qiskit/main
MaldoAlberto Jul 11, 2023
afdf797
Merge pull request #7 from MaldoAlberto/main
MaldoAlberto Jul 11, 2023
d31a1c7
solved typo
MaldoAlberto Jul 11, 2023
fa2bd69
new version
MaldoAlberto Jul 11, 2023
2c6c9ed
solved some suggestions
MaldoAlberto Jul 25, 2023
ece9b0d
remove methods
MaldoAlberto Jul 27, 2023
4e57e6c
change the backend to v2
MaldoAlberto Jul 27, 2023
aff93c4
add recommendations
MaldoAlberto Jul 28, 2023
3306660
improve grammar
MaldoAlberto Aug 1, 2023
9717a67
improve grammar
MaldoAlberto Aug 2, 2023
06b2e82
rename some words
MaldoAlberto Aug 4, 2023
b17a25b
new version
MaldoAlberto Aug 17, 2023
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
3 changes: 2 additions & 1 deletion docs/how_to/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ Use the primitives
:maxdepth: 1

use_sampler
use_estimator
use_estimator
use_optimizationlevel
276 changes: 276 additions & 0 deletions docs/how_to/use_optimizationlevel.rst
MaldoAlberto marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
#####################################
Setting transpiler optimization level
#####################################

This guide shows you how to use the ``optimization_level``
parameter of :func:`~qiskit.compiler.transpile`.
MaldoAlberto marked this conversation as resolved.
Show resolved Hide resolved

``optimization_level`` helps you to optimize your quantum circuit.
This parameter takes an integer which can be a value between 0 and 3,
where the higher the number, the more optimized the result.
You can find more information about this parameter's value and its meaning for
the internals of the :mod:`~.transpiler` in: :ref:`working_with_preset_pass_managers`.

Initialize the quantum circuit
==============================

For this example, you will explore the `CSWAP <https://qiskit.org/documentation/stubs/qiskit.circuit.QuantumCircuit.cswap.html>`_ gate,
MaldoAlberto marked this conversation as resolved.
Show resolved Hide resolved
which is a three qubit gate which will be transpiled into one and two qubit gates.

.. plot::
:include-source:

from qiskit import QuantumCircuit
from qiskit.compiler import transpile
from qiskit.providers.fake_provider import FakeQuitoV2

backend = FakeQuitoV2()

qc = QuantumCircuit(3) # Initialize the quantum circuit with 3 qubits.

qc.cswap(0,1,2) # Add the cswap gate to the quantum circuit.

qc.draw("mpl")


Using backend’s information
===========================

The effect of setting the ``optimization_level`` will differ depending on the backend you are using.
You should adhere to the specific configuration of your backend when utilizing :func:`~qiskit.transpile` .
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really sure what this sentence means – can you rephrase it please?

MaldoAlberto marked this conversation as resolved.
Show resolved Hide resolved
This process entails breaking down your circuit into ``operation_names`` and considering the physical connections specified in the
``coupling_map`` for two qubit gates.
Given the presence of noise in the backend, it is crucial to optimize your circuit by adjusting the ``optimization_level`` parameter.
This will help minimize the number of circuit operations and enhance the overall performance.

When using a backend, you can access its properties through the instruction :meth:`backend.configuration()`.
These properties, such as operation names, the coupling map connection, and init layout, play a crucial role in shaping the behavior of the quantum circuit.

For example, with :meth:`~qiskit.providers.fake_provider.FakeQuitoV2`, you can learn about its qubit connections and the gates it uses to generate your quantum circuits.

.. testcode::

print(f"Operation names of your backend: {backend.operation_names}")
print(f"Coupling map connection of your backend: ",{[i for i in backend.coupling_map.get_edges()]}")

.. testoutput::

Operation names of your backend: ['id', 'rz', 'sx', 'x', 'cx', 'reset']
Coupling map connection of your backend: [(3, 4), (4, 3), (1, 3), (3, 1), (1, 2), (2, 1), (0, 1), (1, 0)]

MaldoAlberto marked this conversation as resolved.
Show resolved Hide resolved
What each optimization level does
=================================

When setting the ``optimization_level`` to 0, the resulting quantum circuit is not optimized and simply mapped to the device, considering a trivial layout and stochastic swap.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
When setting the ``optimization_level`` to 0, the resulting quantum circuit is not optimized and simply mapped to the device, considering a trivial layout and stochastic swap.
When setting the ``optimization_level`` to 0, the resulting quantum circuit is not optimized and simply mapped to the device using a trivial layout and stochastic swap.

Copy link
Member

@frankharkins frankharkins Aug 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MaldoAlberto "considering" is incorrect here, it basically means "think about carefully", so this sentence means "thinking about a trivial layout and stochastic swap".

I'd switch "considering" -> "using".

Suggested change
When setting the ``optimization_level`` to 0, the resulting quantum circuit is not optimized and simply mapped to the device, considering a trivial layout and stochastic swap.
When setting the ``optimization_level`` to 0, the resulting quantum circuit is not optimized and simply mapped to the device using a trivial layout and stochastic swap.

The coupling map, represented by the subset ``[(2,1),(1,2),(1,0),(0,1)]``, describes how the backend's physical qubits are connected.
In this configuration, the quantum circuit is transformed into a combination of one and two-qubit gates,
represented by the ``['id', 'rz', 'sx', 'x', 'cx', 'reset']``.

.. testcode::

qc_b0 = transpile(qc,backend=backend,optimization_level=0)
qc_b0.draw("mpl")
MaldoAlberto marked this conversation as resolved.
Show resolved Hide resolved

.. plot::

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.compiler import transpile
from qiskit.providers.fake_provider import FakeQuitoV2

backend = FakeQuitoV2()

qc = QuantumCircuit(3) # Initialize the quantum circuit with 3 qubits.

qc.cswap(0,1,2) # Add the cswap gate to the quantum circuit.

qc_b0 = transpile(qc,backend=backend,optimization_level=0)
qc_b0.draw("mpl")

When you set the ``optimization_level`` to 1, the circuit undergoes a light optimization process that focuses on collapsing adjacent gates
with the goal to find a heuristic layout and swap insertion algorithm,
improving the overall performance of the circuit. This results in a reduction in :class:`.CXGate` count and changes in the positions of qubits,
MaldoAlberto marked this conversation as resolved.
Show resolved Hide resolved
following the connections ``[(2,1),(1,0),(0,1)]``. In this example, the two adjacent gates :math:`RZ(\pi/4)` and :math:`RZ(\pi/2)` are replaced with a single :math:`RZ(3\pi/4)` operation.

.. note::
This optimization level is the default setting.

.. testcode::

qc_b1 = transpile(qc,backend=backend,optimization_level=1)
qc_b1.draw("mpl")

.. plot::

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.compiler import transpile
from qiskit.providers.fake_provider import FakeQuitoV2

backend = FakeQuitoV2()

qc = QuantumCircuit(3) # Initialize the quantum circuit with 3 qubits.

qc.cswap(0,1,2) # Add the cswap gate to the quantum circuit.

qc_b1 = transpile(qc,backend=backend,optimization_level=1)
qc_b1.draw("mpl")


When you set the ``qiskit.transpile`` to 2, the circuit undergoes a medium optimization process.
This involves utilizing a noise-adaptive layout and gate cancellation techniques based on commutation relationships,
MaldoAlberto marked this conversation as resolved.
Show resolved Hide resolved
while performing the same process as when the ``optimization_level`` is 1, but with an increased number of iterations.
Depending on the circuit, this level of optimization can occasionally yield the same results as light optimization.
MaldoAlberto marked this conversation as resolved.
Show resolved Hide resolved


.. testcode::

qc_b2 = transpile(qc,backend=backend,optimization_level=2)
qc_b2.draw("mpl")


.. plot::

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.compiler import transpile
from qiskit.providers.fake_provider import FakeQuitoV2

backend = FakeQuitoV2()

qc = QuantumCircuit(3) # Initialize the quantum circuit with 3 qubits.

qc.cswap(0,1,2) # Add the cswap gate to the quantum circuit.

qc_b2 = transpile(qc,backend=backend,optimization_level=2)
qc_b2.draw("mpl")

When you set the ``optimization_level`` to 3, it enables heavy optimization.
This level of optimization uses techniques from level 2, and also resynthesizes blocks of two-qubit gates in the circuit.
The result of multiple seeds for different trials is a reduction in the number of quantum gates and the determination of the a coupling map connection, such as **[(2,1),(0,1),(1,0)]**.
MaldoAlberto marked this conversation as resolved.
Show resolved Hide resolved
Based on the operation names, results in one less :class:`.CXGate` and the addition of eight one qubit gates.

.. testcode::

qc_b3 = transpile(qc,backend=backend,optimization_level=3)
qc_b3.draw("mpl")


.. plot::

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.compiler import transpile
from qiskit.providers.fake_provider import FakeQuitoV2

backend = FakeQuitoV2()

qc = QuantumCircuit(3) # Initialize the quantum circuit with 3 qubits.

qc.cswap(0,1,2) # Add the cswap gate to the quantum circuit.

qc_b3 = transpile(qc,backend=backend,optimization_level=3)
qc_b3.draw("mpl")


Plotting the Results
====================

You can visualize the results of your previous examples by generating a plot that show the depth, number of gates, and number of CX gates of your quantum circuits.

.. note::
When you set the ``optimization_level`` to 3, it is important to consider that the number of two-qubit gates decreases, while the number of one-qubit gates increases.
You can observe that the number of two-qubit gates (:class:`.CXGate` gates) is significantly reduced compared to other optimization levels.

.. testcode::

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
my_xticks = [str(i) for i in range(4)]
plt.xticks(range(4), my_xticks)
ax.plot(
range(4),
[qc_b0.depth(), qc_b1.depth(), qc_b2.depth(), qc_b3.depth()],
label="Depth",
marker="o",
color="#6929C4",
)
ax.plot(
range(4),
[qc_b0.size(), qc_b1.size(), qc_b2.size(), qc_b3.size()],
label="Number of gates",
marker="o",
color="blue",
)
ax.plot(
range(4),
[
qc_b0.num_nonlocal_gates(),
qc_b1.num_nonlocal_gates(),
qc_b2.num_nonlocal_gates(),
qc_b3.num_nonlocal_gates(),
],
label="Number of non local gates",
marker="o",
color="green",
)

ax.set_title("Impact of the optimization level on backend ibmq_quito")
ax.set_xlabel("Optimization Level")
ax.set_ylabel("Count")
plt.legend(bbox_to_anchor=(0.75, 1.0))


.. plot::

import matplotlib.pyplot as plt
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.compiler import transpile
from qiskit.providers.fake_provider import FakeQuitoV2
import numpy as np

backend = FakeQuitoV2()

qc = QuantumCircuit(3) # Initialize the quantum circuit with 3 qubits.

qc.cswap(0,1,2) # Add the cswap gate to the quantum circuit.

qc0 = transpile(qc,backend=backend,optimization_level=0)
qc1 = transpile(qc,backend=backend,optimization_level=1)
qc2 = transpile(qc,backend=backend,optimization_level=2)
qc3 = transpile(qc,backend=backend,optimization_level=3)


fig, ax = plt.subplots()
my_xticks = [str(i) for i in range(4)]
plt.xticks(range(4), my_xticks)
ax.plot(
range(4),
[qc0.depth(), qc1.depth(), qc2.depth(), qc3.depth()],
label="Depth",
color="#6929C4",
marker="o",

)
ax.plot(
range(4),
[qc0.size(), qc1.size(), qc2.size(), qc3.size()],
label="Number of gates",
color="blue",
marker="o",

)
ax.plot(
range(4),
[
qc0.num_nonlocal_gates(),
qc1.num_nonlocal_gates(),
qc2.num_nonlocal_gates(),
qc3.num_nonlocal_gates(),
],
label="Number of non local gates",
marker="o",

)

ax.set_title("Impact of the optimization level on backend ibmq_quito")
ax.set_xlabel("Optimization Level")
ax.set_ylabel("Count")
plt.legend(bbox_to_anchor=(0.75, 1.0))