From 8aa10815153fc9d7b26897311f072259095fcbda Mon Sep 17 00:00:00 2001 From: Luciano Date: Fri, 10 May 2019 08:04:25 -0400 Subject: [PATCH] ast_to_dag support for opaque gates (#2343) * support for opaque gates * custom gate * cleaning up * remove test_custom_gate * lint * lint! * do not process standard lib --- qiskit/converters/ast_to_dag.py | 103 ++++++++---------- .../circuit/test_circuit_load_from_qasm.py | 18 +++ 2 files changed, 61 insertions(+), 60 deletions(-) diff --git a/qiskit/converters/ast_to_dag.py b/qiskit/converters/ast_to_dag.py index e95139977c..1ed3107955 100644 --- a/qiskit/converters/ast_to_dag.py +++ b/qiskit/converters/ast_to_dag.py @@ -18,8 +18,7 @@ AST (abstract syntax tree) to DAG (directed acyclic graph) converter. Acts as an OpenQASM interpreter. """ from collections import OrderedDict -from qiskit.circuit import QuantumRegister -from qiskit.circuit import ClassicalRegister +from qiskit.circuit import QuantumRegister, ClassicalRegister, Gate from qiskit.dagcircuit import DAGCircuit from qiskit.exceptions import QiskitError @@ -78,6 +77,34 @@ def ast_to_dag(ast): class AstInterpreter: """Interprets an OpenQASM by expanding subroutines and unrolling loops.""" + standard_extension = {"u0": U0Gate, + "u1": U1Gate, + "u2": U2Gate, + "u3": U3Gate, + "x": XGate, + "y": YGate, + "z": ZGate, + "t": TGate, + "tdg": TdgGate, + "s": SGate, + "sdg": SdgGate, + "swap": SwapGate, + "rx": RXGate, + "ry": RYGate, + "rz": RZGate, + "rzz": RZZGate, + "id": IdGate, + "h": HGate, + "cx": CnotGate, + "cy": CyGate, + "cz": CzGate, + "ch": CHGate, + "crz": CrzGate, + "cu1": Cu1Gate, + "cu3": Cu3Gate, + "ccx": ToffoliGate, + "cswap": FredkinGate} + def __init__(self, dag): """Initialize interpreter's data.""" # DAG object to populate @@ -144,7 +171,7 @@ class AstInterpreter: self.arg_stack.append({gargs[j]: args[j] for j in range(len(gargs))}) # Only index into register arguments. - element = [idx*x for x in + element = [idx * x for x in [len(bits[j]) > 1 for j in range(len(bits))]] self.bit_stack.append({gbits[j]: bits[j][element[j]] for j in range(len(gbits))}) @@ -173,6 +200,8 @@ class AstInterpreter: else: de_gate["args"] = [] de_gate["bits"] = [c.name for c in node.bitlist.children] + if node.name in self.standard_extension: + return if opaque: de_gate["body"] = None else: @@ -182,7 +211,7 @@ class AstInterpreter: """Process a CNOT gate node.""" id0 = self._process_bit_id(node.children[0]) id1 = self._process_bit_id(node.children[1]) - if not(len(id0) == len(id1) or len(id0) == 1 or len(id1) == 1): + if not (len(id0) == len(id1) or len(id0) == 1 or len(id1) == 1): raise QiskitError("internal error: qreg size mismatch", "line=%s" % node.line, "file=%s" % node.file) maxidx = max([len(id0), len(id1)]) @@ -322,63 +351,17 @@ class AstInterpreter: Raises: QiskitError: if encountering a non-basis opaque gate """ - if name == "u0": - op_class = U0Gate - elif name == "u1": - op_class = U1Gate - elif name == "u2": - op_class = U2Gate - elif name == "u3": - op_class = U3Gate - elif name == "x": - op_class = XGate - elif name == "y": - op_class = YGate - elif name == "z": - op_class = ZGate - elif name == "t": - op_class = TGate - elif name == "tdg": - op_class = TdgGate - elif name == "s": - op_class = SGate - elif name == "sdg": - op_class = SdgGate - elif name == "swap": - op_class = SwapGate - elif name == "rx": - op_class = RXGate - elif name == "ry": - op_class = RYGate - elif name == "rz": - op_class = RZGate - elif name == "rzz": - op_class = RZZGate - elif name == "id": - op_class = IdGate - elif name == "h": - op_class = HGate - elif name == "cx": - op_class = CnotGate - elif name == "cy": - op_class = CyGate - elif name == "cz": - op_class = CzGate - elif name == "ch": - op_class = CHGate - elif name == "crz": - op_class = CrzGate - elif name == "cu1": - op_class = Cu1Gate - elif name == "cu3": - op_class = Cu3Gate - elif name == "ccx": - op_class = ToffoliGate - elif name == "cswap": - op_class = FredkinGate + + if name in self.standard_extension: + op = self.standard_extension[name](*params) + elif name in self.gates: + if self.gates[name]['opaque']: + # call an opaque gate + op = Gate(name=name, num_qubits=self.gates[name]['n_bits'], params=params) + else: + # call a custom gate + raise QiskitError('Custom non-opaque gates are not supported by as_to_dag module') else: raise QiskitError("unknown operation for ast node name %s" % name) - op = op_class(*params) - self.dag.apply_operation_back(op, qargs, [], condition=self.condition) diff --git a/test/python/circuit/test_circuit_load_from_qasm.py b/test/python/circuit/test_circuit_load_from_qasm.py index 3062adbe8f..4fe567aa9e 100644 --- a/test/python/circuit/test_circuit_load_from_qasm.py +++ b/test/python/circuit/test_circuit_load_from_qasm.py @@ -17,6 +17,7 @@ from qiskit import QiskitError from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister +from qiskit.circuit import Gate from qiskit.test import QiskitTestCase, Path @@ -123,6 +124,23 @@ class LoadFromQasmTest(QiskitTestCase): self.assertEqual(len(q_circuit.qregs), 1) self.assertEqual(q_circuit, ref) + def test_opaque_gate(self): + """Test parse an opaque gate + See https://github.com/Qiskit/qiskit-terra/issues/1566""" + + qasm_string = '\n'.join(["OPENQASM 2.0;", + "include \"qelib1.inc\";", + "opaque my_gate(theta,phi,lambda) a,b;", + "qreg q[3];", + "my_gate(1,2,3) q[1],q[2];"]) + '\n' + circuit = QuantumCircuit.from_qasm_str(qasm_string) + + qr = QuantumRegister(3, 'q') + expected = QuantumCircuit(qr) + expected.append(Gate(name='my_gate', num_qubits=2, params=[1, 2, 3]), [qr[1], qr[2]]) + + self.assertEqual(circuit, expected) + def test_qasm_example_file(self): """Loads qasm/example.qasm. """