6

I recently added a to_qasm method to stim. An issue I'm having is how to test that the outputs are correct. I can test OpenQASM 2 outputs by giving the output to qiskit, which can parse OpenQASM 2 and execute the result. But it seems that qiskit doesn't support OpenQASM 3 features like classical operations (e.g. dets[5] = rec[1] ^ rec[2]) and subroutines (e.g. def mx(qubit q0) -> bit { bit b; h q0; measure q0 -> b; h q0; return b; }).

Here's an example of how I test OpenQASM 2 output:

def test_to_qasm2_runs_in_qiskit():
    pytest.importorskip("qiskit")
    pytest.importorskip("qiskit_aer")
    import qiskit
    import qiskit_aer
stim_circuit = stim.Circuit("""
    R 0 1
    MZZ !0 1
""")
qasm = stim_circuit.to_qasm(open_qasm_version=2)

# Verify it parses.
qiskit_circuit = qiskit.QuantumCircuit.from_qasm_str(qasm)

# Verify it implements the correct behavior.
counts = qiskit_aer.AerSimulator().run(qiskit_circuit, shots=8).result().get_counts(qiskit_circuit)
assert counts['1'] == 8

Is there something equivalent I can do to verify OpenQASM 3 code is correct? Are there tools out there that parse it? That simulate the parsed result?

Here's an example of the kind of thing I would test. This is a d=5 r=5 repetition code generated by stim, converted to qasm 3:

OPENQASM 3.0;
include "stdgates.inc";
def mr(qubit q0) -> bit { bit b; measure q0 -> b; reset q0; return b; }

qreg q[9]; creg rec[25]; creg dets[24]; creg obs[1];

reset q[0]; reset q[1]; reset q[2]; reset q[3]; reset q[4]; reset q[5]; reset q[6]; reset q[7]; reset q[8]; barrier q;

cx q[0], q[1]; cx q[2], q[3]; cx q[4], q[5]; cx q[6], q[7]; barrier q;

cx q[2], q[1]; cx q[4], q[3]; cx q[6], q[5]; cx q[8], q[7]; barrier q;

rec[0] = mr(q[1]); rec[1] = mr(q[3]); rec[2] = mr(q[5]); rec[3] = mr(q[7]); dets[0] = rec[0] ^ 0; dets[1] = rec[1] ^ 0; dets[2] = rec[2] ^ 0; dets[3] = rec[3] ^ 0; barrier q;

cx q[0], q[1]; cx q[2], q[3]; cx q[4], q[5]; cx q[6], q[7]; barrier q;

cx q[2], q[1]; cx q[4], q[3]; cx q[6], q[5]; cx q[8], q[7]; barrier q;

rec[4] = mr(q[1]); rec[5] = mr(q[3]); rec[6] = mr(q[5]); rec[7] = mr(q[7]); dets[4] = rec[4] ^ rec[0] ^ 0; dets[5] = rec[5] ^ rec[1] ^ 0; dets[6] = rec[6] ^ rec[2] ^ 0; dets[7] = rec[7] ^ rec[3] ^ 0; barrier q;

cx q[0], q[1]; cx q[2], q[3]; cx q[4], q[5]; cx q[6], q[7]; barrier q;

cx q[2], q[1]; cx q[4], q[3]; cx q[6], q[5]; cx q[8], q[7]; barrier q;

rec[8] = mr(q[1]); rec[9] = mr(q[3]); rec[10] = mr(q[5]); rec[11] = mr(q[7]); dets[8] = rec[8] ^ rec[4] ^ 0; dets[9] = rec[9] ^ rec[5] ^ 0; dets[10] = rec[10] ^ rec[6] ^ 0; dets[11] = rec[11] ^ rec[7] ^ 0; barrier q;

cx q[0], q[1]; cx q[2], q[3]; cx q[4], q[5]; cx q[6], q[7]; barrier q;

cx q[2], q[1]; cx q[4], q[3]; cx q[6], q[5]; cx q[8], q[7]; barrier q;

rec[12] = mr(q[1]); rec[13] = mr(q[3]); rec[14] = mr(q[5]); rec[15] = mr(q[7]); dets[12] = rec[12] ^ rec[8] ^ 0; dets[13] = rec[13] ^ rec[9] ^ 0; dets[14] = rec[14] ^ rec[10] ^ 0; dets[15] = rec[15] ^ rec[11] ^ 0; barrier q;

cx q[0], q[1]; cx q[2], q[3]; cx q[4], q[5]; cx q[6], q[7]; barrier q;

cx q[2], q[1]; cx q[4], q[3]; cx q[6], q[5]; cx q[8], q[7]; barrier q;

rec[16] = mr(q[1]); rec[17] = mr(q[3]); rec[18] = mr(q[5]); rec[19] = mr(q[7]); dets[16] = rec[16] ^ rec[12] ^ 0; dets[17] = rec[17] ^ rec[13] ^ 0; dets[18] = rec[18] ^ rec[14] ^ 0; dets[19] = rec[19] ^ rec[15] ^ 0; measure q[0] -> rec[20]; measure q[2] -> rec[21]; measure q[4] -> rec[22]; measure q[6] -> rec[23]; measure q[8] -> rec[24]; dets[20] = rec[21] ^ rec[20] ^ rec[16] ^ 0; dets[21] = rec[22] ^ rec[21] ^ rec[17] ^ 0; dets[22] = rec[23] ^ rec[22] ^ rec[18] ^ 0; dets[23] = rec[24] ^ rec[23] ^ rec[19] ^ 0; obs[0] = obs[0] ^ rec[24] ^ 0;

Craig Gidney
  • 36,389
  • 1
  • 29
  • 95

1 Answers1

1

Following up on Egretta.Thula's comment, the Amazon Braket SDK exposes an interface you can implement to parse OpenQASM 3 into whatever format you want. An example implementation is used by the from_ir method, which converts OpenQASM 3 into Braket SDK circuits.

Cody Wang
  • 1,203
  • 7
  • 13