Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added V2 and ISA support #197

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
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, List, Protocol, Union

from qiskit import QuantumCircuit

_Circuits = Union[List[QuantumCircuit], QuantumCircuit]
Copy link
Member

@woodsp-ibm woodsp-ibm Aug 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use list in 3.8 - which arguably is better and what we have tried to do elsewhere. You do need to add a future import to have that work from __future__ import annotations. You can see examples in this repo e.g. in this file which does that and uses it https://github.com/qiskit-community/qiskit-algorithms/blob/main/qiskit_algorithms/time_evolvers/pvqd/pvqd_result.py Though I am not sure here with custom types defs though as I see Union and elsewhere we try and use the | nowadays instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially went for

_Circuits = list[QuantumCircuit] | QuantumCircuit

but then make lint complained with:

************* Module qiskit_algorithms.custom_types
qiskit_algorithms/custom_types.py:21:12: E1131: unsupported operand type(s) for | (unsupported-binary-operation)

which is why I went for Union instead of |. Or is there a workaround I don't know of?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That future import is needed for | as well - but if you had that import then as I mentioned I was not not sure here as I recall having issues when defining types before using these newer aspects where we had to have them defined with the former Typing constructs. If you search in this repo you will find just a few Union hits where they are pretty much limited to being used in type defines like this - I also see List being used in the ones here so it may well be the same issue with that. I commented mostly as a saw the commit that changed things to fix it for lint in 3.8 from list to List that was all the change I did not see a from future import being removed which is needed to type things this newer way in 3.8.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One further comment around 3.8. Qiskit has already deprecated support for Python 3.8 and support will be removed in 1.3.0 which is due in Nov. Python 3.8 EOL is Oct this year so among the changes here it could be a choice to drop 3.8 support too along with these changes. To drop this would evidently also include dropping it from CI tests etc and bumping things to run at 3.9 min.

In talking about versions I will note 3.13 is planned to be available beginning of Oct. There is an issue on Qiskit Qiskit/qiskit#12903 to support this so again depending on timeline.... but hopefully that's just adding it as supported and to CI to test when dependencies are there and it just works!



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
@@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
22 changes: 18 additions & 4 deletions qiskit_algorithms/phase_estimators/ipe.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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):
Expand All @@ -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.
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand Down
25 changes: 18 additions & 7 deletions qiskit_algorithms/phase_estimators/phase_estimation.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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):
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -189,18 +195,23 @@ 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:
circuit_job = self._sampler.run([pe_circuit])
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(
Expand Down
6 changes: 1 addition & 5 deletions qiskit_algorithms/phase_estimators/phase_estimator.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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."""
Expand Down
Loading
Loading