From 86f81ddb34556a80db61b48ee2f93461d54314e6 Mon Sep 17 00:00:00 2001 From: Nils Quetschlich Date: Sun, 11 Dec 2022 13:47:10 +0100 Subject: [PATCH] Dev (#144) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependencies (#137) * 📦 support versioning in git * ✨updated groundstate benchmark * ✨updated vqe benchmarks * ✨updated vqe benchmarks * ✨updated vqe benchmarks * ✨updated qaoa benchmarks * ✨updated dependency requirements * ✨put very long test case in comment * 🚨 * 🚨 commented out unused imports * 🙈 added gitignore to make sure the folder exists * ✅ added tests for hhl and shor for the indep level and fixed unnecessary conversion * 🎨 removed unnecessary comments * 📦 loosened dependencies * 📦 loosened dependencies * Fix oqc qasm (#138) * ✨ adjusted the OQC adjustment and added it immediately after the qasm file is saved * ✨ removed the indepedent level because there will never be an ECR during that step * 🎨 removed commented code * 🎨 improved OQC qasm file postprocessing * Imrove get one benchmark (#141) * ✨ added error handling and deprecation warning for get_one_benchmark * 🐛 fixed faulty tests * ✨ set bool type for some of the pandas dataframe columns to reduce future warnings * ✨ updated from rigetti m1 to m2 * 🐛 fixed typos * 📌 simplified qiskit dependencies * ✨ added get_benchmark method * ✨ substituted aspen m1 with m2 * 🐛 adjusted tket fullpeephole optimization method call * ✨ added method for get_benchmark in init * ✨ adjusted timeout for benchmark calculation * 🎨 removed assert statement * 🐛 changed if statement such that also OQC files with a custom filename are postprocessed * 🐛 changed oqc postprocessing such that the compiler information is not needed anymore * 🐛 bugfixed the version stamp in qasm files * ✅ added test for oqc postprocessing * 🐛 fixed a bug in qiskit native gates with custom filename * 💡 removed comment and commented out the zipping * 🎨 removed the m2 identifier to show also m1 benchmarks which have the same connectivity * 🎨 removed the m2 identifier to show also m1 benchmarks which have the same connectivity * 📌 added pyscf dependency --- README.md | 2 +- config.json | 2 +- mqt/bench/__init__.py | 1 + mqt/bench/benchmark_generator.py | 102 +++++++- mqt/bench/benchmarks/qaoa.py | 10 +- .../portfolioqaoa.py | 16 +- .../portfoliovqe.py | 16 +- .../qiskit_application_nature/groundstate.py | 30 +-- .../routing.py | 70 +++--- .../qiskit_application_optimization/tsp.py | 13 +- mqt/bench/benchmarks/vqe.py | 10 +- mqt/bench/tests/test_all.py | 110 +++++++-- mqt/bench/utils/qiskit_helper.py | 13 +- mqt/bench/utils/tket_helper.py | 7 +- mqt/bench/utils/utils.py | 229 ++++++++---------- mqt/benchviewer/src/backend.py | 11 +- mqt/benchviewer/src/tests/test_backend.py | 8 +- .../static/files/qasm_output/.gitignore | 4 + mqt/benchviewer/templates/index.html | 10 +- pyproject.toml | 23 +- 20 files changed, 384 insertions(+), 303 deletions(-) create mode 100644 mqt/benchviewer/static/files/qasm_output/.gitignore diff --git a/README.md b/README.md index 85d110652..1d31b7ffa 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ So far, MQT Bench supports the following devices: 1. IBMQ Washington with 127 qubits 2. IBMQ Montreal with 27 qubits -3. Rigetti Aspen-M1 with 80 qubits +3. Rigetti Aspen-M2 with 80 qubits 4. IonQ with 11 qubits 5. OQC Lucy with 8 qubits diff --git a/config.json b/config.json index f0f805d18..19e0d9194 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,5 @@ { - "timeout": 1, + "timeout": 120, "benchmarks": [ { "name": "ae", diff --git a/mqt/bench/__init__.py b/mqt/bench/__init__.py index 138544811..fd510ba2f 100644 --- a/mqt/bench/__init__.py +++ b/mqt/bench/__init__.py @@ -1,4 +1,5 @@ from mqt.bench.benchmark_generator import ( create_benchmarks_from_config, + get_benchmark, get_one_benchmark, ) diff --git a/mqt/bench/benchmark_generator.py b/mqt/bench/benchmark_generator.py index 04ca49220..bdb6c8ab4 100644 --- a/mqt/bench/benchmark_generator.py +++ b/mqt/bench/benchmark_generator.py @@ -5,6 +5,7 @@ import json import signal from os import mkdir, path +from warnings import warn from joblib import Parallel, delayed from qiskit import QuantumCircuit @@ -269,7 +270,7 @@ def generate_target_dep_level_circuit( compilation_paths = [ ("ibm", [("ibm_washington", 127), ("ibm_montreal", 27)]), - ("rigetti", [("rigetti_aspen_m1", 80)]), + ("rigetti", [("rigetti_aspen_m2", 80)]), ("ionq", [("ionq11", 11)]), ("oqc", [("oqc_lucy", 8)]), ] @@ -483,7 +484,7 @@ def get_one_benchmark( gate_set_name: str = "ibm", device_name: str = "ibm_washington", ): - """Returns one benchmark as a Qiskit::QuantumCircuit Object. + """Returns one benchmark as a Qiskit::QuantumCircuit Object. Deprecated method, please use 'get_benchmark' instead. Keyword arguments: benchmark_name -- name of the to be generated benchmark @@ -493,13 +494,103 @@ def get_one_benchmark( compiler -- "qiskit" or "tket" compiler_settings -- Dictionary containing the respective compiler settings for the specified compiler (e.g., optimization level for Qiskit or placement for TKET) gate_set_name -- "ibm", "rigetti", "ionq", or "oqc" - device_name -- "ibm_washington", "ibm_montreal", "aspen_m1", "ionq11", ""lucy"" + device_name -- "ibm_washington", "ibm_montreal", "aspen_m2", "ionq11", ""lucy"" + + Return values: + 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) + """ + + warn( + "'get_one_benchmark' is deprecated and will be removed in the future. Please use the 'get_benchmark' method with the same parameters.", + DeprecationWarning, + stacklevel=2, + ) + + return get_benchmark( + benchmark_name=benchmark_name, + level=level, + circuit_size=circuit_size, + benchmark_instance_name=benchmark_instance_name, + compiler=compiler, + compiler_settings=compiler_settings, + gate_set_name=gate_set_name, + device_name=device_name, + ) + + +def get_benchmark( + benchmark_name: str, + level: str | int, + circuit_size: int = None, + benchmark_instance_name: str = None, + compiler: str = "qiskit", + compiler_settings: dict[str, dict[str, any]] = None, + gate_set_name: str = "ibm", + device_name: str = "ibm_washington", +): + """Returns one benchmark as a Qiskit::QuantumCircuit Object. + + Keyword arguments: + 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" + compiler_settings -- Dictionary containing the respective compiler settings for the specified compiler (e.g., optimization level for Qiskit or placement for TKET) + gate_set_name -- "ibm", "rigetti", "ionq", or "oqc" + device_name -- "ibm_washington", "ibm_montreal", "rigetti_aspen_m2", "ionq11", ""oqc_lucy"" Return values: 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) """ + init_module_paths() + if benchmark_name not in utils.get_supported_benchmarks(): + raise ValueError( + f"Selected benchmark is not supported. Valid benchmarks are {utils.get_supported_benchmarks()}." + ) + + if level not in utils.get_supported_levels(): + raise ValueError(f"Selected level must be in {utils.get_supported_levels()}.") + + if benchmark_name not in ["shor", "groundstate"] and not isinstance( + circuit_size, int + ): + raise ValueError("circuit_size must be None or int for this benchmark.") + elif benchmark_name in ["shor", "groundstate"] and not isinstance( + benchmark_instance_name, str + ): + raise ValueError("benchmark_instance_name must be defined for this benchmark.") + + if benchmark_instance_name is not None and not isinstance( + benchmark_instance_name, str + ): + raise ValueError("Selected benchmark_instance_name must be None or str.") + + if compiler is not None and compiler.lower() not in utils.get_supported_compilers(): + raise ValueError( + f"Selected compiler must be in {utils.get_supported_compilers()}." + ) + + if compiler_settings is not None and not isinstance(compiler_settings, dict): + raise ValueError( + "Selected compiler_settings must be None or 'dict[str, dict[str, any]]'." + ) + + if ( + gate_set_name is not None + and gate_set_name not in utils.get_supported_gatesets() + ): + raise ValueError( + f"Selected gate_set_name must be None or in {utils.get_supported_gatesets()}." + ) + + if device_name is not None and device_name not in utils.get_supported_devices(): + raise ValueError( + f"Selected device_name must be None or in {utils.get_supported_devices()}." + ) + if "grover" in benchmark_name or "qwalk" in benchmark_name: if "noancilla" in benchmark_name: anc_mode = "noancilla" @@ -551,6 +642,7 @@ def get_one_benchmark( "tket": {"placement": "lineplacement"}, } + compiler = compiler.lower() if level == "alg" or level == 0: return qc @@ -617,9 +709,7 @@ def get_one_benchmark( args = parser.parse_args() print("#### Start generating") - # create_benchmarks_from_config(args.file_name) - print("#### Start preprocessing") - utils.postprocess_oqc_qasm_files() + create_benchmarks_from_config(args.file_name) print("#### Start zipping") # utils.create_zip_file() print("#### Generation ended") diff --git a/mqt/bench/benchmarks/qaoa.py b/mqt/bench/benchmarks/qaoa.py index 3c4552b0a..ba233a261 100644 --- a/mqt/bench/benchmarks/qaoa.py +++ b/mqt/bench/benchmarks/qaoa.py @@ -2,10 +2,9 @@ from __future__ import annotations -from qiskit import Aer -from qiskit.algorithms import QAOA +from qiskit.algorithms.minimum_eigensolvers import QAOA from qiskit.algorithms.optimizers import SLSQP -from qiskit.utils import QuantumInstance +from qiskit.primitives import Sampler from mqt.bench.utils.utils import get_examplary_max_cut_qp @@ -19,11 +18,8 @@ def create_circuit(num_qubits: int): """ qp = get_examplary_max_cut_qp(num_qubits) - sim = QuantumInstance( - backend=Aer.get_backend("aer_simulator"), shots=1024, seed_simulator=10 - ) - qaoa = QAOA(reps=2, optimizer=SLSQP(maxiter=25), quantum_instance=sim) + qaoa = QAOA(sampler=Sampler(), reps=2, optimizer=SLSQP(maxiter=25)) qaoa_result = qaoa.compute_minimum_eigenvalue(qp.to_ising()[0]) qc = qaoa.ansatz.bind_parameters(qaoa_result.optimal_point) diff --git a/mqt/bench/benchmarks/qiskit_application_finance/portfolioqaoa.py b/mqt/bench/benchmarks/qiskit_application_finance/portfolioqaoa.py index 860352ec4..07168d3e5 100644 --- a/mqt/bench/benchmarks/qiskit_application_finance/portfolioqaoa.py +++ b/mqt/bench/benchmarks/qiskit_application_finance/portfolioqaoa.py @@ -4,10 +4,9 @@ import datetime -from qiskit import Aer -from qiskit.algorithms import QAOA +from qiskit.algorithms.minimum_eigensolvers import QAOA from qiskit.algorithms.optimizers import COBYLA -from qiskit.utils import QuantumInstance +from qiskit.primitives import Sampler from qiskit_finance.applications import PortfolioOptimization from qiskit_finance.data_providers import RandomDataProvider from qiskit_optimization.converters import QuadraticProgramToQubo @@ -44,18 +43,11 @@ def create_circuit(num_qubits: int): conv = QuadraticProgramToQubo() qp_qubo = conv.convert(qp) - backend = Aer.get_backend("aer_simulator") - seed = 10 - cobyla = COBYLA() cobyla.set_options(maxiter=25) - sim = QuantumInstance( - backend=backend, seed_simulator=seed, seed_transpiler=seed, shots=1024 - ) - - qaoa = QAOA(cobyla, 3, quantum_instance=sim) - qaoa.random_seed = seed + qaoa = QAOA(sampler=Sampler(), optimizer=cobyla, reps=3) + qaoa.random_seed = 10 qaoa_result = qaoa.compute_minimum_eigenvalue(qp_qubo.to_ising()[0]) qc = qaoa.ansatz.bind_parameters(qaoa_result.optimal_point) diff --git a/mqt/bench/benchmarks/qiskit_application_finance/portfoliovqe.py b/mqt/bench/benchmarks/qiskit_application_finance/portfoliovqe.py index 9f33aecf2..8b9dcc523 100644 --- a/mqt/bench/benchmarks/qiskit_application_finance/portfoliovqe.py +++ b/mqt/bench/benchmarks/qiskit_application_finance/portfoliovqe.py @@ -4,11 +4,10 @@ import datetime -from qiskit import Aer -from qiskit.algorithms import VQE +from qiskit.algorithms.minimum_eigensolvers import VQE from qiskit.algorithms.optimizers import COBYLA from qiskit.circuit.library import TwoLocal -from qiskit.utils import QuantumInstance +from qiskit.primitives import Estimator from qiskit_finance.applications import PortfolioOptimization from qiskit_finance.data_providers import RandomDataProvider from qiskit_optimization.converters import QuadraticProgramToQubo @@ -44,19 +43,12 @@ def create_circuit(num_qubits: int): qp = portfolio.to_quadratic_program() conv = QuadraticProgramToQubo() qp_qubo = conv.convert(qp) - - backend = Aer.get_backend("aer_simulator") - seed = 10 - cobyla = COBYLA() cobyla.set_options(maxiter=25) ry = TwoLocal(num_assets, "ry", "cz", reps=3, entanglement="full") - sim = QuantumInstance( - backend=backend, seed_simulator=seed, seed_transpiler=seed, shots=1024 - ) - vqe = VQE(ry, optimizer=cobyla, quantum_instance=sim) - vqe.random_seed = seed + vqe = VQE(ansatz=ry, optimizer=cobyla, estimator=Estimator()) + vqe.random_seed = 10 vqe_result = vqe.compute_minimum_eigenvalue(qp_qubo.to_ising()[0]) qc = vqe.ansatz.bind_parameters(vqe_result.optimal_point) diff --git a/mqt/bench/benchmarks/qiskit_application_nature/groundstate.py b/mqt/bench/benchmarks/qiskit_application_nature/groundstate.py index 8b55b3e0e..a0b926443 100644 --- a/mqt/bench/benchmarks/qiskit_application_nature/groundstate.py +++ b/mqt/bench/benchmarks/qiskit_application_nature/groundstate.py @@ -3,35 +3,27 @@ from __future__ import annotations -from qiskit import Aer -from qiskit.algorithms import VQE +from qiskit.algorithms.minimum_eigensolvers import VQE from qiskit.algorithms.optimizers import COBYLA from qiskit.circuit.library import TwoLocal -from qiskit.utils import QuantumInstance -from qiskit_nature.converters.second_quantization import QubitConverter -from qiskit_nature.drivers.second_quantization import ( - ElectronicStructureDriverType, - ElectronicStructureMoleculeDriver, -) -from qiskit_nature.mappers.second_quantization import JordanWignerMapper -from qiskit_nature.problems.second_quantization import ElectronicStructureProblem +from qiskit.primitives import Estimator +from qiskit_nature.second_q.drivers import PySCFDriver +from qiskit_nature.second_q.mappers import JordanWignerMapper, QubitConverter -def create_circuit(molecule, basis: str = "sto3g"): +def create_circuit(molecule): """Returns a quantum circuit implementing Ground State Estimation. Keyword arguments: molecule -- Molecule for which the ground state shall be estimated. """ - driver = ElectronicStructureMoleculeDriver( - molecule, basis=basis, driver_type=ElectronicStructureDriverType.PYSCF - ) + driver = PySCFDriver(atom=molecule) + es_problem = driver.run() - es_problem = ElectronicStructureProblem(driver) - qubit_converter = QubitConverter(JordanWignerMapper()) + converter = QubitConverter(JordanWignerMapper()) second_q_op = es_problem.second_q_ops() - operator = qubit_converter.convert_only(second_q_op[0]) + operator = converter.convert_only(second_q_op[0]) tl_circuit = TwoLocal( rotation_blocks=["h", "rx"], @@ -42,9 +34,7 @@ def create_circuit(molecule, basis: str = "sto3g"): ) another_solver = VQE( - ansatz=tl_circuit, - quantum_instance=QuantumInstance(Aer.get_backend("aer_simulator"), shots=1024), - optimizer=COBYLA(maxiter=25), + ansatz=tl_circuit, estimator=Estimator(), optimizer=COBYLA(maxiter=25) ) result = another_solver.compute_minimum_eigenvalue(operator) diff --git a/mqt/bench/benchmarks/qiskit_application_optimization/routing.py b/mqt/bench/benchmarks/qiskit_application_optimization/routing.py index 3223a80b5..117ec924c 100644 --- a/mqt/bench/benchmarks/qiskit_application_optimization/routing.py +++ b/mqt/bench/benchmarks/qiskit_application_optimization/routing.py @@ -3,12 +3,12 @@ from __future__ import annotations import numpy as np -from qiskit import Aer -from qiskit.algorithms import VQE +from qiskit.algorithms.minimum_eigensolvers import VQE from qiskit.algorithms.optimizers import SLSQP -from qiskit.utils import QuantumInstance, algorithm_globals +from qiskit.circuit.library import RealAmplitudes +from qiskit.primitives import Estimator +from qiskit.utils import algorithm_globals from qiskit_optimization import QuadraticProgram -from qiskit_optimization.algorithms import MinimumEigenOptimizer class Initializer: @@ -32,19 +32,19 @@ def generate_instance(self): class QuantumOptimizer: - def __init__(self, instance, n, k): + def __init__(self, instance, n, K): self.instance = instance self.n = n - self.K = k + self.K = K def binary_representation(self, x_sol=0): 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) @@ -53,12 +53,13 @@ def binary_representation(self, x_sol=0): for ii in range(len(w_list)): w[ii] = w_list[ii] - 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 + # 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 v = np.zeros([n, n * (n - 1)]) for ii in range(n): @@ -74,38 +75,37 @@ def binary_representation(self, x_sol=0): 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) + - 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: max(x_sol) # Evaluates the cost distance from a binary representation of a path fun = ( - lambda x: np.dot(np.around(x), np.dot(q, np.around(x))) + lambda x: np.dot(np.around(x), np.dot(Q, np.around(x))) + np.dot(g, np.around(x)) + c ) cost = fun(x_sol) - except Exception: + except BaseException: cost = 0 - return q, g, c, cost - - def construct_problem(self, q, g, c): + return Q, g, c, cost + def construct_problem(self, Q, g, c) -> 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 @@ -113,19 +113,12 @@ def construct_problem(self, q, g, c): def solve_problem(self, qp): algorithm_globals.random_seed = 10 - quantum_instance = QuantumInstance( - Aer.get_backend("aer_simulator"), - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - shots=1024, - ) - vqe = VQE(quantum_instance=quantum_instance, optimizer=SLSQP(maxiter=25)) - optimizer = MinimumEigenOptimizer(min_eigen_solver=vqe) - result = optimizer.solve(qp) - # compute cost of the obtained result - _, _, _, level = self.binary_representation(x_sol=result.x) - return result.x, level, vqe + ansatz = RealAmplitudes(self.n) + vqe = VQE(estimator=Estimator(), optimizer=SLSQP(maxiter=25), ansatz=ansatz) + vqe_result = vqe.compute_minimum_eigenvalue(qp.to_ising()[0]) + qc = vqe.ansatz.bind_parameters(vqe_result.optimal_point) + return qc def create_circuit(num_nodes: int = 3, num_vehs: int = 2): @@ -147,10 +140,7 @@ def create_circuit(num_nodes: int = 3, num_vehs: int = 2): q, g, c, binary_cost = quantum_optimizer.binary_representation() qp = quantum_optimizer.construct_problem(q, g, c) # Instantiate the quantum optimizer class with parameters: - quantum_solution, quantum_cost, vqe = quantum_optimizer.solve_problem(qp) - - vqe_result = vqe.compute_minimum_eigenvalue(qp.to_ising()[0]) - qc = vqe.ansatz.bind_parameters(vqe_result.optimal_point) + qc = quantum_optimizer.solve_problem(qp) qc.measure_all() qc.name = "routing" diff --git a/mqt/bench/benchmarks/qiskit_application_optimization/tsp.py b/mqt/bench/benchmarks/qiskit_application_optimization/tsp.py index da3b0ee8a..5d4feac28 100644 --- a/mqt/bench/benchmarks/qiskit_application_optimization/tsp.py +++ b/mqt/bench/benchmarks/qiskit_application_optimization/tsp.py @@ -2,11 +2,11 @@ from __future__ import annotations -from qiskit import Aer -from qiskit.algorithms import VQE +from qiskit.algorithms.minimum_eigensolvers import VQE from qiskit.algorithms.optimizers import SPSA from qiskit.circuit.library import TwoLocal -from qiskit.utils import QuantumInstance, algorithm_globals +from qiskit.primitives import Estimator +from qiskit.utils import algorithm_globals from qiskit_optimization.applications import Tsp from qiskit_optimization.converters import QuadraticProgramToQubo @@ -29,15 +29,10 @@ def create_circuit(num_nodes: int): qubit_op, offset = qubo.to_ising() algorithm_globals.random_seed = 10 - seed = 10 - backend = Aer.get_backend("aer_simulator") - quantum_instance = QuantumInstance( - backend, seed_simulator=seed, seed_transpiler=seed, shots=1024 - ) spsa = SPSA(maxiter=25) ry = TwoLocal(qubit_op.num_qubits, "ry", "cz", reps=5, entanglement="linear") - vqe = VQE(ry, optimizer=spsa, quantum_instance=quantum_instance) + vqe = VQE(ansatz=ry, optimizer=spsa, estimator=Estimator()) vqe_result = vqe.compute_minimum_eigenvalue(qubit_op) qc = vqe.ansatz.bind_parameters(vqe_result.optimal_point) diff --git a/mqt/bench/benchmarks/vqe.py b/mqt/bench/benchmarks/vqe.py index 19f24bf3b..772710edf 100644 --- a/mqt/bench/benchmarks/vqe.py +++ b/mqt/bench/benchmarks/vqe.py @@ -2,11 +2,10 @@ from __future__ import annotations -from qiskit import Aer -from qiskit.algorithms import VQE +from qiskit.algorithms.minimum_eigensolvers import VQE from qiskit.algorithms.optimizers import SLSQP from qiskit.circuit.library import RealAmplitudes -from qiskit.utils import QuantumInstance +from qiskit.primitives import Estimator from mqt.bench.utils.utils import get_examplary_max_cut_qp @@ -20,12 +19,9 @@ def create_circuit(num_qubits: int): """ qp = get_examplary_max_cut_qp(num_qubits) - sim = QuantumInstance( - backend=Aer.get_backend("aer_simulator"), shots=1024, seed_simulator=10 - ) ansatz = RealAmplitudes(num_qubits, reps=2) - vqe = VQE(ansatz, optimizer=SLSQP(maxiter=25), quantum_instance=sim) + vqe = VQE(ansatz=ansatz, optimizer=SLSQP(maxiter=25), estimator=Estimator()) vqe_result = vqe.compute_minimum_eigenvalue(qp.to_ising()[0]) qc = vqe.ansatz.bind_parameters(vqe_result.optimal_point) diff --git a/mqt/bench/tests/test_all.py b/mqt/bench/tests/test_all.py index fae6d61f7..3d0416963 100644 --- a/mqt/bench/tests/test_all.py +++ b/mqt/bench/tests/test_all.py @@ -3,10 +3,10 @@ import os import pytest -from pytket.qasm import circuit_to_qasm_str +from pytket.extensions.qiskit import tk_to_qiskit from qiskit import QuantumCircuit -from mqt.bench.benchmark_generator import get_one_benchmark +from mqt.bench.benchmark_generator import get_benchmark from mqt.bench.benchmarks import ( ae, dj, @@ -102,7 +102,6 @@ def test_quantumcircuit_indep_level(benchmark, input_value, scalable): (dj, 5, True), (graphstate, 8, True), (grover, 5, False), - # (hhl, 2, False), (qaoa, 5, True), (qft, 8, True), (qftentangled, 8, True), @@ -116,7 +115,6 @@ def test_quantumcircuit_indep_level(benchmark, input_value, scalable): (twolocalrandom, 5, True), (wstate, 8, True), (portfolioqaoa, 5, True), - # (shor, 15, False), (portfoliovqe, 5, True), (pricingcall, 5, False), (pricingput, 5, False), @@ -133,7 +131,7 @@ def test_quantumcircuit_native_and_mapped_levels(benchmark, input_value, scalabl compilation_paths = [ ("ibm", [("ibm_washington", 127), ("ibm_montreal", 27)]), - ("rigetti", [("rigetti_aspen_m1", 80)]), + ("rigetti", [("rigetti_aspen_m2", 80)]), ("ionq", [("ionq11", 11)]), ("oqc", [("oqc_lucy", 8)]), ] @@ -222,15 +220,8 @@ def test_openqasm_gates(): assert len(openqasm_gates) == 42 -@pytest.mark.parametrize("num_circles", [2, 3, 4, 5, 10]) -def test_rigetti_cmap_generator(num_circles: int): - if num_circles != 10: - assert ( - len(utils.get_rigetti_c_map(num_circles)) - == (10 * (num_circles - 1) + 8) * 2 - ) - else: - assert len(utils.get_rigetti_c_map(num_circles)) == 212 +def test_rigetti_cmap_generator(): + assert len(utils.get_rigetti_aspen_m2_map()) == 212 def test_dj_constant_oracle(): @@ -257,7 +248,7 @@ def test_routing(): "alg", 5, None, - None, + "qiskit", None, None, None, @@ -267,7 +258,7 @@ def test_routing(): 0, 6, None, - None, + "tket", None, None, None, @@ -438,7 +429,7 @@ def test_routing(): "qiskit": {"optimization_level": 1}, }, "rigetti", - "rigetti_aspen_m1", + "rigetti_aspen_m2", ), ( "qpeexact", @@ -522,7 +513,7 @@ def test_routing(): "qiskit": {"optimization_level": 1}, }, "rigetti", - "rigetti_aspen_m1", + "rigetti_aspen_m2", ), ( "qpeinexact", @@ -534,7 +525,7 @@ def test_routing(): "tket": {"placement": "lineplacement"}, }, "rigetti", - "rigetti_aspen_m1", + "rigetti_aspen_m2", ), ( "qpeinexact", @@ -562,7 +553,7 @@ def test_routing(): ), ], ) -def test_get_one_benchmark( +def test_get_benchmark( benchmark_name, level, circuit_size, @@ -573,7 +564,7 @@ def test_get_one_benchmark( device_name, ): - qc = get_one_benchmark( + qc = get_benchmark( benchmark_name, level, circuit_size, @@ -586,7 +577,7 @@ def test_get_one_benchmark( assert qc.depth() > 0 if gate_set_name and "oqc" not in gate_set_name: if compiler == "tket": - qc = QuantumCircuit.from_qasm_str(circuit_to_qasm_str(qc)) + qc = tk_to_qiskit(qc) for instruction, _qargs, _cargs in qc.data: gate_type = instruction.name assert ( @@ -616,7 +607,7 @@ def test_saving_qasm_to_alternative_location_with_alternative_filename( ): directory = "." filename = "ae_test_qiskit" - qc = get_one_benchmark("ae", abstraction_level, 5) + qc = get_benchmark("ae", abstraction_level, 5) assert qc res = qiskit_helper.get_mapped_level( qc, "ibm", qc.num_qubits, "ibm_washington", 1, False, False, directory, filename @@ -628,12 +619,81 @@ def test_saving_qasm_to_alternative_location_with_alternative_filename( directory = "." filename = "ae_test_tket" - qc = get_one_benchmark("ae", abstraction_level, 7) + qc = get_benchmark("ae", abstraction_level, 7) assert qc res = tket_helper.get_mapped_level( - qc, "ibm", qc.num_qubits, "ibm_washington", 1, False, False, directory, filename + qc, + "ibm", + qc.num_qubits, + "ibm_washington", + False, + False, + False, + directory, + filename, ) assert res path = os.path.join(directory, filename) + ".qasm" assert os.path.isfile(path) os.remove(path) + + +def test_oqc_postprocessing(): + qc = get_benchmark("ghz", 1, 5) + assert qc + directory = "." + filename = "ghz_oqc" + path = os.path.join(directory, filename) + ".qasm" + + tket_helper.get_native_gates_level( + qc, + "oqc", + qc.num_qubits, + file_precheck=False, + return_qc=False, + target_directory=directory, + target_filename=filename, + ) + assert QuantumCircuit.from_qasm_file(path) + os.remove(path) + + tket_helper.get_mapped_level( + qc, + "oqc", + qc.num_qubits, + "oqc_lucy", + lineplacement=False, + file_precheck=False, + return_qc=False, + target_directory=directory, + target_filename=filename, + ) + assert QuantumCircuit.from_qasm_file(path) + os.remove(path) + + qiskit_helper.get_native_gates_level( + qc, + "oqc", + qc.num_qubits, + opt_level=1, + file_precheck=False, + return_qc=False, + target_directory=directory, + target_filename=filename, + ) + assert QuantumCircuit.from_qasm_file(path) + os.remove(path) + + qiskit_helper.get_mapped_level( + qc, + "oqc", + qc.num_qubits, + "oqc_lucy", + opt_level=1, + file_precheck=False, + return_qc=False, + target_directory=directory, + target_filename=filename, + ) + assert QuantumCircuit.from_qasm_file(path) + os.remove(path) diff --git a/mqt/bench/utils/qiskit_helper.py b/mqt/bench/utils/qiskit_helper.py index 8b7e3aa02..d9d97f4b0 100644 --- a/mqt/bench/utils/qiskit_helper.py +++ b/mqt/bench/utils/qiskit_helper.py @@ -118,7 +118,6 @@ def get_native_gates_level( """ gate_set = get_native_gates(gate_set_name) - if not target_filename: filename_native = ( qc.name @@ -141,23 +140,13 @@ def get_native_gates_level( compiled_without_architecture = transpile( qc, basis_gates=gate_set, optimization_level=opt_level, seed_transpiler=10 ) - n_actual = compiled_without_architecture.num_qubits - filename_nativegates = ( - qc.name - + "_nativegates_" - + gate_set_name - + "_qiskit_opt" - + str(opt_level) - + "_" - + str(n_actual) - ) if return_qc: return compiled_without_architecture else: res = utils.save_as_qasm( compiled_without_architecture.qasm(), - filename_nativegates, + filename_native, gate_set, target_directory=target_directory, ) diff --git a/mqt/bench/utils/tket_helper.py b/mqt/bench/utils/tket_helper.py index 767ab2605..ef212e05d 100644 --- a/mqt/bench/utils/tket_helper.py +++ b/mqt/bench/utils/tket_helper.py @@ -113,7 +113,8 @@ def get_indep_level( except Exception as e: print("TKET Exception Indep: ", e) return False - FullPeepholeOptimise().apply(qc_tket) + + FullPeepholeOptimise(target_2qb_gate=OpType.TK2).apply(qc_tket) if return_qc: return qc_tket else: @@ -183,7 +184,7 @@ def get_native_gates_level( native_gatenames = get_rebase(gate_set_name, True) native_gate_set_rebase = get_rebase(gate_set_name) native_gate_set_rebase.apply(qc_tket) - FullPeepholeOptimise().apply(qc_tket) + FullPeepholeOptimise(target_2qb_gate=OpType.TK2).apply(qc_tket) native_gate_set_rebase.apply(qc_tket) if return_qc: @@ -272,7 +273,7 @@ def get_mapped_level( arch = architecture.Architecture(cmap) native_gate_set_rebase.apply(qc_tket) - FullPeepholeOptimise().apply(qc_tket) + FullPeepholeOptimise(target_2qb_gate=OpType.TK2).apply(qc_tket) if lineplacement: PlacementPass(LinePlacement(arch)).apply(qc_tket) else: diff --git a/mqt/bench/utils/utils.py b/mqt/bench/utils/utils.py index 7f02e64b3..422fdc0c7 100644 --- a/mqt/bench/utils/utils.py +++ b/mqt/bench/utils/utils.py @@ -2,19 +2,73 @@ import os import subprocess +import sys from datetime import date import networkx as nx import numpy as np -from joblib import Parallel, delayed from pytket import __version__ as __tket_version__ from qiskit import QuantumCircuit, __qiskit_version__ from qiskit.algorithms import EstimationProblem -from qiskit.test.mock import FakeMontreal, FakeWashington +from qiskit.providers.fake_provider import FakeMontreal, FakeWashington + +if sys.version_info < (3, 10, 0): + import importlib_metadata as metadata +else: + from importlib import metadata qasm_path = "mqt/benchviewer/static/files/qasm_output/" +def get_supported_benchmarks(): + return [ + "ae", + "dj", + "grover-noancilla", + "grover-v-chain", + "ghz", + "graphstate", + "portfolioqaoa", + "portfoliovqe", + "qaoa", + "qft", + "qftentangled", + "qgan", + "qpeexact", + "qpeinexact", + "qwalk-noancilla", + "qwalk-v-chain", + "realamprandom", + "su2random", + "twolocalrandom", + "vqe", + "wstate", + "shor", + "hhl", + "pricingcall", + "pricingput", + "groundstate", + "routing", + "tsp", + ] + + +def get_supported_levels(): + return ["alg", "indep", "nativegates", "mapped", 0, 1, 2, 3] + + +def get_supported_compilers(): + return ["qiskit", "tket"] + + +def get_supported_gatesets(): + return ["ibm", "rigetti", "ionq", "oqc"] + + +def get_supported_devices(): + return ["ibm_washington", "ibm_montreal", "rigetti_aspen_m2", "ionq11", "oqc_lucy"] + + def set_qasm_output_path(new_path: str = "mqt/benchviewer/static/files/qasm_output/"): global qasm_path qasm_path = new_path @@ -88,58 +142,38 @@ def get_estimation_problem(): return problem -def get_rigetti_c_map(circles: int = 4): - """Returns a coupling map of the circular layout scheme used by Rigetti. - - Keyword arguments: - circles -- number of circles, each one comprises 8 qubits - """ - if circles != 10: - c_map_rigetti = [] - for j in range(circles): - for i in range(0, 7): - c_map_rigetti.append([i + j * 8, i + 1 + j * 8]) - - if i == 6: - c_map_rigetti.append([0 + j * 8, 7 + j * 8]) - - if j != 0: - c_map_rigetti.append([j * 8 - 6, j * 8 + 5]) - c_map_rigetti.append([j * 8 - 7, j * 8 + 6]) - - inverted = [[item[1], item[0]] for item in c_map_rigetti] - c_map_rigetti = c_map_rigetti + inverted - else: - c_map_rigetti = [] - for j in range(5): - for i in range(0, 7): - c_map_rigetti.append([i + j * 8, i + 1 + j * 8]) +def get_rigetti_aspen_m2_map(): + """Returns a coupling map of Rigetti Aspen M2 chip.""" + c_map_rigetti = [] + for j in range(5): + for i in range(0, 7): + c_map_rigetti.append([i + j * 8, i + 1 + j * 8]) - if i == 6: - c_map_rigetti.append([0 + j * 8, 7 + j * 8]) + if i == 6: + c_map_rigetti.append([0 + j * 8, 7 + j * 8]) - if j != 0: - c_map_rigetti.append([j * 8 - 6, j * 8 + 5]) - c_map_rigetti.append([j * 8 - 7, j * 8 + 6]) + if j != 0: + c_map_rigetti.append([j * 8 - 6, j * 8 + 5]) + c_map_rigetti.append([j * 8 - 7, j * 8 + 6]) - for j in range(5): - m = 8 * j + 5 * 8 - for i in range(0, 7): - c_map_rigetti.append([i + m, i + 1 + m]) + for j in range(5): + m = 8 * j + 5 * 8 + for i in range(0, 7): + c_map_rigetti.append([i + m, i + 1 + m]) - if i == 6: - c_map_rigetti.append([0 + m, 7 + m]) + if i == 6: + c_map_rigetti.append([0 + m, 7 + m]) - if j != 0: - c_map_rigetti.append([m - 6, m + 5]) - c_map_rigetti.append([m - 7, m + 6]) + if j != 0: + c_map_rigetti.append([m - 6, m + 5]) + c_map_rigetti.append([m - 7, m + 6]) - for n in range(5): - c_map_rigetti.append([n * 8 + 3, n * 8 + 5 * 8]) - c_map_rigetti.append([n * 8 + 4, n * 8 + 7 + 5 * 8]) + for n in range(5): + c_map_rigetti.append([n * 8 + 3, n * 8 + 5 * 8]) + c_map_rigetti.append([n * 8 + 4, n * 8 + 7 + 5 * 8]) - inverted = [[item[1], item[0]] for item in c_map_rigetti] - c_map_rigetti = c_map_rigetti + inverted + inverted = [[item[1], item[0]] for item in c_map_rigetti] + c_map_rigetti = c_map_rigetti + inverted return c_map_rigetti @@ -230,13 +264,22 @@ def save_as_qasm( qasm_output_folder = get_qasm_output_path() filename = os.path.join(qasm_output_folder, filename) + ".qasm" + + try: + mqtbench_module_version = metadata.version("mqt.bench") + except Exception: + print( + "'mqt.bench' is most likely not installed. Please run 'pip install . or pip install mqt.bench'." + ) + return False + with open(filename, "w") as f: f.write("// Benchmark was created by MQT Bench on " + str(date.today()) + "\n") f.write( "// For more information about MQT Bench, please visit https://www.cda.cit.tum.de/mqtbench/" + "\n" ) - f.write("// MQT Bench version: " + "0.1.0" + "\n") + f.write("// MQT Bench version: " + mqtbench_module_version + "\n") if "qiskit" in filename: f.write("// Qiskit version: " + str(__qiskit_version__) + "\n") elif "tket" in filename: @@ -249,6 +292,9 @@ def save_as_qasm( f.write("\n") f.write(qc_str) f.close() + + if gate_set == ["rz", "sx", "x", "ecr", "measure"]: + postprocess_single_oqc_file(filename) return True @@ -267,8 +313,8 @@ def get_cmap_from_devicename(device: str): return FakeWashington().configuration().coupling_map elif device == "ibm_montreal": return FakeMontreal().configuration().coupling_map - elif device == "rigetti_aspen_m1": - return get_rigetti_c_map(10) + elif device == "rigetti_aspen_m2": + return get_rigetti_aspen_m2_map() elif device == "oqc_lucy": return get_cmap_oqc_lucy() elif device == "ionq11": @@ -279,84 +325,23 @@ def get_cmap_from_devicename(device: str): def get_molecule(benchmark_instance_name: str): """Returns a Molecule object depending on the parameter value.""" - try: - from qiskit_nature.drivers import Molecule - except Exception: - print("Please install qiskit_nature.") - return None - m_1 = Molecule( - geometry=[["H", [0.0, 0.0, 0.0]], ["H", [0.0, 0.0, 0.735]]], - charge=0, - multiplicity=1, - ) - m_2 = Molecule( - geometry=[["Li", [0.0, 0.0, 0.0]], ["H", [0.0, 0.0, 2.5]]], - charge=0, - multiplicity=1, - ) - m_3 = Molecule( - geometry=[ - ["O", [0.0, 0.0, 0.0]], - ["H", [0.586, 0.757, 0.0]], - ["H", [0.586, -0.757, 0.0]], - ], - charge=0, - multiplicity=1, - ) + m_1 = ["H 0.0 0.0 0.0", "H 0.0 0.0 0.735"] + m_2 = ["Li 0.0 0.0 0.0", "H 0.0 0.0 2.5"] + m_3 = ["O 0.0 0.0 0.0", "H 0.586, 0.757, 0.0", "H 0.586, -0.757, 0.0"] instances = {"small": m_1, "medium": m_2, "large": m_3} return instances[benchmark_instance_name] -def postprocess_oqc_qasm_files(): - directory = get_qasm_output_path() - Parallel(n_jobs=-1, verbose=100)( - delayed(postprocess_single_oqc_file)(directory, filename) - for filename in os.listdir(directory) - ) - - -def postprocess_single_oqc_file(directory: str, filename: str): - - f = os.path.join(directory, filename) - if "oqc_lucy_qiskit" in f or "oqc_qiskit" in f: - with open(f) as f: - lines = f.readlines() - new_name = os.path.join(directory, filename) - with open(new_name, "w") as f: - for line in lines: - if not ( - "gate rzx" in line.strip("\n") or "gate ecr" in line.strip("\n") - ): - f.write(line) - if "gate ecr" in line.strip("\n"): - f.write( - "gate rzx(param0) q0,q1 { h q1; cx q0,q1; rz(param0) q1; cx q0,q1; h q1; }\n" - ) - f.write( - "gate ecr q0,q1 { rzx(pi/4) q0,q1; x q0; rzx(-pi/4) q0,q1; }\n" - ) - - assert QuantumCircuit.from_qasm_file(new_name) - print("New qasm file for: ", new_name) - - elif "oqc_lucy_tket" in f or "oqc_tket" in f: - with open(f) as f: - lines = f.readlines() - new_name = os.path.join(directory, filename) - with open(new_name, "w") as f: - count = 0 - for line in lines: +def postprocess_single_oqc_file(filename: str): + with open(filename) as f: + lines = f.readlines() + with open(filename, "w") as f: + for line in lines: + if not ("gate rzx" in line.strip("\n") or "gate ecr" in line.strip("\n")): f.write(line) - count += 1 - if count == 9: - f.write( - "gate rzx(param0) q0,q1 { h q1; cx q0,q1; rz(param0) q1; cx q0,q1; h q1; }\n" - ) - f.write( - "gate ecr q0,q1 { rzx(pi/4) q0,q1; x q0; rzx(-pi/4) q0,q1; }\n" - ) - assert QuantumCircuit.from_qasm_file(new_name) + if 'include "qelib1.inc"' in line.strip("\n"): + f.write("opaque ecr q0,q1;\n") def create_zip_file(): diff --git a/mqt/benchviewer/src/backend.py b/mqt/benchviewer/src/backend.py index fd5cae51b..a70bb4bc2 100644 --- a/mqt/benchviewer/src/backend.py +++ b/mqt/benchviewer/src/backend.py @@ -297,8 +297,8 @@ def get_target_device(filename: str): return "ibm_washington" elif "ibm_montreal" in filename: return "ibm_montreal" - elif "rigetti_aspen_m1" in filename: - return "rigetti_aspen_m1" + elif "rigetti_aspen" in filename: + return "rigetti_aspen" elif "ionq11" in filename: return "ionq11" elif "oqc_lucy" in filename: @@ -369,6 +369,9 @@ def filterDatabase(filterCriteria: tuple, database: pd.DataFrame): "path", ] db_filtered = pd.DataFrame(columns=colnames) + db_filtered["indep_flag"] = db_filtered["indep_flag"].astype(bool) + db_filtered["nativegates_flag"] = db_filtered["nativegates_flag"].astype(bool) + db_filtered["mapped_flag"] = db_filtered["mapped_flag"].astype(bool) if len(database) == 0: return [] @@ -660,8 +663,8 @@ def prepareFormInput(formData: list): mapped_devices.append("ibm_montreal") if "device_ibm_washington" in k: mapped_devices.append("ibm_washington") - if "device_rigetti_aspen_m1" in k: - mapped_devices.append("rigetti_aspen_m1") + if "device_rigetti_aspen" in k: + mapped_devices.append("rigetti_aspen") if "device_oqc_lucy" in k: mapped_devices.append("oqc_lucy") if "device_ionq_ionq11" in k: diff --git a/mqt/benchviewer/src/tests/test_backend.py b/mqt/benchviewer/src/tests/test_backend.py index 4d3286e0f..b4c1388d8 100644 --- a/mqt/benchviewer/src/tests/test_backend.py +++ b/mqt/benchviewer/src/tests/test_backend.py @@ -179,7 +179,7 @@ def test_prepareFormInput(): ("mapped_tket_compiler_line", "true"), ("device_ibm_washington", "true"), ("device_ibm_montreal", "true"), - ("device_rigetti_aspen_m1", "true"), + ("device_rigetti_aspen_m2", "true"), ("device_oqc_lucy", "true"), ("device_ionq_ionq11", "true"), ] @@ -225,7 +225,7 @@ def test_prepareFormInput(): [ "ibm_washington", "ibm_montreal", - "rigetti_aspen_m1", + "rigetti_aspen", "oqc_lucy", "ionq11", ], @@ -278,7 +278,7 @@ def test_create_database(): ["2"], (False, False), ((False, False), [], ["rigetti", "ionq"]), - ((True, True), ([1, 3], ["graph"]), ["ibm_washington", "rigetti_aspen_m1"]), + ((True, True), ([1, 3], ["graph"]), ["ibm_washington", "rigetti_aspen_m2"]), ) res = backend.get_selected_file_paths(input_data) assert len(res) > 20 @@ -291,7 +291,7 @@ def test_create_database(): ( (True, True), ([1, 3], ["graph", "line"]), - ["ibm_montreal", "rigetti_aspen_m1", "ionq11", "ocq_lucy"], + ["ibm_montreal", "rigetti_aspen", "ionq11", "ocq_lucy"], ), ) res = backend.get_selected_file_paths(input_data) diff --git a/mqt/benchviewer/static/files/qasm_output/.gitignore b/mqt/benchviewer/static/files/qasm_output/.gitignore new file mode 100644 index 000000000..5e7d2734c --- /dev/null +++ b/mqt/benchviewer/static/files/qasm_output/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/mqt/benchviewer/templates/index.html b/mqt/benchviewer/templates/index.html index 3af4f662a..c7892ec60 100644 --- a/mqt/benchviewer/templates/index.html +++ b/mqt/benchviewer/templates/index.html @@ -288,7 +288,7 @@
Target-dependent: Native-Gates Level
id="nativegates_rigetti" name="nativegates_rigetti" /> - + Target-dependent: Mapped Level class="form-check-input" type="checkbox" value="true" - id="device_rigetti_aspen_m1" + id="device_rigetti_aspen_m2" value="true" - name="device_rigetti_aspen_m1" + name="device_rigetti_aspen_m2" /> -