Improving CouplingGraph.get_subgraphs_of_size#358
Improving CouplingGraph.get_subgraphs_of_size#358jclark-ce wants to merge 5 commits intoBQSKit:mainfrom
CouplingGraph.get_subgraphs_of_size#358Conversation
|
Thanks for the PR! Awesome catch and good use of memoization. I am happy with the way this PR looks currently, but if this is a performance-critical method for your application, it may be better to go with a proper ESU algorithm: ...
if size <= 0:
raise ValueError(f'Nonpositive size; got {size}.')
locations: set[CircuitLocation] = set()
for qudit in range(self.num_qudits):
subgraph = {qudit}
extension = {q for q in self._adj[qudit] if q > qudit}
self._extend_subgraph(size, subgraph, extension, qudit, locations)
return list(locations)
def _extend_subgraph(self, size: int, subgraph: set[int], extension: set[int], vertex: int, subgraphs: set[CircuitLocation]):
if len(subgraph) == size:
subgraphs.add(CircuitLocation(list(subgraph)))
return
while len(extension) > 0:
w = extension.pop()
new_extension = set(extension)
for neighbor in self._adj[w]:
if neighbor > vertex and neighbor not in subgraph and neighbor not in extension:
if all(neighbor_neighbor not in subgraph or neighbor_neighbor == w for neighbor_neighbor in self._adj[neighbor]):
new_extension.add(neighbor)
subgraph.add(w)
self._extend_subgraph(size, subgraph, new_extension, vertex, subgraphs)
subgraph.remove(w)There's probably a better way to write that, too, but it gives an additional factor of ~2 speedup and is more memory efficient. Another factor of ~2 speedup can be had from skipping the CircuitLocation type safety checks. For this, we would guard all the type checks in
I am all for this. If you want to add |
|
You make a good point regarding the ESU algorithm. I wrote the PR the way I did because it was relatively simple and made sense for ScanPartitioner. Perhaps a good compromise would be to use ESU with a flag like Secondarily, regarding skipping the type-safety checks, if I have spare time for it, would you be interested in another PR to implement that behavior on some of the |
👍
Thank you for the thought, but honestly, unless it's an immediate performance concern for you, I wouldn't recommend this. I would still accept the PR, but when OpenQudit gets integrated into BQSKit with a 2.0 release, |
…nd_subgraphs flag
|
Okay, I updated Regarding contributing TDAG to BQSKit, I may be interested in doing that, I'll send you an email in the next few days to discuss it. |
Hello,
I was using ScanPartitioner to partition some circuits, and I noticed that it performs very slowly with increasing block size even on circuits with linear connectivity. After testing$k$ to produce the $\sim{n}$ groups of size $k$ in a linear coupling graph.
CouplingGraph'sget_subgraphs_of_size, I discovered that it takes a time exponentialI have therefore written a small optimization which takes advantage of the fact that if the current group has been encountered before, no new groups can be found by continuing to search. The change reduces the time to find all groups of size$8$ on a 10x10 grid coupling graph from $\sim{88}$ seconds down to $\sim{2}$ seconds.
The changed version saves all qubit groups to$\leq{k}$ are saved, although in most cases this is not an appreciable increase (since the number of groups with size $<k$ is usually less than the number of group with size $=k$ ).
locationsasfrozensetand only recurs if the new group has not been encountered before. Groups with less thansizequbits are dropped before returning fromget_subgraphs_of_size. The change does slightly increase the memory complexity of the method since all groups of sizeBecause groups with size$<k$ are produced for free with this method, it might also be sensible to add an interface which allows them to be left in for use cases like ScanPartitioner which need them anyway.