Skip to content
This repository has been archived by the owner on Aug 14, 2024. It is now read-only.

Added sweeps for number of qubits #47

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ def pytest_addoption(parser):
dest="store_data",
help="",
)

group.addoption(
"--num_qubits",
default="linear:10:100:50",
help="",
)


@pytest.mark.trylast
def pytest_configure(config):
Expand Down
37 changes: 30 additions & 7 deletions red_queen/games/applications/run_bv.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# ------------------------------------------------------------------------------

"""Benchmark Bernstein Vazirani circuits."""

import os

import pytest
Expand All @@ -13,9 +12,30 @@

from red_queen.games.applications import backends, run_qiskit_circuit

import random

import requests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed offline, I don't think this import is needed and is likely the cause of the errors on import since requests is an external python library for HTTP: https://pypi.org/project/requests/ and it's not being part of red queens requirements.


import numpy as np

QASM_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "qasm")
SECRET_STRING = "110011"


@pytest.fixture
def get_num_qubits(request):
user_params = request.config.getoption("--num_qubits")
user_params = user_params.split(":")
if user_params[0] == "linear":
sweep = np.linspace(int(user_params[1]), int(user_params[2]), int(user_params[3]))
sweep = [int(i) for i in sweep]
Comment on lines +29 to +30
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can simplify this by specifying the dtype directly:

Suggested change
sweep = np.linspace(int(user_params[1]), int(user_params[2]), int(user_params[3]))
sweep = [int(i) for i in sweep]
sweep = np.linspace(int(user_params[1]), int(user_params[2]), int(user_params[3]), dtype=int)

This will return a numpy array with an integer datatype

if user_params[0] == "log":
sweep = np.logspace(int(user_params[1]), int(user_params[2]), int(user_params[3]))
sweep = [int(i) for i in sweep]
return sweep


sweep_list = list(requests.get(get_num_qubits))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this might need to be updated to call the function directly without requests.

default_secret_string = "110011"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect this will fail pylint because it likes constants to be all caps, but you can check with tox -elint to confirm and fix it if it does have issues.



def build_bv_circuit(secret_string, mid_circuit_measure=False):
Expand Down Expand Up @@ -58,19 +78,22 @@ def build_bv_circuit(secret_string, mid_circuit_measure=False):
@pytest.mark.parametrize("optimization_level", [0, 1, 2, 3])
@pytest.mark.parametrize("backend", backends)
@pytest.mark.parametrize("method", ["normal", "mid-circuit measurement"])
def bench_qiskit_bv(benchmark, optimization_level, backend, method):
@pytest.mark.parametrize("num_qubits", sweep_list)
def bench_qiskit_bv(benchmark, optimization_level, backend, method, num_qubits):
shots = 65536
SECRET_STRING = str(bin(random.getrandbits(num_qubits - 1))[2:].zfill(num_qubits-1))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of random here is potentially problematic from a reproducibility perspective. Since we're expecting to run these benchmarks for comparison purposes the selected secret string will potentially be different between runs for a fixed input parameter set. Using an random number generator to pick the exact secret string is fine but we should set it with a fixed seed: https://docs.python.org/3/library/random.html#random.seed to ensure the values are consistent/reproducible between runs.

expected_counts = {SECRET_STRING: shots}

if method == "normal":
benchmark.name = "Bernstein Vazirani"
circ = QuantumCircuit.from_qasm_file(os.path.join(QASM_DIR, "bv.qasm"))
circ = build_bv_circuit(SECRET_STRING)
else:
benchmark.name = "Bernstein Vazirani (mid-circuit measurement)"
circ = QuantumCircuit.from_qasm_file(os.path.join(QASM_DIR, "bv_mcm.qasm"))
circ = build_bv_circuit(SECRET_STRING, True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should check that the number of circuit qubits is <= the number of backend qubits. If not this will cause a runtime error and it'd be better to handle that gracefully.

benchmark.algorithm = f"Optimization level: {optimization_level} on {backend.name()}"
run_qiskit_circuit(benchmark, circ, backend, optimization_level, shots, expected_counts)


if __name__ == "__main__":
build_bv_circuit(SECRET_STRING).qasm(filename=os.path.join(QASM_DIR, "bv.qasm"))
build_bv_circuit(SECRET_STRING, True).qasm(filename=os.path.join(QASM_DIR, "bv_mcm.qasm"))
build_bv_circuit(default_secret_string).qasm(filename=os.path.join(QASM_DIR, "bv.qasm"))
build_bv_circuit(default_secret_string, True).qasm(filename=os.path.join(QASM_DIR, "bv_mcm.qasm"))
Comment on lines 97 to +99
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're changing the execution model of these benchmarks to no longer depend on hard coded qasm files we can delete this block of code now as we don't need to regenerate qasm files. Similarly we can remove the bv*.qasm files at the same time because nothing will use them anymore.

34 changes: 28 additions & 6 deletions red_queen/games/applications/run_ft.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,38 @@
import pytest
from red_queen.games.applications import backends, run_qiskit_circuit
import numpy as np
import requests
import random

# Importing standard Qiskit libraries
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister


DIRECTORY = "qasm"
QASM_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), DIRECTORY)
SECRET_STRING = "11111111"


@pytest.fixture
def get_num_qubits(request):
user_params = request.config.getoption("--num_qubits")
user_params = user_params.split(":")
if user_params[0] == "linear":
sweep = np.linspace(int(user_params[1]), int(user_params[2]), int(user_params[3]))
sweep = [int(i) for i in sweep]
if user_params[0] == "log":
sweep = np.logspace(int(user_params[1]), int(user_params[2]), int(user_params[3]))
sweep = [int(i) for i in sweep]
return sweep


sweep_list = requests.get(get_num_qubits)
default_secret_string = "110011"


""" Generates Quantum Fourier Transform circuit using QFT and Inverse QFT"""


@pytest.mark.parametrize("num_qubits", sweep_list)
def generate_ft_circuit_1(binary):
qubits = QuantumRegister(len(binary))
bits = ClassicalRegister(len(binary))
Expand Down Expand Up @@ -87,23 +106,26 @@ def generate_ft_circuit_2(binary):
@pytest.mark.parametrize("optimization_level", [0, 1, 2, 3])
@pytest.mark.parametrize("backend", backends)
@pytest.mark.parametrize("method", ["1", "2"])
def bench_qiskit_ft(benchmark, optimization_level, backend, method):
@pytest.mark.parametrize("num_qubits", sweep_list)
def bench_qiskit_ft(benchmark, optimization_level, backend, method, num_qubits):
shots = 65536
SECRET_STRING = str(bin(random.getrandbits(num_qubits - 1))[2:].zfill(num_qubits-1))
integer_value = int(SECRET_STRING, 2)
binary_1 = format((integer_value + 1) % (2 ** (len(SECRET_STRING))), "b").zfill(
len(SECRET_STRING)
)
expected_counts = {binary_1: shots} if method == "1" else {SECRET_STRING: shots}
if method == "1":
benchmark.name = "Quantum Fourier Transform v1"
circ = QuantumCircuit.from_qasm_file(os.path.join(QASM_DIR, "ft_1.qasm"))
circ = generate_ft_circuit_1(SECRET_STRING)
else:
benchmark.name = "Quantum Fourier Transform v2"
circ = QuantumCircuit.from_qasm_file(os.path.join(QASM_DIR, "ft_2.qasm"))
circ = generate_ft_circuit_2(SECRET_STRING)

benchmark.algorithm = f"Optimization level: {optimization_level} on {backend.name()}"
run_qiskit_circuit(benchmark, circ, backend, optimization_level, shots, expected_counts)


if __name__ == "__main__":
generate_ft_circuit_1(SECRET_STRING).qasm(filename=os.path.join(QASM_DIR, "ft_1.qasm"))
generate_ft_circuit_2(SECRET_STRING).qasm(filename=os.path.join(QASM_DIR, "ft_2.qasm"))
generate_ft_circuit_1(default_secret_string).qasm(filename=os.path.join(QASM_DIR, "ft_1.qasm"))
generate_ft_circuit_2(default_secret_string).qasm(filename=os.path.join(QASM_DIR, "ft_2.qasm"))