Skip to content

Commit 2c2a514

Browse files
authored
Improve initial qubit mapping algorithm (#144)
* Improve initial qubit mapping algorithm. * Include qubits from 1-qubit gates in the initial mapping. * Add better test cases. * Refactor the initial mapping logic into a utils file. * Address comments; integrate with SwapUpdater. * Fix type annotation and comment.
1 parent 5842d9f commit 2c2a514

4 files changed

Lines changed: 896 additions & 31 deletions

File tree

recirq/quantum_chess/circuit_transformer.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import cirq
1818

1919
import recirq.quantum_chess.controlled_iswap as controlled_iswap
20+
import recirq.quantum_chess.initial_mapping_utils as imu
21+
import recirq.quantum_chess.swap_updater as su
2022

2123
ADJACENCY = [(0, 1), (0, -1), (1, 0), (-1, 0)]
2224

@@ -305,6 +307,49 @@ def transform(self, circuit: cirq.Circuit) -> cirq.Circuit:
305307
return circuit.transform_qubits(lambda q: self.mapping[q])
306308

307309

310+
class DynamicLookAheadHeuristicCircuitTransformer(CircuitTransformer):
311+
"""Optimizer that transforms a circuit to satify a device's constraints.
312+
313+
This implements the initial mapping algorithm and the SWAP-based update
314+
algorithm proposed by the paper "A Dynamic Look-Ahead Heuristic for the
315+
Qubit Mapping Problem of NISQ Computer":
316+
https://ieeexplore.ieee.org/abstract/document/8976109.
317+
318+
The initial mapping algorithm first maps the center of the logical qubits
319+
graph to the center of the physical qubits graph. It then traverses the
320+
logical qubits in a breadth-first traversal order starting from the center
321+
of the logical qubits graph. For each logical qubit, it finds the physical
322+
qubit that minimizes the nearest neighbor distance for the leftmost gates.
323+
324+
The SWAP-based update algorithm uses a heuristic cost function of a SWAP
325+
operation called maximum consecutive positive effect (MCPE) to greedily
326+
look ahead in each moment for SWAP operations that will reduce the nearest
327+
neighbor distance for the largest number of gates in the current look-ahead
328+
window.
329+
330+
Reference:
331+
P. Zhu, Z. Guan and X. Cheng, "A Dynamic Look-Ahead Heuristic for the
332+
Qubit Mapping Problem of NISQ Computers," in IEEE Transactions on Computer-
333+
Aided Design of Integrated Circuits and Systems, vol. 39, no. 12, pp. 4721-
334+
4735, Dec. 2020, doi: 10.1109/TCAD.2020.2970594.
335+
"""
336+
def __init__(self, device: cirq.Device):
337+
super().__init__()
338+
self.device = device
339+
340+
def transform(self, circuit: cirq.Circuit) -> cirq.Circuit:
341+
"""Returns a transformed circuit.
342+
343+
The transformed circuit satisfies all physical adjacency constraints
344+
of the device.
345+
346+
Args:
347+
circuit: The circuit to transform.
348+
"""
349+
initial_mapping = imu.calculate_initial_mapping(self.device, circuit)
350+
updater = su.SwapUpdater(circuit, self.device.qubit_set(), initial_mapping)
351+
return cirq.Circuit(updater.add_swaps())
352+
308353
class SycamoreDecomposer(cirq.PointOptimizer):
309354
"""Optimizer that decomposes all three qubit operations into
310355
sqrt-ISWAPs.

recirq/quantum_chess/circuit_transformer_test.py

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -30,62 +30,82 @@
3030
d1 = cirq.NamedQubit('d1')
3131

3232

33+
@pytest.mark.parametrize('transformer',
34+
[ct.ConnectivityHeuristicCircuitTransformer,
35+
ct.DynamicLookAheadHeuristicCircuitTransformer])
3336
@pytest.mark.parametrize('device',
34-
(cirq.google.Sycamore23, cirq.google.Sycamore))
35-
def test_single_qubit_ops(device):
36-
transformer = ct.ConnectivityHeuristicCircuitTransformer(device)
37+
[cirq.google.Sycamore23, cirq.google.Sycamore])
38+
def test_single_qubit_ops(transformer, device):
3739
c = cirq.Circuit(cirq.X(a1), cirq.X(a2), cirq.X(a3))
38-
transformer.qubit_mapping(c)
39-
c = transformer.transform(c)
40-
device.validate_circuit(c)
40+
t = transformer(device)
41+
device.validate_circuit(t.transform(c))
4142

4243

44+
@pytest.mark.parametrize('transformer',
45+
[ct.ConnectivityHeuristicCircuitTransformer,
46+
ct.DynamicLookAheadHeuristicCircuitTransformer])
4347
@pytest.mark.parametrize('device',
44-
(cirq.google.Sycamore23, cirq.google.Sycamore))
45-
def test_single_qubit_with_two_qubits(device):
46-
transformer = ct.ConnectivityHeuristicCircuitTransformer(device)
48+
[cirq.google.Sycamore23, cirq.google.Sycamore])
49+
def test_single_qubit_and_two_qubits_ops(transformer, device):
4750
c = cirq.Circuit(cirq.X(a1), cirq.X(a2), cirq.X(a3),
4851
cirq.ISWAP(a3, a4) ** 0.5)
49-
transformer.qubit_mapping(c)
50-
device.validate_circuit(transformer.transform(c))
52+
t = transformer(device)
53+
device.validate_circuit(t.transform(c))
5154

5255

56+
@pytest.mark.parametrize('transformer',
57+
[ct.ConnectivityHeuristicCircuitTransformer,
58+
ct.DynamicLookAheadHeuristicCircuitTransformer])
5359
@pytest.mark.parametrize('device',
54-
(cirq.google.Sycamore23, cirq.google.Sycamore))
55-
def test_three_split_moves(device):
56-
transformer = ct.ConnectivityHeuristicCircuitTransformer(device)
60+
[cirq.google.Sycamore23, cirq.google.Sycamore])
61+
def test_three_split_moves(transformer, device):
5762
c = cirq.Circuit(qm.split_move(a1, a2, b1), qm.split_move(a2, a3, b3),
5863
qm.split_move(b1, c1, c2))
59-
transformer.qubit_mapping(c)
60-
device.validate_circuit(transformer.transform(c))
64+
t = transformer(device)
65+
device.validate_circuit(t.transform(c))
6166

6267

68+
@pytest.mark.parametrize('transformer',
69+
[ct.ConnectivityHeuristicCircuitTransformer,
70+
ct.DynamicLookAheadHeuristicCircuitTransformer])
6371
@pytest.mark.parametrize('device',
64-
(cirq.google.Sycamore23, cirq.google.Sycamore))
65-
def test_disconnected(device):
66-
transformer = ct.ConnectivityHeuristicCircuitTransformer(device)
72+
[cirq.google.Sycamore23, cirq.google.Sycamore])
73+
def test_disconnected(transformer, device):
6774
c = cirq.Circuit(qm.split_move(a1, a2, a3), qm.split_move(a3, a4, d1),
6875
qm.split_move(b1, b2, b3), qm.split_move(c1, c2, c3))
69-
transformer.qubit_mapping(c)
70-
device.validate_circuit(transformer.transform(c))
76+
t = transformer(device)
77+
device.validate_circuit(t.transform(c))
7178

7279

80+
@pytest.mark.parametrize('transformer',
81+
[ct.ConnectivityHeuristicCircuitTransformer,
82+
ct.DynamicLookAheadHeuristicCircuitTransformer])
7383
@pytest.mark.parametrize('device',
74-
(cirq.google.Sycamore23, cirq.google.Sycamore))
75-
def test_move_around_square(device):
76-
transformer = ct.ConnectivityHeuristicCircuitTransformer(device)
84+
[cirq.google.Sycamore23, cirq.google.Sycamore])
85+
def test_move_around_square(transformer, device):
7786
c = cirq.Circuit(qm.normal_move(a1, a2), qm.normal_move(a2, b2),
7887
qm.normal_move(b2, b1), qm.normal_move(b1, a1))
79-
transformer.qubit_mapping(c)
80-
device.validate_circuit(transformer.transform(c))
88+
t = transformer(device)
89+
device.validate_circuit(t.transform(c))
8190

8291

92+
@pytest.mark.parametrize('transformer',
93+
[ct.ConnectivityHeuristicCircuitTransformer,
94+
ct.DynamicLookAheadHeuristicCircuitTransformer])
8395
@pytest.mark.parametrize('device',
84-
(cirq.google.Sycamore23, cirq.google.Sycamore))
85-
def test_split_then_merge(device):
86-
transformer = ct.ConnectivityHeuristicCircuitTransformer(device)
96+
[cirq.google.Sycamore23, cirq.google.Sycamore])
97+
def test_split_then_merge(transformer, device):
8798
c = cirq.Circuit(qm.split_move(a1, a2, b1), qm.split_move(a2, a3, b3),
8899
qm.split_move(b1, c1, c2), qm.normal_move(c1, d1),
89100
qm.normal_move(a3, a4), qm.merge_move(a4, d1, a1))
90-
transformer.qubit_mapping(c)
91-
device.validate_circuit(transformer.transform(c))
101+
t = transformer(device)
102+
device.validate_circuit(t.transform(c))
103+
104+
105+
@pytest.mark.parametrize('device',
106+
[cirq.google.Sycamore23, cirq.google.Sycamore])
107+
def test_split_then_merge_trapezoid(device):
108+
c = cirq.Circuit(qm.split_move(a1, a2, b1), qm.normal_move(a2, a3),
109+
qm.merge_move(a3, b1, b3))
110+
t = ct.DynamicLookAheadHeuristicCircuitTransformer(device)
111+
device.validate_circuit(t.transform(c))

0 commit comments

Comments
 (0)