Repository: Qiskit/qiskit

Issue:
Measures conditioned on register containing the target bit generate an invalid DAG
When attempting to condition a `measure` on the register containing the target bit:
```
>>> qc = qk.QuantumCircuit(1,1)
>>> qc.measure(0,0).c_if(qc.cregs[0],0)
>>> qc.depth()
1
>>> qk.converters.circuit_to_dag(qc).depth()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/kevin.krsulichibm.com/q/qiskit-terra/qiskit/dagcircuit/dagcircuit.py", line 574, in depth
    raise DAGCircuitError("not a DAG")
qiskit.dagcircuit.exceptions.DAGCircuitError: 'not a DAG'
```

![image](https://user-images.githubusercontent.com/2241698/58880513-0bab9c80-86a6-11e9-85e7-802bff8954ee.png)

The drawer also has difficulty with them:
```
>>> qc = qk.QuantumCircuit(1,1)
>>> qc.measure(0,0).c_if(qc.cregs[0],0)
>>> print(qc)

q_0: |0>

 c_0: 0

>>> qc = qk.QuantumCircuit(2,1)
>>> qc.h([0,1])
>>> qc.measure(0,0)
>>> qc.measure(1,0).c_if(qc.cregs[0], 0)
>>> print(qc)
        ┌───┐┌─┐
q_0: |0>┤ H ├┤M├
        ├───┤└╥┘
q_1: |0>┤ H ├─╫─
        └───┘ ║
 c_0: 0 ══════╩═
```

Conditioning a `measure` on a separate register seems to work okay
```
>>> qr = qk.QuantumRegister(2)
>>> cr1 = qk.ClassicalRegister(1)
>>> cr2 = qk.ClassicalRegister(1)
>>> qc = qk.QuantumCircuit(qr, cr1, cr2)
>>> qc.h(qr)
>>> qc.measure(qr[0], cr1[0])
>>> qc.measure(qr[1], cr2[0]).c_if(cr1, 0)
>>> qc.depth()
3
>>> qk.converters.circuit_to_dag(qc).depth()
3
>>> print(qc)
         ┌───┐┌─┐
q3_0: |0>┤ H ├┤M├───────
         ├───┤└╥┘  ┌─┐
q3_1: |0>┤ H ├─╫───┤M├──
         └───┘ ║ ┌─┴┴┴─┐
 c2_0: 0 ══════╩═╡ = 0 ╞
                 └──║──┘
 c3_0: 0 ═══════════╩═══
```
![image](https://user-images.githubusercontent.com/2241698/58881003-39ddac00-86a7-11e9-95b0-ba20623dd2de.png)



Discussion:
TIL that conditional measurements are a thing.
This is actually probably not a bug, save for in the random testing.  There is no causality defined in this situation, so you get the cyclic graph.
I don't see the lack of causality here. `qc.measure(1,0).c_if(qc.cregs[0], 0)` to me decomposes as "Check the value of creg0; if 0, trigger a measure of qubit 1 into clbit 0" which should be well defined. The presence or absence of the measure is dependent on the value of `clbit 0` is at the start of the operation, not what it will be at the end.

That said, I don't know of an algorithm or use case that requires a conditional measure, I wouldn't be opposed to not supporting them (this is the first time I came across them as well) but then we should raise early when building them, rather than throwing `not a DAG` down the road. We also allow conditional barriers, by the way, which seems more obviously a bug.
Actually I agree with you.  I was wrong, there is an order.  

Resolving patch:
diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py
--- a/qiskit/dagcircuit/dagcircuit.py
+++ b/qiskit/dagcircuit/dagcircuit.py
@@ -256,7 +256,7 @@ def apply_operation_back(self, op, qargs=None, cargs=None, condition=None):
         cargs = cargs or []
 
         all_cbits = self._bits_in_condition(condition)
-        all_cbits.extend(cargs)
+        all_cbits = set(all_cbits).union(cargs)
 
         self._check_condition(op.name, condition)
         self._check_bits(qargs, self.output_map)
@@ -799,6 +799,20 @@ def substitute_node_with_dag(self, node, input_dag, wires=None):
         pred_map, succ_map = self._make_pred_succ_maps(node)
         full_pred_map, full_succ_map = self._full_pred_succ_maps(pred_map, succ_map,
                                                                  input_dag, wire_map)
+
+        if condition_bit_list:
+            # If we are replacing a conditional node, map input dag through
+            # wire_map to verify that it will not modify any of the conditioning
+            # bits.
+            condition_bits = set(condition_bit_list)
+
+            for op_node in input_dag.op_nodes():
+                mapped_cargs = {wire_map[carg] for carg in op_node.cargs}
+
+                if condition_bits & mapped_cargs:
+                    raise DAGCircuitError('Mapped DAG would alter clbits '
+                                          'on which it would be conditioned.')
+
         # Now that we know the connections, delete node
         self._multi_graph.remove_node(node)
 
diff --git a/qiskit/extensions/simulator/snapshot.py b/qiskit/extensions/simulator/snapshot.py
--- a/qiskit/extensions/simulator/snapshot.py
+++ b/qiskit/extensions/simulator/snapshot.py
@@ -20,7 +20,7 @@
 from qiskit import QuantumCircuit
 from qiskit.circuit.quantumregister import QuantumRegister
 from qiskit.circuit import Instruction
-from qiskit.extensions.exceptions import ExtensionError
+from qiskit.extensions.exceptions import QiskitError, ExtensionError
 
 
 class Snapshot(Instruction):
@@ -89,6 +89,9 @@ def label(self, name):
         else:
             raise TypeError('label expects a string')
 
+    def c_if(self, classical, val):
+        raise QiskitError('Snapshots are simulator directives and cannot be conditional.')
+
 
 def snapshot(self,
              label,
diff --git a/qiskit/extensions/standard/barrier.py b/qiskit/extensions/standard/barrier.py
--- a/qiskit/extensions/standard/barrier.py
+++ b/qiskit/extensions/standard/barrier.py
@@ -18,6 +18,7 @@
 from qiskit.circuit import QuantumCircuit
 from qiskit.circuit.quantumregister import QuantumRegister
 from qiskit.circuit import Instruction
+from qiskit.exceptions import QiskitError
 
 
 class Barrier(Instruction):
@@ -34,6 +35,9 @@ def inverse(self):
     def broadcast_arguments(self, qargs, cargs):
         yield [qarg for sublist in qargs for qarg in sublist], []
 
+    def c_if(self, classical, val):
+        raise QiskitError('Barriers are compiler directives and cannot be conditional.')
+
 
 def barrier(self, *qargs):
     """Apply barrier to circuit.


Test patch:
diff --git a/test/python/circuit/test_extensions_standard.py b/test/python/circuit/test_extensions_standard.py
--- a/test/python/circuit/test_extensions_standard.py
+++ b/test/python/circuit/test_extensions_standard.py
@@ -52,6 +52,11 @@ def test_barrier_invalid(self):
         self.assertRaises(QiskitError, qc.barrier, (self.qr, 'a'))
         self.assertRaises(QiskitError, qc.barrier, .0)
 
+    def test_conditional_barrier_invalid(self):
+        qc = self.circuit
+        barrier = qc.barrier(self.qr)
+        self.assertRaises(QiskitError, barrier.c_if, self.cr, 0)
+
     def test_barrier_reg(self):
         self.circuit.barrier(self.qr)
         self.assertEqual(len(self.circuit), 1)
diff --git a/test/python/test_dagcircuit.py b/test/python/test_dagcircuit.py
--- a/test/python/test_dagcircuit.py
+++ b/test/python/test_dagcircuit.py
@@ -17,9 +17,11 @@
 
 import unittest
 
+import networkx as nx
+
 from qiskit.dagcircuit import DAGCircuit
-from qiskit.circuit import QuantumRegister
-from qiskit.circuit import ClassicalRegister
+from qiskit.circuit import QuantumRegister, Qubit
+from qiskit.circuit import ClassicalRegister, Clbit
 from qiskit.circuit import QuantumCircuit
 from qiskit.circuit import Measure
 from qiskit.circuit import Reset
@@ -112,6 +114,125 @@ def test_apply_operation_back(self):
         self.assertEqual(len(list(self.dag.nodes())), 16)
         self.assertEqual(len(list(self.dag.edges())), 17)
 
+    def test_apply_operation_back_conditional(self):
+        """Test consistency of apply_operation_back with condition set."""
+
+        # Single qubit gate conditional: qc.h(qr[2]).c_if(cr, 3)
+
+        h_gate = HGate()
+        h_gate.control = self.condition
+        h_node = self.dag.apply_operation_back(
+            h_gate, [self.qubit2], [], h_gate.control)
+
+        self.assertEqual(h_node.qargs, [self.qubit2])
+        self.assertEqual(h_node.cargs, [])
+        self.assertEqual(h_node.condition, h_gate.control)
+
+        self.assertEqual(
+            sorted(self.dag._multi_graph.in_edges(h_node, data=True)),
+            sorted([
+                (self.dag.input_map[self.qubit2], h_node,
+                 {'wire': Qubit(*self.qubit2), 'name': 'qr[2]'}),
+                (self.dag.input_map[self.clbit0], h_node,
+                 {'wire': Clbit(*self.clbit0), 'name': 'cr[0]'}),
+                (self.dag.input_map[self.clbit1], h_node,
+                 {'wire': Clbit(*self.clbit1), 'name': 'cr[1]'}),
+            ]))
+
+        self.assertEqual(
+            sorted(self.dag._multi_graph.out_edges(h_node, data=True)),
+            sorted([
+                (h_node, self.dag.output_map[self.qubit2],
+                 {'wire': Qubit(*self.qubit2), 'name': 'qr[2]'}),
+                (h_node, self.dag.output_map[self.clbit0],
+                 {'wire': Clbit(*self.clbit0), 'name': 'cr[0]'}),
+                (h_node, self.dag.output_map[self.clbit1],
+                 {'wire': Clbit(*self.clbit1), 'name': 'cr[1]'}),
+            ]))
+
+        self.assertTrue(nx.is_directed_acyclic_graph(self.dag._multi_graph))
+
+    def test_apply_operation_back_conditional_measure(self):
+        """Test consistency of apply_operation_back for conditional measure."""
+
+        # Measure targeting a clbit which is not a member of the conditional
+        # register. qc.measure(qr[0], cr[0]).c_if(cr2, 0)
+
+        new_creg = ClassicalRegister(1, 'cr2')
+        self.dag.add_creg(new_creg)
+
+        meas_gate = Measure()
+        meas_gate.control = (new_creg, 0)
+        meas_node = self.dag.apply_operation_back(
+            meas_gate, [self.qubit0], [self.clbit0], meas_gate.control)
+
+        self.assertEqual(meas_node.qargs, [self.qubit0])
+        self.assertEqual(meas_node.cargs, [self.clbit0])
+        self.assertEqual(meas_node.condition, meas_gate.control)
+
+        self.assertEqual(
+            sorted(self.dag._multi_graph.in_edges(meas_node, data=True)),
+            sorted([
+                (self.dag.input_map[self.qubit0], meas_node,
+                 {'wire': Qubit(*self.qubit0), 'name': 'qr[0]'}),
+                (self.dag.input_map[self.clbit0], meas_node,
+                 {'wire': Clbit(*self.clbit0), 'name': 'cr[0]'}),
+                (self.dag.input_map[new_creg[0]], meas_node,
+                 {'wire': Clbit(new_creg, 0), 'name': 'cr2[0]'}),
+            ]))
+
+        self.assertEqual(
+            sorted(self.dag._multi_graph.out_edges(meas_node, data=True)),
+            sorted([
+                (meas_node, self.dag.output_map[self.qubit0],
+                 {'wire': Qubit(*self.qubit0), 'name': 'qr[0]'}),
+                (meas_node, self.dag.output_map[self.clbit0],
+                 {'wire': Clbit(*self.clbit0), 'name': 'cr[0]'}),
+                (meas_node, self.dag.output_map[new_creg[0]],
+                 {'wire': Clbit(new_creg, 0), 'name': 'cr2[0]'}),
+            ]))
+
+        self.assertTrue(nx.is_directed_acyclic_graph(self.dag._multi_graph))
+
+    def test_apply_operation_back_conditional_measure_to_self(self):
+        """Test consistency of apply_operation_back for measure onto conditioning bit."""
+
+        # Measure targeting a clbit which _is_ a member of the conditional
+        # register. qc.measure(qr[0], cr[0]).c_if(cr, 3)
+
+        meas_gate = Measure()
+        meas_gate.control = self.condition
+        meas_node = self.dag.apply_operation_back(
+            meas_gate, [self.qubit1], [self.clbit1], self.condition)
+
+        self.assertEqual(meas_node.qargs, [self.qubit1])
+        self.assertEqual(meas_node.cargs, [self.clbit1])
+        self.assertEqual(meas_node.condition, meas_gate.control)
+
+        self.assertEqual(
+            sorted(self.dag._multi_graph.in_edges(meas_node, data=True)),
+            sorted([
+                (self.dag.input_map[self.qubit1], meas_node,
+                 {'wire': Qubit(*self.qubit1), 'name': 'qr[1]'}),
+                (self.dag.input_map[self.clbit0], meas_node,
+                 {'wire': Clbit(*self.clbit0), 'name': 'cr[0]'}),
+                (self.dag.input_map[self.clbit1], meas_node,
+                 {'wire': Clbit(*self.clbit1), 'name': 'cr[1]'}),
+            ]))
+
+        self.assertEqual(
+            sorted(self.dag._multi_graph.out_edges(meas_node, data=True)),
+            sorted([
+                (meas_node, self.dag.output_map[self.qubit1],
+                 {'wire': Qubit(*self.qubit1), 'name': 'qr[1]'}),
+                (meas_node, self.dag.output_map[self.clbit0],
+                 {'wire': Clbit(*self.clbit0), 'name': 'cr[0]'}),
+                (meas_node, self.dag.output_map[self.clbit1],
+                 {'wire': Clbit(*self.clbit1), 'name': 'cr[1]'}),
+            ]))
+
+        self.assertTrue(nx.is_directed_acyclic_graph(self.dag._multi_graph))
+
     def test_apply_operation_front(self):
         """The apply_operation_front() method"""
         self.dag.apply_operation_back(HGate(), [self.qubit0], [])
@@ -646,6 +767,32 @@ def test_substitute_circuit_one_back(self):
         """The method substitute_node_with_dag() replaces a leaf-in-the-back node with a DAG."""
         pass
 
+    def test_raise_if_substituting_dag_modifies_its_conditional(self):
+        """Verify that we raise if the input dag modifies any of the bits in node.condition."""
+
+        # Our unroller's rely on substitute_node_with_dag carrying the condition
+        # from the node over to the input dag, which it does by making every
+        # node in the input dag conditioned over the same bits. However, if the
+        # input dag e.g. measures to one of those bits, the behavior of the
+        # remainder of the DAG would be different, so detect and raise in that
+        # case.
+
+        instr = Instruction('opaque', 1, 1, [])
+        instr.control = self.condition
+        instr_node = self.dag.apply_operation_back(
+            instr, [self.qubit0], [self.clbit1], instr.control)
+
+        sub_dag = DAGCircuit()
+        sub_qr = QuantumRegister(1, 'sqr')
+        sub_cr = ClassicalRegister(1, 'scr')
+        sub_dag.add_qreg(sub_qr)
+        sub_dag.add_creg(sub_cr)
+
+        sub_dag.apply_operation_back(Measure(), [sub_qr[0]], [sub_cr[0]])
+
+        with self.assertRaises(DAGCircuitError):
+            self.dag.substitute_node_with_dag(instr_node, sub_dag)
+
 
 class TestDagProperties(QiskitTestCase):
     """Test the DAG properties.
diff --git a/test/randomized/test_transpiler_equivalence.py b/test/randomized/test_transpiler_equivalence.py
--- a/test/randomized/test_transpiler_equivalence.py
+++ b/test/randomized/test_transpiler_equivalence.py
@@ -186,8 +186,8 @@ def add_c_if_last_gate(self, carg, data):
 
         last_gate = self.qc.data[-1]
 
-        # Work around for https://github.com/Qiskit/qiskit-terra/issues/2567
-        assume(not isinstance(last_gate[0], Measure) or creg != last_gate[2][0].register)
+        # Conditional instructions are not supported
+        assume(isinstance(last_gate[0], Gate))
 
         last_gate[0].c_if(creg, val)