cda-tum / mqt-core

MQT Core - The Backbone of the Munich Quantum Toolkit
https://mqt.readthedocs.io/projects/core
MIT License
62 stars 31 forks source link

🐛 Error parsing `ms` gate in OpenQASM with mqt-core. #723

Closed MattePalte closed 1 month ago

MattePalte commented 1 month ago

Environment information

Description

The ms gate is not correctly parsed by mqt-core, resulting in a RuntimeError when trying to verify or parse the circuit.

Expected behavior

The ms gate should be correctly parsed by mqt-core, as it is valid and can be created by Qiskit. A similar circuit parses without issue in Qiskit.

How to Reproduce

Steps to Reproduce

  1. Create a circuit with Qiskit that includes the ms gate.
  2. Export the circuit to OpenQASM using Qiskit’s qasm2.dumps() function.
  3. Attempt to verify the circuit using mqt-core with:
    from mqt import qcec
    result = qcec.verify("path_to_circuit.qasm", "path_to_another_circuit.qasm")
    print(result)

Example Code Snippet

Create a circuit with Qiskit that includes the ms gate.

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
qr = QuantumRegister(3, name='qr')
cr = ClassicalRegister(3, name='cr')
qc = QuantumCircuit(qr, cr, name='qc')
qc.ms(0.844396, [qr[0], qr[1], qr[2]])
qc.measure(qr, cr)

from qiskit import qasm2
qasm_string_a = qasm2.dumps(qc)
print(qasm_string_a)

Export the circuit to OpenQASM.

OPENQASM 2.0;
include "qelib1.inc";
gate ms(param0) q0,q1,q2 { rxx(0.844396) q0,q1; rxx(0.844396) q0,q2; rxx(0.844396) q1,q2; }
qreg qr[3];
creg cr[3];
ms(0.844396) qr[0],qr[1],qr[2];
measure qr[0] -> cr[0];
measure qr[1] -> cr[1];
measure qr[2] -> cr[2];

Attempt to verify the circuit using mqt-core.

from mqt import qcec
qasm_string_a = """
OPENQASM 2.0;
include "qelib1.inc";
gate ms(param0) q0,q1,q2 { rxx(0.844396) q0,q1; rxx(0.844396) q0,q2; rxx(0.844396) q1,q2; }
qreg qr[3];
creg cr[3];
ms(0.844396) qr[0],qr[1],qr[2];
measure qr[0] -> cr[0];
measure qr[1] -> cr[1];
measure qr[2] -> cr[2];
"""

path_a = "a.qasm"
with open(path_a, "w") as f:
    f.write(qasm_string_a)

result = qcec.verify(path_a, "b.qasm")
print(result)

Error Produced

RuntimeError: Could not import first circuit: <input>:4:6: Expected 'Identifier', got 'ms'.

Alternatively, using the mqt.core library directly, without mqt.qcec, also results in the same error.

from mqt.core import QuantumComputation
qasm_string_a = """
OPENQASM 2.0;
OPENQASM 2.0;
include "qelib1.inc";
gate ms(param0) q0,q1,q2 { rxx(0.844396) q0,q1; rxx(0.844396) q0,q2; rxx(0.844396) q1,q2; }
qreg qr[3];
creg cr[3];
ms(0.844396) qr[0],qr[1],qr[2];
measure qr[0] -> cr[0];
measure qr[1] -> cr[1];
measure qr[2] -> cr[2];
"""
QuantumComputation.from_qasm(qasm_string_a)
# RuntimeError: <input>:4:6: Expected 'Identifier', got 'ms'.

However, the same circuit can be parsed without issue using Qiskit's qasm module.

from mqt.core import QuantumComputation
qasm_string_a = """
OPENQASM 2.0;
include "qelib1.inc";
gate ms_test(param0) q0,q1,q2 { rxx(0.844396) q0,q1; rxx(0.844396) q0,q2; rxx(0.844396) q1,q2; }
qreg qr[3];
creg cr[3];
ms_test(0.844396) qr[0],qr[1],qr[2];
measure qr[0] -> cr[0];
measure qr[1] -> cr[1];
measure qr[2] -> cr[2];
"""
QuantumComputation.from_qasm(qasm_string_a)

Output:


i:   0   1   2
1:------------
 : rxx rxx   |  p: (0.844396)
 : rxx   | rxx  p: (0.844396)
 :   | rxx rxx  p: (0.844396)
 -------------
2:   0   |   |
3:   |   1   |
4:   |   |   2
o:   0   1   2

Possible Problem

The issue seems to be related to the parsing of the ms gate. The error message indicates the problem occurs when ms is used, suggesting that the parser might be incorrectly handling this gate's name. This could be due to the parser's check for expected tokens in mqt-core/include/mqt-core/ir/parsers/qasm3_parser/Scanner.hpp.

void expect(const char expected) {
    if (ch != expected) {
        error("Expected '" + std::to_string(expected) + "', got '" + ch + "'");
    } else {
        nextCh();
    }
}
burgholzer commented 1 month ago

Thanks for raising this issue! This is definitely an issue. The underlying problem is that the parser interprets "ms" as a time unit parser token for milliseconds. So the parser expects an identifier after the "gate" token, but gets a TimeUnit token, which is what triggers the error.

AFAIK, "ms" is not a reserved keyword in OpenQASM 2, but it is used in OpenQASM 3 for specifying delays and durations.

Could you maybe check if Qiskit's OpenQASM 3 exporter also yields an "ms" gate declaration? (You just need to change the qasm2 to qasm3 in your code).

By the way: if you work in Qiskit anyways, you can always just pass the Qiskit circuits directly to QCEC instead of the QASM files. Our Qiskit parser should work out of the box in that regard even if the gate is called "ms".

I'll try to see what I can do to address the underlying issue in the parser.

burgholzer commented 1 month ago

Alright, the underlying error was mainly due to how we previously handled timing literals. #724 fixes this. Thanks for bringing this up 🙏🏼