Generating OpenQASM from Q# code

Β· 876 words Β· 5 minutes to read

In the summer of 2024, I announced the Q# Bridge library, which allows you to run Q# simulations from many popular high-level languages - C#, Swift, Python and Kotlin. Today, I would like to write about a brand new feature in the library, an ability to generate OpenQASM 2.0 code from Q# source.

This is a feature that Q# toolchain does not natively supports, and it adds to the value proposition of Q# Bridge - acting as a literal bridge between Q# and other ecosystems (traditional languages or, in this case, quantum).

What is OpenQASM? πŸ”—

OpenQASM is an intermediate representation for quantum circuits. It is a human-readable language that allows you to describe quantum circuits in a way that can be understood by quantum computers. It was originally introuced in a paper by Cross et al., working out of IBM Quantum Computing group, in 2017. The language has become a de facto standard in the quantum computing industry, supported by numerous platforms including Qiskit, Cirq, and various quantum simulators. This broad adoption makes it an ideal target for quantum program translation, as circuits expressed in OpenQASM can be executed on a wide range of quantum computing platforms and simulators.

OpenQASM is developed as an open-source project, and you can find the specification on GitHub, which is currently at version 3.1. The Q# Bridge library generates OpenQASM 2.0, which is the simplest and most widely supported version of the language.

Q# to OpenQASM πŸ”—

The Q# Bridge QASM generator supports a standard set of quantum operations:

  • Single-Qubit Gates:
    • Pauli gates (X, Y, Z)
    • Hadamard gate (H)
    • Phase gates (S, T) and their adjoints (S†, T†)
    • Rotation gates (Rx, Ry, Rz) with arbitrary angles
  • Multi-Qubit Gates:
    • CNOT (controlled-X)
    • CZ (controlled-Z)
    • CY (controlled-Y)
    • SWAP
    • Toffoli (CCX)
    • Two-qubit rotation gates (Rxx, Ryy, Rzz)
  • Measurements
    • Standard quantum measurements (mapping to OpenQASM’s measure operation)
    • Qubit resets
    • Results are captured in classical registers that are automatically allocated

Having a QASM generator allows you to write quantum programs in Q# and then generate OpenQASM code that can be executed on a wide range of quantum computing platforms and simulators.

How it works πŸ”—

The QASM generator in Q# Bridge is made possible by the fact that Q# supports custom compiler backends - and the generator is implemented as such backend. The generator includes the following considerations:

  • Support for Q# language semantics: because the generator acts as a backend, it can leverage the power of Q# language semantics, and generally supports any Q# code, as long as it is compatible with the Q# base profile.
  • Automatic Register Management: The generator automatically manages quantum and classical registers, allocating the necessary number of qubits and classical bits based on your circuit.
  • Two-Qubit Rotation Decomposition: Rxx, Ryy, and Rzz gates are automatically decomposed into sequences of basic OpenQASM 2.0 gates.
  • Global Phase Handling: Global phase adjustments in Q# are safely ignored in the generated QASM (as they are unobservable).
  • Flexible Reset Handling: The generator supports different reset behaviors to accommodate various OpenQASM targets - resets can be fully supported, ignored, or treated as errors depending on the target platform’s capabilities.

It’s important to note a few current limitations:

  • All the limitations of the Q# base profile apply - in particular making use of the measurement results in further operations is not supported. The same is applicable to conditional branching.
  • Custom Q# intrinsics beyond global phase adjustments are not supported (for example custom noise models)

Usage πŸ”—

To get started, you can have a look at the Jupyter notebook that demonstrates the QASM generation in action. The notebook is available in the Q# Bridge repository. Since Q# Bridge is available for multiple languages, you can generate QASM from Q# code in Python, C#, Swift and Kotlin!

The premise is very simple - you write your piece of Q# code (remember - you must conform to the base profile!) and then use the Q# Bridge library to generate OpenQASM code from it. The generated code can then be submitted to any quantum computing platform or simulator that supports OpenQASM 2.0.

Consider the following sample Q# code:

@EntryPoint()
operation Run() : (Result, Result) {
    use (control, target) = (Qubit(), Qubit());
    PrepareBellState(control, target);
    
    let resultControl = MResetZ(control);
    let resultTarget = MResetZ(target);
    return (resultControl, resultTarget);
}

operation PrepareBellState(q1 : Qubit, q2: Qubit) : Unit {
    H(q1);
    CNOT(q1, q2);
}

Assuming you load it to a code variable in your host language, you can then generate OpenQASM code in the following fashion.

Python:

from qsharp_bridge import *

# choose the options as you wish
generation_options = QasmGenerationOptions(include_qelib=True, reset_behavior=QasmResetBehavior.SUPPORTED)
qasm_code = qasm2(code, generation_options)
print(qasm_code)

C#:

// choose the options as you wish
var qasmGenerationOptions = new QasmGenerationOptions(includeQelib: true, resetBehavior: QasmResetBehavior.Supported);
var qasm = QsharpBridge.Qasm2(qsharpSource, qasmGenerationOptions);
Console.WriteLine("Generated QASM:");
Console.WriteLine(qasm);

Swift:

// choose the options as you wish
let qasmGenerationOptions = QasmGenerationOptions(includeQelib: true, resetBehavior: .supported);
let qasm = try! qasm2(source: qsharpSource, generationOptions: qasmGenerationOptions);
print("Generated QASM:");
print(qasm);

Kotlin:

import qsharp.bridge.qasm2
import qsharp.bridge.QasmGenerationOptions
import qsharp.bridge.QasmResetBehavior

// choose the options as you wish
val qasmGenerationOptions = QasmGenerationOptions(true, QasmResetBehavior.SUPPORTED);
val qasm = qasm2(qsharpSource, qasmGenerationOptions);
println("Generated QASM:");
println(qasm);

In all cases, the generated code should look like this:

OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0], q[1];
measure q[0] -> c[0];
reset q[0];
measure q[1] -> c[1];
reset q[1];

Have fun!

About


Hi! I'm Filip W., a software architect from ZΓΌrich πŸ‡¨πŸ‡­. I like Toronto Maple Leafs πŸ‡¨πŸ‡¦, Rancid and quantum computing. Oh, and I love the Lowlands 🏴󠁧󠁒󠁳󠁣󠁴󠁿.

You can find me on Github, on Mastodon and on Bluesky.

My Introduction to Quantum Computing with Q# and QDK book
Microsoft MVP