mirror of https://github.com/Qiskit/qiskit.git
first pass at swap mapper, bugs fixed
This commit is contained in:
parent
dd2f7bcfee
commit
17f9de403e
|
@ -69,6 +69,10 @@ class Circuit:
|
|||
# Output precision for printing floats
|
||||
self.prec = 10
|
||||
|
||||
def get_qubits(self):
|
||||
"""Return a list of qubits as (qreg, index) pairs."""
|
||||
return [(k, i) for k, v in self.qregs.items() for i in range(v)]
|
||||
|
||||
def rename_register(self, regname, newname):
|
||||
"""Rename a classical or quantum register throughout the circuit.
|
||||
|
||||
|
@ -652,65 +656,108 @@ class Circuit:
|
|||
out += "\n{\n" + self.gates[name]["body"].qasm() + "}"
|
||||
return out
|
||||
|
||||
def qasm(self, qeflag=False):
|
||||
def qasm(self, decls_only=False, add_swap=False,
|
||||
no_decls=False, qeflag=False, aliases=None):
|
||||
"""Return a string containing QASM for this circuit.
|
||||
|
||||
if qeflag is True, add a line to include "qelib1.inc"
|
||||
and only generate gate code for gates not in qelib1.
|
||||
|
||||
if no_decls is True, only print the instructions.
|
||||
|
||||
if aliases is not None, aliases contains a dict mapping
|
||||
the current qubits in the circuit to new qubit names.
|
||||
We will deduce the register names and sizes from aliases.
|
||||
|
||||
if decls_only is True, only print the declarations.
|
||||
|
||||
if add_swap is True, add the definition of swap in terms of
|
||||
cx if necessary.
|
||||
"""
|
||||
printed_gates = []
|
||||
out = "OPENQASM 2.0;\n"
|
||||
if qeflag:
|
||||
out += "include \"qelib1.inc\";\n"
|
||||
for k, v in sorted(self.qregs.items()):
|
||||
out += "qreg %s[%d];\n" % (k, v)
|
||||
for k, v in sorted(self.cregs.items()):
|
||||
out += "creg %s[%d];\n" % (k, v)
|
||||
omit = ["U", "CX", "measure", "reset", "barrier"]
|
||||
if qeflag:
|
||||
qelib = ["u3", "u2", "u1", "cx", "id", "x", "y", "z", "h",
|
||||
"s", "sdg", "t", "tdg", "cz", "cy", "ccx", "cu1", "cu3"]
|
||||
omit.extend(qelib)
|
||||
printed_gates.extend(qelib)
|
||||
for k in self.basis.keys():
|
||||
if k not in omit:
|
||||
if not self.gates[k]["opaque"]:
|
||||
calls = self.gates[k]["body"].calls()
|
||||
for c in calls:
|
||||
if c not in printed_gates:
|
||||
out += self._gate_string(c) + "\n"
|
||||
printed_gates.append(c)
|
||||
if k not in printed_gates:
|
||||
out += self._gate_string(k) + "\n"
|
||||
printed_gates.append(k)
|
||||
ts = nx.topological_sort(self.G)
|
||||
for n in ts:
|
||||
nd = self.G.node[n]
|
||||
if nd["type"] == "op":
|
||||
if nd["condition"] is not None:
|
||||
out += "if(%s==%d) " \
|
||||
% (nd["condition"][0], nd["condition"][1])
|
||||
if len(nd["cargs"]) == 0:
|
||||
nm = nd["name"]
|
||||
qarg = ",".join(map(lambda x: "%s[%d]" % (x[0], x[1]),
|
||||
nd["qargs"]))
|
||||
if len(nd["params"]) > 0:
|
||||
param = ",".join(nd["params"])
|
||||
out += "%s(%s) %s;\n" % (nm, param, qarg)
|
||||
# Rename qregs if necessary
|
||||
if aliases:
|
||||
qregdata = {}
|
||||
for q in aliases.values():
|
||||
if q[0] not in qregdata:
|
||||
qregdata[q[0]] = q[1] + 1
|
||||
elif qregdata[q[0]] < q[1] + 1:
|
||||
qregdata[q[0]] = q[1] + 1
|
||||
else:
|
||||
qregdata = self.qregs
|
||||
# Write top matter
|
||||
if no_decls:
|
||||
out = ""
|
||||
else:
|
||||
printed_gates = []
|
||||
out = "OPENQASM 2.0;\n"
|
||||
if qeflag:
|
||||
out += "include \"qelib1.inc\";\n"
|
||||
for k, v in sorted(qregdata.items()):
|
||||
out += "qreg %s[%d];\n" % (k, v)
|
||||
for k, v in sorted(self.cregs.items()):
|
||||
out += "creg %s[%d];\n" % (k, v)
|
||||
omit = ["U", "CX", "measure", "reset", "barrier"]
|
||||
if qeflag:
|
||||
qelib = ["u3", "u2", "u1", "cx", "id", "x", "y", "z", "h",
|
||||
"s", "sdg", "t", "tdg", "cz", "cy", "ccx", "cu1",
|
||||
"cu3"]
|
||||
omit.extend(qelib)
|
||||
printed_gates.extend(qelib)
|
||||
for k in self.basis.keys():
|
||||
if k not in omit:
|
||||
if not self.gates[k]["opaque"]:
|
||||
calls = self.gates[k]["body"].calls()
|
||||
for c in calls:
|
||||
if c not in printed_gates:
|
||||
out += self._gate_string(c) + "\n"
|
||||
printed_gates.append(c)
|
||||
if k not in printed_gates:
|
||||
out += self._gate_string(k) + "\n"
|
||||
printed_gates.append(k)
|
||||
if add_swap and not qeflag and "cx" not in self.basis:
|
||||
out += "gate cx a,b { CX a,b; }\n"
|
||||
if add_swap and "swap" not in self.basis:
|
||||
out += "gate swap a,b { cx a,b; cx b,a; cx a,b; }\n"
|
||||
# Write the instructions
|
||||
if not decls_only:
|
||||
ts = nx.topological_sort(self.G)
|
||||
for n in ts:
|
||||
nd = self.G.node[n]
|
||||
if nd["type"] == "op":
|
||||
if nd["condition"] is not None:
|
||||
out += "if(%s==%d) " \
|
||||
% (nd["condition"][0], nd["condition"][1])
|
||||
if len(nd["cargs"]) == 0:
|
||||
nm = nd["name"]
|
||||
if aliases:
|
||||
qarglist = map(lambda x: aliases[x], nd["qargs"])
|
||||
else:
|
||||
qarglist = nd["qargs"]
|
||||
qarg = ",".join(map(lambda x: "%s[%d]" % (x[0], x[1]),
|
||||
qarglist))
|
||||
if len(nd["params"]) > 0:
|
||||
param = ",".join(nd["params"])
|
||||
out += "%s(%s) %s;\n" % (nm, param, qarg)
|
||||
else:
|
||||
out += "%s %s;\n" % (nm, qarg)
|
||||
else:
|
||||
out += "%s %s;\n" % (nm, qarg)
|
||||
else:
|
||||
if nd["name"] == "measure":
|
||||
assert len(nd["cargs"]) == 1 and \
|
||||
len(nd["qargs"]) == 1 and \
|
||||
len(nd["params"]) == 0, "bad node data"
|
||||
out += "measure %s[%d] -> %s[%d];\n" \
|
||||
% (nd["qargs"][0][0],
|
||||
nd["qargs"][0][1],
|
||||
nd["cargs"][0][0],
|
||||
nd["cargs"][0][1])
|
||||
else:
|
||||
assert False, "bad node data"
|
||||
if nd["name"] == "measure":
|
||||
assert len(nd["cargs"]) == 1 and \
|
||||
len(nd["qargs"]) == 1 and \
|
||||
len(nd["params"]) == 0, "bad node data"
|
||||
qname = nd["qargs"][0][0]
|
||||
qindex = nd["qargs"][0][1]
|
||||
if aliases:
|
||||
newq = aliases[(qname, qindex)]
|
||||
qname = newq[0]
|
||||
qindex = newq[1]
|
||||
out += "measure %s[%d] -> %s[%d];\n" \
|
||||
% (qname,
|
||||
qindex,
|
||||
nd["cargs"][0][0],
|
||||
nd["cargs"][0][1])
|
||||
else:
|
||||
assert False, "bad node data"
|
||||
return out
|
||||
|
||||
def _check_wires_list(self, wires, name, input_circuit):
|
||||
|
@ -1022,6 +1069,10 @@ class Circuit:
|
|||
earliest layer at index 0. The layers are constructed using a
|
||||
greedy algorithm. Each returned layer is a dict containing
|
||||
{"graph": circuit graph, "partition": list of qubit lists}.
|
||||
|
||||
TODO: Gates that use the same cbits will end up in different
|
||||
layers as this is currently implemented. This is may not be
|
||||
the desired behavior.
|
||||
"""
|
||||
layers_list = []
|
||||
# node_map contains an input node or previous layer node for
|
||||
|
@ -1048,6 +1099,7 @@ class Circuit:
|
|||
# foreground node we can add to the current layer.
|
||||
ops_touched = {}
|
||||
wires_loop = list(wires_with_ops_remaining)
|
||||
emit = False
|
||||
for w in wires_loop:
|
||||
oe = list(filter(lambda x: x[2]["name"] == w,
|
||||
self.G.out_edges(nbunch=[node_map[w]],
|
||||
|
@ -1083,10 +1135,52 @@ class Circuit:
|
|||
for v in itertools.chain(qa, ca, cob):
|
||||
node_map[v] = nxt_nd_idx
|
||||
# Add operation to partition
|
||||
support_list.append(list(set(qa) | set(ca) | set(cob)))
|
||||
if support_list:
|
||||
if nxt_nd["name"] != "barrier":
|
||||
# support_list.append(list(set(qa) | set(ca) |
|
||||
# set(cob)))
|
||||
support_list.append(list(set(qa)))
|
||||
emit = True
|
||||
if emit:
|
||||
l_dict = {"graph": new_layer, "partition": support_list}
|
||||
layers_list.append(l_dict)
|
||||
emit = False
|
||||
else:
|
||||
assert not wires_with_ops_remaining, "not finished but empty?"
|
||||
return layers_list
|
||||
|
||||
def serial_layers(self):
|
||||
"""Return a list of layers for all gates of this circuit.
|
||||
|
||||
A serial layer is a circuit with one gate. The layers have the
|
||||
same structure as in layers().
|
||||
"""
|
||||
layers_list = []
|
||||
ts = nx.topological_sort(self.G)
|
||||
for n in ts:
|
||||
nxt_nd = self.G.node[n]
|
||||
if nxt_nd["type"] == "op":
|
||||
new_layer = Circuit()
|
||||
for k, v in self.qregs.items():
|
||||
new_layer.add_qreg(k, v)
|
||||
for k, v in self.cregs.items():
|
||||
new_layer.add_creg(k, v)
|
||||
new_layer.basis = copy.deepcopy(self.basis)
|
||||
new_layer.gates = copy.deepcopy(self.gates)
|
||||
# Save the support of the operation we add to the layer
|
||||
support_list = []
|
||||
# Operation data
|
||||
qa = copy.copy(nxt_nd["qargs"])
|
||||
ca = copy.copy(nxt_nd["cargs"])
|
||||
pa = copy.copy(nxt_nd["params"])
|
||||
co = copy.copy(nxt_nd["condition"])
|
||||
cob = self._bits_in_condition(co)
|
||||
# Add node to new_layer
|
||||
new_layer.apply_operation_back(nxt_nd["name"],
|
||||
qa, ca, pa, co)
|
||||
# Add operation to partition
|
||||
if nxt_nd["name"] != "barrier":
|
||||
# support_list.append(list(set(qa) | set(ca) | set(cob)))
|
||||
support_list.append(list(set(qa)))
|
||||
l_dict = {"graph": new_layer, "partition": support_list}
|
||||
layers_list.append(l_dict)
|
||||
return layers_list
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
from ._coupling import Coupling
|
||||
from ._layout import swap_mapper
|
||||
|
|
|
@ -9,7 +9,6 @@ onto a device with this coupling.
|
|||
Author: Andrew Cross
|
||||
"""
|
||||
import networkx as nx
|
||||
import numpy as np
|
||||
from ._couplingerror import CouplingError
|
||||
|
||||
|
||||
|
@ -32,6 +31,8 @@ class Coupling:
|
|||
"""
|
||||
# self.qubits is dict from qubit (regname,idx) tuples to node indices
|
||||
self.qubits = {}
|
||||
# self.index_to_qubit is a dict from node indices to qubits
|
||||
self.index_to_qubit = {}
|
||||
# self.node_counter is integer counter for labeling nodes
|
||||
self.node_counter = 0
|
||||
# self.G is the coupling digraph
|
||||
|
@ -39,9 +40,6 @@ class Coupling:
|
|||
# self.dist is a dict of dicts from node pairs to distances
|
||||
# it must be computed, it is the distance on the digraph
|
||||
self.dist = None
|
||||
# self.hdist is a dict of dicts from node pairs to distances
|
||||
# it must be computed, it is a heuristic distance function
|
||||
self.hdist = None
|
||||
# Add edges to the graph if the couplingstr is present
|
||||
if couplingstr is not None:
|
||||
edge_list = couplingstr.split(';')
|
||||
|
@ -52,6 +50,23 @@ class Coupling:
|
|||
vtuple0 = (vertex0[0], int(vertex0[1]))
|
||||
vtuple1 = (vertex1[0], int(vertex1[1]))
|
||||
self.add_edge(vtuple0, vtuple1)
|
||||
self.compute_distance()
|
||||
|
||||
def size(self):
|
||||
"""Return the number of qubits in this graph."""
|
||||
return len(self.qubits)
|
||||
|
||||
def get_qubits(self):
|
||||
"""Return the qubits in this graph as (qreg, index) tuples."""
|
||||
return list(self.qubits.keys())
|
||||
|
||||
def get_edges(self):
|
||||
"""Return a list of edges in the coupling graph.
|
||||
|
||||
Each edge is a pair of qubits and each qubit is a tuple (qreg, index).
|
||||
"""
|
||||
return list(map(lambda x: (self.index_to_qubit[x[0]],
|
||||
self.index_to_qubit[x[1]]), self.G.edges()))
|
||||
|
||||
def add_qubit(self, name):
|
||||
"""
|
||||
|
@ -66,6 +81,7 @@ class Coupling:
|
|||
self.G.add_node(self.node_counter)
|
||||
self.G.node[self.node_counter]["name"] = name
|
||||
self.qubits[name] = self.node_counter
|
||||
self.index_to_qubit[self.node_counter] = name
|
||||
|
||||
def add_edge(self, s_name, d_name):
|
||||
"""
|
||||
|
@ -75,15 +91,9 @@ class Coupling:
|
|||
d_name = destination qubit tuple
|
||||
"""
|
||||
if s_name not in self.qubits:
|
||||
self.node_counter += 1
|
||||
self.G.add_node(self.node_counter)
|
||||
self.G.node[self.node_counter]["name"] = s_name
|
||||
self.qubits[s_name] = self.node_counter
|
||||
self.add_qubit(s_name)
|
||||
if d_name not in self.qubits:
|
||||
self.node_counter += 1
|
||||
self.G.add_node(self.node_counter)
|
||||
self.G.node[self.node_counter]["name"] = d_name
|
||||
self.qubits[d_name] = self.node_counter
|
||||
self.add_qubit(d_name)
|
||||
self.G.add_edge(self.qubits[s_name], self.qubits[d_name])
|
||||
|
||||
def connected(self):
|
||||
|
@ -94,53 +104,38 @@ class Coupling:
|
|||
"""
|
||||
return nx.is_weakly_connected(self.G)
|
||||
|
||||
def compute_distance(self, randomize=False):
|
||||
def compute_distance(self):
|
||||
"""
|
||||
Compute the distance function on pairs of nodes.
|
||||
|
||||
The distance map self.dist is computed from the graph using
|
||||
all_pairs_shortest_path_length. The distance map self.hdist is also
|
||||
computed. If randomize is False, we use self.dist. Otherwise, we use
|
||||
Sergey Bravyi's randomization heuristic.
|
||||
all_pairs_shortest_path_length.
|
||||
"""
|
||||
if not self.connected():
|
||||
raise CouplingError("coupling graph not connected")
|
||||
lengths = nx.all_pairs_shortest_path_length(self.G.to_undirected())
|
||||
self.dist = {}
|
||||
self.hdist = {}
|
||||
for i in self.qubits.keys():
|
||||
self.dist[i] = {}
|
||||
self.hdist[i] = {}
|
||||
for j in self.qubits.keys():
|
||||
self.dist[i][j] = lengths[self.qubits[i]][self.qubits[j]]
|
||||
self.hdist[i][j] = self.dist[i][j]
|
||||
if randomize:
|
||||
for i in self.qubits.keys():
|
||||
for j in self.qubits.keys():
|
||||
scale = (1.0 + np.random.normal(0.0, 1.0/len(self.qubits)))
|
||||
self.hdist[i][j] = scale * self.dist[i][j]**2
|
||||
self.hdist[j][i] = self.hdist[i][j]
|
||||
|
||||
def distance(self, q1, q2, h=False):
|
||||
"""
|
||||
Return the distance between qubit q1 to qubit q2.
|
||||
|
||||
We look this up in self.dist if h is False and in self.hdist
|
||||
if h is True.
|
||||
"""
|
||||
def distance(self, q1, q2):
|
||||
"""Return the distance between qubit q1 to qubit q2."""
|
||||
if self.dist is None:
|
||||
raise CouplingError("distance has not been computed")
|
||||
if q1 not in self.qubits:
|
||||
raise CouplingError("%s not in coupling graph" % q1)
|
||||
if q2 not in self.qubits:
|
||||
raise CouplingError("%s not in coupling graph" % q2)
|
||||
if h:
|
||||
return self.hdist[q1][q2]
|
||||
else:
|
||||
return self.dist[q1][q2]
|
||||
return self.dist[q1][q2]
|
||||
|
||||
def __str__(self):
|
||||
"""Return a string representation of the coupling graph."""
|
||||
s = "%s" % self.qubits
|
||||
s += "\n%s" % self.G.edges()
|
||||
s = "qubits: "
|
||||
s += ", ".join(["%s[%d] @ %d" % (k[0], k[1], v)
|
||||
for k, v in self.qubits.items()])
|
||||
s += "\nedges: "
|
||||
s += ", ".join(["%s[%d]-%s[%d]" % (e[0][0], e[0][1], e[1][0], e[1][1])
|
||||
for e in self.get_edges()])
|
||||
return s
|
||||
|
|
|
@ -0,0 +1,248 @@
|
|||
"""
|
||||
Layout object to represent placement of data qubits onto physical qubits.
|
||||
|
||||
Author: Andrew Cross
|
||||
"""
|
||||
import sys
|
||||
import copy
|
||||
import numpy as np
|
||||
from qiskit import QISKitException
|
||||
from qiskit.qasm import Qasm
|
||||
import qiskit.unroll as unroll
|
||||
|
||||
|
||||
def layer_permutation(layer_partition, layout, qubit_subset, coupling, trials):
|
||||
"""Find a swap circuit that implements a permutation for this layer.
|
||||
|
||||
The goal is to swap qubits such that qubits in the same two qubit gates
|
||||
are adjacent.
|
||||
|
||||
The layer_partition is a list of (qu)bit lists and each qubit is a
|
||||
tuple (qreg, index).
|
||||
The layout is a dict mapping qubits in the circuit to qubits in the
|
||||
coupling graph and represents the current positions of the data.
|
||||
The qubit_subset is the subset of qubits in the coupling graph that
|
||||
we have chosen to map into.
|
||||
The coupling is a CouplingGraph.
|
||||
TRIALS is the number of attempts the randomized algorithm makes.
|
||||
|
||||
Returns: success_flag, best_circ, best_d, best_layout
|
||||
|
||||
If success_flag is True, then best_circ contains an OPENQASM string with
|
||||
the swap circuit, best_d contains the depth of the swap circuit, and
|
||||
best_layout contains the new positions of the data qubits after the
|
||||
swap circuit has been applied.
|
||||
"""
|
||||
rev_layout = {b: a for a, b in layout.items()}
|
||||
gates = []
|
||||
for layer in layer_partition:
|
||||
if len(layer) > 2:
|
||||
raise QISKitException("Layer contains >2 qubit gates")
|
||||
elif len(layer) == 2:
|
||||
gates.append(tuple(layer))
|
||||
|
||||
# Begin loop over trials of randomized algorithm
|
||||
n = coupling.size()
|
||||
best_d = sys.maxsize # initialize best depth
|
||||
best_circ = None # initialize best swap circuit
|
||||
best_layout = None # initialize best final layout
|
||||
for trial in range(trials):
|
||||
|
||||
trial_layout = copy.deepcopy(layout)
|
||||
rev_trial_layout = copy.deepcopy(rev_layout)
|
||||
trial_circ = ""
|
||||
|
||||
# Compute Sergey's randomized distance
|
||||
xi = {}
|
||||
for i in coupling.get_qubits():
|
||||
xi[i] = {}
|
||||
for i in coupling.get_qubits():
|
||||
for j in coupling.get_qubits():
|
||||
scale = 1.0 + np.random.normal(0.0, 1.0/n)
|
||||
xi[i][j] = scale * coupling.distance(i, j)**2
|
||||
xi[j][i] = xi[i][j]
|
||||
|
||||
# Loop over depths d up to a max depth of 2n+1
|
||||
for d in range(1, 2*n+1):
|
||||
circ = ""
|
||||
# Set of available qubits
|
||||
qubit_set = set(qubit_subset)
|
||||
# While there are still qubits available
|
||||
while qubit_set:
|
||||
# Compute the objective function
|
||||
min_cost = sum([xi[trial_layout[g[0]]][trial_layout[g[1]]]
|
||||
for g in gates])
|
||||
# Try to decrease objective function
|
||||
progress_made = False
|
||||
# Loop over edges of coupling graph
|
||||
for e in coupling.get_edges():
|
||||
# Are the qubits available?
|
||||
if e[0] in qubit_set and e[1] in qubit_set:
|
||||
# Try this edge to reduce the cost
|
||||
new_layout = copy.deepcopy(trial_layout)
|
||||
new_layout[rev_trial_layout[e[0]]] = e[1]
|
||||
new_layout[rev_trial_layout[e[1]]] = e[0]
|
||||
rev_new_layout = copy.deepcopy(rev_trial_layout)
|
||||
rev_new_layout[e[0]] = rev_trial_layout[e[1]]
|
||||
rev_new_layout[e[1]] = rev_trial_layout[e[0]]
|
||||
# Compute the objective function
|
||||
new_cost = sum([xi[new_layout[g[0]]][new_layout[g[1]]]
|
||||
for g in gates])
|
||||
# Record progress if we succceed
|
||||
if new_cost < min_cost:
|
||||
progress_made = True
|
||||
min_cost = new_cost
|
||||
opt_layout = new_layout
|
||||
rev_opt_layout = rev_new_layout
|
||||
opt_edge = e
|
||||
|
||||
# Were there any good choices?
|
||||
if progress_made:
|
||||
qubit_set.remove(opt_edge[0])
|
||||
qubit_set.remove(opt_edge[1])
|
||||
trial_layout = opt_layout
|
||||
rev_trial_layout = rev_opt_layout
|
||||
circ += "swap %s[%d],%s[%d]; " % (opt_edge[0][0],
|
||||
opt_edge[0][1],
|
||||
opt_edge[1][0],
|
||||
opt_edge[1][1])
|
||||
else:
|
||||
break
|
||||
|
||||
# We have either run out of qubits or failed to improve
|
||||
# Compute the coupling graph distance
|
||||
dist = sum([coupling.distance(trial_layout[g[0]],
|
||||
trial_layout[g[1]]) for g in gates])
|
||||
# If all gates can be applied now, we are finished
|
||||
# Otherwise we need to consider a deeper swap circuit
|
||||
if dist == len(gates):
|
||||
trial_circ = circ
|
||||
break
|
||||
|
||||
# Either we have succeeded at some depth d < dmax or failed
|
||||
dist = sum([coupling.distance(trial_layout[g[0]],
|
||||
trial_layout[g[1]]) for g in gates])
|
||||
if dist == len(gates):
|
||||
if d < best_d:
|
||||
best_circ = trial_circ
|
||||
best_layout = trial_layout
|
||||
best_d = min(best_d, d)
|
||||
|
||||
if best_circ is None:
|
||||
return False, None, None, None
|
||||
else:
|
||||
return True, best_circ, best_d, best_layout
|
||||
|
||||
|
||||
def swap_mapper(circuit_graph, coupling_graph,
|
||||
initial_layout=None,
|
||||
basis="cx,u1,u2,u3", verbose=False):
|
||||
"""Map a Circuit onto a CouplingGraph using swap gates.
|
||||
|
||||
circuit_graph = input Circuit
|
||||
coupling_graph = CouplingGraph to map onto
|
||||
initial_layout = dict from qubits of circuit_graph to qubits
|
||||
of coupling_graph (optional)
|
||||
basis = basis string specifying basis of output Circuit
|
||||
verbose = optional flag to print more information
|
||||
|
||||
Returns a Circuit object containing a circuit equivalent to
|
||||
circuit_graph that respects couplings in coupling_graph.
|
||||
"""
|
||||
if circuit_graph.width() > coupling_graph.size():
|
||||
raise QISKitException("Not enough qubits in CouplingGraph")
|
||||
|
||||
# Schedule the input circuit
|
||||
layerlist = circuit_graph.layers()
|
||||
if verbose:
|
||||
print("schedule:")
|
||||
for i in range(len(layerlist)):
|
||||
print(" %d: %s" % (i, layerlist[i]["partition"]))
|
||||
|
||||
# Check input layout and create default layout if necessary
|
||||
if initial_layout is not None:
|
||||
circ_qubits = circuit_graph.get_qubits()
|
||||
coup_qubits = coupling_graph.get_qubits()
|
||||
qubit_subset = []
|
||||
for k, v in initial_layout.values():
|
||||
qubit_subset.append(v)
|
||||
if k not in circ_qubits:
|
||||
raise QISKitException("initial_layout qubit %s[%d] not " +
|
||||
"in input Circuit" % (k[0], k[1]))
|
||||
if v not in coup_qubits:
|
||||
raise QISKitException("initial_layout qubit %s[%d] not " +
|
||||
" in input CouplingGraph" % (k[0], k[1]))
|
||||
else:
|
||||
# Supply a default layout
|
||||
qubit_subset = coupling_graph.get_qubits()
|
||||
qubit_subset = qubit_subset[0:circuit_graph.width()]
|
||||
initial_layout = {a: b for a, b in
|
||||
zip(circuit_graph.get_qubits(), qubit_subset)}
|
||||
|
||||
# Find swap circuit to preceed to each layer of input circuit
|
||||
layout = copy.deepcopy(initial_layout)
|
||||
openqasm_output = ""
|
||||
first_layer = True
|
||||
for i in range(len(layerlist)):
|
||||
success_flag, best_circ, best_d, best_layout \
|
||||
= layer_permutation(layerlist[i]["partition"], layout,
|
||||
qubit_subset, coupling_graph, 20)
|
||||
if not success_flag:
|
||||
if verbose:
|
||||
print("swap_mapper: failed, layer %d, " % i,
|
||||
" contention? retrying sequentially")
|
||||
serial_layerlist = layerlist[i]["graph"].serial_layers()
|
||||
for j in range(len(serial_layerlist)):
|
||||
success_flag, best_circ, best_d, best_layout \
|
||||
= layer_permutation(serial_layerlist[j]["partition"],
|
||||
layout, qubit_subset, coupling_graph, 20)
|
||||
if not success_flag:
|
||||
raise QISKitException("swap_mapper failed: " +
|
||||
"layer %d, sublayer %d" % (i, j) +
|
||||
", \"%s\"" %
|
||||
serial_layerlist[j]["graph"].qasm(
|
||||
no_decls=True,
|
||||
aliases=layout))
|
||||
else:
|
||||
layout = best_layout
|
||||
if first_layer:
|
||||
initial_layout = layout
|
||||
openqasm_output += circuit_graph.qasm(add_swap=True,
|
||||
decls_only=True,
|
||||
aliases=layout)
|
||||
openqasm_output += serial_layerlist[j]["graph"].qasm(
|
||||
no_decls=True,
|
||||
aliases=layout)
|
||||
first_layer = False
|
||||
else:
|
||||
if verbose:
|
||||
print("swap_mapper: layer %d (%d), depth %d"
|
||||
% (i, j, best_d))
|
||||
if best_circ != "":
|
||||
openqasm_output += best_circ
|
||||
openqasm_output += serial_layerlist[j]["graph"].qasm(
|
||||
no_decls=True,
|
||||
aliases=layout)
|
||||
else:
|
||||
layout = best_layout
|
||||
if first_layer:
|
||||
initial_layout = layout
|
||||
openqasm_output += circuit_graph.qasm(add_swap=True,
|
||||
decls_only=True,
|
||||
aliases=layout)
|
||||
openqasm_output += layerlist[i]["graph"].qasm(no_decls=True,
|
||||
aliases=layout)
|
||||
first_layer = False
|
||||
else:
|
||||
if verbose:
|
||||
print("swap_mapper: layer %s, depth %d" % (i, best_d))
|
||||
if best_circ != "":
|
||||
openqasm_output += best_circ
|
||||
openqasm_output += layerlist[i]["graph"].qasm(no_decls=True,
|
||||
aliases=layout)
|
||||
# Parse openqasm_output into Circuit object
|
||||
basis += ",swap"
|
||||
ast = Qasm(data=openqasm_output).parse()
|
||||
u = unroll.Unroller(ast, unroll.CircuitBackend(basis.split(",")))
|
||||
u.execute()
|
||||
return u.be.C
|
|
@ -694,7 +694,7 @@ class QasmParser(object):
|
|||
'''
|
||||
opaque : OPAQUE id gate_scope bit_list
|
||||
'''
|
||||
p[0] = Opaque([p[2], p[4]])
|
||||
p[0] = node.Opaque([p[2], p[4]])
|
||||
if p[2].name in self.external_functions:
|
||||
raise QasmException("OPAQUE names cannot be reserved words. "
|
||||
+ "Received '" + p[2].name + "'")
|
||||
|
|
|
@ -37,6 +37,7 @@ def unmajority(p, a, b, c):
|
|||
# something like p = Program(c1, c2, c3)
|
||||
# circ.QuantumRegister("a", n)
|
||||
|
||||
|
||||
n = 8
|
||||
|
||||
a = QuantumRegister("a", n)
|
||||
|
@ -83,7 +84,7 @@ print("width = %d" % C.width())
|
|||
print("bits = %d" % C.num_cbits())
|
||||
print("factors = %d" % C.num_tensor_factors())
|
||||
|
||||
# print("")
|
||||
# print("Unrolled OPENQASM")
|
||||
# print("-----------------------")
|
||||
# print(C.qasm(qeflag=True))
|
||||
print("")
|
||||
print("Unrolled OPENQASM")
|
||||
print("-----------------------")
|
||||
print(C.qasm(qeflag=True))
|
||||
|
|
|
@ -49,29 +49,8 @@ c = make_unrolled_circuit(sys.argv[1], basis)
|
|||
|
||||
# Second, create the coupling graph
|
||||
coupling = localize.Coupling(couplingstr)
|
||||
print("coupling = \n%s" % coupling)
|
||||
|
||||
print("CouplingGraph is = \n%s" % coupling)
|
||||
|
||||
if not coupling.connected():
|
||||
print("Coupling graph must be connected")
|
||||
sys.exit(1)
|
||||
|
||||
print("input circuit is = \n%s" % c.qasm())
|
||||
print("circuit depth = %d" % c.depth())
|
||||
|
||||
# Here down is hacking for now; not done
|
||||
|
||||
coupling.compute_distance()
|
||||
for q1 in coupling.qubits.keys():
|
||||
for q2 in coupling.qubits.keys():
|
||||
print("%s[%d] -> %s[%d]: %f" % (q1[0], q1[1], q2[0], q2[1],
|
||||
coupling.distance(q1, q2)))
|
||||
|
||||
layerlist = c.layers()
|
||||
print("len(layerlist) = %d" % len(layerlist))
|
||||
print("partition:")
|
||||
for i in range(len(layerlist)):
|
||||
print(" %d: %s" % (i, layerlist[i]["partition"]))
|
||||
for i in range(len(layerlist)):
|
||||
print("------------ layer %d ------------" % i)
|
||||
print("%s" % layerlist[i]["graph"].qasm())
|
||||
# Third, do the mapping
|
||||
c_prime = localize.swap_mapper(c, coupling)
|
||||
print("c_prime.qasm() = \n%s" % c_prime.qasm(qeflag=True))
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
OPENQASM 2.0;
|
||||
include "qelib1.inc";
|
||||
qreg q[5];
|
||||
creg c[5];
|
||||
cx q[0],q[4];
|
||||
cx q[1],q[2];
|
||||
cx q[2],q[3];
|
||||
cx q[3],q[4];
|
||||
ccx q[0],q[1],q[2];
|
||||
measure q -> c;
|
||||
if(c==1) reset q;
|
Loading…
Reference in New Issue