def test_two_qregs_to_a_single_creg(self): """Two measurements in different qregs to the same creg | q0:--[H]--[m]------ q0:--[H]--|--[m]------ | | | q1:--------|--[m]-- -> q1:-------|---|--[m]-- | | | | | c0:--------.---|--- c0:-----------.---|--- ------------.--- ---------------.--- """ qr0 = QuantumRegister(1, "q0") qr1 = QuantumRegister(1, "q1") cr0 = ClassicalRegister(2, "c0") circuit = QuantumCircuit(qr0, qr1, cr0) circuit.h(qr0) circuit.measure(qr0, cr0[0]) circuit.measure(qr1, cr0[1]) expected = QuantumCircuit(qr0, qr1, cr0) expected.h(qr0) expected.barrier(qr0, qr1) expected.measure(qr0, cr0[0]) expected.measure(qr1, cr0[1]) pass_ = BarrierBeforeFinalMeasurements() result = pass_.run(circuit_to_dag(circuit)) self.assertEqual(result, circuit_to_dag(expected))
def test_should_merge_with_smaller_duplicate_barrier(self): """If an equivalent barrier exists covering a subset of the qubits covered by the new barrier, it should be replaced. q:---|--[m]------------- q:---|--[m]------------- ---|---|---[m]-------- -> ---|---|---[m]-------- -------|----|---[m]--- ---|---|----|---[m]--- | | | | | | c:-------.----|----|---- c:-------.----|----|---- ------------.----|---- ------------.----|---- -----------------.---- -----------------.---- """ qr = QuantumRegister(3, 'q') cr = ClassicalRegister(3, 'c') circuit = QuantumCircuit(qr, cr) circuit.barrier(qr[0], qr[1]) circuit.measure(qr, cr) expected = QuantumCircuit(qr, cr) expected.barrier(qr) expected.measure(qr, cr) pass_ = BarrierBeforeFinalMeasurements() result = pass_.run(circuit_to_dag(circuit)) self.assertEqual(result, circuit_to_dag(expected))
def test_should_merge_with_larger_duplicate_barrier(self): """If a barrier exists and is stronger than the barrier to be inserted, preserve the existing barrier and do not insert a new barrier. q:---|--[m]--|------- q:---|--[m]-|------- ---|---|---|--[m]-- -> ---|---|--|--[m]-- ---|---|---|---|--- ---|---|--|---|--- | | | | c:-------.-------|--- c:-------.------|--- ---------------.--- --------------.--- ------------------- ------------------ """ qr = QuantumRegister(3, 'q') cr = ClassicalRegister(3, 'c') circuit = QuantumCircuit(qr, cr) circuit.barrier(qr) circuit.measure(qr[0], cr[0]) circuit.barrier(qr) circuit.measure(qr[1], cr[1]) expected = circuit pass_ = BarrierBeforeFinalMeasurements() result = pass_.run(circuit_to_dag(circuit)) self.assertEqual(result, circuit_to_dag(expected))
def test_preserve_barriers_for_measurement_ordering(self): """If the circuit has a barrier to enforce a measurement order, preserve it in the output. q:---[m]--|------- q:---|--[m]--|------- ----|---|--[m]-- -> ---|---|---|--[m]-- | | | | c:----.-------|--- c:-------.-------|--- ------------.--- ---------------.--- """ qr = QuantumRegister(2, 'q') cr = ClassicalRegister(2, 'c') circuit = QuantumCircuit(qr, cr) circuit.measure(qr[0], cr[0]) circuit.barrier(qr) circuit.measure(qr[1], cr[1]) expected = QuantumCircuit(qr, cr) expected.barrier(qr) expected.measure(qr[0], cr[0]) expected.barrier(qr) expected.measure(qr[1], cr[1]) pass_ = BarrierBeforeFinalMeasurements() result = pass_.run(circuit_to_dag(circuit)) self.assertEqual(result, circuit_to_dag(expected))
def test_measures_followed_by_barriers_should_be_final(self): """If a measurement is followed only by a barrier, insert the barrier before it. q:---[H]--|--[m]--|------- q:---[H]--|--[m]-|------- ---[H]--|---|---|--[m]-- -> ---[H]--|---|--|--[m]-- | | | | c:------------.-------|--- c:------------.------|--- --------------------.--- -------------------.--- """ qr = QuantumRegister(2, 'q') cr = ClassicalRegister(2, 'c') circuit = QuantumCircuit(qr, cr) circuit.h(qr) circuit.barrier(qr) circuit.measure(qr[0], cr[0]) circuit.barrier(qr) circuit.measure(qr[1], cr[1]) expected = QuantumCircuit(qr, cr) expected.h(qr) expected.barrier(qr) expected.measure(qr[0], cr[0]) expected.barrier(qr) expected.measure(qr[1], cr[1]) pass_ = BarrierBeforeFinalMeasurements() result = pass_.run(circuit_to_dag(circuit)) self.assertEqual(result, circuit_to_dag(expected))
def test_preserve_measure_for_conditional(self): """Test barrier is inserted after any measurements used for conditionals q0:--[H]--[m]------------ q0:--[H]--[m]--------------- | | q1:--------|--[ z]--[m]-- -> q1:--------|--[ z]--|--[m]-- | | | | | | c0:--------.--[=1]---|--- c0:--------.--[=1]------|--- | | c1:------------------.--- c1:---------------------.--- """ qr0 = QuantumRegister(1, 'q0') qr1 = QuantumRegister(1, 'q1') cr0 = ClassicalRegister(1, 'c0') cr1 = ClassicalRegister(1, 'c1') circuit = QuantumCircuit(qr0, qr1, cr0, cr1) circuit.h(qr0) circuit.measure(qr0, cr0) circuit.z(qr1).c_if(cr0, 1) circuit.measure(qr1, cr1) expected = QuantumCircuit(qr0, qr1, cr0, cr1) expected.h(qr0) expected.measure(qr0, cr0) expected.z(qr1).c_if(cr0, 1) expected.barrier(qr1) expected.measure(qr1, cr1) pass_ = BarrierBeforeFinalMeasurements() result = pass_.run(circuit_to_dag(circuit)) self.assertEqual(result, circuit_to_dag(expected))
def test_remove_barrier_in_different_qregs(self): """Two measurements in different qregs to the same creg q0:--|--[m]------ q0:---|--[m]------ | | | q1:--|---|--[m]-- -> q1:---|---|--[m]-- | | | | c0:------.---|--- c0:-------.---|--- ----------.--- -----------.--- """ qr0 = QuantumRegister(1, 'q0') qr1 = QuantumRegister(1, 'q1') cr0 = ClassicalRegister(2, 'c0') circuit = QuantumCircuit(qr0, qr1, cr0) circuit.barrier(qr0) circuit.barrier(qr1) circuit.measure(qr0, cr0[0]) circuit.measure(qr1, cr0[1]) expected = QuantumCircuit(qr0, qr1, cr0) expected.barrier(qr0, qr1) expected.measure(qr0, cr0[0]) expected.measure(qr1, cr0[1]) pass_ = BarrierBeforeFinalMeasurements() result = pass_.run(circuit_to_dag(circuit)) self.assertEqual(result, circuit_to_dag(expected))
def test_two_qregs(self): """Two measurements in different qregs to different cregs | q0:--[H]--[m]------ q0:--[H]--|--[m]------ | | | q1:--------|--[m]-- -> q1:-------|---|--[m]-- | | | | | c0:--------.---|--- c0:----------.---|--- | | c1:------------.--- c0:--------------.--- """ qr0 = QuantumRegister(1, 'q0') qr1 = QuantumRegister(1, 'q1') cr0 = ClassicalRegister(1, 'c0') cr1 = ClassicalRegister(1, 'c1') circuit = QuantumCircuit(qr0, qr1, cr0, cr1) circuit.h(qr0) circuit.measure(qr0, cr0) circuit.measure(qr1, cr1) expected = QuantumCircuit(qr0, qr1, cr0, cr1) expected.h(qr0) expected.barrier(qr0, qr1) expected.measure(qr0, cr0) expected.measure(qr1, cr1) pass_ = BarrierBeforeFinalMeasurements() result = pass_.run(circuit_to_dag(circuit)) self.assertEqual(result, circuit_to_dag(expected))
def test_single_measure_mix(self): """Two measurements, but only one is at the end | q0:--[m]--[H]--[m]-- q0:--[m]--[H]--|-[m]--- | | -> | | | c:---.---------.--- c:---.-----------.--- """ qr = QuantumRegister(1, 'q') cr = ClassicalRegister(1, 'c') circuit = QuantumCircuit(qr, cr) circuit.measure(qr, cr) circuit.h(qr) circuit.measure(qr, cr) expected = QuantumCircuit(qr, cr) expected.measure(qr, cr) expected.h(qr) expected.barrier(qr) expected.measure(qr, cr) pass_ = BarrierBeforeFinalMeasurements() result = pass_.run(circuit_to_dag(circuit)) self.assertEqual(result, circuit_to_dag(expected))
def default_pass_manager(transpile_config): """ The default pass manager that maps to the coupling map. Args: transpile_config (TranspileConfig) Returns: PassManager: A pass manager to map and optimize. """ basis_gates = transpile_config.basis_gates coupling_map = transpile_config.coupling_map initial_layout = transpile_config.initial_layout seed_transpiler = transpile_config.seed_transpiler pass_manager = PassManager() pass_manager.append(SetLayout(initial_layout)) pass_manager.append(Unroller(basis_gates)) # Use the trivial layout if no layout is found pass_manager.append( TrivialLayout(coupling_map), condition=lambda property_set: not property_set['layout']) # if the circuit and layout already satisfy the coupling_constraints, use that layout # otherwise layout on the most densely connected physical qubit subset pass_manager.append(CheckMap(coupling_map)) pass_manager.append( DenseLayout(coupling_map), condition=lambda property_set: not property_set['is_swap_mapped']) # Extend the the dag/layout with ancillas using the full coupling map pass_manager.append(FullAncillaAllocation(coupling_map)) pass_manager.append(EnlargeWithAncilla()) # Circuit must only contain 1- or 2-qubit interactions for swapper to work pass_manager.append(Unroll3qOrMore()) # Swap mapper pass_manager.append(BarrierBeforeFinalMeasurements()) pass_manager.append( LegacySwap(coupling_map, trials=20, seed=seed_transpiler)) # Expand swaps pass_manager.append(Decompose(SwapGate)) # Change CX directions pass_manager.append(CXDirection(coupling_map)) # Simplify single qubit gates and CXs simplification_passes = [ Optimize1qGates(), CXCancellation(), RemoveResetInZeroState() ] pass_manager.append( simplification_passes + [Depth(), FixedPoint('depth')], do_while=lambda property_set: not property_set['depth_fixed_point']) return pass_manager
def __init__(self, coupling_map): """Initialize a LookaheadSwap instance. Arguments: coupling_map (CouplingMap): CouplingMap of the target backend. """ super().__init__() self._coupling_map = coupling_map self.requires.append(BarrierBeforeFinalMeasurements())
def test_final_measurement_barrier_for_devices(self): """Verify BarrierBeforeFinalMeasurements pass is called in default pipeline for devices.""" circ = QuantumCircuit.from_qasm_file(self._get_resource_path('example.qasm', Path.QASMS)) layout = Layout.generate_trivial_layout(*circ.qregs) orig_pass = BarrierBeforeFinalMeasurements() with patch.object(BarrierBeforeFinalMeasurements, 'run', wraps=orig_pass.run) as mock_pass: transpile(circ, coupling_map=FakeRueschlikon().configuration().coupling_map, initial_layout=layout) self.assertTrue(mock_pass.called)
def test_single_measure(self): """A single measurement at the end | q:--[m]-- q:--|-[m]--- | -> | | c:---.--- c:-----.--- """ qr = QuantumRegister(1, "q") cr = ClassicalRegister(1, "c") circuit = QuantumCircuit(qr, cr) circuit.measure(qr, cr) expected = QuantumCircuit(qr, cr) expected.barrier(qr) expected.measure(qr, cr) pass_ = BarrierBeforeFinalMeasurements() result = pass_.run(circuit_to_dag(circuit)) self.assertEqual(result, circuit_to_dag(expected))
def test_ignore_single_measure(self): """Ignore single measurement because it is not at the end q:--[m]-[H]- q:--[m]-[H]- | -> | c:---.------ c:---.------ """ qr = QuantumRegister(1, 'q') cr = ClassicalRegister(1, 'c') circuit = QuantumCircuit(qr, cr) circuit.measure(qr, cr) circuit.h(qr[0]) expected = QuantumCircuit(qr, cr) expected.measure(qr, cr) expected.h(qr[0]) pass_ = BarrierBeforeFinalMeasurements() result = pass_.run(circuit_to_dag(circuit)) self.assertEqual(result, circuit_to_dag(expected))
def __init__(self, coupling_map, initial_layout=None): """ Maps a DAGCircuit onto a `coupling_map` using swap gates. Args: coupling_map (CouplingMap): Directed graph represented a coupling map. initial_layout (Layout): initial layout of qubits in mapping """ super().__init__() self.coupling_map = coupling_map self.initial_layout = initial_layout self.swap_gate = SwapGate self.requires.append(BarrierBeforeFinalMeasurements())
def test_handle_redundancy(self): """The pass is idempotent | | q:--|-[m]-- q:--|-[m]--- | | -> | | c:-----.--- c:-----.--- """ qr = QuantumRegister(1, 'q') cr = ClassicalRegister(1, 'c') circuit = QuantumCircuit(qr, cr) circuit.barrier(qr) circuit.measure(qr, cr) expected = QuantumCircuit(qr, cr) expected.barrier(qr) expected.measure(qr, cr) pass_ = BarrierBeforeFinalMeasurements() result = pass_.run(circuit_to_dag(circuit)) self.assertEqual(result, circuit_to_dag(expected))
def test_barrier_doesnt_reorder_gates(self): """ A barrier should not allow the reordering of gates, as pointed out in #2102 q:--[u1(0)]-----------[m]--------- q:--[u1(0)]------------|--[m]--------- --[u1(1)]------------|-[m]------ -> --[u1(1)]------------|---|-[m]------ --[u1(2)]-|----------|--|-[m]---- --[u1(2)]-|----------|---|--|-[m]---- ----------|-[u1(03)]-|--|--|-[m]- ----------|-[u1(03)]-|---|--|--|-[m]- | | | | | | | | c:---------------------.--|--|--|- c:--------------------------.--|--|--|- ------------------------.--|--|- -----------------------------.--|--|- ---------------------------.--|- --------------------------------.--|- ------------------------------.- -----------------------------------.- """ qr = QuantumRegister(4) cr = ClassicalRegister(4) circuit = QuantumCircuit(qr, cr) circuit.u1(0, qr[0]) circuit.u1(1, qr[1]) circuit.u1(2, qr[2]) circuit.barrier(qr[2], qr[3]) circuit.u1(3, qr[3]) test_circuit = circuit.copy() test_circuit.measure(qr, cr) # expected circuit is the same, just with a barrier before the measurements expected = circuit.copy() expected.barrier(qr) expected.measure(qr, cr) pass_ = BarrierBeforeFinalMeasurements() result = pass_.run(circuit_to_dag(test_circuit)) self.assertEqual(result, circuit_to_dag(expected))
def setUp(self): coupling = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6]] coupling_map = CouplingMap(couplinglist=coupling) basis_gates = ['u1', 'u3', 'u2', 'cx'] qr = QuantumRegister(7, 'q') layout = Layout({qr[i]: i for i in range(coupling_map.size())}) # Create a pass manager with a variety of passes and flow control structures self.pass_manager = PassManager() self.pass_manager.append(SetLayout(layout)) self.pass_manager.append(TrivialLayout(coupling_map), condition=lambda x: True) self.pass_manager.append(FullAncillaAllocation(coupling_map)) self.pass_manager.append(EnlargeWithAncilla()) self.pass_manager.append(Unroller(basis_gates)) self.pass_manager.append(CheckMap(coupling_map)) self.pass_manager.append(BarrierBeforeFinalMeasurements(), do_while=lambda x: False) self.pass_manager.append(CXDirection(coupling_map)) self.pass_manager.append(RemoveResetInZeroState())
def construct_passmanager(basis_gates, coupling_map, synthesis_fidelity, pulse_optimize): def _repeat_condition(property_set): return not property_set["depth_fixed_point"] seed = 2 _map = [SabreLayout(coupling_map, max_iterations=2, seed=seed)] _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] _unroll3q = Unroll3qOrMore() _swap_check = CheckMap(coupling_map) _swap = [ BarrierBeforeFinalMeasurements(), SabreSwap(coupling_map, heuristic="lookahead", seed=seed), ] _check_depth = [Depth(), FixedPoint("depth")] _optimize = [ Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis( basis_gates, synthesis_fidelity, coupling_map, pulse_optimize=pulse_optimize, natural_direction=True, ), Optimize1qGates(basis_gates), ] pm = PassManager() pm.append(_map) # map to hardware by inserting swaps pm.append(_embed) pm.append(_unroll3q) pm.append(_swap_check) pm.append(_swap) pm.append(_check_depth + _optimize, do_while=_repeat_condition ) # translate to & optimize over hardware native gates return pm
def __init__(self, coupling_map, initial_layout=None, trials=20, seed=None): """ Map a DAGCircuit onto a `coupling_map` using swap gates. If initial_layout is not None, we assume the input circuit has been layed out before running this pass, and that the layout process yields a DAG, coupling map, and layout with the following properties: 1. All three have the same number of qubits 2. The layout a bijection from the DAG qubits to the coupling map For this mapping pass, it may also be necessary that 3. The coupling map is a connected graph If these are not satisfied, the behavior is undefined. Args: coupling_map (CouplingMap): Directed graph representing a coupling map. initial_layout (Layout): initial layout of qubits in mapping trials (int): maximum number of iterations to attempt seed (int): seed for random number generator """ super().__init__() self.coupling_map = coupling_map self.input_layout = initial_layout if initial_layout is not None: self.initial_layout = initial_layout.copy() else: self.initial_layout = None self.trials = trials self.seed = seed self.requires.append(BarrierBeforeFinalMeasurements())
def level_0_pass_manager(transpile_config): """ Level 0 pass manager: no explicit optimization other than mapping to backend. This pass manager applies the user-given initial layout. If none is given, a trivial layout consisting of mapping the i-th virtual qubit to the i-th physical qubit is used. Any unused physical qubit is allocated as ancilla space. The pass manager then unrolls the circuit to the desired basis, and transforms the circuit to match the coupling map. Finally, extra resets are removed. Note: in simulators where coupling_map=None, only the unrolling and optimization stages are done. Args: transpile_config (TranspileConfig) 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 # 1. Use trivial layout if no layout given _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set['layout'] _choose_layout = TrivialLayout(coupling_map) # 2. Extend dag/layout with ancillas using the full coupling map _embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla()] # 3. Unroll to the basis _unroll = Unroller(basis_gates) # 4. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [ BarrierBeforeFinalMeasurements(), LegacySwap(coupling_map, trials=20, seed=seed_transpiler), Decompose(SwapGate) ] # 5. Fix any bad CX directions # _direction_check = CheckCXDirection(coupling_map) # TODO def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [CXDirection(coupling_map)] # 6. Remove zero-state reset _reset = RemoveResetInZeroState() pm0 = PassManager() if coupling_map: pm0.append(_given_layout) pm0.append(_choose_layout, condition=_choose_layout_condition) pm0.append(_embed) pm0.append(_unroll) if coupling_map: pm0.append(_swap_check) pm0.append(_swap, condition=_swap_condition) # pm0.append(_direction_check) # TODO pm0.append(_direction, condition=_direction_condition) pm0.append(_reset) return pm0
def level_3_pass_manager(transpile_config): """ Level 3 pass manager: heavy optimization by noise adaptive qubit mapping and gate cancellation using commutativity rules and unitary synthesis. This pass manager applies the user-given initial layout. If none is given, and device calibration information is available, the circuit is mapped to the qubits with best readouts and to CX gates with highest fidelity. Otherwise, a layout on the most densely connected qubits is used. The pass manager then transforms the circuit to match the coupling constraints. It is then unrolled to the basis, and any flipped cx directions are fixed. Finally, optimizations in the form of commutative gate cancellation, resynthesis of two-qubit unitary blocks, and redundant reset removal are performed. Note: in simulators where coupling_map=None, only the unrolling and optimization stages are done. Args: transpile_config (TranspileConfig) 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 # 1. Unroll to the basis first, to prepare for noise-adaptive layout _unroll = Unroller(basis_gates) # 2. Layout on good qubits if calibration info available, otherwise on dense links _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set['layout'] _choose_layout = DenseLayout(coupling_map) if backend_properties: _choose_layout = NoiseAdaptiveLayout(backend_properties) # 3. Extend dag/layout with ancillas using the full coupling map _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] # 4. Unroll to 1q or 2q gates, swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [ BarrierBeforeFinalMeasurements(), Unroll3qOrMore(), StochasticSwap(coupling_map, trials=20, seed=seed_transpiler), Decompose(SwapGate) ] _direction_check = [CheckCXDirection(coupling_map)] def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [CXDirection(coupling_map)] # 5. 1q rotation merge and commutative cancellation iteratively until no more change in depth _depth_check = [Depth(), FixedPoint('depth')] def _opt_control(property_set): return not property_set['depth_fixed_point'] _opt = [ RemoveResetInZeroState(), Collect2qBlocks(), ConsolidateBlocks(), Unroller(basis_gates), # unroll unitaries Optimize1qGates(), CommutativeCancellation(), OptimizeSwapBeforeMeasure(), RemoveDiagonalGatesBeforeMeasure() ] if coupling_map: _opt.append(CXDirection(coupling_map)) # if a coupling map has been provided, match coupling pm3 = PassManager() pm3.append(_unroll) if coupling_map: pm3.append(_given_layout) pm3.append(_choose_layout, condition=_choose_layout_condition) pm3.append(_embed) pm3.append(_swap_check) pm3.append(_swap, condition=_swap_condition) if not coupling_map.is_symmetric: pm3.append(_direction_check) pm3.append(_direction, condition=_direction_condition) pm3.append(_depth_check + _opt, do_while=_opt_control) return pm3
def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager: """Level 3 pass manager: heavy optimization by noise adaptive qubit mapping and gate cancellation using commutativity rules and unitary synthesis. This pass manager applies the user-given initial layout. If none is given, a search for a perfect layout (i.e. one that satisfies all 2-qubit interactions) is conducted. If no such layout is found, and device calibration information is available, the circuit is mapped to the qubits with best readouts and to CX gates with highest fidelity. The pass manager then transforms the circuit to match the coupling constraints. It is then unrolled to the basis, and any flipped cx directions are fixed. Finally, optimizations in the form of commutative gate cancellation, resynthesis of two-qubit unitary blocks, and redundant reset removal are performed. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 3 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "sabre" routing_method = pass_manager_config.routing_method or "sabre" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties approximation_degree = pass_manager_config.approximation_degree unitary_synthesis_method = pass_manager_config.unitary_synthesis_method timing_constraints = pass_manager_config.timing_constraints or TimingConstraints() unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config target = pass_manager_config.target # 1. Unroll to 1q or 2q gates _unroll3q = [ # Use unitary synthesis for basis aware decomposition of UnitaryGates UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, method=unitary_synthesis_method, plugin_config=unitary_synthesis_plugin_config, min_qubits=3, ), Unroll3qOrMore(), ] # 2. Layout on good qubits if calibration info available, otherwise on dense links _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): # layout hasn't been set yet return not property_set["layout"] def _csp_not_found_match(property_set): # If a layout hasn't been set by the time we run csp we need to run layout if property_set["layout"] is None: return True # if CSP layout stopped for any reason other than solution found we need # to run layout since CSP didn't converge. if ( property_set["CSPLayout_stop_reason"] is not None and property_set["CSPLayout_stop_reason"] != "solution found" ): return True return False # 2a. If layout method is not set, first try a trivial layout _choose_layout_0 = ( [] if pass_manager_config.layout_method else [ TrivialLayout(coupling_map), Layout2qDistance(coupling_map, property_name="trivial_layout_score"), ] ) # 2b. If trivial layout wasn't perfect (ie no swaps are needed) then try # using CSP layout to find a perfect layout _choose_layout_1 = ( [] if pass_manager_config.layout_method else CSPLayout(coupling_map, call_limit=10000, time_limit=60, seed=seed_transpiler) ) def _trivial_not_perfect(property_set): # Verify that a trivial layout is perfect. If trivial_layout_score > 0 # the layout is not perfect. The layout property set is unconditionally # set by trivial layout so we clear that before running CSP if property_set["trivial_layout_score"] is not None: if property_set["trivial_layout_score"] != 0: return True return False # 2c. if CSP didn't converge on a solution use layout_method (dense). if layout_method == "trivial": _choose_layout_2 = TrivialLayout(coupling_map) elif layout_method == "dense": _choose_layout_2 = DenseLayout(coupling_map, backend_properties) elif layout_method == "noise_adaptive": _choose_layout_2 = NoiseAdaptiveLayout(backend_properties) elif layout_method == "sabre": _choose_layout_2 = SabreLayout(coupling_map, max_iterations=4, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 3. Extend dag/layout with ancillas using the full coupling map _embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()] # 4. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set["is_swap_mapped"] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == "basic": _swap += [BasicSwap(coupling_map)] elif routing_method == "stochastic": _swap += [StochasticSwap(coupling_map, trials=200, seed=seed_transpiler)] elif routing_method == "lookahead": _swap += [LookaheadSwap(coupling_map, search_depth=5, search_width=6)] elif routing_method == "sabre": _swap += [SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler)] elif routing_method == "none": _swap += [ Error( msg=( "No routing method selected, but circuit is not routed to device. " "CheckMap Error: {check_map_msg}" ), action="raise", ) ] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. Unroll to the basis if translation_method == "unroller": _unroll = [Unroller(basis_gates)] elif translation_method == "translator": from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [ UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, backend_props=backend_properties, plugin_config=unitary_synthesis_plugin_config, method=unitary_synthesis_method, ), UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates, target), ] elif translation_method == "synthesis": _unroll = [ UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, backend_props=backend_properties, method=unitary_synthesis_method, plugin_config=unitary_synthesis_plugin_config, min_qubits=3, ), Unroll3qOrMore(), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, backend_props=backend_properties, method=unitary_synthesis_method, plugin_config=unitary_synthesis_plugin_config, ), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 6. Fix any CX direction mismatch _direction_check = [CheckGateDirection(coupling_map, target)] def _direction_condition(property_set): return not property_set["is_direction_mapped"] _direction = [GateDirection(coupling_map, target)] # 8. Optimize iteratively until no more change in depth. Removes useless gates # after reset and before measure, commutes gates and optimizes contiguous blocks. _depth_check = [Depth(), FixedPoint("depth")] def _opt_control(property_set): return not property_set["depth_fixed_point"] _reset = [RemoveResetInZeroState()] _meas = [OptimizeSwapBeforeMeasure(), RemoveDiagonalGatesBeforeMeasure()] _opt = [ Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis( basis_gates, approximation_degree=approximation_degree, coupling_map=coupling_map, backend_props=backend_properties, method=unitary_synthesis_method, plugin_config=unitary_synthesis_plugin_config, ), Optimize1qGatesDecomposition(basis_gates), CommutativeCancellation(), ] # 9. Unify all durations (either SI, or convert to dt if known) # Schedule the circuit only when scheduling_method is supplied _time_unit_setup = [ContainsInstruction("delay")] _time_unit_conversion = [TimeUnitConversion(instruction_durations)] def _contains_delay(property_set): return property_set["contains_delay"] _scheduling = [] if scheduling_method: _scheduling += _time_unit_conversion if scheduling_method in {"alap", "as_late_as_possible"}: _scheduling += [ALAPSchedule(instruction_durations)] elif scheduling_method in {"asap", "as_soon_as_possible"}: _scheduling += [ASAPSchedule(instruction_durations)] else: raise TranspilerError("Invalid scheduling method %s." % scheduling_method) # 10. Call measure alignment. Should come after scheduling. if ( timing_constraints.granularity != 1 or timing_constraints.min_length != 1 or timing_constraints.acquire_alignment != 1 ): _alignments = [ ValidatePulseGates( granularity=timing_constraints.granularity, min_length=timing_constraints.min_length ), AlignMeasures(alignment=timing_constraints.acquire_alignment), ] else: _alignments = [] # Build pass manager pm3 = PassManager() pm3.append(_unroll3q) pm3.append(_reset + _meas) if coupling_map or initial_layout: pm3.append(_given_layout) pm3.append(_choose_layout_0, condition=_choose_layout_condition) pm3.append(_choose_layout_1, condition=_trivial_not_perfect) pm3.append(_choose_layout_2, condition=_csp_not_found_match) pm3.append(_embed) pm3.append(_swap_check) pm3.append(_swap, condition=_swap_condition) pm3.append(_unroll) if (coupling_map and not coupling_map.is_symmetric) or ( target is not None and target.get_non_global_operation_names(strict_direction=True) ): pm3.append(_direction_check) pm3.append(_direction, condition=_direction_condition) pm3.append(_reset) # For transpiling to a target we need to run GateDirection in the # optimization loop to correct for incorrect directions that might be # inserted by UnitarySynthesis which is direction aware but only via # the coupling map which with a target doesn't give a full picture if target is not None: pm3.append(_depth_check + _opt + _unroll + _direction, do_while=_opt_control) else: pm3.append(_depth_check + _opt + _unroll, do_while=_opt_control) else: pm3.append(_reset) pm3.append(_depth_check + _opt + _unroll, do_while=_opt_control) if inst_map and inst_map.has_custom_gate(): pm3.append(PulseGates(inst_map=inst_map)) if scheduling_method: pm3.append(_scheduling) elif instruction_durations: pm3.append(_time_unit_setup) pm3.append(_time_unit_conversion, condition=_contains_delay) pm3.append(_alignments) return pm3
class TestCompiler(QiskitTestCase): """Qiskit Compiler Tests.""" def setUp(self): self.seed = 42 self.backend = BasicAer.get_backend("qasm_simulator") def test_compile(self): """Test Compiler. If all correct some should exists. """ backend = BasicAer.get_backend('qasm_simulator') qubit_reg = QuantumRegister(2, name='q') clbit_reg = ClassicalRegister(2, name='c') qc = QuantumCircuit(qubit_reg, clbit_reg, name="bell") qc.h(qubit_reg[0]) qc.cx(qubit_reg[0], qubit_reg[1]) qc.measure(qubit_reg, clbit_reg) circuits = transpile(qc, backend) self.assertIsInstance(circuits, QuantumCircuit) def test_compile_two(self): """Test Compiler. If all correct some should exists. """ backend = BasicAer.get_backend('qasm_simulator') qubit_reg = QuantumRegister(2) clbit_reg = ClassicalRegister(2) qubit_reg2 = QuantumRegister(2) clbit_reg2 = ClassicalRegister(2) qc = QuantumCircuit(qubit_reg, clbit_reg, name="bell") qc.h(qubit_reg[0]) qc.cx(qubit_reg[0], qubit_reg[1]) qc.measure(qubit_reg, clbit_reg) qc_extra = QuantumCircuit(qubit_reg, qubit_reg2, clbit_reg, clbit_reg2, name="extra") qc_extra.measure(qubit_reg, clbit_reg) circuits = transpile([qc, qc_extra], backend) self.assertIsInstance(circuits[0], QuantumCircuit) self.assertIsInstance(circuits[1], QuantumCircuit) def test_mapping_correction(self): """Test mapping works in previous failed case. """ backend = FakeRueschlikon() qr = QuantumRegister(name='qr', size=11) cr = ClassicalRegister(name='qc', size=11) circuit = QuantumCircuit(qr, cr) circuit.u3(1.564784764685993, -1.2378965763410095, 2.9746763177861713, qr[3]) circuit.u3(1.2269835563676523, 1.1932982847014162, -1.5597357740824318, qr[5]) circuit.cx(qr[5], qr[3]) circuit.u1(0.856768317675967, qr[3]) circuit.u3(-3.3911273825190915, 0.0, 0.0, qr[5]) circuit.cx(qr[3], qr[5]) circuit.u3(2.159209321625547, 0.0, 0.0, qr[5]) circuit.cx(qr[5], qr[3]) circuit.u3(0.30949966910232335, 1.1706201763833217, 1.738408691990081, qr[3]) circuit.u3(1.9630571407274755, -0.6818742967975088, 1.8336534616728195, qr[5]) circuit.u3(1.330181833806101, 0.6003162754946363, -3.181264980452862, qr[7]) circuit.u3(0.4885914820775024, 3.133297443244865, -2.794457469189904, qr[8]) circuit.cx(qr[8], qr[7]) circuit.u1(2.2196187596178616, qr[7]) circuit.u3(-3.152367609631023, 0.0, 0.0, qr[8]) circuit.cx(qr[7], qr[8]) circuit.u3(1.2646005789809263, 0.0, 0.0, qr[8]) circuit.cx(qr[8], qr[7]) circuit.u3(0.7517780502091939, 1.2828514296564781, 1.6781179605443775, qr[7]) circuit.u3(0.9267400575390405, 2.0526277839695153, 2.034202361069533, qr[8]) circuit.u3(2.550304293455634, 3.8250017126569698, -2.1351609599720054, qr[1]) circuit.u3(0.9566260876600556, -1.1147561503064538, 2.0571590492298797, qr[4]) circuit.cx(qr[4], qr[1]) circuit.u1(2.1899329069137394, qr[1]) circuit.u3(-1.8371715243173294, 0.0, 0.0, qr[4]) circuit.cx(qr[1], qr[4]) circuit.u3(0.4717053496327104, 0.0, 0.0, qr[4]) circuit.cx(qr[4], qr[1]) circuit.u3(2.3167620677708145, -1.2337330260253256, -0.5671322899563955, qr[1]) circuit.u3(1.0468499525240678, 0.8680750644809365, -1.4083720073192485, qr[4]) circuit.u3(2.4204244021892807, -2.211701932616922, 3.8297006565735883, qr[10]) circuit.u3(0.36660280497727255, 3.273119149343493, -1.8003362351299388, qr[6]) circuit.cx(qr[6], qr[10]) circuit.u1(1.067395863586385, qr[10]) circuit.u3(-0.7044917541291232, 0.0, 0.0, qr[6]) circuit.cx(qr[10], qr[6]) circuit.u3(2.1830003849921527, 0.0, 0.0, qr[6]) circuit.cx(qr[6], qr[10]) circuit.u3(2.1538343756723917, 2.2653381826084606, -3.550087952059485, qr[10]) circuit.u3(1.307627685019188, -0.44686656993522567, -2.3238098554327418, qr[6]) circuit.u3(2.2046797998462906, 0.9732961754855436, 1.8527865921467421, qr[9]) circuit.u3(2.1665254613904126, -1.281337664694577, -1.2424905413631209, qr[0]) circuit.cx(qr[0], qr[9]) circuit.u1(2.6209599970201007, qr[9]) circuit.u3(0.04680566321901303, 0.0, 0.0, qr[0]) circuit.cx(qr[9], qr[0]) circuit.u3(1.7728411151289603, 0.0, 0.0, qr[0]) circuit.cx(qr[0], qr[9]) circuit.u3(2.4866395967434443, 0.48684511243566697, -3.0069186877854728, qr[9]) circuit.u3(1.7369112924273789, -4.239660866163805, 1.0623389015296005, qr[0]) circuit.barrier(qr) circuit.measure(qr, cr) circuits = transpile(circuit, backend) self.assertIsInstance(circuits, QuantumCircuit) def test_transpiler_layout_from_intlist(self): """A list of ints gives layout to correctly map circuit. virtual physical q1_0 - 4 ---[H]--- q2_0 - 5 q2_1 - 6 ---[H]--- q3_0 - 8 q3_1 - 9 q3_2 - 10 ---[H]--- """ qr1 = QuantumRegister(1, 'qr1') qr2 = QuantumRegister(2, 'qr2') qr3 = QuantumRegister(3, 'qr3') qc = QuantumCircuit(qr1, qr2, qr3) qc.h(qr1[0]) qc.h(qr2[1]) qc.h(qr3[2]) layout = [4, 5, 6, 8, 9, 10] cmap = [[1, 0], [1, 2], [2, 3], [4, 3], [4, 10], [5, 4], [5, 6], [5, 9], [6, 8], [7, 8], [9, 8], [9, 10], [11, 3], [11, 10], [11, 12], [12, 2], [13, 1], [13, 12]] new_circ = transpile(qc, backend=None, coupling_map=cmap, basis_gates=['u2'], initial_layout=layout) mapped_qubits = [] for _, qargs, _ in new_circ.data: mapped_qubits.append(qargs[0][1]) self.assertEqual(mapped_qubits, [4, 6, 10]) def test_mapping_multi_qreg(self): """Test mapping works for multiple qregs. """ backend = FakeRueschlikon() qr = QuantumRegister(3, name='qr') qr2 = QuantumRegister(1, name='qr2') qr3 = QuantumRegister(4, name='qr3') cr = ClassicalRegister(3, name='cr') qc = QuantumCircuit(qr, qr2, qr3, cr) qc.h(qr[0]) qc.cx(qr[0], qr2[0]) qc.cx(qr[1], qr3[2]) qc.measure(qr, cr) circuits = transpile(qc, backend) self.assertIsInstance(circuits, QuantumCircuit) def test_mapping_already_satisfied(self): """Test compiler doesn't change circuit already matching backend coupling """ backend = FakeRueschlikon() qr = QuantumRegister(16) cr = ClassicalRegister(16) qc = QuantumCircuit(qr, cr) qc.h(qr[1]) qc.x(qr[2]) qc.x(qr[3]) qc.x(qr[4]) qc.cx(qr[1], qr[2]) qc.cx(qr[2], qr[3]) qc.cx(qr[3], qr[4]) qc.cx(qr[3], qr[14]) qc.measure(qr, cr) qobj = compile(qc, backend) compiled_ops = qobj.experiments[0].instructions original_cx_qubits = [[1, 2], [2, 3], [3, 4], [3, 14]] for operation in compiled_ops: if operation.name == 'cx': self.assertIn(operation.qubits, backend.configuration().coupling_map) self.assertIn(operation.qubits, original_cx_qubits) def test_compile_circuits_diff_registers(self): """Compile list of circuits with different qreg names. """ backend = FakeRueschlikon() circuits = [] for _ in range(2): qr = QuantumRegister(2) cr = ClassicalRegister(2) circuit = QuantumCircuit(qr, cr) circuit.h(qr[0]) circuit.cx(qr[0], qr[1]) circuit.measure(qr, cr) circuits.append(circuit) circuits = transpile(circuits, backend) self.assertIsInstance(circuits[0], QuantumCircuit) def test_example_multiple_compile(self): """Test a toy example compiling multiple circuits. Pass if the results are correct. """ backend = BasicAer.get_backend('qasm_simulator') coupling_map = [[0, 1], [0, 2], [1, 2], [3, 2], [3, 4], [4, 2]] qr = QuantumRegister(5) cr = ClassicalRegister(5) bell = QuantumCircuit(qr, cr) ghz = QuantumCircuit(qr, cr) # Create a GHZ state ghz.h(qr[0]) for i in range(4): ghz.cx(qr[i], qr[i + 1]) # Insert a barrier before measurement ghz.barrier() # Measure all of the qubits in the standard basis for i in range(5): ghz.measure(qr[i], cr[i]) # Create a Bell state bell.h(qr[0]) bell.cx(qr[0], qr[1]) bell.barrier() bell.measure(qr[0], cr[0]) bell.measure(qr[1], cr[1]) shots = 2048 bell_qobj = compile(bell, backend=backend, shots=shots, seed=10) ghz_qobj = compile(ghz, backend=backend, shots=shots, coupling_map=coupling_map, seed=10) bell_result = backend.run(bell_qobj).result() ghz_result = backend.run(ghz_qobj).result() threshold = 0.05 * shots counts_bell = bell_result.get_counts() target_bell = {'00000': shots / 2, '00011': shots / 2} self.assertDictAlmostEqual(counts_bell, target_bell, threshold) counts_ghz = ghz_result.get_counts() target_ghz = {'00000': shots / 2, '11111': shots / 2} self.assertDictAlmostEqual(counts_ghz, target_ghz, threshold) def test_compile_coupling_map(self): """Test compile_coupling_map. If all correct should return data with the same stats. The circuit may be different. """ backend = BasicAer.get_backend('qasm_simulator') qr = QuantumRegister(3, 'qr') cr = ClassicalRegister(3, 'cr') qc = QuantumCircuit(qr, cr) qc.h(qr[0]) qc.cx(qr[0], qr[1]) qc.cx(qr[0], qr[2]) qc.measure(qr[0], cr[0]) qc.measure(qr[1], cr[1]) qc.measure(qr[2], cr[2]) shots = 2048 coupling_map = [[0, 1], [1, 2]] # TODO (luciano): this initial_layout should be replaced by # {(qr, 0): 0, (qr, 1): 1, (qr, 2): 2} after 0.8 initial_layout = {("qr", 0): ("q", 0), ("qr", 1): ("q", 1), ("qr", 2): ("q", 2)} qobj = compile(qc, backend=backend, shots=shots, coupling_map=coupling_map, initial_layout=initial_layout, seed=88) job = backend.run(qobj) result = job.result() qasm_to_check = qc.qasm() self.assertEqual(len(qasm_to_check), 173) counts = result.get_counts(qc) target = {'000': shots / 2, '111': shots / 2} threshold = 0.05 * shots self.assertDictAlmostEqual(counts, target, threshold) def test_example_swap_bits(self): """Test a toy example swapping a set bit around. Uses the mapper. Pass if results are correct. """ backend = BasicAer.get_backend('qasm_simulator') coupling_map = [[0, 1], [0, 8], [1, 2], [1, 9], [2, 3], [2, 10], [3, 4], [3, 11], [4, 5], [4, 12], [5, 6], [5, 13], [6, 7], [6, 14], [7, 15], [8, 9], [9, 10], [10, 11], [11, 12], [12, 13], [13, 14], [14, 15]] n = 3 # make this at least 3 qr0 = QuantumRegister(n) qr1 = QuantumRegister(n) ans = ClassicalRegister(2 * n) qc = QuantumCircuit(qr0, qr1, ans) # Set the first bit of qr0 qc.x(qr0[0]) # Swap the set bit qc.swap(qr0[0], qr0[n - 1]) qc.swap(qr0[n - 1], qr1[n - 1]) qc.swap(qr1[n - 1], qr0[1]) qc.swap(qr0[1], qr1[1]) # Insert a barrier before measurement qc.barrier() # Measure all of the qubits in the standard basis for j in range(n): qc.measure(qr0[j], ans[j]) qc.measure(qr1[j], ans[j + n]) # First version: no mapping result = execute(qc, backend=backend, coupling_map=None, shots=1024, seed=14).result() self.assertEqual(result.get_counts(qc), {'010000': 1024}) # Second version: map to coupling graph result = execute(qc, backend=backend, coupling_map=coupling_map, shots=1024, seed=14).result() self.assertEqual(result.get_counts(qc), {'010000': 1024}) def test_parallel_compile(self): """Trigger parallel routines in compile. """ backend = FakeRueschlikon() qr = QuantumRegister(16) cr = ClassicalRegister(2) qc = QuantumCircuit(qr, cr) qc.h(qr[0]) for k in range(1, 15): qc.cx(qr[0], qr[k]) qc.measure(qr[5], cr[0]) qlist = [qc for k in range(10)] qobj = compile(qlist, backend=backend) self.assertEqual(len(qobj.experiments), 10) def test_compile_pass_manager(self): """Test compile with and without an empty pass manager.""" qr = QuantumRegister(2) cr = ClassicalRegister(2) qc = QuantumCircuit(qr, cr) qc.u1(3.14, qr[0]) qc.u2(3.14, 1.57, qr[0]) qc.barrier(qr) qc.measure(qr, cr) backend = BasicAer.get_backend('qasm_simulator') qrtrue = compile(qc, backend, seed=42) rtrue = backend.run(qrtrue).result() qrfalse = compile(qc, backend, seed=42, pass_manager=PassManager()) rfalse = backend.run(qrfalse).result() self.assertEqual(rtrue.get_counts(), rfalse.get_counts()) def test_compile_with_initial_layout(self): """Test compile with an initial layout. Regression test for #1711 """ qr = QuantumRegister(3) cr = ClassicalRegister(3) qc = QuantumCircuit(qr, cr) qc.cx(qr[2], qr[1]) qc.cx(qr[2], qr[0]) initial_layout = {0: (qr, 1), 2: (qr, 0), 15: (qr, 2)} backend = FakeRueschlikon() qobj = compile(qc, backend, seed=42, initial_layout=initial_layout) compiled_ops = qobj.experiments[0].instructions for operation in compiled_ops: if operation.name == 'cx': self.assertIn(operation.qubits, backend.configuration().coupling_map) self.assertIn(operation.qubits, [[15, 0], [15, 2]]) def test_mapper_overoptimization(self): """Check mapper overoptimization. The mapper should not change the semantics of the input. An overoptimization introduced issue #81: https://github.com/Qiskit/qiskit-terra/issues/81 """ # -X-.----- # -Y-+-S-.- # -Z-.-T-+- # ---+-H--- qr = QuantumRegister(4) cr = ClassicalRegister(4) circ = QuantumCircuit(qr, cr) circ.x(qr[0]) circ.y(qr[1]) circ.z(qr[2]) circ.cx(qr[0], qr[1]) circ.cx(qr[2], qr[3]) circ.s(qr[1]) circ.t(qr[2]) circ.h(qr[3]) circ.cx(qr[1], qr[2]) circ.measure(qr[0], cr[0]) circ.measure(qr[1], cr[1]) circ.measure(qr[2], cr[2]) circ.measure(qr[3], cr[3]) coupling_map = [[0, 2], [1, 2], [2, 3]] shots = 1000 result1 = execute(circ, backend=self.backend, coupling_map=coupling_map, seed=self.seed, shots=shots) count1 = result1.result().get_counts() result2 = execute(circ, backend=self.backend, coupling_map=None, seed=self.seed, shots=shots) count2 = result2.result().get_counts() self.assertDictAlmostEqual(count1, count2, shots * 0.02) def test_grovers_circuit(self): """Testing a circuit originated in the Grover algorithm""" shots = 1000 coupling_map = None # 6-qubit grovers qr = QuantumRegister(6) cr = ClassicalRegister(2) circuit = QuantumCircuit(qr, cr, name='grovers') circuit.h(qr[0]) circuit.h(qr[1]) circuit.x(qr[2]) circuit.x(qr[3]) circuit.x(qr[0]) circuit.cx(qr[0], qr[2]) circuit.x(qr[0]) circuit.cx(qr[1], qr[3]) circuit.ccx(qr[2], qr[3], qr[4]) circuit.cx(qr[1], qr[3]) circuit.x(qr[0]) circuit.cx(qr[0], qr[2]) circuit.x(qr[0]) circuit.x(qr[1]) circuit.x(qr[4]) circuit.h(qr[4]) circuit.ccx(qr[0], qr[1], qr[4]) circuit.h(qr[4]) circuit.x(qr[0]) circuit.x(qr[1]) circuit.x(qr[4]) circuit.h(qr[0]) circuit.h(qr[1]) circuit.h(qr[4]) circuit.barrier(qr) circuit.measure(qr[0], cr[0]) circuit.measure(qr[1], cr[1]) result = execute(circuit, backend=self.backend, coupling_map=coupling_map, seed=self.seed, shots=shots) counts = result.result().get_counts() expected_probs = {'00': 0.64, '01': 0.117, '10': 0.113, '11': 0.13} target = {key: shots * val for key, val in expected_probs.items()} threshold = 0.04 * shots self.assertDictAlmostEqual(counts, target, threshold) def test_math_domain_error(self): """Check for floating point errors. The math library operates over floats and introduces floating point errors that should be avoided. See: https://github.com/Qiskit/qiskit-terra/issues/111 """ qr = QuantumRegister(4) cr = ClassicalRegister(4) circ = QuantumCircuit(qr, cr) circ.y(qr[0]) circ.z(qr[2]) circ.h(qr[2]) circ.cx(qr[1], qr[0]) circ.y(qr[2]) circ.t(qr[2]) circ.z(qr[2]) circ.cx(qr[1], qr[2]) circ.measure(qr[0], cr[0]) circ.measure(qr[1], cr[1]) circ.measure(qr[2], cr[2]) circ.measure(qr[3], cr[3]) coupling_map = [[0, 2], [1, 2], [2, 3]] shots = 2000 job = execute(circ, backend=self.backend, coupling_map=coupling_map, seed=self.seed, shots=shots) counts = job.result().get_counts() target = {'0001': shots / 2, '0101': shots / 2} threshold = 0.04 * shots self.assertDictAlmostEqual(counts, target, threshold) def test_random_parameter_circuit(self): """Run a circuit with randomly generated parameters.""" circ = QuantumCircuit.from_qasm_file( self._get_resource_path('random_n5_d5.qasm', Path.QASMS)) coupling_map = [[0, 1], [1, 2], [2, 3], [3, 4]] shots = 1024 qobj = execute(circ, backend=self.backend, coupling_map=coupling_map, shots=shots, seed=self.seed) counts = qobj.result().get_counts() expected_probs = { '00000': 0.079239867254200971, '00001': 0.032859032998526903, '00010': 0.10752610993531816, '00011': 0.018818532050952699, '00100': 0.054830807251011054, '00101': 0.0034141983951965164, '00110': 0.041649309748902276, '00111': 0.039967731207338125, '01000': 0.10516937819949743, '01001': 0.026635620063700002, '01010': 0.0053475143548793866, '01011': 0.01940513314416064, '01100': 0.0044028405481225047, '01101': 0.057524760052126644, '01110': 0.010795354134597078, '01111': 0.026491296821535528, '10000': 0.094827455395274859, '10001': 0.0008373965072688836, '10010': 0.029082297894094441, '10011': 0.012386622870598416, '10100': 0.018739140061148799, '10101': 0.01367656456536896, '10110': 0.039184170706009248, '10111': 0.062339335178438288, '11000': 0.00293674365989009, '11001': 0.012848433960739968, '11010': 0.018472497159499782, '11011': 0.0088903691234912003, '11100': 0.031305389080034329, '11101': 0.0004788556283690458, '11110': 0.002232419390471667, '11111': 0.017684822659235985 } target = {key: shots * val for key, val in expected_probs.items()} threshold = 0.04 * shots self.assertDictAlmostEqual(counts, target, threshold) def test_already_mapped(self): """Circuit not remapped if matches topology. See: https://github.com/Qiskit/qiskit-terra/issues/342 """ backend = FakeRueschlikon() qr = QuantumRegister(16, 'qr') cr = ClassicalRegister(16, 'cr') qc = QuantumCircuit(qr, cr) qc.cx(qr[3], qr[14]) qc.cx(qr[5], qr[4]) qc.h(qr[9]) qc.cx(qr[9], qr[8]) qc.x(qr[11]) qc.cx(qr[3], qr[4]) qc.cx(qr[12], qr[11]) qc.cx(qr[13], qr[4]) for j in range(16): qc.measure(qr[j], cr[j]) qobj = compile(qc, backend=backend) cx_qubits = [x.qubits for x in qobj.experiments[0].instructions if x.name == "cx"] self.assertEqual(sorted(cx_qubits), [[3, 4], [3, 14], [5, 4], [9, 8], [12, 11], [13, 4]]) def test_yzy_zyz_cases(self): """yzy_to_zyz works in previously failed cases. See: https://github.com/Qiskit/qiskit-terra/issues/607 """ backend = FakeTenerife() qr = QuantumRegister(2) circ1 = QuantumCircuit(qr) circ1.cx(qr[0], qr[1]) circ1.rz(0.7, qr[1]) circ1.rx(1.570796, qr[1]) qobj1 = compile(circ1, backend) self.assertIsInstance(qobj1, QasmQobj) circ2 = QuantumCircuit(qr) circ2.y(qr[0]) circ2.h(qr[0]) circ2.s(qr[0]) circ2.h(qr[0]) qobj2 = compile(circ2, backend) self.assertIsInstance(qobj2, QasmQobj) def test_move_measurements(self): """Measurements applied AFTER swap mapping. """ backend = FakeRueschlikon() cmap = backend.configuration().coupling_map circ = QuantumCircuit.from_qasm_file( self._get_resource_path('move_measurements.qasm', Path.QASMS)) dag_circuit = circuit_to_dag(circ) lay = Layout({('qa', 0): ('q', 0), ('qa', 1): ('q', 1), ('qb', 0): ('q', 15), ('qb', 1): ('q', 2), ('qb', 2): ('q', 14), ('qN', 0): ('q', 3), ('qN', 1): ('q', 13), ('qN', 2): ('q', 4), ('qc', 0): ('q', 12), ('qNt', 0): ('q', 5), ('qNt', 1): ('q', 11), ('qt', 0): ('q', 6)}) out_dag = transpile_dag(dag_circuit, initial_layout=lay, coupling_map=cmap) meas_nodes = out_dag.named_nodes('measure') for meas_node in meas_nodes: is_last_measure = all([after_measure.type == 'out' for after_measure in out_dag.quantum_successors(meas_node)]) self.assertTrue(is_last_measure) def test_kak_decomposition(self): """Verify KAK decomposition for random Haar unitaries. """ for _ in range(100): unitary = random_unitary_matrix(4) with self.subTest(unitary=unitary): try: two_qubit_kak(unitary, verify_gate_sequence=True) except MapperError as ex: self.fail(str(ex)) barrier_pass = BarrierBeforeFinalMeasurements() @patch.object(BarrierBeforeFinalMeasurements, 'run', wraps=barrier_pass.run) def test_final_measurement_barrier_for_devices(self, mock_pass): """Verify BarrierBeforeFinalMeasurements pass is called in default pipeline for devices.""" circ = QuantumCircuit.from_qasm_file(self._get_resource_path('example.qasm', Path.QASMS)) dag_circuit = circuit_to_dag(circ) layout = Layout.generate_trivial_layout(*circ.qregs) transpile_dag(dag_circuit, coupling_map=FakeRueschlikon().configuration().coupling_map, initial_layout=layout) self.assertTrue(mock_pass.called) @patch.object(BarrierBeforeFinalMeasurements, 'run', wraps=barrier_pass.run) def test_final_measurement_barrier_for_simulators(self, mock_pass): """Verify BarrierBeforeFinalMeasurements pass is in default pipeline for simulators.""" circ = QuantumCircuit.from_qasm_file(self._get_resource_path('example.qasm', Path.QASMS)) dag_circuit = circuit_to_dag(circ) transpile_dag(dag_circuit) self.assertTrue(mock_pass.called) def test_optimize_to_nothing(self): """ Optimze gates up to fixed point in the default pipeline See https://github.com/Qiskit/qiskit-terra/issues/2035 """ qr = QuantumRegister(2) circ = QuantumCircuit(qr) circ.h(qr[0]) circ.cx(qr[0], qr[1]) circ.x(qr[0]) circ.y(qr[0]) circ.z(qr[0]) circ.cx(qr[0], qr[1]) circ.h(qr[0]) circ.cx(qr[0], qr[1]) circ.cx(qr[0], qr[1]) dag_circuit = circuit_to_dag(circ) after = transpile_dag(dag_circuit, coupling_map=[[0, 1], [1, 0]]) expected = QuantumCircuit(QuantumRegister(2, 'q')) self.assertEqual(after, circuit_to_dag(expected))
def level_0_pass_manager( pass_manager_config: PassManagerConfig) -> PassManager: """Level 0 pass manager: no explicit optimization other than mapping to backend. This pass manager applies the user-given initial layout. If none is given, a trivial layout consisting of mapping the i-th virtual qubit to the i-th physical qubit is used. Any unused physical qubit is allocated as ancilla space. The pass manager then unrolls the circuit to the desired basis, and transforms the circuit to match the coupling map. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 0 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or 'trivial' routing_method = pass_manager_config.routing_method or 'stochastic' translation_method = pass_manager_config.translation_method or 'translator' scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties # 1. Choose an initial layout if not set by user (default: trivial layout) _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set['layout'] if layout_method == 'trivial': _choose_layout = TrivialLayout(coupling_map) elif layout_method == 'dense': _choose_layout = DenseLayout(coupling_map, backend_properties) elif layout_method == 'noise_adaptive': _choose_layout = NoiseAdaptiveLayout(backend_properties) elif layout_method == 'sabre': _choose_layout = SabreLayout(coupling_map, max_iterations=1, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 2. Extend dag/layout with ancillas using the full coupling map _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] # 3. Decompose so only 1-qubit and 2-qubit gates remain _unroll3q = Unroll3qOrMore() # 4. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == 'basic': _swap += [BasicSwap(coupling_map)] elif routing_method == 'stochastic': _swap += [ StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) ] elif routing_method == 'lookahead': _swap += [LookaheadSwap(coupling_map, search_depth=2, search_width=2)] elif routing_method == 'sabre': _swap += [ SabreSwap(coupling_map, heuristic='basic', seed=seed_transpiler) ] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. Unroll to the basis if translation_method == 'unroller': _unroll = [Unroller(basis_gates)] elif translation_method == 'translator': from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [ UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates) ] elif translation_method == 'synthesis': _unroll = [ Unroll3qOrMore(), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 6. Fix any bad CX directions _direction_check = [CheckCXDirection(coupling_map)] def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [CXDirection(coupling_map)] # 7. Schedule the circuit only when scheduling_method is supplied if scheduling_method: _scheduling = [TimeUnitAnalysis(instruction_durations)] if scheduling_method in {'alap', 'as_late_as_possible'}: _scheduling += [ALAPSchedule(instruction_durations)] elif scheduling_method in {'asap', 'as_soon_as_possible'}: _scheduling += [ASAPSchedule(instruction_durations)] else: raise TranspilerError("Invalid scheduling method %s." % scheduling_method) # Build pass manager pm0 = PassManager() if coupling_map: pm0.append(_given_layout) pm0.append(_choose_layout, condition=_choose_layout_condition) pm0.append(_embed) pm0.append(_unroll3q) pm0.append(_swap_check) pm0.append(_swap, condition=_swap_condition) pm0.append(_unroll) if coupling_map and not coupling_map.is_symmetric: pm0.append(_direction_check) pm0.append(_direction, condition=_direction_condition) if scheduling_method: pm0.append(_scheduling) return pm0
def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager: """Level 2 pass manager: medium optimization by initial layout selection and gate cancellation using commutativity rules. This pass manager applies the user-given initial layout. If none is given, a search for a perfect layout (i.e. one that satisfies all 2-qubit interactions) is conducted. If no such layout is found, qubits are laid out on the most densely connected subset which also exhibits the best gate fidelitites. The pass manager then transforms the circuit to match the coupling constraints. It is then unrolled to the basis, and any flipped cx directions are fixed. Finally, optimizations in the form of commutative gate cancellation and redundant reset removal are performed. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 2 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or 'dense' routing_method = pass_manager_config.routing_method or 'stochastic' translation_method = pass_manager_config.translation_method or 'translator' scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties # 1. Search for a perfect layout, or choose a dense layout, if no layout given _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set['layout'] _choose_layout_1 = CSPLayout(coupling_map, call_limit=1000, time_limit=10) if layout_method == 'trivial': _choose_layout_2 = TrivialLayout(coupling_map) elif layout_method == 'dense': _choose_layout_2 = DenseLayout(coupling_map, backend_properties) elif layout_method == 'noise_adaptive': _choose_layout_2 = NoiseAdaptiveLayout(backend_properties) elif layout_method == 'sabre': _choose_layout_2 = SabreLayout(coupling_map, max_iterations=2, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 2. Extend dag/layout with ancillas using the full coupling map _embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()] # 3. Unroll to 1q or 2q gates _unroll3q = Unroll3qOrMore() # 4. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == 'basic': _swap += [BasicSwap(coupling_map)] elif routing_method == 'stochastic': _swap += [StochasticSwap(coupling_map, trials=20, seed=seed_transpiler)] elif routing_method == 'lookahead': _swap += [LookaheadSwap(coupling_map, search_depth=5, search_width=5)] elif routing_method == 'sabre': _swap += [SabreSwap(coupling_map, heuristic='decay', seed=seed_transpiler)] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. Unroll to the basis if translation_method == 'unroller': _unroll = [Unroller(basis_gates)] elif translation_method == 'translator': from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates)] elif translation_method == 'synthesis': _unroll = [ Unroll3qOrMore(), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 6. Fix any bad CX directions _direction_check = [CheckCXDirection(coupling_map)] def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [CXDirection(coupling_map)] # 7. Remove zero-state reset _reset = RemoveResetInZeroState() # 8. 1q rotation merge and commutative cancellation iteratively until no more change in depth _depth_check = [Depth(), FixedPoint('depth')] def _opt_control(property_set): return not property_set['depth_fixed_point'] _opt = [Optimize1qGates(basis_gates), CommutativeCancellation()] # 9. Schedule the circuit only when scheduling_method is supplied if scheduling_method: _scheduling = [TimeUnitAnalysis(instruction_durations)] if scheduling_method in {'alap', 'as_late_as_possible'}: _scheduling += [ALAPSchedule(instruction_durations)] elif scheduling_method in {'asap', 'as_soon_as_possible'}: _scheduling += [ASAPSchedule(instruction_durations)] else: raise TranspilerError("Invalid scheduling method %s." % scheduling_method) # Build pass manager pm2 = PassManager() if coupling_map: pm2.append(_given_layout) pm2.append(_choose_layout_1, condition=_choose_layout_condition) pm2.append(_choose_layout_2, condition=_choose_layout_condition) pm2.append(_embed) pm2.append(_unroll3q) pm2.append(_swap_check) pm2.append(_swap, condition=_swap_condition) pm2.append(_unroll) if coupling_map and not coupling_map.is_symmetric: pm2.append(_direction_check) pm2.append(_direction, condition=_direction_condition) pm2.append(_reset) pm2.append(_depth_check + _opt, do_while=_opt_control) if scheduling_method: pm2.append(_scheduling) return pm2
def level_3_pass_manager( pass_manager_config: PassManagerConfig) -> PassManager: """Level 3 pass manager: heavy optimization by noise adaptive qubit mapping and gate cancellation using commutativity rules and unitary synthesis. This pass manager applies the user-given initial layout. If none is given, a search for a perfect layout (i.e. one that satisfies all 2-qubit interactions) is conducted. If no such layout is found, and device calibration information is available, the circuit is mapped to the qubits with best readouts and to CX gates with highest fidelity. The pass manager then transforms the circuit to match the coupling constraints. It is then unrolled to the basis, and any flipped cx directions are fixed. Finally, optimizations in the form of commutative gate cancellation, resynthesis of two-qubit unitary blocks, and redundant reset removal are performed. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 3 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or 'dense' routing_method = pass_manager_config.routing_method or 'stochastic' translation_method = pass_manager_config.translation_method or 'translator' scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties # 1. Unroll to 1q or 2q gates _unroll3q = Unroll3qOrMore() # 2. Layout on good qubits if calibration info available, otherwise on dense links _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set['layout'] _choose_layout_1 = [] if pass_manager_config.layout_method \ else CSPLayout(coupling_map, call_limit=10000, time_limit=60, seed=seed_transpiler) if layout_method == 'trivial': _choose_layout_2 = TrivialLayout(coupling_map) elif layout_method == 'dense': _choose_layout_2 = DenseLayout(coupling_map, backend_properties) elif layout_method == 'noise_adaptive': _choose_layout_2 = NoiseAdaptiveLayout(backend_properties) elif layout_method == 'sabre': _choose_layout_2 = SabreLayout(coupling_map, max_iterations=4, seed=seed_transpiler) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 3. Extend dag/layout with ancillas using the full coupling map _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] # 4. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == 'basic': _swap += [BasicSwap(coupling_map)] elif routing_method == 'stochastic': _swap += [ StochasticSwap(coupling_map, trials=200, seed=seed_transpiler) ] elif routing_method == 'lookahead': _swap += [LookaheadSwap(coupling_map, search_depth=5, search_width=6)] elif routing_method == 'sabre': _swap += [ SabreSwap(coupling_map, heuristic='decay', seed=seed_transpiler) ] elif routing_method == 'none': _swap += [ Error( msg= 'No routing method selected, but circuit is not routed to device. ' 'CheckMap Error: {check_map_msg}', action='raise') ] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. Unroll to the basis if translation_method == 'unroller': _unroll = [Unroller(basis_gates)] elif translation_method == 'translator': from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [ UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates) ] elif translation_method == 'synthesis': _unroll = [ Unroll3qOrMore(), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 6. Fix any CX direction mismatch _direction_check = [CheckCXDirection(coupling_map)] def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [CXDirection(coupling_map)] # 8. Optimize iteratively until no more change in depth. Removes useless gates # after reset and before measure, commutes gates and optimizes contiguous blocks. _depth_check = [Depth(), FixedPoint('depth')] def _opt_control(property_set): return not property_set['depth_fixed_point'] _reset = [RemoveResetInZeroState()] _meas = [OptimizeSwapBeforeMeasure(), RemoveDiagonalGatesBeforeMeasure()] _opt = [ Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates), Optimize1qGatesDecomposition(basis_gates), CommutativeCancellation(), ] # Schedule the circuit only when scheduling_method is supplied if scheduling_method: _scheduling = [TimeUnitAnalysis(instruction_durations)] if scheduling_method in {'alap', 'as_late_as_possible'}: _scheduling += [ALAPSchedule(instruction_durations)] elif scheduling_method in {'asap', 'as_soon_as_possible'}: _scheduling += [ASAPSchedule(instruction_durations)] else: raise TranspilerError("Invalid scheduling method %s." % scheduling_method) # Build pass manager pm3 = PassManager() pm3.append(_unroll3q) pm3.append(_reset + _meas) if coupling_map or initial_layout: pm3.append(_given_layout) pm3.append(_choose_layout_1, condition=_choose_layout_condition) pm3.append(_choose_layout_2, condition=_choose_layout_condition) pm3.append(_embed) pm3.append(_swap_check) pm3.append(_swap, condition=_swap_condition) pm3.append(_unroll) pm3.append(_depth_check + _opt + _unroll, do_while=_opt_control) if coupling_map and not coupling_map.is_symmetric: pm3.append(_direction_check) pm3.append(_direction, condition=_direction_condition) pm3.append(_reset) if scheduling_method: pm3.append(_scheduling) return pm3
def noise_pass_manager(basis_gates=None, initial_layout=None, coupling_map=None, layout_method=None, translation_method=None, seed_transpiler=None, backend=None, routing_method=None, backend_properties=None, transform=False, readout=True, alpha=0.5, next_gates=5, front=True) -> PassManager: """Level 3 pass manager: heavy optimization by noise adaptive qubit mapping and gate cancellation using commutativity rules and unitary synthesis. This pass manager applies the user-given initial layout. If none is given, a search for a perfect layout (i.e. one that satisfies all 2-qubit interactions) is conducted. If no such layout is found, and device calibration information is available, the circuit is mapped to the qubits with best readouts and to CX gates with highest fidelity. The pass manager then transforms the circuit to match the coupling constraints. It is then unrolled to the basis, and any flipped cx directions are fixed. Finally, optimizations in the form of commutative gate cancellation, resynthesis of two-qubit unitary blocks, and redundant reset removal are performed. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: backend (BaseBackend) Returns: a level 3 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ if basis_gates is None: if getattr(backend, 'configuration', None): basis_gates = getattr(backend.configuration(), 'basis_gates', None) # basis_gates could be None, or a list of basis, e.g. ['u3', 'cx'] if isinstance(basis_gates, list) and all( isinstance(i, str) for i in basis_gates): basis_gates = basis_gates if basis_gates is None: basis_gates = ['u3', 'cx', 'id'] # basis_gates = ['u3', 'cx', 'id'] backend = backend if backend is None or backend.configuration().simulator: if backend_properties is None or coupling_map is None: raise QiskitError( "Backend is simulator or not specified, provide backend properties and coupling map." ) coupling_map = coupling_map backend_properties = backend_properties else: if backend_properties is not None or coupling_map is not None: warnings.warn( "A backend was provide, ignoring backend properties and coupling map", UserWarning) coupling_map = backend.configuration().coupling_map backend_properties = backend.properties() if isinstance(coupling_map, list): coupling_map = CouplingMap(couplinglist=coupling_map) initial_layout = initial_layout layout_method = layout_method or 'dense' routing_method = routing_method or 'stochastic' translation_method = translation_method or 'translator' seed_transpiler = seed_transpiler # 1. Unroll to 1q or 2q gates _unroll3q = Unroll3qOrMore() # 2. Layout on good qubits if calibration info available, otherwise on dense links _given_layout = SetLayout(initial_layout) def _choose_layout_condition(property_set): return not property_set['layout'] _choose_layout_1 = CSPLayout(coupling_map, call_limit=10000, time_limit=60) if layout_method == 'trivial': _choose_layout_2 = TrivialLayout(coupling_map) elif layout_method == 'dense': _choose_layout_2 = DenseLayout(coupling_map, backend_properties) elif layout_method == 'noise_adaptive': _choose_layout_2 = NoiseAdaptiveLayout(backend_properties) elif layout_method == 'sabre': _choose_layout_2 = SabreLayout(coupling_map, max_iterations=4, seed=seed_transpiler) elif layout_method == 'chain': _choose_layout_2 = ChainLayout(coupling_map, backend_properties, readout=readout) else: raise TranspilerError("Invalid layout method %s." % layout_method) # 3. Extend dag/layout with ancillas using the full coupling map _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] # 4. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == 'basic': _swap += [BasicSwap(coupling_map)] elif routing_method == 'stochastic': _swap += [ StochasticSwap(coupling_map, trials=200, seed=seed_transpiler) ] elif routing_method == 'lookahead': _swap += [LookaheadSwap(coupling_map, search_depth=5, search_width=6)] elif routing_method == 'sabre': _swap += [ SabreSwap(coupling_map, heuristic='decay', seed=seed_transpiler) ] elif routing_method == 'noise_adaptive': _swap += [ NoiseAdaptiveSwap(coupling_map, backend_properties, invert_score=invert_score, swap_score=swap_score, readout=readout, alpha=alpha, next_gates=next_gates, front=front) ] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 5. Unroll to the basis if translation_method == 'unroller': _unroll = [Unroller(basis_gates)] elif translation_method == 'translator': from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel _unroll = [ UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates) ] elif translation_method == 'synthesis': _unroll = [ Unroll3qOrMore(), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates), ] else: raise TranspilerError("Invalid translation method %s." % translation_method) # 6. Fix any CX direction mismatch _direction_check = [CheckCXDirection(coupling_map)] def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [CXDirection(coupling_map)] # 8. Optimize iteratively until no more change in depth. Removes useless gates # after reset and before measure, commutes gates and optimizes continguous blocks. _depth_check = [Depth(), FixedPoint('depth')] def _opt_control(property_set): return not property_set['depth_fixed_point'] _reset = [RemoveResetInZeroState()] _meas = [OptimizeSwapBeforeMeasure(), RemoveDiagonalGatesBeforeMeasure()] _opt = [ Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates), UnitarySynthesis(basis_gates), Optimize1qGates(basis_gates), CommutativeCancellation(), ] # Build pass manager pm3 = PassManager() pm3.append(_unroll3q) if transform: _transform = TransformCxCascade() pm3.append(_transform) pm3.append(_reset + _meas) if coupling_map: pm3.append(_given_layout) pm3.append(_choose_layout_1, condition=_choose_layout_condition) pm3.append(_choose_layout_2, condition=_choose_layout_condition) pm3.append(_embed) pm3.append(_swap_check) pm3.append(_swap, condition=_swap_condition) pm3.append(_unroll) pm3.append(_depth_check + _opt + _unroll, do_while=_opt_control) if coupling_map and not coupling_map.is_symmetric: pm3.append(_direction_check) pm3.append(_direction, condition=_direction_condition) pm3.append(_reset) return pm3
def level_1_pass_manager( pass_manager_config: PassManagerConfig) -> PassManager: """Level 1 pass manager: light optimization by simple adjacent gate collapsing. This pass manager applies the user-given initial layout. If none is given, and a trivial layout (i-th virtual -> i-th physical) makes the circuit fit the coupling map, that is used. Otherwise, the circuit is mapped to the most densely connected coupling subgraph, and swaps are inserted to map. Any unused physical qubit is allocated as ancilla space. The pass manager then unrolls the circuit to the desired basis, and transforms the circuit to match the coupling map. Finally, optimizations in the form of adjacent gate collapse and redundant reset removal are performed. Note: In simulators where ``coupling_map=None``, only the unrolling and optimization stages are done. Args: pass_manager_config: configuration of the pass manager. Returns: a level 1 pass manager. Raises: TranspilerError: if the passmanager config is invalid. """ basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or 'dense' routing_method = pass_manager_config.routing_method or 'stochastic' seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties # 1. Use trivial layout if no layout given _given_layout = SetLayout(initial_layout) _choose_layout_and_score = [ TrivialLayout(coupling_map), Layout2qDistance(coupling_map, property_name='trivial_layout_score') ] def _choose_layout_condition(property_set): return not property_set['layout'] # 2. Use a better layout on densely connected qubits, if circuit needs swaps if layout_method == 'trivial': _improve_layout = TrivialLayout(coupling_map) elif layout_method == 'dense': _improve_layout = DenseLayout(coupling_map, backend_properties) elif layout_method == 'noise_adaptive': _improve_layout = NoiseAdaptiveLayout(backend_properties) else: raise TranspilerError("Invalid layout method %s." % layout_method) def _not_perfect_yet(property_set): return property_set['trivial_layout_score'] is not None and \ property_set['trivial_layout_score'] != 0 # 3. Extend dag/layout with ancillas using the full coupling map _embed = [ FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout() ] # 4. Decompose so only 1-qubit and 2-qubit gates remain _unroll3q = Unroll3qOrMore() # 5. Swap to fit the coupling map _swap_check = CheckMap(coupling_map) def _swap_condition(property_set): return not property_set['is_swap_mapped'] _swap = [BarrierBeforeFinalMeasurements()] if routing_method == 'basic': _swap += [BasicSwap(coupling_map)] elif routing_method == 'stochastic': _swap += [ StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) ] elif routing_method == 'lookahead': _swap += [LookaheadSwap(coupling_map, search_depth=4, search_width=4)] else: raise TranspilerError("Invalid routing method %s." % routing_method) # 6. Unroll to the basis _unroll = Unroller(basis_gates) # 7. Fix any bad CX directions _direction_check = [CheckCXDirection(coupling_map)] def _direction_condition(property_set): return not property_set['is_direction_mapped'] _direction = [CXDirection(coupling_map)] # 8. Remove zero-state reset _reset = RemoveResetInZeroState() # 9. Merge 1q rotations and cancel CNOT gates iteratively until no more change in depth _depth_check = [Depth(), FixedPoint('depth')] def _opt_control(property_set): return not property_set['depth_fixed_point'] _opt = [Optimize1qGates(basis_gates), CXCancellation()] # Build pass manager pm1 = PassManager() if coupling_map: pm1.append(_given_layout) pm1.append(_choose_layout_and_score, condition=_choose_layout_condition) pm1.append(_improve_layout, condition=_not_perfect_yet) pm1.append(_embed) pm1.append(_unroll3q) pm1.append(_swap_check) pm1.append(_swap, condition=_swap_condition) pm1.append(_unroll) if coupling_map and not coupling_map.is_symmetric: pm1.append(_direction_check) pm1.append(_direction, condition=_direction_condition) pm1.append(_reset) pm1.append(_depth_check + _opt, do_while=_opt_control) return pm1
class TestTranspile(QiskitTestCase): """Test transpile function.""" barrier_pass = BarrierBeforeFinalMeasurements() def test_pass_manager_none(self): """Test passing the default (None) pass manager to the transpiler. It should perform the default qiskit flow: unroll, swap_mapper, cx_direction, cx_cancellation, optimize_1q_gates and should be equivalent to using tools.compile """ qr = QuantumRegister(2, 'qr') circuit = QuantumCircuit(qr) circuit.h(qr[0]) circuit.h(qr[0]) circuit.cx(qr[0], qr[1]) circuit.cx(qr[1], qr[0]) circuit.cx(qr[0], qr[1]) circuit.cx(qr[1], qr[0]) coupling_map = [[1, 0]] basis_gates = ['u1', 'u2', 'u3', 'cx', 'id'] backend = BasicAer.get_backend('qasm_simulator') circuit2 = transpile(circuit, backend=backend, coupling_map=coupling_map, basis_gates=basis_gates, pass_manager=None) circuit3 = transpile(circuit, backend=backend, coupling_map=coupling_map, basis_gates=basis_gates) self.assertEqual(circuit2, circuit3) def test_transpile_basis_gates_no_backend_no_coupling_map(self): """Verify transpile() works with no coupling_map or backend.""" qr = QuantumRegister(2, 'qr') circuit = QuantumCircuit(qr) circuit.h(qr[0]) circuit.h(qr[0]) circuit.cx(qr[0], qr[1]) circuit.cx(qr[0], qr[1]) circuit.cx(qr[0], qr[1]) circuit.cx(qr[0], qr[1]) basis_gates = ['u1', 'u2', 'u3', 'cx', 'id'] circuit2 = transpile(circuit, basis_gates=basis_gates) dag_circuit = circuit_to_dag(circuit2) resources_after = dag_circuit.count_ops() self.assertEqual({'u2': 2, 'cx': 4}, resources_after) def test_transpile_non_adjacent_layout(self): """Transpile pipeline can handle manual layout on non-adjacent qubits. circuit: qr0:-[H]--.------------ -> 1 | qr1:-----(+)--.-------- -> 2 | qr2:---------(+)--.---- -> 3 | qr3:-------------(+)--- -> 5 device: 0 - 1 - 2 - 3 - 4 - 5 - 6 | | | | | | 13 - 12 - 11 - 10 - 9 - 8 - 7 """ qr = QuantumRegister(4, 'qr') circuit = QuantumCircuit(qr) circuit.h(qr[0]) circuit.cx(qr[0], qr[1]) circuit.cx(qr[1], qr[2]) circuit.cx(qr[2], qr[3]) coupling_map = FakeMelbourne().configuration().coupling_map basis_gates = FakeMelbourne().configuration().basis_gates initial_layout = [None, qr[0], qr[1], qr[2], None, qr[3]] new_circuit = transpile(circuit, basis_gates=basis_gates, coupling_map=coupling_map, initial_layout=initial_layout) for gate, qargs, _ in new_circuit.data: if isinstance(gate, CnotGate): self.assertIn([x[1] for x in qargs], coupling_map) def test_transpile_qft_grid(self): """Transpile pipeline can handle 8-qubit QFT on 14-qubit grid. """ qr = QuantumRegister(8) circuit = QuantumCircuit(qr) for i, _ in enumerate(qr): for j in range(i): circuit.cu1(math.pi / float(2 ** (i - j)), qr[i], qr[j]) circuit.h(qr[i]) coupling_map = FakeMelbourne().configuration().coupling_map basis_gates = FakeMelbourne().configuration().basis_gates new_circuit = transpile(circuit, basis_gates=basis_gates, coupling_map=coupling_map) for gate, qargs, _ in new_circuit.data: if isinstance(gate, CnotGate): self.assertIn([x[1] for x in qargs], coupling_map) def test_already_mapped_1(self): """Circuit not remapped if matches topology. See: https://github.com/Qiskit/qiskit-terra/issues/342 """ backend = FakeRueschlikon() coupling_map = backend.configuration().coupling_map basis_gates = backend.configuration().basis_gates qr = QuantumRegister(16, 'qr') cr = ClassicalRegister(16, 'cr') qc = QuantumCircuit(qr, cr) qc.cx(qr[3], qr[14]) qc.cx(qr[5], qr[4]) qc.h(qr[9]) qc.cx(qr[9], qr[8]) qc.x(qr[11]) qc.cx(qr[3], qr[4]) qc.cx(qr[12], qr[11]) qc.cx(qr[13], qr[4]) qc.measure(qr, cr) new_qc = transpile(qc, coupling_map=coupling_map, basis_gates=basis_gates) cx_qubits = [qargs for (gate, qargs, _) in new_qc.data if gate.name == "cx"] cx_qubits_physical = [[ctrl[1], tgt[1]] for [ctrl, tgt] in cx_qubits] self.assertEqual(sorted(cx_qubits_physical), [[3, 4], [3, 14], [5, 4], [9, 8], [12, 11], [13, 4]]) def test_already_mapped_via_layout(self): """Test that a manual layout that satisfies a coupling map does not get altered. See: https://github.com/Qiskit/qiskit-terra/issues/2036 """ basis_gates = ['u1', 'u2', 'u3', 'cx', 'id'] coupling_map = [[0, 1], [0, 5], [1, 0], [1, 2], [2, 1], [2, 3], [3, 2], [3, 4], [4, 3], [4, 9], [5, 0], [5, 6], [5, 10], [6, 5], [6, 7], [7, 6], [7, 8], [7, 12], [8, 7], [8, 9], [9, 4], [9, 8], [9, 14], [10, 5], [10, 11], [10, 15], [11, 10], [11, 12], [12, 7], [12, 11], [12, 13], [13, 12], [13, 14], [14, 9], [14, 13], [14, 19], [15, 10], [15, 16], [16, 15], [16, 17], [17, 16], [17, 18], [18, 17], [18, 19], [19, 14], [19, 18]] q = QuantumRegister(6, name='qn') c = ClassicalRegister(2, name='cn') qc = QuantumCircuit(q, c) qc.h(q[0]) qc.h(q[5]) qc.cx(q[0], q[5]) qc.u1(2, q[5]) qc.cx(q[0], q[5]) qc.h(q[0]) qc.h(q[5]) qc.barrier(q) qc.measure(q[0], c[0]) qc.measure(q[5], c[1]) initial_layout = [q[3], q[4], None, None, q[5], q[2], q[1], None, None, q[0], None, None, None, None, None, None, None, None, None, None] new_qc = transpile(qc, coupling_map=coupling_map, basis_gates=basis_gates, initial_layout=initial_layout) cx_qubits = [qargs for (gate, qargs, _) in new_qc.data if gate.name == "cx"] cx_qubits_physical = [[ctrl[1], tgt[1]] for [ctrl, tgt] in cx_qubits] self.assertEqual(sorted(cx_qubits_physical), [[9, 4], [9, 4]]) def test_transpile_bell(self): """Test Transpile Bell. If all correct some should exists. """ backend = BasicAer.get_backend('qasm_simulator') qubit_reg = QuantumRegister(2, name='q') clbit_reg = ClassicalRegister(2, name='c') qc = QuantumCircuit(qubit_reg, clbit_reg, name="bell") qc.h(qubit_reg[0]) qc.cx(qubit_reg[0], qubit_reg[1]) qc.measure(qubit_reg, clbit_reg) circuits = transpile(qc, backend) self.assertIsInstance(circuits, QuantumCircuit) def test_transpile_two(self): """Test transpile to circuits. If all correct some should exists. """ backend = BasicAer.get_backend('qasm_simulator') qubit_reg = QuantumRegister(2) clbit_reg = ClassicalRegister(2) qubit_reg2 = QuantumRegister(2) clbit_reg2 = ClassicalRegister(2) qc = QuantumCircuit(qubit_reg, clbit_reg, name="bell") qc.h(qubit_reg[0]) qc.cx(qubit_reg[0], qubit_reg[1]) qc.measure(qubit_reg, clbit_reg) qc_extra = QuantumCircuit(qubit_reg, qubit_reg2, clbit_reg, clbit_reg2, name="extra") qc_extra.measure(qubit_reg, clbit_reg) circuits = transpile([qc, qc_extra], backend) self.assertIsInstance(circuits[0], QuantumCircuit) self.assertIsInstance(circuits[1], QuantumCircuit) def test_mapping_correction(self): """Test mapping works in previous failed case. """ backend = FakeRueschlikon() qr = QuantumRegister(name='qr', size=11) cr = ClassicalRegister(name='qc', size=11) circuit = QuantumCircuit(qr, cr) circuit.u3(1.564784764685993, -1.2378965763410095, 2.9746763177861713, qr[3]) circuit.u3(1.2269835563676523, 1.1932982847014162, -1.5597357740824318, qr[5]) circuit.cx(qr[5], qr[3]) circuit.u1(0.856768317675967, qr[3]) circuit.u3(-3.3911273825190915, 0.0, 0.0, qr[5]) circuit.cx(qr[3], qr[5]) circuit.u3(2.159209321625547, 0.0, 0.0, qr[5]) circuit.cx(qr[5], qr[3]) circuit.u3(0.30949966910232335, 1.1706201763833217, 1.738408691990081, qr[3]) circuit.u3(1.9630571407274755, -0.6818742967975088, 1.8336534616728195, qr[5]) circuit.u3(1.330181833806101, 0.6003162754946363, -3.181264980452862, qr[7]) circuit.u3(0.4885914820775024, 3.133297443244865, -2.794457469189904, qr[8]) circuit.cx(qr[8], qr[7]) circuit.u1(2.2196187596178616, qr[7]) circuit.u3(-3.152367609631023, 0.0, 0.0, qr[8]) circuit.cx(qr[7], qr[8]) circuit.u3(1.2646005789809263, 0.0, 0.0, qr[8]) circuit.cx(qr[8], qr[7]) circuit.u3(0.7517780502091939, 1.2828514296564781, 1.6781179605443775, qr[7]) circuit.u3(0.9267400575390405, 2.0526277839695153, 2.034202361069533, qr[8]) circuit.u3(2.550304293455634, 3.8250017126569698, -2.1351609599720054, qr[1]) circuit.u3(0.9566260876600556, -1.1147561503064538, 2.0571590492298797, qr[4]) circuit.cx(qr[4], qr[1]) circuit.u1(2.1899329069137394, qr[1]) circuit.u3(-1.8371715243173294, 0.0, 0.0, qr[4]) circuit.cx(qr[1], qr[4]) circuit.u3(0.4717053496327104, 0.0, 0.0, qr[4]) circuit.cx(qr[4], qr[1]) circuit.u3(2.3167620677708145, -1.2337330260253256, -0.5671322899563955, qr[1]) circuit.u3(1.0468499525240678, 0.8680750644809365, -1.4083720073192485, qr[4]) circuit.u3(2.4204244021892807, -2.211701932616922, 3.8297006565735883, qr[10]) circuit.u3(0.36660280497727255, 3.273119149343493, -1.8003362351299388, qr[6]) circuit.cx(qr[6], qr[10]) circuit.u1(1.067395863586385, qr[10]) circuit.u3(-0.7044917541291232, 0.0, 0.0, qr[6]) circuit.cx(qr[10], qr[6]) circuit.u3(2.1830003849921527, 0.0, 0.0, qr[6]) circuit.cx(qr[6], qr[10]) circuit.u3(2.1538343756723917, 2.2653381826084606, -3.550087952059485, qr[10]) circuit.u3(1.307627685019188, -0.44686656993522567, -2.3238098554327418, qr[6]) circuit.u3(2.2046797998462906, 0.9732961754855436, 1.8527865921467421, qr[9]) circuit.u3(2.1665254613904126, -1.281337664694577, -1.2424905413631209, qr[0]) circuit.cx(qr[0], qr[9]) circuit.u1(2.6209599970201007, qr[9]) circuit.u3(0.04680566321901303, 0.0, 0.0, qr[0]) circuit.cx(qr[9], qr[0]) circuit.u3(1.7728411151289603, 0.0, 0.0, qr[0]) circuit.cx(qr[0], qr[9]) circuit.u3(2.4866395967434443, 0.48684511243566697, -3.0069186877854728, qr[9]) circuit.u3(1.7369112924273789, -4.239660866163805, 1.0623389015296005, qr[0]) circuit.barrier(qr) circuit.measure(qr, cr) circuits = transpile(circuit, backend) self.assertIsInstance(circuits, QuantumCircuit) def test_transpiler_layout_from_intlist(self): """A list of ints gives layout to correctly map circuit. virtual physical q1_0 - 4 ---[H]--- q2_0 - 5 q2_1 - 6 ---[H]--- q3_0 - 8 q3_1 - 9 q3_2 - 10 ---[H]--- """ qr1 = QuantumRegister(1, 'qr1') qr2 = QuantumRegister(2, 'qr2') qr3 = QuantumRegister(3, 'qr3') qc = QuantumCircuit(qr1, qr2, qr3) qc.h(qr1[0]) qc.h(qr2[1]) qc.h(qr3[2]) layout = [4, 5, 6, 8, 9, 10] cmap = [[1, 0], [1, 2], [2, 3], [4, 3], [4, 10], [5, 4], [5, 6], [5, 9], [6, 8], [7, 8], [9, 8], [9, 10], [11, 3], [11, 10], [11, 12], [12, 2], [13, 1], [13, 12]] new_circ = transpile(qc, backend=None, coupling_map=cmap, basis_gates=['u2'], initial_layout=layout) mapped_qubits = [] for _, qargs, _ in new_circ.data: mapped_qubits.append(qargs[0][1]) self.assertEqual(mapped_qubits, [4, 6, 10]) def test_mapping_multi_qreg(self): """Test mapping works for multiple qregs. """ backend = FakeRueschlikon() qr = QuantumRegister(3, name='qr') qr2 = QuantumRegister(1, name='qr2') qr3 = QuantumRegister(4, name='qr3') cr = ClassicalRegister(3, name='cr') qc = QuantumCircuit(qr, qr2, qr3, cr) qc.h(qr[0]) qc.cx(qr[0], qr2[0]) qc.cx(qr[1], qr3[2]) qc.measure(qr, cr) circuits = transpile(qc, backend) self.assertIsInstance(circuits, QuantumCircuit) def test_transpile_circuits_diff_registers(self): """Transpile list of circuits with different qreg names. """ backend = FakeRueschlikon() circuits = [] for _ in range(2): qr = QuantumRegister(2) cr = ClassicalRegister(2) circuit = QuantumCircuit(qr, cr) circuit.h(qr[0]) circuit.cx(qr[0], qr[1]) circuit.measure(qr, cr) circuits.append(circuit) circuits = transpile(circuits, backend) self.assertIsInstance(circuits[0], QuantumCircuit) def test_wrong_initial_layout(self): """Test transpile with a bad initial layout. """ backend = FakeMelbourne() qubit_reg = QuantumRegister(2, name='q') clbit_reg = ClassicalRegister(2, name='c') qc = QuantumCircuit(qubit_reg, clbit_reg, name="bell") qc.h(qubit_reg[0]) qc.cx(qubit_reg[0], qubit_reg[1]) qc.measure(qubit_reg, clbit_reg) bad_initial_layout = [(QuantumRegister(3, 'q'), 0), (QuantumRegister(3, 'q'), 1), (QuantumRegister(3, 'q'), 2)] self.assertRaises(KeyError, transpile, qc, backend, initial_layout=bad_initial_layout) def test_parameterized_circuit_for_simulator(self): """Verify that a parameterized circuit can be transpiled for a simulator backend.""" qr = QuantumRegister(2, name='qr') qc = QuantumCircuit(qr) theta = Parameter('theta') qc.rz(theta, qr[0]) transpiled_qc = transpile(qc, backend=BasicAer.get_backend('qasm_simulator')) expected_qc = QuantumCircuit(qr) expected_qc.u1(theta, qr[0]) self.assertEqual(expected_qc, transpiled_qc) def test_parameterized_circuit_for_device(self): """Verify that a parameterized circuit can be transpiled for a device backend.""" qr = QuantumRegister(2, name='qr') qc = QuantumCircuit(qr) theta = Parameter('theta') qc.rz(theta, qr[0]) transpiled_qc = transpile(qc, backend=FakeMelbourne()) qr = QuantumRegister(14, 'q') expected_qc = QuantumCircuit(qr) expected_qc.u1(theta, qr[0]) self.assertEqual(expected_qc, transpiled_qc) @unittest.mock.patch.object(BarrierBeforeFinalMeasurements, 'run', wraps=barrier_pass.run) def test_final_measurement_barrier_for_devices(self, mock_pass): """Verify BarrierBeforeFinalMeasurements pass is called in default pipeline for devices.""" circ = QuantumCircuit.from_qasm_file(self._get_resource_path('example.qasm', Path.QASMS)) layout = Layout.generate_trivial_layout(*circ.qregs) transpile(circ, coupling_map=FakeRueschlikon().configuration().coupling_map, initial_layout=layout) self.assertTrue(mock_pass.called) def test_do_not_run_cxdirection_with_symmetric_cm(self): """When the coupling map is symmetric, do not run CXDirection.""" circ = QuantumCircuit.from_qasm_file(self._get_resource_path('example.qasm', Path.QASMS)) layout = Layout.generate_trivial_layout(*circ.qregs) coupling_map = [] for node1, node2 in FakeRueschlikon().configuration().coupling_map: coupling_map.append([node1, node2]) coupling_map.append([node2, node1]) cxdir_pass = CXDirection(CouplingMap(coupling_map)) with unittest.mock.patch.object(CXDirection, 'run', wraps=cxdir_pass.run) as mock_pass: transpile(circ, coupling_map=coupling_map, initial_layout=layout) self.assertFalse(mock_pass.called) def test_optimize_to_nothing(self): """ Optimize gates up to fixed point in the default pipeline See https://github.com/Qiskit/qiskit-terra/issues/2035 """ qr = QuantumRegister(2) circ = QuantumCircuit(qr) circ.h(qr[0]) circ.cx(qr[0], qr[1]) circ.x(qr[0]) circ.y(qr[0]) circ.z(qr[0]) circ.cx(qr[0], qr[1]) circ.h(qr[0]) circ.cx(qr[0], qr[1]) circ.cx(qr[0], qr[1]) after = transpile(circ, coupling_map=[[0, 1], [1, 0]], basis_gates=['u3', 'cx']) expected = QuantumCircuit(QuantumRegister(2, 'q')) self.assertEqual(after, expected) def test_pass_manager_empty(self): """Test passing an empty PassManager() to the transpiler. It should perform no transformations on the circuit. """ qr = QuantumRegister(2) circuit = QuantumCircuit(qr) circuit.h(qr[0]) circuit.h(qr[0]) circuit.cx(qr[0], qr[1]) circuit.cx(qr[0], qr[1]) circuit.cx(qr[0], qr[1]) circuit.cx(qr[0], qr[1]) resources_before = circuit.count_ops() pass_manager = PassManager() out_circuit = transpile(circuit, pass_manager=pass_manager) resources_after = out_circuit.count_ops() self.assertDictEqual(resources_before, resources_after) def test_move_measurements(self): """Measurements applied AFTER swap mapping. """ backend = FakeRueschlikon() cmap = backend.configuration().coupling_map circ = QuantumCircuit.from_qasm_file( self._get_resource_path('move_measurements.qasm', Path.QASMS)) lay = [0, 1, 15, 2, 14, 3, 13, 4, 12, 5, 11, 6] out = transpile(circ, initial_layout=lay, coupling_map=cmap) out_dag = circuit_to_dag(out) meas_nodes = out_dag.named_nodes('measure') for meas_node in meas_nodes: is_last_measure = all([after_measure.type == 'out' for after_measure in out_dag.quantum_successors(meas_node)]) self.assertTrue(is_last_measure) def test_initialize_reset_should_be_removed(self): """The reset in front of initializer should be removed when zero state""" qr = QuantumRegister(1, "qr") qc = QuantumCircuit(qr) qc.initialize([1.0 / math.sqrt(2), 1.0 / math.sqrt(2)], [qr[0]]) qc.initialize([1.0 / math.sqrt(2), -1.0 / math.sqrt(2)], [qr[0]]) expected = QuantumCircuit(qr) expected.u_base(1.5708, 0, 0, qr[0]) expected.u_base(0, 0, 0, qr[0]) expected.reset(qr[0]) expected.u_base(1.5708, 0, 0, qr[0]) expected.u_base(0, 0, 3.1416, qr[0]) after = transpile(qc, basis_gates=['reset', 'U']) self.assertEqual(after, expected) def test_initialize_FakeMelbourne(self): """Test that the zero-state resets are remove in a device not supporting them. """ desired_vector = [1 / math.sqrt(2), 0, 0, 0, 0, 0, 0, 1 / math.sqrt(2)] qr = QuantumRegister(3, "qr") qc = QuantumCircuit(qr) qc.initialize(desired_vector, [qr[0], qr[1], qr[2]]) out = transpile(qc, backend=FakeMelbourne()) out_dag = circuit_to_dag(out) reset_nodes = out_dag.named_nodes('reset') self.assertEqual(reset_nodes, []) def test_non_standard_basis(self): """Test a transpilation with a non-standard basis""" qr1 = QuantumRegister(1, 'q1') qr2 = QuantumRegister(2, 'q2') qr3 = QuantumRegister(3, 'q3') qc = QuantumCircuit(qr1, qr2, qr3) qc.h(qr1[0]) qc.h(qr2[1]) qc.h(qr3[2]) layout = [4, 5, 6, 8, 9, 10] cmap = [[1, 0], [1, 2], [2, 3], [4, 3], [4, 10], [5, 4], [5, 6], [5, 9], [6, 8], [7, 8], [9, 8], [9, 10], [11, 3], [11, 10], [11, 12], [12, 2], [13, 1], [13, 12]] circuit = transpile(qc, backend=None, coupling_map=cmap, basis_gates=['h'], initial_layout=layout) dag_circuit = circuit_to_dag(circuit) resources_after = dag_circuit.count_ops() self.assertEqual({'h': 3}, resources_after) @unittest.skip('skipping due to MacOS specific failure, unrolling to u2') def test_basis_subset(self): """Test a transpilation with a basis subset of the standard basis""" qr = QuantumRegister(1, 'q1') qc = QuantumCircuit(qr) qc.h(qr[0]) qc.x(qr[0]) qc.t(qr[0]) layout = [4, 5, 6, 8, 9, 10] cmap = [[1, 0], [1, 2], [2, 3], [4, 3], [4, 10], [5, 4], [5, 6], [5, 9], [6, 8], [7, 8], [9, 8], [9, 10], [11, 3], [11, 10], [11, 12], [12, 2], [13, 1], [13, 12]] circuit = transpile(qc, backend=None, coupling_map=cmap, basis_gates=['u3'], initial_layout=layout) dag_circuit = circuit_to_dag(circuit) resources_after = dag_circuit.count_ops() self.assertEqual({'u3': 1}, resources_after)