The Lifecycle of a Program
Last updated
Last updated
QCS is designed to give you flexibility in how you write, compile, and execute your programs. While our clients and services streamline much of this process, it can be important to understand these details and how they may affect your approach to writing, compiling, and executing your programs. This guide will cover the different features your programs can use, how they can be compiled, how they are executed, and how each step in that process could affect the next.
When you prepare to run a program on QCS, you will ultimately submit a Quil program. You can do this by writing Quil directly, using an SDK like pyQuil, or transpiling to Quil from another language.
Transpilation to Quil is currently supported from
QIR via qcs-sdk-qir.
QASM2 via quilc or by pyQuil.
Cirq via cirq-rigetti.
Regardless of how you end up with your Quil program, it's important to know whether it's a gate-model program that uses pure Quil, a pulse-model program that uses Quil-T instructions, or a mixture of the two. Because Quil-T isn't supported by the Quil compiler (quilc
) or by the QVM, you may have to take measures to sidestep quilc
or filter these instructions before submitting a program to run on the QVM. Furthermore, Quil-T programs must be nativized for the target QPU by hand, since quilc
typically performs nativization to gate model programs.
Quil Feature | quilc | QVM | QPU |
---|---|---|---|
Quil | Mostly, requires nativization by hand or by | ||
Quil-T |
To learn more about Quil, and the differences between Quil and Quil-T, see What is Quil?.
Gate-model and pulse-model represent two approaches to Quantum computing.
A Gate-model program is defined by applying quantum gates to qubits. This is analogous to classical computing, where classical gates are used to manipulate bits.
A pulse-model program uses instructions that directly control the hardware using analogue pulses. While pulse-model programs are lower-level and more hardware specific, direct control over the pulses give programs more flexibility and ability to perform highly specific optimizations not easily possible with pure gate-model programs.
Quil and Quil-T allows you to use both of these approaches in a single program. Mixed programs can be executed using QCS and Rigetti QPUs, we'll cover this in more detail in the following sections.
In some cases, you may decide to use DELAY
or other Quil-T instructions in your program, but still want to validate that your program works on a QVM as preparation to submit it on a live QPU. This is a good practice! In many cases, you can filter out the Quil-T instructions without otherwise impacting the semantics of a program on a QVM. Luckily, there are some tools in our clients and SDKs that allow you to do this easily:
quil-rs: Program.filter_instructions() and Instruction.is_quil_t().
quil-py: Program.filter_instructions() and Instruction.is_quil_t().
With your Quil program at the ready, you can move on to compilation. There are two compilers you'll commonly use, each with a different set of requirements for input as well as output.
The first possible compilation step is quilc
. quilc
will perform advanced optimization for a program to target a specific, characterized hardware device. It works on pure gate-model programs only, and should generally be used to compile such programs. However, as described above, programs containing pulse-model Quil-T instructions are incompatible with quilc
, so nativization and optimization must be done by hand for these programs.
The QCS Translation Service is a compiler that turns Quil into machine code that the control systems for the QPU can understand. As such, all programs must be translated into an executable by the QCS translation service if they are to be run on a live QPU.
The QCS Translation Service can accept programs containing both gate-model and pulse-model instructions. However, programs must contain Quil native to the device. In many cases, you can use quilc
to nativize a program for you. However, if you aren't using quilc
, (e.g. because your program contains Quil-T instructions), then you must ensure your program only uses available frames and that any gate-model instructions are native to the device. The available frames and native gates can be determined by checking the QPU's calibration program.
A calibration program is itself a Quil program that defines calibrations that describe how to decomopose Quil instructions into an equivalent series of analogue pulse instructions to execute on the QPU. The calibration program for a QPU is carefully constructed to tune various parameters that maximizes for execution quality. By default, QCS Translation Service will apply these calibrations to your program after submission and use them to decompose your program into a pure pulse-model program.
When submitting a program to the QCS Translation Service you can optionally provide options that configure how your program is compiled:
pyQuil: QPUCompilerAPIOptions.
qcs-sdk-rust: ExecutionOptions.
qcs-sdk-python: ExecutionOptions.
After the QCS Translation Service compiles your program, the resulting machine code is encrypted and cannot be inspected nor modified by the user. This encrypted job must be used as is for execution. Compilation via the QCS Translation Service is not necessary nor compatible with QVM.
To summarize, which compilation steps you use depend on both what type of Quil program you have and whether you want to run your program on a QPU or QVM.
Compilation Step | Gate-model programs | Pulse-model programs |
---|---|---|
quilc | 🔵 Optional. | |
QCS Translation Service |
Once your program has been written and sufficiently compiled, it's time to execute your program and get some results!
If you are targeting QVM execution, then you know that you should have a gate-model program using pure Quil. If you are on your JupyterLab IDE, then the QCS server is running in the background and ready to take your submissions via SDKs like pyQuil.
If you are targeting a live QPU, then you have a Quil program nativized by hand or by quilc
, and you've compiled the program into a QPU-ready executable via the QCS Translation Service.
You have a couple options for submitting a program, and which you choose depends on your use case. See Access a QPU for a detailed overview of the options available to you.
When you submit your job for execution on a QPU, you can connect to the QPU in one of two ways:
QPU Endpoint
A QPU Endpoint is a collection of services that enables you to run a job on a QPU. An endpoint is the most direct way to submit jobs to a QPU.
QCS Gateway
The QCS QPU Gateway is a service that operates on top of QPU endpoints to provide high-availability for submitted jobs. While service disruptions to an endpoint should be rare, the QCS Gateway will ensure your job doesn't get lost when those disruptions occur. See the QCS QPU Gatewaydocumentation for more information, including when you should or shouldn't use it.
In most Rigetti clients, the QCS Gateway is the default connection strategy. If you want to override the connection strategy, see the docs in your client for more info:
pyQuil: QCS Gateway and Execution Options.
qcs-sdk-rust: Execution Options.
qcs-sdk-python: ExecutionOptions.
Besides how you submit your job to each target, there are important differences in how they run your program and how data is returned to you. See QPU vs. Simulator (QVM)for an in-depth dive on these differences.
Never, Quil-T is incompatible with quilc
and must be nativized manually.
Always
Always
Quil-T is not compatible with QVM.
Always skip. Not necessary for QVM execution and it will either error or return a object incompatible with the QVM.
Quil-T is not compatible with QVM.
After your job has been run, you'll get back a set of results with the readout data your program generated. In many cases, you'll be able to generate a rectangular matrix for each readout register in your program. However, readout data is another area where the QVM and QPU can have different behaviors. The guide to covers these differences in detail.