Pipelines
The primary way to access QAT’s compilation and execution capabilities is through its pipelines API. Pipelines allow us to define the compilation and execution process in a configurable and modular way.
Using QAT pipelines
QAT has a number of pipelines that are pre-defined and ready to use. We start by creating a
a QAT
object.
from qat import QAT
core = QAT()
We can now add a pipeline to it. Let’s add a pipeline that uses the
EchoEngine
:
from qat.pipelines.echo import echo8
core.pipeline.add(echo8, default=True)
This will add the “echo8” pipeline to core
, which can be used to compile and execute
programs using a simulator that simply returns all readouts as zeroes. The “8” in “echo8”
specifies that the simulator has 8 qubits available. The pipeline already has its
compilation modules chosen so that it is compatible with the echo engine: we’ll cover this
in more detail in Compilation. Now we can use this to execute a QASM program.
1from qat import QAT
2from qat.pipelines.echo import echo8
3from compiler_config.config import CompilerConfig, QuantumResultsFormat
4
5core = QAT()
6core.pipelines.add(echo8, default=True)
7
8qasm_str = """
9OPENQASM 2.0;
10include "qelib1.inc";
11qreg q[2];
12creg c[2];
13h q[0];
14cx q[0], q[1];
15measure q -> c;
16"""
17
18config = CompilerConfig(results_format=QuantumResultsFormat().binary_count())
19results, metrics = core.run(qasm_str, config, pipeline="echo8")
There’s a couple of things to unpack here.
The program
qasm_str
describes a simple QASM2 program to create a bell state on two qubits.Finally, you will note that
metrics
is returned. This is an object that contains various metrics regarding compilation, such as circuits after they have been optimized, or the total number of pulse-level instructions. Since the echo pipeline just returns zeros for readouts, the result returned here isresults = {'c': {'00': 1000}}
.
We could also achieve the same workflow by compiling and executing separately:
pkg, metrics = core.compile(qasm_str, config, pipeline="echo8")
results, metrics = core.execute(pkg, config, pipeline="echo8")
The package pkg
contains the native instructions to be sent to the target (in this
case, the echo simulator).
Default pipelines that are available in QAT
There are a number of pipelines in QAT that are available to use off-the-shelf.
qat.pipelines.echo
: Pipelines that execute using theEchoEngine
. The pipelines available by default areecho8
,echo16
,echo32
. For a custom amount of qubits, the methodget_pipeline
can be used.
There are also pipelines that use legacy hardware and engines, but wrapped in the new pipeline API:
qat.pipelines.legacy.echo
: Pipelines that execute using the legacyEchoEngine
. The pipelines available by default arelegacy_echo8
,legacy_echo16
,lgeacy_echo32
. For a custom amount of qubits, the methodget_pipeline
can be used.qat.pipelines.legacy.rtcs
: Pipelines that execute using the legacyRealtimeChipSimEngine
. The only available pipeline is for two qubits,legacy_rtcs2
.qat.pipelines.legacy.qiskit
: Pipelines that execute using the legacyQiskitEngine
. The available pipelines arelegacy_qiskit8
,legacy_qiskit16
andlegacy_qiskit32
. For a custom amount of qubits, the methodget_pipeline
can be used.
Defining custom pipelines
Pipelines in QAT are highly customisable to allow for diverse compilation behaviour for a range of targets, such as live hardware or custom simulators. Compilation is broken down into three parts: the frontend, the middleend and the backend. We will not go into the details of each module here, but they will be covered in Compilation. Similarly,the execution part of the pipeline is defined by two objects: the engine and the runtime. The engine acts as an adapter to the target, and deals with communicating the instructions and results from the runtime to the target. The runtime handles the the engine, and deals with software post-processing of the results. See Execution for more details.
Let us quickly show how to define a custom pipeline by recreating the “echo8” pipeline.
1from qat import QAT
2from qat.core.pipeline import Pipeline
3from qat.frontend.frontends import DefaultFrontend
4from qat.middleend.middleends import DefaultMiddleend
5from qat.backend.waveform_v1 import WaveformV1Backend
6from qat.engines.waveform_v1 import EchoEngine
7from qat.runtime import SimpleRuntime
8from qat.model.loaders.legacy import EchoModelLoader
9from compiler_config.config import CompilerConfig, QuantumResultsFormat
10
11model = EchoModelLoader(8).load()
12new_echo8 = Pipeline(
13 name="new_echo8",
14 frontend=DefaultFrontend(model),
15 middleend=DefaultMiddleend(model),
16 backend=WaveformV1Backend(model),
17 runtime=SimpleRuntime(EchoEngine()),
18 model=model
19)
20
21core = QAT()
22core.pipelines.add(new_echo8)
23
24qasm_str = """
25OPENQASM 2.0;
26include "qelib1.inc";
27qreg q[2];
28creg c[2];
29h q[0];
30cx q[0], q[1];
31measure q -> c;
32"""
33
34config = CompilerConfig(results_format=QuantumResultsFormat().binary_count())
35results, metrics = core.run(qasm_str, config, pipeline="new_echo8")
Notice that the EchoEngine
and the
WaveformV1Backend
are both
contained in a waveform_v1
package. This is not by coincidence: the engine has to be
appropriately picked to match the code generated from the backend. There will be more
details on the responsibilities of backends and engines in later sections.
Defining pipelines using a configuration file
So far we have manually imported pipelines and added them to a
QAT
to use the pipeline API QAT.compile
and
QAT.execute
. However, we can specify some default pipelines to use via a
configuration file.
1MAX_REPEATS_LIMIT: 1000
2PIPELINES:
3- name: echo8-alt
4 pipeline: qat.pipelines.echo.echo8
5 default: false
6- name: echo16-alt
7 pipeline: qat.pipelines.echo.echo16
8 default: true
9- name: echo32-alt
10 pipeline: qat.pipelines.echo.echo32
11 default: false
12- name: echo6-alt
13 pipeline: qat.pipelines.echo.get_pipeline
14 hardware_loader: echo6loader
15 default: false
16
17HARDWARE:
18- name: echo6loader
19 loader: qat.model.loaders.legacy.EchoModelLoader
20 init:
21 qubit_count: 6
This file currently allows us to specify the maximum number of shots that can be done for
each job, and a number of pipelines. Notice that the first three pipelines just point to
an already defined pipeline. The fourth points to a function that lets us provide our own
hardware model, which is specified under HARDWARE
.
To use this within QAT, we can simply use the directory of the file to instantiate the
QAT
object.
1from qat import QAT
2qat = QAT(qatconfig="path_to_file.yaml")
3
4qasm_str = """
5OPENQASM 2.0;
6include "qelib1.inc";
7qreg q[2];
8creg c[2];
9h q[0];
10cx q[0], q[1];
11measure q -> c;
12"""
13
14results = qat.run(qasm_str, pipeline="echo6-alt")