Authors: Robert S. Smith; Rigetti & Co. Inc.; and contributors
Language Version: 2021.1 (DRAFT)
Note: This document is in DRAFT STATUS and is for review purposes only. It is being developed by the QuilLang group on GitHub.
This is the language specification for Quil, a language for hybrid classical/quantum computations.
Quil is an instructionbased language; each line of a Quil program generally corresponds to a single, discrete action. Despite its resemblance, Quil is not an assembly language.
Note: An assembly language is a textual format for the machine code of a specific computer architecture. Quil is not that, and may be used for a variety of quantum computer architectures.
This is an example Quil program that simulates a coin flip:
DECLARE ro BIT[1] H 0 MEASURE 0 ro[0]Here, we can see the use of both classical data and quantum data. The qubit numbered 0 is prepared in a uniform superposition by the Hadamard gate H, and then measured in the computational basis, depositing the resulting bit into a classic bit register named ro.
The remainder of this document serves as a reference for all Quil language syntax constructs and their associated semantics.
The Quil language is described throughout the ordinarily named sections of this document. There are additional sections, called annexes, which are optional extensions to the Quil language. These extensions may modify the syntax or semantics of the base specification in noted ways.
Define \(\mathscr{B}\) to be a Hilbert space isomorphic to \(\mathbb{C}^2\). Some texts refer to this space as a qubit. We may refer to it as a qubit space.
We fix any orthonormal basis of \(V := \mathscr{B}^{\otimes n}\) and call it the computational basis. We use uppercase letters to refer to the vector space and the corresponding lowercase indexed letters with an overbar to refer to the computational basis elements (e.g., \(V\) for the vector space and \(\bar v_{*}\) for the basis elements). We order the computational basis as \(\bar v_0\) to \(\bar v_{2^n1}\) so the \(k\)th bit of the index (i.e., the coefficient of \(2^k\)) of \(\bar v_{*}\) corresponds to the \(0\)th or \(1\)st basis of the \(k\)th factor of \(\mathscr{B}\) from the right. For example, let \(n=3\), and let the left, middle, and right tensor factors of \(\mathscr{B}\otimes\mathscr{B}\otimes\mathscr{B}\) be called \(R\), \(S\), and \(T\) respectively. Consider the basis element \(\bar v_4\). This index \(4\) is 100 in binary, and thus \(\bar v_4\) corresponds to \(\bar r_1\otimes \bar s_0\otimes \bar t_0\).
Note: Some texts might write \(v_4\) as either \(\vert 001\rangle\) or \(\vert 100\rangle\). We prefer the latter if we are to use Dirac notation.
Given \(V := \mathscr{B}\), we sometimes call \(\bar v_0\) the ground state or zero state, and \(\bar v_1\) the excited state or one state.
Define \(\mathscr{U}(d)\) to be the projective special unitary group of dimension \(d\).
Note: Note that many texts write "PSU" instead of "U". We will often say "unitary" when we really mean "projective special unitary".
The semantics of Quil are defined operationally; that is, each instruction of a Quil program is described by a change of some state. This state is described by a mathematical object called the "quantum abstract machine".
A quantum abstract machine or QAM is specified as:
In order to describe the syntax and semantics of a Quil program, we use a syntax that is similar to extended BackusNaur form, though we deviate occasionally for convenience. While Quil's grammar is contextfree, specifying it is somewhat laborious due to the possibility of identifiers being able to be used in some syntactic constructs and other times not. As such, for syntactic constructs which permit identifiers, we would need one set of productions, and for those which don't, we would need another (nearly identical) set.
In order to avoid this laborious repetition, we write productions of the grammar to always include sometimes forbidden elements, and instead specify in the surrounding text the context in which those elements are or are not allowed.
Note: When writing a recursivedescent parser, one would likely use contextual flags to allow or disallow certain parsing rules, like flags indicating the permission or lack thereof to use identifiers. This leads to considerably shorter and clearer code.
Before proceeding to describe each component of a Quil program, it will be useful to establish a few common pieces of syntax which will be used later.
The Quil language is represented as text. The text must be encoded as UTF8. The standard language constructs of Quil are all expressible in the ASCII subset of UTF8, but user programs may use codepoints outside of UTF8.
Except when noted explicitly, whitespace has no significance and is ignored. Tokens can be separated by arbitrary amounts and kinds of whitespace.
A newline is a single ASCII newline.
A terminator is used to terminate most components of a Quil program syntactically.
::= 

;
 

An indent is defined as exactly four spaces at the start of a line. Indents in Quil programs can only happen following a newline.
Note that since indents must follow a newline, we include the newline as a part of the syntax definition of an indent.
Nonnegative integers are written as usual. Leading zeros do not change the interpretation of these numeric literals.
[09]
Real numbers are written in usual floatingpoint number syntax.
. e E  +
Complex numbers are an extension of this.
::= 

 
i
 
pi 
Strings are characters bounded by doublequotation mark characters '"'. If a doublequotation mark should be used within the string, it must be escaped with a backslash, like so: '\"'. Similarly, if a backslash should be used within a string, it must be escaped, like so: '\\'.
"
[^\"] \" \\ "
Identifiers in Quil are alphanumeric Latin characters, along with hyphens and underscores. Identifiers cannot start or end with a hyphen ''.
::=  [AZaz_]

[AZaz_][AZaz09\_] [AZaz09_] 
However, the following are not identifiers:
i pi
The following identifiers are reserved (as Quil keywords):
ADD AND AS CONTROLLED CONVERT DAGGER DECLARE DEFCIRCUIT DEFGATE DIV EQ EXCHANGE FORKED GE GT HALT INCLUDE IOR JUMP JUMPUNLESS JUMPWHEN LABEL LE LOAD LT MATRIX MEASURE MOVE MUL NEG NOP NOT OFFSET PAULISUM PERMUTATION PRAGMA RESET SHARING STORE SUB WAIT XOR
The following identifiers are also reserved (for standard gate definitions):
CAN CCNOT CNOT CPHASE CPHASE00 CPHASE01 CPHASE10 CSWAP CZ H I ISWAP PHASE PISWAP PSWAP RX RY RZ S SWAP T X XY Y Z
A formal parameter is an identifier prefixed with a percent sign, with no whitespace in between.
%
Multiple parameters may be separated by commas.
,
A formal argument is simply an identifier.
Several formal arguments are separated by spaces, unlike parameters.
Frequently, various kinds of arithmetic expressions are needed. A simple and not unusual grammar defines these arithmetic expressions.
Depending on the specific grammatical context, arithmetic expressions may or may not include references to formal parameters or memory segments. Below, we define the grammar as including all of these things, but certain contexts may disallow either or both of them.
Precedence of binary operators is defined in the following order of descending tightness. Along with the operators are their associativity directions.
^ RIGHT * / LEFT +  LEFT
::=  + 
  
*  
/  
^  

::=   
( )
 
( )
 
 
 
 

A constant expression is an arithmetic expression which does not contain any parameters, memory references, or identifiers.
We use commaseparated lists of arithmetic expressions frequently enough to warrant their own production.
,
A Quil program consists of declarations, directives, and instructions.
::= 

 

A declaration typically specifies the existence of a named object, like classical memory registers.
::= 

 

A directive specifies information to software processing Quil, such as the INCLUDE directive for including files.
::= 

 

An instruction is an actual runtime executable effect.
::= 

 
 
 
 
 
 
 

Comments may exist syntactically, but do not change the semantics of the program. Text including and following the '#' character are ignored up to the end of the line.
#[^\n]
There are no block comments.
A Quil program manipulates quantum resources called qubits. Qubits are indexed by nonnegative integers.
Qubit indexes have no significance on their own. Qubits must always be referred to by their index. There is no bound on the number of qubits in a Quil program, and any finite collection of qubits may interact.
Quil has no ways to allocate an unbounded or nondeterministic number of qubits at runtime. The number of qubits used by a program can always be statically determined.
Sometimes, a qubit may instead have a formal argument in its place. This may not be possible in all cases.
::= 


A gate definition allows us to name a unitary operation for subsequent use in a program. A gate definition in general has the following structure:
DEFGATE <name>(<params>) AS <kind>: <body>
Here, the <name> names the gate, the <kind> states how we are defining the gate, and the <body> depends on the kind. For certain gates, <params> specifies parameters to the gate.
::= 

 

A gate can be defined by its matrix of complex numbers represented in the computational basis with the aforementioned ordering.
DEFGATE
( ) AS MATRIX :
A gate definitions with no parameters represents a static gate. Otherwise it is a parametric gate.
For readability, matrix entries are typically broken up into lines.
Each line contains a list of commaseparated arithmetic expressions, most often simple integers or real numbers.
,
The arithmetic expressions may either be constant or refer to the parameters of the defined gate.
A permutation gate is one that permutes the coefficients of the wavefunction. A permutation \(p\) can be specified mathematically as an ordering of the integers between \(0\) and \(n1\) written out as a list \[(p_1\;p_2\;\ldots\;p_n).\] Here, \(p\) is a map on vectors such that if \(x\) is a column vector \[(x_1,\ldots,x_n)^{\intercal}\] then \(p(x)\) is a column vector \[(x_{p_1}, x_{p_2}, \ldots, x_{p_n})^{\intercal}.\] We specify permutation gates exactly by these numbers.
DEFGATE
AS PERMUTATION:
Here, the permutation is a commaseparated list of nonnegative integers.
,
There must be at least two integers specified, and the number of integers specified must be a poweroftwo.
Quantum gates can be defined by an associated Hamiltonian, as in \[U(\mathbf t) = \exp(i \mathcal H(\mathbf t))\] for a Hermitian operator \(\mathcal H\). Quil allows \(\mathcal H\) to be specified as a Pauli sum, a sum of combinations of Pauli operators.
The syntax is as follows.
DEFGATE
( )
AS PAULISUM:
The collection of Pauli term must be written on their own lines.
Each Pauli term represents some coefficient multiplied against a tensor product of Pauli operators.
( )
A PAULISUM gate definition is subject to some restrictions:
I X Y Z
We describe how one is intended to extract a matrix presentation of an operator from such a Pauli sum, and then we remit the discussion of semantics to that for
.See the next sections for an example semantic reduction.
Many standard examples of gates admit short expression in these terms:
DEFGATE RY(%theta) q AS PAULISUM: Y(%theta/2) qThis also includes many standard multiqubit operators:
DEFGATE CPHASE(%theta) p q AS PAULISUM: ZZ(%theta/4) p q Z(%theta/4) p Z(%theta/4) q DEFGATE CAN(%alpha, %beta, %gamma) p q AS PAULISUM: XX(%alpha/4) p q YY(%beta/4) p q ZZ(%gamma/4) p qIt also includes some operators encountered in practice, e.g., the following reduction of an Ansatz appearing in the electronic structure simulation problem for \(\mathrm H_2\):
DEFGATE UCCH2(%theta) p q r s AS PAULISUM: YXXX(%theta) p q r s
In the example definition of CPHASE above, these steps proceed as follows:
Note that one has the following equivalences in Quil:
CZ == CONTROLLED Z CPHASE = CONTROLLED PHASE
Note: The gate CCNOT is sometimes known as the Toffoli gate. It is a universal classical logic gate.
Note that one has the following equivalences in Quil:
CNOT == CONTROLLED X CCNOT == CONTROLLED CONTROLLED X
Note: The gate CSWAP is sometimes known as the Fredkin gate. It is a universal classical logic gate.
Note that one has the following equivalence in Quil:
CSWAP == CONTROLLED SWAP
Note: Every twoqubit gate can be written in terms of CAN, possibly with up to two preceding and two proceeding arbitrary onequbit gates.
The definition of CAN can be written as the following Quil matrix definition:
DEFGATE CAN(%alpha, %beta, %gamma): (cis((%alpha+%beta%gamma)/2)+cis((%alpha%beta+%gamma)/2))/2, 0, 0, (cis((%alpha%beta+%gamma)/2)cis((%alpha+%beta%gamma)/2))/2 0, (cis((%alpha+%beta+%gamma)/(2))+cis((%beta+%gamma%alpha)/2))/2, (cis((%alpha+%beta+%gamma)/(2))cis((%beta+%gamma%alpha)/2))/2, 0 0, (cis((%alpha+%beta+%gamma)/(2))cis((%beta+%gamma%alpha)/2))/2, (cis((%alpha+%beta+%gamma)/(2))+cis((%beta+%gamma%alpha)/2))/2, 0 (cis((%alpha%beta+%gamma)/2)cis((%alpha+%beta%gamma)/2))/2, 0, 0, (cis((%alpha+%beta%gamma)/2)+cis((%alpha%beta+%gamma)/2))/2It also has a straightforward definition as a PAULISUM.
A gate is applied via the following syntax.
(
)
We refer to Section II.A of A Practical Quantum Instruction Set Architecture for extensive and detailed mathematical discussion on the semantics of gate application realized as a matrix operator on the Hilbert space of the QAM.
Quil supports three kinds of unitary modifiers.
::=  DAGGER

CONTROLLED
 
FORKED( ) 
These are described in the next sections.
The DAGGER modifier represents the adjoint operation or complexconjugate transpose. Since every gate is a unitary operator, this is just the inverse. For example, if G is a gate described by the onequbit operator \[\begin{pmatrix} a & b\\ c & d \end{pmatrix}\] then DAGGER G is \[\begin{pmatrix} a^* & c^*\\ b^* & d^* \end{pmatrix}\] where \(z^*\) is the complexconjugate of \(z\).
Because DAGGER is the inverse, the sequence of Quil instructions
G q1 ... qn DAGGER G q1 ... qnacts as an identity gate. As another example, consider the gate PHASE, which is defined as
DEFGATE PHASE(%alpha): 1, 0 0, cis(%alpha)where \[\operatorname{cis}\alpha := \cos\alpha + i \sin\alpha = e^{i\alpha}.\] Then
DAGGER PHASE(t) qis equivalent to
PHASE(t) qfor all \(\mathtt{t}\in\mathbb{R}\).
The CONTROLLED modifier takes some gate G acting on some number of qubits q1 to qn and makes it conditioned on the state of some new qubit c. In terms of the matrix representation, if c is in the onestate, then G is applied to the remaining qubits; and if c is in the zerostate, no operation is applied. Therefore, an application of the \(n\)qubit operator G as in
G q1 ... qnhas the controlled variant with CONTROLLED G an (n+1)qubit operator:
CONTROLLED G c q1 ... qn
For example, the gate CONTROLLED X 1 0 is the familiar controllednot gate, which can also be written using the standard builtin Quil gate CNOT 1 0.
Specifically, when acting on a gate G that can be represented as an \(N \times N\) matrix \(U\), CONTROLLED G produces a gate G' described by the \(2N \times 2N\) matrix \(C(U)\) such that \(C(U) := I \oplus U\), where \(I\) is the \(N \times N\) identity matrix and \(\oplus\) is a direct sum. For example, if \(U\) is the onequbit operator \[\begin{pmatrix} a & b \\ c & d \end{pmatrix}\] then \(C(U)\) is \[\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & a & b \\ 0 & 0 & c & d \\ \end{pmatrix}.\]
Let G be a parametric gate of \(k\) parameters r1 to rk and \(n\) qubits q1 to qn. This is written:
G(r1, ..., rk) q1 ... qnNext, consider a second set of \(k\) parameters s1 to sk. The FORKED modifier takes such a gate G and allows either set of parameters to be used conditioned on an additional qubit c.
FORKED G(r1, ..., rk, s1, ..., sk) c q1 ... qnRoughly speaking, in terms of the matrix representation of the operator, this is equivalent to the pseudocode:
if c = 0: G(r1, ..., rk) q1 ... qn else if c = 1: G(s1, ..., sk) q1 ... qn
Note: It is very important to note that both CONTROLLED and FORKED are purely quantum, unitary operations. There is no "actual" conditional branching. The use of the above pseudocode is to illustrate how one might write the matrix representation in the standard computational basis.
For example, the builtin gate RX takes a single %theta parameter and acts on a single qubit, like so RX(pi/2) 0. Therefore, FORKED RX(pi/2, pi/4) 1 0 produces a "forked" version of RX, conditioned on qubit 1. If qubit 1 is in the zerostate, this corresponds to RX(pi/2) 0 and to RX(pi/4) 0 if qubit 1 is in the onestate.
In general, when acting on a parametric gate G of \(k\) parameters that can be represented as an \(N \times N\) matrix \[U(p_1,\ldots,p_k),\]FORKED G produces a \(2N \times 2N\) matrix \[F(U)(p_1,\ldots,p_k,p_{k+1},\ldots,p_{2k}) := U(p_1,\ldots,p_k) \oplus U(p_{k+1},\ldots,p_{2k}),\] where \(\oplus\) is the direct sum.
For example, the gate RZ is defined as
DEFGATE RZ(%theta): cis(%theta/2), 0 0, cis(%theta/2)Therefore, FORKED RZ(\(\theta_0\), \(\theta_1\)) 1 0, for real numbers \(\theta_0\) and \(\theta_1\) results in a twoqubit operator that can be described by the matrix \[\begin{pmatrix} \operatorname{cis}(\theta_0/2) & 0 & 0 & 0 \\ 0 & \operatorname{cis}(\theta_0/2) & 0 & 0 \\ 0 & 0 & \operatorname{cis}(\theta_1/2) & 0 \\ 0 & 0 & 0 & \operatorname{cis}(\theta_1/2) \\ \end{pmatrix}.\]
When gate modifiers are chained, they consume qubits lefttoright, so that in the following example, the CONTROLLED modifier is conditioned on qubit 0, FORKED on qubit 1, and the gate G acts on qubit 2.
CONTROLLED FORKED DAGGER G 0 1 2    ^ ^ ^         ++  ++ ++
Note that chaining multiple FORKED modifiers causes the numbers of parameters consumed by the gate to double for each additional FORKED. For example:
RX(pi) 0 FORKED RX(pi, pi/2) 1 0 FORKED FORKED RX(pi, pi/2, pi/4, pi/8) 2 1 0You can think of that last example as representing the following decision tree, where an edge label like q2=0 means that qubit 2 is in the zero state.
++  FORKED FORKED RX(pi, pi/2, pi/4, pi/8) 2 1 0   / \   q2=0 q2=1   / \   FORKED RX(pi, pi/2) 1 0 FORKED RX(pi/4, pi/8) 1 0   / \ / \   q1=0 q1=1 q1=0 q1=1   / \ / \   RX(pi) 0 RX(pi/2) 0 RX(pi/4) 0 RX(pi/8) 0  ++
The quantum state may be reset to \(\bar \psi_0\) (i.e., all qubits in the ground state) by issuing a reset instruction.
RESET
Instead, one may reset an individual qubit.
RESET
This has the semantics corresponding to the following pseudocode:
if 1 == MEASURE(qubit) then X(qubit)
These make up the ways the quantum state may be reset.
::= 


This section explains Quil's classical memory model.
In assembly code, in general, types are considered only at the mnemonic or interpretation level. They're not often a consideration in the code itself. (Though this is not always true, machine codes for dynamic languages included the notion of type checking and type tags at the instruction level.) On modern processor architectures, one has a large random access memory (RAM) which is byteaddressable, and a series of processor registers that hold usually one word's worth of data. (A word is often some multiple of bytes, usually 4 or 8.) A register machine is one that loads values from memory to the registers, does some operation, and often stores the results back into memory.
So far, we've spoken only of bytes or multiples thereof. A byte—or word for that matter—is simply a measure of a number of bits, with no additional attached interpretation. What gives a byte interpretation is the literal machinery attached to the registers in which the bytes are stored. Implicit in the machinery, usually electrical circuitry, is a way of transforming bytes into a new ones. This machinery is invoked with an opcode. Since opcodes relate to physical machinery, opcodes are often only pertinent to a subset of registers that a machine has. From here, we get a usual partitioning of registers: registers that deal with general integer arithmetic, registers that deal with floating point numbers, registers that deal with vectorized lowprecision arithmetic, registers that interact with main memory, and so on. The partition isn't always so strict; general purpose registers often are capable of many disparate operations.
RAM more often than not lacks any serious kind of operation except loading and storing. Similarly, crossregister opcodes also deal with the movement of data and not operation on the data contained within. When we want to do something like adding an integer and floating point number, we have to put the integer into a floating point representation, move it from an integer register to a floating point register, and perform the addition across two floating point registers. Some architectures, such as the x87 floating point unit, can perform the representationchanging and loading in a single instruction (e.g., the FILD instruction).
Quil was designed to be an instruction language that doesn't conform to any physical architecture. It was designed to accommodate evolving quantum architectures in terms of their memory models and their native gates. In some sense, Quil can be seen as a portable bytecode of sorts, (currently) without an actual bytecode representation.
The original 2016 Quil paper assumes there is an unbounded classical memory composed of a series of bits, and segments of these bits can be interpreted as a real or complex number. While very simplistic, it has a few flaws:
In the remainder of this section, we describe a replacement for the notion of classical data in Quil. It is similar to C in that we don't select any particular memory model, and require the user to specify what he or she requires in terms of layout. Similarly departing from usual instruction sets, we allow for memory to be interpreted through multiple type lenses. In C, we accomplish this by casting pointers and dereferencing. Since we don't have a notion of pointers, we accomplish this with explicit declaration and aliasing.
With Quil's classical memory model, we can write code which does the following:
DECLARE count INTEGER DECLARE stats INTEGER DECLARE measurement INTEGER DECLARE angle REAL DECLARE cond BIT # Initialize MOVE stats 0 MOVE angle 0.0 # Start the angle loop LABEL @start_angle_loop LT cond angle 6.283185307179586 JUMPUNLESS @end cond # Perform histogram loop, 1000 shots MOVE count 1000 LABEL @stats_loop RX(angle) 0 MEASURE 0 measurement ADD stats measurement SUB count 1 GT cond count 0 JUMPWHEN @stats_loop cond # Calculate next angle ADD angle 0.3926990816987241 # pi/8 JUMP @start_angle_loop LABEL @end
This will be roughly equivalent to the following C program:
int count, stats, measurement; float angle; stats = 0; for(angle = 0.0; angle < 6.283185307179586; angle += 0.3926990816987241) { for(count = 1000; count > 0; count) { RX(angle) 0 measurement = MEASURE 0 stats += measurement } }
The supported types are BIT which represents one bit, OCTET which represents 8 bits, INTEGER which represents a machinesized signed integer, and REAL which represents a machinesized real number. The formats/layouts of these are specific to the machine being run on.
::=  BIT

OCTET
 
INTEGER
 
REAL 
When we speak of size, we mean the number of octets that a type represents. The notion of size is distinct from length, which instead refers to some count of elements of a particular type.
A fixedlength vector of a type is denoted by the type name followed by an integer in brackets. For instance, REAL[5] is a type that represents five real numbers in sequence. The type INTEGER is guaranteed to be large enough to hold a valid length of octets, and is guaranteed to hold at least the values \(127\) to \(128\).
::= 

[ ] 
There are currently no provisions for adding additional types.
Quil doesn't have a notion of allocating memory, but rather the notion of declaring the existence of memory. In the following, we introduce the DECLARE directive, which describes available memory for a program to use.
Some quantum computing architectures might restrict what can be declared, what types can be used, what names can be used, etc. It is recommended to be as liberal as possible in what can be declared, while remaining true to the architectural constraints of the system on which Quil is executed.
The DECLARE directive is used to declare a vector of typed memory. There are three variants: plain declaration, aliased declaration, and aliased declaration with offset.
::= 

 

The simplest kind of memory declaration is a plain one.
DECLARE
This declares that
designates memory which can hold . If is a scalar type, then it is assumed to designate a vector of length 1. That is, the following two lines are equivalent:DECLARE x INTEGER DECLARE x INTEGER[1]
In the program that would follow either of these declarations, x or equivalently x[0] will refer to an integer quantity.
DECLARE
_{1} SHARING _{2}
This declares that
_{1} designates memory which can hold , but _{1} shares memory with that which is designated by _{2} . Here, the total memory size pointed to by _{1} shall not exceed the total memory size pointed to by _{2} .An implementation is free to reject programs where particular instances of sharing is invalid (e.g., alignment is violated; disparate memories are unshareable; etc.).
DECLARE
_{1}
SHARING _{2}
OFFSET
_{i} _{i}
This is similar to the aliased declaration, but it allows
_{1} to designate memory in the middle of that which is designated by _{2} . In particular, _{1} will point to memory a total of \[\sum_i \langle\texttt{Integer}_i\rangle\cdot \text{sizeof}(\langle\texttt{Type}_i\rangle)\] bits after the start of _{2} . As with an aliased declaration, the memory at _{1} must not overflow the end of _{2} .Implementations may enforce alignment by way of erroring if the stated declaration is invalid. Implementations must not round up or down to alignment boundaries.
Aliased declarations with mixed types require an intimate view of the target architecture. The widths of each data type, which are hitherto unspecified, must be known. For example, the following declarations may not be valid of the size of REAL exceeds the size of INTEGER.
DECLARE x INTEGER DECLARE y REAL SHARING xEven if such a declaration is valid, operations on y are not portably specified. For example, continuing the above,
DECLARE b BIT MOVE x 0 EQ b y 0.0could result in any value for b, depending on the implementation.
An implementation shall describe the bitlevel description of the types, the available declarable memories, the limits on the declared memory, alignment requirements, and limits on sharing and offsets.
It is an error to declare the same name more than once.
Here we consider a layout for a machine that has one integer register, two real registers, and a condition bit used for doing comparisons and branching.
DECLARE f1 REAL DECLARE f2 REAL DECLARE x INTEGER DECLARE cmp BIT # cmp for "comparison"
This might be suitable for a very simple quantum control system with a single counter for loops.
The following is an example of a memory structure that might be used in a system with a fixed and known memory layout optimized for running QAOAlike circuits.
DECLARE memory OCTET[131072] # 128k global memory DECLARE qaoaparams REAL[32] SHARING memory # all QAOA params DECLARE beta REAL[16] SHARING qaoaparams # beta params DECLARE gamma REAL[16] SHARING qaoaparams OFFSET 16 REAL # gamma params DECLARE ro BIT[16] # readout registers
Here, we have two disjoint memories: the global data memory memory, and the readout memory ro. We see that the global data memory memory is further partitioned into a section qaoaparams specifically for QAOA parameters, which may be useful if you're changing them all at once. Nonetheless, for actual use in Quil code, the actual beta and gamma parameters are carved out of this memory.
This particular scheme may be necessary if software processing Quil does not have any ability generate memory maps automatically. If that functionality were possible, one could simply declare beta, gamma, and ro and let the compilation software take care of mapping that to physical memory.
In algorithms like phase estimation, we compute one bit of the result at a time with each measurement. If our INTEGER data type has the standard binary representation, then one can do:
DECLARE unadjustedtheta INTEGER DECLARE ro BIT[16] SHARING unadjustedtheta DECLARE theta REAL # <phase estimation> MEASURE 0 ro[0] MEASURE 1 ro[1] # ... MEASURE 15 ro[15]
Here, we have a 16bit integer unadjustedtheta with the LSB of our estimated phase starting with qubit 0. (This depends on our convention in our implementation of phase estimation.) Since unadjustedtheta and ro are shared, the bits of ro directly affect the bits of our integer. Recalling that phase estimation gives us a bitstring (in this case, an integer between \(0\) and \(2^{16}  1\)), we must actually adjust it by multiplying by \(2\pi/2^{16}\), which is approximately \(9.587379924285257\times 10^{5}\).
Since theta and unadjustedtheta have different types, we can't quite yet do this multiplication. We need to convert unadjustedtheta into a REAL representation on which we can do fractional arithmetic. We can do this with CONVERT, which in other languages is known as a cast or coercion.
CONVERT theta unadjustedtheta # convert INTEGER to REAL MUL theta theta 9.587379924285257e5
Now we can use theta as an argument to an angle if we please. For example, we might do a phase adjustment based off of that angle on qubit 16:
RZ(theta) 16
Memory is dereferenced in a Quil program using common array access syntax. In particular, given a name x pointing to memory of type \(T\), and a nonnegative integer offset \(n\), the syntax x[\(n\)] refers to the \(n\)th element of type \(T\) indexing off of x[0].
If and only if x was declared with just a single element, then x may be referred to simply by its name with no bracket. In this case, x and x[0] would be equivalent.
::= 

[ ] 
Note: Note that this memory reference is formal. The lone
in certain contexts may refer to a named argument of, for example, a circuit definition.Dereferencing with indirection, e.g., something akin to x[y[3]], is supported through the LOAD and STORE instructions. For example,
DECLARE x INTEGER[16] DECLARE y INTEGER[16] DECLARE z INTEGER[16] DECLARE t INTEGER LOAD t y z[3] # t := y[z[3]] LOAD t x t # t := x[t]
With typed memory comes a bag of new instructions. Classical instructions come in unary (singleargument), binary (doubleargument), and ternary (tripleargument) forms. They all share the same syntax.
::= 

 

The unary instruction names are:
::=  NOT 
NEG 
The binary instruction names are:
::=  MOVE 
EXCHANGE  
CONVERT
 
AND  
IOR  
XOR
 
ADD  
SUB  
MUL  
DIV 
The ternary instruction names are:
::=  LOAD 
STORE  
EQ  
GT  
GE  
LT  
LE 
While the instructions all take memory references, they only take memory references of certain type combinations. Each combination is called an "instruction mode". In the following table, we use the following notation to denote an instruction INSTR and its modes:
# Category of instruction INSTR a b # Pseudocode meaning <type1a> <type1b> <type2a> <type2b> ...
The possibilities for <typeXY> are:
Octet literals share the same syntax as integer literals.
We generally follow the destsrc ordering of arguments.
# Move liketyped data to different locations. # Also allows loading immediate values. MOVE a b # a := b; Store contents of b at a <oct> <!int> <oct> <oct> <int> <!int> <int> <int> <real> <!real> <real> <real> <bit> <!int> <bit> <bit> # Exchange the value at two liketyped locations. EXCHANGE a b # Exchange contents of a and b; a <=> b <oct> <oct> <int> <int> <real> <real> <bit> <bit> # Perform an indirect load from x offset by n to a. LOAD a x n # a := x[n] <oct> <oct*> <int> <int> <int*> <int> <real> <real*> <int> <bit> <bit*> <int> # Perform an indirect store of a to x offset by n. STORE x n a # x[n] := a <oct*> <int> <oct> <oct*> <int> <!int> <int*> <int> <int> <int*> <int> <!int> <real*> <int> <real> <real*> <int> <!real> <bit*> <int> <bit> <bit*> <int> <!int> # Perform a move of differently typed data. # The data here is interpreted numerically. CONVERT a b # a := (T)b, where T = typeof(a) <int> <real> #  Best integer approximation of a real. <int> <bit> #  Convert 0 or 1 to an integer. <real> <int> #  Best real approximation of an integer. <real> <bit> #  Convert 0 or 1 to a real. <bit> <int> #  0 if 0, 1 if nonzero. <bit> <real> #  0 if 0.0, 1 if nonzero # Logical Operations NOT a # a := ~a <oct> <int> <bit> AND a b # a := a & b IOR a b # a := a  b XOR a b # a := a ^ b <oct> <oct> <oct> <!int> <int> <int> <int> <!int> <bit> <bit> <bit> <!int> # Arithmetic Operations NEG a # a := a <int> <real> ADD a b # a := a + b SUB a b # a := a  b MUL a b # a := a * b DIV a b # a := a / b <int> <int> <int> <!int> <real> <!real> <real> <real> # Comparison EQ r a b # r := (a == b) GT r a b # r := (a > b) GE r a b # r := (a >= b) LT r a b # r := (a < b) LE r a b # r := (a <= b) <bit> <bit> <bit> <bit> <bit> <!int> <bit> <oct> <oct> <bit> <oct> <!int> <bit> <int> <int> <bit> <int> <!int> <bit> <real> <real> <bit> <real> <!real>
Measurement is the only way in which the quantum state can affect classical memory. Measurement comes in two flavors: measurementforeffect and measurementforrecord.
Measurementforeffect measures a single qubit and discards the result.
MEASURE
Measurement will stochastically project the qubit into either the zerostate or the onestate depending on its probability of such dictated by the wavefunction amplitudes.
MEASURE
Here, the memory reference must be either of type BIT or INTEGER. In either case, a \(0\) is deposited at the memory location if the qubit was measured to be in the zerostate, and a \(1\) otherwise.
These measurement varieties make up all measurement instructions.
::= 


Note that there is no way in Quil to measure all qubits simultaneously.
The program is halted if it is no longer executing. This may happen under one of three conditions:
HALT
Error conditions may happen, for instance, when a divisionbyzero occurs. There may be other ways in which an implementation may error.
Runtime control flow is achieved through a variety of branching instructions. Each branching instructions requires a target place in the program to jump to. These target places are denoted by labels:
@
LABEL
A label (resp. jump target) is said to be at position \(p < \vert P\vert\) if the first instruction that follows the label (resp. jump target's label) is the \(p\)th instruction (zeroindexed). If no instruction follows the label, then it is said to be a halting label at position \(\vert P\vert\).
Each LABEL jump target name must be unique. It is an error to have a duplicate jump target name in a program.
Note: Jump target names may be duplicated across (but not within) DEFCIRCUIT bodies if and only if those names don't appear globally within the program in which the DEFCIRCUIT is expanded. Names may be duplicated because they are made unique when the circuit is expanded.
One may transfer control to the \(p\)th position of a program by using a JUMP instruction targeting a label at position \(p\).
JUMP
One may transfer control to the \(p\)th position of a program conditional on a given memory reference using one of the following two instructions:
::=  JUMPWHEN 
JUMPUNLESS 
The JUMPWHEN (resp. JUMPUNLESS) instruction branches if and only if
references a BITtyped value that is nonzero (resp. exactly zero).Together, these form the branching instructions.
::= 


The nooperation instruction or NOP instruction is an instruction which does not affect the classical or quantum state of the QAM. It only affects the control state by incrementing the program counter by \(1\).
NOP
Programs that process Quil code may want to take advantage of extra information provided by the programmer. This is especially true when targeting quantum processors where additional information about the machine’s characteristics affect how the program will be processed. Quil supports a PRAGMA directive to include extra information in a program which does not otherwise affect execution semantics.
PRAGMA
One can include a valid Quil file in another valid Quil file by inclusion.
INCLUDE
Here,
denotes a file name. Implementations processing Quil must support and document individual file names, and may support operatingsystemdependent file paths.Circuits in Quil are parameterized templates of instructions that can be filled in with parameters and arguments. Circuit applications within a program are expanded according to the circuit's definition in full before a Quil program is executed.
Note: Circuits are intended to be used more as macros than as specifications for general quantum circuits. Indeed, DEFCIRCUIT is very limited in its expressiveness, only performing argument and parameter substitution. It is included mainly to help with the debugging and human readability of Quil code. Circuits in Quil are more like C preprocessor macros than they are like functions. The QAM has no notion of a circuit as a part of its semantics; circuits are simply notational conveniences.
A circuit is defined with the DEFGATE directive.
DEFCIRCUIT
( )
:
Within the circuit body, we can write any Quil instruction, allowing for the named parameters and arguments to show up as instruction parameters or arguments.
A circuit may be used similarly to a gate:
(
)
Circuits are expanded recursively, outside in. Circuits may not be selfrecursive or mutually recursive. These circuits are invalid because they exhibit different kinds of nonterminating recursion.
DEFCIRCUIT FOO: BAR DEFCIRCUIT BAR: FOO DEFCIRCUIT BAZ: BAZ
Labels that are declared within the body of a DEFCIRCUIT are unique to each of that circuit's expansions. While it is possible to jump out of a DEFCIRCUIT to a globally declared label, it is not possible to jump inside of one.
Consider the following two DEFCIRCUIT declarations and their instantiations. Note the comments on correct and incorrect usages of JUMP.
DEFCIRCUIT FOO: LABEL @FOO_A JUMP @GLOBAL # (A) valid, global label JUMP @FOO_A # (B) valid, local to FOO JUMP @BAR_A # (C) invalid DEFCIRCUIT BAR: LABEL @BAR_A JUMP @FOO_A # (D) invalid LABEL @GLOBAL FOO BAR JUMP @FOO_A # (E) invalid JUMP @BAR_A # (F) invalidLine (A) is valid because it is a jump from a circuit to a global label called @GLOBAL. Line (B) is valid because it is a jump to a label within the same circuit body. Line (C) is invalid because it is erroneously attempting to jump to a different circuit body. Line (D) is invalid for the same reason as line (C); it is an erroneous attempt to jump from one local circuit body to another. Lines (E) and (F) are both invalid because they are attempts to jump into a local circuit definition.
In 2016, at Rigetti Computing, Quil was defined in an arXiv paper entitled "A Practical Quantum Instruction Set Architecture" by R. Smith, M. Curtis, and W. Zeng.
In 2018, at Rigetti Computing, R. Smith began work to amend Quil to include a memory model. Its definition lived in a new Git repository containing the Quil specification.
In 2019, at Rigetti Computing, Quil's specification was rewritten in Markdown format by S. Heidel and other Rigettibased contributors for easier consumption. S. Heidel also contributed an ANTLR grammar for Quil.
In 2019, at Rigetti Computing, an extension of Quil for timedomain, pulselevel control was developed by S. Heidel, E. Davis, and other Rigettibased contributors. This was codenamed "Quilt" but was later finalized as "QuilT". QuilT lived as a proposed extension (called an "RFC") in the Git repository.
In 2019, at Rigetti Computing, an addition to Quil to allow gates to be defined as exponentiated Pauli sums was developed by E. Peterson. This was codenamed "defexpi" but was later finalized as syntax DEFGATE AS PAULISUM. This syntax lived as an RFC in the Git repository.
In 2021, R. Smith (whose affiliation since changed to HRL Laboratories) and Rigetti Computing set up the QuilLang GitHub organization for shared and collaborative governance of the definition of Quil as well as its de facto standard software tooling.
In 2021, R. Smith rewrote the specification in a custom format to allow rendering as an HTML page. The specification was synthesized from all previous official sources on the language.
Quil's specification, as well as software implementations, have benefited greatly from their international userbase. Quil has also benefited from a diverse ecosystem of other quantum computing languages, such as OpenQASM, Quipper, Q#, and QCL.
This document only tracks changes since its conception.
This section is about timedomain extensions to Quil, formally known as Annex T of this document, but also known as QuilT.
A frame encapsulates any rotating frame relative to which control/readout waveforms may be defined. For the purposes of scheduling and execution on possibly heterogenous hardware, frames are specified with respect to a specific list of qubits. Thus, 0 1 "cz" is the "cz" frame on qubits 0 and 1. The order of the qubits matters. In particular, the above frame may differ from 1 0 "cz".
QuilT itself has no builtin frames. Frames must be defined using the DEFFRAME directive.
DEFFRAME
:
:
::=  SAMPLERATE 
INITIALFREQUENCY  
DIRECTION  
HARDWAREOBJECT 
All frames used in a program must have a corresponding toplevel definition.
Before execution, a QuilT program is linked with a specific system of control hardware, and frames are mapped to suitable hardware objects (cf. the HARDWAREOBJECT frame attribute below). Native or canonical frame definitions may be provided by a hardware vendor. Some examples of Rigetti's canonical frames are listed below, but this is subject to change.
Examples (names only):
"xy" # eg. for the drive line "ff" # eg. for a generic flux line "cz" # eg. for a flux pulse for enacting CZ gate "iswap" "ro" # eg. for the readout pulse "out" # eg. for the capture line
Frame attributes represent quantities associated with a given frame which need not be specified by the programmer, but which are ultimately required to fully link and execute a QuilT program on a physical device.
::= 

flat ( duration: , iq: )
 
gaussian ( duration: , fwhm: , t0: )
 
draggaussian ( duration: , fwhm: , t0: , anh: , alpha: )
 
erfsquare ( duration: , risetime: , padleft: , padright: ) 
Waveforms are referenced either by name or by a builtin waveform generator.
The builtin waveform generators are:
DEFWAVEFORM
( ) :
New waveforms may be defined by specifying the sample rate (in Hertz) and listing out all the IQ values as complex numbers, separated by commas. Waveform definitions may also be parameterized, although note that Quil has no support for vector level operations.
Example:
DEFWAVEFORM my_custom_waveform 6.0: 1+2i, 3+4i, 5+6i DEFWAVEFORM my_custom_parameterized_waveform(%a) 6.0: (1+2i)*%a, (3+4i)*%a, (5+6i)*%a
The duration (in seconds) of a custom waveform may be computed by dividing the number of samples by the sample rate. In the above example, both waveforms have a duration of 0.5 seconds.
PULSE Frame Waveform
Pulses represent the propagation of a specific waveform (either builtin or custom) on a specific frame.
Examples:
# Simple pulse with previously defined waveform PULSE 0 "xy" my_custom_waveform # Pulse with previously defined parameterized waveform PULSE 0 "xy" my_custom_parameterized_waveform(0.5) # Pulse with builtin waveform generator PULSE 0 "xy" flat(duration: 1e6, iq: 2+3i) # Pulse on a flux line PULSE 0 1 "cz" flat(duration: 1e6, iq: 2+3i)
Each frame has a fixed, hardwarespecific sample rate. The behavior of a PULSE instruction with a custom waveform whose sample rate does not match the corresponding frame's sample rate is undefined.
SETFREQUENCY
SHIFTFREQUENCY
Each frame has a frequency which is tracked throughout the program. Initial frame frequencies are specified in the frame definition's INITIALFREQUENCY attribute. Subsequent code may update this, either assigning an absolute value (SETFREQUENCY) or a relative offset (SHIFTFREQUENCY).
SETFREQUENCY 0 "xy" 5.4e9 SHIFTFREQUENCY 0 "ro" 6.1e9
SETPHASE
SHIFTPHASE
SWAPPHASES
Each frame has a phase which is tracked throughout the program. Initially the phase starts out as 0. It may be set or shifted up and down, as well as swapped with other frames.
The phase must be a rational real number. There is also support for shifted the phase based on some expression, as long as that expression returns a real number.
Example:
SETPHASE 0 "xy" pi/2 SHIFTPHASE 0 "xy" pi SHIFTPHASE 0 "xy" %theta*2/pi SWAPPHASE 0 "xy" 1 "xy"
SETSCALE
Each frame has a scale which is tracked throughout the program. The scale is initially 1.
Example:
SETSCALE 0 "xy" 0.75
CAPTURE
RAWCAPTURE
The capture instruction opens up the readout on a frame and measures its state. An integration waveform will be applied to the raw IQ points and the result is placed in classical memory.
The waveform will define the duration of the capture. The memory reference must be able to store a complex number for each qubit in the frame.
In the case of a raw capture the waveform is replaced with a rational number representing the duration of the capture.
Example:
# Simple capture of an IQ point DECLARE iq REAL[2] CAPTURE 0 "out" flat(1e6, 2+3i) iq # Raw capture DECLARE iqs REAL[400] # length needs to be determined based on the sample rate CAPTURE 0 "out" 200e6 iqs
The behavior of a CAPTURE instruction with a custom waveform whose sample rate does not match the corresponding frame's sample rate is undefined.
DEFCAL
( ) :
DEFCAL
:
Calibrations for highlevel gates can be defined by mapping a combination of (gate name, parameters, qubits) to a sequence of analog control instructions.
Calibrations with the same gate name as a builtin gate definition or custom gate definition are assumed to be the same.
Multiple calibration definitions can be defined for different parameter and qubit values. When a gate is translated into control instructions the calibration definitions are enumerated in reverse order of definition and the first match will be taken.
For example, given the following list of calibration definitions in this order:
The same system applies for MEASURE. Although MEASURE cannot be parameterized, it takes only a single qubit as input, and it has an additional (optional) parameter for the memory reference into which to store the result.
Examples:
# Simple nonparameterized gate on qubit 0 DEFCAL X 0: PULSE 0 "xy" gaussian(duration: 1, fwhm: 2, t0: 3) # Parameterized gate on qubit 0 DEFCAL RX(%theta) 0: PULSE 0 "xy" flat(duration: 1e6, iq: 2+3i)*%theta/(2*pi) # Applying RZ to any qubit DEFCAL RZ(%theta) %qubit: SHIFTPHASE %qubit "xy" %theta # Measurement and classification DEFCAL MEASURE 0 %dest: DECLARE iq REAL[2] CAPTURE 0 "out" flat(1e6, 2+3i) iq LT %dest iq[0] 0.5 # thresholding
Quil supports arbitrarily chained gate modifiers. As such, calibration definitions may also incorporate gate modifiers, with the convention that a calibration definition matches a gate application only if the modifiers match exactly. Thus in
DEFCAL T 0: # ... DEFCAL DAGGER T 0: # ...the first calibration definition matches T 0, the second matches DAGGER T 0, and neither match DAGGER DAGGER T 0.
DELAY
FENCE
Delay allows for the insertion of a gap within a list of pulses or gates with a specified duration in seconds.
If frame names are specified, then the delay instruction affects those frames on those qubits. If no frame names are specified, all frames on precisely those qubits are affected.
Note: Note: this excludes frames which intersect the specified qubits but involve others. For example, DELAY 0 1.0 delays one qubit frames on 0, such as 0 "xy", but leaves other frames, such as 0 1 "cz", unaffected.
Fence ensures that all operations involving the specified qubits that follow the fence statement happen after all operations involving the specified qubits that preceed the fence statement. If no qubits are specified, the FENCE operation implicitly applies to all qubits on the device.
Examples:
X 0 FENCE 0 1 X 1 # This X gate will be applied to qubit 1 AFTER the X gate on qubit 0 # Simple T1 experiment X 0 DELAY 0 100e6 MEASURE 0 ro[0]