Skip to content

Commit 02f2f84

Browse files
authored
Preliminary support for parsing OpenQASM 3.0 (#6797)
* Preliminary support for parsing OpenQASM 3.0 - This adjusts the lexer and parser to handle programs from OpenQASM 3.0. - This also adds the capability for scalar qubits.
1 parent f98490f commit 02f2f84

File tree

3 files changed

+107
-12
lines changed

3 files changed

+107
-12
lines changed

cirq-core/cirq/contrib/qasm_import/_lexer.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,28 @@ class QasmLexer:
2424
def __init__(self):
2525
self.lex = lex.lex(object=self, debug=False)
2626

27-
literals = "{}[]();,+/*-^"
27+
literals = "{}[]();,+/*-^="
2828

2929
reserved = {
30+
'qubit': 'QUBIT',
3031
'qreg': 'QREG',
32+
'bit': 'BIT',
3133
'creg': 'CREG',
3234
'measure': 'MEASURE',
3335
'if': 'IF',
3436
'->': 'ARROW',
3537
'==': 'EQ',
3638
}
3739

38-
tokens = ['FORMAT_SPEC', 'NUMBER', 'NATURAL_NUMBER', 'QELIBINC', 'ID', 'PI'] + list(
39-
reserved.values()
40-
)
40+
tokens = [
41+
'FORMAT_SPEC',
42+
'NUMBER',
43+
'NATURAL_NUMBER',
44+
'STDGATESINC',
45+
'QELIBINC',
46+
'ID',
47+
'PI',
48+
] + list(reserved.values())
4149

4250
def t_newline(self, t):
4351
r"""\n+"""
@@ -83,14 +91,26 @@ def t_QELIBINC(self, t):
8391
r"""include(\s+)"qelib1.inc";"""
8492
return t
8593

94+
def t_STDGATESINC(self, t):
95+
r"""include(\s+)"stdgates.inc";"""
96+
return t
97+
8698
def t_QREG(self, t):
8799
r"""qreg"""
88100
return t
89101

102+
def t_QUBIT(self, t):
103+
r"""qubit"""
104+
return t
105+
90106
def t_CREG(self, t):
91107
r"""creg"""
92108
return t
93109

110+
def t_BIT(self, t):
111+
r"""bit"""
112+
return t
113+
94114
def t_MEASURE(self, t):
95115
r"""measure"""
96116
return t

cirq-core/cirq/contrib/qasm_import/_parser.py

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ def p_qasm_format_only(self, p):
270270

271271
def p_qasm_no_format_specified_error(self, p):
272272
"""qasm : QELIBINC
273+
| STDGATESINC
273274
| circuit"""
274275
if self.supported_format is False:
275276
raise QasmException("Missing 'OPENQASM 2.0;' statement")
@@ -279,15 +280,21 @@ def p_qasm_include(self, p):
279280
self.qelibinc = True
280281
p[0] = Qasm(self.supported_format, self.qelibinc, self.qregs, self.cregs, self.circuit)
281282

283+
def p_qasm_include_stdgates(self, p):
284+
"""qasm : qasm STDGATESINC"""
285+
self.qelibinc = True
286+
p[0] = Qasm(self.supported_format, self.qelibinc, self.qregs, self.cregs, self.circuit)
287+
282288
def p_qasm_circuit(self, p):
283289
"""qasm : qasm circuit"""
284290
p[0] = Qasm(self.supported_format, self.qelibinc, self.qregs, self.cregs, p[2])
285291

286292
def p_format(self, p):
287293
"""format : FORMAT_SPEC"""
288-
if p[1] != "2.0":
294+
if p[1] not in ["2.0", "3.0"]:
289295
raise QasmException(
290-
f"Unsupported OpenQASM version: {p[1]}, only 2.0 is supported currently by Cirq"
296+
f"Unsupported OpenQASM version: {p[1]}, "
297+
"only 2.0 and 3.0 are supported currently by Cirq"
291298
)
292299

293300
# circuit : new_reg circuit
@@ -315,13 +322,28 @@ def p_circuit_empty(self, p):
315322

316323
def p_new_reg(self, p):
317324
"""new_reg : QREG ID '[' NATURAL_NUMBER ']' ';'
318-
| CREG ID '[' NATURAL_NUMBER ']' ';'"""
319-
name, length = p[2], p[4]
325+
| QUBIT '[' NATURAL_NUMBER ']' ID ';'
326+
| QUBIT ID ';'
327+
| CREG ID '[' NATURAL_NUMBER ']' ';'
328+
| BIT '[' NATURAL_NUMBER ']' ID ';'
329+
| BIT ID ';'
330+
"""
331+
if p[1] == "qreg" or p[1] == "creg":
332+
# QREG ID '[' NATURAL_NUMBER ']' ';'
333+
name, length = p[2], p[4]
334+
else:
335+
if len(p) < 5:
336+
# QUBIT ID ';' | BIT ID ';'
337+
name = p[2]
338+
length = 1
339+
else:
340+
# QUBIT '[' NATURAL_NUMBER ']' ID ';'
341+
name, length = p[5], p[3]
320342
if name in self.qregs.keys() or name in self.cregs.keys():
321343
raise QasmException(f"{name} is already defined at line {p.lineno(2)}")
322344
if length == 0:
323345
raise QasmException(f"Illegal, zero-length register '{name}' at line {p.lineno(4)}")
324-
if p[1] == "qreg":
346+
if p[1] == "qreg" or p[1] == "qubit":
325347
self.qregs[name] = length
326348
else:
327349
self.cregs[name] = length
@@ -485,9 +507,14 @@ def p_classical_arg_bit(self, p):
485507
# measurement : MEASURE qarg ARROW carg
486508

487509
def p_measurement(self, p):
488-
"""measurement : MEASURE qarg ARROW carg ';'"""
489-
qreg = p[2]
490-
creg = p[4]
510+
"""measurement : MEASURE qarg ARROW carg ';'
511+
| carg '=' MEASURE qarg ';'"""
512+
if p[1] == 'measure':
513+
qreg = p[2]
514+
creg = p[4]
515+
else:
516+
qreg = p[4]
517+
creg = p[1]
491518

492519
if len(qreg) != len(creg):
493520
raise QasmException(

cirq-core/cirq/contrib/qasm_import/_parser_test.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,3 +1057,51 @@ def test_single_qubit_gates(qasm_gate: str, cirq_gate: cirq.Gate):
10571057

10581058
ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit)
10591059
assert parsed_qasm.qregs == {'q': 2}
1060+
1061+
1062+
def test_openqasm_3_0_qubits():
1063+
qasm = """OPENQASM 3.0;
1064+
include "stdgates.inc";
1065+
qubit[2] q;
1066+
bit[2] b;
1067+
1068+
x q[0];
1069+
1070+
b[0] = measure q[0];
1071+
"""
1072+
parser = QasmParser()
1073+
1074+
q0 = cirq.NamedQubit('q_0')
1075+
1076+
expected_circuit = Circuit([cirq.X.on(q0), cirq.measure(q0, key='b_0')])
1077+
1078+
parsed_qasm = parser.parse(qasm)
1079+
1080+
assert parsed_qasm.supportedFormat
1081+
1082+
ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit)
1083+
assert parsed_qasm.qregs == {'q': 2}
1084+
1085+
1086+
def test_openqasm_3_0_scalar_qubit():
1087+
qasm = """OPENQASM 3.0;
1088+
include "stdgates.inc";
1089+
qubit q;
1090+
bit b;
1091+
1092+
x q;
1093+
1094+
b = measure q;
1095+
"""
1096+
parser = QasmParser()
1097+
1098+
q0 = cirq.NamedQubit('q_0')
1099+
1100+
expected_circuit = Circuit([cirq.X.on(q0), cirq.measure(q0, key='b_0')])
1101+
1102+
parsed_qasm = parser.parse(qasm)
1103+
1104+
assert parsed_qasm.supportedFormat
1105+
1106+
ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit)
1107+
assert parsed_qasm.qregs == {'q': 1}

0 commit comments

Comments
 (0)