diff --git a/cirq-core/cirq/contrib/qasm_import/_parser.py b/cirq-core/cirq/contrib/qasm_import/_parser.py index 29cb0d34726..80a7771ba07 100644 --- a/cirq-core/cirq/contrib/qasm_import/_parser.py +++ b/cirq-core/cirq/contrib/qasm_import/_parser.py @@ -238,41 +238,54 @@ def __init__(self) -> None: } qelib_gates = { - 'rx': QasmGateStatement( - qasm_gate='rx', cirq_gate=(lambda params: ops.rx(params[0])), num_params=1, num_args=1 - ), - 'sx': QasmGateStatement( - qasm_gate='sx', num_params=0, num_args=1, cirq_gate=ops.XPowGate(exponent=0.5) + 'ccx': QasmGateStatement(qasm_gate='ccx', num_params=0, num_args=3, cirq_gate=ops.CCX), + 'ch': QasmGateStatement( + qasm_gate='ch', cirq_gate=ops.ControlledGate(ops.H), num_params=0, num_args=2 ), - 'sxdg': QasmGateStatement( - qasm_gate='sxdg', num_params=0, num_args=1, cirq_gate=ops.XPowGate(exponent=-0.5) + 'crx': QasmGateStatement( + qasm_gate='crx', + num_params=1, + num_args=2, + cirq_gate=(lambda params: ops.ControlledGate(ops.rx(params[0]))), ), - 'ry': QasmGateStatement( - qasm_gate='ry', cirq_gate=(lambda params: ops.ry(params[0])), num_params=1, num_args=1 + 'cry': QasmGateStatement( + qasm_gate='cry', + num_params=1, + num_args=2, + cirq_gate=(lambda params: ops.ControlledGate(ops.ry(params[0]))), ), - 'rz': QasmGateStatement( - qasm_gate='rz', cirq_gate=(lambda params: ops.rz(params[0])), num_params=1, num_args=1 + 'crz': QasmGateStatement( + qasm_gate='crz', + num_params=1, + num_args=2, + cirq_gate=(lambda params: ops.ControlledGate(ops.rz(params[0]))), ), - 'id': QasmGateStatement( - qasm_gate='id', cirq_gate=ops.IdentityGate(1), num_params=0, num_args=1 + 'cswap': QasmGateStatement( + qasm_gate='cswap', num_params=0, num_args=3, cirq_gate=ops.CSWAP ), - 'u1': QasmGateStatement( - qasm_gate='u1', - cirq_gate=(lambda params: QasmUGate(0, 0, params[0] / np.pi)), + 'cu1': QasmGateStatement( + qasm_gate='cu1', num_params=1, - num_args=1, - ), - 'u2': QasmGateStatement( - qasm_gate='u2', - cirq_gate=(lambda params: QasmUGate(0.5, params[0] / np.pi, params[1] / np.pi)), - num_params=2, - num_args=1, + num_args=2, + cirq_gate=(lambda params: ops.ControlledGate(QasmUGate(0, 0, params[0] / np.pi))), ), - 'u3': QasmGateStatement( - qasm_gate='u3', + 'cu3': QasmGateStatement( + qasm_gate='cu3', num_params=3, - num_args=1, - cirq_gate=(lambda params: QasmUGate(*[p / np.pi for p in params])), + num_args=2, + cirq_gate=(lambda params: ops.ControlledGate(QasmUGate(*[p / np.pi for p in params]))), + ), + 'cx': QasmGateStatement(qasm_gate='cx', cirq_gate=CX, num_params=0, num_args=2), + 'cy': QasmGateStatement( + qasm_gate='cy', cirq_gate=ops.ControlledGate(ops.Y), num_params=0, num_args=2 + ), + 'cz': QasmGateStatement(qasm_gate='cz', cirq_gate=ops.CZ, num_params=0, num_args=2), + 'h': QasmGateStatement(qasm_gate='h', num_params=0, num_args=1, cirq_gate=ops.H), + 'id': QasmGateStatement( + qasm_gate='id', cirq_gate=ops.IdentityGate(1), num_params=0, num_args=1 + ), + 'iswap': QasmGateStatement( + qasm_gate='iswap', cirq_gate=ops.ISwapPowGate(), num_params=0, num_args=2 ), 'r': QasmGateStatement( qasm_gate='r', @@ -284,45 +297,65 @@ def __init__(self) -> None: ) ), ), - 'x': QasmGateStatement(qasm_gate='x', num_params=0, num_args=1, cirq_gate=ops.X), - 'y': QasmGateStatement(qasm_gate='y', num_params=0, num_args=1, cirq_gate=ops.Y), - 'z': QasmGateStatement(qasm_gate='z', num_params=0, num_args=1, cirq_gate=ops.Z), - 'h': QasmGateStatement(qasm_gate='h', num_params=0, num_args=1, cirq_gate=ops.H), - 's': QasmGateStatement(qasm_gate='s', num_params=0, num_args=1, cirq_gate=ops.S), - 't': QasmGateStatement(qasm_gate='t', num_params=0, num_args=1, cirq_gate=ops.T), - 'cx': QasmGateStatement(qasm_gate='cx', cirq_gate=CX, num_params=0, num_args=2), - 'cy': QasmGateStatement( - qasm_gate='cy', cirq_gate=ops.ControlledGate(ops.Y), num_params=0, num_args=2 + 'rx': QasmGateStatement( + qasm_gate='rx', cirq_gate=(lambda params: ops.rx(params[0])), num_params=1, num_args=1 ), - 'cz': QasmGateStatement(qasm_gate='cz', cirq_gate=ops.CZ, num_params=0, num_args=2), - 'ch': QasmGateStatement( - qasm_gate='ch', cirq_gate=ops.ControlledGate(ops.H), num_params=0, num_args=2 + 'ry': QasmGateStatement( + qasm_gate='ry', cirq_gate=(lambda params: ops.ry(params[0])), num_params=1, num_args=1 ), - 'cu1': QasmGateStatement( - qasm_gate='cu1', + 'ryy': QasmGateStatement( + qasm_gate='ryy', num_params=1, num_args=2, - cirq_gate=(lambda params: ops.ControlledGate(QasmUGate(0, 0, params[0] / np.pi))), + cirq_gate=(lambda params: ops.YYPowGate(exponent=params[0] / np.pi)), ), - 'cu3': QasmGateStatement( - qasm_gate='cu3', - num_params=3, + 'rz': QasmGateStatement( + qasm_gate='rz', cirq_gate=(lambda params: ops.rz(params[0])), num_params=1, num_args=1 + ), + 'rxx': QasmGateStatement( + qasm_gate='rxx', + num_params=1, num_args=2, - cirq_gate=(lambda params: ops.ControlledGate(QasmUGate(*[p / np.pi for p in params]))), + cirq_gate=(lambda params: ops.XXPowGate(exponent=params[0] / np.pi)), ), - 'crz': QasmGateStatement( - qasm_gate='crz', + 'rzz': QasmGateStatement( + qasm_gate='rzz', num_params=1, num_args=2, - cirq_gate=(lambda params: ops.ControlledGate(ops.rz(params[0]))), + cirq_gate=(lambda params: ops.ZZPowGate(exponent=params[0] / np.pi)), ), + 's': QasmGateStatement(qasm_gate='s', num_params=0, num_args=1, cirq_gate=ops.S), + 'sdg': QasmGateStatement(qasm_gate='sdg', num_params=0, num_args=1, cirq_gate=ops.S**-1), 'swap': QasmGateStatement(qasm_gate='swap', cirq_gate=ops.SWAP, num_params=0, num_args=2), - 'cswap': QasmGateStatement( - qasm_gate='cswap', num_params=0, num_args=3, cirq_gate=ops.CSWAP + 'sx': QasmGateStatement( + qasm_gate='sx', num_params=0, num_args=1, cirq_gate=ops.XPowGate(exponent=0.5) ), - 'ccx': QasmGateStatement(qasm_gate='ccx', num_params=0, num_args=3, cirq_gate=ops.CCX), - 'sdg': QasmGateStatement(qasm_gate='sdg', num_params=0, num_args=1, cirq_gate=ops.S**-1), + 'sxdg': QasmGateStatement( + qasm_gate='sxdg', num_params=0, num_args=1, cirq_gate=ops.XPowGate(exponent=-0.5) + ), + 't': QasmGateStatement(qasm_gate='t', num_params=0, num_args=1, cirq_gate=ops.T), 'tdg': QasmGateStatement(qasm_gate='tdg', num_params=0, num_args=1, cirq_gate=ops.T**-1), + 'u1': QasmGateStatement( + qasm_gate='u1', + cirq_gate=(lambda params: QasmUGate(0, 0, params[0] / np.pi)), + num_params=1, + num_args=1, + ), + 'u2': QasmGateStatement( + qasm_gate='u2', + cirq_gate=(lambda params: QasmUGate(0.5, params[0] / np.pi, params[1] / np.pi)), + num_params=2, + num_args=1, + ), + 'u3': QasmGateStatement( + qasm_gate='u3', + num_params=3, + num_args=1, + cirq_gate=(lambda params: QasmUGate(*[p / np.pi for p in params])), + ), + 'x': QasmGateStatement(qasm_gate='x', num_params=0, num_args=1, cirq_gate=ops.X), + 'y': QasmGateStatement(qasm_gate='y', num_params=0, num_args=1, cirq_gate=ops.Y), + 'z': QasmGateStatement(qasm_gate='z', num_params=0, num_args=1, cirq_gate=ops.Z), } tokens = QasmLexer.tokens diff --git a/cirq-core/cirq/contrib/qasm_import/_parser_test.py b/cirq-core/cirq/contrib/qasm_import/_parser_test.py index f0239b468c0..2f7c1467864 100644 --- a/cirq-core/cirq/contrib/qasm_import/_parser_test.py +++ b/cirq-core/cirq/contrib/qasm_import/_parser_test.py @@ -1051,7 +1051,7 @@ def test_two_qubit_gates_not_enough_args(qasm_gate: str): include "qelib1.inc"; qreg q[2]; {qasm_gate} q[0]; -""" + """ parser = QasmParser() @@ -1512,3 +1512,159 @@ def test_nested_custom_gate_has_keyword_in_name(): parser = QasmParser() parsed_qasm = parser.parse(qasm) assert parsed_qasm.circuit == expected + + +def test_rzz_gate(): + qasm = """ + OPENQASM 2.0; + include "qelib1.inc"; + qreg q[2]; + rzz(pi/2) q[0],q[1]; + """ + parser = QasmParser() + + q0, q1 = cirq.NamedQubit('q_0'), cirq.NamedQubit('q_1') + + expected_circuit = Circuit() + expected_circuit.append(cirq.ZZPowGate(exponent=0.5).on(q0, q1)) + + parsed_qasm = parser.parse(qasm) + + assert parsed_qasm.supportedFormat + assert parsed_qasm.qelib1Include + + ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit) + assert parsed_qasm.qregs == {'q': 2} + + +def test_rxx_gate(): + qasm = """ + OPENQASM 2.0; + include "qelib1.inc"; + qreg q[2]; + rxx(pi/4) q[0],q[1]; + """ + parser = QasmParser() + + q0, q1 = cirq.NamedQubit('q_0'), cirq.NamedQubit('q_1') + + expected_circuit = Circuit() + expected_circuit.append(cirq.XXPowGate(exponent=0.25).on(q0, q1)) + + parsed_qasm = parser.parse(qasm) + + assert parsed_qasm.supportedFormat + assert parsed_qasm.qelib1Include + + ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit) + assert parsed_qasm.qregs == {'q': 2} + + +def test_ryy_gate(): + qasm = """ + OPENQASM 2.0; + include "qelib1.inc"; + qreg q[2]; + ryy(pi/3) q[0],q[1]; + """ + parser = QasmParser() + + q0, q1 = cirq.NamedQubit('q_0'), cirq.NamedQubit('q_1') + + expected_circuit = Circuit() + expected_circuit.append(cirq.YYPowGate(exponent=1 / 3).on(q0, q1)) + + parsed_qasm = parser.parse(qasm) + + assert parsed_qasm.supportedFormat + assert parsed_qasm.qelib1Include + + ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit) + assert parsed_qasm.qregs == {'q': 2} + + +def test_crx_gate(): + qasm = """ + OPENQASM 2.0; + include "qelib1.inc"; + qreg q[2]; + crx(pi/7) q[0],q[1]; + """ + parser = QasmParser() + + q0, q1 = cirq.NamedQubit('q_0'), cirq.NamedQubit('q_1') + + expected_circuit = Circuit() + expected_circuit.append(cirq.ControlledGate(cirq.rx(np.pi / 7)).on(q0, q1)) + + parsed_qasm = parser.parse(qasm) + + assert parsed_qasm.supportedFormat + assert parsed_qasm.qelib1Include + + ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit) + assert parsed_qasm.qregs == {'q': 2} + + +def test_iswap_gate(): + qasm = """ + OPENQASM 2.0; + include "qelib1.inc"; + qreg q[2]; + iswap q[0],q[1]; + """ + parser = QasmParser() + + q0, q1 = cirq.NamedQubit('q_0'), cirq.NamedQubit('q_1') + + expected_circuit = Circuit() + expected_circuit.append(cirq.ISwapPowGate().on(q0, q1)) + + parsed_qasm = parser.parse(qasm) + + assert parsed_qasm.supportedFormat + assert parsed_qasm.qelib1Include + + ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit) + assert parsed_qasm.qregs == {'q': 2} + + +@pytest.mark.parametrize( + "qasm_gate,cirq_gate,num_params,num_args", + [ + (name, stmt.cirq_gate, stmt.num_params, stmt.num_args) + for name, stmt in QasmParser.qelib_gates.items() + ], +) +@pytest.mark.parametrize( + "theta,theta_str", [(np.pi / 4, "pi/4"), (np.pi / 2, "pi/2"), (np.pi, "pi")] +) +def test_all_qelib_gates_unitary_equivalence( + qasm_gate, cirq_gate, num_params, num_args, theta, theta_str +): + thetas = [theta] * num_params + params_str = f"({','.join(theta_str for _ in range(num_params))})" if num_params else "" + qubit_names, qubits = [], [] + for i in range(num_args): + qubit_names.append(f"q[{i}]") + qubits.append(cirq.NamedQubit(f"q_{i}")) + qasm = f""" + OPENQASM 2.0; + include "qelib1.inc"; + qreg q[{num_args}]; + {qasm_gate}{params_str} {','.join(qubit_names)}; + """ + + parser = QasmParser() + parsed_qasm = parser.parse(qasm) + if num_params: + gate = cirq_gate(thetas) + else: + gate = cirq_gate + expected = Circuit() + expected.append(gate.on(*qubits)) + imported = list(parsed_qasm.circuit.all_operations())[0].gate + U_native = cirq.unitary(gate) + U_import = cirq.unitary(imported) + assert np.allclose(U_import, U_native, atol=1e-8) + assert parsed_qasm.qregs == {'q': num_args}