diff --git a/.pylintdict b/.pylintdict index b667eec0..b4bd3457 100644 --- a/.pylintdict +++ b/.pylintdict @@ -151,6 +151,7 @@ panchenko param params parikh +passmanager pauli paulis peleato diff --git a/qiskit_optimization/algorithms/grover_optimizer.py b/qiskit_optimization/algorithms/grover_optimizer.py index 469ebce1..cf243b35 100644 --- a/qiskit_optimization/algorithms/grover_optimizer.py +++ b/qiskit_optimization/algorithms/grover_optimizer.py @@ -20,7 +20,8 @@ import numpy as np from qiskit import QuantumCircuit, QuantumRegister from qiskit.circuit.library import QuadraticForm -from qiskit.primitives import BaseSampler +from qiskit.passmanager import BasePassManager +from qiskit.primitives import BaseSamplerV1, BaseSamplerV2, SamplerResult from qiskit_algorithms import AmplificationProblem from qiskit_algorithms.amplitude_amplifiers.grover import Grover from qiskit_algorithms.utils import algorithm_globals @@ -49,7 +50,8 @@ def __init__( Union[QuadraticProgramConverter, List[QuadraticProgramConverter]] ] = None, penalty: Optional[float] = None, - sampler: Optional[BaseSampler] = None, + sampler: Optional[Union[BaseSamplerV1, BaseSamplerV2]] = None, + passmanager: Optional[BasePassManager] = None, ) -> None: """ Args: @@ -62,6 +64,7 @@ def __init__( penalty: The penalty factor used in the default :class:`~qiskit_optimization.converters.QuadraticProgramToQubo` converter sampler: A Sampler to use for sampling the results of the circuits. + passmanager: A pass manager to use to transpile the circuits Raises: ValueError: If both a quantum instance and sampler are set. @@ -73,6 +76,7 @@ def __init__( self._circuit_results = {} # type: dict self._converters = self._prepare_converters(converters, penalty) self._sampler = sampler + self._passmanager = passmanager def get_compatibility_msg(self, problem: QuadraticProgram) -> str: """Checks whether a given problem can be solved with this optimizer. @@ -294,6 +298,10 @@ def _measure(self, circuit: QuantumCircuit) -> str: def _get_prob_dist(self, qc: QuantumCircuit) -> Dict[str, float]: """Gets probabilities from a given backend.""" + # Transpile the circuit + if self._passmanager: + qc = self._passmanager.run(qc) + # Execute job and filter results. job = self._sampler.run([qc]) @@ -301,12 +309,17 @@ def _get_prob_dist(self, qc: QuantumCircuit) -> Dict[str, float]: result = job.result() except Exception as exc: raise QiskitOptimizationError("Sampler job failed.") from exc - quasi_dist = result.quasi_dists[0] - raw_prob_dist = { - k: v - for k, v in quasi_dist.binary_probabilities(qc.num_qubits).items() - if v >= self._MIN_PROBABILITY - } + + if isinstance(result, SamplerResult): + # SamplerV1 + prob_dist = result.quasi_dists[0].binary_probabilities(qc.num_qubits) + else: + # SamplerV2 + counts = getattr(result[0].data, qc.cregs[0].name).get_counts() + shots = sum(counts.values()) + prob_dist = {k: v / shots for k, v in counts.items()} + + raw_prob_dist = {k: v for k, v in prob_dist.items() if v >= self._MIN_PROBABILITY} prob_dist = {k[::-1]: v for k, v in raw_prob_dist.items()} self._circuit_results = {i: v**0.5 for i, v in raw_prob_dist.items()} return prob_dist diff --git a/qiskit_optimization/algorithms/qrao/encoding_commutation_verifier.py b/qiskit_optimization/algorithms/qrao/encoding_commutation_verifier.py index 56bea30f..cec869c4 100644 --- a/qiskit_optimization/algorithms/qrao/encoding_commutation_verifier.py +++ b/qiskit_optimization/algorithms/qrao/encoding_commutation_verifier.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2023. +# (C) Copyright IBM 2023, 2024. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -14,7 +14,8 @@ from __future__ import annotations -from qiskit.primitives import BaseEstimator +from qiskit.passmanager import BasePassManager +from qiskit.primitives import BaseEstimatorV1, BaseEstimatorV2 from qiskit_optimization.exceptions import QiskitOptimizationError @@ -24,14 +25,21 @@ class EncodingCommutationVerifier: """Class for verifying that the relaxation commutes with the objective function.""" - def __init__(self, encoding: QuantumRandomAccessEncoding, estimator: BaseEstimator): + def __init__( + self, + encoding: QuantumRandomAccessEncoding, + estimator: BaseEstimatorV1 | BaseEstimatorV2, + passmanager: BasePassManager | None = None, + ): """ Args: encoding: The encoding to verify. estimator: The estimator to use for the verification. + passmanager: The pass manager to transpile the circuits """ self._encoding = encoding self._estimator = estimator + self._passmanager = passmanager def __len__(self) -> int: return 2**self._encoding.num_vars @@ -48,6 +56,8 @@ def __getitem__(self, i: int) -> tuple[str, float, float]: str_dvars = f"{i:0{encoding.num_vars}b}" dvars = [int(b) for b in str_dvars] encoded_bitstr_qc = encoding.state_preparation_circuit(dvars) + if self._passmanager: + encoded_bitstr_qc = self._passmanager.run(encoded_bitstr_qc) # Evaluate the original objective function problem = encoding.problem @@ -58,13 +68,24 @@ def __getitem__(self, i: int) -> tuple[str, float, float]: encoded_op = encoding.qubit_op offset = encoding.offset - job = self._estimator.run([encoded_bitstr_qc], [encoded_op]) - - try: - encoded_obj_val = job.result().values[0] + offset - except Exception as exc: - raise QiskitOptimizationError( - "The primitive job to verify commutation failed!" - ) from exc - - return (str_dvars, obj_val, encoded_obj_val) + if isinstance(self._estimator, BaseEstimatorV1): + job = self._estimator.run([encoded_bitstr_qc], [encoded_op]) + + try: + encoded_obj_val = job.result().values[0] + offset + except Exception as exc: + raise QiskitOptimizationError( + "The primitive job to verify commutation failed!" + ) from exc + else: # BaseEstimatorV2 + job = self._estimator.run([(encoded_bitstr_qc, encoded_op)]) + + try: + result = job.result() + encoded_obj_val = result[0].data.evs.item() + offset + except Exception as exc: + raise QiskitOptimizationError( + "The primitive job to verify commutation failed!" + ) from exc + + return str_dvars, obj_val, encoded_obj_val diff --git a/qiskit_optimization/algorithms/qrao/magic_rounding.py b/qiskit_optimization/algorithms/qrao/magic_rounding.py index bc867a80..71b8c3a7 100644 --- a/qiskit_optimization/algorithms/qrao/magic_rounding.py +++ b/qiskit_optimization/algorithms/qrao/magic_rounding.py @@ -17,7 +17,8 @@ import numpy as np from qiskit import QuantumCircuit -from qiskit.primitives import BaseSampler +from qiskit.passmanager import BasePassManager +from qiskit.primitives import BaseSamplerV1, BaseSamplerV2, SamplerResult from qiskit.quantum_info import SparsePauliOp from qiskit_algorithms.exceptions import AlgorithmError @@ -58,9 +59,10 @@ class MagicRounding(RoundingScheme): def __init__( self, - sampler: BaseSampler, + sampler: BaseSamplerV1 | BaseSamplerV2, basis_sampling: str = "uniform", seed: int | None = None, + passmanager: BasePassManager | None = None, ): """ Args: @@ -76,6 +78,7 @@ def __init__( sampling. seed: Seed for random number generator, which is used to sample the magic bases. + passmanager: Pass manager to transpile the circuits Raises: ValueError: If ``basis_sampling`` is not ``"uniform"`` or ``"weighted"``. @@ -89,13 +92,23 @@ def __init__( self._sampler = sampler self._rng = np.random.default_rng(seed) self._basis_sampling = basis_sampling - if self._sampler.options.get("shots") is None: - raise ValueError("Magic rounding requires a sampler configured with a number of shots.") - self._shots = sampler.options.shots + self._passmanager = passmanager + if isinstance(self._sampler, BaseSamplerV1): + if self._sampler.options.get("shots") is None: + raise ValueError( + "Magic rounding requires a sampler configured with a number of shots." + ) + self._shots = sampler.options.shots + else: # BaseSamplerV2 + if self._sampler.default_shots is None: + raise ValueError( + "Magic rounding requires a sampler configured with a number of shots." + ) + self._shots = self._sampler.default_shots super().__init__() @property - def sampler(self) -> BaseSampler: + def sampler(self) -> BaseSamplerV1 | BaseSamplerV2: """Returns the Sampler used to sample the magic bases.""" return self._sampler @@ -166,6 +179,8 @@ def _evaluate_magic_bases( QiskitOptimizationError: If some of the results from the primitive job are not collected. """ circuits = self._make_circuits(circuit, bases, vars_per_qubit) + if self._passmanager: + circuits = self._passmanager.run(circuits) # Execute each of the rotated circuits and collect the results # Batch the circuits into jobs where each group has the same number of # shots, so that you can wait for the queue as few times as possible if @@ -182,14 +197,27 @@ def _evaluate_magic_bases( circuit_indices_by_shots[shots].append(i) for shots, indices in sorted(circuit_indices_by_shots.items(), reverse=True): + circuits_ = [circuits[i] for i in indices] try: - job = self._sampler.run([circuits[i] for i in indices], shots=shots) + job = self._sampler.run(circuits_, shots=shots) result = job.result() except Exception as exc: raise AlgorithmError( "The primitive job to evaluate the magic state failed." ) from exc - counts_list = [dist.binary_probabilities() for dist in result.quasi_dists] + + if isinstance(result, SamplerResult): + counts_list = [dist.binary_probabilities() for dist in result.quasi_dists] + else: + counts_list = [ + getattr(res.data, circ.cregs[0].name).get_counts() + for res, circ in zip(result, circuits_) + ] + counts_list = [ + {k: v / sum(counts.values()) for k, v in counts.items()} + for counts in counts_list + ] + if len(counts_list) != len(indices): raise QiskitOptimizationError( "Internal error: The number of circuits and the results from the primitive job " diff --git a/test/algorithms/qrao/test_magic_rounding.py b/test/algorithms/qrao/test_magic_rounding.py index 15caae12..bca9ce01 100644 --- a/test/algorithms/qrao/test_magic_rounding.py +++ b/test/algorithms/qrao/test_magic_rounding.py @@ -15,8 +15,9 @@ from test.optimization_test_case import QiskitOptimizationTestCase import numpy as np +from ddt import data, ddt from qiskit.circuit import QuantumCircuit -from qiskit.primitives import Sampler +from qiskit.primitives import Sampler, StatevectorSampler from qiskit_algorithms import NumPyMinimumEigensolver from qiskit_optimization.algorithms import OptimizationResultStatus, SolutionSample @@ -30,6 +31,7 @@ from qiskit_optimization.problems import QuadraticProgram +@ddt class TestMagicRounding(QiskitOptimizationTestCase): """MagicRounding tests.""" @@ -41,10 +43,15 @@ def setUp(self): self.problem.binary_var("y") self.problem.binary_var("z") self.problem.minimize(linear={"x": 1, "y": 2, "z": 3}) + self._sampler = { + "v1": Sampler(options={"shots": 10000, "seed": 42}), + "v2": StatevectorSampler(default_shots=10000, seed=42), + } - def test_magic_rounding_constructor(self): + @data("v1", "v2") + def test_magic_rounding_constructor(self, version): """Test constructor""" - sampler = Sampler(options={"shots": 10000, "seed": 42}) + sampler = self._sampler[version] # test default magic_rounding = MagicRounding(sampler) self.assertEqual(magic_rounding.sampler, sampler) @@ -61,9 +68,10 @@ def test_magic_rounding_constructor(self): with self.assertRaises(ValueError): MagicRounding(sampler, basis_sampling="invalid") - def test_magic_rounding_round_uniform_1_1_qrac(self): + @data("v1", "v2") + def test_magic_rounding_round_uniform_1_1_qrac(self, version): """Test round method with uniform basis sampling for max_vars_per_qubit=1""" - sampler = Sampler(options={"shots": 10000, "seed": 42}) + sampler = self._sampler[version] encoding = QuantumRandomAccessEncoding(max_vars_per_qubit=1) encoding.encode(self.problem) np_solver = NumPyMinimumEigensolver() @@ -91,9 +99,10 @@ def test_magic_rounding_round_uniform_1_1_qrac(self): [1, 1, 1], ) - def test_magic_rounding_round_weighted_1_1_qrac(self): + @data("v1", "v2") + def test_magic_rounding_round_weighted_1_1_qrac(self, version): """Test round method with uniform basis sampling for max_vars_per_qubit=1""" - sampler = Sampler(options={"shots": 10000, "seed": 42}) + sampler = self._sampler[version] encoding = QuantumRandomAccessEncoding(max_vars_per_qubit=1) encoding.encode(self.problem) np_solver = NumPyMinimumEigensolver() @@ -121,9 +130,10 @@ def test_magic_rounding_round_weighted_1_1_qrac(self): [1, 1, 1], ) - def test_magic_rounding_round_uniform_2_1_qrac(self): + @data("v1", "v2") + def test_magic_rounding_round_uniform_2_1_qrac(self, version): """Test round method with uniform basis sampling for max_vars_per_qubit=2""" - sampler = Sampler(options={"shots": 10000, "seed": 42}) + sampler = self._sampler[version] encoding = QuantumRandomAccessEncoding(max_vars_per_qubit=2) encoding.encode(self.problem) np_solver = NumPyMinimumEigensolver() @@ -142,7 +152,7 @@ def test_magic_rounding_round_uniform_2_1_qrac(self): ] for i, basis_counts in enumerate(rounding_result.basis_counts): for key, value in basis_counts.items(): - self.assertAlmostEqual(value, expected_basis_counts[i][key], delta=50) + self.assertAlmostEqual(value, expected_basis_counts[i][key], delta=70) samples = rounding_result.samples samples.sort(key=lambda sample: np.array2string(sample.x)) expected_samples = [ @@ -163,9 +173,10 @@ def test_magic_rounding_round_uniform_2_1_qrac(self): [0.44721359549995743, 0.8944271909999162, 1], ) - def test_magic_rounding_round_weighted_2_1_qrac(self): + @data("v1", "v2") + def test_magic_rounding_round_weighted_2_1_qrac(self, version): """Test round method with weighted basis sampling for max_vars_per_qubit=2""" - sampler = Sampler(options={"shots": 10000, "seed": 42}) + sampler = self._sampler[version] encoding = QuantumRandomAccessEncoding(max_vars_per_qubit=2) encoding.encode(self.problem) np_solver = NumPyMinimumEigensolver() @@ -182,7 +193,7 @@ def test_magic_rounding_round_weighted_2_1_qrac(self): ] for i, basis_counts in enumerate(rounding_result.basis_counts): for key, value in basis_counts.items(): - self.assertAlmostEqual(value, expected_basis_counts[i][key], delta=50) + self.assertAlmostEqual(value, expected_basis_counts[i][key], delta=70) samples = rounding_result.samples samples.sort(key=lambda sample: np.array2string(sample.x)) expected_samples = [ @@ -203,9 +214,10 @@ def test_magic_rounding_round_weighted_2_1_qrac(self): [0.44721359549995743, 0.8944271909999162, 1], ) - def test_magic_rounding_round_uniform_3_1_qrac(self): + @data("v1", "v2") + def test_magic_rounding_round_uniform_3_1_qrac(self, version): """Test round method with uniform basis sampling for max_vars_per_qubit=3""" - sampler = Sampler(options={"shots": 10000, "seed": 42}) + sampler = self._sampler[version] encoding = QuantumRandomAccessEncoding(max_vars_per_qubit=3) encoding.encode(self.problem) np_solver = NumPyMinimumEigensolver() @@ -245,14 +257,15 @@ def test_magic_rounding_round_uniform_3_1_qrac(self): [0.2672612419124245, 0.5345224838248487, 0.8017837257372733], ) - def test_magic_rounding_round_weighted_3_1_qrac(self): + @data("v1", "v2") + def test_magic_rounding_round_weighted_3_1_qrac(self, version): """Test round method with weighted basis sampling for max_vars_per_qubit=3""" encoding = QuantumRandomAccessEncoding(max_vars_per_qubit=3) encoding.encode(self.problem) np_solver = NumPyMinimumEigensolver() qrao = QuantumRandomAccessOptimizer(min_eigen_solver=np_solver) _, rounding_context = qrao.solve_relaxed(encoding=encoding) - sampler = Sampler(options={"shots": 10000, "seed": 42}) + sampler = self._sampler[version] magic_rounding = MagicRounding(sampler, basis_sampling="weighted", seed=42) rounding_result = magic_rounding.round(rounding_context) self.assertIsInstance(rounding_result, RoundingResult) diff --git a/test/algorithms/qrao/test_quantum_random_access_encoding.py b/test/algorithms/qrao/test_quantum_random_access_encoding.py index 02b9bdc6..4e19e083 100644 --- a/test/algorithms/qrao/test_quantum_random_access_encoding.py +++ b/test/algorithms/qrao/test_quantum_random_access_encoding.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2023. +# (C) Copyright IBM 2023, 2024. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -13,22 +13,21 @@ """Tests for QuantumRandomAccessEncoding""" import itertools import unittest -from test.optimization_test_case import QiskitOptimizationTestCase +from test import QiskitOptimizationTestCase -from ddt import ddt, data, unpack -import numpy as np import networkx as nx - +import numpy as np +from ddt import data, ddt, unpack from qiskit.circuit import QuantumCircuit -from qiskit.primitives import Estimator +from qiskit.primitives import Estimator, StatevectorEstimator from qiskit.quantum_info import SparsePauliOp from qiskit_optimization.algorithms.qrao import ( EncodingCommutationVerifier, QuantumRandomAccessEncoding, ) -from qiskit_optimization.problems import QuadraticProgram, QuadraticObjective from qiskit_optimization.applications import Maxcut +from qiskit_optimization.problems import QuadraticObjective, QuadraticProgram class TestQuantumRandomAccessEncoding(QiskitOptimizationTestCase): @@ -252,17 +251,20 @@ def test_qrac_unsupported_encoding(self): class TestEncodingCommutationVerifier(QiskitOptimizationTestCase): """Tests for EncodingCommutationVerifier.""" - def check_problem_commutation(self, problem: QuadraticProgram, max_vars_per_qubit: int): + def check_problem_commutation( + self, problem: QuadraticProgram, max_vars_per_qubit: int, version: str + ): """Utility function to check that the problem commutes with its encoding""" encoding = QuantumRandomAccessEncoding(max_vars_per_qubit=max_vars_per_qubit) encoding.encode(problem) - estimator = Estimator() + estimator = Estimator() if version == "v1" else StatevectorEstimator() verifier = EncodingCommutationVerifier(encoding, estimator) self.assertEqual(len(verifier), 2**encoding.num_vars) for _, obj_val, encoded_obj_val in verifier: np.testing.assert_allclose(obj_val, encoded_obj_val, atol=1e-5) - def test_encoding_commutation_verifier(self): + @data("v1", "v2") + def test_encoding_commutation_verifier(self, version): """Test EncodingCommutationVerifier""" problem = QuadraticProgram() problem.binary_var("x") @@ -272,11 +274,11 @@ def test_encoding_commutation_verifier(self): encoding = QuantumRandomAccessEncoding(max_vars_per_qubit=3) encoding.encode(problem) - self.check_problem_commutation(problem, 3) + self.check_problem_commutation(problem, 3, version) - @data(*itertools.product([1, 2, 3], ["minimize", "maximize"])) + @data(*itertools.product([1, 2, 3], ["minimize", "maximize"], ["v1", "v2"])) @unpack - def test_one_qubit_qrac(self, max_vars_per_qubit, task): + def test_one_qubit_qrac(self, max_vars_per_qubit, task, version): """Test commutation of single qubit QRAC with non-uniform weights, degree 1 terms""" problem = QuadraticProgram() @@ -287,15 +289,17 @@ def test_one_qubit_qrac(self, max_vars_per_qubit, task): problem.minimize(linear=obj) else: problem.maximize(linear=obj) - self.check_problem_commutation(problem, max_vars_per_qubit) + self.check_problem_commutation(problem, max_vars_per_qubit, version) @data( *itertools.product( - [1, 2, 3], [QuadraticObjective.Sense.MINIMIZE, QuadraticObjective.Sense.MAXIMIZE] + [1, 2, 3], + [QuadraticObjective.Sense.MINIMIZE, QuadraticObjective.Sense.MAXIMIZE], + ["v1", "v2"], ) ) @unpack - def test_uniform_weights_degree_2(self, max_vars_per_qubit, task): + def test_uniform_weights_degree_2(self, max_vars_per_qubit, task, version): """Test problem commutation with degree 2 terms""" # Note that the variable embedding has some qubits with 1, 2, and 3 qubits elist = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0), (0, 3), (1, 4), (2, 4)] @@ -306,25 +310,27 @@ def test_uniform_weights_degree_2(self, max_vars_per_qubit, task): maxcut = Maxcut(graph) problem = maxcut.to_quadratic_program() problem.objective.sense = task - self.check_problem_commutation(problem, max_vars_per_qubit) + self.check_problem_commutation(problem, max_vars_per_qubit, version) - @data(1, 2, 3) - def test_random_unweighted_maxcut(self, max_vars_per_qubit): + @data(*itertools.product([1, 2, 3], ["v1", "v2"])) + @unpack + def test_random_unweighted_maxcut(self, max_vars_per_qubit, version): """Test problem commutation with random unweighted MaxCut""" graph = nx.random_regular_graph(3, 8) maxcut = Maxcut(graph) problem = maxcut.to_quadratic_program() - self.check_problem_commutation(problem, max_vars_per_qubit) + self.check_problem_commutation(problem, max_vars_per_qubit, version) - @data(1, 2, 3) - def test_random_weighted_maxcut(self, max_vars_per_qubit): + @data(*itertools.product([1, 2, 3], ["v1", "v2"])) + @unpack + def test_random_weighted_maxcut(self, max_vars_per_qubit, version): """Test problem commutation with random weighted MaxCut""" graph = nx.random_regular_graph(3, 8) for w, v in graph.edges: graph[w][v]["weight"] = np.random.randint(1, 10) maxcut = Maxcut(graph) problem = maxcut.to_quadratic_program() - self.check_problem_commutation(problem, max_vars_per_qubit) + self.check_problem_commutation(problem, max_vars_per_qubit, version) if __name__ == "__main__": diff --git a/test/algorithms/test_grover_optimizer.py b/test/algorithms/test_grover_optimizer.py index b16d2b2a..de9b99a0 100644 --- a/test/algorithms/test_grover_optimizer.py +++ b/test/algorithms/test_grover_optimizer.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2020, 2023. +# (C) Copyright IBM 2020, 2024. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -16,9 +16,12 @@ from test import QiskitOptimizationTestCase import numpy as np +from ddt import data, ddt from docplex.mp.model import Model +from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager from qiskit.utils import optionals -from qiskit_aer.primitives import Sampler +from qiskit_aer import AerSimulator +from qiskit_aer.primitives import Sampler, SamplerV2 from qiskit_algorithms import NumPyMinimumEigensolver from qiskit_algorithms.utils import algorithm_globals @@ -38,6 +41,7 @@ from qiskit_optimization.translators import from_docplex_mp +@ddt class TestGroverOptimizer(QiskitOptimizationTestCase): """GroverOptimizer tests.""" @@ -45,16 +49,23 @@ class TestGroverOptimizer(QiskitOptimizationTestCase): def setUp(self): super().setUp() algorithm_globals.random_seed = 1 - self.sampler = Sampler(run_options={"seed_simulator": 123}) + self.sampler = { + "v1": Sampler(run_options={"seed_simulator": 123}), + "v2": SamplerV2(seed=123), + } + self.passmanager = generate_preset_pass_manager( + optimization_level=1, target=AerSimulator().target + ) self.n_iter = 8 - def _prepare_grover_optimizer(self, num_value_qubits, num_iterations, converters=None): + def _prepare_grover_optimizer(self, num_value_qubits, num_iterations, version, converters=None): """Prepare GroverOptimizer.""" grover_optimizer = GroverOptimizer( num_value_qubits=num_value_qubits, num_iterations=num_iterations, converters=converters, - sampler=self.sampler, + sampler=self.sampler[version], + passmanager=self.passmanager, ) return grover_optimizer @@ -71,7 +82,8 @@ def validate_results(self, problem, results): results.fval, problem.objective.sense.value * results.intermediate_fval ) - def test_qubo_gas_int_zero(self): + @data("v1", "v2") + def test_qubo_gas_int_zero(self, version): """Test for when the answer is zero.""" # Input. @@ -85,13 +97,15 @@ def test_qubo_gas_int_zero(self): grover_optimizer = self._prepare_grover_optimizer( num_value_qubits=1, num_iterations=1, + version=version, ) results = grover_optimizer.solve(op) np.testing.assert_array_almost_equal(results.x, [0, 0]) self.assertEqual(results.fval, 0.0) self.assertAlmostEqual(results.fval, results.intermediate_fval) - def test_qubo_gas_int_simple(self): + @data("v1", "v2") + def test_qubo_gas_int_simple(self, version): """Test for simple case, with 2 linear coeffs and no quadratic coeffs or constants.""" # Input. @@ -103,7 +117,9 @@ def test_qubo_gas_int_simple(self): # Get the optimum key and value. grover_optimizer = self._prepare_grover_optimizer( - num_value_qubits=4, num_iterations=self.n_iter + num_value_qubits=4, + num_iterations=self.n_iter, + version=version, ) results = grover_optimizer.solve(op) self.validate_results(op, results) @@ -112,7 +128,8 @@ def test_qubo_gas_int_simple(self): self.assertEqual(results.n_input_qubits, 2) self.assertEqual(results.n_output_qubits, 4) - def test_qubo_gas_int_simple_maximize(self): + @data("v1", "v2") + def test_qubo_gas_int_simple_maximize(self, version): """Test for simple case, but with maximization.""" # Input. @@ -124,12 +141,15 @@ def test_qubo_gas_int_simple_maximize(self): # Get the optimum key and value. grover_optimizer = self._prepare_grover_optimizer( - num_value_qubits=4, num_iterations=self.n_iter + num_value_qubits=4, + num_iterations=self.n_iter, + version=version, ) results = grover_optimizer.solve(op) self.validate_results(op, results) - def test_qubo_gas_int_paper_example(self): + @data("v1", "v2") + def test_qubo_gas_int_paper_example(self, version): """ Test the example from https://arxiv.org/abs/1912.04088 """ @@ -144,12 +164,15 @@ def test_qubo_gas_int_paper_example(self): # Get the optimum key and value. grover_optimizer = self._prepare_grover_optimizer( - num_value_qubits=6, num_iterations=self.n_iter + num_value_qubits=6, + num_iterations=self.n_iter, + version=version, ) results = grover_optimizer.solve(op) self.validate_results(op, results) - def test_converter_list(self): + @data("v1", "v2") + def test_converter_list(self, version): """Test converters list""" # Input. @@ -163,7 +186,9 @@ def test_converter_list(self): # a single converter. qp2qubo = QuadraticProgramToQubo() grover_optimizer = self._prepare_grover_optimizer( - num_value_qubits=4, num_iterations=self.n_iter + num_value_qubits=4, + num_iterations=self.n_iter, + version=version, ) results = grover_optimizer.solve(op) self.validate_results(op, results) @@ -178,6 +203,7 @@ def test_converter_list(self): num_value_qubits=4, num_iterations=self.n_iter, converters=converters, + version=version, ) results = grover_optimizer.solve(op) self.validate_results(op, results) @@ -185,10 +211,14 @@ def test_converter_list(self): with self.assertRaises(TypeError): invalid = [qp2qubo, "invalid converter"] grover_optimizer = self._prepare_grover_optimizer( - 4, num_iterations=self.n_iter, converters=invalid + 4, + num_iterations=self.n_iter, + converters=invalid, + version=version, ) - def test_samples_and_raw_samples(self): + @data("v1", "v2") + def test_samples_and_raw_samples(self, version): """Test samples and raw_samples""" algorithm_globals.random_seed = 2 op = QuadraticProgram() @@ -197,7 +227,7 @@ def test_samples_and_raw_samples(self): op.minimize(linear={"x": 1, "y": 2}) op.linear_constraint(linear={"x": 1, "y": 1}, sense=">=", rhs=1, name="xy") grover_optimizer = self._prepare_grover_optimizer( - num_value_qubits=8, num_iterations=self.n_iter + num_value_qubits=8, num_iterations=self.n_iter, version=version ) opt_sol = 1 success = OptimizationResultStatus.SUCCESS @@ -218,7 +248,8 @@ def test_samples_and_raw_samples(self): self.assertEqual(results.status, results.raw_samples[0].status) np.testing.assert_array_almost_equal([1, 0, 0, 0, 0], results.raw_samples[0].x) - def test_bit_ordering(self): + @data("v1", "v2") + def test_bit_ordering(self, version): """Test bit ordering""" # test minimize algorithm_globals.random_seed = 2 @@ -230,7 +261,9 @@ def test_bit_ordering(self): opt_sol = -2 success = OptimizationResultStatus.SUCCESS grover_optimizer = self._prepare_grover_optimizer( - num_value_qubits=3, num_iterations=self.n_iter + num_value_qubits=3, + num_iterations=self.n_iter, + version=version, ) results = grover_optimizer.solve(op) self.assertEqual(results.fval, opt_sol)