Skip to content

Stats

We generate timing statistics using pytest-benchmark using \(R\) rounds for circuits of type A 1 as ansatze for a variational quantum eigensolver task2 (VQE) for the \(H2\) molecule in the STO-3G basis with a bondlength of \(0.742 \mathring{A}\)3. The underlying gradient-based Adam optimizer is run for \(50\) iterations without shots and \(10\) when using shots. The circuits are defined over \(4, 6\) qubits \(R=5\) for avoiding long jobs time on Github. Additionally, we benchmark two differentiation modes (automatic differentiation and the Adjoint method 1). Finally, we also performing shot-based benchmarks, we use \(100\) shots.

Note we are disabling shots due to an issue.

Variational Quantum Eigensolver

Here are the median execution times for VQE. We compare optimizing with PyQTorch against optimizing with Horqrux and jitting.

import json
import pandas as pd
import re
import matplotlib.pyplot as plt

import os.path

fname = "stats_vqe_noshots.json"
fnameshots = "stats_vqe_shots.json"

if not os.path.isfile(fname):
    fname = "docs/stats_vqe_noshots.json"
    fnameshots = "docs/stats_vqe_shots.json"
with open(fname, 'r') as f:
    data_vqe = json.load(f)['benchmarks']
with open(fnameshots, 'r') as f:
    data_vqeshots = json.load(f)['benchmarks']

data_stats_vqe = [{'name': x['name']} | x['params'] | x['stats'] for x in data_vqe]
data_stats_vqeshots = [{'name': x['name']} | x['params'] | x['stats'] for x in data_vqeshots]

frame_vqe = pd.DataFrame(data_stats_vqe)
frame_vqe['n_qubits'] = frame_vqe['name'].apply(lambda x: int(re.findall('n:(.*)\\D:', x)[0]))
frame_vqe['name'] = frame_vqe['name'].apply(lambda x: re.findall('test_(.*)\\[', x)[0])
frame_vqe['fn_circuit'] = frame_vqe['benchmark_vqe_ansatz'].apply(str)
frame_vqe['fn_circuit'] = frame_vqe['fn_circuit'].apply(lambda x: re.findall('function (.*) at', x)[0])
frame_vqe['name'] = frame_vqe['name'].str.replace('vqe_', '')
frame_vqe['n_shots'] = 0

frame_vqeshots = pd.DataFrame(data_stats_vqeshots)
frame_vqeshots['n_qubits'] = frame_vqeshots['name'].apply(lambda x: int(re.findall('n:(.*)\\D:', x)[0]))
frame_vqeshots['name'] = frame_vqeshots['name'].apply(lambda x: re.findall('test_(.*)\\[', x)[0])
frame_vqeshots['fn_circuit'] = frame_vqeshots['benchmark_vqe_ansatz'].apply(str)
frame_vqeshots['fn_circuit'] = frame_vqeshots['fn_circuit'].apply(lambda x: re.findall('function (.*) at', x)[0])
frame_vqeshots['name'] = frame_vqeshots['name'].str.replace('vqe_', '')
frame_vqeshots['diff_mode'] = 'ad'
frame_vqeshots['n_shots'] = 100

nqubits = frame_vqe.n_qubits.unique()

Timings

Below we present the distribution of median times for each circuit type, with and without shots.

for nq in nqubits:
    axes = frame_vqe[frame_vqe.n_qubits == nq].boxplot('median', by=['fn_circuit', 'name', 'diff_mode'])
    axes.set_title(f"Timing distributions by differentiation methods and circuit \n without shots - {nq} qubits")
    axes.set_xlabel('')
    axes.set_ylabel('Time (s)')
    plt.xticks(rotation=75)
    plt.suptitle('')
    plt.tight_layout()



    axes = frame_vqeshots[frame_vqeshots.n_qubits == nq].boxplot('median', by=['fn_circuit', 'name'])
    axes.set_title(f"Timing distributions by differentiation methods and circuit \n with shots - {nq} qubits")
    axes.set_xlabel('')
    axes.set_ylabel('Time (s)')
    plt.xticks(rotation=75)
    plt.suptitle('')
    plt.tight_layout()
2025-06-28T09:12:02.970082 image/svg+xml Matplotlib v3.10.3, https://matplotlib.org/ 2025-06-28T09:12:03.062765 image/svg+xml Matplotlib v3.10.3, https://matplotlib.org/ 2025-06-28T09:12:03.168525 image/svg+xml Matplotlib v3.10.3, https://matplotlib.org/ 2025-06-28T09:12:03.259582 image/svg+xml Matplotlib v3.10.3, https://matplotlib.org/

Speed-ups

Below we present the distribution of median speed-ups for each circuit type. The timing ratio between PyQTorch (numerator) and Horqrux (denominator) executions is computed. A ratio higher than \(1\) means Horqrux and jitting provide computational speed-up over PyQTorch.

frame_vqe = pd.concat([frame_vqe, frame_vqeshots], ignore_index=True)

pyq_vqe = frame_vqe[frame_vqe.name == 'pyq'][['fn_circuit', 'median', 'n_shots', 'diff_mode', 'n_qubits']]
horqrux_vqe = frame_vqe[frame_vqe.name == 'horqrux'][['fn_circuit', 'median', 'n_shots', 'diff_mode', 'n_qubits']]
ratio_df = pd.merge(pyq_vqe, horqrux_vqe, on=['fn_circuit', 'diff_mode', 'n_shots', 'n_qubits'], suffixes=['_pyq', '_horqrux'])
ratio_df['ratio'] = ratio_df['median_pyq'] / ratio_df['median_horqrux']


axes = ratio_df[ratio_df.n_shots == 0].boxplot('ratio', by=['fn_circuit', 'diff_mode', 'n_qubits'])
axes.set_title(f"Speedup distributions by circuit and qubit number without shots")
axes.set_xlabel('')
axes.set_ylabel('Speedup')
plt.xticks(rotation=75)
plt.suptitle('')
plt.tight_layout()


axes = ratio_df[ratio_df.n_shots > 0].boxplot('ratio', by=['fn_circuit', 'n_qubits'])
axes.set_title(f"Speedup distributions by circuit and qubit number with shots")
axes.set_xlabel('')
axes.set_ylabel('Speedup')
plt.xticks(rotation=75)
plt.suptitle('')
plt.tight_layout()
2025-06-28T09:12:03.402010 image/svg+xml Matplotlib v3.10.3, https://matplotlib.org/ 2025-06-28T09:12:03.487988 image/svg+xml Matplotlib v3.10.3, https://matplotlib.org/