Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Classical conditioning on individual classical bits now supported #6018

Merged
merged 35 commits into from
May 25, 2021
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
33d9b6b
Cbit conditional
TharrmashasthaPV Mar 13, 2021
5fda28f
Merge branch 'master' into fixissue1160
TharrmashasthaPV Mar 13, 2021
873e7c7
Merge branch 'master' of https://github.com/TharrmashasthaPV/qiskit-t…
TharrmashasthaPV Mar 13, 2021
adfb361
lint fixed
TharrmashasthaPV Mar 13, 2021
3c57692
Merge branch 'fixissue1160' of https://github.com/TharrmashasthaPV/qi…
TharrmashasthaPV Mar 13, 2021
26430d2
q
TharrmashasthaPV Mar 19, 2021
b46e0b0
added_tests
TharrmashasthaPV Mar 23, 2021
ccf3c0a
Added tests
TharrmashasthaPV Mar 23, 2021
d4f8b4f
lint fix
TharrmashasthaPV Mar 23, 2021
e39c01b
lint-fix
TharrmashasthaPV Mar 23, 2021
7bf04ca
Merge branch 'master' of https://github.com/Qiskit/qiskit-terra into …
TharrmashasthaPV Mar 26, 2021
69b5b55
added extra test
TharrmashasthaPV Mar 26, 2021
2caa5e2
Merge branch 'master' into fixissue1160
TharrmashasthaPV Mar 29, 2021
54f6f16
Merge branch 'master' of https://github.com/TharrmashasthaPV/qiskit-t…
TharrmashasthaPV Apr 7, 2021
00475e5
Merge branch 'master' of https://github.com/TharrmashasthaPV/qiskit-t…
TharrmashasthaPV Apr 16, 2021
b2d9f42
unwanted comment removed
TharrmashasthaPV Apr 16, 2021
bb55831
refactored for bit.register deprication
TharrmashasthaPV Apr 16, 2021
1fb38e3
Merge branch 'master' of upstream Qiskit into fixissue1160
TharrmashasthaPV Apr 20, 2021
431510b
fixed some recommended changes
TharrmashasthaPV Apr 29, 2021
3fecd08
minor changes
TharrmashasthaPV May 2, 2021
c679daa
Minor fix in instruction.py
TharrmashasthaPV May 2, 2021
bdaf2a3
Merge branch 'main' of https://github.com/Qiskit/qiskit-terra into fi…
TharrmashasthaPV May 4, 2021
17e30f6
Update branch 'fixissue1160'
TharrmashasthaPV May 4, 2021
0f7da6b
Resolved conflicts
TharrmashasthaPV May 7, 2021
bafd04d
Merge branch 'main' into fixissue1160
1ucian0 May 10, 2021
3dc10f8
simpler test
1ucian0 May 10, 2021
da56baa
the other test
1ucian0 May 10, 2021
864f5af
Merge branch 'fixissue1160' of github.com:TharrmashasthaPV/qiskit-ter…
1ucian0 May 12, 2021
f079f5d
reverting my change
1ucian0 May 12, 2021
a97df0d
Merge branch 'main' of github.com:Qiskit/qiskit-terra into fixissue1160
1ucian0 May 13, 2021
3842dec
Merge branch 'main' into fixissue1160
TharrmashasthaPV May 13, 2021
1d6edb3
Merge branch 'main' into fixissue1160
TharrmashasthaPV May 21, 2021
40614a8
Merge branch 'main' of https://github.com/Qiskit/qiskit-terra into fi…
TharrmashasthaPV May 24, 2021
eca3dac
Added reno
TharrmashasthaPV May 24, 2021
8af5afb
Merge branch 'main' into fixissue1160
kdk May 24, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions qiskit/assembler/assemble_circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
15 changes: 10 additions & 5 deletions qiskit/circuit/instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
71 changes: 52 additions & 19 deletions qiskit/dagcircuit/dagcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down
18 changes: 18 additions & 0 deletions test/python/basicaer/test_qasm_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
14 changes: 14 additions & 0 deletions test/python/circuit/test_extensions_standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
23 changes: 23 additions & 0 deletions test/python/compiler/test_assembler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down
75 changes: 75 additions & 0 deletions test/python/dagcircuit/test_dagcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()