diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 277adc783..556c14818 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,6 +35,14 @@ repos: - id: fix-ligatures - id: fix-smartquotes + # Check for common mistakes + - repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.10.0 + hooks: + - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal + # Clean jupyter notebooks - repo: https://github.com/srstevenson/nb-clean rev: "3.2.0" @@ -75,7 +83,7 @@ repos: rev: v1.9.0 hooks: - id: mypy - files: ^(src|tests|setup.py) + files: ^(src|tests) args: [] additional_dependencies: - pytket_qiskit @@ -88,3 +96,19 @@ repos: - types-requests - types-tqdm - types-flask + + # Catch common capitalization mistakes + - repo: local + hooks: + - id: disallow-caps + name: Disallow improper capitalization + language: pygrep + entry: PyBind|Numpy|Cmake|CCache|Github|PyTest|Mqt|Tum + exclude: .pre-commit-config.yaml + + # Check best practices for scientific Python code + - repo: https://github.com/scientific-python/cookie + rev: 2024.03.10 + hooks: + - id: sp-repo-review + additional_dependencies: ["repo-review[cli]"] diff --git a/docs/Abstraction_levels.rst b/docs/Abstraction_levels.rst index 9daba348d..8cac66996 100644 --- a/docs/Abstraction_levels.rst +++ b/docs/Abstraction_levels.rst @@ -52,7 +52,7 @@ shown above. Different quantum computer realizations support different native gate-sets. In our example, we consider the -`ibmq_manila` device as the target device which natively supports I, X, √X, Rz and CX gates. +``ibmq_manila`` device as the target device which natively supports I, X, √X, Rz and CX gates. Consequently, the Ry gates in the previous figure have to be converted using only these native gates. In this case, they are substituted by a sequence of X and Rz gates (denoted as • with a phase of −π). @@ -63,10 +63,10 @@ they are substituted by a sequence of X and Rz gates (denoted as • with a phas .. image:: /_static/arch.png :width: 15% - :alt: Illustration of the `ibmq_manila` device + :alt: Illustration of the ``ibmq_manila`` device :align: center -The architecture of the `ibmq_manila` device is shown above on the right and it defines between which qubits a two-qubit operation may be performed. +The architecture of the ``ibmq_manila`` device is shown above on the right and it defines between which qubits a two-qubit operation may be performed. Since the circuit shown in the previous figure contains CX gates operating between all combination of qubits, there is no mapping directly matching the target architecture's layout. As a consequence, a non-trivial mapping followed by a round of optimization leads to the resulting circuit @@ -80,4 +80,4 @@ shown below. This is also the reason for the different sequence of CX gates compared to the previous example. -This circuit is now executable on the `ibmq_manila` device, since all hardware induced requirements are fulfilled. +This circuit is now executable on the ``ibmq_manila`` device, since all hardware induced requirements are fulfilled. diff --git a/docs/Parameter.rst b/docs/Parameter.rst index b67fcf4ad..73ab5d6e4 100644 --- a/docs/Parameter.rst +++ b/docs/Parameter.rst @@ -16,7 +16,7 @@ The ``mqt.bench.get_benchmark`` method has the following signature: * ``circuit_size``\ : for most of the cases this is equal to number of qubits (all scalable benchmarks except ``"qwalk-v-chain"`` and ``"grover-v-chain"``\ ) while for all other the qubit number is higher * ``compiler``\ : ``"qiskit"`` or ``"tket"`` -* `compiler_settings`: Optimization level for `"qiskit"` (`0`-`3`), placement for `"tket"` (`lineplacement` or `graphplacement`), exemplary shown: +* ``compiler_settings``: Optimization level for ``"qiskit"`` (``0``-``3``), placement for ``"tket"`` (``lineplacement`` or ``graphplacement``), exemplary shown: .. code-block:: python diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 000000000..cfc1a70b4 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,63 @@ +"""Nox sessions.""" + +from __future__ import annotations + +import argparse +import os + +import nox + +nox.needs_version = ">=2024.3.2" +nox.options.default_venv_backend = "uv|virtualenv" + +PYTHON_ALL_VERSIONS = ["3.10", "3.11", "3.12"] + +BUILD_REQUIREMENTS = [ + "setuptools>=61", + "setuptools_scm>=7", +] + +if os.environ.get("CI", None): + nox.options.error_on_missing_interpreters = True + + +@nox.session(reuse_venv=True) +def lint(session: nox.Session) -> None: + """Lint the Python part of the codebase using pre-commit. + + Simply execute `nox -rs lint` to run all configured hooks. + """ + session.install("pre-commit") + session.run("pre-commit", "run", "--all-files", *session.posargs) + + +@nox.session(reuse_venv=True) +def docs(session: nox.Session) -> None: + """Build the docs. Use "--non-interactive" to avoid serving. Pass "-b linkcheck" to check links.""" + parser = argparse.ArgumentParser() + parser.add_argument("-b", dest="builder", default="html", help="Build target (default: html)") + args, posargs = parser.parse_known_args(session.posargs) + + serve = args.builder == "html" and session.interactive + extra_installs = ["sphinx-autobuild"] if serve else [] + session.install(*BUILD_REQUIREMENTS, *extra_installs) + session.install("--no-build-isolation", "-ve.[docs]") + session.chdir("docs") + + if args.builder == "linkcheck": + session.run("sphinx-build", "-b", "linkcheck", ".", "_build/linkcheck", *posargs) + return + + shared_args = ( + "-n", # nitpicky mode + "-T", # full tracebacks + f"-b={args.builder}", + ".", + f"_build/{args.builder}", + *posargs, + ) + + if serve: + session.run("sphinx-autobuild", *shared_args) + else: + session.run("sphinx-build", "--keep-going", *shared_args) diff --git a/pyproject.toml b/pyproject.toml index 4e8d376f8..b8860d4de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -89,6 +89,13 @@ testpaths = ["tests"] addopts = ["-ra", "--strict-markers", "--strict-config", "--showlocals"] log_cli_level = "INFO" xfail_strict = true +filterwarnings = [ + "error", + "ignore:.*pkg_resources.*:DeprecationWarning:", + "ignore:.*sre_.*:DeprecationWarning:", + "ignore:.*Rigetti.*:UserWarning:", + "ignore:.*Values in x.*:RuntimeWarning:", +] [tool.coverage] run.source = ["mqt.bench", "mqt.benchviewer"] @@ -108,7 +115,6 @@ mypy_path = "$MYPY_CONFIG_FILE_DIR/src" files = ["src", "tests", "setup.py"] python_version = "3.10" strict = true -show_error_codes = true enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] warn_unreachable = true explicit_package_bases = true @@ -126,12 +132,13 @@ ignore_missing_imports = true line-length = 120 extend-include = ["*.ipynb"] src = ["src"] +preview = true unsafe-fixes = true [tool.ruff.lint] extend-select = [ "A", # flake8-builtins -# "ANN", # flake8-annotations + "ANN", # flake8-annotations "ARG", # flake8-unused-arguments "ASYNC", # flake8-async "B", "B904", # flake8-bugbear @@ -141,11 +148,13 @@ extend-select = [ "EXE", # flake8-executable "FA", # flake8-future-annotations "FLY", # flynt + "FURB", # refurb "I", # isort "ICN", # flake8-import-conventions "ISC", # flake8-implicit-str-concat - # "N", # flake8-naming - # "NPY", # numpy + "LOG", # flake8-logging-format + "N", # flake8-naming + "NPY", # numpy "PERF", # perflint "PGH", # pygrep-hooks "PIE", # flake8-pie @@ -166,13 +175,18 @@ extend-select = [ "UP", # pyupgrade "YTT", # flake8-2020 ] -extend-ignore = [ - "PLR", # Design related pylint codes - "ISC001", # Added because of ruff-format warning +ignore = [ + "ANN101", # Missing type annotation for self in method + "ANN102", # Missing type annotation for cls in classmethod + "ISC001", # Conflicts with formatter + "E501", # Line too long (Black is enough) + "PLR", # Design related pylint codes + "S101", # Use of assert detected ] +flake8-unused-arguments.ignore-variadic-names = true isort.required-imports = ["from __future__ import annotations"] -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "*.pyi" = ["D"] # pydocstyle "*.ipynb" = [ "D", # pydocstyle @@ -180,5 +194,5 @@ isort.required-imports = ["from __future__ import annotations"] "I002", # Allow missing `from __future__ import annotations` import ] -[tool.ruff.pydocstyle] +[tool.ruff.lint.pydocstyle] convention = "google" diff --git a/src/mqt/bench/__init__.py b/src/mqt/bench/__init__.py index 2112cc9f9..8afb5fbca 100644 --- a/src/mqt/bench/__init__.py +++ b/src/mqt/bench/__init__.py @@ -12,14 +12,14 @@ ) __all__ = [ + "BenchmarkGenerator", + "CompilerSettings", + "QiskitSettings", + "TKETSettings", "generate", "get_benchmark", - "BenchmarkGenerator", - "timeout_watcher", "qiskit_helper", + "timeout_watcher", "tket_helper", "utils", - "CompilerSettings", - "QiskitSettings", - "TKETSettings", ] diff --git a/src/mqt/bench/benchmark_generator.py b/src/mqt/bench/benchmark_generator.py index 3ea6d4d0e..38013039b 100644 --- a/src/mqt/bench/benchmark_generator.py +++ b/src/mqt/bench/benchmark_generator.py @@ -332,7 +332,6 @@ def get_benchmark( 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) """ - if "gate_set_name" in kwargs: msg = "gate_set_name is deprecated and will be removed in a future release. Use provider_name instead." warn(msg, DeprecationWarning, stacklevel=2) @@ -466,18 +465,18 @@ def timeout_watcher( timeout: int, args: list[Any] | int | tuple[int, str] | str, ) -> bool | QuantumCircuit | Circuit: - class TimeoutException(Exception): # Custom exception class + class TimeoutExceptionError(Exception): # Custom exception class pass - def timeout_handler(_signum: Any, _frame: Any) -> None: # Custom signal handler - raise TimeoutException + def timeout_handler(_signum: int, _frame: Any) -> None: # noqa: ANN401 + raise TimeoutExceptionError # Change the behavior of SIGALRM signal.signal(signal.SIGALRM, timeout_handler) signal.alarm(timeout) try: res = func(*args) if isinstance(args, tuple | list) else func(args) - except TimeoutException: + except TimeoutExceptionError: print( "Calculation/Generation exceeded timeout limit for ", func.__name__, diff --git a/src/mqt/bench/benchmarks/__init__.py b/src/mqt/bench/benchmarks/__init__.py index 3562295e5..b1c7b9ebe 100644 --- a/src/mqt/bench/benchmarks/__init__.py +++ b/src/mqt/bench/benchmarks/__init__.py @@ -11,12 +11,12 @@ from mqt.bench.benchmarks.qiskit_application_optimization import routing, tsp __all__ = [ - "pricingput", - "pricingcall", + "groundstate", "portfolioqaoa", "portfoliovqe", - "groundstate", + "pricingcall", + "pricingput", + "qnn", "routing", "tsp", - "qnn", ] diff --git a/src/mqt/bench/benchmarks/ae.py b/src/mqt/bench/benchmarks/ae.py index eb6e160c5..9d3c9d70a 100644 --- a/src/mqt/bench/benchmarks/ae.py +++ b/src/mqt/bench/benchmarks/ae.py @@ -10,10 +10,9 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: """Returns a quantum circuit implementing Quantum Amplitude Estimation. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit """ - ae = AmplitudeEstimation( num_eval_qubits=num_qubits - 1, # -1 because of the to be estimated qubit ) @@ -38,6 +37,9 @@ def __init__(self, probability: float) -> None: def __eq__(self, other: object) -> bool: return isinstance(other, BernoulliQ) and self._theta_p == other._theta_p + def __hash__(self) -> int: + return hash(self._theta_p) + def power(self, power: float, _matrix_power: bool = True) -> QuantumCircuit: # implement the efficient power of Q q_k = QuantumCircuit(1) @@ -47,7 +49,6 @@ def power(self, power: float, _matrix_power: bool = True) -> QuantumCircuit: def get_estimation_problem() -> EstimationProblem: """Returns a estimation problem instance for a fixed p value.""" - p = 0.2 """A circuit representing the Bernoulli A operator.""" diff --git a/src/mqt/bench/benchmarks/dj.py b/src/mqt/bench/benchmarks/dj.py index 9befc87eb..f896fd31f 100644 --- a/src/mqt/bench/benchmarks/dj.py +++ b/src/mqt/bench/benchmarks/dj.py @@ -9,12 +9,12 @@ def dj_oracle(case: str, n: int) -> QuantumCircuit: # plus one output qubit oracle_qc = QuantumCircuit(n + 1) + rng = np.random.default_rng(10) if case == "balanced": - np.random.seed(10) b_str = "" for _ in range(n): - b = np.random.randint(0, 2) + b = rng.integers(0, 2) b_str = b_str + str(b) for qubit in range(len(b_str)): @@ -29,7 +29,7 @@ def dj_oracle(case: str, n: int) -> QuantumCircuit: oracle_qc.x(qubit) if case == "constant": - output = np.random.randint(2) + output = rng.integers(2) if output == 1: oracle_qc.x(n) @@ -62,11 +62,10 @@ def dj_algorithm(oracle: QuantumCircuit, n: int) -> QuantumCircuit: def create_circuit(n: int, balanced: bool = True) -> QuantumCircuit: """Returns a quantum circuit implementing the Deutsch-Josza algorithm. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit balanced -- True for a balanced and False for a constant oracle """ - oracle_mode = "balanced" if balanced else "constant" n = n - 1 # because of ancilla qubit oracle_gate = dj_oracle(oracle_mode, n) diff --git a/src/mqt/bench/benchmarks/ghz.py b/src/mqt/bench/benchmarks/ghz.py index 0cdbf8d87..33ff9a840 100644 --- a/src/mqt/bench/benchmarks/ghz.py +++ b/src/mqt/bench/benchmarks/ghz.py @@ -6,10 +6,9 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: """Returns a quantum circuit implementing the GHZ state. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit """ - q = QuantumRegister(num_qubits, "q") qc = QuantumCircuit(q, name="ghz") qc.h(q[-1]) diff --git a/src/mqt/bench/benchmarks/graphstate.py b/src/mqt/bench/benchmarks/graphstate.py index 20f9c21a2..e6de7f3a6 100644 --- a/src/mqt/bench/benchmarks/graphstate.py +++ b/src/mqt/bench/benchmarks/graphstate.py @@ -8,11 +8,10 @@ def create_circuit(num_qubits: int, degree: int = 2) -> QuantumCircuit: """Returns a quantum circuit implementing a graph state. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit degree -- number of edges per node """ - q = QuantumRegister(num_qubits, "q") qc = QuantumCircuit(q, name="graphstate") diff --git a/src/mqt/bench/benchmarks/grover.py b/src/mqt/bench/benchmarks/grover.py index bbd66e3dd..9ba418cf4 100644 --- a/src/mqt/bench/benchmarks/grover.py +++ b/src/mqt/bench/benchmarks/grover.py @@ -9,11 +9,10 @@ def create_circuit(num_qubits: int, ancillary_mode: str = "noancilla") -> QuantumCircuit: """Returns a quantum circuit implementing Grover's algorithm. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit ancillary_mode -- defining the decomposition scheme """ - num_qubits = num_qubits - 1 # -1 because of the flag qubit q = QuantumRegister(num_qubits, "q") flag = AncillaRegister(1, "flag") diff --git a/src/mqt/bench/benchmarks/qaoa.py b/src/mqt/bench/benchmarks/qaoa.py index c4bf47741..505cf10e7 100644 --- a/src/mqt/bench/benchmarks/qaoa.py +++ b/src/mqt/bench/benchmarks/qaoa.py @@ -19,10 +19,9 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: """Returns a quantum circuit implementing the Quantum Approximation Optimization Algorithm for a specific max-cut example. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit """ - qp = get_examplary_max_cut_qp(num_qubits) assert isinstance(qp, QuadraticProgram) diff --git a/src/mqt/bench/benchmarks/qft.py b/src/mqt/bench/benchmarks/qft.py index d26dcf728..fd32e1184 100644 --- a/src/mqt/bench/benchmarks/qft.py +++ b/src/mqt/bench/benchmarks/qft.py @@ -7,10 +7,9 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: """Returns a quantum circuit implementing the Quantum Fourier Transform algorithm. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit """ - q = QuantumRegister(num_qubits, "q") c = ClassicalRegister(num_qubits, "c") qc = QuantumCircuit(q, c, name="qft") diff --git a/src/mqt/bench/benchmarks/qftentangled.py b/src/mqt/bench/benchmarks/qftentangled.py index c9ed84bca..71a8d680f 100644 --- a/src/mqt/bench/benchmarks/qftentangled.py +++ b/src/mqt/bench/benchmarks/qftentangled.py @@ -7,10 +7,9 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: """Returns a quantum circuit implementing the Quantum Fourier Transform algorithm using entangled qubits. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit """ - q = QuantumRegister(num_qubits, "q") qc = QuantumCircuit(q) qc.h(q[-1]) diff --git a/src/mqt/bench/benchmarks/qiskit_application_finance/portfolioqaoa.py b/src/mqt/bench/benchmarks/qiskit_application_finance/portfolioqaoa.py index 82ab1220e..eb82262f8 100644 --- a/src/mqt/bench/benchmarks/qiskit_application_finance/portfolioqaoa.py +++ b/src/mqt/bench/benchmarks/qiskit_application_finance/portfolioqaoa.py @@ -19,10 +19,9 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: """Returns a quantum circuit of QAOA applied to a specific portfolio optimization task. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit """ - # set number of assets (= number of qubits) num_assets = num_qubits diff --git a/src/mqt/bench/benchmarks/qiskit_application_finance/portfoliovqe.py b/src/mqt/bench/benchmarks/qiskit_application_finance/portfoliovqe.py index 6cf24edbf..2beaaaf12 100644 --- a/src/mqt/bench/benchmarks/qiskit_application_finance/portfoliovqe.py +++ b/src/mqt/bench/benchmarks/qiskit_application_finance/portfoliovqe.py @@ -20,10 +20,9 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: """Returns a quantum circuit of VQE applied to a specific portfolio optimization task. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit """ - # set number of assets (= number of qubits) num_assets = num_qubits diff --git a/src/mqt/bench/benchmarks/qiskit_application_finance/pricingcall.py b/src/mqt/bench/benchmarks/qiskit_application_finance/pricingcall.py index 9c122f252..d89055b7d 100644 --- a/src/mqt/bench/benchmarks/qiskit_application_finance/pricingcall.py +++ b/src/mqt/bench/benchmarks/qiskit_application_finance/pricingcall.py @@ -17,7 +17,7 @@ def create_circuit(num_uncertainty_qubits: int = 5) -> QuantumCircuit: """Returns a quantum circuit of Iterative Amplitude Estimation applied to a problem instance of pricing call options. - Keyword arguments: + Keyword Arguments: num_uncertainty_qubits -- number of qubits to measure uncertainty """ # parameters for considered random distribution diff --git a/src/mqt/bench/benchmarks/qiskit_application_finance/pricingput.py b/src/mqt/bench/benchmarks/qiskit_application_finance/pricingput.py index 4225b6972..d920ff08c 100644 --- a/src/mqt/bench/benchmarks/qiskit_application_finance/pricingput.py +++ b/src/mqt/bench/benchmarks/qiskit_application_finance/pricingput.py @@ -16,7 +16,7 @@ def create_circuit(num_uncertainty_qubits: int = 5) -> QuantumCircuit: """Returns a quantum circuit of Iterative Amplitude Estimation applied to a problem instance of pricing put options. - Keyword arguments: + Keyword Arguments: num_uncertainty_qubits -- number of qubits to measure uncertainty """ # parameters for considered random distribution diff --git a/src/mqt/bench/benchmarks/qiskit_application_ml/qnn.py b/src/mqt/bench/benchmarks/qiskit_application_ml/qnn.py index a51d7af7d..b0887da57 100644 --- a/src/mqt/bench/benchmarks/qiskit_application_ml/qnn.py +++ b/src/mqt/bench/benchmarks/qiskit_application_ml/qnn.py @@ -9,7 +9,7 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: """Returns a quantum circuit implementing a Quantum Neural Network (QNN) with a ZZ FeatureMap and a RealAmplitudes ansatz. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit """ feature_map = ZZFeatureMap(feature_dimension=num_qubits) @@ -17,7 +17,9 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: qc = QuantumCircuit(num_qubits) feature_map = feature_map.assign_parameters([1 for _ in range(feature_map.num_parameters)]) - ansatz = ansatz.assign_parameters(np.random.rand(ansatz.num_parameters)) + + rng = np.random.default_rng(10) + ansatz = ansatz.assign_parameters(rng.random(ansatz.num_parameters) * 2 * np.pi) qc.compose(feature_map, inplace=True) qc.compose(ansatz, inplace=True) diff --git a/src/mqt/bench/benchmarks/qiskit_application_nature/groundstate.py b/src/mqt/bench/benchmarks/qiskit_application_nature/groundstate.py index 42a14fc30..295f1f608 100644 --- a/src/mqt/bench/benchmarks/qiskit_application_nature/groundstate.py +++ b/src/mqt/bench/benchmarks/qiskit_application_nature/groundstate.py @@ -19,7 +19,7 @@ def create_circuit(choice: str) -> QuantumCircuit: """Returns a quantum circuit implementing Ground State Estimation. - Keyword arguments: + Keyword Arguments: molecule -- Molecule for which the ground state shall be estimated. """ molecule = get_molecule(choice) diff --git a/src/mqt/bench/benchmarks/qiskit_application_optimization/routing.py b/src/mqt/bench/benchmarks/qiskit_application_optimization/routing.py index 8fa9538f0..0c8698ace 100644 --- a/src/mqt/bench/benchmarks/qiskit_application_optimization/routing.py +++ b/src/mqt/bench/benchmarks/qiskit_application_optimization/routing.py @@ -24,15 +24,15 @@ def __init__(self, n: int) -> None: def generate_instance( self, ) -> tuple[ - NDArray[np.float_], - NDArray[np.float_], - NDArray[np.float_], + NDArray[np.float64], + NDArray[np.float64], + NDArray[np.float64], ]: n = self.n - np.random.seed(10) + rng = np.random.default_rng(10) - xc = (np.random.rand(n) - 0.5) * 10 - yc = (np.random.rand(n) - 0.5) * 10 + xc = (rng.random(n) - 0.5) * 10 + yc = (rng.random(n) - 0.5) * 10 instance = np.zeros([n, n]) for ii in range(n): @@ -44,19 +44,19 @@ def generate_instance( class QuantumOptimizer: - def __init__(self, instance: NDArray[np.float_], n: int, K: int) -> None: + def __init__(self, instance: NDArray[np.float64], n: int, k: int) -> None: self.instance = instance self.n = n - self.K = K + self.k = k def binary_representation( - self, x_sol: NDArray[np.float_] - ) -> tuple[NDArray[np.float_], NDArray[np.float_], float, float]: + self, x_sol: NDArray[np.float64] + ) -> tuple[NDArray[np.float64], NDArray[np.float64], float, float]: instance = self.instance n = self.n - K = self.K + k = self.k - A = np.max(instance) * 100 # A parameter of cost function + a = np.max(instance) * 100 # A parameter of cost function # Determine the weights w instance_vec = instance.reshape(n**2) @@ -66,12 +66,12 @@ def binary_representation( w[ii] = w_list[ii] # Some variables I will use - Id_n = np.eye(n) - Im_n_1 = np.ones([n - 1, n - 1]) - Iv_n_1 = np.ones(n) - Iv_n_1[0] = 0 - Iv_n = np.ones(n - 1) - neg_Iv_n_1 = np.ones(n) - Iv_n_1 + id_n = np.eye(n) + im_n_1 = np.ones([n - 1, n - 1]) + iv_n_1 = np.ones(n) + iv_n_1[0] = 0 + iv_n = np.ones(n - 1) + neg_iv_n_1 = np.ones(n) - iv_n_1 v = np.zeros([n, n * (n - 1)]) for ii in range(n): @@ -86,34 +86,34 @@ def binary_representation( vn = np.sum(v[1:], axis=0) # Q defines the interactions between variables - Q = A * (np.kron(Id_n, Im_n_1) + np.dot(v.T, v)) + q = a * (np.kron(id_n, im_n_1) + np.dot(v.T, v)) # g defines the contribution from the individual variables - g = w - 2 * A * (np.kron(Iv_n_1, Iv_n) + vn.T) - 2 * A * K * (np.kron(neg_Iv_n_1, Iv_n) + v[0].T) + g = w - 2 * a * (np.kron(iv_n_1, iv_n) + vn.T) - 2 * a * k * (np.kron(neg_iv_n_1, iv_n) + v[0].T) # c is the constant offset - c = 2 * A * (n - 1) + 2 * A * (K**2) + c = 2 * a * (n - 1) + 2 * a * (k**2) try: # Evaluates the cost distance from a binary representation of a path - def fun(x: NDArray[np.float_]) -> float: + def fun(x: NDArray[np.float64]) -> float: return cast( float, - np.dot(np.around(x), np.dot(Q, np.around(x))) + np.dot(g, np.around(x)) + c, + np.dot(np.around(x), np.dot(q, np.around(x))) + np.dot(g, np.around(x)) + c, ) cost = fun(x_sol) except Exception: cost = 0 - return Q, g, cast(float, c), cost + return q, g, cast(float, c), cost - def construct_problem(self, Q: QuadraticExpression, g: LinearExpression, c: float) -> QuadraticProgram: + def construct_problem(self, q: QuadraticExpression, g: LinearExpression, c: float) -> QuadraticProgram: qp = QuadraticProgram() for i in range(self.n * (self.n - 1)): qp.binary_var(str(i)) - qp.objective.quadratic = Q + qp.objective.quadratic = q qp.objective.linear = g qp.objective.constant = c return qp @@ -129,20 +129,19 @@ def solve_problem(self, qp: QuadraticProgram) -> QuantumCircuit: def create_circuit(num_nodes: int = 3, num_vehs: int = 2) -> QuantumCircuit: """Returns a quantum circuit solving a routing problem. - Keyword arguments: + Keyword Arguments: num_nodes -- number of to be visited nodes num_vehs -- number of used vehicles """ - # Initialize the problem by defining the parameters n = num_nodes # number of nodes + depot (n+1) k = num_vehs # number of vehicles # Initialize the problem by randomly generating the instance initializer = Initializer(n) - xc, yc, instance = initializer.generate_instance() + _xc, _yc, instance = initializer.generate_instance() quantum_optimizer = QuantumOptimizer(instance, n, k) - q, g, c, binary_cost = quantum_optimizer.binary_representation(x_sol=np.array(0.0, dtype=float)) + q, g, c, _binary_cost = quantum_optimizer.binary_representation(x_sol=np.array(0.0, dtype=float)) q_casted = cast(QuadraticExpression, q) g_casted = cast(LinearExpression, g) qp = quantum_optimizer.construct_problem(q_casted, g_casted, c) diff --git a/src/mqt/bench/benchmarks/qiskit_application_optimization/tsp.py b/src/mqt/bench/benchmarks/qiskit_application_optimization/tsp.py index c3e9fba02..afbe9a245 100644 --- a/src/mqt/bench/benchmarks/qiskit_application_optimization/tsp.py +++ b/src/mqt/bench/benchmarks/qiskit_application_optimization/tsp.py @@ -18,10 +18,9 @@ def create_circuit(num_nodes: int) -> QuantumCircuit: """Returns a quantum circuit solving the Travelling Salesman Problem (TSP). - Keyword arguments: + Keyword Arguments: num_nodes -- number of to be visited nodes """ - # Generating a graph of 3 nodes n = num_nodes tsp = Tsp.create_random_instance(n, seed=10) @@ -30,7 +29,7 @@ def create_circuit(num_nodes: int) -> QuantumCircuit: qp2qubo = QuadraticProgramToQubo() qubo = qp2qubo.convert(qp) - qubit_op, offset = qubo.to_ising() + qubit_op, _offset = qubo.to_ising() spsa = SPSA(maxiter=25) ry = TwoLocal(qubit_op.num_qubits, "ry", "cz", reps=5, entanglement="linear") diff --git a/src/mqt/bench/benchmarks/qpeexact.py b/src/mqt/bench/benchmarks/qpeexact.py index c3290a750..97faccd42 100644 --- a/src/mqt/bench/benchmarks/qpeexact.py +++ b/src/mqt/bench/benchmarks/qpeexact.py @@ -12,10 +12,9 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: """Returns a quantum circuit implementing the Quantum Phase Estimation algorithm for a phase which can be exactly estimated. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit """ - num_qubits = num_qubits - 1 # because of ancilla qubit q = QuantumRegister(num_qubits, "q") psi = QuantumRegister(1, "psi") diff --git a/src/mqt/bench/benchmarks/qpeinexact.py b/src/mqt/bench/benchmarks/qpeinexact.py index a59fbdde1..96e9a61ee 100644 --- a/src/mqt/bench/benchmarks/qpeinexact.py +++ b/src/mqt/bench/benchmarks/qpeinexact.py @@ -12,10 +12,9 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: """Returns a quantum circuit implementing the Quantum Phase Estimation algorithm for a phase which cannot be exactly estimated. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit """ - num_qubits = num_qubits - 1 # because of ancilla qubit q = QuantumRegister(num_qubits, "q") psi = QuantumRegister(1, "psi") diff --git a/src/mqt/bench/benchmarks/qwalk.py b/src/mqt/bench/benchmarks/qwalk.py index 6bbfb06ee..5c9c225dc 100644 --- a/src/mqt/bench/benchmarks/qwalk.py +++ b/src/mqt/bench/benchmarks/qwalk.py @@ -11,7 +11,7 @@ def create_circuit( ) -> QuantumCircuit: """Returns a quantum circuit implementing the Quantum Walk algorithm. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit depth -- number of quantum steps coin_state_preparation -- optional quantum circuit for state preparation diff --git a/src/mqt/bench/benchmarks/random.py b/src/mqt/bench/benchmarks/random.py index 91fea77b2..93c48615d 100644 --- a/src/mqt/bench/benchmarks/random.py +++ b/src/mqt/bench/benchmarks/random.py @@ -9,7 +9,7 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: """Returns a random quantum circuit twice as deep as wide. The random gate span over four qubits maximum. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit """ qc = random_circuit(num_qubits, num_qubits * 2, measure=False, seed=10) diff --git a/src/mqt/bench/benchmarks/realamprandom.py b/src/mqt/bench/benchmarks/realamprandom.py index 90b14b3e7..4751c5ee3 100644 --- a/src/mqt/bench/benchmarks/realamprandom.py +++ b/src/mqt/bench/benchmarks/realamprandom.py @@ -13,14 +13,13 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: """Returns a quantum circuit implementing the RealAmplitudes ansatz with random parameter values. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit """ - - np.random.seed(10) + rng = np.random.default_rng(10) qc = RealAmplitudes(num_qubits, entanglement="full", reps=3) num_params = qc.num_parameters - qc = qc.assign_parameters(2 * np.pi * np.random.rand(num_params)) + qc = qc.assign_parameters(2 * np.pi * rng.random(num_params)) qc.measure_all() qc.name = "realamprandom" diff --git a/src/mqt/bench/benchmarks/shor.py b/src/mqt/bench/benchmarks/shor.py index 48e326af4..583db3aff 100644 --- a/src/mqt/bench/benchmarks/shor.py +++ b/src/mqt/bench/benchmarks/shor.py @@ -29,11 +29,10 @@ def create_circuit(num_to_be_factorized: int, a: int = 2) -> QuantumCircuit: """Returns a quantum circuit implementing the Shor's algorithm. - Keyword arguments: + Keyword Arguments: num_to_be_factorized -- number which shall be factorized a -- any integer that satisfies 1 < a < num_to_be_factorized and gcd(a, num_to_be_factorized) = 1 """ - qc = Shor().construct_circuit(num_to_be_factorized, a) qc.measure_all() qc.name = "shor_" + str(num_to_be_factorized) + "_" + str(a) @@ -54,7 +53,7 @@ def get_instance(choice: str) -> list[int]: class Shor: @staticmethod - def _get_angles(a: int, n: int) -> NDArray[np.float_]: + def _get_angles(a: int, n: int) -> NDArray[np.float64]: """Calculates the array of angles to be used in the addition in Fourier Space.""" bits_little_endian = (bin(int(a))[2:].zfill(n))[::-1] @@ -68,18 +67,18 @@ def _get_angles(a: int, n: int) -> NDArray[np.float_]: return angles * np.pi @staticmethod - def _phi_add_gate(angles: NDArray[np.float_] | ParameterVector) -> Gate: + def _phi_add_gate(angles: NDArray[np.float64] | ParameterVector) -> Gate: """Gate that performs addition by a in Fourier Space.""" circuit = QuantumCircuit(len(angles), name="phi_add_a") for i, angle in enumerate(angles): circuit.p(angle, i) return circuit.to_gate() - def _double_controlled_phi_add_mod_N( + def _double_controlled_phi_add_mod_n( self, - angles: NDArray[np.float_] | ParameterVector, - c_phi_add_N: Gate, - iphi_add_N: Gate, + angles: NDArray[np.float64] | ParameterVector, + c_phi_add_n: Gate, + iphi_add_n: Gate, qft: Gate, iqft: Gate, ) -> QuantumCircuit: @@ -95,13 +94,13 @@ def _double_controlled_phi_add_mod_N( circuit.append(cc_phi_add_a, [*ctrl_qreg, *b_qreg]) - circuit.append(iphi_add_N, b_qreg) + circuit.append(iphi_add_n, b_qreg) circuit.append(iqft, b_qreg) circuit.cx(b_qreg[-1], flag_qreg[0]) circuit.append(qft, b_qreg) - circuit.append(c_phi_add_N, [*flag_qreg, *b_qreg]) + circuit.append(c_phi_add_n, [*flag_qreg, *b_qreg]) circuit.append(cc_iphi_add_a, [*ctrl_qreg, *b_qreg]) @@ -115,131 +114,132 @@ def _double_controlled_phi_add_mod_N( return circuit - def _controlled_multiple_mod_N( + def _controlled_multiple_mod_n( self, - n: int, - N: int, + num_bits_necessary: int, + to_be_factored_number: int, a: int, - c_phi_add_N: Gate, - iphi_add_N: Gate, + c_phi_add_n: Gate, + iphi_add_n: Gate, qft: Gate, iqft: Gate, ) -> Instruction: """Implements modular multiplication by a as an instruction.""" ctrl_qreg = QuantumRegister(1, "ctrl") - x_qreg = QuantumRegister(n, "x") - b_qreg = QuantumRegister(n + 1, "b") + x_qreg = QuantumRegister(num_bits_necessary, "x") + b_qreg = QuantumRegister(num_bits_necessary + 1, "b") flag_qreg = QuantumRegister(1, "flag") circuit = QuantumCircuit(ctrl_qreg, x_qreg, b_qreg, flag_qreg, name="cmult_a_mod_N") - angle_params = ParameterVector("angles", length=n + 1) - modulo_adder = self._double_controlled_phi_add_mod_N(angle_params, c_phi_add_N, iphi_add_N, qft, iqft) + angle_params = ParameterVector("angles", length=num_bits_necessary + 1) + modulo_adder = self._double_controlled_phi_add_mod_n(angle_params, c_phi_add_n, iphi_add_n, qft, iqft) def append_adder(adder: QuantumCircuit, constant: int, idx: int) -> None: - partial_constant = (pow(2, idx, N) * constant) % N - angles = self._get_angles(partial_constant, n + 1) + partial_constant = (pow(2, idx, to_be_factored_number) * constant) % to_be_factored_number + angles = self._get_angles(partial_constant, num_bits_necessary + 1) bound = adder.assign_parameters({angle_params: angles}) circuit.append(bound, [*ctrl_qreg, x_qreg[idx], *b_qreg, *flag_qreg]) circuit.append(qft, b_qreg) # perform controlled addition by a on the aux register in Fourier space - for i in range(n): + for i in range(num_bits_necessary): append_adder(modulo_adder, a, i) circuit.append(iqft, b_qreg) # perform controlled subtraction by a in Fourier space on both the aux and down register - for i in range(n): + for i in range(num_bits_necessary): circuit.cswap(ctrl_qreg, x_qreg[i], b_qreg[i]) circuit.append(qft, b_qreg) - a_inv = pow(a, -1, mod=N) + a_inv = pow(a, -1, mod=to_be_factored_number) modulo_adder_inv = modulo_adder.inverse() - for i in reversed(range(n)): + for i in reversed(range(num_bits_necessary)): append_adder(modulo_adder_inv, a_inv, i) circuit.append(iqft, b_qreg) return circuit.to_instruction() - def _power_mod_N(self, n: int, N: int, a: int) -> Instruction: + def _power_mod_n(self, num_bits_necessary: int, to_be_factored_number: int, a: int) -> Instruction: """Implements modular exponentiation a^x as an instruction.""" - up_qreg = QuantumRegister(2 * n, name="up") - down_qreg = QuantumRegister(n, name="down") - aux_qreg = QuantumRegister(n + 2, name="aux") + up_qreg = QuantumRegister(2 * num_bits_necessary, name="up") + down_qreg = QuantumRegister(num_bits_necessary, name="down") + aux_qreg = QuantumRegister(num_bits_necessary + 2, name="aux") - circuit = QuantumCircuit(up_qreg, down_qreg, aux_qreg, name=f"{a}^x mod {N}") + circuit = QuantumCircuit(up_qreg, down_qreg, aux_qreg, name=f"{a}^x mod {to_be_factored_number}") - qft = QFT(n + 1, do_swaps=False).to_gate() + qft = QFT(num_bits_necessary + 1, do_swaps=False).to_gate() iqft = qft.inverse() # Create gates to perform addition/subtraction by N in Fourier Space - phi_add_N = self._phi_add_gate(self._get_angles(N, n + 1)) - iphi_add_N = phi_add_N.inverse() - c_phi_add_N = phi_add_N.control(1) + phi_add_n = self._phi_add_gate(self._get_angles(to_be_factored_number, num_bits_necessary + 1)) + iphi_add_n = phi_add_n.inverse() + c_phi_add_n = phi_add_n.control(1) # Apply the multiplication gates as showed in # the report in order to create the exponentiation - for i in range(2 * n): - partial_a = pow(a, pow(2, i), N) - modulo_multiplier = self._controlled_multiple_mod_N(n, N, partial_a, c_phi_add_N, iphi_add_N, qft, iqft) + for i in range(2 * num_bits_necessary): + partial_a = pow(a, pow(2, i), to_be_factored_number) + modulo_multiplier = self._controlled_multiple_mod_n( + num_bits_necessary, to_be_factored_number, partial_a, c_phi_add_n, iphi_add_n, qft, iqft + ) circuit.append(modulo_multiplier, [up_qreg[i], *down_qreg, *aux_qreg]) return circuit.to_instruction() @staticmethod - def _validate_input(N: int, a: int) -> None: + def _validate_input(to_be_factored_number: int, a: int) -> None: """Check parameters of the algorithm. Args: - N: The odd integer to be factored, has a min. value of 3. + to_be_factored_number: The odd integer to be factored, has a min. value of 3. a: Any integer that satisfies 1 < a < N and gcd(a, N) = 1. Raises: ValueError: Invalid input """ - - validate_min("N", N, 3) + validate_min("N", to_be_factored_number, 3) validate_min("a", a, 2) - if N < 1 or N % 2 == 0: + if to_be_factored_number < 1 or to_be_factored_number % 2 == 0: msg = "The input needs to be an odd integer greater than 1." raise ValueError(msg) - if a >= N or math.gcd(a, N) != 1: + if a >= to_be_factored_number or math.gcd(a, to_be_factored_number) != 1: msg = "The integer a needs to satisfy a < N and gcd(a, N) = 1." raise ValueError(msg) - def construct_circuit(self, N: int, a: int = 2) -> QuantumCircuit: + def construct_circuit(self, to_be_factored_number: int, a: int = 2) -> QuantumCircuit: """Construct quantum part of the algorithm. Args: - N: The odd integer to be factored, has a min. value of 3. + to_be_factored_number: The odd integer to be factored, has a min. value of 3. a: Any integer that satisfies 1 < a < N and gcd(a, N) = 1. Returns: Quantum circuit. """ - self._validate_input(N, a) + self._validate_input(to_be_factored_number, a) # Get n value used in Shor's algorithm, to know how many qubits are used - n = N.bit_length() + num_bits_necessary = to_be_factored_number.bit_length() # quantum register where the sequential QFT is performed - up_qreg = QuantumRegister(2 * n, name="up") + up_qreg = QuantumRegister(2 * num_bits_necessary, name="up") # quantum register where the multiplications are made - down_qreg = QuantumRegister(n, name="down") + down_qreg = QuantumRegister(num_bits_necessary, name="down") # auxiliary quantum register used in addition and multiplication - aux_qreg = QuantumRegister(n + 2, name="aux") + aux_qreg = QuantumRegister(num_bits_necessary + 2, name="aux") # Create Quantum Circuit - circuit = QuantumCircuit(up_qreg, down_qreg, aux_qreg, name=f"Shor(N={N}, a={a})") + circuit = QuantumCircuit(up_qreg, down_qreg, aux_qreg, name=f"Shor(N={to_be_factored_number}, a={a})") # Create maximal superposition in top register circuit.h(up_qreg) @@ -248,7 +248,7 @@ def construct_circuit(self, N: int, a: int = 2) -> QuantumCircuit: circuit.x(down_qreg[0]) # Apply modulo exponentiation - modulo_power = self._power_mod_N(n, N, a) + modulo_power = self._power_mod_n(num_bits_necessary, to_be_factored_number, a) circuit.append(modulo_power, circuit.qubits) # Apply inverse QFT diff --git a/src/mqt/bench/benchmarks/su2random.py b/src/mqt/bench/benchmarks/su2random.py index 1eddbfeaa..9944b0700 100644 --- a/src/mqt/bench/benchmarks/su2random.py +++ b/src/mqt/bench/benchmarks/su2random.py @@ -13,14 +13,13 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: """Returns a quantum circuit implementing EfficientSU2 ansatz with random parameter values. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit """ - - np.random.seed(10) + rng = np.random.default_rng(10) qc = EfficientSU2(num_qubits, entanglement="full", reps=3) num_params = qc.num_parameters - qc = qc.assign_parameters(2 * np.pi * np.random.rand(num_params)) + qc = qc.assign_parameters(2 * np.pi * rng.random(num_params)) qc.measure_all() qc.name = "su2random" diff --git a/src/mqt/bench/benchmarks/twolocalrandom.py b/src/mqt/bench/benchmarks/twolocalrandom.py index 290c60f69..fa2bfed92 100644 --- a/src/mqt/bench/benchmarks/twolocalrandom.py +++ b/src/mqt/bench/benchmarks/twolocalrandom.py @@ -13,14 +13,13 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: """Returns a quantum circuit implementing the TwoLocal ansatz with random parameter values. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit """ - - np.random.seed(10) + rng = np.random.default_rng(10) qc = TwoLocal(num_qubits, "ry", "cx", entanglement="full", reps=3) num_params = qc.num_parameters - qc = qc.assign_parameters(2 * np.pi * np.random.rand(num_params)) + qc = qc.assign_parameters(2 * np.pi * rng.random(num_params)) qc.measure_all() qc.name = "twolocalrandom" diff --git a/src/mqt/bench/benchmarks/vqe.py b/src/mqt/bench/benchmarks/vqe.py index e068549a3..f4197707f 100644 --- a/src/mqt/bench/benchmarks/vqe.py +++ b/src/mqt/bench/benchmarks/vqe.py @@ -20,10 +20,9 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: """Returns a quantum circuit implementing the Variational Quantum Eigensolver Algorithm for a specific max-cut example. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit """ - qp = get_examplary_max_cut_qp(num_qubits) assert isinstance(qp, QuadraticProgram) diff --git a/src/mqt/bench/benchmarks/wstate.py b/src/mqt/bench/benchmarks/wstate.py index 08822d4a3..f27fe8b8c 100644 --- a/src/mqt/bench/benchmarks/wstate.py +++ b/src/mqt/bench/benchmarks/wstate.py @@ -7,10 +7,9 @@ def create_circuit(num_qubits: int) -> QuantumCircuit: """Returns a quantum circuit implementing the W state. - Keyword arguments: + Keyword Arguments: num_qubits -- number of qubits of the returned quantum circuit """ - q = QuantumRegister(num_qubits, "q") qc = QuantumCircuit(q, name="wstate") diff --git a/src/mqt/bench/devices/__init__.py b/src/mqt/bench/devices/__init__.py index a78a84308..2df61009e 100644 --- a/src/mqt/bench/devices/__init__.py +++ b/src/mqt/bench/devices/__init__.py @@ -17,22 +17,17 @@ class NotFoundError(Exception): def get_available_providers() -> list[Provider]: - """ - Get a list of all available providers - """ + """Get a list of all available providers.""" return [IBMProvider(), IonQProvider(), OQCProvider(), RigettiProvider(), QuantinuumProvider()] def get_available_provider_names() -> list[str]: - """ - Get a list of all available provider names - """ + """Get a list of all available provider names.""" return [prov.provider_name for prov in get_available_providers()] def get_provider_by_name(provider_name: str) -> Provider: - """ - Get a provider by its name + """Get a provider by its name. Args: provider_name: the name of the provider @@ -51,8 +46,7 @@ def get_provider_by_name(provider_name: str) -> Provider: def get_available_devices(sanitize_device: bool = False) -> list[Device]: - """ - Get a list of all available devices + """Get a list of all available devices. Args: sanitize_device: whether to sanitize the device calibration data @@ -63,15 +57,12 @@ def get_available_devices(sanitize_device: bool = False) -> list[Device]: def get_available_device_names() -> list[str]: - """ - Get a list of all available device names - """ + """Get a list of all available device names.""" return [name for prov in get_available_providers() for name in prov.get_available_device_names()] def get_device_by_name(device_name: str) -> Device: - """ - Get a device by its name + """Get a device by its name. Args: device_name: the name of the device @@ -92,18 +83,18 @@ def get_device_by_name(device_name: str) -> Device: __all__ = [ - "Provider", "Device", "DeviceCalibration", "IBMProvider", "IonQProvider", "OQCProvider", - "RigettiProvider", + "Provider", "QuantinuumProvider", - "get_available_providers", - "get_available_provider_names", - "get_provider_by_name", - "get_available_devices", + "RigettiProvider", "get_available_device_names", + "get_available_devices", + "get_available_provider_names", + "get_available_providers", "get_device_by_name", + "get_provider_by_name", ] diff --git a/src/mqt/bench/devices/calibration.py b/src/mqt/bench/devices/calibration.py index ea0edb51a..d8f9f36b7 100644 --- a/src/mqt/bench/devices/calibration.py +++ b/src/mqt/bench/devices/calibration.py @@ -5,10 +5,9 @@ @dataclass class DeviceCalibration: - """ - Calibration data for a (generic) device. + """Calibration data for a (generic) device. - Attributes + Attributes: single_qubit_gate_fidelity: single-qubit fidelity for each qubit and gate single_qubit_gate_duration: single-qubit gate duration for each qubit and gate two_qubit_gate_fidelity: two-qubit fidelity for each qubit pair and gate @@ -30,10 +29,9 @@ class DeviceCalibration: t2: dict[int, float] = field(default_factory=dict) def get_single_qubit_gate_fidelity(self, gate_type: str, qubit: int) -> float: - """ - Get the single-qubit fidelity for a given gate type and qubit. + """Get the single-qubit fidelity for a given gate type and qubit. - Args + Args: gate_type: name of the gate qubit: index of the qubit """ @@ -48,10 +46,9 @@ def get_single_qubit_gate_fidelity(self, gate_type: str, qubit: int) -> float: raise ValueError(msg) from None def get_single_qubit_gate_duration(self, gate_type: str, qubit: int) -> float: - """ - Get the single-qubit duration for a given gate type and qubit. + """Get the single-qubit duration for a given gate type and qubit. - Args + Args: gate_type: name of the gate qubit: index of the qubit """ @@ -66,10 +63,9 @@ def get_single_qubit_gate_duration(self, gate_type: str, qubit: int) -> float: raise ValueError(msg) from None def get_two_qubit_gate_fidelity(self, gate_type: str, qubit1: int, qubit2: int) -> float: - """ - Get the two-qubit fidelity for a given gate type and qubit pair. + """Get the two-qubit fidelity for a given gate type and qubit pair. - Args + Args: gate_type: name of the gate qubit1: index of the first qubit qubit2: index of the second qubit @@ -85,10 +81,9 @@ def get_two_qubit_gate_fidelity(self, gate_type: str, qubit1: int, qubit2: int) raise ValueError(msg) from None def get_two_qubit_gate_duration(self, gate_type: str, qubit1: int, qubit2: int) -> float: - """ - Get the two-qubit duration for a given gate type and qubit pair. + """Get the two-qubit duration for a given gate type and qubit pair. - Args + Args: gate_type: name of the gate qubit1: index of the first qubit qubit2: index of the second qubit @@ -104,10 +99,9 @@ def get_two_qubit_gate_duration(self, gate_type: str, qubit1: int, qubit2: int) raise ValueError(msg) from None def get_readout_fidelity(self, qubit: int) -> float: - """ - Get the readout fidelity for a given qubit. + """Get the readout fidelity for a given qubit. - Args + Args: qubit: index of the qubit """ if not self.readout_fidelity: @@ -121,10 +115,9 @@ def get_readout_fidelity(self, qubit: int) -> float: raise ValueError(msg) from None def get_readout_duration(self, qubit: int) -> float: - """ - Get the readout duration for a given qubit. + """Get the readout duration for a given qubit. - Args + Args: qubit: index of the qubit """ if not self.readout_duration: @@ -138,10 +131,9 @@ def get_readout_duration(self, qubit: int) -> float: raise ValueError(msg) from None def get_t1(self, qubit: int) -> float: - """ - Get the T1 time for a given qubit. + """Get the T1 time for a given qubit. - Args + Args: qubit: index of the qubit """ if not self.t1: @@ -155,10 +147,9 @@ def get_t1(self, qubit: int) -> float: raise ValueError(msg) from None def get_t2(self, qubit: int) -> float: - """ - Get the T2 time for a given qubit. + """Get the T2 time for a given qubit. - Args + Args: qubit: index of the qubit """ if not self.t2: @@ -172,9 +163,7 @@ def get_t2(self, qubit: int) -> float: raise ValueError(msg) from None def compute_average_single_qubit_gate_fidelity(self, gate: str) -> float: - """ - Compute the average single-qubit fidelity. - """ + """Compute the average single-qubit fidelity.""" if not self.single_qubit_gate_fidelity: msg = "Single-qubit gate fidelity values not available." raise ValueError(msg) @@ -188,9 +177,7 @@ def compute_average_single_qubit_gate_fidelity(self, gate: str) -> float: return avg_single_qubit_gate_fidelity / entries def compute_average_single_qubit_gate_duration(self, gate: str) -> float: - """ - Compute the average single-qubit duration. - """ + """Compute the average single-qubit duration.""" if not self.single_qubit_gate_duration: msg = "Single-qubit gate duration values not available." raise ValueError(msg) @@ -204,9 +191,7 @@ def compute_average_single_qubit_gate_duration(self, gate: str) -> float: return avg_single_qubit_gate_duration / entries def compute_average_two_qubit_gate_fidelity(self, gate: str) -> float: - """ - Compute the average two-qubit gate fidelity. - """ + """Compute the average two-qubit gate fidelity.""" if not self.two_qubit_gate_fidelity: msg = "Two-qubit gate fidelity values not available." raise ValueError(msg) @@ -220,9 +205,7 @@ def compute_average_two_qubit_gate_fidelity(self, gate: str) -> float: return avg_two_qubit_gate_fidelity / entries def compute_average_two_qubit_gate_duration(self, gate: str) -> float: - """ - Compute the average two-qubit duration. - """ + """Compute the average two-qubit duration.""" if not self.two_qubit_gate_duration: msg = "Two-qubit gate duration values not available." raise ValueError(msg) @@ -236,9 +219,7 @@ def compute_average_two_qubit_gate_duration(self, gate: str) -> float: return avg_two_qubit_gate_duration / entries def compute_average_readout_fidelity(self) -> float: - """ - Compute the average readout fidelity. - """ + """Compute the average readout fidelity.""" if not self.readout_fidelity: msg = "Readout fidelity values not available." raise ValueError(msg) @@ -246,9 +227,7 @@ def compute_average_readout_fidelity(self) -> float: return sum(self.readout_fidelity.values()) / len(self.readout_fidelity) def compute_average_readout_duration(self) -> float: - """ - Compute the average readout duration. - """ + """Compute the average readout duration.""" if not self.readout_duration: msg = "Readout duration values not available." raise ValueError(msg) diff --git a/src/mqt/bench/devices/device.py b/src/mqt/bench/devices/device.py index e77853e40..0e315b322 100644 --- a/src/mqt/bench/devices/device.py +++ b/src/mqt/bench/devices/device.py @@ -9,10 +9,9 @@ @dataclass class Device: - """ - A class to represent a (generic) quantum device. + """A class to represent a (generic) quantum device. - Attributes + Attributes: name: name of the device num_qubits: number of qubits basis_gates: list of basis gates supported by the device @@ -27,10 +26,9 @@ class Device: calibration: DeviceCalibration | None = None def get_single_qubit_gate_fidelity(self, gate_type: str, qubit: int) -> float: - """ - Get the single-qubit fidelity for a given gate type and qubit. + """Get the single-qubit fidelity for a given gate type and qubit. - Args + Args: gate_type: name of the gate qubit: index of the qubit """ @@ -45,10 +43,9 @@ def get_single_qubit_gate_fidelity(self, gate_type: str, qubit: int) -> float: return self.calibration.get_single_qubit_gate_fidelity(gate_type, qubit) def get_single_qubit_gate_duration(self, gate_type: str, qubit: int) -> float: - """ - Get the single-qubit gate duration for a given gate type and qubit. + """Get the single-qubit gate duration for a given gate type and qubit. - Args + Args: gate_type: name of the gate qubit: index of the qubit """ @@ -63,10 +60,9 @@ def get_single_qubit_gate_duration(self, gate_type: str, qubit: int) -> float: return self.calibration.get_single_qubit_gate_duration(gate_type, qubit) def get_two_qubit_gate_fidelity(self, gate_type: str, qubit1: int, qubit2: int) -> float: - """ - Get the two-qubit fidelity for a given gate type and qubit pair. + """Get the two-qubit fidelity for a given gate type and qubit pair. - Args + Args: gate_type: name of the gate qubit1: index of the first qubit qubit2: index of the second qubit @@ -82,10 +78,9 @@ def get_two_qubit_gate_fidelity(self, gate_type: str, qubit1: int, qubit2: int) return self.calibration.get_two_qubit_gate_fidelity(gate_type, qubit1, qubit2) def get_two_qubit_gate_duration(self, gate_type: str, qubit1: int, qubit2: int) -> float: - """ - Get the two-qubit gate duration for a given gate type and qubit pair. + """Get the two-qubit gate duration for a given gate type and qubit pair. - Args + Args: gate_type: name of the gate qubit1: index of the first qubit qubit2: index of the second qubit @@ -101,10 +96,9 @@ def get_two_qubit_gate_duration(self, gate_type: str, qubit1: int, qubit2: int) return self.calibration.get_two_qubit_gate_duration(gate_type, qubit1, qubit2) def get_readout_fidelity(self, qubit: int) -> float: - """ - Get the readout fidelity for a given qubit. + """Get the readout fidelity for a given qubit. - Args + Args: qubit: index of the qubit """ if self.calibration is None: @@ -114,10 +108,9 @@ def get_readout_fidelity(self, qubit: int) -> float: return self.calibration.get_readout_fidelity(qubit) def get_readout_duration(self, qubit: int) -> float: - """ - Get the readout duration for a given qubit. + """Get the readout duration for a given qubit. - Args + Args: qubit: index of the qubit """ if self.calibration is None: @@ -127,20 +120,18 @@ def get_readout_duration(self, qubit: int) -> float: return self.calibration.get_readout_duration(qubit) def get_single_qubit_gates(self) -> set[str]: - """ - Get the set of single-qubit gates supported by the device. + """Get the set of single-qubit gates supported by the device. - Returns + Returns: list of single-qubit gates """ assert self.calibration is not None return {gate for qubit in range(self.num_qubits) for gate in self.calibration.single_qubit_gate_fidelity[qubit]} def get_two_qubit_gates(self) -> set[str]: - """ - Get the set of two-qubit gates supported by the device. + """Get the set of two-qubit gates supported by the device. - Returns + Returns: list of two-qubit gates """ assert self.calibration is not None @@ -151,8 +142,7 @@ def get_two_qubit_gates(self) -> set[str]: } def sanitize_device(self) -> None: - """ - Tries to sanitize the device information so that it produces the least amount of problems when used. + """Tries to sanitize the device information so that it produces the least amount of problems when used. It is assumed that any edge, where the average two-qubit gate fidelity is 0, is not a valid edge. Thus, such edges are removed from the coupling map. diff --git a/src/mqt/bench/devices/ibm.py b/src/mqt/bench/devices/ibm.py index 14e0b9fe0..4abe43a2d 100644 --- a/src/mqt/bench/devices/ibm.py +++ b/src/mqt/bench/devices/ibm.py @@ -15,9 +15,7 @@ class QubitProperties(TypedDict): - """ - Class to store the properties of a single qubit. - """ + """Class to store the properties of a single qubit.""" T1: float # us T2: float # us @@ -31,9 +29,7 @@ class QubitProperties(TypedDict): class IBMCalibration(TypedDict): - """ - Class to store the calibration data of an IBM device. - """ + """Class to store the calibration data of an IBM device.""" name: str basis_gates: list[str] @@ -43,32 +39,26 @@ class IBMCalibration(TypedDict): class IBMProvider(Provider): - """ - Class to manage IBM devices. - """ + """Class to manage IBM devices.""" provider_name = "ibm" @classmethod def get_available_device_names(cls) -> list[str]: - """ - Get the names of all available IBM devices. - """ + """Get the names of all available IBM devices.""" return ["ibm_washington", "ibm_montreal"] # NOTE: update when adding new devices @classmethod def get_native_gates(cls) -> list[str]: - """ - Get a list of provider specific native gates. - """ + """Get a list of provider specific native gates.""" return ["id", "rz", "sx", "x", "cx", "measure", "barrier"] # washington, montreal @classmethod def import_backend(cls, path: Path) -> Device: - """ - Import an IBM backend. + """Import an IBM backend. + Args: - path: the path to the JSON file containing the calibration data + path: the path to the JSON file containing the calibration data. Returns: the Device object """ @@ -108,10 +98,10 @@ def import_backend(cls, path: Path) -> Device: @classmethod def __import_backend_properties(cls, backend_properties: BackendProperties) -> DeviceCalibration: - """ - Import calibration data from a Qiskit `BackendProperties` object. + """Import calibration data from a Qiskit `BackendProperties` object. + Args: - backend_properties: the Qiskit `BackendProperties` object + backend_properties: the Qiskit `BackendProperties` object. Returns: Collection of calibration data """ @@ -151,10 +141,10 @@ def __import_backend_properties(cls, backend_properties: BackendProperties) -> D @classmethod def __import_backend_v1(cls, backend: BackendV1) -> Device: - """ - Import device data from a Qiskit `BackendV1` object. + """Import device data from a Qiskit `BackendV1` object. + Args: - backend: the Qiskit `BackendV1` object + backend: the Qiskit `BackendV1` object. Returns: Collection of device data """ @@ -168,10 +158,10 @@ def __import_backend_v1(cls, backend: BackendV1) -> Device: @classmethod def __import_target(cls, target: Target) -> DeviceCalibration: - """ - Import calibration data from a Qiskit `Target` object. + """Import calibration data from a Qiskit `Target` object. + Args: - target: the Qiskit `Target` object + target: the Qiskit `Target` object. Returns: Collection of calibration data """ @@ -212,10 +202,10 @@ def __import_target(cls, target: Target) -> DeviceCalibration: @classmethod def __import_backend_v2(cls, backend: BackendV2) -> Device: - """ - Import device data from a Qiskit `BackendV2` object. + """Import device data from a Qiskit `BackendV2` object. + Args: - backend: the Qiskit `BackendV2` object + backend: the Qiskit `BackendV2` object. Returns: Collection of device data """ @@ -229,10 +219,10 @@ def __import_backend_v2(cls, backend: BackendV2) -> Device: @classmethod def import_qiskit_backend(cls, backend: BackendV1 | BackendV2) -> Device: - """ - Import device data from a Qiskit `Backend` object. + """Import device data from a Qiskit `Backend` object. + Args: - backend: the Qiskit `Backend` object + backend: the Qiskit `Backend` object. Returns: Collection of device data """ diff --git a/src/mqt/bench/devices/ionq.py b/src/mqt/bench/devices/ionq.py index 3b664c871..01577aa4c 100644 --- a/src/mqt/bench/devices/ionq.py +++ b/src/mqt/bench/devices/ionq.py @@ -18,9 +18,8 @@ class Statistics(TypedDict): class IonQCalibration(TypedDict): - """ - Class to store the calibration data of an IonQ device. - Follows https://docs.ionq.com/#tag/characterizations + """Class to store the calibration data of an IonQ device. + Follows https://docs.ionq.com/#tag/characterizations. """ name: str @@ -32,32 +31,26 @@ class IonQCalibration(TypedDict): class IonQProvider(Provider): - """ - Class to manage IonQ devices. - """ + """Class to manage IonQ devices.""" provider_name = "ionq" @classmethod def get_available_device_names(cls) -> list[str]: - """ - Get the names of all available IonQ devices. - """ + """Get the names of all available IonQ devices.""" return ["ionq_harmony", "ionq_aria1"] # NOTE: update when adding new devices @classmethod def get_native_gates(cls) -> list[str]: - """ - Get a list of provider specific native gates. - """ + """Get a list of provider specific native gates.""" return ["rxx", "rz", "ry", "rx", "measure", "barrier"] # harmony, aria1 @classmethod def import_backend(cls, path: Path) -> Device: - """ - Import an IonQ backend as a Device object. + """Import an IonQ backend as a Device object. + Args: - path: the path to the JSON file containing the calibration data + path: the path to the JSON file containing the calibration data. Returns: the Device object """ @@ -71,13 +64,13 @@ def import_backend(cls, path: Path) -> Device: device.coupling_map = list(ionq_calibration["connectivity"]) calibration = DeviceCalibration() for qubit in range(device.num_qubits): - calibration.single_qubit_gate_fidelity[qubit] = { - gate: ionq_calibration["fidelity"]["1q"]["mean"] for gate in ["ry", "rx"] - } + calibration.single_qubit_gate_fidelity[qubit] = dict.fromkeys( + ["ry", "rx"], ionq_calibration["fidelity"]["1q"]["mean"] + ) calibration.single_qubit_gate_fidelity[qubit]["rz"] = 1 # rz is always perfect - calibration.single_qubit_gate_duration[qubit] = { - gate: ionq_calibration["timing"]["1q"] for gate in ["ry", "rx"] - } + calibration.single_qubit_gate_duration[qubit] = dict.fromkeys( + ["ry", "rx"], ionq_calibration["timing"]["1q"] + ) calibration.single_qubit_gate_duration[qubit]["rz"] = 0 # rz is always instantaneous calibration.readout_fidelity[qubit] = ionq_calibration["fidelity"]["spam"]["mean"] calibration.readout_duration[qubit] = ionq_calibration["timing"]["readout"] diff --git a/src/mqt/bench/devices/oqc.py b/src/mqt/bench/devices/oqc.py index 930cd80af..ff288d53d 100644 --- a/src/mqt/bench/devices/oqc.py +++ b/src/mqt/bench/devices/oqc.py @@ -10,9 +10,7 @@ class QubitProperties(TypedDict): - """ - Class to store the properties of a single qubit. - """ + """Class to store the properties of a single qubit.""" T1: float T2: float @@ -22,36 +20,28 @@ class QubitProperties(TypedDict): class Coupling(TypedDict): - """ - Class to store the connectivity of a two-qubit gate. - """ + """Class to store the connectivity of a two-qubit gate.""" control_qubit: float target_qubit: float class TwoQubitProperties(TypedDict): - """ - Class to store the properties of a two-qubit gate. - """ + """Class to store the properties of a two-qubit gate.""" coupling: Coupling fCX: float class Properties(TypedDict): - """ - Class to store the properties of a device. - """ + """Class to store the properties of a device.""" one_qubit: dict[str, QubitProperties] two_qubit: dict[str, TwoQubitProperties] class OQCCalibration(TypedDict): - """ - Class to store the calibration data of an OQC device. - """ + """Class to store the calibration data of an OQC device.""" name: str basis_gates: list[str] @@ -61,32 +51,26 @@ class OQCCalibration(TypedDict): class OQCProvider(Provider): - """ - Class to manage OQC devices - """ + """Class to manage OQC devices.""" provider_name = "oqc" @classmethod def get_available_device_names(cls) -> list[str]: - """ - Get the names of all available OQC devices. - """ + """Get the names of all available OQC devices.""" return ["oqc_lucy"] # NOTE: update when adding new devices @classmethod def get_native_gates(cls) -> list[str]: - """ - Get a list of provider specific native gates. - """ + """Get a list of provider specific native gates.""" return ["rz", "sx", "x", "ecr", "measure", "barrier"] # lucy @classmethod def import_backend(cls, path: Path) -> Device: - """ - Import an OQC backend. + """Import an OQC backend. + Args: - path: the path to the JSON file containing the calibration data + path: the path to the JSON file containing the calibration data. Returns: the Device object """ @@ -109,8 +93,8 @@ def import_backend(cls, path: Path) -> Device: calibration.t2[qubit] = oqc_calibration["properties"]["one_qubit"][str(qubit)]["T2"] for qubit1, qubit2 in device.coupling_map: - calibration.two_qubit_gate_fidelity[(qubit1, qubit2)] = { - gate: oqc_calibration["properties"]["two_qubit"][f"{qubit1}-{qubit2}"]["fCX"] for gate in ["ecr"] - } + calibration.two_qubit_gate_fidelity[(qubit1, qubit2)] = dict.fromkeys( + ["ecr"], oqc_calibration["properties"]["two_qubit"][f"{qubit1}-{qubit2}"]["fCX"] + ) device.calibration = calibration return device diff --git a/src/mqt/bench/devices/provider.py b/src/mqt/bench/devices/provider.py index 7e69bee1c..df72407b7 100644 --- a/src/mqt/bench/devices/provider.py +++ b/src/mqt/bench/devices/provider.py @@ -19,21 +19,16 @@ @dataclass class Provider(ABC): - """ - Abstract class for a quantum device provider. - """ + """Abstract class for a quantum device provider.""" @property @abstractmethod def provider_name(self) -> str: - """ - Get the name of the provider. - """ + """Get the name of the provider.""" @classmethod def get_available_devices(cls, sanitize_device: bool = False) -> list[Device]: - """ - Get a list of all available devices. + """Get a list of all available devices. Args: sanitize_device: whether to sanitize the device calibration data @@ -43,43 +38,32 @@ def get_available_devices(cls, sanitize_device: bool = False) -> list[Device]: @classmethod @abstractmethod def get_available_device_names(cls) -> list[str]: - """ - Get a list of all available device names. - """ + """Get a list of all available device names.""" @classmethod @abstractmethod def get_native_gates(cls) -> list[str]: - """ - Get a list of provider specific native gates. - """ + """Get a list of provider specific native gates.""" @classmethod def get_available_basis_gates(cls) -> list[list[str]]: - """ - Get a list of all available basis gates. - """ + """Get a list of all available basis gates.""" unique_basis_gates = {tuple(device.basis_gates) for device in cls.get_available_devices()} return [list(basis_gates) for basis_gates in unique_basis_gates] @classmethod def get_max_qubits(cls) -> int: - """ - Get the maximum number of qubits offered by a device from the provider. - """ + """Get the maximum number of qubits offered by a device from the provider.""" return max([device.num_qubits for device in cls.get_available_devices()]) @classmethod @abstractmethod def import_backend(cls, path: Path) -> Device: - """ - Import a device from a file containing calibration data. - """ + """Import a device from a file containing calibration data.""" @classmethod def get_device(cls, name: str, sanitize_device: bool = False) -> Device: - """ - Get a device by name. + """Get a device by name. Args: name: the name of the device diff --git a/src/mqt/bench/devices/quantinuum.py b/src/mqt/bench/devices/quantinuum.py index 7e95e1c3f..76ed86e41 100644 --- a/src/mqt/bench/devices/quantinuum.py +++ b/src/mqt/bench/devices/quantinuum.py @@ -17,9 +17,8 @@ class Statistics(TypedDict): class QuantinuumCalibration(TypedDict): - """ - Class to store the calibration data of an Quantinuum device. - Follows https://docs.quantinuum.com/#tag/characterizations + """Class to store the calibration data of an Quantinuum device. + Follows https://docs.quantinuum.com/#tag/characterizations. """ name: str @@ -30,32 +29,26 @@ class QuantinuumCalibration(TypedDict): class QuantinuumProvider(Provider): - """ - Class to manage Quantinuum devices. - """ + """Class to manage Quantinuum devices.""" provider_name = "quantinuum" @classmethod def get_available_device_names(cls) -> list[str]: - """ - Get the names of all available Quantinuum devices. - """ + """Get the names of all available Quantinuum devices.""" return ["quantinuum_h2"] # NOTE: update when adding new devices @classmethod def get_native_gates(cls) -> list[str]: - """ - Get a list of provider specific native gates. - """ + """Get a list of provider specific native gates.""" return ["rzz", "rz", "ry", "rx", "measure", "barrier"] # h2 @classmethod def import_backend(cls, path: Path) -> Device: - """ - Import an Quantinuum backend as a Device object. + """Import an Quantinuum backend as a Device object. + Args: - path: the path to the JSON file containing the calibration data + path: the path to the JSON file containing the calibration data. Returns: the Device object """ @@ -69,9 +62,9 @@ def import_backend(cls, path: Path) -> Device: device.coupling_map = list(quantinuum_calibration["connectivity"]) calibration = DeviceCalibration() for qubit in range(device.num_qubits): - calibration.single_qubit_gate_fidelity[qubit] = { - gate: quantinuum_calibration["fidelity"]["1q"]["mean"] for gate in ["ry", "rx"] - } + calibration.single_qubit_gate_fidelity[qubit] = dict.fromkeys( + ["ry", "rx"], quantinuum_calibration["fidelity"]["1q"]["mean"] + ) calibration.single_qubit_gate_fidelity[qubit]["rz"] = 1 # rz is always perfect calibration.readout_fidelity[qubit] = quantinuum_calibration["fidelity"]["spam"]["mean"] diff --git a/src/mqt/bench/devices/rigetti.py b/src/mqt/bench/devices/rigetti.py index 5bc6f4df4..c95fbf032 100644 --- a/src/mqt/bench/devices/rigetti.py +++ b/src/mqt/bench/devices/rigetti.py @@ -11,9 +11,7 @@ class QubitProperties(TypedDict): - """ - Class to store the properties of a single qubit. - """ + """Class to store the properties of a single qubit.""" fActiveReset: float fRO: float @@ -26,9 +24,7 @@ class QubitProperties(TypedDict): class TwoQubitProperties(TypedDict): - """ - Class to store the properties of a two-qubit gate. - """ + """Class to store the properties of a two-qubit gate.""" fCZ: float fCZ_std_err: float @@ -42,9 +38,7 @@ class TwoQubitProperties(TypedDict): class RigettiCalibration(TypedDict): - """ - Class to store the calibration data of a Rigetti device. - """ + """Class to store the calibration data of a Rigetti device.""" name: str num_qubits: int @@ -54,30 +48,23 @@ class RigettiCalibration(TypedDict): class RigettiProvider(Provider): - """ - Class to manage Rigetti devices. - """ + """Class to manage Rigetti devices.""" provider_name = "rigetti" @classmethod def get_available_device_names(cls) -> list[str]: - """ - Get the names of all available Rigetti devices. - """ + """Get the names of all available Rigetti devices.""" return ["rigetti_aspen_m3"] # NOTE: update when adding new devices @classmethod def get_native_gates(cls) -> list[str]: - """ - Get a list of provider specific native gates. - """ + """Get a list of provider specific native gates.""" return ["rx", "rz", "cz", "cp", "xx_plus_yy", "measure", "barrier"] # aspen_m3 @classmethod def __from_rigetti_index(cls, rigetti_index: int) -> int: - """ - Convert the Rigetti qubit index to a consecutive index. + """Convert the Rigetti qubit index to a consecutive index. The Rigetti architectures consist of 8-qubit rings arranged in a two-dimensional grid. Each qubit is identified by a three digit number, where: * the first digit is the row index, @@ -103,10 +90,10 @@ def __from_rigetti_index(cls, rigetti_index: int) -> int: @classmethod def __to_rigetti_index(cls, index: int) -> int: - """ - Convert the consecutive index to the Rigetti qubit index. + """Convert the consecutive index to the Rigetti qubit index. + Args: - index: the consecutive index + index: the consecutive index. Returns: the Rigetti qubit index """ @@ -123,10 +110,10 @@ def __to_rigetti_index(cls, index: int) -> int: @classmethod def import_backend(cls, path: Path) -> Device: - """ - Import a Rigetti backend. + """Import a Rigetti backend. + Args: - path: the path to the JSON file containing the calibration data + path: the path to the JSON file containing the calibration data. Returns: the Device object """ diff --git a/src/mqt/bench/evaluation/__init__.py b/src/mqt/bench/evaluation/__init__.py index c6e672901..e68bf9eff 100644 --- a/src/mqt/bench/evaluation/__init__.py +++ b/src/mqt/bench/evaluation/__init__.py @@ -9,9 +9,9 @@ ) __all__ = [ - "evaluate_qasm_file", - "create_statistics", "EvaluationResult", "count_occurrences", "count_qubit_numbers_per_compiler", + "create_statistics", + "evaluate_qasm_file", ] diff --git a/src/mqt/bench/qiskit_helper.py b/src/mqt/bench/qiskit_helper.py index ea7bc980f..a6e27e3ed 100644 --- a/src/mqt/bench/qiskit_helper.py +++ b/src/mqt/bench/qiskit_helper.py @@ -46,7 +46,7 @@ def get_indep_level( ) -> bool | QuantumCircuit: """Handles the creation of the benchmark on the target-independent level. - Keyword arguments: + Keyword Arguments: qc -- quantum circuit which the to be created benchmark circuit is based on num_qubits -- number of qubits file_precheck -- flag indicating whether to check whether the file already exists before creating it (again) @@ -58,7 +58,6 @@ def get_indep_level( if return_qc == True -- quantum circuit object else -- True/False indicating whether the function call was successful or not """ - filename_indep = target_filename if target_filename else qc.name + "_indep_qiskit_" + str(num_qubits) path = Path(target_directory, filename_indep + ".qasm") @@ -114,7 +113,7 @@ def get_native_gates_level( ) -> bool | QuantumCircuit: """Handles the creation of the benchmark on the target-dependent native gates level. - Keyword arguments: + Keyword Arguments: qc -- quantum circuit which the to be created benchmark circuit is based on provider -- determines the native gate set num_qubits -- number of qubits @@ -128,7 +127,6 @@ def get_native_gates_level( if return_qc == True -- quantum circuit object else -- True/False indicating whether the function call was successful or not """ - if not target_filename: filename_native = ( qc.name + "_nativegates_" + provider.provider_name + "_qiskit_opt" + str(opt_level) + "_" + str(num_qubits) @@ -192,7 +190,7 @@ def get_mapped_level( ) -> bool | QuantumCircuit: """Handles the creation of the benchmark on the target-dependent mapped level. - Keyword arguments: + Keyword Arguments: qc -- quantum circuit which the to be created benchmark circuit is based on num_qubits -- number of qubits device -- target device @@ -206,7 +204,6 @@ def get_mapped_level( if return_qc == True -- quantum circuit object else -- True/False indicating whether the function call was successful or not """ - if not target_filename: filename_mapped = qc.name + "_mapped_" + device.name + "_qiskit_opt" + str(opt_level) + "_" + str(num_qubits) else: diff --git a/src/mqt/bench/tket_helper.py b/src/mqt/bench/tket_helper.py index 834a5d8d8..d91f81465 100644 --- a/src/mqt/bench/tket_helper.py +++ b/src/mqt/bench/tket_helper.py @@ -77,7 +77,7 @@ def get_indep_level( ) -> bool | Circuit: """Handles the creation of the benchmark on the target-independent level. - Keyword arguments: + Keyword Arguments: qc -- quantum circuit which the to be created benchmark circuit is based on num_qubits -- number of qubits file_precheck -- flag indicating whether to check whether the file already exists before creating it (again) @@ -151,7 +151,7 @@ def get_native_gates_level( ) -> bool | Circuit: """Handles the creation of the benchmark on the target-dependent native gates level. - Keyword arguments: + Keyword Arguments: qc -- quantum circuit which the to be created benchmark circuit is based on provider -- determines the native gate set num_qubits -- number of qubits @@ -164,7 +164,6 @@ def get_native_gates_level( if return_qc == True -- quantum circuit object else -- True/False indicating whether the function call was successful or not """ - if not target_filename: filename_native = qc.name + "_nativegates_" + provider.provider_name + "_tket_" + str(num_qubits) else: @@ -241,7 +240,7 @@ def get_mapped_level( ) -> bool | Circuit: """Handles the creation of the benchmark on the target-dependent mapped level. - Keyword arguments: + Keyword Arguments: qc -- quantum circuit which the to be created benchmark circuit is based on num_qubits -- number of qubits device -- target device @@ -255,7 +254,6 @@ def get_mapped_level( if return_qc == True -- quantum circuit object else -- True/False indicating whether the function call was successful or not """ - placement = "line" if lineplacement else "graph" if not target_filename: diff --git a/src/mqt/bench/utils.py b/src/mqt/bench/utils.py index 98cf4391c..b5ac134f5 100644 --- a/src/mqt/bench/utils.py +++ b/src/mqt/bench/utils.py @@ -108,7 +108,7 @@ def get_zip_file_path() -> str: def get_examplary_max_cut_qp(n_nodes: int, degree: int = 2) -> QuadraticProgram: """Returns a quadratic problem formulation of a max cut problem of a random graph. - Keyword arguments: + Keyword Arguments: n_nodes -- number of graph nodes (and also number of qubits) degree -- edges per node """ @@ -176,14 +176,13 @@ def save_as_qasm( ) -> bool: """Saves a quantum circuit as a qasm file. - Keyword arguments: + Keyword Arguments: qc_str -- Quantum circuit to be stored as a string filename -- filename gate_set -- set of used gates mapped -- boolean indicating whether the quantum circuit is mapped to a specific hardware layout c_map -- coupling map of used hardware layout """ - if c_map is None: c_map = [] @@ -238,7 +237,7 @@ def create_zip_file(zip_path: str | None = None, qasm_path: str | None = None) - def calc_supermarq_features( qc: QuantumCircuit, ) -> SupermarqFeatures: - """Calculates the Supermarq features for a given quantum circuit. Code adapted from https://github.com/Infleqtion/client-superstaq/blob/91d947f8cc1d99f90dca58df5248d9016e4a5345/supermarq-benchmarks/supermarq/converters.py""" + """Calculates the Supermarq features for a given quantum circuit. Code adapted from https://github.com/Infleqtion/client-superstaq/blob/91d947f8cc1d99f90dca58df5248d9016e4a5345/supermarq-benchmarks/supermarq/converters.py.""" num_qubits = qc.num_qubits dag = circuit_to_dag(qc) dag.remove_all_ops_named("barrier") diff --git a/src/mqt/benchviewer/__init__.py b/src/mqt/benchviewer/__init__.py index 4fa32d7af..16dd24e99 100644 --- a/src/mqt/benchviewer/__init__.py +++ b/src/mqt/benchviewer/__init__.py @@ -3,4 +3,4 @@ from mqt.benchviewer.backend import Backend, BenchmarkConfiguration from mqt.benchviewer.main import Server, start_server -__all__ = ["start_server", "Server", "Backend", "BenchmarkConfiguration"] +__all__ = ["Backend", "BenchmarkConfiguration", "Server", "start_server"] diff --git a/src/mqt/benchviewer/backend.py b/src/mqt/benchviewer/backend.py index 3d2357557..a367a8ba3 100644 --- a/src/mqt/benchviewer/backend.py +++ b/src/mqt/benchviewer/backend.py @@ -146,7 +146,7 @@ def __init__(self) -> None: def filter_database(self, benchmark_config: BenchmarkConfiguration) -> list[str]: """Filters the database according to the filter criteria. - Keyword arguments: + Keyword Arguments: filterCriteria -- list of all filter criteria database -- database containing all available benchmarks @@ -251,7 +251,7 @@ def generate_zip_ephemeral_chunks( ) -> Iterable[bytes]: """Generates the zip file for the selected benchmarks and returns a generator of the chunks. - Keyword arguments: + Keyword Arguments: paths -- list of file paths for all selected benchmarks Return values: @@ -280,7 +280,7 @@ def generate_zip_ephemeral_chunks( def get_selected_file_paths(self, prepared_data: BenchmarkConfiguration) -> list[str]: """Extracts all file paths according to the prepared user's filter criteria. - Keyword arguments: + Keyword Arguments: prepared_data -- user's filter criteria after preparation step Return values: @@ -290,7 +290,6 @@ def get_selected_file_paths(self, prepared_data: BenchmarkConfiguration) -> list def init_database(self) -> bool: """Generates the database and saves it into a global variable.""" - assert self.mqtbench_all_zip is not None print("Initiating database...") @@ -465,7 +464,7 @@ def handle_downloading_benchmarks(self, target_location: str, download_url: str) def parse_data(filename: str) -> ParsedBenchmarkName: """Extracts the necessary information from a given filename. - Keyword arguments: + Keyword Arguments: filename -- name of file Return values: @@ -542,12 +541,12 @@ def parse_benchmark_id_from_form_key(k: str) -> int | bool: def get_opt_level(filename: str) -> int: """Extracts the optimization level based on a filename. - Keyword arguments: + + Keyword Arguments: filename -- filename of a benchmark Return values: - num -- optimization level + num -- optimization level. """ - pat = re.compile(r"opt\d") m = pat.search(filename) num = m.group()[-1:] if m else -1 @@ -556,12 +555,12 @@ def get_opt_level(filename: str) -> int: def get_num_qubits(filename: str) -> int: """Extracts the number of qubits based on a filename. - Keyword arguments: + + Keyword Arguments: filename -- filename of a benchmark Return values: - num -- number of qubits + num -- number of qubits. """ - pat = re.compile(r"(\d+)\.") m = pat.search(filename) num = m.group()[0:-1] if m else -1 @@ -619,10 +618,11 @@ def get_compiler_and_settings(filename: str) -> tuple[str, str | int | None]: def create_database(zip_file: ZipFile) -> pd.DataFrame: """Creates the database based on the provided directories. - Keyword arguments: + + Keyword Arguments: qasm_path -- zip containing all .qasm files Return values: - database -- database containing all available benchmarks + database -- database containing all available benchmarks. """ rows_list = [] diff --git a/src/mqt/benchviewer/main.py b/src/mqt/benchviewer/main.py index c8b491ffa..f4b0966d2 100644 --- a/src/mqt/benchviewer/main.py +++ b/src/mqt/benchviewer/main.py @@ -25,7 +25,7 @@ def __init__( target_location: str, skip_question: bool = False, activate_logging: bool = False, - ): + ) -> None: self.backend = Backend() self.target_location = target_location @@ -126,14 +126,12 @@ def download_data() -> str | Response: @app.route(f"{PREFIX}/legal") def legal() -> str: """Return the legal.html file.""" - return render_template("legal.html") @app.route(f"{PREFIX}/description") def description() -> str: """Return the description.html file in which the file formats are described.""" - return render_template("description.html") @@ -142,7 +140,6 @@ def benchmark_description() -> str: """Return the benchmark_description.html file together in which all benchmark algorithms are described in detail. """ - return render_template("benchmark_description.html") diff --git a/src/mqt/benchviewer/templates/index.html b/src/mqt/benchviewer/templates/index.html index d9c1bdc87..bbd908ecf 100644 --- a/src/mqt/benchviewer/templates/index.html +++ b/src/mqt/benchviewer/templates/index.html @@ -642,7 +642,7 @@

Reference

>"MQT Bench: Benchmarking Software and Design Automation Tools for Quantum Computing". Our implementation is available on - GithubGitHub. None: - """ - Test that all devices can be sanitized and provide complete fidelity data. - """ + """Test that all devices can be sanitized and provide complete fidelity data.""" assert device.calibration is not None for qubit in range(device.num_qubits): assert qubit in device.calibration.single_qubit_gate_fidelity @@ -37,9 +35,7 @@ def test_sanitized_devices(device: Device) -> None: def test_device_calibration_errors() -> None: - """ - Test that all device calibration methods raise errors when no calibration data is available. - """ + """Test that all device calibration methods raise errors when no calibration data is available.""" device = Device(name="test", num_qubits=1, basis_gates=[], coupling_map=[], calibration=None) # Test all methods with no calibration @@ -103,9 +99,7 @@ def test_device_calibration_errors() -> None: def test_provider() -> None: - """ - Test that all providers can be imported. - """ + """Test that all providers can be imported.""" for provider in get_available_providers(): assert provider.provider_name in ["ibm", "rigetti", "oqc", "ionq", "quantinuum"] diff --git a/tests/devices/test_ibm_device_support.py b/tests/devices/test_ibm_device_support.py index f7ec92ea6..d42cc224d 100644 --- a/tests/devices/test_ibm_device_support.py +++ b/tests/devices/test_ibm_device_support.py @@ -7,12 +7,11 @@ def test_ibm_provider_methods() -> None: - """ - Test the methods of the IBMProvider class: + """Test the methods of the IBMProvider class: - get_available_device_names - get_available_basis_gates - get_native_gates - - get_max_qubits + - get_max_qubits. """ assert IBMProvider.get_available_device_names() == ["ibm_washington", "ibm_montreal"] assert IBMProvider.get_available_basis_gates() == [["id", "rz", "sx", "x", "cx", "measure", "barrier"]] @@ -23,9 +22,7 @@ def test_ibm_provider_methods() -> None: def test_import_v1_backend() -> None: - """ - Test importing a Qiskit `BackendV1` object. - """ + """Test importing a Qiskit `BackendV1` object.""" backend = FakeMontreal() device = IBMProvider.import_qiskit_backend(backend) single_qubit_gates = device.get_single_qubit_gates() @@ -66,9 +63,7 @@ def test_import_v1_backend() -> None: def test_import_v2_backend() -> None: - """ - Test importing a Qiskit `BackendV2` object. - """ + """Test importing a Qiskit `BackendV2` object.""" backend = FakeMontrealV2() device = IBMProvider.import_qiskit_backend(backend) single_qubit_gates = device.get_single_qubit_gates() @@ -109,9 +104,7 @@ def test_import_v2_backend() -> None: def test_get_ibm_washington_device() -> None: - """ - Test getting the IBM Washington device. - """ + """Test getting the IBM Washington device.""" device = IBMProvider.get_device("ibm_washington") single_qubit_gates = device.get_single_qubit_gates() two_qubit_gates = device.get_two_qubit_gates() @@ -137,9 +130,7 @@ def test_get_ibm_washington_device() -> None: def test_get_ibmq_montreal_device() -> None: - """ - Test getting the IBM Montreal device. - """ + """Test getting the IBM Montreal device.""" device = IBMProvider.get_device("ibm_montreal") single_qubit_gates = device.get_single_qubit_gates() two_qubit_gates = device.get_two_qubit_gates() diff --git a/tests/devices/test_ionq_device_support.py b/tests/devices/test_ionq_device_support.py index abd2e9532..84a1c31f1 100644 --- a/tests/devices/test_ionq_device_support.py +++ b/tests/devices/test_ionq_device_support.py @@ -4,12 +4,11 @@ def test_ionq_provider_methods() -> None: - """ - Test the methods of the IonQProvider class: + """Test the methods of the IonQProvider class: - get_available_device_names - get_available_basis_gates - get_native_gates - - get_max_qubits + - get_max_qubits. """ assert IonQProvider.get_available_device_names() == ["ionq_harmony", "ionq_aria1"] assert IonQProvider.get_available_basis_gates() == [["rxx", "rz", "ry", "rx", "measure", "barrier"]] @@ -18,9 +17,7 @@ def test_ionq_provider_methods() -> None: def test_ionq_harmony_device() -> None: - """ - Test the import of the IonQ Harmony quantum computer. - """ + """Test the import of the IonQ Harmony quantum computer.""" device = IonQProvider.get_device("ionq_harmony") single_qubit_gates = device.get_single_qubit_gates() two_qubit_gates = device.get_two_qubit_gates() @@ -45,9 +42,7 @@ def test_ionq_harmony_device() -> None: def test_ionq_aria1_device() -> None: - """ - Test the import of the IonQ Aria quantum computer. - """ + """Test the import of the IonQ Aria quantum computer.""" device = IonQProvider.get_device("ionq_aria1") single_qubit_gates = device.get_single_qubit_gates() two_qubit_gates = device.get_two_qubit_gates() diff --git a/tests/devices/test_oqc_device_support.py b/tests/devices/test_oqc_device_support.py index 0545f1420..98949f64b 100644 --- a/tests/devices/test_oqc_device_support.py +++ b/tests/devices/test_oqc_device_support.py @@ -6,12 +6,11 @@ def test_oqc_provider_methods() -> None: - """ - Test the methods of the OQCrovider class: + """Test the methods of the OQCrovider class: - get_available_device_names - get_available_basis_gates - get_native_gates - - get_max_qubits + - get_max_qubits. """ assert OQCProvider.get_available_device_names() == ["oqc_lucy"] assert OQCProvider.get_available_basis_gates() == [["rz", "sx", "x", "ecr", "measure", "barrier"]] @@ -20,9 +19,7 @@ def test_oqc_provider_methods() -> None: def test_oqc_lucy_device() -> None: - """ - Test the import of the OQC Lucy quantum computer. - """ + """Test the import of the OQC Lucy quantum computer.""" device = OQCProvider.get_device("oqc_lucy") single_qubit_gates = device.get_single_qubit_gates() two_qubit_gates = device.get_two_qubit_gates() diff --git a/tests/devices/test_quantinuum_device_support.py b/tests/devices/test_quantinuum_device_support.py index 38af7dd3c..6e0ad6752 100644 --- a/tests/devices/test_quantinuum_device_support.py +++ b/tests/devices/test_quantinuum_device_support.py @@ -6,12 +6,11 @@ def test_quantinuum_provider_methods() -> None: - """ - Test the methods of the QuantinuumProvider class: + """Test the methods of the QuantinuumProvider class: - get_available_device_names - get_available_basis_gates - get_native_gates - - get_max_qubits + - get_max_qubits. """ assert QuantinuumProvider.get_available_device_names() == ["quantinuum_h2"] assert QuantinuumProvider.get_available_basis_gates() == [["rzz", "rz", "ry", "rx", "measure", "barrier"]] @@ -20,9 +19,7 @@ def test_quantinuum_provider_methods() -> None: def test_quantinuum_h2_device() -> None: - """ - Test the import of the Quantinuum H2 quantum computer. - """ + """Test the import of the Quantinuum H2 quantum computer.""" device = QuantinuumProvider.get_device("quantinuum_h2") single_qubit_gates = device.get_single_qubit_gates() two_qubit_gates = device.get_two_qubit_gates() diff --git a/tests/devices/test_rigetti_device_support.py b/tests/devices/test_rigetti_device_support.py index 412db9d8c..cbac3e9ab 100644 --- a/tests/devices/test_rigetti_device_support.py +++ b/tests/devices/test_rigetti_device_support.py @@ -6,12 +6,11 @@ def test_rigetti_provider_methods() -> None: - """ - Test the methods of the RigettiProvider class: + """Test the methods of the RigettiProvider class: - get_available_device_names - get_available_basis_gates - get_native_gates - - get_max_qubits + - get_max_qubits. """ assert RigettiProvider.get_available_device_names() == ["rigetti_aspen_m3"] assert RigettiProvider.get_available_basis_gates() == [["rx", "rz", "cz", "cp", "xx_plus_yy", "measure", "barrier"]] @@ -20,9 +19,7 @@ def test_rigetti_provider_methods() -> None: def test_rigetti_aspen_m3_device() -> None: - """ - Test the import of the Rigetti Aspen-M3 quantum computer. - """ + """Test the import of the Rigetti Aspen-M3 quantum computer.""" device = RigettiProvider.get_device("rigetti_aspen_m3") single_qubit_gates = device.get_single_qubit_gates() two_qubit_gates = device.get_two_qubit_gates() diff --git a/tests/test_bench.py b/tests/test_bench.py index 90826beb9..60b865077 100644 --- a/tests/test_bench.py +++ b/tests/test_bench.py @@ -334,8 +334,6 @@ def test_get_benchmark_deprecation_warning() -> None: def test_unidirectional_coupling_map() -> None: - from pytket.architecture import Architecture - qc = get_benchmark( benchmark_name="dj", level="mapped", @@ -347,7 +345,7 @@ def test_unidirectional_coupling_map() -> None: ) # check that all gates in the circuit are in the coupling map cmap = utils.convert_cmap_to_tuple_list(OQCProvider.get_device("oqc_lucy").coupling_map) - assert qc.valid_connectivity(arch=Architecture(cmap), directed=True) + assert qc.valid_connectivity(arch=pytket.architecture.Architecture(cmap), directed=True) @pytest.mark.parametrize( @@ -1092,7 +1090,7 @@ def test_calc_supermarq_features() -> None: assert 0 < regular_features.liveness < 1 -def test_BenchmarkGenerator() -> None: +def test_benchmark_generator() -> None: generator = BenchmarkGenerator(qasm_output_path="test") assert generator.qasm_output_path == "test" assert generator.timeout > 0 @@ -1107,7 +1105,7 @@ def endless_loop(arg1: SampleObject, run_forever: bool) -> bool: # noqa: ARG001 class SampleObject: - def __init__(self, name: str): + def __init__(self, name: str) -> None: self.name = name