6 from squander.partitioning.tools
import build_dependency
10 Get starting gates for each qubit in the circuit 14 g: Forward dependency graph 16 rg: Reverse dependency graph 18 gate_to_qubit: Mapping of gates to qubits 20 S: Set of initial gates 24 Dictionary mapping qubits to their starting gates 26 g = { x: set(y)
for x, y
in g.items() }
27 rg = { x: set(y)
for x, y
in rg.items() }
38 for qubit
in gate_to_qubit[n]:
39 if not qubit
in start_qubit: start_qubit[qubit] = {}
40 start_qubit[qubit][n] =
None 45 Partition a circuit using TDAG algorithm 49 c: SQUANDER Circuit to partition 51 max_qubit: Max qubits per partition 53 use_gtqcp: Use GTQCP variant 57 Partitioned circuit, parameter order (source_idx, dest_idx, param_count), partition assignments 63 groups, deps =
_enumerate_groups(g, rg, gate_to_qubit, start_qubit, max_qubit, use_gtqcp)
65 assert sum(len(x)
for x
in L) == len(gate_dict), (sum(len(x)
for x
in L), len(gate_dict))
66 from squander.partitioning.kahn
import kahn_partition_preparts
72 Get gate dependencies for each gate within qubit constraints 76 g: Forward dependency graph 78 rg: Reverse dependency graph 80 gate_to_qubit: Mapping of gates to qubits 82 S: Starting gates per qubit 84 max_qubit: Max qubits per partition 88 Dictionary of gate dependencies 90 deps, visited = {}, set()
91 level, next_level = set(), set()
93 level.add(next(iter(S[qubit])))
95 for curr_gate
in level:
96 if not rg[curr_gate] <= visited:
continue 97 deps[curr_gate] = set.union(gate_to_qubit[curr_gate], *(deps[gate]
for gate
in rg[curr_gate]))
98 if len(deps[curr_gate]) <= max_qubit:
99 next_level |= g[curr_gate]
100 visited.add(curr_gate)
101 else: del deps[curr_gate]
102 level, next_level = next_level, level
109 Enumerate possible gate groups for partitioning 113 g: Forward dependency graph 115 rg: Reverse dependency graph 117 gate_to_qubit: Mapping of gates to qubits 119 S: Starting gates per qubit 121 max_qubit: Max qubits per partition 123 use_gtqcp: Use GTQCP variant 127 Set of gate groups and their dependencies 131 func = _enumerate
if not use_gtqcp
else _enumerate_gtqcp
133 result |= func(next(iter(S[qubit])), qubit, {qubit}
if use_gtqcp
else {frozenset({qubit})}, deps, g, gate_to_qubit, S, max_qubit)
137 def _enumerate(target_gate, target_qubit, input_groups, deps, g, gate_to_qubit, S, max_qubit):
139 Recursively enumerate gate groups for a target gate 143 target_gate: Gate to start enumeration 145 target_qubit: Qubit index 147 input_groups: Current qubit groups 149 deps: Gate dependencies 151 g: Forward dependency graph 153 gate_to_qubit: Mapping of gates to qubits 155 S: Starting gates per qubit 157 max_qubit: Max qubits per partition 161 Set of valid qubit groups 163 output, result = set(), set()
164 if not target_gate
in deps:
return result
165 for group
in input_groups:
166 qubits = frozenset.union(group, deps[target_gate])
167 if len(qubits) <= max_qubit:
169 if not deps[target_gate] <= group:
172 while any(len(group) < max_qubit
for group
in output):
173 target_gate = next(iter(x
for x
in g[target_gate]
if target_qubit
in gate_to_qubit[x]),
None)
174 if target_gate
is None:
176 input_groups = output
177 next_qubit = next(iter(gate_to_qubit[target_gate] - {target_qubit}),
None)
178 if next_qubit
is None:
180 output =
_enumerate(target_gate, next_qubit, input_groups, deps, g, gate_to_qubit, S, max_qubit)
185 def _enumerate_gtqcp(target_gate, target_qubit, input_groups, deps, g, gate_to_qubit, S, max_qubit):
187 Recursively enumerate gate groups for a target gate using GTQCP variant. 191 target_gate: Gate to start enumeration 193 target_qubit: Qubit index 195 input_groups: Current qubit groups 197 deps: Gate dependencies 199 g: Forward dependency graph 201 gate_to_qubit: Mapping of gates to qubits 203 S: Starting gates per qubit 205 max_qubit: Max qubits per partition 209 Set of valid qubit groups 212 if not target_gate
in deps:
return result
215 next_gate = next(iter(x
for x
in g[gate]
if target_qubit
in gate_to_qubit[x]),
None)
216 if next_gate
is None or next_gate
not in deps
or len(input_groups | deps[next_gate]) > max_qubit:
219 group = frozenset(input_groups) | frozenset(deps[gate])
220 if len(group) > max_qubit:
222 if group
not in result:
224 if len(group) < max_qubit:
225 for qubit
in group - input_groups:
228 gate = next(iter(S[qubit]))
229 _enumerate_gtqcp(gate, qubit, input_groups | frozenset({qubit}), deps, g, gate_to_qubit, S, max_qubit)
235 Remove the best partition from the dependency graphs 239 qubit_results: Candidate qubit groups 241 g: Forward dependency graph 243 rg: Reverse dependency graph 245 gate_to_qubit: Mapping of gates to qubits 247 start_qubit: Starting gates per qubit 249 deps: Gate dependencies 253 List of gate indices in the best partition 255 gate_info = [[x
for x, y
in deps.items()
if y <= result]
for result
in qubit_results]
256 best_part = max(gate_info, key=
lambda x: len(x))
257 for gate
in best_part:
258 for child
in g[gate]:
259 rg[child].remove(gate)
260 for child
in rg[gate]:
261 g[child].remove(gate)
262 for gate
in best_part:
265 for qubit
in gate_to_qubit[gate]:
266 del start_qubit[qubit][gate]
267 if len(start_qubit[qubit]) == 0: del start_qubit[qubit]
273 Test TDAG partitioning on a QASM file 277 use_gtqcp: Use GTQCP variant 281 filename =
"benchmarks/partitioning/test_circuit/9symml_195.qasm" 282 from squander
import utils
284 circ, parameters = utils.qasm_to_squander_circuit(filename)
292 Test TDAG partitioning on a simple two-qubit circuit 315 Test GTQCP variant for TDAG partitioning 327 expected = {0: {0, 1}, 1: {2, 3}, 2: {2, 3, 4}, 3: {2, 3, 4}, 4: {2, 3, 4, 5}, 5: {0, 1, 2, 3, 4}}
329 expected_groups = {frozenset({2, 3, 4}), frozenset({0, 1, 2, 3, 4}), frozenset({0, 1}), frozenset({2, 3, 4, 5})}
337 assert result == expected, (result, expected)
341 assert result_groups == expected_groups, (result_groups, expected_groups)
350 Test TDAG partitioning on a sample circuit 364 expected = {0: {3, 4}, 1: {2, 3, 4}, 2: {3, 4, 5}, 3: {1, 2, 3, 4}, 4: {3, 4, 5, 6}, 5: {0, 2, 3, 4}, 6: {3, 4, 5, 7}}
366 expected_groups = {frozenset({3, 4}), frozenset({3, 4, 5, 6}), frozenset({2, 3, 4}), frozenset({0, 2, 3, 4}), frozenset({1, 2, 3, 4}), frozenset({3, 4, 5}), frozenset({3, 4, 5, 7})}
374 assert result == expected, (result, expected)
376 result_groups, _ =
_enumerate_groups(g, rg, gate_to_qubit, start_qubit, K,
False)
378 assert result_groups == expected_groups, (result_groups, expected_groups)
386 if __name__ ==
"__main__":
def _test_tdag_qasm(use_gtqcp=False)
def kahn_partition_preparts(c, max_qubit, preparts)
def _remove_best_partition(qubit_results, g, rg, gate_to_qubit, start_qubit, deps)
def _enumerate_gtqcp(target_gate, target_qubit, input_groups, deps, g, gate_to_qubit, S, max_qubit)
def _get_starting_gates(g, rg, gate_to_qubit, S)
def tdag_max_partitions(c, max_qubit, use_gtqcp=False)
def _test_tdag_single_qubit()
def _get_gate_dependencies(g, rg, gate_to_qubit, S, max_qubit)
def _enumerate(target_gate, target_qubit, input_groups, deps, g, gate_to_qubit, S, max_qubit)
def _enumerate_groups(g, rg, gate_to_qubit, S, max_qubit, use_gtqcp)