Repository: Qiskit/qiskit

Issue:
qobj_to_circuits/disassemble doesn't work for conditionals
<!-- ⚠️ 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**: Master
- **Python version**: any
- **Operating system**: any

### What is the current behavior?

When trying to run `disassemble()` (or `qobj_to_circuits()`) on a qobj that includes conditionals for instructions this isn't handled properly. This was listed as a todo in the code doing the conversion: https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/compiler/disassembler.py#L17 but it has yet to be handled.


### Steps to reproduce the problem

Build a qobj with experiments that include a conditional and then run disassemble or qobj_to_circuits on it.


### What is the expected behavior?

The conditional is reflected in the output circuit.

### Suggested solutions

Add the missing conditional support. (Likely after #2147 merges)



Discussion:

Resolving patch:
diff --git a/qiskit/assembler/disassemble.py b/qiskit/assembler/disassemble.py
--- a/qiskit/assembler/disassemble.py
+++ b/qiskit/assembler/disassemble.py
@@ -14,13 +14,14 @@
 
 """Disassemble function for a qobj into a list of circuits and it's config"""
 
+import collections
+
 from qiskit.circuit.classicalregister import ClassicalRegister
 from qiskit.circuit.instruction import Instruction
 from qiskit.circuit.quantumcircuit import QuantumCircuit
 from qiskit.circuit.quantumregister import QuantumRegister
 
 
-# TODO: This is broken for conditionals. Will fix after circuits_2_qobj pr
 def _experiments_to_circuits(qobj):
     """Return a list of QuantumCircuit object(s) from a qobj
 
@@ -40,12 +41,13 @@ def _experiments_to_circuits(qobj):
             circuit = QuantumCircuit(*quantum_registers,
                                      *classical_registers,
                                      name=x.header.name)
-            qreg_dict = {}
-            creg_dict = {}
+            qreg_dict = collections.OrderedDict()
+            creg_dict = collections.OrderedDict()
             for reg in quantum_registers:
                 qreg_dict[reg.name] = reg
             for reg in classical_registers:
                 creg_dict[reg.name] = reg
+            conditional = {}
             for i in x.instructions:
                 name = i.name
                 if i.name == 'id':
@@ -70,20 +72,53 @@ def _experiments_to_circuits(qobj):
                 if hasattr(circuit, name):
                     instr_method = getattr(circuit, name)
                     if i.name in ['snapshot']:
-                        instr_method(
+                        _inst = instr_method(
                             i.label,
                             snapshot_type=i.snapshot_type,
                             qubits=qubits,
                             params=params)
                     elif i.name == 'initialize':
-                        instr_method(params, qubits)
+                        _inst = instr_method(params, qubits)
                     else:
-                        instr_method(*params, *qubits, *clbits)
+                        _inst = instr_method(*params, *qubits, *clbits)
+                elif name == 'bfunc':
+                    conditional['value'] = int(i.val, 16)
+                    full_bit_size = sum([creg_dict[x].size for x in creg_dict])
+                    mask_map = {}
+                    raw_map = {}
+                    raw = []
+
+                    for creg in creg_dict:
+                        size = creg_dict[creg].size
+                        reg_raw = [1] * size
+                        if not raw:
+                            raw = reg_raw
+                        else:
+                            for pos, val in enumerate(raw):
+                                if val == 1:
+                                    raw[pos] = 0
+                            raw = reg_raw + raw
+                        mask = [0] * (full_bit_size - len(raw)) + raw
+                        raw_map[creg] = mask
+                        mask_map[int("".join(str(x) for x in mask), 2)] = creg
+                    creg = mask_map[int(i.mask, 16)]
+                    conditional['register'] = creg_dict[creg]
+                    val = int(i.val, 16)
+                    mask = raw_map[creg]
+                    for j in reversed(mask):
+                        if j == 0:
+                            val = val >> 1
+                        else:
+                            conditional['value'] = val
+                            break
                 else:
-                    temp_opaque_instruction = Instruction(
+                    _inst = temp_opaque_instruction = Instruction(
                         name=name, num_qubits=len(qubits),
                         num_clbits=len(clbits), params=params)
                     circuit.append(temp_opaque_instruction, qubits, clbits)
+                if conditional and name != 'bfunc':
+                    _inst.c_if(conditional['register'], conditional['value'])
+                    conditional = {}
             circuits.append(circuit)
         return circuits
     return None


Test patch:
diff --git a/test/python/compiler/test_disassembler.py b/test/python/compiler/test_disassembler.py
--- a/test/python/compiler/test_disassembler.py
+++ b/test/python/compiler/test_disassembler.py
@@ -19,6 +19,7 @@
 import numpy as np
 
 from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit
+from qiskit.circuit import Instruction
 from qiskit.compiler.assemble import assemble
 from qiskit.assembler.disassemble import disassemble
 from qiskit.assembler.run_config import RunConfig
@@ -115,6 +116,79 @@ def test_assemble_initialize(self):
         self.assertEqual(circuits[0], circ)
         self.assertEqual({}, header)
 
+    def test_opaque_instruction(self):
+        """Test the disassembler handles opaque instructions correctly."""
+        opaque_inst = Instruction(name='my_inst', num_qubits=4,
+                                  num_clbits=2, params=[0.5, 0.4])
+        q = QuantumRegister(6, name='q')
+        c = ClassicalRegister(4, name='c')
+        circ = QuantumCircuit(q, c, name='circ')
+        circ.append(opaque_inst, [q[0], q[2], q[5], q[3]], [c[3], c[0]])
+        qobj = assemble(circ)
+        circuits, run_config_out, header = disassemble(qobj)
+        run_config_out = RunConfig(**run_config_out)
+        self.assertEqual(run_config_out.n_qubits, 6)
+        self.assertEqual(run_config_out.memory_slots, 4)
+        self.assertEqual(len(circuits), 1)
+        self.assertEqual(circuits[0], circ)
+        self.assertEqual({}, header)
+
+    def test_circuit_with_conditionals(self):
+        """Verify disassemble sets conditionals correctly."""
+        qr = QuantumRegister(2)
+        cr1 = ClassicalRegister(1)
+        cr2 = ClassicalRegister(2)
+        qc = QuantumCircuit(qr, cr1, cr2)
+        qc.measure(qr[0], cr1)  # Measure not required for a later conditional
+        qc.measure(qr[1], cr2[1])  # Measure required for a later conditional
+        qc.h(qr[1]).c_if(cr2, 3)
+        qobj = assemble(qc)
+        circuits, run_config_out, header = disassemble(qobj)
+        run_config_out = RunConfig(**run_config_out)
+        self.assertEqual(run_config_out.n_qubits, 2)
+        self.assertEqual(run_config_out.memory_slots, 3)
+        self.assertEqual(len(circuits), 1)
+        self.assertEqual(circuits[0], qc)
+        self.assertEqual({}, header)
+
+    def test_circuit_with_simple_conditional(self):
+        """Verify disassemble handles a simple conditional on the only bits."""
+        qr = QuantumRegister(1)
+        cr = ClassicalRegister(1)
+        qc = QuantumCircuit(qr, cr)
+        qc.h(qr[0]).c_if(cr, 1)
+        qobj = assemble(qc)
+        circuits, run_config_out, header = disassemble(qobj)
+        run_config_out = RunConfig(**run_config_out)
+        self.assertEqual(run_config_out.n_qubits, 1)
+        self.assertEqual(run_config_out.memory_slots, 1)
+        self.assertEqual(len(circuits), 1)
+        self.assertEqual(circuits[0], qc)
+        self.assertEqual({}, header)
+
+    def test_multiple_conditionals_multiple_registers(self):
+        """Verify disassemble handles multiple conditionals and registers."""
+        qr = QuantumRegister(3)
+        cr1 = ClassicalRegister(3)
+        cr2 = ClassicalRegister(5)
+        cr3 = ClassicalRegister(6)
+        cr4 = ClassicalRegister(1)
+
+        qc = QuantumCircuit(qr, cr1, cr2, cr3, cr4)
+        qc.x(qr[1])
+        qc.h(qr)
+        qc.cx(qr[1], qr[0]).c_if(cr3, 14)
+        qc.ccx(qr[0], qr[2], qr[1]).c_if(cr4, 1)
+        qc.h(qr).c_if(cr1, 3)
+        qobj = assemble(qc)
+        circuits, run_config_out, header = disassemble(qobj)
+        run_config_out = RunConfig(**run_config_out)
+        self.assertEqual(run_config_out.n_qubits, 3)
+        self.assertEqual(run_config_out.memory_slots, 15)
+        self.assertEqual(len(circuits), 1)
+        self.assertEqual(circuits[0], qc)
+        self.assertEqual({}, header)
+
 
 if __name__ == '__main__':
     unittest.main(verbosity=2)