Computation speed benchmark

Scope

This notebook is an environment-specific engineering benchmark. It helps estimate how statevector dimension affects the current local backend. Absolute timings should be compared only when the recorded machine and software environment are similar.

The benchmark measures mean execution time for several model operations and dimensions under the recorded environment.

import multiprocessing
import os
import platform
import re
import subprocess
import time

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from IPython.display import HTML, display

Local machine details

Log the local machine details on which the test is carried out:

def get_cpu_model(spec="model name"):
    if platform.system() == "Windows":
        return platform.processor()
    if platform.system() == "Darwin":
        try:
            return subprocess.check_output(
                ["sysctl", "-n", "machdep.cpu.brand_string"], text=True
            ).strip()
        except OSError, subprocess.CalledProcessError:
            return platform.processor() or platform.machine()
    if platform.system() == "Linux":
        command = "cat /proc/cpuinfo"
        stream = os.popen(command)
        all_info = stream.read()
        all_info.strip()
        all_info = all_info.split("\n")
        # all_info = str(subprocess.check_output(command, shell=True).strip())
        for line in all_info:
            if spec in line:
                return re.sub(f".*{spec}.*: ", "", line, 1)
def get_specs():
    table = [
        # ["", platform.version()],
        ["Machine", platform.machine()],
        ["Platform", platform.platform()],
        ["Architecture", platform.architecture()],
        ["Cores", get_cpu_model("model name")],
        ["Number of cores", multiprocessing.cpu_count()],
        ["Python version", platform.python_version()],
    ]
    dataframe = pd.DataFrame(table, columns=["Spec", "Value"])
    display(HTML(dataframe.to_html(index=False)))
get_specs()
/tmp/ipykernel_1122/3135866240.py:20: DeprecationWarning: 'count' is passed as positional argument
  return re.sub(f".*{spec}.*: ", "", line, 1)
Spec Value
Machine x86_64
Platform Linux-7.0.0-1004-aws-x86_64-with-glibc2.35
Architecture (64bit, )
Cores AMD EPYC 7R13 Processor
Number of cores 2
Python version 3.14.6

Test function

Create a test function to test the models:

def test_model(Model, n):

    init_time = list()
    encode_time = list()
    decode_time = list()
    total_time = list()

    # Iterations for computing mean and standard deviation
    iterations = 50

    for i in range(1, iterations + 1):
        # Initialization
        start_process = time.time()
        model = Model(n, 1)
        # Encoding
        start_encode = time.time()
        for dim in range(model.n):
            model.encode(0.5, dim)
        # Decoding
        start_decode = time.time()
        result = model.decode()
        end_process = time.time()

        # Store timings
        init_time.append(start_encode - start_process)
        encode_time.append(start_decode - start_encode)
        decode_time.append(end_process - start_decode)
        total_time.append(end_process - start_process)

    return [
        n,
        np.mean(init_time),
        np.mean(encode_time),
        np.mean(decode_time),
        np.mean(total_time),
        np.std(total_time),
    ]
def plot_results(df, title="Model"):
    """Plot all the results"""

    # Total timewith STD
    fig = plt.figure(figsize=(15, 6), dpi=150)

    df.plot(
        x="n",
        y=[4],
        yerr="Total STD",
        uplims=True,
        lolims=True,
        kind="line",
        color="r",
        ax=plt.gca(),
    )
    plt.title(f"{title} - Total time with STD")

    plt.grid(visible=True, which="major", linestyle="-")
    plt.grid(visible=True, which="minor", linestyle="--", alpha=0.2)
    plt.minorticks_on()

    plt.show()

    # Timings insight
    fig = plt.figure(figsize=(15, 6), dpi=150)

    df.plot(x="n", y=[1, 2, 3, 4], kind="line", ax=plt.gca())
    plt.title(f"{title} - Timings insight")

    plt.grid(visible=True, which="major", linestyle="-")
    plt.grid(visible=True, which="minor", linestyle="--", alpha=0.2)
    plt.minorticks_on()

    plt.show()

    # Zooming in
    print("Zooming in:")
    fig = plt.figure(figsize=(15, 6), dpi=150)

    df.plot(x="n", y=[1, 2], kind="line", ax=plt.subplot(1, 2, 1))
    plt.title("Initialization and encoding only")

    plt.grid(visible=True, which="major", linestyle="-")
    plt.grid(visible=True, which="minor", linestyle="--", alpha=0.2)
    plt.minorticks_on()

    ax = plt.subplot(1, 2, 2)
    df[1:19].plot(x="n", y=[1, 2, 3], kind="line", ax=ax)
    df[1:19].plot(x="n", y=[4], yerr="Total STD", kind="line", ax=ax)
    plt.title("Showing results for n < 20")

    plt.grid(visible=True, which="major", linestyle="-")
    plt.grid(visible=True, which="minor", linestyle="--", alpha=0.2)
    plt.minorticks_on()

    plt.show()

AngularModel

We test initialization, encoding, and decoding for an input of .5 on each dimension. The executable documentation caps the statevector benchmark at 12 qubits so it remains practical on documentation builders.

from qrobot.models import AngularModel

max_n = 12
table = list()

for n in range(1, max_n + 1):
    print(f"Testing n={n}", end="\r")
    table.append(test_model(AngularModel, n))
print("             ")

df_angular = pd.DataFrame(
    table, columns=["n", "Initialization", "Encode", "Decode", "Total", "Total STD"]
)
Testing n=1
Testing n=2
Testing n=3
Testing n=4
Testing n=5
Testing n=6
Testing n=7
Testing n=8
Testing n=9
Testing n=10
Testing n=11
Testing n=12
             

Plotting the results:

plot_results(df_angular, "AngularModel")
../_images/313b389fce4d65d5ae0ade674abd961ec0c2741a98d53bb0f871aad4ede7da3b.png ../_images/ba7bddbd53ec9085c189600afc7c3540767b3e8b478e972012efc4b158a6976a.png
Zooming in:
../_images/2f5c33a1ad3e1616d9d573d5fb53b84e4d5dcedd116f3e36b929126e0bae1320.png

Numerical values:

df_angular
n Initialization Encode Decode Total Total STD
0 1 0.000036 0.000018 0.000428 0.000481 0.000111
1 2 0.000033 0.000024 0.000461 0.000519 0.000496
2 3 0.000032 0.000031 0.000450 0.000512 0.000019
3 4 0.000033 0.000039 0.000539 0.000611 0.000116
4 5 0.000036 0.000047 0.000593 0.000676 0.000048
5 6 0.000034 0.000054 0.000723 0.000811 0.000123
6 7 0.000034 0.000059 0.000980 0.001074 0.000182
7 8 0.000036 0.000067 0.001362 0.001465 0.000185
8 9 0.000039 0.000077 0.002234 0.002349 0.000158
9 10 0.000051 0.000089 0.004187 0.004328 0.000323
10 11 0.000083 0.000114 0.011905 0.012102 0.000280
11 12 0.000086 0.000118 0.025033 0.025237 0.001038

LinearModel

We test initialization, encoding, and decoding for an input of .5 on each dimension up to the same 12-qubit practical documentation limit.

from qrobot.models import LinearModel

max_n = 12
table = list()

for n in range(1, max_n + 1):
    print(f"Testing n={n}", end="\r")
    table.append(test_model(LinearModel, n))
print("             ")


df_linear = pd.DataFrame(
    table, columns=["n", "Initialization", "Encode", "Decode", "Total", "Total STD"]
)
Testing n=1
Testing n=2
Testing n=3
Testing n=4
Testing n=5
Testing n=6
Testing n=7
Testing n=8
Testing n=9
Testing n=10
Testing n=11
Testing n=12
             
plot_results(df_linear, "LinearModel")
../_images/c8df5391f0c5e35860dc338311f952f855835df76128e606bd4f4179b6e6a7ce.png ../_images/8ff007ed026864c6c5584596a72d5639f51f441e1e0bb22a94dc6b748b834d07.png
Zooming in:
../_images/479c51bd12964acbbb35c6926832fa620f1e1b476e03d9f8c0f853df3c5ded36.png
df_linear
n Initialization Encode Decode Total Total STD
0 1 0.000034 0.000021 0.000421 0.000476 0.000135
1 2 0.000032 0.000027 0.000503 0.000562 0.000025
2 3 0.000031 0.000035 0.000627 0.000693 0.000100
3 4 0.000032 0.000042 0.000716 0.000789 0.000023
4 5 0.000030 0.000051 0.000603 0.000684 0.000163
5 6 0.000030 0.000053 0.000659 0.000741 0.000047
6 7 0.000032 0.000062 0.000870 0.000965 0.000133
7 8 0.000033 0.000066 0.001164 0.001263 0.000024
8 9 0.000036 0.000077 0.001894 0.002007 0.000147
9 10 0.000043 0.000083 0.003379 0.003504 0.000190
10 11 0.000068 0.000114 0.007084 0.007266 0.000303
11 12 0.000079 0.000118 0.014581 0.014778 0.000824

Comparison

fig = plt.figure(figsize=(15, 6), dpi=150)

df_angular.plot(x="n", y=[4], kind="line", ax=plt.gca())
df_linear.plot(x="n", y=[4], kind="line", ax=plt.gca())
plt.legend(["AngularModel", "LinearModel"])
plt.title("Total times comparison")

plt.grid(visible=True, which="major", linestyle="-")
plt.grid(visible=True, which="minor", linestyle="--", alpha=0.2)
plt.minorticks_on()

plt.show()
../_images/df2f78a5f0bc5d9c265599cad1bfc9d3ab542978a6dcd6365605d551f6683ff0.png
fig = plt.figure(figsize=(15, 6), dpi=150)

df_angular.plot(x="n", y=[5], kind="line", ax=plt.gca())
df_linear.plot(x="n", y=[5], kind="line", ax=plt.gca())

plt.legend(["AngularModel", "LinearModel"])
plt.title("Total times STD comparison")

plt.grid(visible=True, which="major", linestyle="-")
plt.grid(visible=True, which="minor", linestyle="--", alpha=0.2)
plt.minorticks_on()

plt.show()
../_images/f1c32b6900b7a66c33a6cf5ab62043eb0e8e83b4415c10c788e6fb0130e15da1.png
fig = plt.figure(figsize=(15, 6), dpi=150)

df_angular[1:19].plot(x="n", y=[4], kind="line", ax=plt.gca())
df_linear[1:19].plot(x="n", y=[4], kind="line", ax=plt.gca())

plt.legend(["AngularModel", "LinearModel"])
plt.title("Total times comparison (n < 20)")

plt.grid(visible=True, which="major", linestyle="-")
plt.grid(visible=True, which="minor", linestyle="--", alpha=0.2)
plt.minorticks_on()

plt.show()
../_images/ec10f368320566cb84117eba9acf345fc9be6fd3ce20882ccd815becde4770aa.png