Skip to content

Commit

Permalink
Further 2QAN troubleshooting
Browse files Browse the repository at this point in the history
  • Loading branch information
Misty-W committed Jan 3, 2025
1 parent a16212d commit ca0938f
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 18 deletions.
118 changes: 118 additions & 0 deletions ucc/transpiler_passes/bench_arch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import numpy as np
import math
import networkx as nx
from qiskit import QuantumCircuit
from qiskit.circuit import Barrier, Measure
from qiskit.circuit.library.standard_gates import HGate

class BenchArch(object):
"""Base class for generating device coupling graph, circuit dag graph"""
def __init__(self, qasm, lattice_xy=None, coupling_map=None):
"""
Args:
qasm: circuit in OpenQASM format
lattice_xy: (x,y) if coupling_map is not given and the topology is grid
coupling_map: connectivity between qubits such as
[(0,1), (1,2), (2,3), (3,4), (4,5), (5,6), (6,7), (0,7), (0,8), (1,9), (0,10)]
"""
# Transform OpenQASM to qiskit circuit
self.benchmark = QuantumCircuit.from_qasm_str(qasm)
self.b_qbts = len(self.benchmark.qubits)
# Generate the topology graph (2d grid) and the distance matrix
if coupling_map:
self.coupling_map = coupling_map
else:
self.lattice_x, self.lattice_y = lattice_xy
self.coupling_map = self.grid_coupling()

# n_qbits is the number of qubits in the given topology
self.topology, self.distmat, self.n_qbits = self.topology_graph()
self.G_circ, self.pairs, self.instrs, self.q1_instrs = self.circuit_graph()
adjmat_csr = nx.adjacency_matrix(self.G_circ)
self.adjmat = adjmat_csr.todense()

def zz_tuple(self, qbits):
"""
Representation for two-qubit gate
"""
if isinstance(qbits[0], int):
physical_q_0 = qbits[0]
physical_q_1 = qbits[1]
else:
physical_q_0 = qbits[0]._index
physical_q_1 = qbits[1]._index
r_0 = min(physical_q_0, physical_q_1)
r_1 = max(physical_q_0, physical_q_1)
return (r_0, r_1)

def circuit_graph(self):
# Generate the interaction graph of the input circuit and the interaction matrix
qpairs = []
all_qs = []
instrs2q = {}
instrs1q = {0: [], 1:[]}
for instr, qbits, cbits in self.benchmark.data:
if len(qbits) == 2:
qpairs.append((qbits[0]._index, qbits[1]._index))
instrs2q[self.zz_tuple(qbits)] = instr
elif len(qbits) == 1:
if isinstance(instr, Barrier):
continue
elif isinstance(instr, Measure):
continue
elif isinstance(instr, HGate):
instrs1q[0].append((instr, qbits[0]._index))
else:
instrs1q[1].append((instr, qbits[0]._index))
for q in qbits:
if q not in all_qs:
all_qs.append(q)
G_circ = nx.Graph()
G_circ.add_nodes_from(np.arange(self.n_qbits))
G_circ.add_edges_from(qpairs)
return G_circ, qpairs, instrs2q, instrs1q

def instruction_graph(self, qpairs):
qbits = []
for qpair in qpairs:
for q in qpair:
if q not in qbits:
qbits.append(q)

G = nx.Graph()
G.add_nodes_from(qbits)
G.add_edges_from(qpairs)
return G

def grid_coupling(self):
# Generate the 2d grid coupling map
pnodes = [(x, y) for y in range(0, self.lattice_y) for x in range(0, self.lattice_x)]
locs_pqbits = {}
for node in pnodes:
locs_pqbits[node] = node[1]*self.lattice_x+node[0]
topology_edges = []
for i in range(len(pnodes)):
node = pnodes[i]
for j in range(i+1,len(pnodes)):
if abs(pnodes[j][0]-node[0]) + abs(pnodes[j][1]-node[1]) == 1:
topology_edges.append((locs_pqbits[node], locs_pqbits[pnodes[j]]))
return topology_edges

def topology_graph(self):
# Generate the topology graph and the distance matrix
all_qbts = []
for qpair in self.coupling_map:
for q in qpair:
if q not in all_qbts:
all_qbts.append(q)
n_qbits = len(all_qbts)
topology = nx.Graph()
topology.add_nodes_from(np.arange(n_qbits))
topology.add_edges_from(self.coupling_map)

distmat = np.zeros((n_qbits, n_qbits))
for i in range(n_qbits-1):
for j in range(i+1, n_qbits):
distmat[i,j] = nx.shortest_path_length(topology, source=i, target=j)
distmat[j,i] = distmat[i,j]
return topology, distmat, n_qbits
56 changes: 49 additions & 7 deletions ucc/transpiler_passes/py2qan_plugin.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,63 @@
from qiskit import QuantumCircuit
from qiskit.transpiler import TransformationPass
import os

from qiskit import QuantumCircuit, user_config
from qiskit.transpiler import TransformationPass, PassManager
from qiskit.transpiler.passes import SabreLayout
from qiskit.converters import circuit_to_dag, dag_to_circuit
from qiskit.qasm2 import dumps
from qiskit.utils.parallel import CPU_COUNT

from py2qan import HeuristicMapper, QuRouter


CONFIG = user_config.get_config()


class Py2QAN_Routing(TransformationPass):

def __init__(self, init_map, coupling_map):
def __init__(self, coupling_map):
super().__init__()
self.init_map = init_map
self.coupling_map = coupling_map
self.inner_pm = PassManager()

def fill_partial_mapping(self, partial_map, coupling_map):
# Fill the qubit map when #qubits in the circuit is fewer than #qubits in the given topology
final_map = partial_map.copy()
n_qbts = len(partial_map)
device_qubits = []
for edge in coupling_map:
for q in edge:
if q not in device_qubits:
device_qubits.append(q)
unused_dqs = [q for q in device_qubits if q not in partial_map.values()]
for i in range(len(unused_dqs)):
final_map[n_qbts+i]=unused_dqs[i]
return final_map

def run(self, dag):
qasm_rep = dumps(dag_to_circuit(dag))
circ = dag_to_circuit(dag)
# hmapper = HeuristicMapper(qasm_rep, coupling_map=self.coupling_map)
# init_map = hmapper.run_qiskit(max_iterations=5)
router = QuRouter(qasm_rep, init_map=self.init_map, coupling_map=self.coupling_map)
self.inner_pm.append(
SabreLayout(
self.coupling_map,
max_iterations=4,
swap_trials=_get_trial_count(20),
layout_trials=_get_trial_count(20),
)
)
self.inner_pm.run(circ)
vpmap = self.inner_pm.property_set["layout"].get_virtual_bits()
init_map0 = {}
for k,v in vpmap.items():
init_map0[k._index]=v
init_map = self.fill_partial_mapping(init_map0, self.coupling_map)

router = QuRouter(dumps(circ), init_map=init_map, coupling_map=self.coupling_map)
routed_circ, _ = router.run(layers=1, msmt=False)
return circuit_to_dag(QuantumCircuit.from_qasm_str(routed_circ))
return circuit_to_dag(QuantumCircuit.from_qasm_str(routed_circ))

def _get_trial_count(default_trials=5):
if CONFIG.get("sabre_all_threads", None) or os.getenv("QISKIT_SABRE_ALL_THREADS"):
return max(CPU_COUNT, default_trials)
return default_trials
20 changes: 9 additions & 11 deletions ucc/transpilers/ucc_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from qiskit.utils.parallel import CPU_COUNT
from qiskit.transpiler import PassManager
from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel
from qiskit.utils.parallel import CPU_COUNT
from qiskit import user_config
from qiskit.transpiler import CouplingMap
from qiskit.transpiler.passes import (
Expand Down Expand Up @@ -78,23 +77,22 @@ def add_map_passes(self, coupling_list = None):
# self.pass_manager.append(ElidePermutations())
# self.pass_manager.append(SpectralMapping(coupling_list))
# self.pass_manager.append(SetLayout(pass_manager_config.initial_layout))
self.pass_manager.append(
SabreLayout(
coupling_map,
max_iterations=4,
swap_trials=_get_trial_count(20),
layout_trials=_get_trial_count(20),
)
)
# self.pass_manager.append(
# SabreLayout(
# coupling_map,
# max_iterations=4,
# swap_trials=_get_trial_count(20),
# layout_trials=_get_trial_count(20),
# )
# )
# self.pass_manager.append(
# SabreSwap(
# coupling_map,
# heuristic="decay",
# trials=_get_trial_count(20),
# )
# )
init_map = self.pass_manager.property_set["layout"]
self.pass_manager.append(Py2QAN_Routing(init_map=init_map,coupling_map=coupling_map))
self.pass_manager.append(Py2QAN_Routing(coupling_map=coupling_map))
self.add_local_passes(1)

def run(self, circuits, coupling_list=None):
Expand Down

0 comments on commit ca0938f

Please sign in to comment.