diff --git a/qiskit/compiler/transpile.py b/qiskit/compiler/transpile.py index e761e8127082..b10cfe331ae0 100644 --- a/qiskit/compiler/transpile.py +++ b/qiskit/compiler/transpile.py @@ -15,13 +15,17 @@ """Circuit transpile function""" from qiskit.transpiler import Layout, CouplingMap from qiskit.tools.parallel import parallel_map -from qiskit.transpiler.transpile_config import TranspileConfig -from qiskit.transpiler.transpile_circuit import transpile_circuit +from qiskit.transpiler.pass_manager_config import PassManagerConfig from qiskit.pulse import Schedule from qiskit.circuit.quantumregister import Qubit from qiskit import user_config from qiskit.transpiler.exceptions import TranspilerError from qiskit.converters import isinstanceint, isinstancelist +from qiskit.transpiler.passes.basis.ms_basis_decomposer import MSBasisDecomposer +from qiskit.transpiler.preset_passmanagers import (level_0_pass_manager, + level_1_pass_manager, + level_2_pass_manager, + level_3_pass_manager) def transpile(circuits, @@ -176,14 +180,15 @@ def callback_func(**kwargs): config = user_config.get_config() optimization_level = config.get('transpile_optimization_level', None) - # Get TranspileConfig(s) to configure the circuit transpilation job(s) + # Get transpile_args to configure the circuit transpilation job(s) circuits = circuits if isinstance(circuits, list) else [circuits] - transpile_configs = _parse_transpile_args(circuits, backend, basis_gates, coupling_map, - backend_properties, initial_layout, - seed_transpiler, optimization_level, - pass_manager, callback, output_name) + transpile_args = _parse_transpile_args(circuits, backend, basis_gates, coupling_map, + backend_properties, initial_layout, + seed_transpiler, optimization_level, + pass_manager, callback, output_name) # Check circuit width against number of qubits in coupling_map(s) - coupling_maps_list = list(config.coupling_map for config in transpile_configs) + coupling_maps_list = list(config['pass_manager_config'].coupling_map for config in + transpile_args) for circuit, parsed_coupling_map in zip(circuits, coupling_maps_list): # If coupling_map is not None or n_qubits == 1 n_qubits = len(circuit.qubits) @@ -201,28 +206,72 @@ def callback_func(**kwargs): 'is greater than maximum ({}) '.format(max_qubits) + 'in the coupling_map') # Transpile circuits in parallel - circuits = parallel_map(_transpile_circuit, list(zip(circuits, transpile_configs))) + circuits = parallel_map(_transpile_circuit, list(zip(circuits, transpile_args))) if len(circuits) == 1: return circuits[0] return circuits -# FIXME: This is a helper function because of parallel tools. def _transpile_circuit(circuit_config_tuple): """Select a PassManager and run a single circuit through it. - Args: circuit_config_tuple (tuple): circuit (QuantumCircuit): circuit to transpile - transpile_config (TranspileConfig): configuration dictating how to transpile - + transpile_config (dict): configuration dictating how to transpile. The + dictionary has the following format: + {'optimization_level': int, + 'pass_manager': PassManager, + 'output_name': string, + 'callback': callable, + 'pass_manager_config': PassManagerConfig} Returns: QuantumCircuit: transpiled circuit + Raises: + TranspilerError: if transpile_config is not valid or transpilation incurs error """ circuit, transpile_config = circuit_config_tuple - return transpile_circuit(circuit, transpile_config) + pass_manager_config = transpile_config['pass_manager_config'] + + # Workaround for ion trap support: If basis gates includes + # Mølmer-Sørensen (rxx) and the circuit includes gates outside the basis, + # first unroll to u3, cx, then run MSBasisDecomposer to target basis. + basic_insts = ['measure', 'reset', 'barrier', 'snapshot'] + device_insts = set(pass_manager_config.basis_gates).union(basic_insts) + ms_basis_swap = None + if 'rxx' in pass_manager_config.basis_gates and \ + not device_insts >= circuit.count_ops().keys(): + ms_basis_swap = pass_manager_config.basis_gates + pass_manager_config.basis_gates = list( + set(['u3', 'cx']).union(pass_manager_config.basis_gates)) + + if transpile_config['pass_manager'] is not None: + # either the pass manager is already selected... + pass_manager = transpile_config['pass_manager'] + else: + # or we choose an appropriate one based on desired optimization level (default: level 1) + if transpile_config['optimization_level'] is not None: + level = transpile_config['optimization_level'] + else: + level = 1 + + if level == 0: + pass_manager = level_0_pass_manager(pass_manager_config) + elif level == 1: + pass_manager = level_1_pass_manager(pass_manager_config) + elif level == 2: + pass_manager = level_2_pass_manager(pass_manager_config) + elif level == 3: + pass_manager = level_3_pass_manager(pass_manager_config) + else: + raise TranspilerError("optimization_level can range from 0 to 3.") + + if ms_basis_swap is not None: + pass_manager.append(MSBasisDecomposer(ms_basis_swap)) + + return pass_manager.run(circuit, callback=transpile_config['callback'], + output_name=transpile_config['output_name']) def _parse_transpile_args(circuits, backend, @@ -238,45 +287,38 @@ def _parse_transpile_args(circuits, backend, arg has more priority than the arg set by backend). Returns: - list[TranspileConfig]: a transpile config for each circuit, which is a standardized - object that configures the transpiler and determines the pass manager to use. + list[dicts]: a list of transpile parameters. """ # Each arg could be single or a list. If list, it must be the same size as # number of circuits. If single, duplicate to create a list of that size. num_circuits = len(circuits) basis_gates = _parse_basis_gates(basis_gates, backend, circuits) - coupling_map = _parse_coupling_map(coupling_map, backend, num_circuits) - backend_properties = _parse_backend_properties(backend_properties, backend, num_circuits) - initial_layout = _parse_initial_layout(initial_layout, circuits) - seed_transpiler = _parse_seed_transpiler(seed_transpiler, num_circuits) - optimization_level = _parse_optimization_level(optimization_level, num_circuits) - pass_manager = _parse_pass_manager(pass_manager, num_circuits) - output_name = _parse_output_name(output_name, circuits) + callback = _parse_callback(callback, num_circuits) - transpile_configs = [] + list_transpile_args = [] for args in zip(basis_gates, coupling_map, backend_properties, initial_layout, seed_transpiler, optimization_level, - pass_manager, output_name): - transpile_config = TranspileConfig(basis_gates=args[0], - coupling_map=args[1], - backend_properties=args[2], - initial_layout=args[3], - seed_transpiler=args[4], - optimization_level=args[5], - pass_manager=args[6], - callback=callback, - output_name=args[7]) - transpile_configs.append(transpile_config) + pass_manager, output_name, callback): + transpile_args = {'pass_manager_config': PassManagerConfig(basis_gates=args[0], + coupling_map=args[1], + backend_properties=args[2], + initial_layout=args[3], + seed_transpiler=args[4]), + 'optimization_level': args[5], + 'pass_manager': args[6], + 'output_name': args[7], + 'callback': args[8]} + list_transpile_args.append(transpile_args) - return transpile_configs + return list_transpile_args def _parse_basis_gates(basis_gates, backend, circuits): @@ -303,14 +345,19 @@ def _parse_coupling_map(coupling_map, backend, num_circuits): # try getting coupling_map from user, else backend if coupling_map is None: if getattr(backend, 'configuration', None): - coupling_map = getattr(backend.configuration(), 'coupling_map', None) + configuration = backend.configuration() + if hasattr(configuration, 'coupling_map') and configuration.coupling_map: + coupling_map = CouplingMap(configuration.coupling_map) + # coupling_map could be None, or a list of lists, e.g. [[0, 1], [2, 1]] if coupling_map is None or isinstance(coupling_map, CouplingMap): coupling_map = [coupling_map] * num_circuits elif isinstance(coupling_map, list) and all(isinstance(i, list) and len(i) == 2 for i in coupling_map): coupling_map = [coupling_map] * num_circuits + coupling_map = [CouplingMap(cm) if isinstance(cm, list) else cm for cm in coupling_map] + return coupling_map @@ -372,6 +419,12 @@ def _parse_pass_manager(pass_manager, num_circuits): return pass_manager +def _parse_callback(callback, num_circuits): + if not isinstance(callback, list): + callback = [callback] * num_circuits + return callback + + def _parse_output_name(output_name, circuits): # naming and returning circuits # output_name could be either a string or a list diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index 9a11eb4d8aba..4eb221197dc2 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -19,14 +19,6 @@ .. currentmodule:: qiskit.transpiler -Circuit Transpilation -===================== - -.. autosummary:: - :toctree: ../stubs/ - - transpile_circuit - Pass Managment ============== @@ -73,4 +65,3 @@ from .basepasses import AnalysisPass, TransformationPass from .coupling import CouplingMap from .layout import Layout -from .transpile_circuit import transpile_circuit diff --git a/qiskit/transpiler/models.py b/qiskit/transpiler/models.py index 05121384312b..f3489f8da7be 100644 --- a/qiskit/transpiler/models.py +++ b/qiskit/transpiler/models.py @@ -12,13 +12,13 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Models for TranspileConfig and RunConfig.""" +"""Models for PassManagerConfig and RunConfig.""" from qiskit.validation import BaseSchema -class TranspileConfigSchema(BaseSchema): - """Schema for TranspileConfig.""" +class PassManagerConfigSchema(BaseSchema): + """Schema for PassManagerConfig.""" # Required properties. diff --git a/qiskit/transpiler/pass_manager_config.py b/qiskit/transpiler/pass_manager_config.py new file mode 100644 index 000000000000..ff4f6b3a2a00 --- /dev/null +++ b/qiskit/transpiler/pass_manager_config.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 2019. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Models for PassManagerConfig and its related components.""" + +from qiskit.transpiler.models import PassManagerConfigSchema +from qiskit.validation import BaseModel, bind_schema + + +@bind_schema(PassManagerConfigSchema) +class PassManagerConfig(BaseModel): + """Model for PassManagerConfig. + + Please note that this class only describes the required fields. For the + full description of the model, please check ``PassManagerConfigSchema``. + + Attributes: + initial_layout (Layout): Initial position of virtual qubits on physical qubits. + basis_gates (list): List of basis gate names to unroll to. + coupling_map (CouplingMap): Directed graph represented a coupling map. + backend_properties (BackendProperties): Properties returned by a backend, including + information on gate errors, readout errors, qubit coherence times, etc. + seed_transpiler (int): Sets random seed for the stochastic parts of the transpiler. + """ + + def __init__(self, + initial_layout=None, + basis_gates=None, + coupling_map=None, + backend_properties=None, + seed_transpiler=None, + **kwargs): + super().__init__(initial_layout=initial_layout, + basis_gates=basis_gates, + coupling_map=coupling_map, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + **kwargs) diff --git a/qiskit/transpiler/passmanager.py b/qiskit/transpiler/passmanager.py index b6597d413433..a0d87cdc1203 100644 --- a/qiskit/transpiler/passmanager.py +++ b/qiskit/transpiler/passmanager.py @@ -14,6 +14,7 @@ """PassManager class for the transpiler.""" +import warnings import dill from qiskit.visualization import pass_manager_drawer @@ -35,40 +36,22 @@ def __init__(self, passes=None, max_iteration=1000, callback=None): to be added to the pass manager schedule. The default is None. max_iteration (int): The schedule looping iterates until the condition is met or until max_iteration is reached. - callback (func): A callback function that will be called after each - pass execution. The function will be called with 5 keyword - arguments:: - pass_ (Pass): the pass being run - dag (DAGCircuit): the dag output of the pass - time (float): the time to execute the pass - property_set (PropertySet): the property set - count (int): the index for the pass execution - - The exact arguments pass expose the internals of the pass - manager and are subject to change as the pass manager internals - change. If you intend to reuse a callback function over - multiple releases be sure to check that the arguments being - passed are the same. - - To use the callback feature you define a function that will - take in kwargs dict and access the variables. For example:: - - def callback_func(**kwargs): - pass_ = kwargs['pass_'] - dag = kwargs['dag'] - time = kwargs['time'] - property_set = kwargs['property_set'] - count = kwargs['count'] - ... + callback (func): DEPRECATED - A callback function that will be called after each + pass execution. + """ + self.callback = None - PassManager(callback=callback_func) + if callback: + warnings.warn("Setting a callback at construction time is being deprecated in favor of" + "PassManager.run(..., callback=callback,...)", DeprecationWarning, 2) + self.callback = callback + # the pass manager's schedule of passes, including any control-flow. + # Populated via PassManager.append(). - """ self._pass_sets = [] if passes is not None: self.append(passes) self.max_iteration = max_iteration - self.callback = callback self.property_set = None def append(self, passes, max_iteration=None, **flow_controller_conditions): @@ -141,9 +124,7 @@ def __len__(self): return len(self._pass_sets) def __getitem__(self, index): - max_iteration = self.max_iteration - call_back = self.callback - new_passmanager = PassManager(max_iteration=max_iteration, callback=call_back) + new_passmanager = PassManager(max_iteration=self.max_iteration, callback=self.callback) _pass_sets = self._pass_sets[index] if isinstance(_pass_sets, dict): _pass_sets = [_pass_sets] @@ -152,16 +133,13 @@ def __getitem__(self, index): def __add__(self, other): if isinstance(other, PassManager): - max_iteration = self.max_iteration - call_back = self.callback - new_passmanager = PassManager(max_iteration=max_iteration, callback=call_back) + new_passmanager = PassManager(max_iteration=self.max_iteration, callback=self.callback) new_passmanager._pass_sets = self._pass_sets + other._pass_sets return new_passmanager else: try: - max_iteration = self.max_iteration - call_back = self.callback - new_passmanager = PassManager(max_iteration=max_iteration, callback=call_back) + new_passmanager = PassManager(max_iteration=self.max_iteration, + callback=self.callback) new_passmanager._pass_sets += self._pass_sets new_passmanager.append(other) return new_passmanager @@ -179,23 +157,49 @@ def _normalize_passes(passes): raise TranspilerError('%s is not a pass instance' % pass_.__class__) return passes - def run(self, circuits): + def run(self, circuits, output_name=None, callback=None): """Run all the passes on circuit or circuits Args: circuits (QuantumCircuit or list[QuantumCircuit]): circuit(s) to - transform via all the registered passes. + transform via all the registered passes. + output_name (str): The output circuit name. If not given, the same as the + input circuit + callback (func): A callback function that will be called after each + pass execution. The function will be called with 5 keyword + arguments:: + pass_ (Pass): the pass being run + dag (DAGCircuit): the dag output of the pass + time (float): the time to execute the pass + property_set (PropertySet): the property set + count (int): the index for the pass execution + + The exact arguments pass expose the internals of the pass + manager and are subject to change as the pass manager internals + change. If you intend to reuse a callback function over + multiple releases be sure to check that the arguments being + passed are the same. + To use the callback feature you define a function that will + take in kwargs dict and access the variables. For example:: + + def callback_func(**kwargs): + pass_ = kwargs['pass_'] + dag = kwargs['dag'] + time = kwargs['time'] + property_set = kwargs['property_set'] + count = kwargs['count'] + ... Returns: QuantumCircuit or list[QuantumCircuit]: Transformed circuit(s). """ if isinstance(circuits, QuantumCircuit): - return self._run_single_circuit(circuits) + return self._run_single_circuit(circuits, output_name, callback) else: - return self._run_several_circuits(circuits) + return self._run_several_circuits(circuits, output_name, callback) def _create_running_passmanager(self): - running_passmanager = RunningPassManager(self.max_iteration, self.callback) + running_passmanager = RunningPassManager(self.max_iteration) for pass_set in self._pass_sets: running_passmanager.append(pass_set['passes'], **pass_set['flow_controllers']) return running_passmanager @@ -207,29 +211,61 @@ def _in_parallel(circuit, pm_dill=None): result = running_passmanager.run(circuit) return result - def _run_several_circuits(self, circuits): + def _run_several_circuits(self, circuits, output_name=None, callback=None): """Run all the passes on each of the circuits in the circuits list - - Args: - circuits (list[QuantumCircuit]): circuit to transform via all the registered passes - Returns: list[QuantumCircuit]: Transformed circuits. """ + # TODO support for List(output_name) and List(callback) + del output_name + del callback + return parallel_map(PassManager._in_parallel, circuits, task_kwargs={'pm_dill': dill.dumps(self)}) - def _run_single_circuit(self, circuit): + def _run_single_circuit(self, circuit, output_name=None, callback=None): """Run all the passes on a QuantumCircuit Args: circuit (QuantumCircuit): circuit to transform via all the registered passes + output_name (str): The output circuit name. If not given, the same as the + input circuit + callback (func): A callback function that will be called after each + pass execution. The function will be called with 5 keyword + arguments: + pass_ (Pass): the pass being run + dag (DAGCircuit): the dag output of the pass + time (float): the time to execute the pass + property_set (PropertySet): the property set + count (int): the index for the pass execution + + The exact arguments pass expose the internals of the pass + manager and are subject to change as the pass manager internals + change. If you intend to reuse a callback function over + multiple releases be sure to check that the arguments being + passed are the same. + + To use the callback feature you define a function that will + take in kwargs dict and access the variables. For example:: + + def callback_func(**kwargs): + pass_ = kwargs['pass_'] + dag = kwargs['dag'] + time = kwargs['time'] + property_set = kwargs['property_set'] + count = kwargs['count'] + ... + + PassManager(callback=callback_func) + Returns: QuantumCircuit: Transformed circuit. """ running_passmanager = self._create_running_passmanager() - result = running_passmanager.run(circuit) + if callback is None and self.callback: # TODO to remove with __init__(callback) + callback = self.callback + result = running_passmanager.run(circuit, output_name=output_name, callback=callback) self.property_set = running_passmanager.property_set return result diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index 0135d88bc45c..62087570b5df 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -35,7 +35,7 @@ from qiskit.transpiler.passes import CheckCXDirection -def level_0_pass_manager(transpile_config): +def level_0_pass_manager(pass_manager_config): """ Level 0 pass manager: no explicit optimization other than mapping to backend. @@ -48,15 +48,15 @@ def level_0_pass_manager(transpile_config): stages are done. Args: - transpile_config (TranspileConfig) + pass_manager_config (PassManagerConfig) Returns: PassManager: a level 0 pass manager. """ - basis_gates = transpile_config.basis_gates - coupling_map = transpile_config.coupling_map - initial_layout = transpile_config.initial_layout - seed_transpiler = transpile_config.seed_transpiler + basis_gates = pass_manager_config.basis_gates + coupling_map = pass_manager_config.coupling_map + initial_layout = pass_manager_config.initial_layout + seed_transpiler = pass_manager_config.seed_transpiler # 1. Use trivial layout if no layout given _given_layout = SetLayout(initial_layout) diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index 8eb8e7245005..4a5c8f0d9f7e 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -42,7 +42,7 @@ from qiskit.transpiler.passes import DenseLayout -def level_1_pass_manager(transpile_config): +def level_1_pass_manager(pass_manager_config): """ Level 1 pass manager: light optimization by simple adjacent gate collapsing @@ -57,16 +57,16 @@ def level_1_pass_manager(transpile_config): stages are done. Args: - transpile_config (TranspileConfig) + pass_manager_config (PassManagerConfig) Returns: PassManager: a level 1 pass manager. """ - basis_gates = transpile_config.basis_gates - coupling_map = transpile_config.coupling_map - initial_layout = transpile_config.initial_layout - seed_transpiler = transpile_config.seed_transpiler - backend_properties = getattr(transpile_config, 'backend_properties', None) + basis_gates = pass_manager_config.basis_gates + coupling_map = pass_manager_config.coupling_map + initial_layout = pass_manager_config.initial_layout + seed_transpiler = pass_manager_config.seed_transpiler + backend_properties = pass_manager_config.backend_properties # 1. Use trivial layout if no layout given _set_initial_layout = SetLayout(initial_layout) diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index a3457529298a..8f28eff81484 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -42,7 +42,7 @@ from qiskit.transpiler.passes import CheckCXDirection -def level_2_pass_manager(transpile_config): +def level_2_pass_manager(pass_manager_config): """ Level 2 pass manager: medium optimization by noise adaptive qubit mapping and gate cancellation using commutativity rules. @@ -59,16 +59,16 @@ def level_2_pass_manager(transpile_config): stages are done. Args: - transpile_config (TranspileConfig) + pass_manager_config (PassManagerConfig) Returns: PassManager: a level 2 pass manager. """ - basis_gates = transpile_config.basis_gates - coupling_map = transpile_config.coupling_map - initial_layout = transpile_config.initial_layout - seed_transpiler = transpile_config.seed_transpiler - backend_properties = transpile_config.backend_properties + basis_gates = pass_manager_config.basis_gates + coupling_map = pass_manager_config.coupling_map + initial_layout = pass_manager_config.initial_layout + seed_transpiler = pass_manager_config.seed_transpiler + backend_properties = pass_manager_config.backend_properties # 1. Unroll to the basis first, to prepare for noise-adaptive layout _unroll = Unroller(basis_gates) diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index 087a3d49a762..57f7f343ca4e 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -46,7 +46,7 @@ from qiskit.transpiler.passes import CheckCXDirection -def level_3_pass_manager(transpile_config): +def level_3_pass_manager(pass_manager_config): """ Level 3 pass manager: heavy optimization by noise adaptive qubit mapping and gate cancellation using commutativity rules and unitary synthesis. @@ -63,16 +63,16 @@ def level_3_pass_manager(transpile_config): stages are done. Args: - transpile_config (TranspileConfig) + pass_manager_config (PassManagerConfig) Returns: PassManager: a level 3 pass manager. """ - basis_gates = transpile_config.basis_gates - coupling_map = transpile_config.coupling_map - initial_layout = transpile_config.initial_layout - seed_transpiler = transpile_config.seed_transpiler - backend_properties = transpile_config.backend_properties + basis_gates = pass_manager_config.basis_gates + coupling_map = pass_manager_config.coupling_map + initial_layout = pass_manager_config.initial_layout + seed_transpiler = pass_manager_config.seed_transpiler + backend_properties = pass_manager_config.backend_properties # 1. Unroll to the basis first, to prepare for noise-adaptive layout _unroll = Unroller(basis_gates) diff --git a/qiskit/transpiler/runningpassmanager.py b/qiskit/transpiler/runningpassmanager.py index e80f77cc3949..6b5b3fe3876b 100644 --- a/qiskit/transpiler/runningpassmanager.py +++ b/qiskit/transpiler/runningpassmanager.py @@ -29,45 +29,17 @@ logger = logging.getLogger(__name__) -class RunningPassManager(): +class RunningPassManager: """A RunningPassManager is a running pass manager.""" - def __init__(self, max_iteration, callback): + def __init__(self, max_iteration): """Initialize an empty PassManager object (with no passes scheduled). Args: max_iteration (int): The schedule looping iterates until the condition is met or until max_iteration is reached. - callback (func): A callback function that will be called after each - pass execution. The function will be called with 5 keyword - arguments: - pass_ (Pass): the pass being run - dag (DAGCircuit): the dag output of the pass - time (float): the time to execute the pass - property_set (PropertySet): the property set - count (int): the index for the pass execution - - The exact arguments pass expose the internals of the pass - manager and are subject to change as the pass manager internals - change. If you intend to reuse a callback function over - multiple releases be sure to check that the arguments being - passed are the same. - - To use the callback feature you define a function that will - take in kwargs dict and access the variables. For example:: - - def callback_func(**kwargs): - pass_ = kwargs['pass_'] - dag = kwargs['dag'] - time = kwargs['time'] - property_set = kwargs['property_set'] - count = kwargs['count'] - ... - - PassManager(callback=callback_func) - """ - self.callback = callback + self.callback = None # the pass manager's schedule of passes, including any control-flow. # Populated via PassManager.append(). self.working_list = [] @@ -120,12 +92,14 @@ def _normalize_flow_controller(self, flow_controller): raise TranspilerError('The flow controller parameter %s is not callable' % name) return flow_controller - def run(self, circuit): + def run(self, circuit, output_name=None, callback=None): """Run all the passes on a QuantumCircuit Args: circuit (QuantumCircuit): circuit to transform via all the registered passes - + output_name (str): The output circuit name. If not given, the same as the + input circuit + callback (callable): A callback function that will be called after each pass execution. Returns: QuantumCircuit: Transformed circuit. """ @@ -133,12 +107,18 @@ def run(self, circuit): dag = circuit_to_dag(circuit) del circuit + if callback: + self.callback = callback + for passset in self.working_list: for pass_ in passset: dag = self._do_pass(pass_, dag, passset.options) circuit = dag_to_circuit(dag) - circuit.name = name + if output_name: + circuit.name = output_name + else: + circuit.name = name circuit._layout = self.property_set['layout'] return circuit diff --git a/qiskit/transpiler/transpile_circuit.py b/qiskit/transpiler/transpile_circuit.py deleted file mode 100644 index 28df2186ef4b..000000000000 --- a/qiskit/transpiler/transpile_circuit.py +++ /dev/null @@ -1,82 +0,0 @@ -# -*- coding: utf-8 -*- - -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Circuit transpile function""" - -from qiskit.transpiler.preset_passmanagers import (level_0_pass_manager, - level_1_pass_manager, - level_2_pass_manager, - level_3_pass_manager) -from qiskit.transpiler.passes.basis.ms_basis_decomposer import MSBasisDecomposer -from qiskit.transpiler.exceptions import TranspilerError - - -def transpile_circuit(circuit, transpile_config): - """Select a PassManager and run a single circuit through it. - - Args: - circuit (QuantumCircuit): circuit to transpile - transpile_config (TranspileConfig): configuration dictating how to transpile - - Returns: - QuantumCircuit: transpiled circuit - - Raises: - TranspilerError: if transpile_config is not valid or transpilation incurs error - """ - # either the pass manager is already selected... - if transpile_config.pass_manager is not None: - pass_manager = transpile_config.pass_manager - - # or we choose an appropriate one based on desired optimization level (default: level 1) - else: - # Workaround for ion trap support: If basis gates includes - # Mølmer-Sørensen (rxx) and the circuit includes gates outside the basis, - # first unroll to u3, cx, then run MSBasisDecomposer to target basis. - basic_insts = ['measure', 'reset', 'barrier', 'snapshot'] - device_insts = set(transpile_config.basis_gates).union(basic_insts) - - ms_basis_swap = None - if 'rxx' in transpile_config.basis_gates and \ - not device_insts >= circuit.count_ops().keys(): - ms_basis_swap = transpile_config.basis_gates - transpile_config.basis_gates = list(set(['u3', 'cx']).union( - transpile_config.basis_gates)) - - level = transpile_config.optimization_level - if level is None: - level = 1 - - if level == 0: - pass_manager = level_0_pass_manager(transpile_config) - elif level == 1: - pass_manager = level_1_pass_manager(transpile_config) - elif level == 2: - pass_manager = level_2_pass_manager(transpile_config) - elif level == 3: - pass_manager = level_3_pass_manager(transpile_config) - else: - raise TranspilerError("optimization_level can range from 0 to 3.") - - if ms_basis_swap is not None: - pass_manager.append(MSBasisDecomposer(ms_basis_swap)) - - # Set a callback on the pass manager there is one - if getattr(transpile_config, 'callback', None): - pass_manager.callback = transpile_config.callback - - out_circuit = pass_manager.run(circuit) - out_circuit.name = transpile_config.output_name - - return out_circuit diff --git a/qiskit/transpiler/transpile_config.py b/qiskit/transpiler/transpile_config.py deleted file mode 100644 index 82d6dc4ab2c3..000000000000 --- a/qiskit/transpiler/transpile_config.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- - -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Models for TranspileConfig and its related components.""" - -from qiskit.transpiler.models import TranspileConfigSchema -from qiskit.validation import BaseModel, bind_schema - - -@bind_schema(TranspileConfigSchema) -class TranspileConfig(BaseModel): - """Model for TranspileConfig. - - Please note that this class only describes the required fields. For the - full description of the model, please check ``TranspileConfigSchema``. - - Attributes: - optimization_level (int): a non-negative integer indicating the - optimization level. 0 means no transformation on the circuit. Higher - levels may produce more optimized circuits, but may take longer. - """ - def __init__(self, optimization_level, **kwargs): - self.optimization_level = optimization_level - super().__init__(**kwargs) diff --git a/qiskit/validation/base.py b/qiskit/validation/base.py index 8696e678a8ed..7020d7c4889a 100644 --- a/qiskit/validation/base.py +++ b/qiskit/validation/base.py @@ -234,8 +234,7 @@ def _decorated(self, **kwargs): do_validation = kwargs.pop('validate', True) if do_validation: try: - _ = self.shallow_schema._do_load(kwargs, - postprocess=False) + _ = self.shallow_schema._do_load(kwargs, postprocess=False) except ValidationError as ex: raise ModelValidationError( ex.messages, ex.field_name, ex.data, ex.valid_data, **ex.kwargs) from None diff --git a/releasenotes/notes/defaults_TranspileConfig-4109a90c278d46df.yaml b/releasenotes/notes/defaults_TranspileConfig-4109a90c278d46df.yaml new file mode 100644 index 000000000000..119448743bcb --- /dev/null +++ b/releasenotes/notes/defaults_TranspileConfig-4109a90c278d46df.yaml @@ -0,0 +1,18 @@ +--- +upgrade: + - | + The ``TranspileConfig`` was restructured to include only the information + needed to construct a ``PassManager`` and it was renamed to ``PassManagerConfig``. + That information is: + - ``initial_layout`` + - ``basis_gates`` + - ``coupling_map`` + - ``backend_properties`` + - ``seed_transpiler`` +deprecations: + - | + ``PassManager(..., callback=..., ...)`` parameter is deprecated. The parameter now + can be set at run-time, ``PassManager.run(..., callback=callback,...)``. + - | + The function ``transpile_circuit`` is deprecated. To transpile a circuit a + passmanager's ``run`` method should be called ``PassManager.run(cirucit, ...)``. \ No newline at end of file diff --git a/test/python/transpiler/test_passmanager.py b/test/python/transpiler/test_passmanager.py index cae6c927af89..4606297e6d82 100644 --- a/test/python/transpiler/test_passmanager.py +++ b/test/python/transpiler/test_passmanager.py @@ -56,10 +56,10 @@ def callback(**kwargs): out_dict['dag'] = copy.deepcopy(kwargs['dag']) calls.append(out_dict) - passmanager = PassManager(callback=callback) + passmanager = PassManager() passmanager.append(Unroller(['u2'])) passmanager.append(Optimize1qGates()) - transpile(circuit, FakeRueschlikon(), pass_manager=passmanager) + transpile(circuit, FakeRueschlikon(), pass_manager=passmanager, callback=callback) self.assertEqual(len(calls), 2) self.assertEqual(len(calls[0]), 5) self.assertEqual(calls[0]['count'], 0) @@ -100,9 +100,9 @@ def callback(**kwargs): out_dict['dag'] = copy.deepcopy(kwargs['dag']) calls.append(out_dict) - passmanager = PassManager(callback=callback) + passmanager = PassManager() passmanager.append(CommutativeCancellation()) - passmanager.run(circuit) + passmanager.run(circuit, callback=callback) self.assertEqual(len(calls), 2) self.assertEqual(len(calls[0]), 5) self.assertEqual(calls[0]['count'], 0) diff --git a/test/python/transpiler/test_passmanager_run.py b/test/python/transpiler/test_passmanager_run.py index 99077ff9b16e..8d2c9911ef18 100644 --- a/test/python/transpiler/test_passmanager_run.py +++ b/test/python/transpiler/test_passmanager_run.py @@ -20,7 +20,7 @@ from qiskit.test import QiskitTestCase from qiskit.test.mock import FakeMelbourne from qiskit.transpiler import Layout, CouplingMap -from qiskit.transpiler.transpile_config import TranspileConfig +from qiskit.transpiler.pass_manager_config import PassManagerConfig class TestPassManagerRun(QiskitTestCase): @@ -56,12 +56,11 @@ def test_default_pass_manager_single(self): basis_gates = FakeMelbourne().configuration().basis_gates initial_layout = [None, qr[0], qr[1], qr[2], None, qr[3]] - pass_manager = level_1_pass_manager(TranspileConfig( + pass_manager = level_1_pass_manager(PassManagerConfig( basis_gates=basis_gates, coupling_map=CouplingMap(coupling_map), initial_layout=Layout.from_qubit_list(initial_layout), - seed_transpiler=42, - optimization_level=1)) + seed_transpiler=42)) new_circuit = pass_manager.run(circuit) for gate, qargs, _ in new_circuit.data: @@ -103,12 +102,11 @@ def test_default_pass_manager_two(self): basis_gates = FakeMelbourne().configuration().basis_gates initial_layout = [None, qr[0], qr[1], qr[2], None, qr[3]] - pass_manager = level_1_pass_manager(TranspileConfig( + pass_manager = level_1_pass_manager(PassManagerConfig( basis_gates=basis_gates, coupling_map=CouplingMap(coupling_map), initial_layout=Layout.from_qubit_list(initial_layout), - seed_transpiler=42, - optimization_level=1)) + seed_transpiler=42)) new_circuits = pass_manager.run([circuit1, circuit2]) for new_circuit in new_circuits: