Repository: BQSKit/bqskit

Issue:
Is it possible to get the QASM string representation of a BQSKit circuit?
Hi,

Is it possible to get the QASM string representation of a BQSKit circuit?

Discussion:

Patch:
diff --git a/bqskit/ir/gates/composed/controlled.py b/bqskit/ir/gates/composed/controlled.py
index 3ed8a15..0056e18 100644
--- a/bqskit/ir/gates/composed/controlled.py
+++ b/bqskit/ir/gates/composed/controlled.py
@@ -286,6 +286,33 @@ class ControlledGate(ComposedGate, DifferentiableUnitary):
             ctrl_U = np.kron(self.ctrl, U) + self.ihalf
             self._utry = UnitaryMatrix(ctrl_U, self.radixes)
 
+    @property
+    def qasm_name(self) -> str:
+        """
+        Override default `Gate.qasm_name` method.
+
+        If the core gate is a standard gate, this function will output
+        qasm in the form 'c+<gate_qasm>'. Otherwise an error will be raised.
+
+        Raises:
+            ValueError: If the core gate is non-standard in OpenQASM 2.0.
+        """
+        _core_gate = self.gate.qasm_name
+        if self.num_controls <= 2:
+            _controls = 'c' * self.num_controls
+        else:
+            _controls = f'c{self.num_controls}'
+        qasm_name = _controls + _core_gate
+        supported_gates = ('cu1', 'cu2', 'cu3', 'cswap', 'c3x', 'c4x')
+        if qasm_name not in supported_gates:
+            raise ValueError(
+                f'Controlled gate {_core_gate} with {self.num_controls} '
+                'controls is not a standard OpenQASM 2.0 identifier. '
+                'To encode this gate, try decomposing it into gates with'
+                'standard identifiers.',
+            )
+        return qasm_name
+
     def get_unitary(self, params: RealVector = []) -> UnitaryMatrix:
         """Return the unitary for this gate, see :class:`Unitary` for more."""
         if hasattr(self, '_utry'):


Test patch:
diff --git a/tests/ir/lang/test_controlled_qasm.py b/tests/ir/lang/test_controlled_qasm.py
new file mode 100644
index 0000000..d399494
--- /dev/null
+++ b/tests/ir/lang/test_controlled_qasm.py
@@ -0,0 +1,102 @@
+from __future__ import annotations
+
+from bqskit.ir.lang.qasm2 import OPENQASM2Language
+
+
+class TestControlledQASM:
+    def test_cu1(self) -> None:
+
+        input_qasm = (
+            'OPENQASM 2.0;\n'
+            'include "qelib1.inc";\n'
+            'qreg q[2];\n'
+            'cu1(3.1415) q[0], q[1];\n'
+        )
+        circuit = OPENQASM2Language().decode(input_qasm)
+
+        output_qasm = circuit.to('qasm')
+
+        assert input_qasm == output_qasm
+
+    def test_cu2(self) -> None:
+
+        input_qasm = (
+            'OPENQASM 2.0;\n'
+            'include "qelib1.inc";\n'
+            'qreg q[2];\n'
+            'cu2(3.1415, 0.0) q[0], q[1];\n'
+        )
+        circuit = OPENQASM2Language().decode(input_qasm)
+
+        output_qasm = circuit.to('qasm')
+
+        assert input_qasm == output_qasm
+
+    def test_cu3(self) -> None:
+
+        input_qasm = (
+            'OPENQASM 2.0;\n'
+            'include "qelib1.inc";\n'
+            'qreg q[2];\n'
+            'cu3(3.1415, 0.0, -4.0) q[0], q[1];\n'
+        )
+        circuit = OPENQASM2Language().decode(input_qasm)
+
+        output_qasm = circuit.to('qasm')
+
+        assert input_qasm == output_qasm
+
+    def test_cswap(self) -> None:
+
+        input_qasm = (
+            'OPENQASM 2.0;\n'
+            'include "qelib1.inc";\n'
+            'qreg q[3];\n'
+            'cswap q[0], q[1], q[2];\n'
+        )
+        circuit = OPENQASM2Language().decode(input_qasm)
+
+        output_qasm = circuit.to('qasm')
+
+        assert input_qasm == output_qasm
+
+    def test_c3x(self) -> None:
+
+        input_qasm = (
+            'OPENQASM 2.0;\n'
+            'include "qelib1.inc";\n'
+            'qreg q[4];\n'
+            'c3x q[0], q[1], q[2], q[3];\n'
+        )
+        circuit = OPENQASM2Language().decode(input_qasm)
+
+        output_qasm = circuit.to('qasm')
+
+        assert input_qasm == output_qasm
+
+    def test_c4x(self) -> None:
+
+        input_qasm = (
+            'OPENQASM 2.0;\n'
+            'include "qelib1.inc";\n'
+            'qreg q[5];\n'
+            'c4x q[0], q[1], q[2], q[3], q[4];\n'
+        )
+        circuit = OPENQASM2Language().decode(input_qasm)
+
+        output_qasm = circuit.to('qasm')
+
+        assert input_qasm == output_qasm
+
+    def test_ch(self) -> None:
+
+        input_qasm = (
+            'OPENQASM 2.0;\n'
+            'include "qelib1.inc";\n'
+            'qreg q[2];\n'
+            'ch q[0], q[1];\n'
+        )
+        try:
+            OPENQASM2Language().decode(input_qasm)
+        except ValueError:
+            assert True