Skip to content

Commit

Permalink
Make VF2Layout pass Target aware
Browse files Browse the repository at this point in the history
This commit updates the VF2Layout pass to be target aware. It adds a new
kwarg for specifying a target and if it's specified that target object
is used to define the parameters for finding the isomorphic mapping used
for the layout. However, with this commit the extra information contained in
the target isn't really leveraged yet and just the global coupling graph
and measurement error rates are pulled from the target just as in the
BackendV1 case. This commit is mostly to facilitate future expansion where
we will improve the layout scoring heuristic used and having the full set
of data available in the target will be useful.

Part of Qiskit#7113
  • Loading branch information
mtreinish committed Mar 4, 2022
1 parent f08e647 commit b2c9f70
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 20 deletions.
62 changes: 44 additions & 18 deletions qiskit/transpiler/passes/layout/vf2_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,14 @@ class VF2Layout(AnalysisPass):

def __init__(
self,
coupling_map,
coupling_map=None,
strict_direction=False,
seed=None,
call_limit=None,
time_limit=None,
properties=None,
max_trials=None,
target=None,
):
"""Initialize a ``VF2Layout`` pass instance
Expand All @@ -80,14 +81,26 @@ def __init__(
based on the number of edges in the interaction graph or the coupling graph
(whichever is larger). If set to a value <= 0 no limit on the number of trials
will be set.
target (Target): A target representing the backend device to run vf2layout on.
If specified it will supersede a set value for ``properties`` and
``coupling_map``.
Raises:
TypeError: If neither ``coupling_map`` or ``target`` are provided.
"""
super().__init__()
self.coupling_map = coupling_map
self.target = target
if target is not None:
self.coupling_map = self.target.build_coupling_map()
elif coupling_map is not None:
self.coupling_map = coupling_map
else:
raise TypeError("coupling_map or target must be specified.")
self.properties = properties
self.strict_direction = strict_direction
self.seed = seed
self.call_limit = call_limit
self.time_limit = time_limit
self.properties = properties
self.max_trials = max_trials

def run(self, dag):
Expand Down Expand Up @@ -197,20 +210,33 @@ def _score_layout(self, layout):
to weight against higher connectivity qubits."""
bits = layout.get_physical_bits()
score = 0
if self.properties is None:
# Sum qubit degree for each qubit in chosen layout as really rough estimate of error
if self.target:
if "measure" in self.target:
for bit in bits:
props = self.target["measure"].get((bit,))
if props is None or props.error is None:
score += (
self.coupling_map.graph.out_degree(bit)
+ self.coupling_map.graph.in_degree(bit)
) / len(self.coupling_map.graph)
else:
score += props.error
else:
if self.properties is None:
# Sum qubit degree for each qubit in chosen layout as really rough estimate of error
for bit in bits:
score += self.coupling_map.graph.out_degree(
bit
) + self.coupling_map.graph.in_degree(bit)
return score
for bit in bits:
score += self.coupling_map.graph.out_degree(
bit
) + self.coupling_map.graph.in_degree(bit)
return score
for bit in bits:
try:
score += self.properties.readout_error(bit)
# If readout error can't be found in properties fallback to degree
# divided by number of qubits as a terrible approximation
except BackendPropertyError:
score += (
self.coupling_map.graph.out_degree(bit) + self.coupling_map.graph.in_degree(bit)
) / len(self.coupling_map.graph)
try:
score += self.properties.readout_error(bit)
# If readout error can't be found in properties fallback to degree
# divided by number of qubits as a terrible approximation
except BackendPropertyError:
score += (
self.coupling_map.graph.out_degree(bit)
+ self.coupling_map.graph.in_degree(bit)
) / len(self.coupling_map.graph)
return score
1 change: 1 addition & 0 deletions qiskit/transpiler/preset_passmanagers/level1.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ def _vf2_match_not_found(property_set):
call_limit=int(5e4), # Set call limit to ~100ms with retworkx 0.10.2
time_limit=0.1,
properties=backend_properties,
target=target,
)
)

Expand Down
1 change: 1 addition & 0 deletions qiskit/transpiler/preset_passmanagers/level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ def _vf2_match_not_found(property_set):
call_limit=int(5e6), # Set call limit to ~10 sec with retworkx 0.10.2
time_limit=10.0,
properties=backend_properties,
target=target,
)
)

Expand Down
1 change: 1 addition & 0 deletions qiskit/transpiler/preset_passmanagers/level3.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ def _vf2_match_not_found(property_set):
call_limit=int(3e7), # Set call limit to ~60 sec with retworkx 0.10.2
time_limit=60,
properties=backend_properties,
target=target,
)
)
# 2b. if VF2 didn't converge on a solution use layout_method (dense).
Expand Down
23 changes: 21 additions & 2 deletions test/python/transpiler/test_vf2_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@
import retworkx

from qiskit import QuantumRegister, QuantumCircuit
from qiskit.transpiler import CouplingMap, Layout
from qiskit.transpiler import CouplingMap, Layout, Target
from qiskit.transpiler.passes.layout.vf2_layout import VF2Layout, VF2LayoutStopReason
from qiskit.converters import circuit_to_dag
from qiskit.test import QiskitTestCase
from qiskit.test.mock import FakeTenerife, FakeRueschlikon, FakeManhattan, FakeYorktown
from qiskit.circuit.library import GraphState
from qiskit.circuit.library import GraphState, CXGate


class LayoutTestCase(QiskitTestCase):
Expand Down Expand Up @@ -119,6 +119,25 @@ def test_call_limit(self):
pass_.property_set["VF2Layout_stop_reason"], VF2LayoutStopReason.NO_SOLUTION_FOUND
)

def test_coupling_map_and_target(self):
"""Test that a Target is used instead of a CouplingMap if both are specified."""
cmap = CouplingMap([[0, 1], [1, 2], [2, 0]])
target = Target()
target.add_instruction(CXGate(), {(0, 1): None, (1, 2): None, (1, 0): None})
qr = QuantumRegister(3, "qr")
circuit = QuantumCircuit(qr)
circuit.cx(qr[0], qr[1]) # qr0-> qr1
circuit.cx(qr[1], qr[2]) # qr1-> qr2
dag = circuit_to_dag(circuit)
pass_ = VF2Layout(cmap, seed=-1, max_trials=1, target=target)
pass_.run(dag)
self.assertLayout(dag, target.build_coupling_map(), pass_.property_set)

def test_neither_coupling_map_or_target(self):
"""Test that we raise if neither a target or coupling map is specified."""
with self.assertRaises(TypeError):
VF2Layout(seed=123, call_limit=1000, time_limit=20, max_trials=7)


class TestVF2LayoutLattice(LayoutTestCase):
"""Fit in 25x25 hexagonal lattice coupling map"""
Expand Down

0 comments on commit b2c9f70

Please sign in to comment.