Skip to content

Commit

Permalink
Make DenseLayout transpiler pass target aware
Browse files Browse the repository at this point in the history
This commit updates the dense layout pass target aware so that at
its instantiation a Target object can be specified to define the target
device for compilation. When specified this target will be used for
all device aware operations in the pass. When a target is specified the
error matrix used is the average of all 1q and 2q operations while
before the matrix was composed of just cx and readout error rates.

Part of Qiskit#7113
  • Loading branch information
mtreinish committed Mar 14, 2022
1 parent 1980326 commit 1966e3e
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 28 deletions.
87 changes: 64 additions & 23 deletions qiskit/transpiler/passes/layout/dense_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,39 +35,33 @@ class DenseLayout(AnalysisPass):
by being set in `property_set`.
"""

def __init__(self, coupling_map, backend_prop=None):
def __init__(self, coupling_map, backend_prop=None, target=None):
"""DenseLayout initializer.
Args:
coupling_map (Coupling): directed graph representing a coupling map.
backend_prop (BackendProperties): backend properties object
target (Target): A target representing the target backend.
"""
super().__init__()
self.coupling_map = coupling_map
self.backend_prop = backend_prop
self.target = target
num_qubits = 0
self.adjacency_matrix = None
if self.coupling_map:
num_qubits = self.coupling_map.size()
self.adjacency_matrix = retworkx.adjacency_matrix(self.coupling_map.graph)
self.error_mat = np.zeros((num_qubits, num_qubits))
if self.backend_prop and self.coupling_map:
error_dict = {
tuple(gate.qubits): gate.parameters[0].value
for gate in self.backend_prop.gates
if len(gate.qubits) == 2
}
for edge in self.coupling_map.get_edges():
gate_error = error_dict.get(edge)
if gate_error is not None:
self.error_mat[edge[0]][edge[1]] = gate_error
for index, qubit_data in enumerate(self.backend_prop.qubits):
# Handle faulty qubits edge case
if index >= num_qubits:
break
for item in qubit_data:
if item.name == "readout_error":
self.error_mat[index][index] = item.value
if target is not None:
num_qubits = target.num_qubits
self.coupling_map = target.build_coupling_map()
if self.coupling_map is not None:
self.adjacency_matrix = retworkx.adjacency_matrix(self.coupling_map.graph)
self.error_mat, self._use_error = _build_error_matrix(num_qubits, target=target)
else:
if self.coupling_map:
num_qubits = self.coupling_map.size()
self.adjacency_matrix = retworkx.adjacency_matrix(self.coupling_map.graph)
self.error_mat, self._use_error = _build_error_matrix(
num_qubits, backend_prop=self.backend_prop, coupling_map=self.coupling_map
)

def run(self, dag):
"""Run the DenseLayout pass on `dag`.
Expand Down Expand Up @@ -125,7 +119,7 @@ def _best_subset(self, num_qubits, num_meas, num_cx):
self.adjacency_matrix,
num_meas,
num_cx,
bool(self.backend_prop),
self._use_error,
self.coupling_map.is_symmetric,
self.error_mat,
)
Expand All @@ -134,3 +128,50 @@ def _best_subset(self, num_qubits, num_meas, num_cx):
perm = csgraph.reverse_cuthill_mckee(sp_sub_graph)
best_map = best_map[perm]
return best_map


def _build_error_matrix(num_qubits, target=None, coupling_map=None, backend_prop=None):
error_mat = np.zeros((num_qubits, num_qubits))
use_error = False
if target is not None and target.qargs is not None:
for qargs in target.qargs:
# Ignore gates over 2q DenseLayout only works with 2q
if len(qargs) > 2:
continue
count = 0.0
error = 0.0
ops = target.operation_names_for_qargs(qargs)
for op in ops:
props = target[op][qargs]
if props is not None and props.error is not None:
error += props.error
count += 1.0
avg_error = error / count
# TODO: Factor in T1 and T2 to error matrix after #7736
if len(qargs) == 1:
qubit = qargs[0]
error_mat[qubit][qubit] = avg_error
use_error = True
elif len(qargs) == 2:
error_mat[qargs[0]][qargs[1]] = avg_error
use_error = True
elif backend_prop and coupling_map:
error_dict = {
tuple(gate.qubits): gate.parameters[0].value
for gate in backend_prop.gates
if len(gate.qubits) == 2
}
for edge in coupling_map.get_edges():
gate_error = error_dict.get(edge)
if gate_error is not None:
error_mat[edge[0]][edge[1]] = gate_error
use_error = True
for index, qubit_data in enumerate(backend_prop.qubits):
# Handle faulty qubits edge case
if index >= num_qubits:
break
for item in qubit_data:
if item.name == "readout_error":
error_mat[index][index] = item.value
use_error = True
return error_mat, use_error
2 changes: 1 addition & 1 deletion qiskit/transpiler/preset_passmanagers/level0.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def _choose_layout_condition(property_set):
if layout_method == "trivial":
_choose_layout = TrivialLayout(coupling_map)
elif layout_method == "dense":
_choose_layout = DenseLayout(coupling_map, backend_properties)
_choose_layout = DenseLayout(coupling_map, backend_properties, target=target)
elif layout_method == "noise_adaptive":
_choose_layout = NoiseAdaptiveLayout(backend_properties)
elif layout_method == "sabre":
Expand Down
2 changes: 1 addition & 1 deletion qiskit/transpiler/preset_passmanagers/level1.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ def _vf2_match_not_found(property_set):
if layout_method == "trivial":
_improve_layout = TrivialLayout(coupling_map)
elif layout_method == "dense":
_improve_layout = DenseLayout(coupling_map, backend_properties)
_improve_layout = DenseLayout(coupling_map, backend_properties, target=target)
elif layout_method == "noise_adaptive":
_improve_layout = NoiseAdaptiveLayout(backend_properties)
elif layout_method == "sabre":
Expand Down
2 changes: 1 addition & 1 deletion qiskit/transpiler/preset_passmanagers/level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def _vf2_match_not_found(property_set):
if layout_method == "trivial":
_choose_layout_1 = TrivialLayout(coupling_map)
elif layout_method == "dense":
_choose_layout_1 = DenseLayout(coupling_map, backend_properties)
_choose_layout_1 = DenseLayout(coupling_map, backend_properties, target=target)
elif layout_method == "noise_adaptive":
_choose_layout_1 = NoiseAdaptiveLayout(backend_properties)
elif layout_method == "sabre":
Expand Down
2 changes: 1 addition & 1 deletion qiskit/transpiler/preset_passmanagers/level3.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def _vf2_match_not_found(property_set):
if layout_method == "trivial":
_choose_layout_1 = TrivialLayout(coupling_map)
elif layout_method == "dense":
_choose_layout_1 = DenseLayout(coupling_map, backend_properties)
_choose_layout_1 = DenseLayout(coupling_map, backend_properties, target=target)
elif layout_method == "noise_adaptive":
_choose_layout_1 = NoiseAdaptiveLayout(backend_properties)
elif layout_method == "sabre":
Expand Down
20 changes: 19 additions & 1 deletion qiskit/transpiler/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ def operation_from_name(self, instruction):
return self._gate_name_map[instruction]

def operations_for_qargs(self, qargs):
"""Get the operation class object for a specified qarg
"""Get the operation class object for a specified qargs tuple
Args:
qargs (tuple): A qargs tuple of the qubits to get the gates that apply
Expand All @@ -484,6 +484,24 @@ def operations_for_qargs(self, qargs):
raise KeyError(f"{qargs} not in target.")
return [self._gate_name_map[x] for x in self._qarg_gate_map[qargs]]

def operation_names_for_qargs(self, qargs):
"""Get the operation names for a specified qargs tuple
Args:
qargs (tuple): A qargs tuple of the qubits to get the gates that apply
to it. For example, ``(0,)`` will return the set of all
instructions that apply to qubit 0.
Returns:
set: The set of operation names that apply to the specified
`qargs``.
Raises:
KeyError: If qargs is not in target
"""
if qargs not in self._qarg_gate_map:
raise KeyError(f"{qargs} not in target.")
return self._qarg_gate_map[qargs]

@property
def operation_names(self):
"""Get the operation names in the target."""
Expand Down
13 changes: 13 additions & 0 deletions releasenotes/notes/dense-layout-target-aware-2b330ccee948d31a.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
features:
- |
The :class:`~qiskit.transpiler.DenseLayout` pass has a new keyword argument
on it's constructor, ``target``. This argument is used to specify a
:class:`~qiskit.transpiler.Target` object representing the compilation target
for the pass. If it is specified it superscedes the other arguments,
``coupling_map`` and ``backend_prop``.
- |
The :class:`~qiskit.transpiler.Target` class has a new method,
:meth:`~qiskit.transpiler.Target.operation_names_from_qargs`. This method is
used to get the operation names (i.e. lookup key in the target) for the operations
on a given qargs tuple.
18 changes: 18 additions & 0 deletions test/python/transpiler/test_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,24 @@ def test_get_instructions_for_qargs(self):
for gate in ideal_sim_expected:
self.assertIn(gate, self.ideal_sim_target.operations_for_qargs(None))

def test_get_operation_names_for_qargs(self):
with self.assertRaises(KeyError):
self.empty_target.operation_names_for_qargs((0,))
expected = {"rz", "id", "sx", "x", "measure"}
res = self.ibm_target.operation_names_for_qargs((0,))
for gate in expected:
self.assertIn(gate, res)
expected = {"ecr"}
res = self.fake_backend_target.operation_names_for_qargs((1, 0))
for gate in expected:
self.assertIn(gate, res)
expected = {"cx"}
res = self.fake_backend_target.operation_names_for_qargs((0, 1))
self.assertEqual(expected, res)
ideal_sim_expected = ["u", "rx", "ry", "rz", "cx", "ecr", "ccx", "measure"]
for gate in ideal_sim_expected:
self.assertIn(gate, self.ideal_sim_target.operation_names_for_qargs(None))

def test_coupling_map(self):
self.assertEqual(
CouplingMap().get_edges(), self.empty_target.build_coupling_map().get_edges()
Expand Down

0 comments on commit 1966e3e

Please sign in to comment.