Exporting to OpenQASM¶
To facilitate the integration of simulators and hardware devices, hybridlane provides an intermediate representation (IR) format based on OpenQASM 3.0, with a few (minimal) modifications to capture hybrid CV-DV programs. We detail the extensions to OpenQASM in a later section to first focus on introducing how to use it.
A quantum program can be exported using the to_openqasm() function. This
function inspects the circuit for qumodes and qubits, and declares them separately in registers
m and q, respectively. Based on the measurements and their observables, it also infers whether
to use homodyne (hl.X) or Fock number (hl.N) measurements. Finally, noncommuting
measurements are run on separate calls to the state preparation circuit, so a single OpenQASM
program may contain multiple circuit executions using the function invocation feature of OpenQASM
3.0.
Basic usage¶
Here we give an example of exporting a basic circuit to OpenQASM. Consider the following circuit
dev = qp.device("bosonicqiskit.hybrid")
@qp.qnode(dev)
def circuit(n):
for j in range(n):
qp.X(0)
hl.JaynesCummings(np.pi / (2 * np.sqrt(j + 1)), np.pi / 2, [0, 1])
return (
hl.var(hl.QuadP(1)),
hl.expval(qp.PauliZ(0)),
)
Note that it has DV gates, hybrid gates, and DV and CV measurements. Furthermore, the QuadP
observable is not diagonal in the position basis. This can be exported with
qasm = hl.to_openqasm(circuit, precision=5)(5)
which produces the following IR
OPENQASM 3.0;
include "stdgates.inc";
include "cvstdgates.inc";
qubit[1] q;
qumode[1] m;
def state_prep() {
reset q;
reset m;
x q[0];
cv_jc(1.5708, 1.5708) q[0], m[0];
x q[0];
cv_jc(1.1107, 1.5708) q[0], m[0];
x q[0];
cv_jc(0.9069, 1.5708) q[0], m[0];
x q[0];
cv_jc(0.7854, 1.5708) q[0], m[0];
x q[0];
cv_jc(0.70248, 1.5708) q[0], m[0];
}
state_prep();
cv_r(1.5708) m[0];
float c0 = measure_x m[0];
bit[1] c1;
c1[0] = measure q[0];
To measure the momentum operator \(\hat{p}\), you can see that the export function added the
diagonalizing gates (\(R(\pi/2)\)). To disable this behavior, you can set
rotations = False. Additionally, CV measurements automatically are stored in a data type at
machine precision.
Encoding custom operators¶
Sometimes, you might want to keep an operator in the IR that isn’t part of our “standard library,”
meaning hybridlane doesn’t know how to serialize it. You can teach it to encode your operations by
registering an implementation with the function format_gate_as_qasm().
We’ll give an example encoding the ECD gate with the instruction cv_ecd:
from typing import Any
import hybridlane as hl
from hybridlane.io.openqasm import format_gate_as_qasm
@format_gate_as_qasm.register
def _(op: hl.ECD, wire_to_str: dict[Any, str], precision: int | None = None) -> str:
if precision:
params = list(map(lambda p: f"{p:.{precision}f}", op.parameters))
else:
params = list(map(str, op.parameters))
wires = list(map(lambda w: wire_to_str[w], op.wires))
param_str = "(" + ", ".join(params) + ")" if params else ""
wire_str = ", ".join(wires)
gate_str = f"cv_ecd{param_str} {wire_str};"
return gate_str
The first parameter of the function needs to be the operator to be encoded, and its type must be
annotated because Python’s functools.singledispatch() decorator relies on the type hints to
determine which implementation to use for a given input.
The second argument is a dictionary mapping the wire labels in the operator to the corresponding
OpenQASM register label. For example, qumode m in op might have OpenQASM reference
m[3].
The final argument is the number of decimal places used to encode each angle parameter.
Strict mode¶
This custom IR format is not compatible with the official OpenQASM parser. If, for some reason,
your application needs to erase the type information to be compatible with OpenQASM, you can pass
the strict=True flag to produce OpenQASM-compatible circuits. This will remove the custom
CV-DV extensions.
qasm = hl.to_openqasm(circuit, precision=5, strict=True)(5)
produces
OPENQASM 3.0;
include "stdgates.inc";
include "cvstdgates.inc";
// Position measurement x
defcal measure_x m -> float {}
// Fock measurement n
defcal measure_n m -> uint {}
qubit[1] q;
qubit[1] m;
def state_prep() {
reset q;
reset m;
x q[0];
cv_jc(1.5708, 1.5708) q[0], m[0];
x q[0];
cv_jc(1.1107, 1.5708) q[0], m[0];
x q[0];
cv_jc(0.9069, 1.5708) q[0], m[0];
x q[0];
cv_jc(0.7854, 1.5708) q[0], m[0];
x q[0];
cv_jc(0.70248, 1.5708) q[0], m[0];
}
state_prep();
cv_r(1.5708) m[0];
float c0 = measure_x(m[0]);
bit[1] c1;
c1[0] = measure q[0];
Notice how the register declaration qumode m[1] became qubit m[1], and the measure_x keyword was replaced with a corresponding defcal and function call.
OpenQASM modifications¶
Our superset of OpenQASM contains the following extra features:
The
qumodekeyword has the same semantics asqubit, just telling the compiler that a register specifically contains qumodes instead. This enables a compiler to perform type checking on gates and measurements.Example:
// Can declare a register of qumodes qumode[3] m; // and reset them reset m; // or use them in a subroutine definition def pmeasure(qumode m) -> float[32] { cv_r(pi/2) m; return measure_x m; }
The
measure_xkeyword has the same syntax as the qubitmeasurekeyword, but it performs homodyne measurement of a qumode and stores the result in afloatvariable. The bit width of the result dictates the precision of the measurement.Example:
// Performs position readout into 32 bit precision float[32] c = measure_x m[0]; // or can do a lower-precision measurement float[5] c2 = measure_x m[1];
Similarly, the
measure_nkeyword performs a Fock readout of a qumode, and stores the result in auintvariable. Again, the bit width of the resulting variable determines the precision of the measurement.Example:
// Performs fock readout into 32 bit precision uint[32] c = measure_n m[0]; // or can do a lower-precision measurement uint[5] c2 = measure_n m[1];
We introduce a CV-DV standard gate library based on Liu et al. This library should be handled by compilers using the statement
include "cvstdgates.inc";, and we include its definitions in the fileexamples/cvstdgates.inc. All of our gates follow the definitions of this library, so you can use the documentation ofhl.opsas a reference.