Repository: Qiskit/qiskit

Issue:
Classical conditioning on single classical bits
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues to confirm this idea does not exist. -->

### What is the expected enhancement?

As far as I understand, classical conditional statements are done using `c_if` to check whether a classical register holds the binary string for a given integer. It would be useful to be able to do this for individual bits.

This can be done by declaring each bit as an individual classical register. But if there is not a better way in the current version of Terra, there probably should be.

Note that this has previously been discussed in [issue 356 of the qiskit-tutorial](https://github.com/Qiskit/qiskit-tutorial/issues/356) and on the [Quantum Computing Stack Exchange](https://quantumcomputing.stackexchange.com/questions/4260/how-to-create-a-condition-on-only-one-classical-bit-when-we-have-a-total-of-2-cl).




Discussion:
This was an OpenQASM design decision (see Fig. 7 of the [OpenQASM paper](https://arxiv.org/pdf/1707.03429.pdf)). I think the idea was that complex conditional would be easier to write like this, rather than doing OR/AND on individual bits.

But yes, then if you want a condition on a single bit you have to declare that as a single register. Another problem is that it creates false dependencies on all bits of the classical register, even if only one bit is used (for example in building the dependency graph).

I think ideally we should keep both ways doable. Something like:

```python
h(q[0]).c_if(c, 3)   # condition on a register
h(q[0]).c_if(c[0], 1)  # condition on a bit
```
I'm not fully sure is doable to have single-bit conditionals without major changes. Is something that the current Qobj could handle?
The current Qobj spec handles this case directly (in fact, there's some code in https://github.com/Qiskit/qiskit-terra/blob/ef828be2c7e99e50993805c2c57b428cb3d540ed/qiskit/assembler/assemble_circuits.py#L73 to convert from QASM-style register-based conditionals to Qobj-style bit-based conditionals).
May I ask if it's possible yet to use `c_if` on the IBMQ?
> May I ask if it's possible yet to use c_if on the IBMQ?

Only in the simulator. At the moment, no real device have `c_if` support yet.
Since it seems that there is consensus on supporting this feature. I'm removing the `discussion` tag.
I saw a use case for this today, at an event cohosted by IBMQ for students at an IBMQ network hub. I watched an IBMQ advocate go through a demo of teleportation today, and this is what he had to do:
```
qc.x(2).c_if(creg[0], 2)
qc.x(2).c_if(creg[0], 3)
qc.z(2).c_if(creg[0], 1)
qc.z(2).c_if(creg[0], 3)
```
Compare to the same code if we could use bits:
```
qc.x(2).c_if(clbit[0])
qc.z(2).c_if(clbit[1])
```
This required him to explain to the class why the first had 4 operations, and why it was identical to the algorithm they had just learned.

Note:
I'm not sure why he had to index into `creg`, maybe an error on his part, I'm not sure; but since it executed, the type of `creg[0]` must be a `ClassicalRegister`. I imagine the snippet I typed is flawed, too. I'm not recommending a style for implementation, I'm only demonstrating a use case.
Hi @1ucian0 , @ajavadia . I need some clarification. For reference look at:

https://github.com/Qiskit/qiskit-terra/blob/c74f87f47cb91192e77bfb3d123740157f5cc55c/qiskit/circuit/instruction.py#L265

I think if a cbit is given as input to c_if, for the given instruction we could set self.condition as the tuple containing the register corresponding to the cbit and the adjusted control value. What do you think? Or would this lead to some more than required computations in backend?
Any news on the ability to do condition on a single bit of a multi-bit classical register?
as in ...

      h(q[0]).c_if(c[0], 1)  # condition on a bit

Having to make a separate classical one-qubit reg for every simple condition is quite a limitation.
It prevents easy auto-generation of code (need to make the preamble where regs are defined depend on
the contents of the circuit ... yechh!)
And you have to have multiple classical registers instead of just a simple array.

Is there any simple way to implement this, a check on a single bit of CR?



Resolving patch:
diff --git a/qiskit/assembler/assemble_circuits.py b/qiskit/assembler/assemble_circuits.py
--- a/qiskit/assembler/assemble_circuits.py
+++ b/qiskit/assembler/assemble_circuits.py
@@ -17,6 +17,7 @@
 from qiskit.assembler.run_config import RunConfig
 from qiskit.assembler.assemble_schedules import _assemble_instructions as _assemble_schedule
 from qiskit.circuit import QuantumCircuit
+from qiskit.circuit.classicalregister import Clbit
 from qiskit.exceptions import QiskitError
 from qiskit.qobj import (
     QasmQobj,
@@ -136,10 +137,16 @@ def _assemble_circuit(
             ctrl_reg, ctrl_val = instruction._condition
             mask = 0
             val = 0
-            for clbit in clbit_labels:
-                if clbit[0] == ctrl_reg.name:
-                    mask |= 1 << clbit_labels.index(clbit)
-                    val |= ((ctrl_val >> clbit[1]) & 1) << clbit_labels.index(clbit)
+            if isinstance(ctrl_reg, Clbit):
+                mask = 1 << clbit_indices[ctrl_reg]
+                val = (ctrl_val & 1) << clbit_indices[ctrl_reg]
+            else:
+                for clbit in clbit_indices:
+                    if clbit in ctrl_reg:
+                        mask |= 1 << clbit_indices[clbit]
+                        val |= ((ctrl_val >> list(ctrl_reg).index(clbit)) & 1) << clbit_indices[
+                            clbit
+                        ]
 
             conditional_reg_idx = memory_slots + max_conditional_idx
             conversion_bfunc = QasmQobjInstruction(
diff --git a/qiskit/circuit/instruction.py b/qiskit/circuit/instruction.py
--- a/qiskit/circuit/instruction.py
+++ b/qiskit/circuit/instruction.py
@@ -38,7 +38,7 @@
 
 from qiskit.circuit.exceptions import CircuitError
 from qiskit.circuit.quantumregister import QuantumRegister
-from qiskit.circuit.classicalregister import ClassicalRegister
+from qiskit.circuit.classicalregister import ClassicalRegister, Clbit
 from qiskit.qobj.qasm_qobj import QasmQobjInstruction
 from qiskit.circuit.parameter import ParameterExpression
 from .tools import pi_check
@@ -80,7 +80,8 @@ def __init__(self, name, num_qubits, num_clbits, params, duration=None, unit="dt
 
         self._params = []  # a list of gate params stored
 
-        # tuple (ClassicalRegister, int) when the instruction has a conditional ("if")
+        # tuple (ClassicalRegister, int), tuple (Clbit, bool) or tuple (Clbit, int)
+        # when the instruction has a conditional ("if")
         self.condition = None
         # list of instructions (and their contexts) that this instruction is composed of
         # empty definition means opaque or fundamental instruction
@@ -361,11 +362,15 @@ def inverse(self):
         return inverse_gate
 
     def c_if(self, classical, val):
-        """Add classical condition on register classical and value val."""
-        if not isinstance(classical, ClassicalRegister):
-            raise CircuitError("c_if must be used with a classical register")
+        """Add classical condition on register or cbit classical and value val."""
+        if not isinstance(classical, (ClassicalRegister, Clbit)):
+            raise CircuitError("c_if must be used with a classical register or classical bit")
         if val < 0:
             raise CircuitError("condition value should be non-negative")
+        if isinstance(classical, Clbit):
+            # Casting the conditional value as Boolean when
+            # the classical condition is on a classical bit.
+            val = bool(val)
         self.condition = (classical, val)
         return self
 
diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py
--- a/qiskit/dagcircuit/dagcircuit.py
+++ b/qiskit/dagcircuit/dagcircuit.py
@@ -29,6 +29,7 @@
 import numpy as np
 import retworkx as rx
 
+from qiskit.circuit.exceptions import CircuitError
 from qiskit.circuit.quantumregister import QuantumRegister, Qubit
 from qiskit.circuit.classicalregister import ClassicalRegister, Clbit
 from qiskit.circuit.gate import Gate
@@ -318,13 +319,16 @@ def _check_condition(self, name, condition):
 
         Args:
             name (string): used for error reporting
-            condition (tuple or None): a condition tuple (ClassicalRegister,int)
+            condition (tuple or None): a condition tuple (ClassicalRegister, int) or (Clbit, bool)
 
         Raises:
             DAGCircuitError: if conditioning on an invalid register
         """
-        # Verify creg exists
-        if condition is not None and condition[0].name not in self.cregs:
+        if (
+            condition is not None
+            and condition[0] not in self.clbits
+            and condition[0].name not in self.cregs
+        ):
             raise DAGCircuitError("invalid creg in condition for %s" % name)
 
     def _check_bits(self, args, amap):
@@ -348,12 +352,24 @@ def _bits_in_condition(self, cond):
         """Return a list of bits in the given condition.
 
         Args:
-            cond (tuple or None): optional condition (ClassicalRegister, int)
+            cond (tuple or None): optional condition (ClassicalRegister, int) or (Clbit, bool)
 
         Returns:
             list[Clbit]: list of classical bits
+
+        Raises:
+            CircuitError: if cond[0] is not ClassicalRegister or Clbit
         """
-        return [] if cond is None else list(cond[0])
+        if cond is None:
+            return []
+        elif isinstance(cond[0], ClassicalRegister):
+            # Returns a list of all the cbits in the given creg cond[0].
+            return cond[0][:]
+        elif isinstance(cond[0], Clbit):
+            # Returns a singleton list of the conditional cbit.
+            return [cond[0]]
+        else:
+            raise CircuitError("Condition must be used with ClassicalRegister or Clbit.")
 
     def _add_op_node(self, op, qargs, cargs):
         """Add a new operation node to the graph and assign properties.
@@ -561,12 +577,18 @@ def _map_condition(wire_map, condition, target_cregs):
         else:
             # if there is a condition, map the condition bits to the
             # composed cregs based on the wire_map
-            cond_creg = condition[0]
+            is_reg = False
+            if isinstance(condition[0], Clbit):
+                cond_creg = [condition[0]]
+            else:
+                cond_creg = condition[0]
+                is_reg = True
             cond_val = condition[1]
             new_cond_val = 0
             new_creg = None
-            for bit in wire_map:
-                if bit in cond_creg:
+            bits_in_condcreg = [bit for bit in wire_map if bit in cond_creg]
+            for bit in bits_in_condcreg:
+                if is_reg:
                     try:
                         candidate_creg = next(
                             creg for creg in target_cregs if wire_map[bit] in creg
@@ -575,18 +597,29 @@ def _map_condition(wire_map, condition, target_cregs):
                         raise DAGCircuitError(
                             "Did not find creg containing " "mapped clbit in conditional."
                         ) from ex
+                else:
+                    # If cond is on a single Clbit then the candidate_creg is
+                    # the target Clbit to which 'bit' is mapped to.
+                    candidate_creg = wire_map[bit]
+
+                if new_creg is None:
+                    new_creg = candidate_creg
+                elif new_creg != candidate_creg:
+                    # Raise if wire_map maps condition creg on to more than one
+                    # creg in target DAG.
+                    raise DAGCircuitError(
+                        "wire_map maps conditional " "register onto more than one creg."
+                    )
 
-                    if new_creg is None:
-                        new_creg = candidate_creg
-                    elif new_creg != candidate_creg:
-                        # Raise if wire_map maps condition creg on to more than one
-                        # creg in target DAG.
-                        raise DAGCircuitError(
-                            "wire_map maps conditional " "register onto more than one creg."
-                        )
-
-                    if 2 ** (cond_creg[:].index(bit)) & cond_val:
-                        new_cond_val += 2 ** (new_creg[:].index(wire_map[bit]))
+                if not is_reg:
+                    # If the cond is on a single Clbit then the new_cond_val is the
+                    # same as the cond_val since the new_creg is also a single Clbit.
+                    new_cond_val = cond_val
+                elif 2 ** (cond_creg[:].index(bit)) & cond_val:
+                    # If the conditional values of the Clbit 'bit' is 1 then the new_cond_val
+                    # is updated such that the conditional value of the Clbit to which 'bit'
+                    # is mapped to in new_creg is 1.
+                    new_cond_val += 2 ** (new_creg[:].index(wire_map[bit]))
             if new_creg is None:
                 raise DAGCircuitError("Condition registers not found in wire_map.")
             new_condition = (new_creg, new_cond_val)


Test patch:
diff --git a/test/python/basicaer/test_qasm_simulator.py b/test/python/basicaer/test_qasm_simulator.py
--- a/test/python/basicaer/test_qasm_simulator.py
+++ b/test/python/basicaer/test_qasm_simulator.py
@@ -192,6 +192,24 @@ def test_if_statement(self):
         self.assertEqual(counts_if_true, {"111": 100})
         self.assertEqual(counts_if_false, {"001": 100})
 
+    def test_bit_cif_crossaffect(self):
+        """Test if bits in a classical register other than
+        the single conditional bit affect the conditioned operation."""
+        shots = 100
+        qr = QuantumRegister(3)
+        cr = ClassicalRegister(3)
+        cr1 = ClassicalRegister(1)
+        circuit = QuantumCircuit(qr, cr, cr1)
+        circuit.x([qr[1], qr[2]])
+        circuit.measure(qr[1], cr[1])
+        circuit.measure(qr[2], cr[2])
+        circuit.h(qr[0]).c_if(cr[0], True)
+        circuit.measure(qr[0], cr1[0])
+        job = execute(circuit, backend=self.backend, shots=shots, seed_simulator=self.seed)
+        result = job.result().get_counts()
+        target = {"0 110": 100}
+        self.assertEqual(result, target)
+
     def test_teleport(self):
         """Test teleportation as in tutorials"""
         self.log.info("test_teleport")
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
@@ -120,6 +120,20 @@ def test_ch_invalid(self):
         self.assertRaises(CircuitError, qc.ch, self.cr, self.qr)
         self.assertRaises(CircuitError, qc.ch, "a", self.qr[1])
 
+    def test_cif_reg(self):
+        self.circuit.h(self.qr[0]).c_if(self.cr, 7)
+        op, qargs, _ = self.circuit[0]
+        self.assertEqual(op.name, "h")
+        self.assertEqual(qargs, [self.qr[0]])
+        self.assertEqual(op.condition, (self.cr, 7))
+
+    def test_cif_single_bit(self):
+        self.circuit.h(self.qr[0]).c_if(self.cr[0], True)
+        op, qargs, _ = self.circuit[0]
+        self.assertEqual(op.name, "h")
+        self.assertEqual(qargs, [self.qr[0]])
+        self.assertEqual(op.condition, (self.cr[0], True))
+
     def test_crz(self):
         self.circuit.crz(1, self.qr[0], self.qr[1])
         op, qargs, _ = self.circuit[0]
diff --git a/test/python/compiler/test_assembler.py b/test/python/compiler/test_assembler.py
--- a/test/python/compiler/test_assembler.py
+++ b/test/python/compiler/test_assembler.py
@@ -293,6 +293,29 @@ def test_convert_to_bfunc_plus_conditional(self):
         self.assertTrue(hasattr(h_op, "conditional"))
         self.assertEqual(bfunc_op.register, h_op.conditional)
 
+    def test_convert_to_bfunc_plus_conditional_onebit(self):
+        """Verify assemble_circuits converts single bit conditionals from QASM to Qobj."""
+        qr = QuantumRegister(1)
+        cr = ClassicalRegister(3)
+        qc = QuantumCircuit(qr, cr)
+
+        qc.h(qr[0]).c_if(cr[2], 1)
+
+        qobj = assemble(qc)
+        validate_qobj_against_schema(qobj)
+
+        inst_set = qobj.experiments[0].instructions
+        [bfunc_op, h_op] = inst_set
+
+        self.assertEqual(len(inst_set), 2)
+        self.assertEqual(bfunc_op.name, "bfunc")
+        self.assertEqual(bfunc_op.mask, "0x4")
+        self.assertEqual(bfunc_op.val, "0x4")
+        self.assertEqual(bfunc_op.relation, "==")
+
+        self.assertTrue(hasattr(h_op, "conditional"))
+        self.assertEqual(bfunc_op.register, h_op.conditional)
+
     def test_resize_value_to_register(self):
         """Verify assemble_circuits converts the value provided on the classical
         creg to its mapped location on the device register."""
diff --git a/test/python/dagcircuit/test_dagcircuit.py b/test/python/dagcircuit/test_dagcircuit.py
--- a/test/python/dagcircuit/test_dagcircuit.py
+++ b/test/python/dagcircuit/test_dagcircuit.py
@@ -1362,5 +1362,80 @@ def test_dag_depth3(self):
         self.assertEqual(dag.depth(), 6)
 
 
+class TestConditional(QiskitTestCase):
+    """Test the classical conditional gates."""
+
+    def setUp(self):
+        super().setUp()
+        self.qreg = QuantumRegister(3, "q")
+        self.creg = ClassicalRegister(2, "c")
+        self.creg2 = ClassicalRegister(2, "c2")
+        self.qubit0 = self.qreg[0]
+        self.circuit = QuantumCircuit(self.qreg, self.creg, self.creg2)
+        self.dag = None
+
+    def test_creg_conditional(self):
+        """Test consistency of conditional on classical register."""
+
+        self.circuit.h(self.qreg[0]).c_if(self.creg, 1)
+        self.dag = circuit_to_dag(self.circuit)
+        gate_node = self.dag.gate_nodes()[0]
+        self.assertEqual(gate_node.op, HGate())
+        self.assertEqual(gate_node.qargs, [self.qreg[0]])
+        self.assertEqual(gate_node.cargs, [])
+        self.assertEqual(gate_node.condition, (self.creg, 1))
+        self.assertEqual(
+            sorted(self.dag._multi_graph.in_edges(gate_node._node_id)),
+            sorted(
+                [
+                    (self.dag.input_map[self.qreg[0]]._node_id, gate_node._node_id, self.qreg[0]),
+                    (self.dag.input_map[self.creg[0]]._node_id, gate_node._node_id, self.creg[0]),
+                    (self.dag.input_map[self.creg[1]]._node_id, gate_node._node_id, self.creg[1]),
+                ]
+            ),
+        )
+
+        self.assertEqual(
+            sorted(self.dag._multi_graph.out_edges(gate_node._node_id)),
+            sorted(
+                [
+                    (gate_node._node_id, self.dag.output_map[self.qreg[0]]._node_id, self.qreg[0]),
+                    (gate_node._node_id, self.dag.output_map[self.creg[0]]._node_id, self.creg[0]),
+                    (gate_node._node_id, self.dag.output_map[self.creg[1]]._node_id, self.creg[1]),
+                ]
+            ),
+        )
+
+    def test_clbit_conditional(self):
+        """Test consistency of conditional on single classical bit."""
+
+        self.circuit.h(self.qreg[0]).c_if(self.creg[0], 1)
+        self.dag = circuit_to_dag(self.circuit)
+        gate_node = self.dag.gate_nodes()[0]
+        self.assertEqual(gate_node.op, HGate())
+        self.assertEqual(gate_node.qargs, [self.qreg[0]])
+        self.assertEqual(gate_node.cargs, [])
+        self.assertEqual(gate_node.condition, (self.creg[0], 1))
+        self.assertEqual(
+            sorted(self.dag._multi_graph.in_edges(gate_node._node_id)),
+            sorted(
+                [
+                    (self.dag.input_map[self.qreg[0]]._node_id, gate_node._node_id, self.qreg[0]),
+                    (self.dag.input_map[self.creg[0]]._node_id, gate_node._node_id, self.creg[0]),
+                ]
+            ),
+        )
+
+        self.assertEqual(
+            sorted(self.dag._multi_graph.out_edges(gate_node._node_id)),
+            sorted(
+                [
+                    (gate_node._node_id, self.dag.output_map[self.qreg[0]]._node_id, self.qreg[0]),
+                    (gate_node._node_id, self.dag.output_map[self.creg[0]]._node_id, self.creg[0]),
+                ]
+            ),
+        )
+
+
 if __name__ == "__main__":
     unittest.main()