Repository: Qiskit/qiskit

Issue:
QasmError when parsing quantum volume qc.qasm()
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Terra version**: 0.9.0.dev0+de3f909
- **Python version**: 3.7.4
- **Operating system**: Windows Subsystem for Linux

### What is the current behavior?
If you pass a quantum volume circuit's qc.qasm() into qc.from_qasm_str, which calls qiskit's qasm parser, throws a QasmError.  The same error occurs for other circuits besides quantum volume.

### Steps to reproduce the problem
import qiskit.ignis.verification.quantum_volume as qv
qcs = qv.qv_circuits(qubit_lists=[[0,1]])
qc = qcs[0][0][0]
print(qc.qasm() + "\n")
new_qc = qc.from_qasm_str(qc.qasm())

### What is the expected behavior?
Not throwing an error and parsing correctly.


### Suggested solutions





Discussion:
I think the issue here is that the circuit contains custom unitaries, which are then added into the qasm as `unitary([*the matrix*]) q[0], q[1];` which is not a format supported by qasm. Currently, this is because the unitary is just being added to the qasm using the base `Instruction.qasm()` but this should be overwritten to use `_define()` (or the methods it uses) to decompose the gate before adding it to the qasm.

Resolving patch:
diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py
--- a/qiskit/circuit/quantumcircuit.py
+++ b/qiskit/circuit/quantumcircuit.py
@@ -576,6 +576,7 @@ def qasm(self):
             string_temp += register.qasm() + "\n"
         for register in self.cregs:
             string_temp += register.qasm() + "\n"
+        unitary_gates = []
         for instruction, qargs, cargs in self._data:
             if instruction.name == 'measure':
                 qubit = qargs[0]
@@ -587,6 +588,12 @@ def qasm(self):
                 string_temp += "%s %s;\n" % (instruction.qasm(),
                                              ",".join(["%s[%d]" % (j.register.name, j.index)
                                                        for j in qargs + cargs]))
+            if instruction.name == 'unitary':
+                unitary_gates.append(instruction)
+
+        # this resets them, so if another call to qasm() is made the gate def is added again
+        for gate in unitary_gates:
+            gate._qasm_def_written = False
         return string_temp
 
     def draw(self, scale=0.7, filename=None, style=None, output=None,
diff --git a/qiskit/extensions/unitary.py b/qiskit/extensions/unitary.py
--- a/qiskit/extensions/unitary.py
+++ b/qiskit/extensions/unitary.py
@@ -16,6 +16,7 @@
 Arbitrary unitary circuit instruction.
 """
 
+from collections import OrderedDict
 import numpy
 
 from qiskit.circuit import Gate
@@ -62,6 +63,10 @@ def __init__(self, data, label=None):
         if input_dim != output_dim or 2**n_qubits != input_dim:
             raise ExtensionError(
                 "Input matrix is not an N-qubit operator.")
+
+        self._qasm_name = None
+        self._qasm_definition = None
+        self._qasm_def_written = False
         # Store instruction params
         super().__init__('unitary', n_qubits, [data], label=label)
 
@@ -106,6 +111,55 @@ def _define(self):
             raise NotImplementedError("Not able to generate a subcircuit for "
                                       "a {}-qubit unitary".format(self.num_qubits))
 
+    def qasm(self):
+        """ The qasm for a custom unitary gate
+        This is achieved by adding a custom gate that corresponds to the definition
+        of this gate. It gives the gate a random name if one hasn't been given to it.
+        """
+        # if this is true then we have written the gate definition already
+        # so we only need to write the name
+        if self._qasm_def_written:
+            return self._qasmif(self._qasm_name)
+
+        # we have worked out the definition before, but haven't written it yet
+        # so we need to write definition + name
+        if self._qasm_definition:
+            self._qasm_def_written = True
+            return self._qasm_definition + self._qasmif(self._qasm_name)
+
+        # need to work out the definition and then write it
+
+        # give this unitary a name
+        self._qasm_name = self.label if self.label else "unitary" + str(id(self))
+
+        # map from gates in the definition to params in the method
+        reg_to_qasm = OrderedDict()
+        current_reg = 0
+
+        gates_def = ""
+        for gate in self.definition:
+
+            # add regs from this gate to the overall set of params
+            for reg in gate[1] + gate[2]:
+                if reg not in reg_to_qasm:
+                    reg_to_qasm[reg] = 'p' + str(current_reg)
+                    current_reg += 1
+
+            curr_gate = "\t%s %s;\n" % (gate[0].qasm(),
+                                        ",".join([reg_to_qasm[j]
+                                                  for j in gate[1] + gate[2]]))
+            gates_def += curr_gate
+
+        # name of gate + params + {definition}
+        overall = "gate " + self._qasm_name + \
+                  " " + ",".join(reg_to_qasm.values()) + \
+                  " {\n" + gates_def + "}\n"
+
+        self._qasm_def_written = True
+        self._qasm_definition = overall
+
+        return self._qasm_definition + self._qasmif(self._qasm_name)
+
 
 def unitary(self, obj, qubits, label=None):
     """Apply u2 to q."""


Test patch:
diff --git a/test/python/circuit/test_unitary.py b/test/python/circuit/test_unitary.py
--- a/test/python/circuit/test_unitary.py
+++ b/test/python/circuit/test_unitary.py
@@ -157,7 +157,7 @@ def test_qobj_with_unitary_matrix(self):
         sigmax = numpy.array([[0, 1], [1, 0]])
         sigmay = numpy.array([[0, -1j], [1j, 0]])
         matrix = numpy.kron(sigmay, numpy.kron(sigmax, sigmay))
-        qc.rx(numpy.pi/4, qr[0])
+        qc.rx(numpy.pi / 4, qr[0])
         uni = UnitaryGate(matrix)
         qc.append(uni, [qr[0], qr[1], qr[3]])
         qc.cx(qr[3], qr[2])
@@ -183,3 +183,77 @@ def test_labeled_unitary(self):
         instr = qobj.experiments[0].instructions[0]
         self.assertEqual(instr.name, 'unitary')
         self.assertEqual(instr.label, 'xy')
+
+    def test_qasm_unitary_only_one_def(self):
+        """test that a custom unitary can be converted to qasm and the
+        definition is only written once"""
+        qr = QuantumRegister(2, 'q0')
+        cr = ClassicalRegister(1, 'c0')
+        qc = QuantumCircuit(qr, cr)
+        matrix = numpy.array([[1, 0], [0, 1]])
+        unitary_gate = UnitaryGate(matrix, label="custom_gate")
+
+        qc.x(qr[0])
+        qc.append(unitary_gate, [qr[0]])
+        qc.append(unitary_gate, [qr[1]])
+
+        expected_qasm = "OPENQASM 2.0;\n" \
+                        "include \"qelib1.inc\";\n" \
+                        "qreg q0[2];\ncreg c0[1];\n" \
+                        "x q0[0];\n" \
+                        "gate custom_gate p0 {\n" \
+                        "\tu3(0.0,0.0,0.0) p0;\n" \
+                        "}\n" \
+                        "custom_gate q0[0];\n" \
+                        "custom_gate q0[1];\n"
+        self.assertEqual(expected_qasm, qc.qasm())
+
+    def test_qasm_unitary_twice(self):
+        """test that a custom unitary can be converted to qasm and that if
+        the qasm is called twice it is the same every time"""
+        qr = QuantumRegister(2, 'q0')
+        cr = ClassicalRegister(1, 'c0')
+        qc = QuantumCircuit(qr, cr)
+        matrix = numpy.array([[1, 0], [0, 1]])
+        unitary_gate = UnitaryGate(matrix, label="custom_gate")
+
+        qc.x(qr[0])
+        qc.append(unitary_gate, [qr[0]])
+        qc.append(unitary_gate, [qr[1]])
+
+        expected_qasm = "OPENQASM 2.0;\n" \
+                        "include \"qelib1.inc\";\n" \
+                        "qreg q0[2];\ncreg c0[1];\n" \
+                        "x q0[0];\n" \
+                        "gate custom_gate p0 {\n" \
+                        "\tu3(0.0,0.0,0.0) p0;\n" \
+                        "}\n" \
+                        "custom_gate q0[0];\n" \
+                        "custom_gate q0[1];\n"
+        self.assertEqual(expected_qasm, qc.qasm())
+        self.assertEqual(expected_qasm, qc.qasm())
+
+    def test_qasm_2q_unitary(self):
+        """test that a 2 qubit custom unitary can be converted to qasm"""
+        qr = QuantumRegister(2, 'q0')
+        cr = ClassicalRegister(1, 'c0')
+        qc = QuantumCircuit(qr, cr)
+        matrix = numpy.eye(4)
+        unitary_gate = UnitaryGate(matrix, label="custom_gate")
+
+        qc.x(qr[0])
+        qc.append(unitary_gate, [qr[0], qr[1]])
+        qc.append(unitary_gate, [qr[1], qr[0]])
+
+        expected_qasm = "OPENQASM 2.0;\n" \
+                        "include \"qelib1.inc\";\n" \
+                        "qreg q0[2];\n" \
+                        "creg c0[1];\n" \
+                        "x q0[0];\n" \
+                        "gate custom_gate p0,p1 {\n" \
+                        "\tu3(0.0,0.0,0.0) p0;\n" \
+                        "\tu3(0.0,0.0,0.0) p1;\n" \
+                        "}\n" \
+                        "custom_gate q0[0],q0[1];\n" \
+                        "custom_gate q0[1],q0[0];\n"
+        self.assertEqual(expected_qasm, qc.qasm())