mirror of https://github.com/openqasm/openqasm.git
229 lines
8.2 KiB
ReStructuredText
229 lines
8.2 KiB
ReStructuredText
Directives
|
|
==========
|
|
|
|
OpenQASM supports directive mechanisms that allow other information to
|
|
be included in the program. Directives are either pragmas or annotations.
|
|
Both are used to supply additional information to the compiler passes and the
|
|
target system or simulator. Ideally the meaning of the program does not change
|
|
if some or all of the directives are ignored, so they can be interpreted
|
|
at the discretion of the consuming process.
|
|
|
|
Pragma and annotation namespacing
|
|
---------------------------------
|
|
|
|
It is intended that different implementators of the specification might
|
|
define their own pragmas and annotations. This creates the possibility that
|
|
different implementations might define pragmas or annotations that collide.
|
|
|
|
To avoid such collisions we recommend that implementations namespace their
|
|
pragmas and annotations as follows:
|
|
|
|
- Pragmas start with `pragma <namespace>.<name>(.<name>)*`.
|
|
- Annotations start with `@<namespace>.<name>(.<name>)*`.
|
|
|
|
Where `namespace` and `name` are valid OpenQASM identifiers.
|
|
|
|
If the pragma or annotation takes additional arguments the
|
|
final `<name>` should be followed by a single space before the
|
|
additional text.
|
|
|
|
The namespace should be a short name that identifies the organization that
|
|
defines behaviour of the pragmas and annotations contained within it.
|
|
|
|
The namespace `openqasm` is reserved for future use by the OpenQASM
|
|
specification.
|
|
|
|
|
|
Pragmas
|
|
-------
|
|
|
|
Pragma directives start with ``pragma`` and continue to the end of line. The
|
|
text after ``pragma`` is a single string, and parsing is left to the specific
|
|
implementation, although implementors are encourage to use the namespacing
|
|
described above.
|
|
|
|
Implementations may optionally choose to support the older ``#pragma`` keyword
|
|
as a custom extension.
|
|
|
|
Pragmas should be processed as soon as they are encountered; if a
|
|
pragma is not supported by a compiler pass it should be ignored and preserved
|
|
intact for future passes. Pragmas should avoid stateful or positional
|
|
interactions to avoid unexpected behaviors between included source files. If the
|
|
position is relevant to a pragma, an annotation should be used instead.
|
|
|
|
Pragmas are useful for extending OpenQASM functionality that is not described in
|
|
this specification, such as adding directives to a simulator.
|
|
|
|
\*Note: The following examples are simply possible implementations, this
|
|
specification does not define any pragmas. Please consult your tool's
|
|
documentation for supported pragmas.
|
|
|
|
.. code-block::
|
|
|
|
pragma qiskit.simulator noise model "qpu1.noise"
|
|
|
|
Pragmas can also be used to specify system-level information or assertions for
|
|
the entire circuit.
|
|
|
|
.. code-block::
|
|
|
|
OPENQASM 3.0;
|
|
|
|
// Attach billing information
|
|
pragma ibm.user alice account 12345678
|
|
|
|
// Assert that the QPU is healthy to run this circuit
|
|
pragma ibm.max_temp qpu 0.4
|
|
|
|
qubit[2] q;
|
|
|
|
|
|
Annotations
|
|
-----------
|
|
|
|
Annotations can be added to supply additional information to the following
|
|
OpenQASM ``statement`` as defined in the grammar. Annotations will start with a
|
|
``@`` symbol, have a dotted list of identifying keywords and continue to the end
|
|
of the line.
|
|
|
|
Multiple annotations may be added to a single statement. No ordering or
|
|
interaction between annotations are prescribed by this specification.
|
|
|
|
\*Note: The following examples are simply possible implementations, this
|
|
specification does not define any annotations. Please consult your tool's
|
|
documentation for supported annotations.
|
|
|
|
.. code-block::
|
|
|
|
// Manage port binding on a physical device
|
|
@bind IOPORT[3:2]
|
|
input bit[2] control_flags;
|
|
|
|
// Instruct compiler to create a reversible version of the function
|
|
@openqasm.reversible
|
|
gate multiply a, b, x {
|
|
x = a * b;
|
|
}
|
|
|
|
// Prevent swap insertion
|
|
@openqasm.noswap
|
|
box {
|
|
rx(pi) q[0];
|
|
cnot q[0], q[3];
|
|
}
|
|
|
|
// Apply multiple annotations
|
|
@openqasm.crosstalk
|
|
@openqasm.noise profile "gate_noise.qnf"
|
|
defcal noisy_gate $0 $1 { ... }
|
|
|
|
|
|
Input/output
|
|
============
|
|
|
|
OpenQASM introduces features targeted to support near-time computation, in
|
|
the form of parameterized circuits. These features are modifiers ``input``
|
|
and ``output``, which allow OpenQASM3 circuits to accept input parameters
|
|
and return select output parameters.
|
|
|
|
The ``input`` modifier can be used to indicate that one or more variable
|
|
declarations represent values which will be provided at run-time, upon
|
|
invocation. This allows the programmer to use the same compiled circuits
|
|
which only differ in the values of certain parameters. For backward compatibility,
|
|
OpenQASM3 does not require an ``input`` declaration to be provided. When
|
|
an ``input`` declaration is provided, the compiler produces an executable
|
|
that leaves these free parameters unspecified: a circuit run would take as
|
|
input both the executable and some choice of the parameters.
|
|
|
|
Similarly, the ``output`` modifier can be used to indicate that one or more variables
|
|
are to be provided as an explicit output of the quantum procedure. Note that
|
|
OpenQASM 2 did not allow the programmer to specify that only a subset of its
|
|
variables should be returned as output, and so it would return all classical
|
|
variables (which were all creg variables) as output. For compatibility,
|
|
OpenQASM 3 does not require an output declaration to be provided: in this
|
|
case it assumes that all of the declared variables are to be returned as
|
|
output. If the programmer provides one or more output declarations, then only
|
|
those variables described as outputs will be returned as an output of the
|
|
quantum process. A variable may not be marked as both input and output.
|
|
|
|
The input and output modifiers allow the programmer to more easily write
|
|
variational quantum algorithms: a quantum algorithm with some free parameters,
|
|
which may be run many times with different parameter values which are determined
|
|
by a classical optimiser at near-time. Rather than write a circuit which
|
|
generates a new sequence of operations for each run, OpenQASM 3 allows such
|
|
circuits to be expressed as a single program with input parameters. This
|
|
allows the programmer to communicate many different circuits with a single
|
|
file, which only has to be compiled once, amortizing the cost of compilation
|
|
across many runs. For an example, we may consider a parameterized circuit which
|
|
performs a measurement in a basis given by an input parameter:
|
|
|
|
.. code-block::
|
|
|
|
input int basis; // 0 = X basis, 1 = Y basis, 2 = Z basis
|
|
output bit result;
|
|
qubit q;
|
|
|
|
// Some complicated circuit...
|
|
|
|
if (basis == 0) h q;
|
|
else if (basis == 1) rx(π/2) q;
|
|
result = measure q;
|
|
|
|
For a second example, consider the Variable Quantum Eigensolver (VQE) algorithm :cite:`peruzzo2014variational`.
|
|
In this algorithm the same circuit is repeated
|
|
many times using different sets of free parameters to minimize an expectation
|
|
value. The following is an example, in which there is also more than one input
|
|
variable:
|
|
|
|
.. code-block::
|
|
|
|
input angle[32] param1;
|
|
input angle[32] param2;
|
|
qubit q;
|
|
|
|
// Build an ansatz using the above free parameters, eg.
|
|
rx(param1) q;
|
|
ry(param2) q;
|
|
|
|
// Estimate the expectation value and store in an output variable
|
|
|
|
The above example could also be written using an input array:
|
|
|
|
.. code-block::
|
|
|
|
input array[angle[32], 2] params;
|
|
qubit q;
|
|
|
|
// Build an ansatz using the above free parameters, eg.
|
|
rx(params[0]) q;
|
|
ry(params[1]) q;
|
|
|
|
// Estimate the expectation value and store in an output variable
|
|
|
|
The following Python pseudocode illustrates the differences between using and
|
|
not using parameterized circuits in a quantum program for the case of the VQE:
|
|
|
|
.. code-block:: python
|
|
|
|
# Example without using parametric circuits:
|
|
|
|
for theta in thetas:
|
|
# Create an OpenQASM circuit with θ defined
|
|
circuit = subsitute_theta(read("circuit.qasm"))
|
|
|
|
# The slow compilation step is run on each iteration of the inner loop
|
|
binary = compile_qasm(circuit)
|
|
result = run_program(binary)
|
|
|
|
# Example using parametric circuits:
|
|
|
|
# parametric_circuit.qasm begins with the line "input angle θ;"
|
|
circuit = read("parametric_circuit.qasm")
|
|
|
|
# The slow compilation step only happens once
|
|
binary = compile_qasm(circuit)
|
|
|
|
for theta in thetas:
|
|
# Each iteration of the inner loop is reduced to only running the circuit
|
|
result = run_program(binary, θ=theta)
|