Skip to content

Commit

Permalink
First attempt at supporting a virtual target (FakeTarget) with no bas…
Browse files Browse the repository at this point in the history
…is gates in the preset pass managers.
  • Loading branch information
ElePT committed Oct 17, 2024
1 parent 50f2965 commit 8eea4cf
Show file tree
Hide file tree
Showing 13 changed files with 275 additions and 134 deletions.
4 changes: 4 additions & 0 deletions qiskit/transpiler/passes/basis/basis_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ def __init__(self, equivalence_library, target_basis, target=None, min_qubits=0)
for qarg in self._target[gate]:
self._qargs_with_non_global_operation[qarg].add(gate)

print("inside pas: target:", target, "target basis", target_basis)

def run(self, dag):
"""Translate an input DAGCircuit to the target basis.
Expand Down Expand Up @@ -153,6 +155,8 @@ def run(self, dag):
source_basis, qargs_local_source_basis = self._extract_basis_target(dag, qarg_indices)

target_basis = set(target_basis).union(basic_instrs)
print("final target basis", target_basis)

# If the source basis is a subset of the target basis and we have no circuit
# instructions on qargs that have non-global operations there is nothing to
# translate and we can exit early.
Expand Down
2 changes: 1 addition & 1 deletion qiskit/transpiler/passes/layout/vf2_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def build_average_error_map(target, properties, coupling_map):
# running the fallback heuristic
if not built and target is not None and coupling_map is None:
coupling_map = target.build_coupling_map()
if not built and coupling_map is not None:
if not built and coupling_map is not None and num_qubits is not None:
for qubit in range(num_qubits):
avg_map.add_error(
(qubit, qubit),
Expand Down
182 changes: 117 additions & 65 deletions qiskit/transpiler/preset_passmanagers/builtin_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import os

from qiskit.transpiler.target import FakeTarget
from qiskit.transpiler.passes.optimization.split_2q_unitaries import Split2QUnitaries
from qiskit.transpiler.passmanager import PassManager
from qiskit.transpiler.exceptions import TranspilerError
Expand Down Expand Up @@ -94,6 +95,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
or (
pass_manager_config.target is not None
and pass_manager_config.target.build_coupling_map() is not None
and not isinstance(pass_manager_config.target, FakeTarget)
)
):
init = common.generate_unroll_3q(
Expand All @@ -113,6 +115,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
or (
pass_manager_config.target is not None
and pass_manager_config.target.build_coupling_map() is not None
and not isinstance(pass_manager_config.target, FakeTarget)
)
):
init += common.generate_unroll_3q(
Expand Down Expand Up @@ -144,15 +147,17 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
)

elif optimization_level in {2, 3}:
init = common.generate_unroll_3q(
pass_manager_config.target,
pass_manager_config.basis_gates,
pass_manager_config.approximation_degree,
pass_manager_config.unitary_synthesis_method,
pass_manager_config.unitary_synthesis_plugin_config,
pass_manager_config.hls_config,
pass_manager_config.qubits_initially_zero,
)
init = PassManager()
if not isinstance(pass_manager_config.target, FakeTarget):
init = common.generate_unroll_3q(
pass_manager_config.target,
pass_manager_config.basis_gates,
pass_manager_config.approximation_degree,
pass_manager_config.unitary_synthesis_method,
pass_manager_config.unitary_synthesis_plugin_config,
pass_manager_config.hls_config,
pass_manager_config.qubits_initially_zero,
)
if pass_manager_config.routing_method != "none":
init.append(ElidePermutations())
init.append(RemoveDiagonalGatesBeforeMeasure())
Expand All @@ -176,7 +181,12 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
)
init.append(CommutativeCancellation())
init.append(Collect2qBlocks())
init.append(ConsolidateBlocks())
if (
pass_manager_config.basis_gates is not None
and len(pass_manager_config.basis_gates) > 0
):
print("HERE??", pass_manager_config.basis_gates)
init.append(ConsolidateBlocks())
# If approximation degree is None that indicates a request to approximate up to the
# error rates in the target. However, in the init stage we don't yet know the target
# qubits being used to figure out the fidelity so just use the default fidelity parameter
Expand Down Expand Up @@ -209,7 +219,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana


class UnitarySynthesisPassManager(PassManagerStagePlugin):
"""Plugin class for translation stage with :class:`~.BasisTranslator`"""
"""Plugin class for translation stage with :class:`~.UnitarySynthesis`"""

def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager:
return common.generate_translation_passmanager(
Expand Down Expand Up @@ -557,10 +567,18 @@ def _opt_control(property_set):
)
if optimization_level == 1:
# Steps for optimization level 1
_opt = [
Optimize1qGatesDecomposition(
basis=pass_manager_config.basis_gates, target=pass_manager_config.target
),
if (
pass_manager_config.basis_gates is not None
and len(pass_manager_config.basis_gates) > 0
):
_opt = [
Optimize1qGatesDecomposition(
basis=pass_manager_config.basis_gates, target=pass_manager_config.target
)
]
else:
_opt = []
_opt += [
InverseCancellation(
[
CXGate(),
Expand All @@ -580,60 +598,29 @@ def _opt_control(property_set):
]

elif optimization_level == 2:
_opt = [
Optimize1qGatesDecomposition(
basis=pass_manager_config.basis_gates, target=pass_manager_config.target
),
if (
pass_manager_config.basis_gates is not None
and len(pass_manager_config.basis_gates) > 0
):
_opt = [
Optimize1qGatesDecomposition(
basis=pass_manager_config.basis_gates, target=pass_manager_config.target
)
]
else:
_opt = []
_opt += [
CommutativeCancellation(target=pass_manager_config.target),
]
elif optimization_level == 3:
# Steps for optimization level 3
_opt = [
Collect2qBlocks(),
ConsolidateBlocks(
basis_gates=pass_manager_config.basis_gates,
target=pass_manager_config.target,
approximation_degree=pass_manager_config.approximation_degree,
),
UnitarySynthesis(
pass_manager_config.basis_gates,
approximation_degree=pass_manager_config.approximation_degree,
coupling_map=pass_manager_config.coupling_map,
backend_props=pass_manager_config.backend_properties,
method=pass_manager_config.unitary_synthesis_method,
plugin_config=pass_manager_config.unitary_synthesis_plugin_config,
target=pass_manager_config.target,
),
Optimize1qGatesDecomposition(
basis=pass_manager_config.basis_gates, target=pass_manager_config.target
),
CommutativeCancellation(target=pass_manager_config.target),
]

def _opt_control(property_set):
return not property_set["optimization_loop_minimum_point"]

else:
raise TranspilerError(f"Invalid optimization_level: {optimization_level}")

unroll = translation.to_flow_controller()
_opt = [Collect2qBlocks()]

# Build nested Flow controllers
def _unroll_condition(property_set):
return not property_set["all_gates_in_basis"]

# Check if any gate is not in the basis, and if so, run unroll passes
_unroll_if_out_of_basis = [
GatesInBasis(pass_manager_config.basis_gates, target=pass_manager_config.target),
ConditionalController(unroll, condition=_unroll_condition),
]

if optimization_level == 3:
optimization.append(_minimum_point_check)
elif optimization_level == 2:
optimization.append(
[
Collect2qBlocks(),
if (
pass_manager_config.basis_gates is not None
and len(pass_manager_config.basis_gates) > 0
):
_opt += [
ConsolidateBlocks(
basis_gates=pass_manager_config.basis_gates,
target=pass_manager_config.target,
Expand All @@ -648,8 +635,71 @@ def _unroll_condition(property_set):
plugin_config=pass_manager_config.unitary_synthesis_plugin_config,
target=pass_manager_config.target,
),
Optimize1qGatesDecomposition(
basis=pass_manager_config.basis_gates, target=pass_manager_config.target
),
]
)

_opt += [CommutativeCancellation(target=pass_manager_config.target)]

def _opt_control(property_set):
return not property_set["optimization_loop_minimum_point"]

else:
raise TranspilerError(f"Invalid optimization_level: {optimization_level}")

if (
pass_manager_config.basis_gates is not None
and len(pass_manager_config.basis_gates) > 0
):
unroll = translation.to_flow_controller()

# Build nested Flow controllers
def _unroll_condition(property_set):
return not property_set["all_gates_in_basis"]

# Check if any gate is not in the basis, and if so, run unroll passes
_unroll_if_out_of_basis = [
GatesInBasis(
pass_manager_config.basis_gates, target=pass_manager_config.target
),
ConditionalController(unroll, condition=_unroll_condition),
]
else:
_unroll_if_out_of_basis = []

if optimization_level == 3:
optimization.append(_minimum_point_check)
elif optimization_level == 2:
if (
pass_manager_config.basis_gates is not None
and len(pass_manager_config.basis_gates) > 0
):
optimization.append(
[
Collect2qBlocks(),
ConsolidateBlocks(
basis_gates=pass_manager_config.basis_gates,
target=pass_manager_config.target,
approximation_degree=pass_manager_config.approximation_degree,
),
UnitarySynthesis(
pass_manager_config.basis_gates,
approximation_degree=pass_manager_config.approximation_degree,
coupling_map=pass_manager_config.coupling_map,
backend_props=pass_manager_config.backend_properties,
method=pass_manager_config.unitary_synthesis_method,
plugin_config=pass_manager_config.unitary_synthesis_plugin_config,
target=pass_manager_config.target,
),
]
)
else:
optimization.append(
[
Collect2qBlocks(),
]
)
optimization.append(_depth_check + _size_check)
else:
optimization.append(_depth_check + _size_check)
Expand All @@ -659,6 +709,8 @@ def _unroll_condition(property_set):
else _opt + _unroll_if_out_of_basis + _depth_check + _size_check
)
optimization.append(DoWhileController(opt_loop, do_while=_opt_control))
print("opt", optimization._tasks)

return optimization
else:
return None
Expand Down
5 changes: 5 additions & 0 deletions qiskit/transpiler/preset_passmanagers/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,11 @@ def generate_translation_passmanager(
Raises:
TranspilerError: If the ``method`` kwarg is not a valid value
"""
if basis_gates is None and (
target is None or (target is not None and len(target.operation_names) == 0)
):
return PassManager([])

if method == "translator":
unroll = [
# Use unitary synthesis for basis aware decomposition of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,8 @@ def generate_preset_pass_manager(
_skip_target = (
target is None
and backend is None
and (basis_gates is None or coupling_map is None or instruction_durations is not None)
# and (basis_gates is None or coupling_map is None or instruction_durations is not None)
and (instruction_durations is not None)
)

# Resolve loose constraints case-by-case against backend constraints.
Expand Down Expand Up @@ -344,7 +345,7 @@ def generate_preset_pass_manager(
timing_constraints = target.timing_constraints()
if backend_properties is None:
with warnings.catch_warnings():
# TODO this approach (target-to-properties) is going to be removed soon (1.3) in favor
# TODO: this approach (target-to-properties) is going to be removed soon (1.3) in favor
# of backend-to-target approach
# https://github.com/Qiskit/qiskit/pull/12850
warnings.filterwarnings(
Expand All @@ -360,6 +361,7 @@ def generate_preset_pass_manager(
approximation_degree = _parse_approximation_degree(approximation_degree)
seed_transpiler = _parse_seed_transpiler(seed_transpiler)

print("target", target)
pm_options = {
"target": target,
"basis_gates": basis_gates,
Expand Down Expand Up @@ -398,6 +400,8 @@ def generate_preset_pass_manager(
pm = level_3_pass_manager(pm_config)
else:
raise ValueError(f"Invalid optimization level {optimization_level}")

# print("all", pm._tasks)
return pm


Expand Down Expand Up @@ -481,6 +485,7 @@ def _parse_coupling_map(coupling_map, backend):
if isinstance(coupling_map, list) and all(
isinstance(i, list) and len(i) == 2 for i in coupling_map
):
print("here")
return CouplingMap(coupling_map)
else:
raise TranspilerError(
Expand Down
18 changes: 12 additions & 6 deletions qiskit/transpiler/preset_passmanagers/level0.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,12 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa
layout = None
routing = None

translation = plugin_manager.get_passmanager_stage(
"translation", translation_method, pass_manager_config, optimization_level=0
)
if basis_gates:
translation = plugin_manager.get_passmanager_stage(
"translation", translation_method, pass_manager_config, optimization_level=0
)
else:
translation = None

if (coupling_map and not coupling_map.is_symmetric) or (
target is not None and target.get_non_global_operation_names(strict_direction=True)
Expand All @@ -97,9 +100,12 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa
init = plugin_manager.get_passmanager_stage(
"init", init_method, pass_manager_config, optimization_level=0
)
optimization = plugin_manager.get_passmanager_stage(
"optimization", optimization_method, pass_manager_config, optimization_level=0
)
if basis_gates or (target and not isinstance(target, FakeTarget)):
optimization = plugin_manager.get_passmanager_stage(
"optimization", optimization_method, pass_manager_config, optimization_level=0
)
else:
optimization = None

return StagedPassManager(
pre_init=pre_init,
Expand Down
Loading

0 comments on commit 8eea4cf

Please sign in to comment.