This plugin provides an interface between Graphix and the MQT Bench suite for benchmarking purposes.
This package supports uv:
git clone https://github.com/TeamGraphix/graphix-mqtbench.git
cd graphix-mqtbench
uv syncThis creates a virtual environment and installs the necessary dependencies from the pyproject.toml and uv lockfile.
Note: This package depends on the graphix-qasm-parser plugin to transpile qiskit circuits into Graphix circuits.
The package provides a wrapper around MQT Bench quantum circuits. Given a benchmark name and a qubit count, the class MQTBenchmark exposes a raw Qiskit circuit, and the corresponding Graphix circuit and pattern.
from graphix_mqtbench import MQTBenchmark, BenchmarkName
bench = MQTBenchmark(name=BenchmarkName.QFT, nqubits=2)
pattern = bench.pattern
pattern.to_bloch().draw()The definitions in _benchmark_names.py depend on the version of the mqtbench package. In particular, mqtbench 2.2.3 introduced the new benchmarks dynamic_qft and iqpe which contain feed-forward primitives that are still unrepresentable on Graphix circuits, so test_benchmark_names fails if mqtbench 2.2.3 is used with the current code. We currently pin mqtbench to version 2.2.2 and provide the script _generate_benchmark_names.py, which developers can run to regenerate _benchmark_names.py. Running
uv run python _generate_benchmark_names.pyregenerates _benchmark_names.py.
To run this notebook, install the package with extra dependencies:
uv sync --extra examplesAs of version 0.3.5, Graphix supports two optimizations at the pattern level: space minimization and Pauli removal.
-
Space minimization rearranges the pattern commands in order to minimize the maximum number of qubits alive at any given time during the execution. This optimization is crucial to reduce the memory allocation in dense-state simulations. For patterns with causal flow (e.g., those directly transpiled from a quantum circuit),
Pattern.minimize_spacereturns an optimal vale:max_space = n_qubits + 1. -
Pauli removal removes Pauli measurements on non-input qubits at the expense of adding local Clifford commands. This optimization can significantly reduce the number of commands (and, specially, measurement commands which are the bottleneck in dense-state simulations). However, it comes with a trade-off: patterns with causal flow are only guaranteed to have gflow after this optimization step. Performing space minimization on patterns without causal flow is known to be an NP-hard problem, and heuristics can fail to find a "good" measurement order.
As show in the table below, "Pauli removal + Space minimization" will often reduce the number of commands but max_space can become significantly larger than if only "Space minimization" is applied.
from graphix_mqtbench import generate_benchmarks
import pandas as pd
nqubit = 4
benchmarks = generate_benchmarks(nqubit)
rows = []
for bench in benchmarks:
p = bench.pattern
p_space = p.minimize_space(copy=True)
p_pauli = p.infer_pauli_measurements().remove_pauli_measurements(copy=True)
p_pauli_space = p_pauli.minimize_space(copy=True)
rows.append({
("Circuit", "Benchmark"): bench.name.value,
("Circuit", "Qubits"): bench.nqubits,
("Circuit", "Gates"): len(bench.circuit.instruction),
("Transpilation", "Max Space"): p.max_space(),
("Transpilation", "Cmds"): len(p),
("Space min.", "Max Space"): p_space.max_space(),
("Space min.", "Cmds"): len(p_space),
("Pauli removal", "Max Space"): p_pauli.max_space(),
("Pauli removal", "Cmds"): len(p_pauli),
("Pauli removal + Space min.", "Max Space"): p_pauli_space.max_space(),
("Pauli removal + Space min.", "Cmds"): len(p_pauli_space),
})
df = pd.DataFrame(rows)
df.columns = pd.MultiIndex.from_tuples(df.columns)
df| Circuit | Transpilation | Space min. | Pauli removal | Pauli removal + Space min. | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Benchmark | Qubits | Gates | Max Space | Cmds | Max Space | Cmds | Max Space | Cmds | Max Space | Cmds | |
| 0 | ae | 4 | 119 | 5 | 716 | 5 | 716 | 23 | 183 | 10 | 183 |
| 1 | bmw_quark_cardinality | 4 | 123 | 5 | 656 | 5 | 656 | 12 | 167 | 10 | 167 |
| 2 | bmw_quark_copula | 4 | 72 | 5 | 396 | 5 | 396 | 12 | 145 | 12 | 145 |
| 3 | bv | 4 | 4 | 5 | 17 | 5 | 17 | 6 | 11 | 5 | 11 |
| 4 | cdkm_ripple_carry_adder | 4 | 7 | 5 | 235 | 5 | 235 | 20 | 99 | 20 | 99 |
| 5 | dj | 4 | 16 | 5 | 89 | 5 | 89 | 5 | 23 | 5 | 23 |
| 6 | draper_qft_adder | 4 | 29 | 5 | 180 | 5 | 180 | 13 | 54 | 9 | 54 |
| 7 | full_adder | 4 | 6 | 5 | 228 | 5 | 228 | 14 | 74 | 12 | 74 |
| 8 | ghz | 4 | 4 | 5 | 32 | 5 | 32 | 5 | 28 | 5 | 28 |
| 9 | graphstate | 4 | 8 | 5 | 24 | 5 | 24 | 5 | 24 | 5 | 24 |
| 10 | grover | 4 | 132 | 5 | 939 | 5 | 939 | 19 | 309 | 19 | 309 |
| 11 | hhl | 4 | 49 | 5 | 306 | 5 | 306 | 16 | 94 | 16 | 94 |
| 12 | modular_adder | 4 | 31 | 5 | 180 | 5 | 180 | 12 | 57 | 9 | 57 |
| 13 | multiplier | 4 | 43 | 5 | 397 | 5 | 397 | 22 | 123 | 8 | 123 |
| 14 | qaoa | 4 | 24 | 5 | 151 | 5 | 151 | 8 | 66 | 8 | 66 |
| 15 | qft | 4 | 34 | 5 | 212 | 5 | 212 | 17 | 79 | 11 | 79 |
| 16 | qftentangled | 4 | 38 | 5 | 236 | 5 | 236 | 13 | 98 | 9 | 98 |
| 17 | qnn | 4 | 27 | 5 | 197 | 5 | 197 | 6 | 66 | 5 | 66 |
| 18 | qpeexact | 4 | 25 | 5 | 154 | 5 | 154 | 9 | 51 | 9 | 51 |
| 19 | qpeinexact | 4 | 37 | 5 | 224 | 5 | 224 | 17 | 90 | 8 | 90 |
| 20 | qwalk | 4 | 250 | 5 | 2135 | 5 | 2135 | 117 | 695 | 109 | 695 |
| 21 | randomcircuit | 4 | 108 | 5 | 968 | 5 | 968 | 45 | 299 | 24 | 299 |
| 22 | rg_qft_multiplier | 4 | 40 | 5 | 252 | 5 | 252 | 15 | 96 | 13 | 96 |
| 23 | vbe_ripple_carry_adder | 4 | 6 | 5 | 228 | 5 | 228 | 14 | 74 | 12 | 74 |
| 24 | vqe_real_amp | 4 | 25 | 5 | 263 | 5 | 263 | 8 | 112 | 8 | 112 |
| 25 | vqe_su2 | 4 | 89 | 5 | 551 | 5 | 551 | 11 | 167 | 8 | 167 |
| 26 | vqe_two_local | 4 | 34 | 5 | 326 | 5 | 326 | 10 | 110 | 8 | 110 |
| 27 | wstate | 4 | 13 | 5 | 110 | 5 | 110 | 10 | 58 | 7 | 58 |
import timeit
from graphix_mqtbench import MQTBenchmark, BenchmarkName
import numpy as np
rng = np.random.default_rng(42)
def simulate(pattern, backend):
def run():
return pattern.simulate_pattern(backend=backend, rng=rng)
return run
benchmark = MQTBenchmark(name=BenchmarkName.QFT, nqubits=14)
pattern = benchmark.pattern.minimize_space()
run = simulate(pattern, backend="statevector")
timer = timeit.Timer(run)
t = min(timer.repeat(number=1, repeat=5))
print(
f"Benchmark = {benchmark.name.value}\n\
nqubits = {benchmark.nqubits}\n\
max_space = {pattern.max_space()}\n\
n_commands = {len(pattern)}\n\
simulation time = {t:.5f} s")Benchmark = qft
nqubits = 14
max_space = 15
n_commands = 2982
simulation time = 0.65946 s
The function graphix_mqtbench.converter.qiskit_to_graphix_circuit was developped by @ACE07-Sev for the unitaryDESIGN 2025 edition.
