From 394c5244a56ca9853bcd87004b24c5f06baf5e48 Mon Sep 17 00:00:00 2001 From: Nils Quetschlich Date: Wed, 27 Nov 2024 09:19:26 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A5=20removed=20all=20benchmarks=20rel?= =?UTF-8?q?ying=20on=20qiskit=5Foptimization,=20qiskit=5Fnature,=20and=20q?= =?UTF-8?q?iskit=5Falgorithm=20dependencies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 3 - pyproject.toml | 3 - src/mqt/bench/benchmark_generator.py | 18 +- src/mqt/bench/benchmarks/__init__.py | 15 -- src/mqt/bench/benchmarks/qaoa.py | 57 +++--- .../portfolioqaoa.py | 55 ------ .../portfoliovqe.py | 59 ------- .../qiskit_application_finance/pricingcall.py | 67 ------- .../qiskit_application_finance/pricingput.py | 84 --------- .../qiskit_application_nature/groundstate.py | 68 ------- .../routing.py | 167 ------------------ .../qiskit_application_optimization/tsp.py | 44 ----- src/mqt/bench/benchmarks/vqe.py | 39 ---- src/mqt/bench/cli.py | 2 +- src/mqt/bench/config.json | 58 ------ src/mqt/bench/utils.py | 32 ---- tests/test_benchmark_generation.py | 35 ---- tests/test_get_benchmark.py | 12 +- 18 files changed, 41 insertions(+), 777 deletions(-) delete mode 100644 src/mqt/bench/benchmarks/qiskit_application_finance/portfolioqaoa.py delete mode 100644 src/mqt/bench/benchmarks/qiskit_application_finance/portfoliovqe.py delete mode 100644 src/mqt/bench/benchmarks/qiskit_application_finance/pricingcall.py delete mode 100644 src/mqt/bench/benchmarks/qiskit_application_finance/pricingput.py delete mode 100644 src/mqt/bench/benchmarks/qiskit_application_nature/groundstate.py delete mode 100644 src/mqt/bench/benchmarks/qiskit_application_optimization/routing.py delete mode 100644 src/mqt/bench/benchmarks/qiskit_application_optimization/tsp.py delete mode 100644 src/mqt/bench/benchmarks/vqe.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 261f39eb9..91a3a3884 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -86,9 +86,6 @@ repos: args: [] additional_dependencies: - pytket_qiskit - - qiskit_optimization - - qiskit_nature - - qiskit_finance - importlib_resources - pytest - types-setuptools diff --git a/pyproject.toml b/pyproject.toml index c47ce6916..bcd3bbe81 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,9 +26,6 @@ dependencies = [ # there is a bug in 1.2.0 that causes an error some benchmarks, see https://github.com/Qiskit/qiskit/issues/12969 "qiskit!=1.2.0", "qiskit_qasm3_import>=0.5.0", - "qiskit_optimization>=0.6", - "qiskit_nature[pyscf]>=0.7", - "qiskit_finance>=0.4.1", "networkx>=2.8.8", "joblib>=1.3.0", "numpy>=1.26; python_version >= '3.12'", diff --git a/src/mqt/bench/benchmark_generator.py b/src/mqt/bench/benchmark_generator.py index ed015cdee..80245ec7a 100644 --- a/src/mqt/bench/benchmark_generator.py +++ b/src/mqt/bench/benchmark_generator.py @@ -128,15 +128,6 @@ def define_benchmark_instances(self, benchmark: Benchmark) -> None: elif benchmark["name"] == "shor": instances = [lib.get_instance(choice) for choice in benchmark["instances"]] - elif benchmark["name"] in ("routing", "tsp"): - instances = range(benchmark["min_nodes"], benchmark["max_nodes"]) - - elif benchmark["name"] == "groundstate": - instances = benchmark["instances"] - - elif benchmark["name"] in ("pricingcall", "pricingput"): - instances = range(benchmark["min_uncertainty"], benchmark["max_uncertainty"]) - else: instances = range( benchmark["min_qubits"], @@ -344,7 +335,7 @@ def get_benchmark( 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" + benchmark_instance_name: Input selection for some benchmarks, namely and "shor" compiler: "qiskit" or "tket" compiler_settings: Data class containing the respective compiler settings for the specified compiler (e.g., optimization level for Qiskit) gateset: Name of the gateset or tuple containing the name of the gateset and the gateset itself (required for "nativegates" level) @@ -362,11 +353,11 @@ def get_benchmark( msg = f"Selected level must be in {get_supported_levels()}." raise ValueError(msg) - if benchmark_name not in ["shor", "groundstate"] and not (isinstance(circuit_size, int) and circuit_size > 0): + if benchmark_name != "shor" and not (isinstance(circuit_size, int) and circuit_size > 0): msg = "circuit_size must be None or int for this benchmark." raise ValueError(msg) - if benchmark_name in ["shor", "groundstate"] and not isinstance(benchmark_instance_name, str): + if benchmark_name == "shor" and not isinstance(benchmark_instance_name, str): msg = "benchmark_instance_name must be defined for this benchmark." raise ValueError(msg) @@ -386,9 +377,6 @@ def get_benchmark( to_be_factored_number, a_value = lib.get_instance(benchmark_instance_name) qc = lib.create_circuit(to_be_factored_number, a_value) - elif benchmark_name == "groundstate": - qc = lib.create_circuit(benchmark_instance_name) - else: qc = lib.create_circuit(circuit_size) diff --git a/src/mqt/bench/benchmarks/__init__.py b/src/mqt/bench/benchmarks/__init__.py index d77918927..f85bcdd3b 100644 --- a/src/mqt/bench/benchmarks/__init__.py +++ b/src/mqt/bench/benchmarks/__init__.py @@ -6,23 +6,8 @@ from __future__ import annotations -from mqt.bench.benchmarks.qiskit_application_finance import ( - portfolioqaoa, - portfoliovqe, - pricingcall, - pricingput, -) from mqt.bench.benchmarks.qiskit_application_ml import qnn -from mqt.bench.benchmarks.qiskit_application_nature import groundstate -from mqt.bench.benchmarks.qiskit_application_optimization import routing, tsp __all__ = [ - "groundstate", - "portfolioqaoa", - "portfoliovqe", - "pricingcall", - "pricingput", "qnn", - "routing", - "tsp", ] diff --git a/src/mqt/bench/benchmarks/qaoa.py b/src/mqt/bench/benchmarks/qaoa.py index addeb67fc..aac0221ec 100644 --- a/src/mqt/bench/benchmarks/qaoa.py +++ b/src/mqt/bench/benchmarks/qaoa.py @@ -2,34 +2,49 @@ from __future__ import annotations -from typing import TYPE_CHECKING +import numpy as np +from qiskit import QuantumCircuit -from qiskit.primitives import Sampler -from qiskit_algorithms.minimum_eigensolvers import QAOA -from qiskit_algorithms.optimizers import SLSQP -from qiskit_optimization import QuadraticProgram -from mqt.bench.utils import get_examplary_max_cut_qp - -if TYPE_CHECKING: # pragma: no cover - from qiskit import QuantumCircuit - - -def create_circuit(num_qubits: int) -> QuantumCircuit: - """Returns a quantum circuit implementing the Quantum Approximation Optimization Algorithm for a specific max-cut example. +def create_circuit(num_qubits: int, repetitions: int = 2, seed: int = 42) -> QuantumCircuit: + """Constructs a quantum circuit implementing QAOA for a Max-Cut example with random parameters. Arguments: - num_qubits: number of qubits of the returned quantum circuit + num_qubits: Number of qubits in the circuit (equal to the number of graph nodes). + repetitions: Number of QAOA layers (repetitions of the ansatz). + seed: Random seed for reproducibility. Returns: - QuantumCircuit: quantum circuit implementing the Quantum Approximation Optimization Algorithm + QuantumCircuit: Quantum circuit implementing QAOA. """ - qp = get_examplary_max_cut_qp(num_qubits) - assert isinstance(qp, QuadraticProgram) - - qaoa = QAOA(sampler=Sampler(), reps=2, optimizer=SLSQP(maxiter=25)) - qaoa_result = qaoa.compute_minimum_eigenvalue(qp.to_ising()[0]) - qc = qaoa.ansatz.assign_parameters(qaoa_result.optimal_point) + # Set the random number generator + rng = np.random.default_rng(seed) + + # Example adjacency matrix for Max-Cut (toy problem) + adjacency_matrix = rng.integers(0, 2, size=(num_qubits, num_qubits)) + adjacency_matrix = np.triu(adjacency_matrix, 1) # Upper triangular part for undirected graph + + # Random initialization of parameters + gamma_values = rng.uniform(0, np.pi, repetitions) + beta_values = rng.uniform(0, np.pi, repetitions) + + # Initialize QAOA circuit + qc = QuantumCircuit(num_qubits) + + # Start in uniform superposition + qc.h(range(num_qubits)) + + # Define cost and mixer operators for each layer + for layer in range(repetitions): + # Cost Hamiltonian + for i in range(num_qubits): + for j in range(i + 1, num_qubits): + if adjacency_matrix[i, j] != 0: + qc.rzz(2 * gamma_values[layer], i, j) + + # Mixer Hamiltonian + for i in range(num_qubits): + qc.rx(2 * beta_values[layer], i) qc.name = "qaoa" diff --git a/src/mqt/bench/benchmarks/qiskit_application_finance/portfolioqaoa.py b/src/mqt/bench/benchmarks/qiskit_application_finance/portfolioqaoa.py deleted file mode 100644 index 9f49099f4..000000000 --- a/src/mqt/bench/benchmarks/qiskit_application_finance/portfolioqaoa.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Portfolio QAOA benchmark definition. Code is based on https://qiskit.org/documentation/tutorials/finance/01_portfolio_optimization.html.""" - -from __future__ import annotations - -import datetime -from typing import TYPE_CHECKING - -from qiskit.primitives import Sampler -from qiskit_algorithms.minimum_eigensolvers import QAOA -from qiskit_algorithms.optimizers import COBYLA -from qiskit_finance.applications import PortfolioOptimization -from qiskit_finance.data_providers import RandomDataProvider -from qiskit_optimization.converters import QuadraticProgramToQubo - -if TYPE_CHECKING: # pragma: no cover - from qiskit import QuantumCircuit - - -def create_circuit(num_qubits: int) -> QuantumCircuit: - """Returns a quantum circuit of QAOA applied to a specific portfolio optimization task. - - Arguments: - num_qubits: number of qubits of the returned quantum circuit - """ - # set number of assets (= number of qubits) - num_assets = num_qubits - - # Generate expected return and covariance matrix from (random) time-series - stocks = [(f"TICKER{i}") for i in range(num_assets)] - data = RandomDataProvider( - tickers=stocks, - start=datetime.datetime(2016, 1, 1), - end=datetime.datetime(2016, 1, 30), - ) - data.run() - mu = data.get_period_return_mean_vector() - sigma = data.get_period_return_covariance_matrix() - - q = 0.5 # set risk factor - budget = num_assets // 2 # set budget - - portfolio = PortfolioOptimization(expected_returns=mu, covariances=sigma, risk_factor=q, budget=budget) - qp = portfolio.to_quadratic_program() - conv = QuadraticProgramToQubo() - qp_qubo = conv.convert(qp) - - cobyla = COBYLA() - cobyla.set_options(maxiter=25) - 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.assign_parameters(qaoa_result.optimal_point) - - qc.name = "portfolioqaoa" - return qc diff --git a/src/mqt/bench/benchmarks/qiskit_application_finance/portfoliovqe.py b/src/mqt/bench/benchmarks/qiskit_application_finance/portfoliovqe.py deleted file mode 100644 index 560eb4e2f..000000000 --- a/src/mqt/bench/benchmarks/qiskit_application_finance/portfoliovqe.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Portfolio VQE benchmark definition. Code is based on https://qiskit.org/documentation/tutorials/finance/01_portfolio_optimization.html.""" - -from __future__ import annotations - -import datetime -from typing import TYPE_CHECKING - -from qiskit.circuit.library import TwoLocal -from qiskit.primitives import Estimator -from qiskit_algorithms.minimum_eigensolvers import VQE -from qiskit_algorithms.optimizers import COBYLA -from qiskit_finance.applications import PortfolioOptimization -from qiskit_finance.data_providers import RandomDataProvider -from qiskit_optimization.converters import QuadraticProgramToQubo - -if TYPE_CHECKING: # pragma: no cover - from qiskit import QuantumCircuit - - -def create_circuit(num_qubits: int) -> QuantumCircuit: - """Returns a quantum circuit of VQE applied to a specific portfolio optimization task. - - Arguments: - num_qubits: number of qubits of the returned quantum circuit - """ - # set number of assets (= number of qubits) - num_assets = num_qubits - - # Generate expected return and covariance matrix from (random) time-series - stocks = [(f"TICKER{i}") for i in range(num_assets)] - data = RandomDataProvider( - tickers=stocks, - start=datetime.datetime(2016, 1, 1), - end=datetime.datetime(2016, 1, 30), - ) - data.run() - mu = data.get_period_return_mean_vector() - sigma = data.get_period_return_covariance_matrix() - - q = 0.5 # set risk factor - budget = num_assets // 2 # set budget - - portfolio = PortfolioOptimization(expected_returns=mu, covariances=sigma, risk_factor=q, budget=budget) - qp = portfolio.to_quadratic_program() - conv = QuadraticProgramToQubo() - qp_qubo = conv.convert(qp) - cobyla = COBYLA() - cobyla.set_options(maxiter=25) - ry = TwoLocal(num_assets, "ry", "cz", reps=3, entanglement="full") - - 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.assign_parameters(vqe_result.optimal_point) - - qc.name = "portfoliovqe" - qc.measure_all() - - return qc diff --git a/src/mqt/bench/benchmarks/qiskit_application_finance/pricingcall.py b/src/mqt/bench/benchmarks/qiskit_application_finance/pricingcall.py deleted file mode 100644 index d0898daa1..000000000 --- a/src/mqt/bench/benchmarks/qiskit_application_finance/pricingcall.py +++ /dev/null @@ -1,67 +0,0 @@ -"""Pricing call benchmark definition. Code is based on https://qiskit.org/documentation/tutorials/finance/03_european_call_option_pricing.html.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -import numpy as np -from qiskit_algorithms import IterativeAmplitudeEstimation -from qiskit_finance.applications.estimation import EuropeanCallPricing -from qiskit_finance.circuit.library import LogNormalDistribution - -if TYPE_CHECKING: # pragma: no cover - from qiskit import QuantumCircuit - - -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. - - Arguments: - num_uncertainty_qubits: number of qubits to measure uncertainty - """ - # parameters for considered random distribution - s = 2.0 # initial spot price - vol = 0.4 # volatility of 40% - r = 0.05 # annual interest rate of 4% - t = 40 / 365 # 40 days to maturity - - mu = (r - 0.5 * vol**2) * t + np.log(s) - sigma = vol * np.sqrt(t) - mean = np.exp(mu + sigma**2 / 2) - variance = (np.exp(sigma**2) - 1) * np.exp(2 * mu + sigma**2) - stddev = np.sqrt(variance) - - # lowest and highest value considered for the spot price; in between, an equidistant discretization is considered. - low = np.maximum(0, mean - 3 * stddev) - high = mean + 3 * stddev - - # construct A operator for QAE for the payoff function by - # composing the uncertainty model and the objective - uncertainty_model = LogNormalDistribution(num_uncertainty_qubits, mu=mu, sigma=sigma**2, bounds=(low, high)) - - # set the strike price (should be within the low and the high value of the uncertainty) - strike_price = 1.896 - - # set the approximation scaling for the payoff function - c_approx = 0.25 - - european_call_pricing = EuropeanCallPricing( - num_state_qubits=num_uncertainty_qubits, - strike_price=strike_price, - rescaling_factor=c_approx, - bounds=(low, high), - uncertainty_model=uncertainty_model, - ) - - # set target precision and confidence level - epsilon = 0.01 - alpha = 0.05 - - problem = european_call_pricing.to_estimation_problem() - iae = IterativeAmplitudeEstimation(epsilon, alpha=alpha) - - qc = iae.construct_circuit(problem) - qc.measure_all() - qc.name = "pricingcall" - - return qc diff --git a/src/mqt/bench/benchmarks/qiskit_application_finance/pricingput.py b/src/mqt/bench/benchmarks/qiskit_application_finance/pricingput.py deleted file mode 100644 index 16700f7cd..000000000 --- a/src/mqt/bench/benchmarks/qiskit_application_finance/pricingput.py +++ /dev/null @@ -1,84 +0,0 @@ -"""Pricing put benchmark definition. Code is based on https://qiskit.org/documentation/tutorials/finance/04_european_put_option_pricing.html.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -import numpy as np -from qiskit.circuit.library import LinearAmplitudeFunction -from qiskit_algorithms import EstimationProblem, IterativeAmplitudeEstimation -from qiskit_finance.circuit.library import LogNormalDistribution - -if TYPE_CHECKING: # pragma: no cover - from qiskit import QuantumCircuit - - -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. - - Arguments: - num_uncertainty_qubits: number of qubits to measure uncertainty - """ - # parameters for considered random distribution - s = 2.0 # initial spot price - vol = 0.4 # volatility of 40% - r = 0.05 # annual interest rate of 4% - t = 40 / 365 # 40 days to maturity - - mu = (r - 0.5 * vol**2) * t + np.log(s) - sigma = vol * np.sqrt(t) - mean = np.exp(mu + sigma**2 / 2) - variance = (np.exp(sigma**2) - 1) * np.exp(2 * mu + sigma**2) - stddev = np.sqrt(variance) - - # lowest and highest value considered for the spot price; in between, an equidistant discretization is considered. - low = np.maximum(0, mean - 3 * stddev) - high = mean + 3 * stddev - - # construct A operator for QAE for the payoff function by - # composing the uncertainty model and the objective - uncertainty_model = LogNormalDistribution(num_uncertainty_qubits, mu=mu, sigma=sigma**2, bounds=(low, high)) - - # set the strike price (should be within the low and the high value of the uncertainty) - strike_price = 2.126 - - # set the approximation scaling for the payoff function - rescaling_factor = 0.25 - - # setup piecewise linear objective fcuntion - breakpoints = [low, strike_price] - slopes = [-1, 0] - offsets = [strike_price - low, 0] - f_min = 0 - f_max = strike_price - low - european_put_objective = LinearAmplitudeFunction( - num_uncertainty_qubits, - slopes, - offsets, - domain=(low, high), - image=(f_min, f_max), - breakpoints=breakpoints, - rescaling_factor=rescaling_factor, - ) - - # construct A operator for QAE for the payoff function by - # composing the uncertainty model and the objective - european_put = european_put_objective.compose(uncertainty_model, front=True) - - # set target precision and confidence level - epsilon = 0.01 - alpha = 0.05 - - problem = EstimationProblem( - state_preparation=european_put, - objective_qubits=[num_uncertainty_qubits], - post_processing=european_put_objective.post_processing, - ) - - iae = IterativeAmplitudeEstimation(epsilon, alpha=alpha) - qc = iae.construct_circuit(problem) - - qc.measure_all() - qc.name = "pricingput" - - return qc diff --git a/src/mqt/bench/benchmarks/qiskit_application_nature/groundstate.py b/src/mqt/bench/benchmarks/qiskit_application_nature/groundstate.py deleted file mode 100644 index 10719d51f..000000000 --- a/src/mqt/bench/benchmarks/qiskit_application_nature/groundstate.py +++ /dev/null @@ -1,68 +0,0 @@ -"""Groundstate benchmark definition. Code is based on https://qiskit.org/documentation/nature/tutorials/03_ground_state_solvers.html.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from qiskit.circuit.library import TwoLocal -from qiskit.exceptions import MissingOptionalLibraryError -from qiskit.primitives import Estimator -from qiskit_algorithms.minimum_eigensolvers import VQE -from qiskit_algorithms.optimizers import COBYLA -from qiskit_nature.second_q.drivers import PySCFDriver -from qiskit_nature.second_q.mappers import JordanWignerMapper - -if TYPE_CHECKING: # pragma: no cover - from qiskit import QuantumCircuit - - -def create_circuit(choice: str) -> QuantumCircuit: - """Returns a quantum circuit implementing Ground State Estimation. - - Arguments: - choice: problem instance for which the ground state shall be estimated - """ - molecule = get_molecule(choice) - - try: - driver = PySCFDriver(atom=molecule) - except MissingOptionalLibraryError: - msg = ( - "PySCF is not installed (most likely because you are on a Windows system)." - "Please either download benchmark from https://www.cda.cit.tum.de/mqtbench/ or try to manually install PySCF." - ) - raise ImportError(msg) from None - - es_problem = driver.run() - mapper = JordanWignerMapper() - second_q_op = es_problem.second_q_ops() - operator = mapper.map(second_q_op[0]) - - tl_circuit = TwoLocal( - rotation_blocks=["h", "rx"], - entanglement_blocks="cz", - entanglement="full", - reps=2, - parameter_prefix="y", - ) - - another_solver = VQE(ansatz=tl_circuit, estimator=Estimator(), optimizer=COBYLA(maxiter=25)) - - result = another_solver.compute_minimum_eigenvalue(operator) - qc = another_solver.ansatz.assign_parameters(result.optimal_point) - - qc.name = "groundstate" - qc.name = qc.name + "_" + choice - qc.measure_all() - - return qc - - -def get_molecule(benchmark_instance_name: str) -> list[str]: - """Returns a Molecule object depending on the parameter value.""" - 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] diff --git a/src/mqt/bench/benchmarks/qiskit_application_optimization/routing.py b/src/mqt/bench/benchmarks/qiskit_application_optimization/routing.py deleted file mode 100644 index 230b4e0e5..000000000 --- a/src/mqt/bench/benchmarks/qiskit_application_optimization/routing.py +++ /dev/null @@ -1,167 +0,0 @@ -"""Routing benchmark definition. Code is based on https://qiskit.org/documentation/tutorials/optimization/7_examples_vehicle_routing.html.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, cast - -import numpy as np -from qiskit.circuit.library import RealAmplitudes -from qiskit.primitives import Estimator -from qiskit_algorithms.minimum_eigensolvers import VQE -from qiskit_algorithms.optimizers import SLSQP -from qiskit_optimization import QuadraticProgram -from qiskit_optimization.problems import LinearExpression, QuadraticExpression - -if TYPE_CHECKING: # pragma: no cover - from numpy.typing import NDArray - from qiskit import QuantumCircuit - - -class Initializer: - """Initializes the problem by randomly generating the instance.""" - - def __init__(self, n: int) -> None: - """Initializes the problem by randomly generating the instance.""" - self.n = n - - def generate_instance( - self, - ) -> tuple[ - NDArray[np.float64], - NDArray[np.float64], - NDArray[np.float64], - ]: - """Generates a random instance of the problem.""" - n = self.n - rng = np.random.default_rng(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): - for jj in range(ii + 1, n): - instance[ii, jj] = (xc[ii] - xc[jj]) ** 2 + (yc[ii] - yc[jj]) ** 2 - instance[jj, ii] = instance[ii, jj] - - return xc, yc, instance - - -class QuantumOptimizer: - """Class to solve the problem using a quantum optimizer.""" - - def __init__(self, instance: NDArray[np.float64], n: int, k: int) -> None: - """Initializes the class to solve the problem using a quantum optimizer.""" - self.instance = instance - self.n = n - self.k = k - - def binary_representation( - self, x_sol: NDArray[np.float64] - ) -> tuple[NDArray[np.float64], NDArray[np.float64], float, float]: - """Returns the binary representation of the problem.""" - instance = self.instance - n = self.n - k = self.k - - a = np.max(instance) * 100 # A parameter of cost function - - # Determine the weights w - instance_vec = instance.reshape(n**2) - w_list = [instance_vec[x] for x in range(n**2) if instance_vec[x] > 0] - w = np.zeros(n * (n - 1)) - for ii in range(len(w_list)): - 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 - - v = np.zeros([n, n * (n - 1)]) - for ii in range(n): - count = ii - 1 - for jj in range(n * (n - 1)): - if jj // (n - 1) == ii: - count = ii - - if jj // (n - 1) != ii and jj % (n - 1) == count: - v[ii][jj] = 1.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)) - - # 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) - - # c is the constant offset - 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.float64]) -> float: - return cast( - float, - 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 - - def construct_problem(self, q: QuadraticExpression, g: LinearExpression, c: float) -> QuadraticProgram: - """Constructs the problem.""" - qp = QuadraticProgram() - for i in range(self.n * (self.n - 1)): - qp.binary_var(str(i)) - - qp.objective.quadratic = q - qp.objective.linear = g - qp.objective.constant = c - return qp - - def solve_problem(self, qp: QuadraticProgram) -> QuantumCircuit: - """Solves the problem.""" - ansatz = RealAmplitudes(self.n) - vqe = VQE(estimator=Estimator(), optimizer=SLSQP(maxiter=25), ansatz=ansatz) - vqe.random_seed = 10 - vqe_result = vqe.compute_minimum_eigenvalue(qp.to_ising()[0]) - return vqe.ansatz.assign_parameters(vqe_result.optimal_point) - - -def create_circuit(num_nodes: int = 3, num_vehs: int = 2) -> QuantumCircuit: - """Returns a quantum circuit solving a routing problem. - - Arguments: - num_nodes: number of to be visited nodes - num_vehs: number of used vehicles - - Returns: - QuantumCircuit: quantum circuit solving the routing problem - """ - # 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() - - quantum_optimizer = QuantumOptimizer(instance, n, k) - 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) - # Instantiate the quantum optimizer class with parameters: - qc = quantum_optimizer.solve_problem(qp) - - qc.measure_all() - qc.name = "routing" - - return qc diff --git a/src/mqt/bench/benchmarks/qiskit_application_optimization/tsp.py b/src/mqt/bench/benchmarks/qiskit_application_optimization/tsp.py deleted file mode 100644 index d77d145f1..000000000 --- a/src/mqt/bench/benchmarks/qiskit_application_optimization/tsp.py +++ /dev/null @@ -1,44 +0,0 @@ -"""TSP benchmark definition. Code is based on https://qiskit.org/documentation/optimization/tutorials/06_examples_max_cut_and_tsp.html.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from qiskit.circuit.library import TwoLocal -from qiskit.primitives import Estimator -from qiskit_algorithms.minimum_eigensolvers import VQE -from qiskit_algorithms.optimizers import SPSA -from qiskit_optimization.applications import Tsp -from qiskit_optimization.converters import QuadraticProgramToQubo - -if TYPE_CHECKING: # pragma: no cover - from qiskit import QuantumCircuit - - -def create_circuit(num_nodes: int) -> QuantumCircuit: - """Returns a quantum circuit solving the Travelling Salesman Problem (TSP). - - 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) - - qp = tsp.to_quadratic_program() - - qp2qubo = QuadraticProgramToQubo() - qubo = qp2qubo.convert(qp) - qubit_op, _offset = qubo.to_ising() - - spsa = SPSA(maxiter=25) - ry = TwoLocal(qubit_op.num_qubits, "ry", "cz", reps=5, entanglement="linear") - vqe = VQE(ansatz=ry, optimizer=spsa, estimator=Estimator()) - vqe.random_seed = 10 - - vqe_result = vqe.compute_minimum_eigenvalue(qubit_op) - qc = vqe.ansatz.assign_parameters(vqe_result.optimal_point) - qc.measure_all() - qc.name = "tsp" - - return qc diff --git a/src/mqt/bench/benchmarks/vqe.py b/src/mqt/bench/benchmarks/vqe.py deleted file mode 100644 index 69da9d6ef..000000000 --- a/src/mqt/bench/benchmarks/vqe.py +++ /dev/null @@ -1,39 +0,0 @@ -"""VQE benchmark definition. Code is based on https://github.com/qiskit-community/qiskit-application-modules-demo-sessions/blob/main/qiskit-optimization/qiskit-optimization-demo.ipynb.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from qiskit.circuit.library import RealAmplitudes -from qiskit.primitives import Estimator -from qiskit_algorithms.minimum_eigensolvers import VQE -from qiskit_algorithms.optimizers import SLSQP -from qiskit_optimization import QuadraticProgram - -from mqt.bench.utils import get_examplary_max_cut_qp - -if TYPE_CHECKING: # pragma: no cover - from qiskit import QuantumCircuit - - -def create_circuit(num_qubits: int) -> QuantumCircuit: - """Returns a quantum circuit implementing the Variational Quantum Eigensolver Algorithm for a specific max-cut example. - - Arguments: - num_qubits: number of qubits of the returned quantum circuit - - Returns: - QuantumCircuit: a quantum circuit implementing the Variational Quantum Eigensolver Algorithm for a specific - """ - qp = get_examplary_max_cut_qp(num_qubits) - assert isinstance(qp, QuadraticProgram) - - ansatz = RealAmplitudes(num_qubits, reps=2) - vqe = VQE(ansatz=ansatz, optimizer=SLSQP(maxiter=25), estimator=Estimator()) - vqe_result = vqe.compute_minimum_eigenvalue(qp.to_ising()[0]) - qc = vqe.ansatz.assign_parameters(vqe_result.optimal_point) - - qc.measure_all() - qc.name = "vqe" - - return qc diff --git a/src/mqt/bench/cli.py b/src/mqt/bench/cli.py index 602c5b1bb..13d85eab7 100644 --- a/src/mqt/bench/cli.py +++ b/src/mqt/bench/cli.py @@ -80,7 +80,7 @@ def main() -> None: def parse_benchmark_name_and_instance(algorithm: str) -> tuple[str, str | None]: """Parse an algorithm name like "shor_xlarge" into a benchmark and instance name as expected by :func:`get_benchmark`.""" - if algorithm.startswith(("shor_", "groundstate_")): + if algorithm.startswith("shor_"): as_list = algorithm.split("_", 2) assert len(as_list) == 2 return cast(tuple[str, str], tuple(as_list)) diff --git a/src/mqt/bench/config.json b/src/mqt/bench/config.json index ca6eeacc5..975c0b09c 100644 --- a/src/mqt/bench/config.json +++ b/src/mqt/bench/config.json @@ -41,12 +41,6 @@ "stepsize": 1, "precheck_possible": true }, - { - "name": "groundstate", - "include": true, - "instances": ["small", "medium", "large"], - "precheck_possible": false - }, { "name": "grover", "include": true, @@ -56,36 +50,6 @@ "ancillary_mode": ["noancilla", "v-chain"], "precheck_possible": false }, - { - "name": "portfolioqaoa", - "include": true, - "min_qubits": 3, - "max_qubits": 14, - "stepsize": 1, - "precheck_possible": true - }, - { - "name": "portfoliovqe", - "include": true, - "min_qubits": 3, - "max_qubits": 14, - "stepsize": 1, - "precheck_possible": true - }, - { - "name": "pricingcall", - "include": true, - "min_uncertainty": 2, - "max_uncertainty": 131, - "precheck_possible": false - }, - { - "name": "pricingput", - "include": true, - "min_uncertainty": 2, - "max_uncertainty": 131, - "precheck_possible": false - }, { "name": "qaoa", "include": true, @@ -159,13 +123,6 @@ "stepsize": 1, "precheck_possible": true }, - { - "name": "routing", - "include": true, - "min_nodes": 2, - "max_nodes": 131, - "precheck_possible": false - }, { "name": "shor", "include": true, @@ -180,13 +137,6 @@ "stepsize": 1, "precheck_possible": true }, - { - "name": "tsp", - "include": true, - "min_nodes": 2, - "max_nodes": 131, - "precheck_possible": false - }, { "name": "twolocalrandom", "include": true, @@ -195,14 +145,6 @@ "stepsize": 1, "precheck_possible": true }, - { - "name": "vqe", - "include": true, - "min_qubits": 3, - "max_qubits": 131, - "stepsize": 1, - "precheck_possible": true - }, { "name": "wstate", "include": true, diff --git a/src/mqt/bench/utils.py b/src/mqt/bench/utils.py index 4b5ed1cd1..1bb9ec522 100644 --- a/src/mqt/bench/utils.py +++ b/src/mqt/bench/utils.py @@ -23,8 +23,6 @@ if TYPE_CHECKING: # pragma: no cover from types import ModuleType - from qiskit_optimization import QuadraticProgram - @dataclass class SupermarqFeatures: @@ -47,8 +45,6 @@ def get_supported_benchmarks() -> list[str]: "grover-v-chain", "ghz", "graphstate", - "portfolioqaoa", - "portfoliovqe", "qaoa", "qft", "qftentangled", @@ -61,14 +57,8 @@ def get_supported_benchmarks() -> list[str]: "realamprandom", "su2random", "twolocalrandom", - "vqe", "wstate", "shor", - "pricingcall", - "pricingput", - "groundstate", - "routing", - "tsp", ] @@ -92,20 +82,6 @@ def get_default_qasm_output_path() -> str: return str(resources.files("mqt.bench") / "qasm_output") -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. - - Arguments: - n_nodes: number of graph nodes (and also number of qubits) - degree: edges per node - """ - from qiskit_optimization.applications import Maxcut # noqa: PLC0415 lazy import to reduce import cost - - graph = nx.random_regular_graph(d=degree, n=n_nodes, seed=111) - maxcut = Maxcut(graph) - return maxcut.to_quadratic_program() - - def get_openqasm_gates() -> list[str]: """Returns a list of all quantum gates within the openQASM 2.0 standard header.""" # according to https://github.com/Qiskit/qiskit-terra/blob/main/qiskit/qasm/libs/qelib1.inc @@ -278,16 +254,8 @@ def calc_supermarq_features( def get_module_for_benchmark(benchmark_name: str) -> ModuleType: """Returns the module for a specific benchmark.""" - if benchmark_name in ["portfolioqaoa", "portfoliovqe", "pricingcall", "pricingput"]: - return import_module("mqt.bench.benchmarks.qiskit_application_finance." + benchmark_name) if benchmark_name == "qnn": return import_module("mqt.bench.benchmarks.qiskit_application_ml.qnn") - if benchmark_name == "groundstate": - return import_module("mqt.bench.benchmarks.qiskit_application_nature.groundstate") - if benchmark_name == "routing": - return import_module("mqt.bench.benchmarks.qiskit_application_optimization.routing") - if benchmark_name == "tsp": - return import_module("mqt.bench.benchmarks.qiskit_application_optimization.tsp") return import_module("mqt.bench.benchmarks." + benchmark_name) diff --git a/tests/test_benchmark_generation.py b/tests/test_benchmark_generation.py index 542b9b803..0c4312f42 100644 --- a/tests/test_benchmark_generation.py +++ b/tests/test_benchmark_generation.py @@ -31,7 +31,6 @@ dj, ghz, graphstate, - groundstate, grover, qaoa, qft, @@ -42,11 +41,9 @@ qwalk, randomcircuit, realamprandom, - routing, shor, su2random, twolocalrandom, - vqe, wstate, ) from mqt.bench.devices import ( @@ -85,7 +82,6 @@ def quantum_circuit() -> str: (qpeexact, 3, True), (qpeinexact, 3, True), (qwalk, 3, False), - (vqe, 3, True), (randomcircuit, 3, True), (realamprandom, 3, True), (su2random, 3, True), @@ -274,12 +270,6 @@ def test_dj_constant_oracle() -> None: assert qc.depth() > 0 -def test_routing() -> None: - """Test the creation of the routing benchmark.""" - qc = routing.create_circuit(4, 2) - assert qc.depth() > 0 - - def test_configure_end(output_path: str) -> None: """Removes all temporarily created files while testing.""" # delete all files in the test directory and the directory itself @@ -337,31 +327,6 @@ def test_benchmark_helper_shor() -> None: assert res_shor -@pytest.mark.skipif( - sys.platform == "win32", - reason="PySCF is not available on Windows.", -) -def test_benchmark_groundstate_non_windows() -> None: - """Testing the Groundstate benchmarks.""" - groundstate_instances = ["small", "medium", "large"] - for elem in groundstate_instances: - res_groundstate = groundstate.get_molecule(elem) - assert res_groundstate - - qc = groundstate.create_circuit("small") - assert qc.depth() > 0 - - -@pytest.mark.skipif( - sys.platform != "win32", - reason="Windows-specific test.", -) -def test_benchmark_groundstate_windows() -> None: - """Testing the Groundstate benchmarks on Windows.""" - with pytest.raises(ImportError, match=r"PySCF is not installed"): - groundstate.create_circuit("small") - - def evaluate_benchmark_with_qasm_formats( fct: Callable, args: list[int | QuantumCircuit | str | bool], output_path: str ) -> None: diff --git a/tests/test_get_benchmark.py b/tests/test_get_benchmark.py index 48d135082..368c095fa 100644 --- a/tests/test_get_benchmark.py +++ b/tests/test_get_benchmark.py @@ -2,7 +2,6 @@ from __future__ import annotations -import sys from pathlib import Path import pytest @@ -105,15 +104,6 @@ def test_get_benchmark( assert gate_type in gateset.gates or gate_type == "barrier" -@pytest.mark.skipif( - sys.platform == "win32", - reason="PySCF is not available on Windows.", -) -def test_get_benchmark_groundstate() -> None: - """Test the Groundstate benchmark when not on Windows.""" - assert get_benchmark("groundstate", "alg", None, "small", "qiskit").depth() > 0 - - def test_get_benchmark_faulty_parameters() -> None: """Test the get_benchmark method with faulty parameters.""" match = "Selected benchmark is not supported. Valid benchmarks are" @@ -309,7 +299,7 @@ def test_calc_supermarq_features() -> None: assert dense_features.critical_depth == 0.0 assert dense_features.program_communication == 0.0 - regular_qc = get_benchmark("vqe", 1, 5) + regular_qc = get_benchmark("qaoa", 1, 5) regular_features = utils.calc_supermarq_features(regular_qc) assert 0 < regular_features.parallelism < 1 assert 0 < regular_features.entanglement_ratio < 1