Skip to content

Commit d221ac8

Browse files
committed
Fix some more bugs and linting issues and increase test coverage
1 parent c92854c commit d221ac8

File tree

4 files changed

+347
-247
lines changed

4 files changed

+347
-247
lines changed

projectq/cengines/_graphmapper.py

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -149,15 +149,15 @@ def _add_qubits_to_mapping_smart_init(current_mapping, graph,
149149
150150
Returns: A new mapping
151151
"""
152-
qubit_interaction_subgraphs = \
153-
commands_dag.calculate_qubit_interaction_subgraphs(max_order=2)
152+
if not current_mapping:
153+
qubit_interaction_subgraphs = \
154+
commands_dag.calculate_qubit_interaction_subgraphs(max_order=2)
154155

155-
# Interaction subgraph list can be empty if only single qubit gates are
156-
# present
157-
if not qubit_interaction_subgraphs:
158-
qubit_interaction_subgraphs = [list(new_logical_qubit_ids)]
156+
# Interaction subgraph list can be empty if only single qubit gates are
157+
# present
158+
if not qubit_interaction_subgraphs:
159+
qubit_interaction_subgraphs = [list(new_logical_qubit_ids)]
159160

160-
if not current_mapping:
161161
return _generate_mapping_minimize_swaps(graph,
162162
qubit_interaction_subgraphs)
163163
return _add_qubits_to_mapping_fcfs(current_mapping, graph,
@@ -185,15 +185,15 @@ def _add_qubits_to_mapping(current_mapping, graph, new_logical_qubit_ids,
185185
186186
Returns: A new mapping
187187
"""
188-
qubit_interaction_subgraphs = \
189-
commands_dag.calculate_qubit_interaction_subgraphs(max_order=2)
188+
if not current_mapping:
189+
qubit_interaction_subgraphs = \
190+
commands_dag.calculate_qubit_interaction_subgraphs(max_order=2)
190191

191-
# Interaction subgraph list can be empty if only single qubit gates are
192-
# present
193-
if not qubit_interaction_subgraphs:
194-
qubit_interaction_subgraphs = [list(new_logical_qubit_ids)]
192+
# Interaction subgraph list can be empty if only single qubit gates are
193+
# present
194+
if not qubit_interaction_subgraphs:
195+
qubit_interaction_subgraphs = [list(new_logical_qubit_ids)]
195196

196-
if not current_mapping:
197197
return _generate_mapping_minimize_swaps(graph,
198198
qubit_interaction_subgraphs)
199199

@@ -213,17 +213,24 @@ def _add_qubits_to_mapping(current_mapping, graph, new_logical_qubit_ids,
213213
backend_id = None
214214

215215
if len(qubit_interactions) == 1:
216+
# If there's only a single qubit interacting and it is already
217+
# present within the mapping, find the neighbour with the highest
218+
# degree
219+
216220
qubit = qubit_interactions[0]
217221

218222
if qubit in mapping:
219-
candidates = sorted([
220-
n for n in graph[mapping[qubit]]
221-
if n not in currently_used_nodes
222-
],
223-
key=lambda n: len(graph[n]))
223+
candidates = sorted(
224+
[n for n in graph[mapping[qubit]] if n in available_nodes],
225+
key=lambda n: len(graph[n]))
224226
if candidates:
225227
backend_id = candidates[-1]
226228
elif qubit_interactions:
229+
# If there are multiple qubits interacting, find out all the
230+
# neighbouring nodes for each interaction. Then within those
231+
# nodes, try to find the one that maximizes the number of
232+
# interactions without swapping
233+
227234
neighbours = []
228235
for qubit in qubit_interactions:
229236
if qubit in mapping:
@@ -233,14 +240,18 @@ def _add_qubits_to_mapping(current_mapping, graph, new_logical_qubit_ids,
233240
else:
234241
break
235242

243+
# Try to find an intersection that maximizes the number of
244+
# interactions by iteratively reducing the number of considered
245+
# interactions
246+
236247
intersection = set()
237-
while neighbours:
248+
while neighbours and not intersection:
238249
intersection = neighbours[0].intersection(*neighbours[1:])
239-
if intersection:
240-
backend_id = intersection.pop()
241-
break
242250
neighbours.pop()
243251

252+
if intersection:
253+
backend_id = intersection.pop()
254+
244255
if backend_id is None:
245256
backend_id = available_nodes.pop()
246257
else:
@@ -342,6 +353,11 @@ def __init__(self,
342353
- | Extra options to pass onto the cost function
343354
| (see :py:meth:`.MultiQubitGateManager.generate_swaps`)
344355
| Defaults to ``{'W': 0.5}``.
356+
* - max_swap_steps
357+
- ``int``
358+
- | Maximum number of swap steps per mapping
359+
| (see :py:meth:`.MultiQubitGateManager.generate_swaps`)
360+
| Defaults to 30
345361
"""
346362
BasicMapperEngine.__init__(self)
347363

@@ -483,7 +499,7 @@ def _send_possible_commands(self):
483499
if allocate_cmds and num_available_qubits > 0:
484500

485501
def rank_allocate_cmds(cmds_list, dag):
486-
#pylint: disable=unused-argument
502+
# pylint: disable=unused-argument
487503
return cmds_list
488504

489505
allocate_cmds = rank_allocate_cmds(
@@ -663,7 +679,7 @@ def __str__(self):
663679
664680
Returns:
665681
A summary (string) of resources used, including depth of swaps and
666-
statistics about the paths generated
682+
statistics about the swaps themselves
667683
"""
668684

669685
depth_of_swaps_str = ""

projectq/cengines/_graphmapper_test.py

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -182,19 +182,18 @@ def test_invalid_gates(simple_mapper):
182182
mapper.receive([cmd0, cmd1, cmd2, cmd3, cmd_flush])
183183

184184

185-
# def test_run_infinite_loop_detection(simple_mapper):
186-
# mapper, backend = simple_mapper
187-
# mapper.current_mapping = {i: i for i in range(7)}
185+
def test_init(simple_graph):
186+
opts = {'decay_opts': {'delta': 0.002}}
188187

189-
# qb0 = WeakQubitRef(engine=None, idx=0)
190-
# qb1 = WeakQubitRef(engine=None, idx=6)
188+
mapper = graphm.GraphMapper(graph=simple_graph, opts=opts)
189+
assert mapper.qubit_manager._decay._delta == 0.002
190+
assert mapper.qubit_manager._decay._cutoff == 5
191191

192-
# qb_flush = WeakQubitRef(engine=None, idx=-1)
193-
# cmd_flush = Command(engine=None, gate=FlushGate(), qubits=([qb_flush], ))
192+
opts = {'decay_opts': {'delta': 0.002, 'max_lifetime': 10}}
194193

195-
# cmd0 = Command(engine=None, gate=X, qubits=([qb0], ), controls=[qb1])
196-
# with pytest.raises(RuntimeError):
197-
# mapper.receive([cmd0, cmd_flush])
194+
mapper = graphm.GraphMapper(graph=simple_graph, opts=opts)
195+
assert mapper.qubit_manager._decay._delta == 0.002
196+
assert mapper.qubit_manager._decay._cutoff == 10
198197

199198

200199
def test_resetting_mapping_to_none(simple_graph):
@@ -215,6 +214,27 @@ def test_add_qubits_to_mapping_methods_failure(simple_graph):
215214
graphm.GraphMapper(graph=simple_graph, add_qubits_to_mapping="as")
216215

217216

217+
@pytest.mark.parametrize("add_qubits", ["fcfs", "fcfs_init", "FCFS"])
218+
def test_add_qubits_to_mapping_methods_only_single(simple_graph, add_qubits):
219+
mapper = graphm.GraphMapper(graph=simple_graph,
220+
add_qubits_to_mapping=add_qubits)
221+
backend = DummyEngine(save_commands=True)
222+
backend.is_last_engine = True
223+
mapper.next_engine = backend
224+
225+
qb, allocate_cmds = allocate_all_qubits_cmd(mapper)
226+
227+
qb_flush = WeakQubitRef(engine=None, idx=-1)
228+
cmd_flush = Command(engine=None, gate=FlushGate(), qubits=([qb_flush], ))
229+
gates = [
230+
Command(None, X, qubits=([qb[1]], )),
231+
Command(None, X, qubits=([qb[2]], )),
232+
]
233+
234+
mapper.receive(list(itertools.chain(allocate_cmds, gates, [cmd_flush])))
235+
assert mapper.num_mappings == 0
236+
237+
218238
@pytest.mark.parametrize("add_qubits", ["fcfs", "fcfs_init", "FCFS"])
219239
def test_add_qubits_to_mapping_methods(simple_graph, add_qubits):
220240
mapper = graphm.GraphMapper(graph=simple_graph,
@@ -594,7 +614,7 @@ def test_send_two_qubit_gate_before_swap_nonallocated_qubits(simple_mapper):
594614
assert mapper.current_mapping[4] == 4
595615
assert mapper.current_mapping[5] == 5
596616
assert mapper.current_mapping[6] in [3, 6]
597-
617+
598618
if mapper.current_mapping[6] == 3:
599619
# qb[6] is on position 3, all commands are possible
600620
assert mapper.qubit_manager.size() == 0

0 commit comments

Comments
 (0)