diff --git a/.pylintdict b/.pylintdict index 245f309e..6846bce4 100644 --- a/.pylintdict +++ b/.pylintdict @@ -362,6 +362,7 @@ trainability transpilation transpile transpiled +transpiler trotterization trotterized uncompute diff --git a/qiskit_algorithms/amplitude_amplifiers/grover.py b/qiskit_algorithms/amplitude_amplifiers/grover.py index 56fd2ad1..aaab79d6 100644 --- a/qiskit_algorithms/amplitude_amplifiers/grover.py +++ b/qiskit_algorithms/amplitude_amplifiers/grover.py @@ -18,16 +18,14 @@ from typing import Any import numpy as np - from qiskit import ClassicalRegister, QuantumCircuit -from qiskit.primitives import BaseSampler -from qiskit.quantum_info import Statevector +from qiskit.primitives import BaseSamplerV2 from qiskit_algorithms.exceptions import AlgorithmError from qiskit_algorithms.utils import algorithm_globals - from .amplification_problem import AmplificationProblem from .amplitude_amplifier import AmplitudeAmplifier, AmplitudeAmplifierResult +from ..custom_types import Transpiler class Grover(AmplitudeAmplifier): @@ -116,7 +114,9 @@ def __init__( iterations: list[int] | Iterator[int] | int | None = None, growth_rate: float | None = None, sample_from_iterations: bool = False, - sampler: BaseSampler | None = None, + sampler: BaseSamplerV2 | None = None, + transpiler: Transpiler | None = None, + transpiler_options: dict[str, Any] | None = None, ) -> None: r""" Args: @@ -136,6 +136,11 @@ def __init__( powers of the Grover operator, a random integer sample between 0 and smaller value than the iteration is used as a power, see [1], Section 4. sampler: A Sampler to use for sampling the results of the circuits. + transpiler: An optional object with a `run` method allowing to transpile the circuits + that are produced within this algorithm. If set to `None`, these won't be + transpiled. + transpiler_options: A dictionary of options to be passed to the transpiler's `run` + method as keyword arguments. Raises: ValueError: If ``growth_rate`` is a float but not larger than 1. @@ -165,9 +170,11 @@ def __init__( self._sampler = sampler self._sample_from_iterations = sample_from_iterations self._iterations_arg = iterations + self._transpiler = transpiler + self._transpiler_options = transpiler_options if transpiler_options is not None else {} @property - def sampler(self) -> BaseSampler | None: + def sampler(self) -> BaseSamplerV2 | None: """Get the sampler. Returns: @@ -176,7 +183,7 @@ def sampler(self) -> BaseSampler | None: return self._sampler @sampler.setter - def sampler(self, sampler: BaseSampler) -> None: + def sampler(self, sampler: BaseSamplerV2) -> None: """Set the sampler. Args: @@ -234,23 +241,29 @@ def amplify(self, amplification_problem: AmplificationProblem) -> "GroverResult" # sample from [0, power) if specified if self._sample_from_iterations: power = algorithm_globals.random.integers(power) + # Run a grover experiment for a given power of the Grover operator. - if self._sampler is not None: - qc = self.construct_circuit(amplification_problem, power, measurement=True) - job = self._sampler.run([qc]) - - try: - results = job.result() - except Exception as exc: - raise AlgorithmError("Sampler job failed.") from exc - - num_bits = len(amplification_problem.objective_qubits) - circuit_results: dict[str, Any] | Statevector | np.ndarray = { - np.binary_repr(k, num_bits): v for k, v in results.quasi_dists[0].items() - } - top_measurement, max_probability = max( - circuit_results.items(), key=lambda x: x[1] # type: ignore[union-attr] - ) + qc = self.construct_circuit(amplification_problem, power, measurement=True) + + if self._transpiler is not None: + qc = self._transpiler.run(qc, **self._transpiler_options) + + job = self._sampler.run([qc]) + + try: + results = job.result() + except Exception as exc: + raise AlgorithmError("Sampler job failed.") from exc + + circuit_results = getattr(results[0].data, qc.cregs[0].name) + circuit_results = { + label: value / circuit_results.num_shots + for label, value in circuit_results.get_counts().items() + } + + top_measurement, max_probability = max( + circuit_results.items(), key=lambda x: x[1] # type: ignore[union-attr] + ) all_circuit_results.append(circuit_results) diff --git a/qiskit_algorithms/custom_types.py b/qiskit_algorithms/custom_types.py new file mode 100644 index 00000000..3d98fd3e --- /dev/null +++ b/qiskit_algorithms/custom_types.py @@ -0,0 +1,28 @@ +# This code is part of a Qiskit project. +# +# (C) Copyright IBM 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 +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Types used by the qiskit-algorithms package.""" +from __future__ import annotations + +from typing import Any, Protocol, Union + +from qiskit import QuantumCircuit + +_Circuits = Union[list[QuantumCircuit], QuantumCircuit] + + +class Transpiler(Protocol): + """A Generic type to represent a transpiler.""" + + def run(self, circuits: _Circuits, **options: Any) -> _Circuits: + """Transpile a circuit or a list of quantum circuits.""" + pass diff --git a/qiskit_algorithms/eigensolvers/vqd.py b/qiskit_algorithms/eigensolvers/vqd.py index 8fee8b76..ff8ac75f 100644 --- a/qiskit_algorithms/eigensolvers/vqd.py +++ b/qiskit_algorithms/eigensolvers/vqd.py @@ -25,7 +25,7 @@ import numpy as np from qiskit.circuit import QuantumCircuit -from qiskit.primitives import BaseEstimator +from qiskit.primitives import BaseEstimatorV2 from qiskit.quantum_info.operators.base_operator import BaseOperator from qiskit.quantum_info import SparsePauliOp @@ -88,7 +88,7 @@ class VQD(VariationalAlgorithm, Eigensolver): updated once the VQD object has been constructed. Attributes: - estimator (BaseEstimator): The primitive instance used to perform the expectation + estimator (BaseEstimatorV2): The primitive instance used to perform the expectation estimation as indicated in the VQD paper. fidelity (BaseStateFidelity): The fidelity class instance used to compute the overlap estimation as indicated in the VQD paper. @@ -112,7 +112,7 @@ class VQD(VariationalAlgorithm, Eigensolver): def __init__( self, - estimator: BaseEstimator, + estimator: BaseEstimatorV2, fidelity: BaseStateFidelity, ansatz: QuantumCircuit, optimizer: Optimizer | Minimizer | Sequence[Optimizer | Minimizer], @@ -389,9 +389,7 @@ def evaluate_energy(parameters: np.ndarray) -> float | np.ndarray: parameters = np.reshape(parameters, (-1, num_parameters)) batch_size = len(parameters) - estimator_job = self.estimator.run( - batch_size * [self.ansatz], batch_size * [operator], parameters - ) + estimator_job = self.estimator.run([(self.ansatz, operator, parameters)]) total_cost = np.zeros(batch_size) @@ -410,18 +408,17 @@ def evaluate_energy(parameters: np.ndarray) -> float | np.ndarray: total_cost += np.real(betas[state] * cost) try: - estimator_result = estimator_job.result() + estimator_result = estimator_job.result()[0] except Exception as exc: raise AlgorithmError("The primitive job to evaluate the energy failed!") from exc - values = estimator_result.values + total_cost + values = estimator_result.data.evs + total_cost if self.callback is not None: - metadata = estimator_result.metadata - for params, value, meta in zip(parameters, values, metadata): + for params, value in zip(parameters, values): self._eval_count += 1 - self.callback(self._eval_count, params, value, meta, step) + self.callback(self._eval_count, params, value, estimator_result.metadata, step) else: self._eval_count += len(values) diff --git a/qiskit_algorithms/observables_evaluator.py b/qiskit_algorithms/observables_evaluator.py index ae125bfb..74323218 100644 --- a/qiskit_algorithms/observables_evaluator.py +++ b/qiskit_algorithms/observables_evaluator.py @@ -20,7 +20,7 @@ from qiskit import QuantumCircuit from qiskit.quantum_info import SparsePauliOp -from qiskit.primitives import BaseEstimator +from qiskit.primitives import BaseEstimatorV2 from qiskit.quantum_info.operators.base_operator import BaseOperator from .exceptions import AlgorithmError @@ -28,7 +28,7 @@ def estimate_observables( - estimator: BaseEstimator, + estimator: BaseEstimatorV2, quantum_state: QuantumCircuit, observables: ListOrDict[BaseOperator], parameter_values: Sequence[float] | None = None, @@ -63,21 +63,19 @@ def estimate_observables( if len(observables_list) > 0: observables_list = _handle_zero_ops(observables_list) - quantum_state = [quantum_state] * len(observables) parameter_values_: Sequence[float] | Sequence[Sequence[float]] | None = parameter_values - if parameter_values is not None: - parameter_values_ = [parameter_values] * len(observables) try: - estimator_job = estimator.run(quantum_state, observables_list, parameter_values_) - expectation_values = estimator_job.result().values + estimator_job = estimator.run([(quantum_state, observables_list, parameter_values_)]) + estimator_result = estimator_job.result()[0] + expectation_values = estimator_result.data.evs except Exception as exc: raise AlgorithmError("The primitive job failed!") from exc - metadata = estimator_job.result().metadata + metadata = estimator_result.metadata # Discard values below threshold observables_means = expectation_values * (np.abs(expectation_values) > threshold) # zip means and metadata into tuples - observables_results = list(zip(observables_means, metadata)) + observables_results = list(zip(observables_means, [metadata] * len(observables_means))) else: observables_results = [] diff --git a/qiskit_algorithms/phase_estimators/hamiltonian_phase_estimation.py b/qiskit_algorithms/phase_estimators/hamiltonian_phase_estimation.py index bfb36e9a..058a884b 100644 --- a/qiskit_algorithms/phase_estimators/hamiltonian_phase_estimation.py +++ b/qiskit_algorithms/phase_estimators/hamiltonian_phase_estimation.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 @@ -14,16 +14,16 @@ from __future__ import annotations - from qiskit import QuantumCircuit from qiskit.circuit.library import PauliEvolutionGate -from qiskit.primitives import BaseSampler +from qiskit.primitives import BaseSamplerV2 from qiskit.quantum_info import SparsePauliOp, Statevector, Pauli from qiskit.synthesis import EvolutionSynthesis -from .phase_estimation import PhaseEstimation from .hamiltonian_phase_estimation_result import HamiltonianPhaseEstimationResult +from .phase_estimation import PhaseEstimation from .phase_estimation_scale import PhaseEstimationScale +from ..custom_types import Transpiler class HamiltonianPhaseEstimation: @@ -83,17 +83,22 @@ class HamiltonianPhaseEstimation: def __init__( self, num_evaluation_qubits: int, - sampler: BaseSampler | None = None, + sampler: BaseSamplerV2 | None = None, + transpiler: Transpiler | None = None, ) -> None: r""" Args: num_evaluation_qubits: The number of qubits used in estimating the phase. The phase will be estimated as a binary string with this many bits. sampler: The sampler primitive on which the circuit will be sampled. + transpiler: An optional object with a `run` method allowing to transpile the circuits + that are produced within this algorithm. If set to `None`, these won't be + transpiled. """ self._phase_estimation = PhaseEstimation( num_evaluation_qubits=num_evaluation_qubits, sampler=sampler, + transpiler=transpiler, ) def _get_scale(self, hamiltonian, bound=None) -> PhaseEstimationScale: diff --git a/qiskit_algorithms/phase_estimators/ipe.py b/qiskit_algorithms/phase_estimators/ipe.py index 7c797182..42b88e0b 100644 --- a/qiskit_algorithms/phase_estimators/ipe.py +++ b/qiskit_algorithms/phase_estimators/ipe.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2021, 2023. +# (C) Copyright IBM 2021, 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 @@ -19,12 +19,13 @@ from qiskit.circuit import QuantumCircuit, QuantumRegister from qiskit.circuit.classicalregister import ClassicalRegister -from qiskit.primitives import BaseSampler +from qiskit.primitives import BaseSamplerV2 from qiskit_algorithms.exceptions import AlgorithmError from .phase_estimator import PhaseEstimator from .phase_estimator import PhaseEstimatorResult +from ..custom_types import Transpiler class IterativePhaseEstimation(PhaseEstimator): @@ -40,12 +41,16 @@ class IterativePhaseEstimation(PhaseEstimator): def __init__( self, num_iterations: int, - sampler: BaseSampler | None = None, + sampler: BaseSamplerV2 | None = None, + transpiler: Transpiler | None = None, ) -> None: r""" Args: num_iterations: The number of iterations (rounds) of the phase estimation to run. sampler: The sampler primitive on which the circuit will be sampled. + transpiler: An optional object with a `run` method allowing to transpile the circuits + that are produced within this algorithm. If set to `None`, these won't be + transpiled. Raises: ValueError: if num_iterations is not greater than zero. @@ -58,6 +63,7 @@ def __init__( raise ValueError("`num_iterations` must be greater than zero.") self._num_iterations = num_iterations self._sampler = sampler + self._pass_manager = transpiler def construct_circuit( self, @@ -125,9 +131,17 @@ def _estimate_phase_iteratively(self, unitary, state_preparation): qc = self.construct_circuit( unitary, state_preparation, k, -2 * numpy.pi * omega_coef, True ) + + if self._pass_manager is not None: + qc = self._pass_manager.run(qc) + try: sampler_job = self._sampler.run([qc]) - result = sampler_job.result().quasi_dists[0] + result = sampler_job.result()[0].data.c + result = { + label: value / result.num_shots + for label, value in result.get_int_counts().items() + } except Exception as exc: raise AlgorithmError("The primitive job failed!") from exc x = 1 if result.get(1, 0) > result.get(0, 0) else 0 diff --git a/qiskit_algorithms/phase_estimators/phase_estimation.py b/qiskit_algorithms/phase_estimators/phase_estimation.py index bf84b736..930fed65 100644 --- a/qiskit_algorithms/phase_estimators/phase_estimation.py +++ b/qiskit_algorithms/phase_estimators/phase_estimation.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 @@ -20,13 +20,14 @@ from qiskit import circuit from qiskit.circuit import QuantumCircuit from qiskit.circuit.classicalregister import ClassicalRegister -from qiskit.primitives import BaseSampler +from qiskit.primitives import BaseSamplerV2 from qiskit.result import Result from qiskit_algorithms.exceptions import AlgorithmError from .phase_estimation_result import PhaseEstimationResult, _sort_phases from .phase_estimator import PhaseEstimator +from ..custom_types import Transpiler class PhaseEstimation(PhaseEstimator): @@ -82,13 +83,17 @@ class PhaseEstimation(PhaseEstimator): def __init__( self, num_evaluation_qubits: int, - sampler: BaseSampler | None = None, + sampler: BaseSamplerV2 | None = None, + transpiler: Transpiler | None = None, ) -> None: r""" Args: num_evaluation_qubits: The number of qubits used in estimating the phase. The phase will be estimated as a binary string with this many bits. sampler: The sampler primitive on which the circuit will be sampled. + transpiler: An optional object with a `run` method allowing to transpile the circuits + that are produced within this algorithm. If set to `None`, these won't be + transpiled. Raises: AlgorithmError: If a sampler is not provided @@ -101,6 +106,7 @@ def __init__( self._num_evaluation_qubits = num_evaluation_qubits self._sampler = sampler + self._pass_manager = transpiler def construct_circuit( self, unitary: QuantumCircuit, state_preparation: QuantumCircuit | None = None @@ -189,6 +195,9 @@ def estimate_from_pe_circuit(self, pe_circuit: QuantumCircuit) -> PhaseEstimatio AlgorithmError: Primitive job failed. """ + if self._pass_manager is not None: + pe_circuit = self._pass_manager.run(pe_circuit) + self._add_measurement_if_required(pe_circuit) try: @@ -196,11 +205,13 @@ def estimate_from_pe_circuit(self, pe_circuit: QuantumCircuit) -> PhaseEstimatio circuit_result = circuit_job.result() except Exception as exc: raise AlgorithmError("The primitive job failed!") from exc - phases = circuit_result.quasi_dists[0] + phases = circuit_result[0].data.meas.get_counts() + # Ensure we still return the measurement strings in sorted order, which SamplerV2 doesn't + # guarantee + measurement_labels = sorted(phases.keys()) phases_bitstrings = {} - for key, phase in phases.items(): - bitstring_key = self._get_reversed_bitstring(self._num_evaluation_qubits, key) - phases_bitstrings[bitstring_key] = phase + for key in measurement_labels: + phases_bitstrings[key[::-1]] = phases[key] / circuit_result[0].data.meas.num_shots phases = phases_bitstrings return PhaseEstimationResult( diff --git a/qiskit_algorithms/phase_estimators/phase_estimator.py b/qiskit_algorithms/phase_estimators/phase_estimator.py index 1f0f5002..2a78f7f8 100644 --- a/qiskit_algorithms/phase_estimators/phase_estimator.py +++ b/qiskit_algorithms/phase_estimators/phase_estimator.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 @@ -38,10 +38,6 @@ def estimate( """Estimate the phase.""" raise NotImplementedError - @staticmethod - def _get_reversed_bitstring(length: int, number: int) -> str: - return f"{number:b}".zfill(length)[::-1] - class PhaseEstimatorResult(AlgorithmResult): """Phase Estimator Result.""" diff --git a/test/eigensolvers/test_vqd.py b/test/eigensolvers/test_vqd.py index 78b398c6..aec32483 100644 --- a/test/eigensolvers/test_vqd.py +++ b/test/eigensolvers/test_vqd.py @@ -20,7 +20,7 @@ from qiskit import QuantumCircuit from qiskit.circuit.library import TwoLocal, RealAmplitudes -from qiskit.primitives import Sampler, Estimator +from qiskit.primitives import Sampler, StatevectorEstimator as Estimator from qiskit.quantum_info import SparsePauliOp from qiskit_algorithms.eigensolvers import VQD, VQDResult @@ -57,8 +57,7 @@ def setUp(self): ) self.ry_wavefunction = TwoLocal(rotation_blocks="ry", entanglement_blocks="cz") - self.estimator = Estimator() - self.estimator_shots = Estimator(options={"shots": 1024, "seed": self.seed}) + self.estimator = Estimator(seed=self.seed) self.fidelity = ComputeUncompute(Sampler()) self.betas = [50, 50] @@ -92,11 +91,16 @@ def test_basic_operator(self, op): with self.subTest(msg="assert return ansatz is set"): job = self.estimator.run( - result.optimal_circuits, - [op] * len(result.optimal_points), - result.optimal_points, + [ + (circuits, op, optimal_points) + for (circuits, optimal_points) in zip( + result.optimal_circuits, result.optimal_points + ) + ] ) - np.testing.assert_array_almost_equal(job.result().values, result.eigenvalues, 6) + job_result = job.result() + eigenvalues = np.array([job_result[i].data.evs for i in range(len(result.eigenvalues))]) + np.testing.assert_array_almost_equal(eigenvalues, result.eigenvalues, 6) with self.subTest(msg="assert returned values are eigenvalues"): np.testing.assert_array_almost_equal( @@ -116,9 +120,7 @@ def test_beta_autoeval(self, op): """Test beta auto-evaluation for different operator types.""" with self.assertLogs(level="INFO") as logs: - vqd = VQD( - self.estimator_shots, self.fidelity, self.ryrz_wavefunction, optimizer=L_BFGS_B() - ) + vqd = VQD(self.estimator, self.fidelity, self.ryrz_wavefunction, optimizer=L_BFGS_B()) _ = vqd.compute_eigenvalues(op) # the first log message shows the value of beta[0] @@ -172,7 +174,7 @@ def store_intermediate_result(eval_count, parameters, mean, metadata, step): wavefunction = self.ry_wavefunction vqd = VQD( - estimator=self.estimator_shots, + estimator=self.estimator, fidelity=self.fidelity, ansatz=wavefunction, optimizer=optimizer, diff --git a/test/test_grover.py b/test/test_grover.py index 966c69a6..93080d3b 100644 --- a/test/test_grover.py +++ b/test/test_grover.py @@ -12,17 +12,17 @@ """Test Grover's algorithm.""" -import itertools import unittest +from itertools import product from test import QiskitAlgorithmsTestCase import numpy as np from ddt import data, ddt, idata, unpack - from qiskit import QuantumCircuit from qiskit.circuit.library import GroverOperator, PhaseOracle -from qiskit.primitives import Sampler +from qiskit.primitives import StatevectorSampler as Sampler from qiskit.quantum_info import Operator, Statevector +from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager from qiskit.utils.optionals import HAS_TWEEDLEDUM from qiskit_algorithms import AmplificationProblem, Grover @@ -72,9 +72,7 @@ def is_good_state(bitstr): # same as ``bitstr in ['01', '11']`` return bitstr[1] == "1" - possible_states = [ - "".join(list(map(str, item))) for item in itertools.product([0, 1], repeat=2) - ] + possible_states = ["".join(list(map(str, item))) for item in product([0, 1], repeat=2)] oracle = QuantumCircuit(2) problem = AmplificationProblem(oracle, is_good_state=is_good_state) @@ -91,50 +89,112 @@ class TestGrover(QiskitAlgorithmsTestCase): def setUp(self): super().setUp() - self._sampler = Sampler() - self._sampler_with_shots = Sampler(options={"shots": 1024, "seed": 123}) + self._sampler = Sampler(seed=123) @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum required for this test") - @data("ideal", "shots") - def test_implicit_phase_oracle_is_good_state(self, use_sampler): + @data( + [None, None], + [ + generate_preset_pass_manager(optimization_level=1, seed_transpiler=42), + {"num_processes": None}, + ], + ) + @unpack + def test_implicit_phase_oracle_is_good_state(self, transpiler, transpiler_options): """Test implicit default for is_good_state with PhaseOracle.""" - grover = self._prepare_grover(use_sampler) + grover = self._prepare_grover(transpiler=transpiler, transpiler_options=transpiler_options) oracle = PhaseOracle("x & y") problem = AmplificationProblem(oracle) result = grover.amplify(problem) self.assertEqual(result.top_measurement, "11") - @idata(itertools.product(["ideal", "shots"], [[1, 2, 3], None, 2])) + @idata( + product( + [[1, 2, 3], None, 2], + [ + [None, None], + [ + generate_preset_pass_manager(optimization_level=1, seed_transpiler=42), + {"num_processes": None}, + ], + ], + ) + ) @unpack - def test_iterations_with_good_state(self, use_sampler, iterations): + def test_iterations_with_good_state(self, iterations, transpiler_and_options): """Test the algorithm with different iteration types and with good state""" - grover = self._prepare_grover(use_sampler, iterations) + transpiler, transpiler_options = transpiler_and_options + grover = self._prepare_grover( + iterations, transpiler=transpiler, transpiler_options=transpiler_options + ) problem = AmplificationProblem(Statevector.from_label("111"), is_good_state=["111"]) result = grover.amplify(problem) self.assertEqual(result.top_measurement, "111") - @idata(itertools.product(["shots"], [[1, 2, 3], None, 2])) + @idata( + product( + [[1, 2, 3], None, 2], + [ + [None, None], + [ + generate_preset_pass_manager(optimization_level=1, seed_transpiler=42), + {"num_processes": None}, + ], + ], + ) + ) @unpack - def test_iterations_with_good_state_sample_from_iterations(self, use_sampler, iterations): + def test_iterations_with_good_state_sample_from_iterations( + self, iterations, transpiler_and_options + ): """Test the algorithm with different iteration types and with good state""" - grover = self._prepare_grover(use_sampler, iterations, sample_from_iterations=True) + transpiler, transpiler_options = transpiler_and_options + grover = self._prepare_grover( + iterations, + sample_from_iterations=True, + transpiler=transpiler, + transpiler_options=transpiler_options, + ) problem = AmplificationProblem(Statevector.from_label("111"), is_good_state=["111"]) result = grover.amplify(problem) self.assertEqual(result.top_measurement, "111") - @data("ideal", "shots") - def test_fixed_iterations_without_good_state(self, use_sampler): + @data( + [None, None], + [ + generate_preset_pass_manager(optimization_level=1, seed_transpiler=42), + {"num_processes": None}, + ], + ) + @unpack + def test_fixed_iterations_without_good_state(self, transpiler, transpiler_options): """Test the algorithm with iterations as an int and without good state""" - grover = self._prepare_grover(use_sampler, iterations=2) + grover = self._prepare_grover( + iterations=2, transpiler=transpiler, transpiler_options=transpiler_options + ) problem = AmplificationProblem(Statevector.from_label("111")) result = grover.amplify(problem) self.assertEqual(result.top_measurement, "111") - @idata(itertools.product(["ideal", "shots"], [[1, 2, 3], None])) + @idata( + product( + [[1, 2, 3], None], + [ + [None, None], + [ + generate_preset_pass_manager(optimization_level=1, seed_transpiler=42), + {"num_processes": None}, + ], + ], + ) + ) @unpack - def test_iterations_without_good_state(self, use_sampler, iterations): + def test_iterations_without_good_state(self, iterations, transpiler_and_options): """Test the correct error is thrown for none/list of iterations and without good state""" - grover = self._prepare_grover(use_sampler, iterations=iterations) + transpiler, transpiler_options = transpiler_and_options + grover = self._prepare_grover( + iterations=iterations, transpiler=transpiler, transpiler_options=transpiler_options + ) problem = AmplificationProblem(Statevector.from_label("111")) with self.assertRaisesRegex( @@ -142,8 +202,7 @@ def test_iterations_without_good_state(self, use_sampler, iterations): ): grover.amplify(problem) - @data("ideal", "shots") - def test_iterator(self, use_sampler): + def test_iterator(self): """Test running the algorithm on an iterator.""" # step-function iterator @@ -155,63 +214,57 @@ def iterator(): if count % wait == 0: value += 1 - grover = self._prepare_grover(use_sampler, iterations=iterator()) + grover = self._prepare_grover(iterations=iterator()) problem = AmplificationProblem(Statevector.from_label("111"), is_good_state=["111"]) result = grover.amplify(problem) self.assertEqual(result.top_measurement, "111") - @data("ideal", "shots") - def test_growth_rate(self, use_sampler): + def test_growth_rate(self): """Test running the algorithm on a growth rate""" - grover = self._prepare_grover(use_sampler, growth_rate=8 / 7) + grover = self._prepare_grover(growth_rate=8 / 7) problem = AmplificationProblem(Statevector.from_label("111"), is_good_state=["111"]) result = grover.amplify(problem) self.assertEqual(result.top_measurement, "111") - @data("ideal", "shots") - def test_max_num_iterations(self, use_sampler): + def test_max_num_iterations(self): """Test the iteration stops when the maximum number of iterations is reached.""" def zero(): while True: yield 0 - grover = self._prepare_grover(use_sampler, iterations=zero()) + grover = self._prepare_grover(iterations=zero()) n = 5 problem = AmplificationProblem(Statevector.from_label("1" * n), is_good_state=["1" * n]) result = grover.amplify(problem) self.assertEqual(len(result.iterations), 2**n) - @data("ideal", "shots") - def test_max_power(self, use_sampler): + def test_max_power(self): """Test the iteration stops when the maximum power is reached.""" lam = 10.0 - grover = self._prepare_grover(use_sampler, growth_rate=lam) + grover = self._prepare_grover(growth_rate=lam) problem = AmplificationProblem(Statevector.from_label("111"), is_good_state=["111"]) result = grover.amplify(problem) self.assertEqual(len(result.iterations), 0) - @data("ideal", "shots") - def test_run_circuit_oracle(self, use_sampler): + def test_run_circuit_oracle(self): """Test execution with a quantum circuit oracle""" oracle = QuantumCircuit(2) oracle.cz(0, 1) problem = AmplificationProblem(oracle, is_good_state=["11"]) - grover = self._prepare_grover(use_sampler) + grover = self._prepare_grover() result = grover.amplify(problem) self.assertIn(result.top_measurement, ["11"]) - @data("ideal", "shots") - def test_run_state_vector_oracle(self, use_sampler): + def test_run_state_vector_oracle(self): """Test execution with a state vector oracle""" mark_state = Statevector.from_label("11") problem = AmplificationProblem(mark_state, is_good_state=["11"]) - grover = self._prepare_grover(use_sampler) + grover = self._prepare_grover() result = grover.amplify(problem) self.assertIn(result.top_measurement, ["11"]) - @data("ideal", "shots") - def test_run_custom_grover_operator(self, use_sampler): + def test_run_custom_grover_operator(self): """Test execution with a grover operator oracle""" oracle = QuantumCircuit(2) oracle.cz(0, 1) @@ -219,7 +272,7 @@ def test_run_custom_grover_operator(self, use_sampler): problem = AmplificationProblem( oracle=oracle, grover_operator=grover_op, is_good_state=["11"] ) - grover = self._prepare_grover(use_sampler) + grover = self._prepare_grover() result = grover.amplify(problem) self.assertIn(result.top_measurement, ["11"]) @@ -247,14 +300,13 @@ def test_construct_circuit(self): self.assertTrue(Operator(constructed).equiv(Operator(expected))) - @data("ideal", "shots") - def test_circuit_result(self, use_sampler): + def test_circuit_result(self): """Test circuit_result""" oracle = QuantumCircuit(2) oracle.cz(0, 1) # is_good_state=['00'] is intentionally selected to obtain a list of results problem = AmplificationProblem(oracle, is_good_state=["00"]) - grover = self._prepare_grover(use_sampler, iterations=[1, 2, 3, 4]) + grover = self._prepare_grover(iterations=[1, 2, 3, 4]) result = grover.amplify(problem) @@ -267,23 +319,21 @@ def test_circuit_result(self, use_sampler): self.assertTupleEqual(keys, ("00", "01", "10", "11")) np.testing.assert_allclose(values, [0.25, 0.25, 0.25, 0.25], atol=0.2) - @data("ideal", "shots") - def test_max_probability(self, use_sampler): + def test_max_probability(self): """Test max_probability""" oracle = QuantumCircuit(2) oracle.cz(0, 1) problem = AmplificationProblem(oracle, is_good_state=["11"]) - grover = self._prepare_grover(use_sampler) + grover = self._prepare_grover() result = grover.amplify(problem) self.assertAlmostEqual(result.max_probability, 1.0) @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum required for this test") - @data("ideal", "shots") - def test_oracle_evaluation(self, use_sampler): + def test_oracle_evaluation(self): """Test oracle_evaluation for PhaseOracle""" oracle = PhaseOracle("x1 & x2 & (not x3)") problem = AmplificationProblem(oracle, is_good_state=oracle.evaluate_bitstring) - grover = self._prepare_grover(use_sampler) + grover = self._prepare_grover() result = grover.amplify(problem) self.assertTrue(result.oracle_evaluation) self.assertEqual("011", result.top_measurement) @@ -295,26 +345,22 @@ def test_sampler_setter(self): self.assertEqual(grover.sampler, self._sampler) def _prepare_grover( - self, use_sampler, iterations=None, growth_rate=None, sample_from_iterations=False + self, + iterations=None, + growth_rate=None, + sample_from_iterations=False, + transpiler=None, + transpiler_options=None, ): """Prepare Grover instance for test""" - if use_sampler == "ideal": - grover = Grover( - sampler=self._sampler, - iterations=iterations, - growth_rate=growth_rate, - sample_from_iterations=sample_from_iterations, - ) - elif use_sampler == "shots": - grover = Grover( - sampler=self._sampler_with_shots, - iterations=iterations, - growth_rate=growth_rate, - sample_from_iterations=sample_from_iterations, - ) - else: - raise RuntimeError("Unexpected `use_sampler` value {use_sampler}") - return grover + return Grover( + sampler=self._sampler, + iterations=iterations, + growth_rate=growth_rate, + sample_from_iterations=sample_from_iterations, + transpiler=transpiler, + transpiler_options=transpiler_options, + ) if __name__ == "__main__": diff --git a/test/test_phase_estimator.py b/test/test_phase_estimator.py index 1b7ca114..ebed64fb 100644 --- a/test/test_phase_estimator.py +++ b/test/test_phase_estimator.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2018, 2023. +# (C) Copyright IBM 2018, 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 @@ -11,22 +11,24 @@ # that they have been altered from the originals. """Test phase estimation""" - import unittest +from itertools import product from test import QiskitAlgorithmsTestCase -from ddt import ddt, data, unpack + import numpy as np -from qiskit.circuit.library import ZGate, XGate, HGate, IGate -from qiskit.quantum_info import Pauli, SparsePauliOp, Statevector, Operator -from qiskit.synthesis import MatrixExponential, SuzukiTrotter -from qiskit.primitives import Sampler +from ddt import ddt, data, unpack from qiskit import QuantumCircuit +from qiskit.circuit.library import HGate, XGate, IGate, ZGate +from qiskit.primitives import StatevectorSampler as Sampler +from qiskit.quantum_info import SparsePauliOp, Pauli, Statevector, Operator +from qiskit.synthesis import MatrixExponential, SuzukiTrotter +from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager -from qiskit_algorithms import PhaseEstimationScale -from qiskit_algorithms.phase_estimators import ( - PhaseEstimation, +from qiskit_algorithms import ( HamiltonianPhaseEstimation, IterativePhaseEstimation, + PhaseEstimation, + PhaseEstimationScale, ) @@ -42,11 +44,12 @@ def hamiltonian_pe_sampler( num_evaluation_qubits=6, evolution=None, bound=None, + pass_manager=None, ): """Run HamiltonianPhaseEstimation and return result with all phases.""" - sampler = Sampler() + sampler = Sampler(default_shots=10_000, seed=42) phase_est = HamiltonianPhaseEstimation( - num_evaluation_qubits=num_evaluation_qubits, sampler=sampler + num_evaluation_qubits=num_evaluation_qubits, sampler=sampler, transpiler=pass_manager ) result = phase_est.estimate( hamiltonian=hamiltonian, @@ -56,13 +59,21 @@ def hamiltonian_pe_sampler( ) return result - @data(MatrixExponential(), SuzukiTrotter(reps=4)) - def test_pauli_sum_1_sampler(self, evolution): + @data( + *product( + (MatrixExponential(), SuzukiTrotter(reps=4)), + (None, generate_preset_pass_manager(optimization_level=1, seed_transpiler=42)), + ) + ) + @unpack + def test_pauli_sum_1_sampler(self, evolution, pass_manager): """Two eigenvalues from Pauli sum with X, Z""" hamiltonian = SparsePauliOp.from_list([("X", 0.5), ("Z", 1)]) state_preparation = QuantumCircuit(1).compose(HGate()) - result = self.hamiltonian_pe_sampler(hamiltonian, state_preparation, evolution=evolution) + result = self.hamiltonian_pe_sampler( + hamiltonian, state_preparation, evolution=evolution, pass_manager=pass_manager + ) phase_dict = result.filter_phases(0.162, as_float=True) phases = list(phase_dict.keys()) phases.sort() @@ -70,13 +81,21 @@ def test_pauli_sum_1_sampler(self, evolution): self.assertAlmostEqual(phases[0], -1.125, delta=0.001) self.assertAlmostEqual(phases[1], 1.125, delta=0.001) - @data(MatrixExponential(), SuzukiTrotter(reps=3)) - def test_pauli_sum_2_sampler(self, evolution): + @data( + *product( + (MatrixExponential(), SuzukiTrotter(reps=3)), + (None, generate_preset_pass_manager(optimization_level=1, seed_transpiler=42)), + ) + ) + @unpack + def test_pauli_sum_2_sampler(self, evolution, pass_manager): """Two eigenvalues from Pauli sum with X, Y, Z""" hamiltonian = SparsePauliOp.from_list([("X", 0.5), ("Z", 1), ("Y", 1)]) state_preparation = None - result = self.hamiltonian_pe_sampler(hamiltonian, state_preparation, evolution=evolution) + result = self.hamiltonian_pe_sampler( + hamiltonian, state_preparation, evolution=evolution, pass_manager=pass_manager + ) phase_dict = result.filter_phases(0.1, as_float=True) phases = list(phase_dict.keys()) phases.sort() @@ -84,12 +103,15 @@ def test_pauli_sum_2_sampler(self, evolution): self.assertAlmostEqual(phases[0], -1.484, delta=0.001) self.assertAlmostEqual(phases[1], 1.484, delta=0.001) - def test_single_pauli_op_sampler(self): + @data(None, generate_preset_pass_manager(optimization_level=1, seed_transpiler=42)) + def test_single_pauli_op_sampler(self, pass_manager): """Two eigenvalues from Pauli sum with X, Y, Z""" hamiltonian = SparsePauliOp(Pauli("Z")) state_preparation = None - result = self.hamiltonian_pe_sampler(hamiltonian, state_preparation, evolution=None) + result = self.hamiltonian_pe_sampler( + hamiltonian, state_preparation, evolution=None, pass_manager=pass_manager + ) eigv = result.most_likely_eigenvalue with self.subTest("First eigenvalue"): self.assertAlmostEqual(eigv, 1.0, delta=0.001) @@ -102,10 +124,16 @@ def test_single_pauli_op_sampler(self): self.assertAlmostEqual(eigv, -0.98, delta=0.01) @data( - (Statevector(QuantumCircuit(2).compose(IGate()).compose(HGate()))), - (QuantumCircuit(2).compose(IGate()).compose(HGate())), + *product( + ( + (Statevector(QuantumCircuit(2).compose(IGate()).compose(HGate()))), + (QuantumCircuit(2).compose(IGate()).compose(HGate())), + ), + (None, generate_preset_pass_manager(optimization_level=1, seed_transpiler=42)), + ) ) - def test_H2_hamiltonian_sampler(self, state_preparation): + @unpack + def test_H2_hamiltonian_sampler(self, state_preparation, pass_manager): """Test H2 hamiltonian""" hamiltonian = SparsePauliOp.from_list( @@ -119,7 +147,9 @@ def test_H2_hamiltonian_sampler(self, state_preparation): ) evo = SuzukiTrotter(reps=4) - result = self.hamiltonian_pe_sampler(hamiltonian, state_preparation, evolution=evo) + result = self.hamiltonian_pe_sampler( + hamiltonian, state_preparation, evolution=evo, pass_manager=pass_manager + ) with self.subTest("Most likely eigenvalues"): self.assertAlmostEqual(result.most_likely_eigenvalue, -1.855, delta=0.001) with self.subTest("Most likely phase"): @@ -131,14 +161,15 @@ def test_H2_hamiltonian_sampler(self, state_preparation): self.assertAlmostEqual(phases[1], -1.2376, delta=0.001) self.assertAlmostEqual(phases[2], -0.8979, delta=0.001) - def test_matrix_evolution_sampler(self): + @data(None, generate_preset_pass_manager(optimization_level=1, seed_transpiler=42)) + def test_matrix_evolution_sampler(self, pass_manager): """1Q Hamiltonian with MatrixEvolution""" # hamiltonian = PauliSumOp(SparsePauliOp.from_list([("X", 0.5), ("Y", 0.6), ("I", 0.7)])) hamiltonian = SparsePauliOp.from_list([("X", 0.5), ("Y", 0.6), ("I", 0.7)]) state_preparation = None result = self.hamiltonian_pe_sampler( - hamiltonian, state_preparation, evolution=MatrixExponential() + hamiltonian, state_preparation, evolution=MatrixExponential(), pass_manager=pass_manager ) phase_dict = result.filter_phases(0.2, as_float=True) phases = sorted(phase_dict.keys()) @@ -150,6 +181,8 @@ def test_matrix_evolution_sampler(self): class TestPhaseEstimation(QiskitAlgorithmsTestCase): """Evolution tests.""" + pm = generate_preset_pass_manager(optimization_level=1, seed_transpiler=42) + # sampler tests def one_phase_sampler( self, @@ -158,21 +191,26 @@ def one_phase_sampler( phase_estimator=None, num_iterations=6, shots=None, + pass_manager=None, ): """Run phase estimation with operator, eigenvalue pair `unitary_circuit`, `state_preparation`. Return the estimated phase as a value in :math:`[0,1)`. """ + if shots is not None: - options = {"shots": shots} + sampler = Sampler(default_shots=shots, seed=42) else: - options = {} - sampler = Sampler(options=options) + sampler = Sampler(seed=42) if phase_estimator is None: phase_estimator = IterativePhaseEstimation if phase_estimator == IterativePhaseEstimation: - p_est = IterativePhaseEstimation(num_iterations=num_iterations, sampler=sampler) + p_est = IterativePhaseEstimation( + num_iterations=num_iterations, sampler=sampler, transpiler=pass_manager + ) elif phase_estimator == PhaseEstimation: - p_est = PhaseEstimation(num_evaluation_qubits=6, sampler=sampler) + p_est = PhaseEstimation( + num_evaluation_qubits=6, sampler=sampler, transpiler=pass_manager + ) else: raise ValueError("Unrecognized phase_estimator") result = p_est.estimate(unitary=unitary_circuit, state_preparation=state_preparation) @@ -180,102 +218,111 @@ def one_phase_sampler( return phase @data( - (QuantumCircuit(1).compose(XGate()), 0.5, None, IterativePhaseEstimation), - (QuantumCircuit(1).compose(XGate()), 0.5, 1000, IterativePhaseEstimation), - (None, 0.0, 1000, IterativePhaseEstimation), - (QuantumCircuit(1).compose(XGate()), 0.5, 1000, PhaseEstimation), - (None, 0.0, 1000, PhaseEstimation), - (QuantumCircuit(1).compose(XGate()), 0.5, None, PhaseEstimation), + *product( + ((None, 0.0), (QuantumCircuit(1).compose(XGate()), 0.5)), + (None, 1000), + (IterativePhaseEstimation, PhaseEstimation), + (None, pm), + ) ) @unpack - def test_qpe_Z_sampler(self, state_preparation, expected_phase, shots, phase_estimator): + def test_qpe_Z_sampler( + self, state_preparation_and_expected_phase, shots, phase_estimator, pass_manager + ): """eigenproblem Z, |0> and |1>""" + state_preparation, expected_phase = state_preparation_and_expected_phase unitary_circuit = QuantumCircuit(1).compose(ZGate()) phase = self.one_phase_sampler( unitary_circuit, state_preparation=state_preparation, phase_estimator=phase_estimator, shots=shots, + pass_manager=pass_manager, ) self.assertEqual(phase, expected_phase) @data( - (QuantumCircuit(1).compose(HGate()), 0.0, IterativePhaseEstimation), - (QuantumCircuit(1).compose(HGate()).compose(ZGate()), 0.5, IterativePhaseEstimation), - (QuantumCircuit(1).compose(HGate()), 0.0, PhaseEstimation), - (QuantumCircuit(1).compose(HGate()).compose(ZGate()), 0.5, PhaseEstimation), + *product( + ( + (QuantumCircuit(1).compose(HGate()), 0.0), + (QuantumCircuit(1).compose(HGate()).compose(ZGate()), 0.5), + ), + (IterativePhaseEstimation, PhaseEstimation), + (None, pm), + ) ) @unpack - def test_qpe_X_plus_minus_sampler(self, state_preparation, expected_phase, phase_estimator): + def test_qpe_X_plus_minus_sampler( + self, state_preparation_and_expected_phase, phase_estimator, pass_manager + ): """eigenproblem X, (|+>, |->)""" + state_preparation, expected_phase = state_preparation_and_expected_phase unitary_circuit = QuantumCircuit(1).compose(XGate()) phase = self.one_phase_sampler( - unitary_circuit, - state_preparation, - phase_estimator, + unitary_circuit, state_preparation, phase_estimator, pass_manager=pass_manager ) self.assertEqual(phase, expected_phase) @data( - (QuantumCircuit(1).compose(XGate()), 0.125, IterativePhaseEstimation), - (QuantumCircuit(1).compose(IGate()), 0.875, IterativePhaseEstimation), - (QuantumCircuit(1).compose(XGate()), 0.125, PhaseEstimation), - (QuantumCircuit(1).compose(IGate()), 0.875, PhaseEstimation), + *product( + ( + (QuantumCircuit(1).compose(XGate()), 0.125), + (QuantumCircuit(1).compose(IGate()), 0.875), + ), + (IterativePhaseEstimation, PhaseEstimation), + (None, pm), + ) ) @unpack - def test_qpe_RZ_sampler(self, state_preparation, expected_phase, phase_estimator): + def test_qpe_RZ_sampler( + self, state_preparation_and_expected_phase, phase_estimator, pass_manager + ): """eigenproblem RZ, (|0>, |1>)""" + state_preparation, expected_phase = state_preparation_and_expected_phase alpha = np.pi / 2 unitary_circuit = QuantumCircuit(1) unitary_circuit.rz(alpha, 0) phase = self.one_phase_sampler( - unitary_circuit, - state_preparation, - phase_estimator, + unitary_circuit, state_preparation, phase_estimator, pass_manager=pass_manager ) self.assertEqual(phase, expected_phase) @data( - ( - QuantumCircuit(2).compose(XGate(), qubits=[0]).compose(XGate(), qubits=[1]), - 0.25, - IterativePhaseEstimation, - ), - ( - QuantumCircuit(2).compose(IGate(), qubits=[0]).compose(XGate(), qubits=[1]), - 0.125, - IterativePhaseEstimation, - ), - ( - QuantumCircuit(2).compose(XGate(), qubits=[0]).compose(XGate(), qubits=[1]), - 0.25, - PhaseEstimation, - ), - ( - QuantumCircuit(2).compose(IGate(), qubits=[0]).compose(XGate(), qubits=[1]), - 0.125, - PhaseEstimation, - ), + *product( + ( + (QuantumCircuit(2).compose(XGate(), qubits=[0]).compose(XGate(), qubits=[1]), 0.25), + ( + QuantumCircuit(2).compose(IGate(), qubits=[0]).compose(XGate(), qubits=[1]), + 0.125, + ), + ), + (IterativePhaseEstimation, PhaseEstimation), + (None, pm), + ) ) @unpack - def test_qpe_two_qubit_unitary(self, state_preparation, expected_phase, phase_estimator): + def test_qpe_two_qubit_unitary( + self, state_preparation_and_expected_phase, phase_estimator, pass_manager + ): """two qubit unitary T ^ T""" + state_preparation, expected_phase = state_preparation_and_expected_phase unitary_circuit = QuantumCircuit(2) unitary_circuit.t(0) unitary_circuit.t(1) phase = self.one_phase_sampler( - unitary_circuit, - state_preparation, - phase_estimator, + unitary_circuit, state_preparation, phase_estimator, pass_manager=pass_manager ) self.assertEqual(phase, expected_phase) - def test_check_num_iterations_sampler(self): + @data(None, pm) + def test_check_num_iterations_sampler(self, pass_manager): """test check for num_iterations greater than zero""" unitary_circuit = QuantumCircuit(1).compose(XGate()) state_preparation = None with self.assertRaises(ValueError): - self.one_phase_sampler(unitary_circuit, state_preparation, num_iterations=-1) + self.one_phase_sampler( + unitary_circuit, state_preparation, num_iterations=-1, pass_manager=pass_manager + ) def test_phase_estimation_scale_from_operator(self): """test that PhaseEstimationScale from_pauli_sum works with Operator""" @@ -291,11 +338,14 @@ def phase_estimation_sampler( state_preparation=None, num_evaluation_qubits=6, construct_circuit=False, + pass_manager=None, ): """Run phase estimation with operator, eigenvalue pair `unitary_circuit`, `state_preparation`. Return all results """ - phase_est = PhaseEstimation(num_evaluation_qubits=num_evaluation_qubits, sampler=sampler) + phase_est = PhaseEstimation( + num_evaluation_qubits=num_evaluation_qubits, sampler=sampler, transpiler=pass_manager + ) if construct_circuit: pe_circuit = phase_est.construct_circuit(unitary_circuit, state_preparation) result = phase_est.estimate_from_pe_circuit(pe_circuit) @@ -305,17 +355,19 @@ def phase_estimation_sampler( ) return result - @data(True, False) - def test_qpe_Zplus_sampler(self, construct_circuit): + @data(*product((True, False), (None, pm))) + @unpack + def test_qpe_Zplus_sampler(self, construct_circuit, pass_manager): """superposition eigenproblem Z, |+>""" unitary_circuit = QuantumCircuit(1).compose(ZGate()) state_preparation = QuantumCircuit(1).compose(HGate()) # prepare |+> - sampler = Sampler() + sampler = Sampler(default_shots=10_000, seed=42) result = self.phase_estimation_sampler( unitary_circuit, sampler, state_preparation, construct_circuit=construct_circuit, + pass_manager=pass_manager, ) phases = result.filter_phases(1e-15, as_float=True) @@ -323,7 +375,7 @@ def test_qpe_Zplus_sampler(self, construct_circuit): self.assertEqual(list(phases.keys()), [0.0, 0.5]) with self.subTest("test phases has correct probabilities"): - np.testing.assert_allclose(list(phases.values()), [0.5, 0.5]) + np.testing.assert_allclose(list(phases.values()), [0.5, 0.5], atol=1e-2, rtol=1e-2) with self.subTest("test bitstring representation"): phases = result.filter_phases(1e-15, as_float=False)