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

Remove transpilation hook #884

Merged
merged 3 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 3 additions & 3 deletions doc/source/main-documentation/qibolab.rst
Original file line number Diff line number Diff line change
Expand Up @@ -659,11 +659,11 @@ This procedure typically involves the following steps:
2. All gates are transpiled to native gates, which represent the universal set of gates that can be implemented (via pulses) in the chip.
3. Native gates are compiled to a pulse sequence.

The transpilation and compilation process is taken care of automatically by the :class:`qibolab.backends.QibolabBackend` when a circuit is executed, using circuit transpilers provided by Qibo and :class:`qibolab.compilers.compiler.Compiler`.
The transpiler is responsible for steps 1 and 2, while the compiler for step 3 of the list above.
For more information on the transpiler please refer the `examples in the Qibo documentation <https://qibo.science/qibo/stable/code-examples/advancedexamples.html#how-to-modify-the-transpiler>`_.
To be executed in Qibolab, a circuit should be already transpiled. It possible to use the transpilers provided by Qibo to do it. For more information, please refer the `examples in the Qibo documentation <https://qibo.science/qibo/stable/code-examples/advancedexamples.html#how-to-modify-the-transpiler>`_.
On the other hand, the compilation process is taken care of automatically by the :class:`qibolab.backends.QibolabBackend`.

Once a circuit has been transpiled, it is converted to a :class:`qibolab.pulses.PulseSequence` by the :class:`qibolab.compilers.compiler.Compiler`.
Once a circuit has been compiled, it is converted to a :class:`qibolab.pulses.PulseSequence` by the :class:`qibolab.compilers.compiler.Compiler`.
This is a container of rules which define how each native gate can be translated to pulses.
A rule is a Python function that accepts a Qibo gate and a platform object and returns the :class:`qibolab.pulses.PulseSequence` implementing this gate and a dictionary with potential virtual-Z phases that need to be applied in later pulses.
Examples of rules can be found on :py:mod:`qibolab.compilers.default`, which defines the default rules used by Qibolab.
Expand Down
2 changes: 1 addition & 1 deletion doc/source/tutorials/circuits.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ Returns the following plot:
:class: only-dark

.. note::
Executing circuits using the Qibolab backend results to automatic application of the transpilation and compilation pipelines (:ref:`main_doc_compiler`) which convert the circuit to a pulse sequence that is executed by the given platform.
Executing circuits using the Qibolab backend results to automatic application of the compilation pipeline (:ref:`main_doc_compiler`) which convert the circuit to a pulse sequence that is executed by the given platform.
It is possible to modify these pipelines following the instructions in the :ref:`tutorials_compiler` example.

QASM Execution
Expand Down
23 changes: 8 additions & 15 deletions doc/source/tutorials/compiler.rst
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
.. _tutorials_compiler:

How to modify the default circuit transpilation.
================================================
How to modify the default circuit compilation
=============================================

A Qibolab platform can execute pulse sequences.
As shown in :ref:`tutorials_circuits`, Qibo circuits can be executed by invoking the :class:`qibolab.backends.QibolabBackend`, which is the object integrating Qibolab to Qibo.
When a Qibo circuit is executed, the backend will automatically transpile and compile it to a pulse sequence, which will be sent to the platform for execution.
When a Qibo circuit is executed, the backend will automatically compile it to a pulse sequence, which will be sent to the platform for execution.
The default compiler outlined in the :ref:`main_doc_compiler` section will be used in this process.
In this tutorial we will demonstrate how the user can modify this process for custom applications.

Expand All @@ -27,14 +27,14 @@ Creating an instance of the backend provides access to these objects:

The transpiler is responsible for transforming any circuit to one that respects
the chip connectivity and native gates. The compiler then transforms this circuit
to the equivalent pulse sequence. Note that there is no default transpiler, therefore
to the equivalent pulse sequence. Note that there is no transpiler in Qibolab, therefore
the backend can only execute circuits that contain native gates by default.
The user can modify the transpilation and compilation process by changing
the ``transpiler`` and ``compiler`` attributes of the ``QibolabBackend``.
The user can modify the compilation process by changing the ``compiler`` attributes of
the ``QibolabBackend``.

In this example, we executed circuits using the backend ``backend.execute_circuit`` method,
unlike the previous example (:ref:`tutorials_circuits`) where circuits were executed directly using ``circuit(nshots=1000)``.
It is possible to perform transpiler and compiler manipulation in both approaches.
It is possible to perform compiler manipulation in both approaches.
When using ``circuit(nshots=1000)``, Qibo is automatically initializing a ``GlobalBackend()`` singleton that is used to execute the circuit.
Therefore the previous manipulations can be done as follows:

Expand All @@ -52,8 +52,6 @@ Therefore the previous manipulations can be done as follows:

# set backend to qibolab
qibo.set_backend("qibolab", platform="dummy")
# disable the transpiler
GlobalBackend().transpiler = None

# execute circuit
result = circuit(nshots=1000)
Expand All @@ -66,7 +64,7 @@ The compiler can be modified by adding new compilation rules or changing existin
As explained in :ref:`main_doc_compiler` section, a rule is a function that accepts a Qibo gate and a Qibolab platform
and returns the pulse sequence implementing this gate.

The following example shows how to modify the transpiler and compiler in order to execute a circuit containing a Pauli X gate using a single pi-pulse:
The following example shows how to modify the compiler in order to execute a circuit containing a Pauli X gate using a single pi-pulse:

.. testcode:: python

Expand All @@ -93,19 +91,14 @@ The following example shows how to modify the transpiler and compiler in order t
# the empty dictionary is needed because the X gate does not require any virtual Z-phases

backend = QibolabBackend(platform="dummy")
# disable the transpiler
backend.transpiler = None
# register the new X rule in the compiler
backend.compiler[gates.X] = x_rule

# execute the circuit
result = backend.execute_circuit(circuit, nshots=1000)

Here we completely disabled the transpiler to avoid transforming the X gate to a different gate and we added a rule that instructs the compiler how to transform the X gate.

The default set of compiler rules is defined in :py:mod:`qibolab.compilers.default`.

.. note::
If the compiler receives a circuit that contains a gate for which it has no rule, an error will be raised.
This means that the native gate set that the transpiler uses, should be compatible with the available compiler rules.
If the transpiler is disabled, a rule should be available for all gates in the original circuit.
25 changes: 2 additions & 23 deletions src/qibolab/backends.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from collections import deque
from typing import Callable, Optional

import numpy as np
from qibo import __version__ as qibo_version
Expand Down Expand Up @@ -29,28 +28,13 @@ def __init__(self, platform):
"qibolab": qibolab_version,
}
self.compiler = Compiler.default()
self.transpiler: Optional[Callable] = None

def apply_gate(self, gate, state, nqubits): # pragma: no cover
raise_error(NotImplementedError, "Qibolab cannot apply gates directly.")

def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover
raise_error(NotImplementedError, "Qibolab cannot apply gates directly.")

def transpile(self, circuit):
"""Applies the transpiler to a single circuit.

This transforms the circuit into proper connectivity and native
gates.
"""
# TODO: Move this method to transpilers
if self.transpiler is None or self.transpiler.is_satisfied(circuit):
native = circuit
qubit_map = {q: q for q in range(circuit.nqubits)}
else:
native, qubit_map = self.transpiler(circuit) # pylint: disable=E1102
return native, qubit_map

def assign_measurements(self, measurement_map, readout):
"""Assigning measurement outcomes to
:class:`qibo.states.MeasurementResult` for each gate.
Expand Down Expand Up @@ -92,8 +76,7 @@ def execute_circuit(self, circuit, initial_state=None, nshots=1000):
"Hardware backend only supports circuits as initial states.",
)

native_circuit, qubit_map = self.transpile(circuit)
sequence, measurement_map = self.compiler.compile(native_circuit, self.platform)
sequence, measurement_map = self.compiler.compile(circuit, self.platform)

if not self.platform.is_connected:
self.platform.connect()
Expand Down Expand Up @@ -136,12 +119,8 @@ def execute_circuits(self, circuits, initial_state=None, nshots=1000):
)

# TODO: Maybe these loops can be parallelized
native_circuits, _ = zip(*(self.transpile(circuit) for circuit in circuits))
sequences, measurement_maps = zip(
*(
self.compiler.compile(circuit, self.platform)
for circuit in native_circuits
)
*(self.compiler.compile(circuit, self.platform) for circuit in circuits)
)

if not self.platform.is_connected:
Expand Down
Loading