Skip to content

Commit

Permalink
Changed PassManager to more generic transpiler
Browse files Browse the repository at this point in the history
  • Loading branch information
tnemoz committed Aug 12, 2024
1 parent e390037 commit da40425
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 25 deletions.
27 changes: 27 additions & 0 deletions qiskit_algorithms/custom_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 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 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
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,16 @@

from __future__ import annotations


from qiskit import QuantumCircuit
from qiskit.circuit.library import PauliEvolutionGate
from qiskit.passmanager import BasePassManager
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:
Expand Down Expand Up @@ -85,19 +84,21 @@ def __init__(
self,
num_evaluation_qubits: int,
sampler: BaseSamplerV2 | None = None,
pass_manager: BasePassManager | 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.
pass_manager: A pass manager to use to transpile 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.
"""
self._phase_estimation = PhaseEstimation(
num_evaluation_qubits=num_evaluation_qubits,
sampler=sampler,
pass_manager=pass_manager,
transpiler=transpiler,
)

def _get_scale(self, hamiltonian, bound=None) -> PhaseEstimationScale:
Expand Down
10 changes: 6 additions & 4 deletions qiskit_algorithms/phase_estimators/ipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@

from qiskit.circuit import QuantumCircuit, QuantumRegister
from qiskit.circuit.classicalregister import ClassicalRegister
from qiskit.passmanager import BasePassManager
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):
Expand All @@ -42,13 +42,15 @@ def __init__(
self,
num_iterations: int,
sampler: BaseSamplerV2 | None = None,
pass_manager: BasePassManager | 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.
pass_manager: A pass manager to use to transpile 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.
Raises:
ValueError: if num_iterations is not greater than zero.
Expand All @@ -61,7 +63,7 @@ def __init__(
raise ValueError("`num_iterations` must be greater than zero.")
self._num_iterations = num_iterations
self._sampler = sampler
self._pass_manager = pass_manager
self._pass_manager = transpiler

def construct_circuit(
self,
Expand Down
10 changes: 6 additions & 4 deletions qiskit_algorithms/phase_estimators/phase_estimation.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
from qiskit import circuit
from qiskit.circuit import QuantumCircuit
from qiskit.circuit.classicalregister import ClassicalRegister
from qiskit.passmanager import BasePassManager
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):
Expand Down Expand Up @@ -84,14 +84,16 @@ def __init__(
self,
num_evaluation_qubits: int,
sampler: BaseSamplerV2 | None = None,
pass_manager: BasePassManager | 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.
pass_manager: A pass manager to use to transpile 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.
Raises:
AlgorithmError: If a sampler is not provided
Expand All @@ -104,7 +106,7 @@ def __init__(
self._num_evaluation_qubits = num_evaluation_qubits

self._sampler = sampler
self._pass_manager = pass_manager
self._pass_manager = transpiler

def construct_circuit(
self, unitary: QuantumCircuit, state_preparation: QuantumCircuit | None = None
Expand Down
30 changes: 19 additions & 11 deletions test/test_phase_estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@
class TestHamiltonianPhaseEstimation(QiskitAlgorithmsTestCase):
"""Tests for obtaining eigenvalues from phase estimation"""

pm = generate_preset_pass_manager(optimization_level=1, seed_transpiler=42)

# sampler tests
def hamiltonian_pe_sampler(
self,
Expand All @@ -51,7 +49,7 @@ def hamiltonian_pe_sampler(
"""Run HamiltonianPhaseEstimation and return result with all phases."""
sampler = Sampler(default_shots=10_000, seed=42)
phase_est = HamiltonianPhaseEstimation(
num_evaluation_qubits=num_evaluation_qubits, sampler=sampler, pass_manager=pass_manager
num_evaluation_qubits=num_evaluation_qubits, sampler=sampler, transpiler=pass_manager
)
result = phase_est.estimate(
hamiltonian=hamiltonian,
Expand All @@ -61,7 +59,12 @@ def hamiltonian_pe_sampler(
)
return result

@data(*product((MatrixExponential(), SuzukiTrotter(reps=4)), (None, pm)))
@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"""
Expand All @@ -78,7 +81,12 @@ def test_pauli_sum_1_sampler(self, evolution, pass_manager):
self.assertAlmostEqual(phases[0], -1.125, delta=0.001)
self.assertAlmostEqual(phases[1], 1.125, delta=0.001)

@data(*product((MatrixExponential(), SuzukiTrotter(reps=3)), (None, pm)))
@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"""
Expand All @@ -95,7 +103,7 @@ def test_pauli_sum_2_sampler(self, evolution, pass_manager):
self.assertAlmostEqual(phases[0], -1.484, delta=0.001)
self.assertAlmostEqual(phases[1], 1.484, delta=0.001)

@data(None, pm)
@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"))
Expand All @@ -121,7 +129,7 @@ def test_single_pauli_op_sampler(self, pass_manager):
(Statevector(QuantumCircuit(2).compose(IGate()).compose(HGate()))),
(QuantumCircuit(2).compose(IGate()).compose(HGate())),
),
(None, pm),
(None, generate_preset_pass_manager(optimization_level=1, seed_transpiler=42)),
)
)
@unpack
Expand Down Expand Up @@ -153,7 +161,7 @@ def test_H2_hamiltonian_sampler(self, state_preparation, pass_manager):
self.assertAlmostEqual(phases[1], -1.2376, delta=0.001)
self.assertAlmostEqual(phases[2], -0.8979, delta=0.001)

@data(None, pm)
@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)]))
Expand Down Expand Up @@ -197,11 +205,11 @@ def one_phase_sampler(
phase_estimator = IterativePhaseEstimation
if phase_estimator == IterativePhaseEstimation:
p_est = IterativePhaseEstimation(
num_iterations=num_iterations, sampler=sampler, pass_manager=pass_manager
num_iterations=num_iterations, sampler=sampler, transpiler=pass_manager
)
elif phase_estimator == PhaseEstimation:
p_est = PhaseEstimation(
num_evaluation_qubits=6, sampler=sampler, pass_manager=pass_manager
num_evaluation_qubits=6, sampler=sampler, transpiler=pass_manager
)
else:
raise ValueError("Unrecognized phase_estimator")
Expand Down Expand Up @@ -336,7 +344,7 @@ def phase_estimation_sampler(
`state_preparation`. Return all results
"""
phase_est = PhaseEstimation(
num_evaluation_qubits=num_evaluation_qubits, sampler=sampler, pass_manager=pass_manager
num_evaluation_qubits=num_evaluation_qubits, sampler=sampler, transpiler=pass_manager
)
if construct_circuit:
pe_circuit = phase_est.construct_circuit(unitary_circuit, state_preparation)
Expand Down

0 comments on commit da40425

Please sign in to comment.