Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Expr support to DAGCircuit.substitute_node_with_dag
Browse files Browse the repository at this point in the history
Most of this was automatically upgraded during the switchover of
`DAGCircuit.compose` to use the same general variable-remapping forms
that `QuantumCircuit.compose` does.  The remaining piece is to skip
attempting to propagate the condition onto the subnodes; that is legacy
behaviour from an old model of treating conditions as some "extra" to an
operation, and not an inherent component of what was being applied.
jakelishman committed Jul 4, 2023

Verified

This commit was signed with the committer’s verified signature. The key has expired.
aitbw Angel Perez
1 parent ac942ee commit 0797e90
Showing 2 changed files with 104 additions and 2 deletions.
9 changes: 7 additions & 2 deletions qiskit/dagcircuit/dagcircuit.py
Original file line number Diff line number Diff line change
@@ -1122,8 +1122,13 @@ def substitute_node_with_dag(self, node, input_dag, wires=None, propagate_condit
)

reverse_wire_map = {b: a for a, b in wire_map.items()}
op_condition = getattr(node.op, "condition", None)
if propagate_condition and op_condition is not None:
# It doesn't make sense to try and propagate a condition from a control-flow op; a
# replacement for the control-flow op should implement the operation completely.
if (
propagate_condition
and not isinstance(node.op, ControlFlowOp)
and (op_condition := getattr(node.op, "condition", None)) is not None
):
in_dag = input_dag.copy_empty_like()
# The remapping of `condition` below is still using the old code that assumes a 2-tuple.
# This is because this remapping code only makes sense in the case of non-control-flow
97 changes: 97 additions & 0 deletions test/python/dagcircuit/test_dagcircuit.py
Original file line number Diff line number Diff line change
@@ -1737,6 +1737,103 @@ def test_substitute_circuit_one_back(self):

self.assertEqual(self.dag, expected)

def test_substitute_dag_if_else_expr(self):
"""Test that an `IfElseOp` with an `Expr` condition can be substituted for a DAG that
contains an `IfElseOp` with an `Expr` condition."""

body_rep = QuantumCircuit(1)
body_rep.z(0)

q_rep = QuantumRegister(1)
c_rep = ClassicalRegister(2)
replacement = DAGCircuit()
replacement.add_qreg(q_rep)
replacement.add_creg(c_rep)
replacement.apply_operation_back(XGate(), [q_rep[0]], [])
replacement.apply_operation_back(
IfElseOp(expr.logic_not(expr.bit_and(c_rep, 1)), body_rep, None), [q_rep[0]], []
)

true_src = QuantumCircuit(1)
true_src.x(0)
true_src.z(0)
false_src = QuantumCircuit(1)
false_src.x(0)
q_src = QuantumRegister(4)
c1_src = ClassicalRegister(2)
c2_src = ClassicalRegister(2)
src = DAGCircuit()
src.add_qreg(q_src)
src.add_creg(c1_src)
src.add_creg(c2_src)
node = src.apply_operation_back(
IfElseOp(expr.logic_not(expr.bit_and(c1_src, 1)), true_src, false_src), [q_src[2]], []
)

wires = {q_rep[0]: q_src[2], c_rep[0]: c1_src[0], c_rep[1]: c1_src[1]}
src.substitute_node_with_dag(node, replacement, wires=wires)

expected = DAGCircuit()
expected.add_qreg(q_src)
expected.add_creg(c1_src)
expected.add_creg(c2_src)
expected.apply_operation_back(XGate(), [q_src[2]], [])
expected.apply_operation_back(
IfElseOp(expr.logic_not(expr.bit_and(c1_src, 1)), body_rep, None), [q_src[2]], []
)

self.assertEqual(src, expected)

def test_substitute_dag_switch_expr(self):
"""Test that a `SwitchCaseOp` with an `Expr` target can be substituted for a DAG that
contains another `SwitchCaseOp` with an `Expr` target."""

case_rep = QuantumCircuit(1)
case_rep.z(0)

q_rep = QuantumRegister(1)
c_rep = ClassicalRegister(2)
replacement = DAGCircuit()
replacement.add_qreg(q_rep)
replacement.add_creg(c_rep)
replacement.apply_operation_back(XGate(), [q_rep[0]], [])
# This could logically be an `IfElseOp`, but the point is just to test the `target`.
replacement.apply_operation_back(
SwitchCaseOp(expr.equal(c_rep[0], c_rep[1]), [(True, case_rep)]), [q_rep[0]], []
)

same_src = QuantumCircuit(1)
same_src.x(0)
same_src.z(0)
diff_src = QuantumCircuit(1)
diff_src.x(0)
q_src = QuantumRegister(4)
c1_src = ClassicalRegister(2)
c2_src = ClassicalRegister(2)
src = DAGCircuit()
src.add_qreg(q_src)
src.add_creg(c1_src)
src.add_creg(c2_src)
node = src.apply_operation_back(
SwitchCaseOp(expr.lift(c1_src), [((1, 2), diff_src), ((0, 3), same_src)]),
[q_src[2]],
[],
)

wires = {q_rep[0]: q_src[2], c_rep[0]: c1_src[0], c_rep[1]: c1_src[1]}
src.substitute_node_with_dag(node, replacement, wires=wires)

expected = DAGCircuit()
expected.add_qreg(q_src)
expected.add_creg(c1_src)
expected.add_creg(c2_src)
expected.apply_operation_back(XGate(), [q_src[2]], [])
node = expected.apply_operation_back(
SwitchCaseOp(expr.equal(c1_src[0], c1_src[1]), [(True, case_rep)]), [q_src[2]], []
)

self.assertEqual(src, expected)

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.op.condition."""

0 comments on commit 0797e90

Please sign in to comment.