Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrating BQSKit into MQTBench benchmark generation module #286

Draft
wants to merge 35 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
66c31de
➕ Add bqskit dependency
jagandecapri Jan 16, 2024
0bde489
🎉 Commit BQSKit helper module
jagandecapri Jan 16, 2024
60839c5
✅ Add bqskit tests to native and mapped levels
jagandecapri Jan 18, 2024
3b20140
🔧 Add bqskit to mypy overrides in pyproject.toml
jagandecapri Jan 18, 2024
ef31b7b
✅ Add bqskit tests to get benchmark test
jagandecapri Jan 18, 2024
773a2f6
✅ Add bqskit test for save qasm in alternative location
jagandecapri Jan 18, 2024
9dd359e
✅ Add bqskit test case for count occurrences from sample file
jagandecapri Jan 18, 2024
c494956
✅ Add bqskit test case to count qubit number
jagandecapri Jan 18, 2024
9b281e9
✅ Add bqskit test to count qubit number for mapped circuit
jagandecapri Jan 18, 2024
73bbb83
✨ Commit draft of bqskit helper module
jagandecapri Jan 18, 2024
9b627a1
✨ Commit draft of bqskit integration into benchmark generator
jagandecapri Jan 18, 2024
eeb55c0
✅ Rearrange test codes
jagandecapri Jan 18, 2024
db47eda
🐛 Qiskit sub-routines, e.g: QFT, need to be decomposed before giving …
jagandecapri Jan 18, 2024
09a0b76
🔥 Remove commented out code in bqskit_helper.py
jagandecapri Jan 19, 2024
a848561
📝 Update benchmark_generator.py to include BQSKit::Circuit as a possi…
jagandecapri Jan 19, 2024
b973e61
📝 Fix compiler order in benchmark_generator.py
jagandecapri Jan 19, 2024
4dbe280
🐛 Transpile circuit to QASM 2.0 compatible
jagandecapri Jan 19, 2024
f6941e3
🔥 Remove unnecessary code in indep level
jagandecapri Jan 19, 2024
b4d46f6
✨ Add custom ECRGate to bqskit_helper.py
jagandecapri Jan 19, 2024
f565786
✨ Add ECRGate to gate set
jagandecapri Jan 19, 2024
4b58409
🐛 Transpile circuit to QASM 2.0 compatible
jagandecapri Jan 19, 2024
4471f03
🐛 Transpile circuit to QASM 2.0 compatible
jagandecapri Jan 19, 2024
67e465b
🔥 Remove error threshold arg to compile call
jagandecapri Jan 20, 2024
5631883
🐛 Add seed parameter to compile function for reproducibility
jagandecapri Jan 20, 2024
8251496
⚡️ Add CachedCompiler class to improve performance
jagandecapri Jan 22, 2024
2175e16
⚡️ Refactor ECRGate unitary matrix initialization
jagandecapri Jan 22, 2024
c4d6d51
✨ Add XXPlusYY gate for Rigetti backend
jagandecapri Jan 22, 2024
086d9d2
✨ Add CPGate to gate set for Rigetti
jagandecapri Jan 22, 2024
56698e9
✨ Update benchmark generator to include bqskit_helper in indep level
jagandecapri Jan 22, 2024
73058ee
✅ Clean-up test cases for indep level
jagandecapri Jan 22, 2024
34b98ba
✅ Update native and mapped level test case
jagandecapri Jan 22, 2024
b15c493
🐛 Fix bug due from bqskit benchmark_generator
jagandecapri Jan 23, 2024
f5b0776
✅ Fix failing test case for saving qasm to alt loc
jagandecapri Jan 23, 2024
f426caf
✅ Add TODOs to test_bench.py
jagandecapri Jan 23, 2024
918a3c0
Merge branch 'main' into bqskit_benchmark_generator
jagandecapri Jan 23, 2024
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
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ requires-python = ">=3.9"
dynamic = ["version"]

dependencies = [
"bqskit>=1.1.1",
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would be great to add an upper cap as well.

"pytket-qiskit>=0.40.0,<0.48.0", # manages the dependencies for qiskit and tket in a combined fashion
"qiskit_optimization",
"qiskit_nature",
Expand Down Expand Up @@ -115,11 +116,11 @@ explicit_package_bases = true
pretty = true

[[tool.mypy.overrides]]
module = ["pytket.*", "qiskit_finance.*"]
module = ["bqskit.*", "pytket.*", "qiskit_finance.*"]
implicit_reexport = true

[[tool.mypy.overrides]]
module = ["qiskit.*", "qiskit_finance.*", "joblib.*", "networkx.*", "pandas.*"]
module = ["bqskit.*", "qiskit.*", "qiskit_finance.*", "joblib.*", "networkx.*", "pandas.*"]
ignore_missing_imports = true

[tool.ruff]
Expand Down
2 changes: 2 additions & 0 deletions src/mqt/bench/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from mqt.bench import qiskit_helper, tket_helper, utils
from mqt.bench.benchmark_generator import (
BenchmarkGenerator,
BQSKitSettings,
CompilerSettings,
QiskitSettings,
TKETSettings,
Expand All @@ -20,6 +21,7 @@
"tket_helper",
"utils",
"CompilerSettings",
"BQSKitSettings",
"QiskitSettings",
"TKETSettings",
]
109 changes: 100 additions & 9 deletions src/mqt/bench/benchmark_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from joblib import Parallel, delayed
from qiskit import QuantumCircuit

from mqt.bench import devices, qiskit_helper, tket_helper, utils
from mqt.bench import bqskit_helper, devices, qiskit_helper, tket_helper, utils
from mqt.bench.devices import (
get_available_provider_names,
get_available_providers,
Expand All @@ -22,6 +22,7 @@
if TYPE_CHECKING: # pragma: no cover
from types import ModuleType

from bqskit.ir import Circuit as BQSKitCircuit
from pytket.circuit import Circuit

if TYPE_CHECKING or sys.version_info >= (3, 10, 0): # pragma: no cover
Expand Down Expand Up @@ -49,6 +50,11 @@ class Benchmark(TypedDict, total=False):
precheck_possible: bool


@dataclass
class BQSKitSettings:
optimization_level: int = 1


@dataclass
class QiskitSettings:
optimization_level: int = 1
Expand All @@ -61,6 +67,7 @@ class TKETSettings:

@dataclass
class CompilerSettings:
bqskit: BQSKitSettings | None = None
qiskit: QiskitSettings | None = None
tket: TKETSettings | None = None

Expand Down Expand Up @@ -141,6 +148,31 @@ def generate_mapped_levels(
) -> None:
for provider in get_available_providers():
for device in provider.get_available_devices():
for opt_level in [1, 2, 3, 4]:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should be iterate over BQSKitSettings that cover those optimization levels? I am aware that this is not the case for Qiskit and TKET as well but desired (see #88). Therefore, I would like this new implementation to already follow this direction.

for parameter_instance in parameter_space:
qc = timeout_watcher(lib.create_circuit, self.timeout, parameter_instance)
if not qc:
break
assert isinstance(qc, QuantumCircuit)
if qc.num_qubits <= device.num_qubits:
res = timeout_watcher(
bqskit_helper.get_mapped_level,
self.timeout,
[
qc,
qc.num_qubits,
device,
opt_level,
file_precheck,
False,
self.qasm_output_path,
],
)
if not res:
break
else:
break

for opt_level in [0, 1, 2, 3]:
for parameter_instance in parameter_space:
qc = timeout_watcher(lib.create_circuit, self.timeout, parameter_instance)
Expand Down Expand Up @@ -198,6 +230,28 @@ def generate_native_gates_levels(
parameter_space: list[tuple[int, str]] | list[int] | list[str] | range,
) -> None:
for provider in get_available_providers():
for opt_level in [1, 2, 3, 4]:
Copy link
Collaborator

Choose a reason for hiding this comment

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

See above.

for parameter_instance in parameter_space:
qc = timeout_watcher(lib.create_circuit, self.timeout, parameter_instance)
if not qc:
break
assert isinstance(qc, QuantumCircuit)
res = timeout_watcher(
bqskit_helper.get_native_gates_level,
self.timeout,
[
qc,
provider,
qc.num_qubits,
opt_level,
file_precheck,
False,
self.qasm_output_path,
],
)
if not res:
break

for opt_level in [0, 1, 2, 3]:
for parameter_instance in parameter_space:
qc = timeout_watcher(lib.create_circuit, self.timeout, parameter_instance)
Expand Down Expand Up @@ -246,7 +300,7 @@ def generate_indep_levels(
lib: ModuleType,
parameter_space: list[tuple[int, str]] | list[int] | list[str] | range,
) -> None:
for function in [qiskit_helper.get_indep_level, tket_helper.get_indep_level]:
for function in [bqskit_helper.get_indep_level, qiskit_helper.get_indep_level, tket_helper.get_indep_level]:
for parameter_instance in parameter_space:
qc = timeout_watcher(lib.create_circuit, self.timeout, parameter_instance)
if not qc:
Expand All @@ -261,6 +315,21 @@ def generate_indep_levels(
break


@overload
def get_benchmark(
benchmark_name: str,
level: str | int,
circuit_size: int | None = None,
benchmark_instance_name: str | None = None,
compiler: Literal["bqskit"] = "bqskit",
compiler_settings: CompilerSettings | None = None,
provider_name: str = "ibm",
device_name: str = "ibm_washington",
**kwargs: str,
) -> BQSKitCircuit:
...


@overload
def get_benchmark(
benchmark_name: str,
Expand Down Expand Up @@ -302,7 +371,7 @@ def get_benchmark(
provider_name: str = "ibm",
device_name: str = "ibm_washington",
**kwargs: str,
) -> QuantumCircuit | Circuit:
) -> BQSKitCircuit | QuantumCircuit | Circuit:
...


Expand All @@ -316,21 +385,21 @@ def get_benchmark(
provider_name: str = "ibm",
device_name: str = "ibm_washington",
**kwargs: str,
) -> QuantumCircuit | Circuit:
) -> BQSKitCircuit | QuantumCircuit | Circuit:
"""Returns one benchmark as a qiskit.QuantumCircuit Object or a pytket.Circuit object.

Args:
benchmark_name: name of the to be generated benchmark
level: Choice of level, either as a string ("alg", "indep", "nativegates" or "mapped") or as a number between 0-3 where 0 corresponds to "alg" level and 3 to "mapped" level
circuit_size: Input for the benchmark creation, in most cases this is equal to the qubit number
benchmark_instance_name: Input selection for some benchmarks, namely "groundstate" and "shor"
compiler: "qiskit" or "tket"
CompilerSettings: Data class containing the respective compiler settings for the specified compiler (e.g., optimization level for Qiskit or placement for TKET)
compiler: "qiskit", "bqskit" or "tket"
CompilerSettings: Data class containing the respective compiler settings for the specified compiler (e.g., optimization level for BQSKit and Qiskit or placement for TKET)
provider_name: "ibm", "rigetti", "ionq", "oqc", or "quantinuum" (required for "nativegates" level)
device_name: "ibm_washington", "ibm_montreal", "rigetti_aspen_m2", "ionq_harmony", "ionq_aria1", "oqc_lucy", "quantinuum_h2" (required for "mapped" level)

Returns:
Quantum Circuit Object representing the benchmark with the selected options, either as Qiskit::QuantumCircuit or Pytket::Circuit object (depending on the chosen compiler---while the algorithm level is always provided using Qiskit)
Quantum Circuit Object representing the benchmark with the selected options, either as Qiskit::QuantumCircuit, BQSKit::Cicuit, or Pytket::Circuit object (depending on the chosen compiler---while the algorithm level is always provided using Qiskit)
"""

if "gate_set_name" in kwargs:
Expand Down Expand Up @@ -389,15 +458,21 @@ def get_benchmark(
raise ValueError(msg)

if compiler_settings is None:
compiler_settings = CompilerSettings(QiskitSettings(), TKETSettings())
compiler_settings = CompilerSettings(BQSKitSettings(), QiskitSettings(), TKETSettings())
elif not isinstance(compiler_settings, CompilerSettings):
msg = "compiler_settings must be of type CompilerSettings or None." # type: ignore[unreachable]
raise ValueError(msg)

assert (compiler_settings.tket is not None) or (compiler_settings.qiskit is not None)
assert (
(compiler_settings.bqskit is not None)
or (compiler_settings.tket is not None)
or (compiler_settings.qiskit is not None)
)

independent_level = 1
if level in ("indep", independent_level):
if compiler == "bqskit":
return bqskit_helper.get_indep_level(qc, circuit_size, False, True)
if compiler == "qiskit":
return qiskit_helper.get_indep_level(qc, circuit_size, False, True)
if compiler == "tket":
Expand All @@ -410,6 +485,10 @@ def get_benchmark(
native_gates_level = 2
if level in ("nativegates", native_gates_level):
provider = get_provider_by_name(provider_name)
if compiler == "bqskit":
assert compiler_settings.bqskit is not None
opt_level = compiler_settings.bqskit.optimization_level
return bqskit_helper.get_native_gates_level(qc, provider, circuit_size, opt_level, False, True)
if compiler == "qiskit":
assert compiler_settings.qiskit is not None
opt_level = compiler_settings.qiskit.optimization_level
Expand All @@ -424,6 +503,18 @@ def get_benchmark(
mapped_level = 3
if level in ("mapped", mapped_level):
device = get_device_by_name(device_name)
if compiler == "bqskit":
assert compiler_settings.bqskit is not None
opt_level = compiler_settings.bqskit.optimization_level
assert isinstance(opt_level, int)
return bqskit_helper.get_mapped_level(
qc,
circuit_size,
device,
opt_level,
False,
True,
)
if compiler == "qiskit":
assert compiler_settings.qiskit is not None
opt_level = compiler_settings.qiskit.optimization_level
Expand Down
Loading
Loading