-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Implements the LineInititialMapper strategy #5831
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
CirqBot
merged 72 commits into
quantumlib:master
from
ammareltigani:routing-line_initial_mapper
Aug 27, 2022
Merged
Changes from 70 commits
Commits
Show all changes
72 commits
Select commit
Hold shift + click to select a range
62100c1
added abstract initial mapper and identity initial mapper
ammareltigani 9df051b
added __str__ and __repr__ for MappingManager
ammareltigani 8610ae2
minor bug
ammareltigani d9a7a3c
made MappingManager not serializable
ammareltigani c774674
removed unused import
ammareltigani a06c240
merging with mapping-manager repr and str PR #5828
ammareltigani c12cc03
pushed AbstractInitialMapping and IdentityInitialMapping name to 'cir…
ammareltigani 83e976f
minor lint fix
ammareltigani 24acc3d
addressed comments
ammareltigani 2e1cec7
Merge branch 'add-str-and-repr-to-mapping_manager' into routing-initi…
ammareltigani 79061de
addressed comments
ammareltigani dfe80a9
fixed bug with edges not being sorted for graph equality testing
ammareltigani cb798dc
Merge branch 'add-str-and-repr-to-mapping_manager' into routing-initi…
ammareltigani 6ee60a9
fixed bug with digraphs repr method in MappingManager and added test …
ammareltigani 0c6de8b
Merge branch 'add-str-and-repr-to-mapping_manager' into routing-initi…
ammareltigani ecadfdb
addressed some comments
ammareltigani 7819263
added grid testing device
ammareltigani 5dd4577
added grid routing testing device
ammareltigani cc79064
formatting
ammareltigani fecc4b0
added line_initial_mapper and some tests; needs more testing
ammareltigani b3a1445
merged with device setup
ammareltigani 9bcf54f
formatting
ammareltigani f4dae72
formatting
ammareltigani 9ecb52a
changed interface for LineInitialMapper and added better tests; test …
ammareltigani 719b284
addressed comments and added ring device
ammareltigani 8cbbe8f
Merge branch 'routing-initial_mapping_device_setup' into routing-line…
ammareltigani 735cba8
added test for supportin directed graphs
ammareltigani 9ba9cef
changed interface for AbstractInitialMapper
ammareltigani 211bb2d
Merge branch 'routing-initial_mapping_setup' into routing-line_initia…
ammareltigani 82cdbc0
formatting
ammareltigani 632dfe6
Merge branch 'master' into routing-initial_mapping_device_setup
ammareltigani fca9052
changed RoutingTestingDevice interface; need to change is_isomorphic …
ammareltigani 34ef897
added hard-coded isomorphism tests
ammareltigani 67546d8
fixed type issue
ammareltigani e120be2
Merge branch 'master' into routing-line_initial_mapper
ammareltigani 0d2345c
removed redundant imports
ammareltigani 209cc35
Merge branch 'routing-initial_mapping_device_setup' into routing-line…
ammareltigani 2544069
merged with routing testing device PR #5830
ammareltigani 4493826
simplified _value_equalit_values_
ammareltigani 8827b91
addressed comments
ammareltigani 70de81e
Merge branch 'routing-initial_mapping_device_setup' into routing-line…
ammareltigani 60ed0ac
removed unused import
ammareltigani eefc089
Merge branch 'routing-initial_mapping_device_setup' into routing-line…
ammareltigani c7506bd
fixed nits
ammareltigani d92f515
Merge branch 'routing-initial_mapping_device_setup' into routing-line…
ammareltigani d41cedb
Merge branch 'master' into routing-line_initial_mapper
tanujkhattar 7509aa9
addressed comments
ammareltigani c21eae2
formatting
ammareltigani 854e867
Merge branch 'routing-line_initial_mapper' of https://github.com/amma…
ammareltigani bad20d3
small fixes
ammareltigani 26f14bd
removed unused import
ammareltigani 4170236
modified test file
ammareltigani 6bed99a
debugging
ammareltigani 5f4b848
removed print statements
ammareltigani e1de30a
debugging statement
ammareltigani 3237bd7
debugging statement
ammareltigani 64f6ba8
fix
ammareltigani a2d8d2d
fix
ammareltigani 1b8c315
print statement
ammareltigani 06ab64e
edges sorting
ammareltigani 80b1f0e
addressed comments; ready for review
ammareltigani 0f1454d
fixed type bug
ammareltigani 7c4ebbd
cleanup
ammareltigani 9835eb1
ready for review
ammareltigani c72de13
type and lint fixes
ammareltigani 2ecb278
slightly modified _make_circuit_graph()
ammareltigani 54e96ca
Merge branch 'master' into routing-line_initial_mapper
tanujkhattar 6989acc
added test for testing valid circuits and fixed bug in _make_circuit_…
ammareltigani 6c8a661
Merge branch 'routing-line_initial_mapper' of https://github.com/amma…
ammareltigani b6c4bb3
Merge branch 'master' into routing-line_initial_mapper
ammareltigani cfe8704
fixed nits
ammareltigani c69a0db
Merge branch 'routing-line_initial_mapper' of https://github.com/amma…
ammareltigani File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
223 changes: 223 additions & 0 deletions
223
cirq-core/cirq/transformers/routing/line_initial_mapper.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,223 @@ | ||
| # Copyright 2022 The Cirq Developers | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # https://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| """Maps logical to physical qubits by greedily placing lines of logical qubits on the device. | ||
|
|
||
| This is the default placement strategy used in the CQC router. | ||
|
|
||
| It first creates a partial connectivity graph between logical qubits in the given circuit and then | ||
| maps these logical qubits on physical qubits on the device by starting at the center of the device | ||
| and greedily choosing the highest degree neighbor. | ||
|
|
||
| If some logical qubits are unampped after this first procedure then there are two cases: | ||
| (1) These unmammep logical qubits do interact in the circuit with some other logical partner. | ||
| In this case we map such a qubit to the nearest available physical qubit on the device to the | ||
| one that its partner was mapped to. | ||
|
|
||
| (2) These unampped logical qubits only have single qubit operations on them (i.e they do not | ||
| interact with any other logical qubit at any point in the circuit). In this case we map them to | ||
| the nearest available neighbor to the center of the device. | ||
| """ | ||
|
|
||
| from typing import Deque, Dict, List, Set, Tuple, TYPE_CHECKING | ||
| from collections import deque | ||
| import networkx as nx | ||
|
|
||
| from cirq.transformers.routing import initial_mapper | ||
| from cirq import protocols, value | ||
|
|
||
| if TYPE_CHECKING: | ||
| import cirq | ||
|
|
||
|
|
||
| @value.value_equality | ||
| class LineInitialMapper(initial_mapper.AbstractInitialMapper): | ||
| """Places logical qubits in the circuit onto physical qubits on the device. | ||
|
|
||
| Starting from the center physical qubit on the device, attempts to map disjoint lines of | ||
| logical qubits given by the circuit graph onto one long line of physical qubits on the | ||
| device, greedily maximizing each physical qubit's degree. | ||
| If this mapping cannot be completed as one long line of qubits in the circuit graph mapped | ||
| to qubits in the device graph, the line can be split as several line segments and then we: | ||
| (i) Map first line segment. | ||
| (ii) Find another high degree vertex in G near the center. | ||
| (iii) Map the second line segment | ||
| (iv) etc. | ||
| A line is split by mapping the next logical qubit to the nearest available physical qubit | ||
| to the center of the device graph. | ||
|
|
||
| The expected runtime of this strategy is O(m logn + n^2) where m is the # of operations in the | ||
| given circuit and n is the number of qubits. The first term corresponds to the runtime of | ||
| 'make_circuit_graph()' and the second for 'initial_mapping()'. | ||
| """ | ||
|
|
||
| def __init__(self, device_graph: nx.Graph) -> None: | ||
| """Initializes a LineInitialMapper. | ||
|
|
||
| Args: | ||
| device_graph: device graph | ||
| """ | ||
| if nx.is_directed(device_graph): | ||
| self.device_graph = nx.DiGraph() | ||
| self.device_graph.add_nodes_from(sorted(list(device_graph.nodes(data=True)))) | ||
| self.device_graph.add_edges_from(sorted(list(device_graph.edges))) | ||
| else: | ||
| self.device_graph = nx.Graph() | ||
| self.device_graph.add_nodes_from(sorted(list(device_graph.nodes(data=True)))) | ||
| self.device_graph.add_edges_from( | ||
| sorted(list(sorted(edge) for edge in device_graph.edges)) | ||
| ) | ||
| self.center = nx.center(self.device_graph)[0] | ||
|
|
||
| def _make_circuit_graph( | ||
| self, circuit: 'cirq.AbstractCircuit' | ||
| ) -> Tuple[List[Deque['cirq.Qid']], Dict['cirq.Qid', 'cirq.Qid']]: | ||
| """Creates a (potentially incomplete) qubit connectivity graph of the circuit. | ||
|
|
||
| Iterates over moments in the circuit from left to right and adds edges between logical | ||
| qubits if the logical qubit pair l1 and l2 | ||
| (1) have degree < 2, | ||
| (2) are involved in a 2-qubit operation in the current moment, and | ||
| (3) adding such an edge will not produce a cycle in the graph. | ||
|
|
||
| Args: | ||
| circuit: the input circuit with logical qubits | ||
|
|
||
| Returns: | ||
| The (potentially incomplete) qubit connectivity graph of the circuit, which is | ||
| guaranteed to be a forest of line graphs. | ||
| """ | ||
| circuit_graph: List[Deque['cirq.Qid']] = [deque([q]) for q in sorted(circuit.all_qubits())] | ||
| component_id: Dict['cirq.Qid', int] = {q[0]: i for i, q in enumerate(circuit_graph)} | ||
| partners: Dict['cirq.Qid', 'cirq.Qid'] = {} | ||
|
|
||
| def degree_lt_two(q: 'cirq.Qid'): | ||
| return any(circuit_graph[component_id[q]][i] == q for i in [-1, 0]) | ||
|
|
||
| for op in circuit.all_operations(): | ||
| if protocols.num_qubits(op) != 2: | ||
| continue | ||
|
|
||
| q0, q1 = op.qubits | ||
| c0, c1 = component_id[q0], component_id[q1] | ||
| # Keep track of partners for mapping isolated qubits later. | ||
| partners[q0] = partners[q0] if q0 in partners else q1 | ||
| partners[q1] = partners[q1] if q1 in partners else q0 | ||
|
|
||
| if not (degree_lt_two(q0) and degree_lt_two(q1) and c0 != c1): | ||
| continue | ||
|
|
||
| # Make sure c0/q0 are for the largest component. | ||
| if len(circuit_graph[c0]) < len(circuit_graph[c1]): | ||
| c0, c1, q0, q1 = c1, c0, q1, q0 | ||
|
|
||
| # copy smaller component into larger one. | ||
| c1_order = ( | ||
| reversed(circuit_graph[c1]) | ||
| if circuit_graph[c1][-1] == q1 | ||
| else iter(circuit_graph[c1]) | ||
| ) | ||
| for q in c1_order: | ||
| if circuit_graph[c0][0] == q0: | ||
| circuit_graph[c0].appendleft(q) | ||
| else: | ||
| circuit_graph[c0].append(q) | ||
| component_id[q] = c0 | ||
|
|
||
| graph = sorted( | ||
| [circuit_graph[c] for c in set(component_id.values())], key=len, reverse=True | ||
| ) | ||
| return graph, partners | ||
|
|
||
| def initial_mapping(self, circuit: 'cirq.AbstractCircuit') -> Dict['cirq.Qid', 'cirq.Qid']: | ||
| """Maps disjoint lines of logical qubits onto lines of physical qubits. | ||
|
|
||
| Args: | ||
| circuit: the input circuit with logical qubits | ||
|
|
||
| Returns: | ||
| a dictionary that maps logical qubits in the circuit (keys) to physical qubits on the | ||
| device (values). | ||
| """ | ||
| mapped_physicals: Set['cirq.Qid'] = set() | ||
| qubit_map: Dict['cirq.Qid', 'cirq.Qid'] = {} | ||
| circuit_graph, partners = self._make_circuit_graph(circuit) | ||
|
|
||
| def next_physical( | ||
| current_physical: 'cirq.Qid', partner: 'cirq.Qid', isolated: bool = False | ||
| ) -> 'cirq.Qid': | ||
| # Greedily map to highest degree neighbor that is available | ||
|
ammareltigani marked this conversation as resolved.
|
||
| if not isolated: | ||
| sorted_neighbors = sorted( | ||
| self.device_graph.neighbors(current_physical), | ||
| key=lambda x: self.device_graph.degree(x), | ||
| reverse=True, | ||
| ) | ||
| for neighbor in sorted_neighbors: | ||
| if neighbor not in mapped_physicals: | ||
| return neighbor | ||
| # If cannot map onto one long line of physical qubits, then break down into multiple | ||
| # small lines by finding nearest available qubit to the physical center | ||
| return self._closest_unmapped_qubit(partner, mapped_physicals) | ||
|
|
||
| pq = self.center | ||
| for i, logical_line in enumerate(circuit_graph): | ||
| for j, lq in enumerate(logical_line): | ||
| mapped_physicals.add(pq) | ||
| qubit_map[lq] = pq | ||
|
|
||
| if j < len(logical_line) - 1: | ||
| pq = next_physical(pq, self.center) | ||
|
|
||
| # Edge case: if mapping n qubits on an n-qubit device should not call next_physical | ||
| # when finished mapping the last logical qubit else will raise an error. | ||
| elif i < len(circuit_graph) - 1: | ||
| if len(circuit_graph[i + 1]) > 1: | ||
| pq = next_physical(pq, self.center) | ||
| else: | ||
| partner = qubit_map[partners[lq]] if lq in partners else self.center | ||
| pq = next_physical(pq, partner, isolated=True) | ||
|
ammareltigani marked this conversation as resolved.
Outdated
|
||
|
|
||
| return qubit_map | ||
|
tanujkhattar marked this conversation as resolved.
|
||
|
|
||
| def _closest_unmapped_qubit( | ||
| self, source: 'cirq.Qid', mapped_physicals: Set['cirq.Qid'] | ||
| ) -> 'cirq.Qid': | ||
| """Finds the closest available neighbor to a physical qubit 'source' on the device. | ||
|
|
||
| Args: | ||
| source: a physical qubit on the device. | ||
|
|
||
| Returns: | ||
| the closest available physical qubit to 'source'. | ||
|
|
||
| Raises: | ||
| ValueError: if there are no available qubits left on the device. | ||
| """ | ||
| for _, successors in nx.bfs_successors(self.device_graph, source): | ||
| for successor in successors: | ||
| if successor not in mapped_physicals: | ||
| return successor | ||
| raise ValueError("No available physical qubits left on the device.") | ||
|
|
||
| def _value_equality_values_(self): | ||
| return ( | ||
| tuple(self.device_graph.nodes), | ||
| tuple(self.device_graph.edges), | ||
| nx.is_directed(self.device_graph), | ||
| ) | ||
|
|
||
| def __repr__(self): | ||
| graph_type = type(self.device_graph).__name__ | ||
| return f'cirq.LineInitialMapper(nx.{graph_type}({dict(self.device_graph.adjacency())}))' | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.