Skip to content

Commit

Permalink
Fix mypy errors (algorithms) (Qiskit#8271)
Browse files Browse the repository at this point in the history
* Fix mypy errors (algorithms)

* Fix formating

Co-authored-by: Julien Gacon <gaconju@gmail.com>

* Remove ignores, fix imports and a couple of small bugs

* More fixes

* Update qiskit/algorithms/minimum_eigen_solvers/vqe.py

Co-authored-by: Julien Gacon <gaconju@gmail.com>

* More fixes

* Remove TODO

* Fix `eval_observables` with `Backend` parameter

* Fix some more annotations

* Fix some TODOs

* Get rid of typing imports where possible

* Fix annotation

* Fix wrong result population

* Fix result types for VQD

* Fix lint

* Remove trailing mypy comment

---------

Co-authored-by: Julien Gacon <gaconju@gmail.com>
  • Loading branch information
2 people authored and king-p3nguin committed May 22, 2023
1 parent c5ef3c4 commit ed0ed39
Show file tree
Hide file tree
Showing 88 changed files with 706 additions and 601 deletions.
37 changes: 18 additions & 19 deletions qiskit/algorithms/amplitude_amplifiers/amplification_problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
# that they have been altered from the originals.

"""The Amplification problem class."""
from __future__ import annotations

from typing import Optional, Callable, Any, Union, List
from collections.abc import Callable
from typing import Any

from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import GroverOperator
Expand All @@ -29,14 +31,12 @@ class AmplificationProblem:

def __init__(
self,
oracle: Union[QuantumCircuit, Statevector],
state_preparation: Optional[QuantumCircuit] = None,
grover_operator: Optional[QuantumCircuit] = None,
post_processing: Optional[Callable[[str], Any]] = None,
objective_qubits: Optional[Union[int, List[int]]] = None,
is_good_state: Optional[
Union[Callable[[str], bool], List[int], List[str], Statevector]
] = None,
oracle: QuantumCircuit | Statevector,
state_preparation: QuantumCircuit | None = None,
grover_operator: QuantumCircuit | None = None,
post_processing: Callable[[str], Any] | None = None,
objective_qubits: int | list[int] | None = None,
is_good_state: Callable[[str], bool] | list[int] | list[str] | Statevector | None = None,
) -> None:
r"""
Args:
Expand Down Expand Up @@ -68,7 +68,7 @@ def __init__(
self._is_good_state = None

@property
def oracle(self) -> Union[QuantumCircuit, Statevector]:
def oracle(self) -> QuantumCircuit | Statevector:
"""Return the oracle.
Returns:
Expand All @@ -77,7 +77,7 @@ def oracle(self) -> Union[QuantumCircuit, Statevector]:
return self._oracle

@oracle.setter
def oracle(self, oracle: Union[QuantumCircuit, Statevector]) -> None:
def oracle(self, oracle: QuantumCircuit | Statevector) -> None:
"""Set the oracle.
Args:
Expand All @@ -100,7 +100,7 @@ def state_preparation(self) -> QuantumCircuit:
return self._state_preparation

@state_preparation.setter
def state_preparation(self, state_preparation: Optional[QuantumCircuit]) -> None:
def state_preparation(self, state_preparation: QuantumCircuit | None) -> None:
r"""Set the :math:`\mathcal{A}` operator. If None, a layer of Hadamard gates is used.
Args:
Expand Down Expand Up @@ -130,7 +130,7 @@ def post_processing(self, post_processing: Callable[[str], Any]) -> None:
self._post_processing = post_processing

@property
def objective_qubits(self) -> List[int]:
def objective_qubits(self) -> list[int]:
"""The indices of the objective qubits.
Returns:
Expand All @@ -145,7 +145,7 @@ def objective_qubits(self) -> List[int]:
return self._objective_qubits

@objective_qubits.setter
def objective_qubits(self, objective_qubits: Optional[Union[int, List[int]]]) -> None:
def objective_qubits(self, objective_qubits: int | list[int] | None) -> None:
"""Set the objective qubits.
Args:
Expand All @@ -170,15 +170,14 @@ def is_good_state(self) -> Callable[[str], bool]:
return lambda bitstr: bitstr in self._is_good_state
else:
return lambda bitstr: all(
bitstr[good_index] == "1" # type:ignore
for good_index in self._is_good_state
bitstr[good_index] == "1" for good_index in self._is_good_state
)

return lambda bitstr: bitstr in self._is_good_state.probabilities_dict()

@is_good_state.setter
def is_good_state(
self, is_good_state: Union[Callable[[str], bool], List[int], List[str], Statevector]
self, is_good_state: Callable[[str], bool] | list[int] | list[str] | Statevector
) -> None:
"""Set the ``is_good_state`` function.
Expand All @@ -188,7 +187,7 @@ def is_good_state(
self._is_good_state = is_good_state

@property
def grover_operator(self) -> Optional[QuantumCircuit]:
def grover_operator(self) -> QuantumCircuit | None:
r"""Get the :math:`\mathcal{Q}` operator, or Grover operator.
If the Grover operator is not set, we try to build it from the :math:`\mathcal{A}` operator
Expand All @@ -203,7 +202,7 @@ def grover_operator(self) -> Optional[QuantumCircuit]:
return self._grover_operator

@grover_operator.setter
def grover_operator(self, grover_operator: Optional[QuantumCircuit]) -> None:
def grover_operator(self, grover_operator: QuantumCircuit | None) -> None:
r"""Set the :math:`\mathcal{Q}` operator.
If None, this operator is constructed from the ``oracle`` and ``state_preparation``.
Expand Down
17 changes: 9 additions & 8 deletions qiskit/algorithms/amplitude_amplifiers/amplitude_amplifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
# that they have been altered from the originals.

"""The interface for amplification algorithms and results."""
from __future__ import annotations

from abc import ABC, abstractmethod
from typing import Optional, Any, Union, Dict, List
from typing import Any

import numpy as np

Expand Down Expand Up @@ -43,14 +44,14 @@ class AmplitudeAmplifierResult(AlgorithmResult):

def __init__(self) -> None:
super().__init__()
self._top_measurement = None
self._top_measurement: str | None = None
self._assignment = None
self._oracle_evaluation = None
self._circuit_results = None
self._max_probability = None
self._oracle_evaluation: bool | None = None
self._circuit_results: list[np.ndarray] | list[dict[str, int]] | None = None
self._max_probability: float | None = None

@property
def top_measurement(self) -> Optional[str]:
def top_measurement(self) -> str | None:
"""The most frequently measured output as bitstring.
Returns:
Expand Down Expand Up @@ -106,12 +107,12 @@ def oracle_evaluation(self, value: bool) -> None:
self._oracle_evaluation = value

@property
def circuit_results(self) -> Optional[Union[List[np.ndarray], List[Dict[str, int]]]]:
def circuit_results(self) -> list[np.ndarray] | list[dict[str, int]] | None:
"""Return the circuit results. Can be a statevector or counts dictionary."""
return self._circuit_results

@circuit_results.setter
def circuit_results(self, value: Union[List[np.ndarray], List[Dict[str, int]]]) -> None:
def circuit_results(self, value: list[np.ndarray] | list[dict[str, int]]) -> None:
"""Set the circuit results."""
self._circuit_results = value

Expand Down
40 changes: 22 additions & 18 deletions qiskit/algorithms/amplitude_amplifiers/grover.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,21 @@
# that they have been altered from the originals.

"""Grover's search algorithm."""
from __future__ import annotations

import itertools
import operator
import warnings
from typing import Iterator, List, Optional, Union
from collections.abc import Iterator, Generator
from typing import Any

import numpy as np

from qiskit import ClassicalRegister, QuantumCircuit
from qiskit.algorithms.exceptions import AlgorithmError
from qiskit.primitives import BaseSampler
from qiskit.providers import Backend
from qiskit.quantum_info import partial_trace
from qiskit.quantum_info import partial_trace, Statevector
from qiskit.utils import QuantumInstance, algorithm_globals
from qiskit.utils.deprecation import deprecate_arg, deprecate_func

Expand Down Expand Up @@ -120,11 +122,11 @@ class Grover(AmplitudeAmplifier):
)
def __init__(
self,
iterations: Optional[Union[List[int], Iterator[int], int]] = None,
growth_rate: Optional[float] = None,
iterations: list[int] | Iterator[int] | int | None = None,
growth_rate: float | None = None,
sample_from_iterations: bool = False,
quantum_instance: Optional[Union[QuantumInstance, Backend]] = None,
sampler: Optional[BaseSampler] = None,
quantum_instance: QuantumInstance | Backend | None = None,
sampler: BaseSampler | None = None,
) -> None:
r"""
Args:
Expand Down Expand Up @@ -163,7 +165,9 @@ def __init__(

if growth_rate is not None:
# yield iterations ** 1, iterations ** 2, etc. and casts to int
self._iterations = (int(growth_rate**x) for x in itertools.count(1))
self._iterations: Generator[int, None, None] | list[int] = (
int(growth_rate**x) for x in itertools.count(1)
)
elif isinstance(iterations, int):
self._iterations = [iterations]
else:
Expand All @@ -178,7 +182,7 @@ def __init__(
sampler = quantum_instance
quantum_instance = None

self._quantum_instance = None
self._quantum_instance: QuantumInstance | None = None
if quantum_instance is not None:
with warnings.catch_warnings():
warnings.simplefilter("ignore", category=PendingDeprecationWarning)
Expand All @@ -191,7 +195,7 @@ def __init__(

@property
@deprecate_func(since="0.23.0", pending=True, is_property=True)
def quantum_instance(self) -> Optional[QuantumInstance]:
def quantum_instance(self) -> QuantumInstance | None:
r"""Pending deprecation\; Get the quantum instance.
Returns:
Expand All @@ -201,7 +205,7 @@ def quantum_instance(self) -> Optional[QuantumInstance]:

@quantum_instance.setter
@deprecate_func(since="0.23.0", pending=True, is_property=True)
def quantum_instance(self, quantum_instance: Union[QuantumInstance, Backend]) -> None:
def quantum_instance(self, quantum_instance: QuantumInstance | Backend) -> None:
r"""Pending deprecation\; Set quantum instance.
Args:
Expand All @@ -212,7 +216,7 @@ def quantum_instance(self, quantum_instance: Union[QuantumInstance, Backend]) ->
self._quantum_instance = quantum_instance

@property
def sampler(self) -> Optional[BaseSampler]:
def sampler(self) -> BaseSampler | None:
"""Get the sampler.
Returns:
Expand Down Expand Up @@ -254,7 +258,7 @@ def amplify(self, amplification_problem: AmplificationProblem) -> "GroverResult"
if isinstance(self._iterations, list):
max_iterations = len(self._iterations)
max_power = np.inf # no cap on the power
iterator = iter(self._iterations)
iterator: Iterator[int] = iter(self._iterations)
else:
max_iterations = max(10, 2**amplification_problem.oracle.num_qubits)
max_power = np.ceil(
Expand Down Expand Up @@ -282,7 +286,7 @@ def amplify(self, amplification_problem: AmplificationProblem) -> "GroverResult"

# sample from [0, power) if specified
if self._sample_from_iterations:
power = algorithm_globals.random.randint(power)
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)
Expand All @@ -294,7 +298,7 @@ def amplify(self, amplification_problem: AmplificationProblem) -> "GroverResult"
raise AlgorithmError("Sampler job failed.") from exc

num_bits = len(amplification_problem.objective_qubits)
circuit_results = {
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])
Expand Down Expand Up @@ -373,7 +377,7 @@ def optimal_num_iterations(num_solutions: int, num_qubits: int) -> int:
return round(np.arccos(amplitude) / (2 * np.arcsin(amplitude)))

def construct_circuit(
self, problem: AmplificationProblem, power: Optional[int] = None, measurement: bool = False
self, problem: AmplificationProblem, power: int | None = None, measurement: bool = False
) -> QuantumCircuit:
"""Construct the circuit for Grover's algorithm with ``power`` Grover operators.
Expand Down Expand Up @@ -412,10 +416,10 @@ class GroverResult(AmplitudeAmplifierResult):

def __init__(self) -> None:
super().__init__()
self._iterations = None
self._iterations: list[int] | None = None

@property
def iterations(self) -> List[int]:
def iterations(self) -> list[int]:
"""All the powers of the Grover operator that have been tried.
Returns:
Expand All @@ -424,7 +428,7 @@ def iterations(self) -> List[int]:
return self._iterations

@iterations.setter
def iterations(self, value: List[int]) -> None:
def iterations(self, value: list[int]) -> None:
"""Set the powers of the Grover operator that have been tried.
Args:
Expand Down
35 changes: 18 additions & 17 deletions qiskit/algorithms/amplitude_estimators/ae.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ def evaluate_measurements(
self,
circuit_results: dict[str, int] | np.ndarray,
threshold: float = 1e-6,
) -> tuple[dict[int, float], dict[float, float]]:
) -> tuple[dict[float, float], dict[int, float]]:
"""Evaluate the results from the circuit simulation.
Given the probabilities from statevector simulation of the QAE circuit, compute the
Expand Down Expand Up @@ -249,10 +249,10 @@ def _evaluate_quasi_probabilities_results(self, circuit_results):

return samples, measurements

def _evaluate_count_results(self, counts):
def _evaluate_count_results(self, counts) -> tuple[dict[float, float], dict[int, float]]:
# construct probabilities
measurements = OrderedDict()
samples = OrderedDict()
measurements: dict[int, float] = OrderedDict()
samples: dict[float, float] = OrderedDict()
shots = sum(counts.values())
for state, count in counts.items():
y = int(state.replace(" ", "")[: self._m][::-1], 2)
Expand Down Expand Up @@ -405,7 +405,7 @@ def estimate(self, estimation_problem: EstimationProblem) -> "AmplitudeEstimatio
# store the number of oracle queries
result.num_oracle_queries = result.shots * (self._M - 1)

# run the MLE post processing
# run the MLE post-processing
mle = self.compute_mle(result)
result.mle = mle
result.mle_processed = estimation_problem.post_processing(mle)
Expand Down Expand Up @@ -457,13 +457,13 @@ class AmplitudeEstimationResult(AmplitudeEstimatorResult):

def __init__(self) -> None:
super().__init__()
self._num_evaluation_qubits = None
self._mle = None
self._mle_processed = None
self._samples = None
self._samples_processed = None
self._y_measurements = None
self._max_probability = None
self._num_evaluation_qubits: int | None = None
self._mle: float | None = None
self._mle_processed: float | None = None
self._samples: dict[float, float] | None = None
self._samples_processed: dict[float, float] | None = None
self._y_measurements: dict[int, float] | None = None
self._max_probability: float | None = None

@property
def num_evaluation_qubits(self) -> int:
Expand Down Expand Up @@ -571,7 +571,7 @@ def integrand(x):

def _fisher_confint(
result: AmplitudeEstimationResult, alpha: float, observed: bool = False
) -> list[float]:
) -> tuple[float, float]:
"""Compute the Fisher information confidence interval for the MLE of the previous run.
Args:
Expand All @@ -588,10 +588,12 @@ def _fisher_confint(
confint = result.mle + norm.ppf(1 - alpha / 2) / std * np.array([-1, 1])

# transform the confidence interval from [0, 1] to the target interval
return tuple(result.post_processing(bound) for bound in confint)
return result.post_processing(confint[0]), result.post_processing(confint[1])


def _likelihood_ratio_confint(result: AmplitudeEstimationResult, alpha: float) -> list[float]:
def _likelihood_ratio_confint(
result: AmplitudeEstimationResult, alpha: float
) -> tuple[float, float]:
"""Compute the likelihood ratio confidence interval for the MLE of the previous run.
Args:
Expand Down Expand Up @@ -659,5 +661,4 @@ def cut(x):
upper = np.maximum(upper, right)

# Put together CI
confint = [lower, upper]
return tuple(result.post_processing(bound) for bound in confint)
return result.post_processing(lower), result.post_processing(upper)
Loading

0 comments on commit ed0ed39

Please sign in to comment.