Skip to content

Commit 0f174c1

Browse files
Added QASM Import Support for rzz, rxx, ryy, crx, cry, and iswap Gates (#7290)
## Description Fixes the code in #7007 Builds on #7072 Newly Supported Gates: rzz: Imported as cirq.ZZPowGate rxx: Imported as cirq.XXPowGate ryy: Imported as cirq.YYPowGate crx: Imported as cirq.ControlledGate(cirq.Rx) cry: Imported as cirq.ControlledGate(cirq.Ry) iswap: Imported as cirq.ISwapPowGate Added unit tests for these gates, and turned gates into alphabetical order for future additions --------- Co-authored-by: Michael Hucka <mhucka@caltech.edu>
1 parent c45df8b commit 0f174c1

File tree

2 files changed

+243
-54
lines changed

2 files changed

+243
-54
lines changed

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

Lines changed: 86 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -228,41 +228,54 @@ def __init__(self) -> None:
228228
}
229229

230230
qelib_gates = {
231-
'rx': QasmGateStatement(
232-
qasm_gate='rx', cirq_gate=(lambda params: ops.rx(params[0])), num_params=1, num_args=1
233-
),
234-
'sx': QasmGateStatement(
235-
qasm_gate='sx', num_params=0, num_args=1, cirq_gate=ops.XPowGate(exponent=0.5)
231+
'ccx': QasmGateStatement(qasm_gate='ccx', num_params=0, num_args=3, cirq_gate=ops.CCX),
232+
'ch': QasmGateStatement(
233+
qasm_gate='ch', cirq_gate=ops.ControlledGate(ops.H), num_params=0, num_args=2
236234
),
237-
'sxdg': QasmGateStatement(
238-
qasm_gate='sxdg', num_params=0, num_args=1, cirq_gate=ops.XPowGate(exponent=-0.5)
235+
'crx': QasmGateStatement(
236+
qasm_gate='crx',
237+
num_params=1,
238+
num_args=2,
239+
cirq_gate=(lambda params: ops.ControlledGate(ops.rx(params[0]))),
239240
),
240-
'ry': QasmGateStatement(
241-
qasm_gate='ry', cirq_gate=(lambda params: ops.ry(params[0])), num_params=1, num_args=1
241+
'cry': QasmGateStatement(
242+
qasm_gate='cry',
243+
num_params=1,
244+
num_args=2,
245+
cirq_gate=(lambda params: ops.ControlledGate(ops.ry(params[0]))),
242246
),
243-
'rz': QasmGateStatement(
244-
qasm_gate='rz', cirq_gate=(lambda params: ops.rz(params[0])), num_params=1, num_args=1
247+
'crz': QasmGateStatement(
248+
qasm_gate='crz',
249+
num_params=1,
250+
num_args=2,
251+
cirq_gate=(lambda params: ops.ControlledGate(ops.rz(params[0]))),
245252
),
246-
'id': QasmGateStatement(
247-
qasm_gate='id', cirq_gate=ops.IdentityGate(1), num_params=0, num_args=1
253+
'cswap': QasmGateStatement(
254+
qasm_gate='cswap', num_params=0, num_args=3, cirq_gate=ops.CSWAP
248255
),
249-
'u1': QasmGateStatement(
250-
qasm_gate='u1',
251-
cirq_gate=(lambda params: QasmUGate(0, 0, params[0] / np.pi)),
256+
'cu1': QasmGateStatement(
257+
qasm_gate='cu1',
252258
num_params=1,
253-
num_args=1,
254-
),
255-
'u2': QasmGateStatement(
256-
qasm_gate='u2',
257-
cirq_gate=(lambda params: QasmUGate(0.5, params[0] / np.pi, params[1] / np.pi)),
258-
num_params=2,
259-
num_args=1,
259+
num_args=2,
260+
cirq_gate=(lambda params: ops.ControlledGate(QasmUGate(0, 0, params[0] / np.pi))),
260261
),
261-
'u3': QasmGateStatement(
262-
qasm_gate='u3',
262+
'cu3': QasmGateStatement(
263+
qasm_gate='cu3',
263264
num_params=3,
264-
num_args=1,
265-
cirq_gate=(lambda params: QasmUGate(*[p / np.pi for p in params])),
265+
num_args=2,
266+
cirq_gate=(lambda params: ops.ControlledGate(QasmUGate(*[p / np.pi for p in params]))),
267+
),
268+
'cx': QasmGateStatement(qasm_gate='cx', cirq_gate=CX, num_params=0, num_args=2),
269+
'cy': QasmGateStatement(
270+
qasm_gate='cy', cirq_gate=ops.ControlledGate(ops.Y), num_params=0, num_args=2
271+
),
272+
'cz': QasmGateStatement(qasm_gate='cz', cirq_gate=ops.CZ, num_params=0, num_args=2),
273+
'h': QasmGateStatement(qasm_gate='h', num_params=0, num_args=1, cirq_gate=ops.H),
274+
'id': QasmGateStatement(
275+
qasm_gate='id', cirq_gate=ops.IdentityGate(1), num_params=0, num_args=1
276+
),
277+
'iswap': QasmGateStatement(
278+
qasm_gate='iswap', cirq_gate=ops.ISwapPowGate(), num_params=0, num_args=2
266279
),
267280
'r': QasmGateStatement(
268281
qasm_gate='r',
@@ -274,45 +287,65 @@ def __init__(self) -> None:
274287
)
275288
),
276289
),
277-
'x': QasmGateStatement(qasm_gate='x', num_params=0, num_args=1, cirq_gate=ops.X),
278-
'y': QasmGateStatement(qasm_gate='y', num_params=0, num_args=1, cirq_gate=ops.Y),
279-
'z': QasmGateStatement(qasm_gate='z', num_params=0, num_args=1, cirq_gate=ops.Z),
280-
'h': QasmGateStatement(qasm_gate='h', num_params=0, num_args=1, cirq_gate=ops.H),
281-
's': QasmGateStatement(qasm_gate='s', num_params=0, num_args=1, cirq_gate=ops.S),
282-
't': QasmGateStatement(qasm_gate='t', num_params=0, num_args=1, cirq_gate=ops.T),
283-
'cx': QasmGateStatement(qasm_gate='cx', cirq_gate=CX, num_params=0, num_args=2),
284-
'cy': QasmGateStatement(
285-
qasm_gate='cy', cirq_gate=ops.ControlledGate(ops.Y), num_params=0, num_args=2
290+
'rx': QasmGateStatement(
291+
qasm_gate='rx', cirq_gate=(lambda params: ops.rx(params[0])), num_params=1, num_args=1
286292
),
287-
'cz': QasmGateStatement(qasm_gate='cz', cirq_gate=ops.CZ, num_params=0, num_args=2),
288-
'ch': QasmGateStatement(
289-
qasm_gate='ch', cirq_gate=ops.ControlledGate(ops.H), num_params=0, num_args=2
293+
'ry': QasmGateStatement(
294+
qasm_gate='ry', cirq_gate=(lambda params: ops.ry(params[0])), num_params=1, num_args=1
290295
),
291-
'cu1': QasmGateStatement(
292-
qasm_gate='cu1',
296+
'ryy': QasmGateStatement(
297+
qasm_gate='ryy',
293298
num_params=1,
294299
num_args=2,
295-
cirq_gate=(lambda params: ops.ControlledGate(QasmUGate(0, 0, params[0] / np.pi))),
300+
cirq_gate=(lambda params: ops.YYPowGate(exponent=params[0] / np.pi)),
296301
),
297-
'cu3': QasmGateStatement(
298-
qasm_gate='cu3',
299-
num_params=3,
302+
'rz': QasmGateStatement(
303+
qasm_gate='rz', cirq_gate=(lambda params: ops.rz(params[0])), num_params=1, num_args=1
304+
),
305+
'rxx': QasmGateStatement(
306+
qasm_gate='rxx',
307+
num_params=1,
300308
num_args=2,
301-
cirq_gate=(lambda params: ops.ControlledGate(QasmUGate(*[p / np.pi for p in params]))),
309+
cirq_gate=(lambda params: ops.XXPowGate(exponent=params[0] / np.pi)),
302310
),
303-
'crz': QasmGateStatement(
304-
qasm_gate='crz',
311+
'rzz': QasmGateStatement(
312+
qasm_gate='rzz',
305313
num_params=1,
306314
num_args=2,
307-
cirq_gate=(lambda params: ops.ControlledGate(ops.rz(params[0]))),
315+
cirq_gate=(lambda params: ops.ZZPowGate(exponent=params[0] / np.pi)),
308316
),
317+
's': QasmGateStatement(qasm_gate='s', num_params=0, num_args=1, cirq_gate=ops.S),
318+
'sdg': QasmGateStatement(qasm_gate='sdg', num_params=0, num_args=1, cirq_gate=ops.S**-1),
309319
'swap': QasmGateStatement(qasm_gate='swap', cirq_gate=ops.SWAP, num_params=0, num_args=2),
310-
'cswap': QasmGateStatement(
311-
qasm_gate='cswap', num_params=0, num_args=3, cirq_gate=ops.CSWAP
320+
'sx': QasmGateStatement(
321+
qasm_gate='sx', num_params=0, num_args=1, cirq_gate=ops.XPowGate(exponent=0.5)
312322
),
313-
'ccx': QasmGateStatement(qasm_gate='ccx', num_params=0, num_args=3, cirq_gate=ops.CCX),
314-
'sdg': QasmGateStatement(qasm_gate='sdg', num_params=0, num_args=1, cirq_gate=ops.S**-1),
323+
'sxdg': QasmGateStatement(
324+
qasm_gate='sxdg', num_params=0, num_args=1, cirq_gate=ops.XPowGate(exponent=-0.5)
325+
),
326+
't': QasmGateStatement(qasm_gate='t', num_params=0, num_args=1, cirq_gate=ops.T),
315327
'tdg': QasmGateStatement(qasm_gate='tdg', num_params=0, num_args=1, cirq_gate=ops.T**-1),
328+
'u1': QasmGateStatement(
329+
qasm_gate='u1',
330+
cirq_gate=(lambda params: QasmUGate(0, 0, params[0] / np.pi)),
331+
num_params=1,
332+
num_args=1,
333+
),
334+
'u2': QasmGateStatement(
335+
qasm_gate='u2',
336+
cirq_gate=(lambda params: QasmUGate(0.5, params[0] / np.pi, params[1] / np.pi)),
337+
num_params=2,
338+
num_args=1,
339+
),
340+
'u3': QasmGateStatement(
341+
qasm_gate='u3',
342+
num_params=3,
343+
num_args=1,
344+
cirq_gate=(lambda params: QasmUGate(*[p / np.pi for p in params])),
345+
),
346+
'x': QasmGateStatement(qasm_gate='x', num_params=0, num_args=1, cirq_gate=ops.X),
347+
'y': QasmGateStatement(qasm_gate='y', num_params=0, num_args=1, cirq_gate=ops.Y),
348+
'z': QasmGateStatement(qasm_gate='z', num_params=0, num_args=1, cirq_gate=ops.Z),
316349
}
317350

318351
tokens = QasmLexer.tokens

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

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1053,7 +1053,7 @@ def test_two_qubit_gates_not_enough_args(qasm_gate: str) -> None:
10531053
include "qelib1.inc";
10541054
qreg q[2];
10551055
{qasm_gate} q[0];
1056-
"""
1056+
"""
10571057

10581058
parser = QasmParser()
10591059

@@ -1514,3 +1514,159 @@ def test_nested_custom_gate_has_keyword_in_name() -> None:
15141514
parser = QasmParser()
15151515
parsed_qasm = parser.parse(qasm)
15161516
assert parsed_qasm.circuit == expected
1517+
1518+
1519+
def test_rzz_gate():
1520+
qasm = """
1521+
OPENQASM 2.0;
1522+
include "qelib1.inc";
1523+
qreg q[2];
1524+
rzz(pi/2) q[0],q[1];
1525+
"""
1526+
parser = QasmParser()
1527+
1528+
q0, q1 = cirq.NamedQubit('q_0'), cirq.NamedQubit('q_1')
1529+
1530+
expected_circuit = Circuit()
1531+
expected_circuit.append(cirq.ZZPowGate(exponent=0.5).on(q0, q1))
1532+
1533+
parsed_qasm = parser.parse(qasm)
1534+
1535+
assert parsed_qasm.supportedFormat
1536+
assert parsed_qasm.qelib1Include
1537+
1538+
ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit)
1539+
assert parsed_qasm.qregs == {'q': 2}
1540+
1541+
1542+
def test_rxx_gate():
1543+
qasm = """
1544+
OPENQASM 2.0;
1545+
include "qelib1.inc";
1546+
qreg q[2];
1547+
rxx(pi/4) q[0],q[1];
1548+
"""
1549+
parser = QasmParser()
1550+
1551+
q0, q1 = cirq.NamedQubit('q_0'), cirq.NamedQubit('q_1')
1552+
1553+
expected_circuit = Circuit()
1554+
expected_circuit.append(cirq.XXPowGate(exponent=0.25).on(q0, q1))
1555+
1556+
parsed_qasm = parser.parse(qasm)
1557+
1558+
assert parsed_qasm.supportedFormat
1559+
assert parsed_qasm.qelib1Include
1560+
1561+
ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit)
1562+
assert parsed_qasm.qregs == {'q': 2}
1563+
1564+
1565+
def test_ryy_gate():
1566+
qasm = """
1567+
OPENQASM 2.0;
1568+
include "qelib1.inc";
1569+
qreg q[2];
1570+
ryy(pi/3) q[0],q[1];
1571+
"""
1572+
parser = QasmParser()
1573+
1574+
q0, q1 = cirq.NamedQubit('q_0'), cirq.NamedQubit('q_1')
1575+
1576+
expected_circuit = Circuit()
1577+
expected_circuit.append(cirq.YYPowGate(exponent=1 / 3).on(q0, q1))
1578+
1579+
parsed_qasm = parser.parse(qasm)
1580+
1581+
assert parsed_qasm.supportedFormat
1582+
assert parsed_qasm.qelib1Include
1583+
1584+
ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit)
1585+
assert parsed_qasm.qregs == {'q': 2}
1586+
1587+
1588+
def test_crx_gate():
1589+
qasm = """
1590+
OPENQASM 2.0;
1591+
include "qelib1.inc";
1592+
qreg q[2];
1593+
crx(pi/7) q[0],q[1];
1594+
"""
1595+
parser = QasmParser()
1596+
1597+
q0, q1 = cirq.NamedQubit('q_0'), cirq.NamedQubit('q_1')
1598+
1599+
expected_circuit = Circuit()
1600+
expected_circuit.append(cirq.ControlledGate(cirq.rx(np.pi / 7)).on(q0, q1))
1601+
1602+
parsed_qasm = parser.parse(qasm)
1603+
1604+
assert parsed_qasm.supportedFormat
1605+
assert parsed_qasm.qelib1Include
1606+
1607+
ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit)
1608+
assert parsed_qasm.qregs == {'q': 2}
1609+
1610+
1611+
def test_iswap_gate():
1612+
qasm = """
1613+
OPENQASM 2.0;
1614+
include "qelib1.inc";
1615+
qreg q[2];
1616+
iswap q[0],q[1];
1617+
"""
1618+
parser = QasmParser()
1619+
1620+
q0, q1 = cirq.NamedQubit('q_0'), cirq.NamedQubit('q_1')
1621+
1622+
expected_circuit = Circuit()
1623+
expected_circuit.append(cirq.ISwapPowGate().on(q0, q1))
1624+
1625+
parsed_qasm = parser.parse(qasm)
1626+
1627+
assert parsed_qasm.supportedFormat
1628+
assert parsed_qasm.qelib1Include
1629+
1630+
ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit)
1631+
assert parsed_qasm.qregs == {'q': 2}
1632+
1633+
1634+
@pytest.mark.parametrize(
1635+
"qasm_gate,cirq_gate,num_params,num_args",
1636+
[
1637+
(name, stmt.cirq_gate, stmt.num_params, stmt.num_args)
1638+
for name, stmt in QasmParser.qelib_gates.items()
1639+
],
1640+
)
1641+
@pytest.mark.parametrize(
1642+
"theta,theta_str", [(np.pi / 4, "pi/4"), (np.pi / 2, "pi/2"), (np.pi, "pi")]
1643+
)
1644+
def test_all_qelib_gates_unitary_equivalence(
1645+
qasm_gate, cirq_gate, num_params, num_args, theta, theta_str
1646+
):
1647+
thetas = [theta] * num_params
1648+
params_str = f"({','.join(theta_str for _ in range(num_params))})" if num_params else ""
1649+
qubit_names, qubits = [], []
1650+
for i in range(num_args):
1651+
qubit_names.append(f"q[{i}]")
1652+
qubits.append(cirq.NamedQubit(f"q_{i}"))
1653+
qasm = f"""
1654+
OPENQASM 2.0;
1655+
include "qelib1.inc";
1656+
qreg q[{num_args}];
1657+
{qasm_gate}{params_str} {','.join(qubit_names)};
1658+
"""
1659+
1660+
parser = QasmParser()
1661+
parsed_qasm = parser.parse(qasm)
1662+
if num_params:
1663+
gate = cirq_gate(thetas)
1664+
else:
1665+
gate = cirq_gate
1666+
expected = Circuit()
1667+
expected.append(gate.on(*qubits))
1668+
imported = list(parsed_qasm.circuit.all_operations())[0].gate
1669+
U_native = cirq.unitary(gate)
1670+
U_import = cirq.unitary(imported)
1671+
assert np.allclose(U_import, U_native, atol=1e-8)
1672+
assert parsed_qasm.qregs == {'q': num_args}

0 commit comments

Comments
 (0)