Skip to content

Commit

Permalink
fix while loop test
Browse files Browse the repository at this point in the history
  • Loading branch information
ewinston committed Sep 29, 2022
1 parent 463a466 commit fb0496e
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 49 deletions.
1 change: 0 additions & 1 deletion qiskit/circuit/instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ def __eq__(self, other):
or self.definition != other.definition
):
return False

for self_param, other_param in zip_longest(self.params, other.params):
try:
if self_param == other_param:
Expand Down
45 changes: 45 additions & 0 deletions qiskit/dagcircuit/dagcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,51 @@ def remove_cregs(self, *cregs):
for creg in cregs:
del self.cregs[creg.name]

def remove_qubits(self, *qubits):
if any(not isinstance(qubit, Qubit) for qubit in qubits):
raise DAGCircuitError(
"qubits not of type Qubit: %s" % [b for b in qubits if not isinstance(b, Qubit)]
)

qubits = set(qubits)
unknown_qubits = qubits.difference(self.qubits)
if unknown_qubits:
raise DAGCircuitError("qubits not in circuit: %s" % unknown_qubits)

busy_qubits = {bit for bit in qubits if not self._is_wire_idle(bit)}
if busy_qubits:
raise DAGCircuitError("qubits not idle: %s" % busy_qubits)

# remove any references to bits
qregs_to_remove = {qreg for qreg in self.qregs.values() if not qubits.isdisjoint(qreg)}
self.remove_qregs(*qregs_to_remove)

for qubit in qubits:
self._remove_idle_wire(qubit)
self.qubits.remove(qubit)

def remove_qregs(self, *qregs):
"""
Remove classical registers from the circuit, leaving underlying bits
in place.
Raises:
DAGCircuitError: a qreg is not a QuantumRegister, or is not in
the circuit.
"""
if any(not isinstance(qreg, QuantumRegister) for qreg in qregs):
raise DAGCircuitError(
"qregs not of type QuantumRegister: %s"
% [r for r in qregs if not isinstance(r, QuantumRegister)]
)

unknown_qregs = set(qregs).difference(self.qregs.values())
if unknown_qregs:
raise DAGCircuitError("qregs not in circuit: %s" % unknown_qregs)

for qreg in qregs:
del self.qregs[qreg.name]

def _is_wire_idle(self, wire):
"""Check if a wire is idle.
Expand Down
2 changes: 1 addition & 1 deletion qiskit/dagcircuit/dagnode.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def semantic_eq(node1, node2, bit_indices1=None, bit_indices2=None):
if node1.op.name == node2.op.name and node1.name in {"barrier", "swap", "cz"}:
return set(node1_qargs) == set(node2_qargs)

if node1_qargs == node2_qargs):
if node1_qargs == node2_qargs:
if node1_cargs == node2_cargs:
if getattr(node1.op, "condition", None) == getattr(node2.op, "condition", None):
if node1.op == node2.op:
Expand Down
45 changes: 29 additions & 16 deletions qiskit/transpiler/passes/routing/stochastic_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class StochasticSwap(TransformationPass):
2. We do not use the fact that the input state is zero to simplify
the circuit.
"""
_count = 0 # track number of instances of this class

def __init__(
self, coupling_map, trials=20, seed=None, fake_run=False, initial_layout=None, _depth=0
Expand All @@ -73,11 +74,12 @@ def __init__(
self.seed = seed
self.fake_run = fake_run
self.qregs = None
self.rng = None
self.trivial_layout = None
self.initial_layout = initial_layout
self._qubit_indices = None
self._depth = _depth
self._count += 1
self._instance_num = self._count

def run(self, dag):
"""Run the StochasticSwap pass on `dag`.
Expand All @@ -100,10 +102,6 @@ def run(self, dag):
raise TranspilerError("The layout does not match the amount of qubits in the DAG")

canonical_register = dag.qregs["q"]
# if self.initial_layout:
# self.trivial_layout = self.initial_layout
# else:
# self.trivial_layout = Layout.generate_trivial_layout(canonical_register)
self.trivial_layout = Layout.generate_trivial_layout(canonical_register)
if self.initial_layout is None:
self.initial_layout = self.trivial_layout
Expand Down Expand Up @@ -246,7 +244,7 @@ def _layer_update(self, dag, layer, best_layout, best_depth, best_circuit):
"""
layout = best_layout
logger.debug("layer_update: layout = %s", layout)
logger.debug("layer_update: self.trivial_layout = %s", self.trivial_layout)
logger.debug("layer_update: self.initial_layout = %s", self.initial_layout)

# Output any swaps
if best_depth > 0:
Expand Down Expand Up @@ -297,16 +295,24 @@ def _mapper(self, circuit_graph, coupling_graph, trials=20):

logger.debug("layout = %s", layout)
# Iterate over layers
from qiskit.converters import dag_to_circuit, circuit_to_dag
for i, layer in enumerate(layerlist):
subdag = layer["graph"]
cf_layer = False
cf_layout = None
for node in subdag.op_nodes(op=ControlFlowOp):
updated_ctrl_op, cf_layout = self._transpile_controlflow_op(node.op, layout)
node.op = updated_ctrl_op
updated_ctrl_op, cf_layout, idle_qubits = self._transpile_controlflow_op(node, layout, circuit_graph)
cf_subdag = DAGCircuit()
cf_qubits = [qubit for qubit in circuit_graph.qubits if qubit not in idle_qubits]
qreg = QuantumRegister(len(cf_qubits), "q")
cf_subdag.add_qreg(qreg)
for creg in subdag.cregs.values():
cf_subdag.add_creg(creg)
cf_subdag.apply_operation_back(updated_ctrl_op, cf_subdag.qubits, node.cargs)
subdag = cf_subdag
cf_layer = True
if cf_layer:
order = layout.reorder_bits(dagcircuit_output.qubits)
order = layout.reorder_bits(range(len(cf_qubits)))
dagcircuit_output.compose(subdag, qubits=order)
success_flag = True
else:
Expand Down Expand Up @@ -374,14 +380,21 @@ def _mapper(self, circuit_graph, coupling_graph, trials=20):
return circuit_graph
return dagcircuit_output

def _transpile_controlflow_op(self, cf_op, current_layout, _depth=0):
def _transpile_controlflow_op(self, cf_opnode, current_layout, dag, _depth=0):
"""handle controlflow ops by type"""
# seed = None
# if self.seed is not None:
# seed = self.seed + self._instance_num
seed = self.seed

_pass = self.__class__(
self.coupling_map, initial_layout=current_layout, seed=self.seed, _depth=self._depth + 1
self.coupling_map, initial_layout=current_layout, seed=seed,
_depth=self._depth + 1
)
if isinstance(cf_op, IfElseOp):
return route_cf_multiblock(_pass, cf_op, current_layout, self.qregs, seed=self.seed)
elif isinstance(cf_op, (ForLoopOp, WhileLoopOp)):
return route_cf_looping(_pass, cf_op, current_layout, seed=self.seed)
if isinstance(cf_opnode.op, IfElseOp):
return route_cf_multiblock(_pass, cf_opnode, current_layout, self.qregs,
dag, seed=seed)
elif isinstance(cf_opnode.op, (ForLoopOp, WhileLoopOp)):
return route_cf_looping(_pass, cf_opnode, current_layout, dag, seed=seed)
else:
raise TranspilerError(f"unsupported control flow operation: {cf_op}")
raise TranspilerError(f"unsupported control flow operation: {cf_opnode}")
108 changes: 82 additions & 26 deletions qiskit/transpiler/passes/routing/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,27 @@

import numpy as np
from qiskit.transpiler.layout import Layout
from qiskit.dagcircuit import DAGCircuit
from qiskit.circuit import QuantumRegister, ClassicalRegister


def route_cf_multiblock(tpass, cf_op, current_layout, qregs, seed=None):
def route_cf_multiblock(tpass, cf_opnode, current_layout, qregs, root_dag, seed=None):
"""Transpile control flow instructions which may contain multiple
blocks (e.g. IfElseOp). Since each control flow block may
induce a yield a different layout, this function applies swaps
to the shorter depth blocks to make all final layouts match.
blocks (e.g. IfElseOp). Since each control flow block may yield a
different layout, this function applies swaps to the shorter depth
blocks to make all final layouts match.
Args:
tpass (BasePass): Transpiler pass object to use recursively.
cf_op (IfElseOp): multiblock instruction.
current_layout (Layout): The current layout at the start of the instruction.
qregs (list(QuantumRegister)): quantum registers for circuit
seed (int): seed for RNG of internal layout transformation.
root_dag (DAGCircuit): root dag of compilation
Returns:
IfElseOp: transpiled control flow operation
final_layout (Layout): layout after instruction
"""
# pylint: disable=cyclic-import
from qiskit.transpiler.passes.routing.layout_transformation import LayoutTransformation
Expand All @@ -43,41 +46,60 @@ def route_cf_multiblock(tpass, cf_op, current_layout, qregs, seed=None):
coupling = tpass.coupling_map
block_dags = [] # control flow dag blocks
block_layouts = [] # control flow layouts

for i, block in enumerate(cf_op.blocks):
tpass.initial_layout = current_layout
# expand to full width for routing
cf_op = cf_opnode.op
order = [root_dag.qubits.index(bit) for bit in cf_opnode.qargs]
for block in cf_op.blocks:
full_dag_block = root_dag.copy_empty_like()
dag_block = circuit_to_dag(block)
updated_dag_block = tpass.run(dag_block)

full_dag_block.compose(dag_block, qubits=order)
tpass.initial_layout = current_layout
updated_dag_block = tpass.run(full_dag_block)
block_dags.append(updated_dag_block)
block_layouts.append(tpass.property_set["final_layout"].copy())

depth_cnt = [bdag.depth() for bdag in block_dags]
maxind = np.argmax(depth_cnt)
block_circuits = [None] * len(block_layouts)
p2v = current_layout._p2v
for i, dag in enumerate(block_dags):
idle_qubits = [None] * len(block_dags)
for i, updated_dag_block in enumerate(block_dags):
if i == maxind:
block_circuits[i] = dag_to_circuit(dag)
block_circuits[i] = dag_to_circuit(updated_dag_block)
else:
layout_xform = LayoutTransformation(
coupling, block_layouts[i], block_layouts[maxind], seed=seed, inplace=False
)
layout_xform.run(dag)
physical_swap_dag = layout_xform.perm_circ.circuit
layout_xform.run(updated_dag_block)
physical_swap_dag = layout_xform.property_set["perm_circ"].circuit
if physical_swap_dag.depth():
virtual_swap_dag = dag.copy_empty_like()
virtual_swap_dag = updated_dag_block.copy_empty_like()
order = [
p2v[virtual_swap_dag.qubits.index(qubit)] for qubit in layout_xform.perm_qubits
p2v[virtual_swap_dag.qubits.index(qubit)]
for qubit in layout_xform.property_set["perm_qubits"]
]
virtual_swap_dag.compose(physical_swap_dag, qubits=order)
dag.compose(virtual_swap_dag)
block_circuits[i] = dag_to_circuit(dag)
updated_dag_block.compose(virtual_swap_dag)
idle_qubits[i] = _qubit_wires(updated_dag_block.idle_wires())
cfop_idle_qubits = set.intersection(*map(set, idle_qubits))
# contract idle bits from full width post routing
for i, updated_dag_block in enumerate(block_dags):
updated_dag_block.remove_qubits(*cfop_idle_qubits)
new_dag_block = DAGCircuit()
new_num_qubits = updated_dag_block.num_qubits()
qreg = QuantumRegister(new_num_qubits, "q")
new_dag_block.add_qreg(qreg)
for creg in updated_dag_block.cregs.values():
new_dag_block.add_creg(creg)
for node in updated_dag_block.op_nodes():
new_qargs = [qreg[updated_dag_block.qubits.index(bit)] for bit in node.qargs]
new_dag_block.apply_operation_back(node.op, new_qargs, node.cargs)
block_circuits[i] = dag_to_circuit(new_dag_block)

final_layout = block_layouts[maxind]
return cf_op.replace_blocks(block_circuits), final_layout
return cf_op.replace_blocks(block_circuits), final_layout, cfop_idle_qubits


def route_cf_looping(tpass, cf_op, current_layout, seed=None):
def route_cf_looping(tpass, cf_opnode, current_layout, dag, seed=None):
"""For looping this pass adds a swap layer using LayoutTransformation
to the end of the loop body to bring the layout back to the
starting layout. This prevents reapplying layout changing
Expand All @@ -99,25 +121,45 @@ def route_cf_looping(tpass, cf_op, current_layout, seed=None):
from qiskit.transpiler.passes.routing.layout_transformation import LayoutTransformation
from qiskit.converters import dag_to_circuit, circuit_to_dag

cf_op = cf_opnode.op
coupling = tpass.coupling_map
full_num_qubits = coupling.size()
dag_block = circuit_to_dag(cf_op.blocks[0])
# expand to full width for routing
full_dag_block = dag.copy_empty_like()
start_layout = current_layout
updated_dag_block = tpass.run(dag_block)
order = [dag.qubits.index(bit) for bit in cf_opnode.qargs]
full_dag_block.compose(dag_block, qubits=order)
updated_dag_block = tpass.run(full_dag_block)

updated_layout = tpass.property_set["final_layout"].copy()

layout_xform = LayoutTransformation(
coupling, updated_layout, start_layout, seed=seed, inplace=False
)
layout_xform.run(updated_dag_block)
physical_swap_dag = layout_xform.perm_circ.circuit
physical_swap_dag = layout_xform.property_set["perm_circ"].circuit
if physical_swap_dag.depth():
p2v = current_layout._p2v
virtual_swap_dag = updated_dag_block.copy_empty_like()
order = [p2v[virtual_swap_dag.qubits.index(qubit)] for qubit in layout_xform.perm_qubits]
order = [p2v[virtual_swap_dag.qubits.index(qubit)] for qubit in layout_xform.property_set["perm_qubits"]]
virtual_swap_dag.compose(physical_swap_dag, qubits=order)
updated_dag_block.compose(virtual_swap_dag)
updated_circ_block = dag_to_circuit(updated_dag_block)
return cf_op.replace_blocks([updated_circ_block]), current_layout
# contract from full width post routing
idle_qubits = _qubit_wires(updated_dag_block.idle_wires())
updated_dag_block.remove_qubits(*idle_qubits)
new_dag_block = DAGCircuit()
new_num_qubits = updated_dag_block.num_qubits()
qreg = QuantumRegister(new_num_qubits, "q")
new_dag_block.add_qreg(qreg)
for creg in updated_dag_block.cregs.values():
new_dag_block.add_creg(creg)
for node in updated_dag_block.op_nodes():
new_qargs = [qreg[updated_dag_block.qubits.index(bit)] for bit in node.qargs]
new_dag_block.apply_operation_back(node.op, new_qargs, node.cargs)
updated_circ_block = dag_to_circuit(new_dag_block)
cf_op.num_qubits = updated_circ_block.num_qubits
return cf_op.replace_blocks([updated_circ_block]), current_layout, idle_qubits


def get_ordered_virtual_qubits(layout, qregs):
Expand Down Expand Up @@ -149,3 +191,17 @@ def combine_permutations(*permutations):
for this_order in permutations[1:]:
order = [order[i] for i in this_order]
return order

def _qubit_wires(wires):
"""
Return list of Qubits from list of wires.
Args:
wires (list(Bit)): list of bits
Returns:
list(Qubit)
"""
from qiskit.circuit import Qubit

return [bit for bit in wires if isinstance(bit, Qubit)]
Loading

0 comments on commit fb0496e

Please sign in to comment.