Skip to content

Commit

Permalink
Add benchmarks for mapping passes (Qiskit#699)
Browse files Browse the repository at this point in the history
* Add benchmarks for mapping passes

This commit adds new benchmark methods for mapping passes. There are 2
benchmarks for each analysis pass that track the run time of the pass
and the memory consumption. Then for transformation passes a third
benchmark is added to track the depth after the pass is run. For swap
mappers a 4th benchmark also keeping track of how many swap gates are
in the circuit.

* Fix lint

The linter fails because two passes have not been released yet, this
commit disables this check for the file so we can run the benchmark for
these new passes.

* Add python-constraint to asv venv

The csp layout pass uses python-constraint which is an optional
dependency. This needs to be manually installed to make the csp layout
pass benchmarks work, this commit adds it to the list of dependencies
installed.
  • Loading branch information
mtreinish authored and nonhermitian committed Nov 25, 2019
1 parent a9ba9eb commit 3f6e209
Show file tree
Hide file tree
Showing 4 changed files with 295 additions and 1 deletion.
3 changes: 2 additions & 1 deletion asv.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"return-code=any python -c \"import shutil; shutil.rmtree('{build_dir}/build')\"",
"return-code=any python -c \"import shutil; shutil.rmtree('{build_dir}/qiskit_terra.egg-info')\"",
"python -mpip install {wheel_file}",
"python -mpip install -U qiskit-ignis==0.2.0"
"python -mpip install -U qiskit-ignis==0.2.0",
"python -mpip install -U python-constraint"
],
"uninstall_command": [
"return-code=any python -mpip uninstall -y {project}",
Expand Down
73 changes: 73 additions & 0 deletions test/benchmarks/backends/fake_singapore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2019.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""
Fake Boeblingen device (20 qubit).
"""

import os
import json

from qiskit.providers.models import (GateConfig, QasmBackendConfiguration,
BackendProperties)
from qiskit.test.mock.fake_backend import FakeBackend


class FakeSingapore(FakeBackend):
"""A fake Singapore backend."""

def __init__(self):
"""
00 ↔ 01 ↔ 02 ↔ 03 ↔ 04
↕ ↕
05 ↔ 06 ↔ 07 ↔ 08 ↔ 09
↕ ↕ ↕
10 ↔ 11 ↔ 12 ↔ 13 ↔ 14
↕ ↕
15 ↔ 16 ↔ 17 ↔ 18 ↔ 19
"""
cmap = [[0, 1], [1, 0], [1, 2], [1, 6], [2, 1], [2, 3], [3, 2],
[3, 4], [3, 8], [4, 3], [5, 6], [5, 10], [6, 1], [6, 5],
[6, 7], [7, 6], [7, 8], [7, 12], [8, 3], [8, 7], [8, 9],
[9, 8], [9, 14], [10, 5], [10, 11], [11, 10], [11, 12],
[11, 16], [12, 7], [12, 11], [12, 13], [13, 12], [13, 14],
[13, 18], [14, 9], [14, 13], [15, 16], [16, 11], [16, 15],
[16, 17], [17, 16], [17, 18], [18, 13], [18, 17], [18, 19],
[19, 18]]

configuration = QasmBackendConfiguration(
backend_name='fake_singapore',
backend_version='0.0.0',
n_qubits=20,
basis_gates=['u1', 'u2', 'u3', 'cx', 'id'],
simulator=False,
local=True,
conditional=False,
open_pulse=False,
memory=True,
max_shots=8192,
gates=[GateConfig(name='TODO', parameters=[], qasm_def='TODO')],
coupling_map=cmap,
)

super().__init__(configuration)

def properties(self):
"""Returns a snapshot of device properties as recorded on 10/08/19.
"""
dirname = os.path.dirname(__file__)
filename = "props_singapore.json"
with open(os.path.join(dirname, filename), "r") as f_prop:
props = json.load(f_prop)
return BackendProperties.from_dict(props)
1 change: 1 addition & 0 deletions test/benchmarks/backends/props_singapore.json

Large diffs are not rendered by default.

219 changes: 219 additions & 0 deletions test/benchmarks/mapping_passes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2019.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

# pylint: disable=no-member,invalid-name,missing-docstring,no-name-in-module
# pylint: disable=attribute-defined-outside-init,unsubscriptable-object
# pylint: disable=unused-wildcard-import,wildcard-import,undefined-variable

from qiskit.transpiler import CouplingMap
from qiskit.transpiler.passes import *
from qiskit.converters import circuit_to_dag

from .backends import fake_singapore
from .utils import random_circuit


class PassBenchmarks:

params = ([1, 2, 5, 8, 14, 20],
[8, 128, 1024])

param_names = ['n_qubits', 'depth']
timeout = 300

def setup(self, n_qubits, depth):
seed = 42
self.circuit = random_circuit(n_qubits, depth, measure=True,
conditional=True, reset=True, seed=seed,
max_operands=2)
self.fresh_dag = circuit_to_dag(self.circuit)
self.basis_gates = ['u1', 'u2', 'u3', 'cx', 'iid']
self.cmap = [[0, 1], [1, 0], [1, 2], [1, 6], [2, 1], [2, 3], [3, 2],
[3, 4], [3, 8], [4, 3], [5, 6], [5, 10], [6, 1], [6, 5],
[6, 7], [7, 6], [7, 8], [7, 12], [8, 3], [8, 7], [8, 9],
[9, 8], [9, 14], [10, 5], [10, 11], [11, 10], [11, 12],
[11, 16], [12, 7], [12, 11], [12, 13], [13, 12], [13, 14],
[13, 18], [14, 9], [14, 13], [15, 16], [16, 11], [16, 15],
[16, 17], [17, 16], [17, 18], [18, 13], [18, 17],
[18, 19], [19, 18]]
self.coupling_map = CouplingMap(self.cmap)

layout_pass = DenseLayout(self.coupling_map)
layout_pass.run(self.fresh_dag)
self.layout = layout_pass.property_set['layout']
full_ancilla_pass = FullAncillaAllocation(self.coupling_map)
full_ancilla_pass.property_set['layout'] = self.layout
self.full_ancilla_dag = full_ancilla_pass.run(self.fresh_dag)
enlarge_pass = EnlargeWithAncilla()
enlarge_pass.property_set['layout'] = self.layout
self.enlarge_dag = enlarge_pass.run(self.full_ancilla_dag)
apply_pass = ApplyLayout()
apply_pass.property_set['layout'] = self.layout
self.dag = apply_pass.run(self.enlarge_dag)
self.backend_props = fake_singapore.FakeSingapore().properties()

def time_stochastic_swap(self, _, __):
swap = StochasticSwap(self.coupling_map, seed=42)
swap.property_set['layout'] = self.layout
swap.run(self.dag)

def peakmem_stochastic_swap(self, _, __):
swap = StochasticSwap(self.coupling_map, seed=42)
swap.property_set['layout'] = self.layout
swap.run(self.dag)

def track_stochastic_swap_depth(self, _, __):
swap = StochasticSwap(self.coupling_map, seed=42)
swap.property_set['layout'] = self.layout
return swap.run(self.dag).depth()

def track_stochastic_swap_swap_count(self, _, __):
swap = StochasticSwap(self.coupling_map, seed=42)
swap.property_set['layout'] = self.layout
return swap.run(self.dag).count_ops().get('swap')

def time_lookahead_swap(self, _, __):
swap = LookaheadSwap(self.coupling_map)
swap.property_set['layout'] = self.layout
swap.run(self.dag)

def peakmem_lookahead_swap(self, _, __):
swap = LookaheadSwap(self.coupling_map)
swap.property_set['layout'] = self.layout
swap.run(self.dag)

def track_lookahead_swap_depth(self, _, __):
swap = LookaheadSwap(self.coupling_map)
swap.property_set['layout'] = self.layout
return swap.run(self.dag).depth()

def track_lookahead_swap_swap_count(self, _, __):
swap = LookaheadSwap(self.coupling_map)
swap.property_set['layout'] = self.layout
return swap.run(self.dag).depth().count_ops().get('swap')

def time_basic_swap(self, _, __):
swap = BasicSwap(self.coupling_map)
swap.property_set['layout'] = self.layout
swap.run(self.dag)

def peakmem_basic_swap(self, _, __):
swap = BasicSwap(self.coupling_map)
swap.property_set['layout'] = self.layout
swap.run(self.dag)

def track_basic_swap_depth(self, _, __):
swap = BasicSwap(self.coupling_map)
swap.property_set['layout'] = self.layout
return swap.run(self.dag).depth()

def track_basic_swap_swap_count(self, _, __):
swap = BasicSwap(self.coupling_map)
swap.property_set['layout'] = self.layout
return swap.run(self.dag).depth().count_ops().get('swap')

def time_csp_layout(self, _, __):
CSPLayout(self.coupling_map, seed=42).run(self.fresh_dag)

def peakmem_csp_layout(self, _, __):
CSPLayout(self.coupling_map, seed=42).run(self.fresh_dag)

def time_dense_layout(self, _, __):
DenseLayout(self.coupling_map).run(self.fresh_dag)

def peakmem_dense_layout(self, _, __):
DenseLayout(self.coupling_map).run(self.fresh_dag)

def time_layout_2q_distance(self, _, __):
layout = Layout2qDistance(self.coupling_map)
layout.property_set['layout'] = self.layout
layout.run(self.dag)

def peakmem_layout_2q_distance(self, _, __):
layout = Layout2qDistance(self.coupling_map)
layout.property_set['layout'] = self.layout
layout.run(self.dag)

def time_cxdirection(self, _, __):
CXDirection(self.coupling_map).run(self.dag)

def peakmem_cxdirection(self, _, __):
CXDirection(self.coupling_map).run(self.dag)

def track_cxdirection_depth(self, _, __):
return CXDirection(self.coupling_map).run(self.dag).depth()

def track_cxdirection_cnot_count(self, _, __):
return CXDirection(
self.coupling_map).run(self.dag).count_ops().get('cx')

def time_apply_layout(self, _, __):
layout = ApplyLayout()
layout.property_set['layout'] = self.layout
layout.run(self.dag)

def peakmem_apply_layout(self, _, __):
layout = ApplyLayout()
layout.property_set['layout'] = self.layout
layout.run(self.dag)

def time_full_ancilla_allocation(self, _, __):
ancilla = FullAncillaAllocation(self.coupling_map)
ancilla.property_set['layout'] = self.layout
ancilla.run(self.fresh_dag)

def peakmem_full_ancilla_allocation(self, _, __):
ancilla = FullAncillaAllocation(self.coupling_map)
ancilla.property_set['layout'] = self.layout
ancilla.run(self.fresh_dag)

def time_enlarge_with_ancilla(self, _, __):
ancilla = EnlargeWithAncilla()
ancilla.property_set['layout'] = self.layout
ancilla.run(self.full_ancilla_dag)

def peakmem_enlarge_with_ancilla(self, _, __):
ancilla = EnlargeWithAncilla()
ancilla.property_set['layout'] = self.layout
ancilla.run(self.full_ancilla_dag)

def time_check_map(self, _, __):
CheckMap(self.coupling_map).run(self.dag)

def peakmem_check_map(self, _, __):
CheckMap(self.coupling_map).run(self.dag)

def time_check_cx_direction(self, _, __):
CheckCXDirection(self.coupling_map).run(self.dag)

def peakmem_check_cx_direction(self, _, __):
CheckCXDirection(self.coupling_map).run(self.dag)

def time_trivial_layout(self, _, __):
TrivialLayout(self.coupling_map).run(self.fresh_dag)

def peakmem_trivial_layout(self, _, __):
TrivialLayout(self.coupling_map).run(self.fresh_dag)

def time_set_layout(self, _, __):
SetLayout(self.layout).run(self.fresh_dag)

def peakmem_set_layout(self, _, __):
SetLayout(self.layout).run(self.fresh_dag)

def time_noise_adaptive_layout(self, _, __):
NoiseAdaptiveLayout(self.backend_props).run(self.fresh_dag)

def peakmem_noise_adaptive_layout(self, _, __):
NoiseAdaptiveLayout(self.backend_props).run(self.fresh_dag)

0 comments on commit 3f6e209

Please sign in to comment.