Skip to content

Commit

Permalink
Make ConsolidateBlocks transpiler pass target aware
Browse files Browse the repository at this point in the history
This commit updates the consolidate blocks 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
consolidate blocks pass will only consolidate blocks with instructions
outside the target (both operation and qubits).

Part of Qiskit#7113
  • Loading branch information
mtreinish committed Mar 14, 2022
1 parent 1980326 commit 4a27221
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 10 deletions.
18 changes: 13 additions & 5 deletions qiskit/transpiler/passes/optimization/consolidate_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,18 @@ class ConsolidateBlocks(TransformationPass):
collected by a previous pass, such as `Collect2qBlocks`.
"""

def __init__(self, kak_basis_gate=None, force_consolidate=False, basis_gates=None):
def __init__(self, kak_basis_gate=None, force_consolidate=False, basis_gates=None, target=None):
"""ConsolidateBlocks initializer.
Args:
kak_basis_gate (Gate): Basis gate for KAK decomposition.
force_consolidate (bool): Force block consolidation
basis_gates (List(str)): Basis gates from which to choose a KAK gate.
target (Target): The target object for the compilation target backend
"""
super().__init__()
self.basis_gates = None
self.target = target
if basis_gates is not None:
self.basis_gates = set(basis_gates)
self.force_consolidate = force_consolidate
Expand All @@ -73,7 +75,7 @@ def run(self, dag):
basis_gate_name = self.decomposer.gate.name
all_block_gates = set()
for block in blocks:
if len(block) == 1 and (self.basis_gates and block[0].name not in self.basis_gates):
if len(block) == 1 and self._check_not_in_basis(block[0].name, block[0].qargs):
all_block_gates.add(block[0])
dag.substitute_node(block[0], UnitaryGate(block[0].op.to_matrix()))
else:
Expand All @@ -95,7 +97,7 @@ def run(self, dag):
for nd in block:
if nd.op.name == basis_gate_name:
basis_count += 1
if self.basis_gates and nd.op.name not in self.basis_gates:
if self._check_not_in_basis(nd.op.name, nd.qargs):
outside_basis = True
qc.append(nd.op, [q[block_index_map[i]] for i in nd.qargs])
unitary = UnitaryGate(Operator(qc))
Expand All @@ -106,15 +108,15 @@ def run(self, dag):
or unitary.num_qubits > 2
or self.decomposer.num_basis_gates(unitary) < basis_count
or len(block) > max_2q_depth
or (self.basis_gates is not None and outside_basis)
or ((self.basis_gates is not None) and outside_basis)
):
dag.replace_block_with_op(block, unitary, block_index_map, cycle_check=False)
# If 1q runs are collected before consolidate those too
runs = self.property_set["run_list"] or []
for run in runs:
if run[0] in all_block_gates:
continue
if len(run) == 1 and self.basis_gates and run[0].name not in self.basis_gates:
if len(run) == 1 and not self._check_in_basis(run[0].name, run[0].qargs):
dag.substitute_node(run[0], UnitaryGate(run[0].op.to_matrix()))
else:
qubit = run[0].qargs[0]
Expand All @@ -130,6 +132,12 @@ def run(self, dag):
dag.replace_block_with_op(run, unitary, {qubit: 0}, cycle_check=False)
return dag

def _check_not_in_basis(self, gate_name, qargs):
if self.target is not None:
return not self.target.instruction_supported(gate_name, tuple(qargs))
else:
return self.basis_gates and gate_name not in self.basis_gates

def _block_qargs_to_indices(self, block_qargs, global_index_map):
"""Map each qubit in block_qargs to its wire position among the block's wires.
Args:
Expand Down
2 changes: 1 addition & 1 deletion qiskit/transpiler/preset_passmanagers/level0.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ def _swap_condition(property_set):
Unroll3qOrMore(),
Collect2qBlocks(),
Collect1qRuns(),
ConsolidateBlocks(basis_gates=basis_gates),
ConsolidateBlocks(basis_gates=basis_gates, target=target),
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
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 @@ -247,7 +247,7 @@ def _swap_condition(property_set):
),
Unroll3qOrMore(),
Collect2qBlocks(),
ConsolidateBlocks(basis_gates=basis_gates),
ConsolidateBlocks(basis_gates=basis_gates, target=target),
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
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 @@ -232,7 +232,7 @@ def _swap_condition(property_set):
),
Unroll3qOrMore(),
Collect2qBlocks(),
ConsolidateBlocks(basis_gates=basis_gates),
ConsolidateBlocks(basis_gates=basis_gates, target=target),
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
Expand Down
4 changes: 2 additions & 2 deletions qiskit/transpiler/preset_passmanagers/level3.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ def _swap_condition(property_set):
),
Unroll3qOrMore(),
Collect2qBlocks(),
ConsolidateBlocks(basis_gates=basis_gates),
ConsolidateBlocks(basis_gates=basis_gates, target=target),
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
Expand Down Expand Up @@ -264,7 +264,7 @@ def _opt_control(property_set):

_opt = [
Collect2qBlocks(),
ConsolidateBlocks(basis_gates=basis_gates),
ConsolidateBlocks(basis_gates=basis_gates, target=target),
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
Expand Down
20 changes: 20 additions & 0 deletions qiskit/transpiler/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,26 @@ 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 instruction_supported(self, operation_name, qargs):
"""Return whether the instruction (operation + qubits) is supported by the target
Args:
operation_name (str): The name of the operation for the instruction
qargs (tuple): The tuple of qubit indices for the instruction
Returns:
bool: Returns ``True`` if the instruction is supported and ``False`` if it isn't.
"""
if operation_name in self._gate_map:
if (
qargs not in self._gate_map[operation_name]
or self._gate_map[operation_name] is None
or None in self._gate_map[operation_name]
):
return True
return False

@property
def operation_names(self):
"""Get the operation names in the target."""
Expand Down

0 comments on commit 4a27221

Please sign in to comment.