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

Fix mypy errors (algorithms) #8271

Merged
merged 19 commits into from
Apr 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor

Choose a reason for hiding this comment

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

This annotation is duplicated in the return of circuit_results method. You should make a type alias CircuitResultsT = list[np.ndarray] | list[dict[str, int]] | None. And I think you have to use Optional and Union for the type alias declaration. Because it is not supported yet until Python 3.10.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, you should exclude the | None in the CircuitResultsT declaration, because the circuit_results setter doesn't expect 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 = {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is just FYI, the quantum instance is deprecated and will be removed in following qiskit releases. Once the quantum instance is removed, I believe that the circuit_results will always be a dict[str,float], because the type of results.quasi_dists in the line below is fixed. This is not the only annotation that will be simplified, so I can keep track of these when we open the removal PR.

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