diff --git a/qiskit/algorithms/amplitude_amplifiers/amplification_problem.py b/qiskit/algorithms/amplitude_amplifiers/amplification_problem.py index c2908a86aa90..67b20751c417 100644 --- a/qiskit/algorithms/amplitude_amplifiers/amplification_problem.py +++ b/qiskit/algorithms/amplitude_amplifiers/amplification_problem.py @@ -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 @@ -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: @@ -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: @@ -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: @@ -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: @@ -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: @@ -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: @@ -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. @@ -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 @@ -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``. diff --git a/qiskit/algorithms/amplitude_amplifiers/amplitude_amplifier.py b/qiskit/algorithms/amplitude_amplifiers/amplitude_amplifier.py index dd32dc96ec47..33ef90cb624e 100644 --- a/qiskit/algorithms/amplitude_amplifiers/amplitude_amplifier.py +++ b/qiskit/algorithms/amplitude_amplifiers/amplitude_amplifier.py @@ -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 @@ -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: @@ -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 diff --git a/qiskit/algorithms/amplitude_amplifiers/grover.py b/qiskit/algorithms/amplitude_amplifiers/grover.py index 6bb737d34ff2..60d00f62d5dc 100644 --- a/qiskit/algorithms/amplitude_amplifiers/grover.py +++ b/qiskit/algorithms/amplitude_amplifiers/grover.py @@ -11,11 +11,13 @@ # 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 @@ -23,7 +25,7 @@ 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 @@ -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: @@ -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: @@ -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) @@ -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: @@ -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: @@ -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: @@ -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( @@ -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) @@ -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]) @@ -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. @@ -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: @@ -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: diff --git a/qiskit/algorithms/amplitude_estimators/ae.py b/qiskit/algorithms/amplitude_estimators/ae.py index 0df4b89be98d..be3a287237cf 100644 --- a/qiskit/algorithms/amplitude_estimators/ae.py +++ b/qiskit/algorithms/amplitude_estimators/ae.py @@ -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 @@ -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) @@ -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) @@ -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: @@ -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: @@ -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: @@ -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) diff --git a/qiskit/algorithms/amplitude_estimators/amplitude_estimator.py b/qiskit/algorithms/amplitude_estimators/amplitude_estimator.py index eb2e5a7f4f67..613827c6ccd6 100644 --- a/qiskit/algorithms/amplitude_estimators/amplitude_estimator.py +++ b/qiskit/algorithms/amplitude_estimators/amplitude_estimator.py @@ -14,7 +14,8 @@ from __future__ import annotations from abc import abstractmethod, ABC -from typing import Callable +from collections.abc import Callable + import numpy as np from .estimation_problem import EstimationProblem @@ -40,14 +41,14 @@ class AmplitudeEstimatorResult(AlgorithmResult): def __init__(self) -> None: super().__init__() - self._circuit_results = None - self._shots = None - self._estimation = None - self._estimation_processed = None - self._num_oracle_queries = None - self._post_processing = None - self._confidence_interval = None - self._confidence_interval_processed = None + self._circuit_results: np.ndarray | dict[str, int] | None = None + self._shots: int | None = None + self._estimation: float | None = None + self._estimation_processed: float | None = None + self._num_oracle_queries: int | None = None + self._post_processing: Callable[[float], float] | None = None + self._confidence_interval: tuple[float, float] | None = None + self._confidence_interval_processed: tuple[float, float] | None = None @property def circuit_results(self) -> np.ndarray | dict[str, int] | None: diff --git a/qiskit/algorithms/amplitude_estimators/estimation_problem.py b/qiskit/algorithms/amplitude_estimators/estimation_problem.py index 93b02991af2b..0232cb821021 100644 --- a/qiskit/algorithms/amplitude_estimators/estimation_problem.py +++ b/qiskit/algorithms/amplitude_estimators/estimation_problem.py @@ -14,7 +14,8 @@ from __future__ import annotations import warnings -from typing import Callable +from collections.abc import Callable + import numpy from qiskit.circuit import QuantumCircuit, QuantumRegister diff --git a/qiskit/algorithms/amplitude_estimators/fae.py b/qiskit/algorithms/amplitude_estimators/fae.py index 143660422676..cbfeecad499b 100644 --- a/qiskit/algorithms/amplitude_estimators/fae.py +++ b/qiskit/algorithms/amplitude_estimators/fae.py @@ -185,7 +185,7 @@ def _cos_estimate(self, estimation_problem, k, shots): return cos_estimate - def _chernoff(self, cos, shots): + def _chernoff(self, cos, shots) -> list[float]: width = np.sqrt(np.log(2 / self._delta) * 12 / shots) confint = [np.maximum(-1, cos - width), np.minimum(1, cos + width)] return confint @@ -306,14 +306,14 @@ def cos_estimate(power, shots): theta = np.mean(theta_ci) rescaling = 4 if self._rescale else 1 value = (rescaling * np.sin(theta)) ** 2 - value_ci = [(rescaling * np.sin(x)) ** 2 for x in theta_ci] + value_ci = ((rescaling * np.sin(theta_ci[0])) ** 2, (rescaling * np.sin(theta_ci[1])) ** 2) result = FasterAmplitudeEstimationResult() result.num_oracle_queries = self._num_oracle_calls result.num_steps = num_steps result.num_first_state_steps = num_first_stage_steps if self._quantum_instance is not None and self._quantum_instance.is_statevector: - result.success_probability = 1 + result.success_probability = 1.0 else: result.success_probability = 1 - (2 * self._maxiter - j_0) * self._delta @@ -335,18 +335,18 @@ class FasterAmplitudeEstimationResult(AmplitudeEstimatorResult): def __init__(self) -> None: super().__init__() - self._success_probability = None - self._num_steps = None - self._num_first_state_steps = None - self._theta_intervals = None + self._success_probability: float | None = None + self._num_steps: int | None = None + self._num_first_state_steps: int | None = None + self._theta_intervals: list[list[float]] | None = None @property - def success_probability(self) -> int: + def success_probability(self) -> float: """Return the success probability of the algorithm.""" return self._success_probability @success_probability.setter - def success_probability(self, probability: int) -> None: + def success_probability(self, probability: float) -> None: """Set the success probability of the algorithm.""" self._success_probability = probability diff --git a/qiskit/algorithms/amplitude_estimators/iae.py b/qiskit/algorithms/amplitude_estimators/iae.py index d1e0f9fe2631..f5aa04c926c6 100644 --- a/qiskit/algorithms/amplitude_estimators/iae.py +++ b/qiskit/algorithms/amplitude_estimators/iae.py @@ -420,7 +420,7 @@ def estimate( if estimation_problem.is_good_state(bit): prob += probabilities - a_confidence_interval = [prob, prob] # type: list[float] + a_confidence_interval = [prob, prob] a_intervals.append(a_confidence_interval) theta_i_interval = [ @@ -523,15 +523,15 @@ class IterativeAmplitudeEstimationResult(AmplitudeEstimatorResult): def __init__(self) -> None: super().__init__() - self._alpha = None - self._epsilon_target = None - self._epsilon_estimated = None - self._epsilon_estimated_processed = None - self._estimate_intervals = None - self._theta_intervals = None - self._powers = None - self._ratios = None - self._confidence_interval_processed = None + self._alpha: float | None = None + self._epsilon_target: float | None = None + self._epsilon_estimated: float | None = None + self._epsilon_estimated_processed: float | None = None + self._estimate_intervals: list[list[float]] | None = None + self._theta_intervals: list[list[float]] | None = None + self._powers: list[int] | None = None + self._ratios: list[float] | None = None + self._confidence_interval_processed: tuple[float, float] | None = None @property def alpha(self) -> float: diff --git a/qiskit/algorithms/amplitude_estimators/mlae.py b/qiskit/algorithms/amplitude_estimators/mlae.py index cfb36a438216..469dcb09a97a 100644 --- a/qiskit/algorithms/amplitude_estimators/mlae.py +++ b/qiskit/algorithms/amplitude_estimators/mlae.py @@ -13,8 +13,10 @@ """The Maximum Likelihood Amplitude Estimation algorithm.""" from __future__ import annotations -import typing import warnings +from collections.abc import Sequence +from typing import Callable, List, Tuple + import numpy as np from scipy.optimize import brute from scipy.stats import norm, chi2 @@ -29,9 +31,7 @@ from .estimation_problem import EstimationProblem from ..exceptions import AlgorithmError -MINIMIZER = typing.Callable[ - [typing.Callable[[float], float], typing.List[typing.Tuple[float, float]]], float -] +MINIMIZER = Callable[[Callable[[float], float], List[Tuple[float, float]]], float] class MaximumLikelihoodAmplitudeEstimation(AmplitudeEstimator): @@ -229,11 +229,11 @@ def compute_confidence_interval( AlgorithmError: If `run()` hasn't been called yet. NotImplementedError: If the method `kind` is not supported. """ - interval = None + interval: tuple[float, float] | None = None # if statevector simulator the estimate is exact if all(isinstance(data, (list, np.ndarray)) for data in result.circuit_results): - interval = 2 * [result.estimation] + interval = (result.estimation, result.estimation) elif kind in ["likelihood_ratio", "lr"]: interval = _likelihood_ratio_confint(result, alpha) @@ -248,13 +248,13 @@ def compute_confidence_interval( raise NotImplementedError(f"CI `{kind}` is not implemented.") if apply_post_processing: - return tuple(result.post_processing(value) for value in interval) + return result.post_processing(interval[0]), result.post_processing(interval[1]) return interval def compute_mle( self, - circuit_results: list[dict[str, int]] | list[np.ndarray], + circuit_results: list[dict[str, int] | np.ndarray], estimation_problem: EstimationProblem, num_state_qubits: int | None = None, return_counts: bool = False, @@ -401,11 +401,11 @@ class MaximumLikelihoodAmplitudeEstimationResult(AmplitudeEstimatorResult): def __init__(self) -> None: super().__init__() - self._theta = None - self._minimizer = None - self._good_counts = None - self._evaluation_schedule = None - self._fisher_information = None + self._theta: float | None = None + self._minimizer: Callable | None = None + self._good_counts: list[float] | None = None + self._evaluation_schedule: list[int] | None = None + self._fisher_information: float | None = None @property def theta(self) -> float: @@ -418,12 +418,12 @@ def theta(self, value: float) -> None: self._theta = value @property - def minimizer(self) -> callable: + def minimizer(self) -> Callable: """Return the minimizer used for the search of the likelihood function.""" return self._minimizer @minimizer.setter - def minimizer(self, value: callable) -> None: + def minimizer(self, value: Callable) -> None: """Set the number minimizer used for the search of the likelihood function.""" self._minimizer = value @@ -557,15 +557,14 @@ def _fisher_confint( confint = np.real(result.estimation) + normal_quantile / np.sqrt(fisher_information) * np.array( [-1, 1] ) - mapped_confint = tuple(result.post_processing(bound) for bound in confint) - return mapped_confint + return result.post_processing(confint[0]), result.post_processing(confint[1]) def _likelihood_ratio_confint( result: MaximumLikelihoodAmplitudeEstimationResult, alpha: float = 0.05, nevals: int | None = None, -) -> list[float]: +) -> tuple[float, float]: """Compute the likelihood-ratio confidence interval. Args: @@ -613,7 +612,7 @@ def loglikelihood(theta, one_counts, all_counts): def _get_counts( - circuit_results: list[np.ndarray | list[float], dict[str, int]], + circuit_results: Sequence[np.ndarray | list[float] | dict[str, int]], estimation_problem: EstimationProblem, num_state_qubits: int, ) -> tuple[list[float], list[int]]: @@ -626,7 +625,8 @@ def _get_counts( AlgorithmError: If self.run() has not been called yet. """ one_hits = [] # h_k: how often 1 has been measured, for a power Q^(m_k) - all_hits = [] # shots_k: how often has been measured at a power Q^(m_k) + # shots_k: how often has been measured at a power Q^(m_k) + all_hits: np.ndarray | list[float] = [] if all(isinstance(data, (list, np.ndarray)) for data in circuit_results): probabilities = [] num_qubits = int(np.log2(len(circuit_results[0]))) # the total number of qubits diff --git a/qiskit/algorithms/aux_ops_evaluator.py b/qiskit/algorithms/aux_ops_evaluator.py index b8708aad15ee..d0348f381d19 100644 --- a/qiskit/algorithms/aux_ops_evaluator.py +++ b/qiskit/algorithms/aux_ops_evaluator.py @@ -11,7 +11,7 @@ # that they have been altered from the originals. """Evaluator of auxiliary operators for algorithms.""" -from typing import Tuple, Union, List +from __future__ import annotations import numpy as np @@ -40,16 +40,12 @@ pending=True, ) def eval_observables( - quantum_instance: Union[QuantumInstance, Backend], - quantum_state: Union[ - Statevector, - QuantumCircuit, - OperatorBase, - ], + quantum_instance: QuantumInstance | Backend, + quantum_state: Statevector | QuantumCircuit | OperatorBase, observables: ListOrDict[OperatorBase], expectation: ExpectationBase, threshold: float = 1e-12, -) -> ListOrDict[Tuple[complex, complex]]: +) -> ListOrDict[tuple[complex, complex]]: """ Pending deprecation: Accepts a list or a dictionary of operators and calculates their expectation values - means @@ -101,8 +97,9 @@ def eval_observables( values = np.real(observables_expect_sampled.eval()) # compute standard deviations + # We use sampler.quantum_instance to take care of case in which quantum_instance is Backend std_devs = _compute_std_devs( - observables_expect_sampled, observables, expectation, quantum_instance + observables_expect_sampled, observables, expectation, sampler.quantum_instance ) # Discard values below threshold @@ -117,11 +114,7 @@ def eval_observables( def _prepare_list_op( - quantum_state: Union[ - Statevector, - QuantumCircuit, - OperatorBase, - ], + quantum_state: Statevector | QuantumCircuit | OperatorBase, observables: ListOrDict[OperatorBase], ) -> ListOp: """ @@ -145,9 +138,9 @@ def _prepare_list_op( def _prepare_result( - observables_results: List[Tuple[complex, complex]], + observables_results: list[tuple[complex, complex]], observables: ListOrDict[OperatorBase], -) -> ListOrDict[Tuple[complex, complex]]: +) -> ListOrDict[tuple[complex, complex]]: """ Prepares a list or a dictionary of eigenvalues from ``observables_results`` and ``observables``. @@ -161,7 +154,7 @@ def _prepare_result( A list or a dictionary of tuples (mean, standard deviation). """ if isinstance(observables, list): - observables_eigenvalues = [None] * len(observables) + observables_eigenvalues: ListOrDict[tuple[complex, complex]] = [None] * len(observables) key_value_iterator = enumerate(observables_results) else: observables_eigenvalues = {} @@ -176,8 +169,8 @@ def _compute_std_devs( observables_expect_sampled: OperatorBase, observables: ListOrDict[OperatorBase], expectation: ExpectationBase, - quantum_instance: Union[QuantumInstance, Backend], -) -> List[complex]: + quantum_instance: QuantumInstance | Backend, +) -> list[complex]: """ Calculates a list of standard deviations from expectation values of observables provided. @@ -197,5 +190,6 @@ def _compute_std_devs( # when `variances` is a single value equal to 0., our expectation value is exact and we # manually ensure the variances to be a list of the correct length variances = np.zeros(len(observables), dtype=float) + # TODO: this will crash if quantum_instance is a backend std_devs = np.sqrt(variances / quantum_instance.run_config.shots) return std_devs diff --git a/qiskit/algorithms/eigen_solvers/eigen_solver.py b/qiskit/algorithms/eigen_solvers/eigen_solver.py index 3bb0379d4397..2fc42a509627 100644 --- a/qiskit/algorithms/eigen_solvers/eigen_solver.py +++ b/qiskit/algorithms/eigen_solvers/eigen_solver.py @@ -11,9 +11,8 @@ # that they have been altered from the originals. """The Eigensolver interface""" - +from __future__ import annotations from abc import ABC, abstractmethod -from typing import Optional, List, Tuple import numpy as np @@ -46,7 +45,7 @@ def __init__(self) -> None: @abstractmethod def compute_eigenvalues( - self, operator: OperatorBase, aux_operators: Optional[ListOrDict[OperatorBase]] = None + self, operator: OperatorBase, aux_operators: ListOrDict[OperatorBase] | None = None ) -> "EigensolverResult": """ Computes eigenvalues. Operator and aux_operators can be supplied here and @@ -95,12 +94,12 @@ class EigensolverResult(AlgorithmResult): ) def __init__(self) -> None: super().__init__() - self._eigenvalues = None - self._eigenstates = None - self._aux_operator_eigenvalues = None + self._eigenvalues: np.ndarray | None = None + self._eigenstates: np.ndarray | None = None + self._aux_operator_eigenvalues: list[ListOrDict[tuple[complex, complex]]] | None = None @property - def eigenvalues(self) -> Optional[np.ndarray]: + def eigenvalues(self) -> np.ndarray | None: """returns eigen values""" return self._eigenvalues @@ -110,7 +109,7 @@ def eigenvalues(self, value: np.ndarray) -> None: self._eigenvalues = value @property - def eigenstates(self) -> Optional[np.ndarray]: + def eigenstates(self) -> np.ndarray | None: """return eigen states""" return self._eigenstates @@ -120,7 +119,7 @@ def eigenstates(self, value: np.ndarray) -> None: self._eigenstates = value @property - def aux_operator_eigenvalues(self) -> Optional[List[ListOrDict[Tuple[complex, complex]]]]: + def aux_operator_eigenvalues(self) -> list[ListOrDict[tuple[complex, complex]]] | None: """Return aux operator expectation values. These values are in fact tuples formatted as (mean, standard deviation). @@ -128,6 +127,6 @@ def aux_operator_eigenvalues(self) -> Optional[List[ListOrDict[Tuple[complex, co return self._aux_operator_eigenvalues @aux_operator_eigenvalues.setter - def aux_operator_eigenvalues(self, value: List[ListOrDict[Tuple[complex, complex]]]) -> None: + def aux_operator_eigenvalues(self, value: list[ListOrDict[tuple[complex, complex]]]) -> None: """set aux operator eigen values""" self._aux_operator_eigenvalues = value diff --git a/qiskit/algorithms/eigen_solvers/numpy_eigen_solver.py b/qiskit/algorithms/eigen_solvers/numpy_eigen_solver.py index e2d50e23be1b..98da79cfff79 100755 --- a/qiskit/algorithms/eigen_solvers/numpy_eigen_solver.py +++ b/qiskit/algorithms/eigen_solvers/numpy_eigen_solver.py @@ -11,10 +11,12 @@ # that they have been altered from the originals. """The Eigensolver algorithm.""" +from __future__ import annotations import logging -from typing import Callable, List, Optional, Tuple, Union import warnings +from collections.abc import Callable + import numpy as np from scipy import sparse as scisparse @@ -57,7 +59,7 @@ def __init__( self, k: int = 1, filter_criterion: Callable[ - [Union[List, np.ndarray], float, Optional[ListOrDict[float]]], bool + [list | np.ndarray, float, ListOrDict[float] | None], bool ] = None, ) -> None: """ @@ -97,16 +99,15 @@ def k(self, k: int) -> None: @property def filter_criterion( self, - ) -> Optional[Callable[[Union[List, np.ndarray], float, Optional[ListOrDict[float]]], bool]]: + ) -> Callable[[list | np.ndarray, float, ListOrDict[float] | None], bool] | None: """returns the filter criterion if set""" return self._filter_criterion @filter_criterion.setter def filter_criterion( self, - filter_criterion: Optional[ - Callable[[Union[List, np.ndarray], float, Optional[ListOrDict[float]]], bool] - ], + filter_criterion: Callable[[list | np.ndarray, float, ListOrDict[float] | None], bool] + | None, ) -> None: """set the filter criterion""" self._filter_criterion = filter_criterion @@ -158,7 +159,7 @@ def _get_ground_state_energy(self, operator: OperatorBase) -> None: self._solve(operator) def _get_energies( - self, operator: OperatorBase, aux_operators: Optional[ListOrDict[OperatorBase]] + self, operator: OperatorBase, aux_operators: ListOrDict[OperatorBase] | None ) -> None: if self._ret.eigenvalues is None or self._ret.eigenstates is None: self._solve(operator) @@ -174,9 +175,9 @@ def _get_energies( @staticmethod def _eval_aux_operators( aux_operators: ListOrDict[OperatorBase], wavefn, threshold: float = 1e-12 - ) -> ListOrDict[Tuple[complex, complex]]: + ) -> ListOrDict[tuple[complex, complex]]: - values: ListOrDict[Tuple[complex, complex]] + values: ListOrDict[tuple[complex, complex]] # As a list, aux_operators can contain None operators for which None values are returned. # As a dict, the None operators in aux_operators have been dropped in compute_eigenvalues. @@ -206,7 +207,7 @@ def _eval_aux_operators( return values def compute_eigenvalues( - self, operator: OperatorBase, aux_operators: Optional[ListOrDict[OperatorBase]] = None + self, operator: OperatorBase, aux_operators: ListOrDict[OperatorBase] | None = None ) -> EigensolverResult: super().compute_eigenvalues(operator, aux_operators) diff --git a/qiskit/algorithms/eigen_solvers/vqd.py b/qiskit/algorithms/eigen_solvers/vqd.py index e9c7e91a4165..90297a7dc972 100644 --- a/qiskit/algorithms/eigen_solvers/vqd.py +++ b/qiskit/algorithms/eigen_solvers/vqd.py @@ -14,10 +14,11 @@ See https://arxiv.org/abs/1805.08138. """ +from __future__ import annotations -from typing import Optional, List, Callable, Union, Dict, Tuple import logging import warnings +from collections.abc import Callable from time import time import numpy as np @@ -103,17 +104,17 @@ class VQD(VariationalAlgorithm, Eigensolver): ) def __init__( self, - ansatz: Optional[QuantumCircuit] = None, + ansatz: QuantumCircuit | None = None, k: int = 2, - betas: Optional[List[float]] = None, - optimizer: Optional[Union[Optimizer, Minimizer]] = None, - initial_point: Optional[np.ndarray] = None, - gradient: Optional[Union[GradientBase, Callable]] = None, - expectation: Optional[ExpectationBase] = None, + betas: list[float] | None = None, + optimizer: Optimizer | Minimizer | None = None, + initial_point: np.ndarray | None = None, + gradient: GradientBase | Callable | None = None, + expectation: ExpectationBase | None = None, include_custom: bool = False, max_evals_grouped: int = 1, - callback: Optional[Callable[[int, np.ndarray, float, float], None]] = None, - quantum_instance: Optional[Union[QuantumInstance, Backend]] = None, + callback: Callable[[int, np.ndarray, float, float, int], None] | None = None, + quantum_instance: QuantumInstance | Backend | None = None, ) -> None: """ @@ -166,34 +167,34 @@ def __init__( super().__init__() self._max_evals_grouped = max_evals_grouped - self._circuit_sampler = None # type: Optional[CircuitSampler] + self._circuit_sampler: CircuitSampler | None = None self._expectation = None self.expectation = expectation self._include_custom = include_custom # set ansatz -- still supporting pre 0.18.0 sorting - self._ansatz = None + self._ansatz: QuantumCircuit | None = None self.ansatz = ansatz self.k = k self.betas = betas - self._optimizer = None + self._optimizer: Optimizer | None = None self.optimizer = optimizer - self._initial_point = None + self._initial_point: np.ndarray | None = None self.initial_point = initial_point - self._gradient = None + self._gradient: GradientBase | Callable | None = None self.gradient = gradient - self._quantum_instance = None + self._quantum_instance: QuantumInstance | None = None if quantum_instance is not None: self.quantum_instance = quantum_instance self._eval_time = None self._eval_count = 0 - self._callback = None + self._callback: Callable[[int, np.ndarray, float, float, int], None] | None = None self.callback = callback logger.info(self.print_settings()) @@ -204,7 +205,7 @@ def ansatz(self) -> QuantumCircuit: return self._ansatz @ansatz.setter - def ansatz(self, ansatz: Optional[QuantumCircuit]): + def ansatz(self, ansatz: QuantumCircuit | None): """Sets the ansatz. Args: @@ -218,22 +219,22 @@ def ansatz(self, ansatz: Optional[QuantumCircuit]): self._ansatz = ansatz @property - def gradient(self) -> Optional[Union[GradientBase, Callable]]: + def gradient(self) -> GradientBase | Callable | None: """Returns the gradient.""" return self._gradient @gradient.setter - def gradient(self, gradient: Optional[Union[GradientBase, Callable]]): + def gradient(self, gradient: GradientBase | Callable | None): """Sets the gradient.""" self._gradient = gradient @property - def quantum_instance(self) -> Optional[QuantumInstance]: + def quantum_instance(self) -> QuantumInstance | None: """Returns quantum instance.""" return self._quantum_instance @quantum_instance.setter - def quantum_instance(self, quantum_instance: Union[QuantumInstance, Backend]) -> None: + def quantum_instance(self, quantum_instance: QuantumInstance | Backend) -> None: """Sets a quantum_instance.""" if not isinstance(quantum_instance, QuantumInstance): quantum_instance = QuantumInstance(quantum_instance) @@ -244,7 +245,7 @@ def quantum_instance(self, quantum_instance: Union[QuantumInstance, Backend]) -> ) @property - def initial_point(self) -> Optional[np.ndarray]: + def initial_point(self) -> np.ndarray | None: """Returns initial point.""" return self._initial_point @@ -279,23 +280,23 @@ def include_custom(self, include_custom: bool): self.expectation = None @property - def callback(self) -> Optional[Callable[[int, np.ndarray, float, float, int], None]]: + def callback(self) -> Callable[[int, np.ndarray, float, float, int], None] | None: """Returns callback""" return self._callback @callback.setter - def callback(self, callback: Optional[Callable[[int, np.ndarray, float, float, int], None]]): + def callback(self, callback: Callable[[int, np.ndarray, float, float, int], None] | None): """Sets callback""" self._callback = callback @property - def expectation(self) -> Optional[ExpectationBase]: + def expectation(self) -> ExpectationBase | None: """The expectation value algorithm used to construct the expectation measurement from the observable.""" return self._expectation @expectation.setter - def expectation(self, exp: Optional[ExpectationBase]) -> None: + def expectation(self, exp: ExpectationBase | None) -> None: self._expectation = exp def _check_operator_ansatz(self, operator: OperatorBase): @@ -318,7 +319,7 @@ def optimizer(self) -> Optimizer: return self._optimizer @optimizer.setter - def optimizer(self, optimizer: Optional[Optimizer]): + def optimizer(self, optimizer: Optimizer | None): """Sets the optimizer attribute. Args: @@ -370,10 +371,10 @@ def print_settings(self): def construct_expectation( self, - parameter: Union[List[float], List[Parameter], np.ndarray], + parameter: list[float] | list[Parameter] | np.ndarray, operator: OperatorBase, return_expectation: bool = False, - ) -> Union[OperatorBase, Tuple[OperatorBase, ExpectationBase]]: + ) -> OperatorBase | tuple[OperatorBase, ExpectationBase]: r""" Generate the ansatz circuit and expectation value measurement, and return their runnable composition. @@ -422,9 +423,9 @@ def construct_expectation( def construct_circuit( self, - parameter: Union[List[float], List[Parameter], np.ndarray], + parameter: list[float] | list[Parameter] | np.ndarray, operator: OperatorBase, - ) -> List[QuantumCircuit]: + ) -> list[QuantumCircuit]: """Return the circuits used to compute the expectation value. Args: @@ -460,7 +461,7 @@ def _eval_aux_ops( aux_operators: ListOrDict[OperatorBase], expectation: ExpectationBase, threshold: float = 1e-12, - ) -> ListOrDict[Tuple[complex, complex]]: + ) -> ListOrDict[tuple[complex, complex]]: # Create new CircuitSampler to avoid breaking existing one's caches. sampler = CircuitSampler(self.quantum_instance) @@ -493,7 +494,9 @@ def _eval_aux_ops( # None operators are already dropped in compute_minimum_eigenvalue if aux_operators is a # dict. if isinstance(aux_operators, list): - aux_operator_eigenvalues = [None] * len(aux_operators) + aux_operator_eigenvalues: ListOrDict[tuple[complex, complex]] = [None] * len( + aux_operators + ) key_value_iterator = enumerate(aux_op_results) else: aux_operator_eigenvalues = {} @@ -506,7 +509,7 @@ def _eval_aux_ops( return aux_operator_eigenvalues def compute_eigenvalues( - self, operator: OperatorBase, aux_operators: Optional[ListOrDict[OperatorBase]] = None + self, operator: OperatorBase, aux_operators: ListOrDict[OperatorBase] | None = None ) -> EigensolverResult: super().compute_eigenvalues(operator, aux_operators) @@ -532,7 +535,7 @@ def compute_eigenvalues( # Drop None and convert zero values when aux_operators is a dict. if isinstance(aux_operators, list): key_op_iterator = enumerate(aux_operators) - converted = [zero_op] * len(aux_operators) + converted: ListOrDict[OperatorBase] = [zero_op] * len(aux_operators) else: key_op_iterator = aux_operators.items() converted = {} @@ -660,8 +663,10 @@ def get_energy_evaluation( step: int, operator: OperatorBase, return_expectation: bool = False, - prev_states: Optional[List[np.ndarray]] = None, - ) -> Callable[[np.ndarray], Union[float, List[float]]]: + prev_states: list[np.ndarray] | None = None, + ) -> Callable[[np.ndarray], float | list[float]] | tuple[ + Callable[[np.ndarray], float | list[float]], ExpectationBase + ]: """Returns a function handle to evaluates the energy at given parameters for the ansatz. This return value is the objective function to be passed to the optimizer for evaluation. @@ -742,7 +747,7 @@ def energy_evaluation(parameters): return energy_evaluation - def _get_eigenstate(self, optimal_parameters) -> Union[List[float], Dict[str, int]]: + def _get_eigenstate(self, optimal_parameters) -> list[float] | dict[str, int]: """Get the simulation outcome of the ansatz, provided with parameters.""" optimal_circuit = self.ansatz.bind_parameters(optimal_parameters) state_fn = self._circuit_sampler.convert(StateFn(optimal_circuit)).eval() @@ -771,10 +776,10 @@ class VQDResult(VariationalResult, EigensolverResult): ) def __init__(self) -> None: super().__init__() - self._cost_function_evals = None + self._cost_function_evals: int | None = None @property - def cost_function_evals(self) -> Optional[int]: + def cost_function_evals(self) -> int | None: """Returns number of cost optimizer evaluations""" return self._cost_function_evals @@ -784,7 +789,7 @@ def cost_function_evals(self, value: int) -> None: self._cost_function_evals = value @property - def eigenstates(self) -> Optional[np.ndarray]: + def eigenstates(self) -> np.ndarray | None: """return eigen state""" return self._eigenstates diff --git a/qiskit/algorithms/eigensolvers/eigensolver.py b/qiskit/algorithms/eigensolvers/eigensolver.py index 0cafc9d64dcf..30fb7c488844 100644 --- a/qiskit/algorithms/eigensolvers/eigensolver.py +++ b/qiskit/algorithms/eigensolvers/eigensolver.py @@ -73,8 +73,10 @@ class EigensolverResult(AlgorithmResult): def __init__(self) -> None: super().__init__() - self._eigenvalues = None - self._aux_operators_evaluated = None + self._eigenvalues: np.ndarray | None = None + self._aux_operators_evaluated: list[ + ListOrDict[tuple[complex, dict[str, Any]]] + ] | None = None @property def eigenvalues(self) -> np.ndarray | None: diff --git a/qiskit/algorithms/eigensolvers/numpy_eigensolver.py b/qiskit/algorithms/eigensolvers/numpy_eigensolver.py index c09f4bc911ed..954292ecc5d0 100755 --- a/qiskit/algorithms/eigensolvers/numpy_eigensolver.py +++ b/qiskit/algorithms/eigensolvers/numpy_eigensolver.py @@ -14,7 +14,7 @@ from __future__ import annotations -from typing import Callable, List, Union, Optional +from typing import Callable, Union, List, Optional import logging import numpy as np from scipy import sparse as scisparse @@ -310,7 +310,7 @@ class NumPyEigensolverResult(EigensolverResult): def __init__(self) -> None: super().__init__() - self._eigenstates = None + self._eigenstates: list[Statevector] | None = None @property def eigenstates(self) -> list[Statevector] | None: diff --git a/qiskit/algorithms/eigensolvers/vqd.py b/qiskit/algorithms/eigensolvers/vqd.py index c01152732dcb..99a0fb3076d1 100644 --- a/qiskit/algorithms/eigensolvers/vqd.py +++ b/qiskit/algorithms/eigensolvers/vqd.py @@ -103,7 +103,7 @@ class VQD(VariationalAlgorithm, Eigensolver): initial point (list[float]): An optional initial point (i.e. initial parameter values) for the optimizer. If ``None`` then VQD will look to the ansatz for a preferred point and if not will simply compute a random one. - callback (Callable[[int, np.ndarray, float, dict[str, Any]], None] | None): + callback (Callable[[int, np.ndarray, float, dict[str, Any], int], None] | None): A callback that can access the intermediate data during the optimization. Four parameter values are passed to the callback as follows during each evaluation by the optimizer: the evaluation count, @@ -121,7 +121,7 @@ def __init__( k: int = 2, betas: Sequence[float] | None = None, initial_point: Sequence[float] | None = None, - callback: Callable[[int, np.ndarray, float, dict[str, Any]], None] | None = None, + callback: Callable[[int, np.ndarray, float, dict[str, Any], int], None] | None = None, ) -> None: """ @@ -211,7 +211,7 @@ def compute_eigenvalues( # Drop None and convert zero values when aux_operators is a dict. if isinstance(aux_operators, list): key_op_iterator = enumerate(aux_operators) - converted = [zero_op] * len(aux_operators) + converted: ListOrDict[BaseOperator | PauliSumOp] = [zero_op] * len(aux_operators) else: key_op_iterator = aux_operators.items() converted = {} @@ -311,10 +311,6 @@ def compute_eigenvalues( # To match the signature of EigensolverResult result.eigenvalues = np.array(result.eigenvalues) - result.optimal_points = np.array(result.optimal_points) - result.optimal_values = np.array(result.optimal_values) - result.cost_function_evals = np.array(result.cost_function_evals) - result.optimizer_times = np.array(result.optimizer_times) if aux_operators is not None: result.aux_operators_evaluated = aux_values @@ -327,7 +323,7 @@ def _get_evaluate_energy( operator: BaseOperator | PauliSumOp, betas: Sequence[float], prev_states: list[QuantumCircuit] | None = None, - ) -> Callable[[np.ndarray], float | list[float]]: + ) -> Callable[[np.ndarray], float | np.ndarray]: """Returns a function handle to evaluate the ansatz's energy for any given parameters. This is the objective function to be passed to the optimizer that is used for evaluation. @@ -359,7 +355,7 @@ def _get_evaluate_energy( self._check_operator_ansatz(operator) - def evaluate_energy(parameters: np.ndarray) -> np.ndarray | float: + def evaluate_energy(parameters: np.ndarray) -> float | np.ndarray: # handle broadcasting: ensure parameters is of shape [array, array, ...] if len(parameters.shape) == 1: parameters = np.reshape(parameters, (-1, num_parameters)) @@ -408,23 +404,29 @@ def evaluate_energy(parameters: np.ndarray) -> np.ndarray | float: @staticmethod def _build_vqd_result() -> VQDResult: result = VQDResult() - result.optimal_points = [] + result.optimal_points = np.array([]) result.optimal_parameters = [] - result.optimal_values = [] - result.cost_function_evals = [] - result.optimizer_times = [] + result.optimal_values = np.array([]) + result.cost_function_evals = np.array([], dtype=int) + result.optimizer_times = np.array([]) result.eigenvalues = [] result.optimizer_results = [] result.optimal_circuits = [] return result @staticmethod - def _update_vqd_result(result, opt_result, eval_time, ansatz) -> VQDResult: - result.optimal_points.append(opt_result.x) + def _update_vqd_result( + result: VQDResult, opt_result: OptimizerResult, eval_time, ansatz + ) -> VQDResult: + result.optimal_points = ( + np.concatenate([result.optimal_points, [opt_result.x]]) + if len(result.optimal_points) > 0 + else np.array([opt_result.x]) + ) result.optimal_parameters.append(dict(zip(ansatz.parameters, opt_result.x))) - result.optimal_values.append(opt_result.fun) - result.cost_function_evals.append(opt_result.nfev) - result.optimizer_times.append(eval_time) + result.optimal_values = np.concatenate([result.optimal_points, [opt_result.x]]) + result.cost_function_evals = np.concatenate([result.cost_function_evals, [opt_result.nfev]]) + result.optimizer_times = np.concatenate([result.optimizer_times, [eval_time]]) result.eigenvalues.append(opt_result.fun + 0j) result.optimizer_results.append(opt_result) result.optimal_circuits.append(ansatz) @@ -436,76 +438,77 @@ class VQDResult(EigensolverResult): def __init__(self) -> None: super().__init__() - self._cost_function_evals = None - self._optimizer_times = None - self._optimal_values = None - self._optimal_points = None - self._optimal_parameters = None - self._optimizer_results = None - self._optimal_circuits = None + + self._cost_function_evals: np.ndarray | None = None + self._optimizer_times: np.ndarray | None = None + self._optimal_values: np.ndarray | None = None + self._optimal_points: np.ndarray | None = None + self._optimal_parameters: list[dict] | None = None + self._optimizer_results: list[OptimizerResult] | None = None + self._optimal_circuits: list[QuantumCircuit] | None = None @property - def cost_function_evals(self) -> Sequence[int] | None: + def cost_function_evals(self) -> np.ndarray | None: """Returns number of cost optimizer evaluations""" return self._cost_function_evals @cost_function_evals.setter - def cost_function_evals(self, value: Sequence[int]) -> None: + def cost_function_evals(self, value: np.ndarray) -> None: """Sets number of cost function evaluations""" self._cost_function_evals = value @property - def optimizer_times(self) -> Sequence[float] | None: + def optimizer_times(self) -> np.ndarray | None: """Returns time taken for optimization for each step""" return self._optimizer_times @optimizer_times.setter - def optimizer_times(self, value: Sequence[float]) -> None: + def optimizer_times(self, value: np.ndarray) -> None: """Sets time taken for optimization for each step""" self._optimizer_times = value @property - def optimal_values(self) -> Sequence[float] | None: + def optimal_values(self) -> np.ndarray | None: """Returns optimal value for each step""" return self._optimal_values @optimal_values.setter - def optimal_values(self, value: Sequence[float]) -> None: + def optimal_values(self, value: np.ndarray) -> None: """Sets optimal values""" self._optimal_values = value @property - def optimal_points(self) -> Sequence[np.ndarray] | None: + def optimal_points(self) -> np.ndarray | None: """Returns optimal point for each step""" return self._optimal_points @optimal_points.setter - def optimal_points(self, value: Sequence[np.ndarray]) -> None: + def optimal_points(self, value: np.ndarray) -> None: """Sets optimal points""" self._optimal_points = value @property - def optimal_parameters(self) -> Sequence[dict] | None: + def optimal_parameters(self) -> list[dict] | None: """Returns the optimal parameters for each step""" return self._optimal_parameters @optimal_parameters.setter - def optimal_parameters(self, value: Sequence[dict]) -> None: + def optimal_parameters(self, value: list[dict]) -> None: """Sets optimal parameters""" self._optimal_parameters = value @property - def optimizer_results(self) -> Sequence[OptimizerResult] | None: + def optimizer_results(self) -> list[OptimizerResult] | None: """Returns the optimizer results for each step""" return self._optimizer_results @optimizer_results.setter - def optimizer_results(self, value: Sequence[OptimizerResult]) -> None: + def optimizer_results(self, value: list[OptimizerResult]) -> None: """Sets optimizer results""" self._optimizer_results = value @property - def optimal_circuits(self) -> list[QuantumCircuit]: + def optimal_circuits(self) -> list[QuantumCircuit] | None: """The optimal circuits. Along with the optimal parameters, these can be used to retrieve the different eigenstates.""" return self._optimal_circuits diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index aa50f4c2a43f..856d62e19039 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -12,7 +12,7 @@ """Evolution problem class.""" -from typing import Union, Optional, Dict +from __future__ import annotations from qiskit import QuantumCircuit from qiskit.circuit import Parameter @@ -44,11 +44,11 @@ def __init__( self, hamiltonian: OperatorBase, time: float, - initial_state: Optional[Union[StateFn, QuantumCircuit]] = None, - aux_operators: Optional[ListOrDict[OperatorBase]] = None, + initial_state: StateFn | QuantumCircuit | None = None, + aux_operators: ListOrDict[OperatorBase] | None = None, truncation_threshold: float = 1e-12, - t_param: Optional[Parameter] = None, - param_value_dict: Optional[Dict[Parameter, complex]] = None, + t_param: Parameter | None = None, + param_value_dict: dict[Parameter, complex] | None = None, ): """ Args: @@ -107,7 +107,7 @@ def validate_params(self) -> None: t_param_set = set() if self.t_param is not None: t_param_set.add(self.t_param) - hamiltonian_dict_param_set = set() + hamiltonian_dict_param_set: set[Parameter] = set() if self.param_value_dict is not None: hamiltonian_dict_param_set = hamiltonian_dict_param_set.union( set(self.param_value_dict.keys()) diff --git a/qiskit/algorithms/evolvers/evolution_result.py b/qiskit/algorithms/evolvers/evolution_result.py index 1dcb176b9995..f5513923ac9e 100644 --- a/qiskit/algorithms/evolvers/evolution_result.py +++ b/qiskit/algorithms/evolvers/evolution_result.py @@ -12,7 +12,7 @@ """Class for holding evolution result.""" -from typing import Optional, Union, Tuple +from __future__ import annotations from qiskit import QuantumCircuit from qiskit.algorithms.list_or_dict import ListOrDict @@ -40,8 +40,8 @@ class EvolutionResult(AlgorithmResult): ) def __init__( self, - evolved_state: Union[StateFn, QuantumCircuit, OperatorBase], - aux_ops_evaluated: Optional[ListOrDict[Tuple[complex, complex]]] = None, + evolved_state: StateFn | QuantumCircuit | OperatorBase, + aux_ops_evaluated: ListOrDict[tuple[complex, complex]] | None = None, ): """ Args: diff --git a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py index dc43248de05e..10c5c107efcf 100644 --- a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py @@ -12,7 +12,8 @@ """An algorithm to implement a Trotterization real time-evolution.""" -from typing import Union, Optional +from __future__ import annotations + import warnings from qiskit import QuantumCircuit @@ -73,9 +74,9 @@ class TrotterQRTE(RealEvolver): ) def __init__( self, - product_formula: Optional[ProductFormula] = None, - expectation: Optional[ExpectationBase] = None, - quantum_instance: Optional[Union[QuantumInstance, Backend]] = None, + product_formula: ProductFormula | None = None, + expectation: ExpectationBase | None = None, + quantum_instance: QuantumInstance | Backend | None = None, ) -> None: """ Args: @@ -93,7 +94,7 @@ def __init__( product_formula = LieTrotter() self._product_formula = product_formula self._quantum_instance = None - self._circuit_sampler = None + self._circuit_sampler: CircuitSampler | None = None if quantum_instance is not None: self.quantum_instance = quantum_instance self._expectation = expectation @@ -113,12 +114,12 @@ def product_formula(self, product_formula: ProductFormula) -> None: self._product_formula = product_formula @property - def quantum_instance(self) -> Optional[QuantumInstance]: + def quantum_instance(self) -> QuantumInstance | None: """Returns a quantum instance used in the algorithm.""" return self._quantum_instance @quantum_instance.setter - def quantum_instance(self, quantum_instance: Optional[Union[QuantumInstance, Backend]]) -> None: + def quantum_instance(self, quantum_instance: QuantumInstance | Backend | None) -> None: """ Sets a quantum instance and a circuit sampler. Args: @@ -134,12 +135,12 @@ def quantum_instance(self, quantum_instance: Optional[Union[QuantumInstance, Bac self._quantum_instance = quantum_instance @property - def expectation(self) -> Optional[ExpectationBase]: + def expectation(self) -> ExpectationBase | None: """Returns an expectation used in the algorithm.""" return self._expectation @expectation.setter - def expectation(self, expectation: Optional[ExpectationBase]) -> None: + def expectation(self, expectation: ExpectationBase | None) -> None: """ Sets an expectation. Args: @@ -235,7 +236,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: @staticmethod def _summed_op_to_pauli_sum_op( hamiltonian: SummedOp, - ) -> Union[PauliSumOp, PauliOp]: + ) -> PauliSumOp | PauliOp: """ Tries binding parameters in a Hamiltonian. diff --git a/qiskit/algorithms/gradients/base_estimator_gradient.py b/qiskit/algorithms/gradients/base_estimator_gradient.py index a1e84b0cd492..926797db0a2a 100644 --- a/qiskit/algorithms/gradients/base_estimator_gradient.py +++ b/qiskit/algorithms/gradients/base_estimator_gradient.py @@ -75,7 +75,10 @@ def __init__( self._default_options.update_options(**options) self._derivative_type = derivative_type - self._gradient_circuit_cache: dict[QuantumCircuit, GradientCircuit] = {} + self._gradient_circuit_cache: dict[ + tuple, + GradientCircuit, + ] = {} @property def derivative_type(self) -> DerivativeType: diff --git a/qiskit/algorithms/gradients/base_qgt.py b/qiskit/algorithms/gradients/base_qgt.py index 2a196cf4a0a2..ffda914b878a 100644 --- a/qiskit/algorithms/gradients/base_qgt.py +++ b/qiskit/algorithms/gradients/base_qgt.py @@ -98,8 +98,8 @@ def __init__( self._default_options = Options() if options is not None: self._default_options.update_options(**options) - self._qgt_circuit_cache = {} - self._gradient_circuit_cache: dict[QuantumCircuit, GradientCircuit] = {} + self._qgt_circuit_cache: dict[tuple, GradientCircuit] = {} + self._gradient_circuit_cache: dict[tuple, GradientCircuit] = {} @property def derivative_type(self) -> DerivativeType: @@ -242,7 +242,7 @@ def _postprocess( zip(circuits, parameter_values, parameters) ): dtype = complex if self.derivative_type == DerivativeType.COMPLEX else float - qgt = np.zeros((len(parameters_), len(parameters_)), dtype=dtype) + qgt: np.ndarray = np.zeros((len(parameters_), len(parameters_)), dtype=dtype) gradient_circuit = self._gradient_circuit_cache[_circuit_key(circuit)] g_parameters = _make_gradient_parameters(gradient_circuit, parameters_) diff --git a/qiskit/algorithms/gradients/base_sampler_gradient.py b/qiskit/algorithms/gradients/base_sampler_gradient.py index b4983fd33cc8..436a7a0dbe12 100644 --- a/qiskit/algorithms/gradients/base_sampler_gradient.py +++ b/qiskit/algorithms/gradients/base_sampler_gradient.py @@ -54,7 +54,7 @@ def __init__(self, sampler: BaseSampler, options: Options | None = None): self._default_options = Options() if options is not None: self._default_options.update_options(**options) - self._gradient_circuit_cache: dict[QuantumCircuit, GradientCircuit] = {} + self._gradient_circuit_cache: dict[tuple, GradientCircuit] = {} def run( self, @@ -190,7 +190,7 @@ def _postprocess( # by using the chain rule. gradient = [] for parameter in parameters_: - grad_dist = defaultdict(float) + grad_dist: dict[int, float] = defaultdict(float) for g_parameter, coeff in gradient_circuit.parameter_map[parameter]: # Compute the coefficient if isinstance(coeff, ParameterExpression): diff --git a/qiskit/algorithms/gradients/finite_diff_estimator_gradient.py b/qiskit/algorithms/gradients/finite_diff_estimator_gradient.py index 9d1d751da541..a588b74dedcf 100644 --- a/qiskit/algorithms/gradients/finite_diff_estimator_gradient.py +++ b/qiskit/algorithms/gradients/finite_diff_estimator_gradient.py @@ -15,7 +15,7 @@ from __future__ import annotations import sys -from typing import Sequence +from collections.abc import Sequence import numpy as np @@ -75,7 +75,6 @@ def __init__( if epsilon <= 0: raise ValueError(f"epsilon ({epsilon}) should be positive.") self._epsilon = epsilon - self._base_parameter_values_dict = {} if method not in ("central", "forward", "backward"): raise TypeError( f"The argument method should be central, forward, or backward: {method} is given." diff --git a/qiskit/algorithms/gradients/finite_diff_sampler_gradient.py b/qiskit/algorithms/gradients/finite_diff_sampler_gradient.py index c9331230df55..8926e13892d4 100644 --- a/qiskit/algorithms/gradients/finite_diff_sampler_gradient.py +++ b/qiskit/algorithms/gradients/finite_diff_sampler_gradient.py @@ -16,7 +16,6 @@ import sys from collections import defaultdict -from typing import Sequence import numpy as np @@ -31,7 +30,7 @@ if sys.version_info >= (3, 8): # pylint: disable=ungrouped-imports - from typing import Literal + from typing import Literal, Sequence else: from typing_extensions import Literal @@ -133,7 +132,7 @@ def _run( if self._method == "central": result = results.quasi_dists[partial_sum_n : partial_sum_n + n] for dist_plus, dist_minus in zip(result[: n // 2], result[n // 2 :]): - grad_dist = defaultdict(float) + grad_dist: dict[int, float] = defaultdict(float) for key, value in dist_plus.items(): grad_dist[key] += value / (2 * self._epsilon) for key, value in dist_minus.items(): diff --git a/qiskit/algorithms/gradients/lin_comb_estimator_gradient.py b/qiskit/algorithms/gradients/lin_comb_estimator_gradient.py index e558b9c02eff..9edb15df0572 100644 --- a/qiskit/algorithms/gradients/lin_comb_estimator_gradient.py +++ b/qiskit/algorithms/gradients/lin_comb_estimator_gradient.py @@ -15,7 +15,7 @@ from __future__ import annotations -from typing import Sequence +from collections.abc import Sequence import numpy as np @@ -87,7 +87,7 @@ def __init__( default options > primitive's default setting. Higher priority setting overrides lower priority setting. """ - self._lin_comb_cache = {} + self._lin_comb_cache: dict[tuple, dict[Parameter, QuantumCircuit]] = {} super().__init__(estimator, options, derivative_type=derivative_type) @BaseEstimatorGradient.derivative_type.setter diff --git a/qiskit/algorithms/gradients/lin_comb_qgt.py b/qiskit/algorithms/gradients/lin_comb_qgt.py index dddd08604661..d84a4eb6f11f 100644 --- a/qiskit/algorithms/gradients/lin_comb_qgt.py +++ b/qiskit/algorithms/gradients/lin_comb_qgt.py @@ -113,7 +113,9 @@ def __init__( self._gradient = LinCombEstimatorGradient( estimator, derivative_type=DerivativeType.COMPLEX, options=options ) - self._lin_comb_qgt_circuit_cache = {} + self._lin_comb_qgt_circuit_cache: dict[ + tuple, dict[tuple[Parameter, Parameter], QuantumCircuit] + ] = {} def _run( self, diff --git a/qiskit/algorithms/gradients/lin_comb_sampler_gradient.py b/qiskit/algorithms/gradients/lin_comb_sampler_gradient.py index 45cccb679f3d..41bd9488d1b9 100644 --- a/qiskit/algorithms/gradients/lin_comb_sampler_gradient.py +++ b/qiskit/algorithms/gradients/lin_comb_sampler_gradient.py @@ -16,7 +16,7 @@ from __future__ import annotations from collections import defaultdict -from typing import Sequence +from collections.abc import Sequence from qiskit.circuit import Parameter, QuantumCircuit from qiskit.primitives import BaseSampler @@ -71,7 +71,7 @@ def __init__(self, sampler: BaseSampler, options: Options | None = None): default options > primitive's default setting. Higher priority setting overrides lower priority setting """ - self._lin_comb_cache = {} + self._lin_comb_cache: dict[tuple, dict[Parameter, QuantumCircuit]] = {} super().__init__(sampler, options) def _run( @@ -133,7 +133,7 @@ def _run_unique( result = results.quasi_dists[partial_sum_n : partial_sum_n + n] m = 2 ** circuits[i].num_qubits for dist in result: - grad_dist = defaultdict(float) + grad_dist: dict[int, float] = defaultdict(float) for key, value in dist.items(): if key < m: grad_dist[key] += value diff --git a/qiskit/algorithms/gradients/param_shift_sampler_gradient.py b/qiskit/algorithms/gradients/param_shift_sampler_gradient.py index af40e5f07403..b3d474c24f8b 100644 --- a/qiskit/algorithms/gradients/param_shift_sampler_gradient.py +++ b/qiskit/algorithms/gradients/param_shift_sampler_gradient.py @@ -16,7 +16,7 @@ from __future__ import annotations from collections import defaultdict -from typing import Sequence +from collections.abc import Sequence from qiskit.circuit import Parameter, QuantumCircuit @@ -104,7 +104,7 @@ def _run_unique( gradient = [] result = results.quasi_dists[partial_sum_n : partial_sum_n + n] for dist_plus, dist_minus in zip(result[: n // 2], result[n // 2 :]): - grad_dist = defaultdict(float) + grad_dist: dict[int, float] = defaultdict(float) for key, val in dist_plus.items(): grad_dist[key] += val / 2 for key, val in dist_minus.items(): diff --git a/qiskit/algorithms/gradients/reverse_gradient/derive_circuit.py b/qiskit/algorithms/gradients/reverse_gradient/derive_circuit.py index 0932a5100b78..461ab01557c2 100644 --- a/qiskit/algorithms/gradients/reverse_gradient/derive_circuit.py +++ b/qiskit/algorithms/gradients/reverse_gradient/derive_circuit.py @@ -14,6 +14,7 @@ from __future__ import annotations import itertools +from collections.abc import Sequence from qiskit.circuit import QuantumCircuit, Parameter, Gate from qiskit.circuit.library import RXGate, RYGate, RZGate, CRXGate, CRYGate, CRZGate @@ -90,7 +91,7 @@ def gradient_lookup(gate: Gate) -> list[tuple[complex, QuantumCircuit]]: def derive_circuit( circuit: QuantumCircuit, parameter: Parameter -) -> list[tuple[complex, QuantumCircuit]]: +) -> Sequence[tuple[complex, QuantumCircuit]]: """Return the analytic gradient expression of the input circuit wrt. a single parameter. Returns a list of ``(coeff, gradient_circuit)`` tuples, where the derivative of the circuit is diff --git a/qiskit/algorithms/gradients/spsa_estimator_gradient.py b/qiskit/algorithms/gradients/spsa_estimator_gradient.py index 5b17713f1855..bb6868e12545 100644 --- a/qiskit/algorithms/gradients/spsa_estimator_gradient.py +++ b/qiskit/algorithms/gradients/spsa_estimator_gradient.py @@ -14,7 +14,7 @@ from __future__ import annotations -from typing import Sequence +from collections.abc import Sequence import numpy as np diff --git a/qiskit/algorithms/gradients/spsa_sampler_gradient.py b/qiskit/algorithms/gradients/spsa_sampler_gradient.py index 622da468c32b..5ffebcc1af60 100644 --- a/qiskit/algorithms/gradients/spsa_sampler_gradient.py +++ b/qiskit/algorithms/gradients/spsa_sampler_gradient.py @@ -15,7 +15,7 @@ from __future__ import annotations from collections import defaultdict -from typing import Sequence +from collections.abc import Sequence import numpy as np @@ -114,7 +114,7 @@ def _run( dist_diffs = {} result = results.quasi_dists[partial_sum_n : partial_sum_n + n] for j, (dist_plus, dist_minus) in enumerate(zip(result[: n // 2], result[n // 2 :])): - dist_diff = defaultdict(float) + dist_diff: dict[int, float] = defaultdict(float) for key, value in dist_plus.items(): dist_diff[key] += value / (2 * self._epsilon) for key, value in dist_minus.items(): @@ -123,7 +123,7 @@ def _run( gradient = [] indices = [circuits[i].parameters.data.index(p) for p in metadata[i]["parameters"]] for j in indices: - gradient_j = defaultdict(float) + gradient_j: dict[int, float] = defaultdict(float) for k in range(self._batch_size): for key, value in dist_diffs[k].items(): gradient_j[key] += value * offsets[i][k][j] diff --git a/qiskit/algorithms/gradients/utils.py b/qiskit/algorithms/gradients/utils.py index f3235f2c0b3a..d3115c8f7e8b 100644 --- a/qiskit/algorithms/gradients/utils.py +++ b/qiskit/algorithms/gradients/utils.py @@ -88,7 +88,7 @@ class LinearCombGradientCircuit: ################################################################################ def _make_param_shift_parameter_values( circuit: QuantumCircuit, - parameter_values: np.ndarray, + parameter_values: np.ndarray | list[float], parameters: Sequence[Parameter], ) -> list[np.ndarray]: """Returns a list of parameter values with offsets for parameter shift rule. @@ -101,7 +101,6 @@ def _make_param_shift_parameter_values( Returns: A list of parameter values with offsets for parameter shift rule. """ - plus_offsets, minus_offsets = [], [] indices = [circuit.parameters.data.index(p) for p in parameters] offset = np.identity(circuit.num_parameters)[indices, :] plus_offsets = parameter_values + offset * np.pi / 2 @@ -313,7 +312,7 @@ def _assign_unique_parameters( else: new_parameter = Parameter(f"__gθ{num_gradient_parameters}") substitution_map[parameter] = new_parameter - parameter_map[parameter].append(new_parameter, 1) + parameter_map[parameter].append((new_parameter, 1)) num_gradient_parameters += 1 gradient_circuit.global_phase = gradient_circuit.global_phase.subs(substitution_map) return GradientCircuit(gradient_circuit, parameter_map, gradient_parameter_map) diff --git a/qiskit/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py b/qiskit/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py index 09bbab4fd2f6..da0d084c20e3 100644 --- a/qiskit/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py +++ b/qiskit/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py @@ -11,9 +11,9 @@ # that they have been altered from the originals. """The Minimum Eigensolver interface""" +from __future__ import annotations from abc import ABC, abstractmethod -from typing import Optional, Tuple import numpy as np @@ -49,7 +49,7 @@ def __init__(self) -> None: @abstractmethod def compute_minimum_eigenvalue( - self, operator: OperatorBase, aux_operators: Optional[ListOrDict[OperatorBase]] = None + self, operator: OperatorBase, aux_operators: ListOrDict[OperatorBase] | None = None ) -> "MinimumEigensolverResult": """ Computes minimum eigenvalue. Operator and aux_operators can be supplied here and @@ -103,12 +103,12 @@ class MinimumEigensolverResult(AlgorithmResult): ) def __init__(self) -> None: super().__init__() - self._eigenvalue = None - self._eigenstate = None - self._aux_operator_eigenvalues = None + self._eigenvalue: complex | None = None + self._eigenstate: np.ndarray | None = None + self._aux_operator_eigenvalues: ListOrDict[tuple[complex, complex]] | None = None @property - def eigenvalue(self) -> Optional[complex]: + def eigenvalue(self) -> complex | None: """returns eigen value""" return self._eigenvalue @@ -118,7 +118,7 @@ def eigenvalue(self, value: complex) -> None: self._eigenvalue = value @property - def eigenstate(self) -> Optional[np.ndarray]: + def eigenstate(self) -> np.ndarray | None: """return eigen state""" return self._eigenstate @@ -128,7 +128,7 @@ def eigenstate(self, value: np.ndarray) -> None: self._eigenstate = value @property - def aux_operator_eigenvalues(self) -> Optional[ListOrDict[Tuple[complex, complex]]]: + def aux_operator_eigenvalues(self) -> ListOrDict[tuple[complex, complex]] | None: """Return aux operator expectation values. These values are in fact tuples formatted as (mean, standard deviation). @@ -136,6 +136,6 @@ def aux_operator_eigenvalues(self) -> Optional[ListOrDict[Tuple[complex, complex return self._aux_operator_eigenvalues @aux_operator_eigenvalues.setter - def aux_operator_eigenvalues(self, value: ListOrDict[Tuple[complex, complex]]) -> None: + def aux_operator_eigenvalues(self, value: ListOrDict[tuple[complex, complex]]) -> None: """set aux operator eigen values""" self._aux_operator_eigenvalues = value diff --git a/qiskit/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py b/qiskit/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py index 227588da509b..31438445b796 100644 --- a/qiskit/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py +++ b/qiskit/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py @@ -11,10 +11,12 @@ # that they have been altered from the originals. """The Numpy Minimum Eigensolver algorithm.""" +from __future__ import annotations -from typing import List, Optional, Union, Callable import logging import warnings +from collections.abc import Callable + import numpy as np from qiskit.opflow import OperatorBase @@ -48,7 +50,7 @@ class NumPyMinimumEigensolver(MinimumEigensolver): def __init__( self, filter_criterion: Callable[ - [Union[List, np.ndarray], float, Optional[ListOrDict[float]]], bool + [list | np.ndarray, float, ListOrDict[float] | None], bool ] = None, ) -> None: """ @@ -69,16 +71,15 @@ def __init__( @property def filter_criterion( self, - ) -> Optional[Callable[[Union[List, np.ndarray], float, Optional[ListOrDict[float]]], bool]]: + ) -> Callable[[list | np.ndarray, float, ListOrDict[float] | None], bool] | None: """returns the filter criterion if set""" return self._ces.filter_criterion @filter_criterion.setter def filter_criterion( self, - filter_criterion: Optional[ - Callable[[Union[List, np.ndarray], float, Optional[ListOrDict[float]]], bool] - ], + filter_criterion: Callable[[list | np.ndarray, float, ListOrDict[float] | None], bool] + | None, ) -> None: """set the filter criterion""" self._ces.filter_criterion = filter_criterion @@ -88,7 +89,7 @@ def supports_aux_operators(cls) -> bool: return NumPyEigensolver.supports_aux_operators() def compute_minimum_eigenvalue( - self, operator: OperatorBase, aux_operators: Optional[ListOrDict[OperatorBase]] = None + self, operator: OperatorBase, aux_operators: ListOrDict[OperatorBase] | None = None ) -> MinimumEigensolverResult: super().compute_minimum_eigenvalue(operator, aux_operators) result_ces = self._ces.compute_eigenvalues(operator, aux_operators) diff --git a/qiskit/algorithms/minimum_eigen_solvers/qaoa.py b/qiskit/algorithms/minimum_eigen_solvers/qaoa.py index 36bb1b68d39b..db31c22694df 100644 --- a/qiskit/algorithms/minimum_eigen_solvers/qaoa.py +++ b/qiskit/algorithms/minimum_eigen_solvers/qaoa.py @@ -11,9 +11,11 @@ # that they have been altered from the originals. """The Quantum Approximate Optimization Algorithm.""" +from __future__ import annotations -from typing import List, Callable, Optional, Union import warnings +from collections.abc import Callable + import numpy as np from qiskit.algorithms.optimizers import Minimizer, Optimizer @@ -66,17 +68,17 @@ class QAOA(VQE): ) def __init__( self, - optimizer: Optional[Union[Optimizer, Minimizer]] = None, + optimizer: Optimizer | Minimizer | None = None, reps: int = 1, - initial_state: Optional[QuantumCircuit] = None, - mixer: Union[QuantumCircuit, OperatorBase] = None, - initial_point: Optional[np.ndarray] = None, - gradient: Optional[Union[GradientBase, Callable[[Union[np.ndarray, List]], List]]] = None, - expectation: Optional[ExpectationBase] = None, + initial_state: QuantumCircuit | None = None, + mixer: QuantumCircuit | OperatorBase = None, + initial_point: np.ndarray | None = None, + gradient: GradientBase | Callable[[np.ndarray | list], list] | None = None, + expectation: ExpectationBase | None = None, include_custom: bool = False, max_evals_grouped: int = 1, - callback: Optional[Callable[[int, np.ndarray, float, float], None]] = None, - quantum_instance: Optional[Union[QuantumInstance, Backend]] = None, + callback: Callable[[int, np.ndarray, float, float], None] | None = None, + quantum_instance: QuantumInstance | Backend | None = None, ) -> None: """ Args: @@ -124,7 +126,7 @@ def __init__( self._reps = reps self._mixer = mixer self._initial_state = initial_state - self._cost_operator = None + self._cost_operator: OperatorBase | None = None with warnings.catch_warnings(): warnings.simplefilter("ignore") @@ -140,7 +142,7 @@ def __init__( quantum_instance=quantum_instance, ) - def _check_operator_ansatz(self, operator: OperatorBase) -> OperatorBase: + def _check_operator_ansatz(self, operator: OperatorBase) -> None: # Recreates a circuit based on operator parameter. if operator != self._cost_operator: self._cost_operator = operator @@ -149,7 +151,7 @@ def _check_operator_ansatz(self, operator: OperatorBase) -> OperatorBase: ).decompose() # TODO remove decompose once #6674 is fixed @property - def initial_state(self) -> Optional[QuantumCircuit]: + def initial_state(self) -> QuantumCircuit | None: """ Returns: Returns the initial state. @@ -157,7 +159,7 @@ def initial_state(self) -> Optional[QuantumCircuit]: return self._initial_state @initial_state.setter - def initial_state(self, initial_state: Optional[QuantumCircuit]) -> None: + def initial_state(self, initial_state: QuantumCircuit | None) -> None: """ Args: initial_state: Initial state to set. @@ -165,7 +167,7 @@ def initial_state(self, initial_state: Optional[QuantumCircuit]) -> None: self._initial_state = initial_state @property - def mixer(self) -> Union[QuantumCircuit, OperatorBase]: + def mixer(self) -> QuantumCircuit | OperatorBase: """ Returns: Returns the mixer. @@ -173,7 +175,7 @@ def mixer(self) -> Union[QuantumCircuit, OperatorBase]: return self._mixer @mixer.setter - def mixer(self, mixer: Union[QuantumCircuit, OperatorBase]) -> None: + def mixer(self, mixer: QuantumCircuit | OperatorBase) -> None: """ Args: mixer: Mixer to set. diff --git a/qiskit/algorithms/minimum_eigen_solvers/vqe.py b/qiskit/algorithms/minimum_eigen_solvers/vqe.py index 6e7d536e20c7..2742edace2ef 100755 --- a/qiskit/algorithms/minimum_eigen_solvers/vqe.py +++ b/qiskit/algorithms/minimum_eigen_solvers/vqe.py @@ -19,8 +19,8 @@ import logging import warnings +from collections.abc import Callable from time import time -from typing import Callable, Dict, List, Optional, Tuple, Union import numpy as np @@ -134,15 +134,15 @@ def my_minimizer(fun, x0, jac=None, bounds=None) -> OptimizerResult: ) def __init__( self, - ansatz: Optional[QuantumCircuit] = None, - optimizer: Optional[Union[Optimizer, Minimizer]] = None, - initial_point: Optional[np.ndarray] = None, - gradient: Optional[Union[GradientBase, Callable]] = None, - expectation: Optional[ExpectationBase] = None, + ansatz: QuantumCircuit | None = None, + optimizer: Optimizer | Minimizer | None = None, + initial_point: np.ndarray | None = None, + gradient: GradientBase | Callable | None = None, + expectation: ExpectationBase | None = None, include_custom: bool = False, max_evals_grouped: int = 1, - callback: Optional[Callable[[int, np.ndarray, float, float], None]] = None, - quantum_instance: Optional[Union[QuantumInstance, Backend]] = None, + callback: Callable[[int, np.ndarray, float, float], None] | None = None, + quantum_instance: QuantumInstance | Backend | None = None, ) -> None: """ @@ -188,35 +188,35 @@ def __init__( super().__init__() self._max_evals_grouped = max_evals_grouped - self._circuit_sampler = None # type: Optional[CircuitSampler] + self._circuit_sampler: CircuitSampler | None = None self._expectation = None self.expectation = expectation self._include_custom = include_custom - self._ansatz = None + self._ansatz: QuantumCircuit | None = None self.ansatz = ansatz - self._optimizer = None + self._optimizer: Optimizer | None = None self.optimizer = optimizer - self._initial_point = None + self._initial_point: np.ndarray | None = None self.initial_point = initial_point - self._gradient = None + self._gradient: GradientBase | Callable | None = None self.gradient = gradient - self._quantum_instance = None + self._quantum_instance: QuantumInstance | None = None if quantum_instance is not None: self.quantum_instance = quantum_instance self._eval_time = None self._eval_count = 0 - self._callback = None + self._callback: Callable[[int, np.ndarray, float, float], None] | None = None self.callback = callback logger.info(self.print_settings()) # TODO remove this once the stateful methods are deleted - self._ret = None + self._ret: VQEResult | None = None @property def ansatz(self) -> QuantumCircuit: @@ -224,7 +224,7 @@ def ansatz(self) -> QuantumCircuit: return self._ansatz @ansatz.setter - def ansatz(self, ansatz: Optional[QuantumCircuit]): + def ansatz(self, ansatz: QuantumCircuit | None): """Sets the ansatz. Args: @@ -238,22 +238,22 @@ def ansatz(self, ansatz: Optional[QuantumCircuit]): self._ansatz = ansatz @property - def gradient(self) -> Optional[Union[GradientBase, Callable]]: + def gradient(self) -> GradientBase | Callable | None: """Returns the gradient.""" return self._gradient @gradient.setter - def gradient(self, gradient: Optional[Union[GradientBase, Callable]]): + def gradient(self, gradient: GradientBase | Callable | None): """Sets the gradient.""" self._gradient = gradient @property - def quantum_instance(self) -> Optional[QuantumInstance]: + def quantum_instance(self) -> QuantumInstance | None: """Returns quantum instance.""" return self._quantum_instance @quantum_instance.setter - def quantum_instance(self, quantum_instance: Union[QuantumInstance, Backend]) -> None: + def quantum_instance(self, quantum_instance: QuantumInstance | Backend) -> None: """Sets quantum_instance""" if not isinstance(quantum_instance, QuantumInstance): quantum_instance = QuantumInstance(quantum_instance) @@ -264,7 +264,7 @@ def quantum_instance(self, quantum_instance: Union[QuantumInstance, Backend]) -> ) @property - def initial_point(self) -> Optional[np.ndarray]: + def initial_point(self) -> np.ndarray | None: """Returns initial point""" return self._initial_point @@ -299,23 +299,23 @@ def include_custom(self, include_custom: bool): self.expectation = None @property - def callback(self) -> Optional[Callable[[int, np.ndarray, float, float], None]]: + def callback(self) -> Callable[[int, np.ndarray, float, float], None] | None: """Returns callback""" return self._callback @callback.setter - def callback(self, callback: Optional[Callable[[int, np.ndarray, float, float], None]]): + def callback(self, callback: Callable[[int, np.ndarray, float, float], None] | None): """Sets callback""" self._callback = callback @property - def expectation(self) -> Optional[ExpectationBase]: + def expectation(self) -> ExpectationBase | None: """The expectation value algorithm used to construct the expectation measurement from the observable.""" return self._expectation @expectation.setter - def expectation(self, exp: Optional[ExpectationBase]) -> None: + def expectation(self, exp: ExpectationBase | None) -> None: self._expectation = exp def _check_operator_ansatz(self, operator: OperatorBase): @@ -338,7 +338,7 @@ def optimizer(self) -> Optimizer: return self._optimizer @optimizer.setter - def optimizer(self, optimizer: Optional[Optimizer]): + def optimizer(self, optimizer: Optimizer | None): """Sets the optimizer attribute. Args: @@ -394,10 +394,10 @@ def print_settings(self): def construct_expectation( self, - parameter: Union[List[float], List[Parameter], np.ndarray], + parameter: list[float] | list[Parameter] | np.ndarray, operator: OperatorBase, return_expectation: bool = False, - ) -> Union[OperatorBase, Tuple[OperatorBase, ExpectationBase]]: + ) -> OperatorBase | tuple[OperatorBase, ExpectationBase]: r""" Generate the ansatz circuit and expectation value measurement, and return their runnable composition. @@ -446,9 +446,9 @@ def construct_expectation( def construct_circuit( self, - parameter: Union[List[float], List[Parameter], np.ndarray], + parameter: list[float] | list[Parameter] | np.ndarray, operator: OperatorBase, - ) -> List[QuantumCircuit]: + ) -> list[QuantumCircuit]: """Return the circuits used to compute the expectation value. Args: @@ -479,7 +479,7 @@ def supports_aux_operators(cls) -> bool: return True def compute_minimum_eigenvalue( - self, operator: OperatorBase, aux_operators: Optional[ListOrDict[OperatorBase]] = None + self, operator: OperatorBase, aux_operators: ListOrDict[OperatorBase] | None = None ) -> MinimumEigensolverResult: super().compute_minimum_eigenvalue(operator, aux_operators) @@ -505,7 +505,7 @@ def compute_minimum_eigenvalue( # Drop None and convert zero values when aux_operators is a dict. if isinstance(aux_operators, list): key_op_iterator = enumerate(aux_operators) - converted = [zero_op] * len(aux_operators) + converted: ListOrDict[OperatorBase] = [zero_op] * len(aux_operators) else: key_op_iterator = aux_operators.items() converted = {} @@ -580,7 +580,9 @@ def get_energy_evaluation( self, operator: OperatorBase, return_expectation: bool = False, - ) -> Callable[[np.ndarray], Union[float, List[float]]]: + ) -> Callable[[np.ndarray], float | list[float]] | tuple[ + Callable[[np.ndarray], float | list[float]], ExpectationBase + ]: """Returns a function handle to evaluates the energy at given parameters for the ansatz. This is the objective function to be passed to the optimizer that is used for evaluation. @@ -642,7 +644,7 @@ def energy_evaluation(parameters): return energy_evaluation - def _get_eigenstate(self, optimal_parameters) -> Union[List[float], Dict[str, int]]: + def _get_eigenstate(self, optimal_parameters) -> list[float] | dict[str, int]: """Get the simulation outcome of the ansatz, provided with parameters.""" optimal_circuit = self.ansatz.bind_parameters(optimal_parameters) state_fn = self._circuit_sampler.convert(StateFn(optimal_circuit)).eval() @@ -675,10 +677,10 @@ def __init__(self) -> None: with warnings.catch_warnings(): warnings.simplefilter("ignore") super().__init__() - self._cost_function_evals = None + self._cost_function_evals: int | None = None @property - def cost_function_evals(self) -> Optional[int]: + def cost_function_evals(self) -> int | None: """Returns number of cost optimizer evaluations""" return self._cost_function_evals @@ -688,7 +690,7 @@ def cost_function_evals(self, value: int) -> None: self._cost_function_evals = value @property - def eigenstate(self) -> Optional[np.ndarray]: + def eigenstate(self) -> np.ndarray | None: """return eigen state""" return self._eigenstate diff --git a/qiskit/algorithms/minimum_eigensolvers/adapt_vqe.py b/qiskit/algorithms/minimum_eigensolvers/adapt_vqe.py index 276e1756a5a1..59674ab7b617 100644 --- a/qiskit/algorithms/minimum_eigensolvers/adapt_vqe.py +++ b/qiskit/algorithms/minimum_eigensolvers/adapt_vqe.py @@ -11,14 +11,14 @@ # that they have been altered from the originals. """An implementation of the AdaptVQE algorithm.""" - from __future__ import annotations +from collections.abc import Sequence from enum import Enum -from typing import Optional, Sequence import re import logging +from typing import Any import numpy as np @@ -135,7 +135,7 @@ def _compute_gradients( self, theta: list[float], operator: OperatorBase, - ) -> list[tuple[complex, complex]]: + ) -> list[tuple[complex, dict[str, Any]]]: """ Computes the gradients for all available excitation operators. @@ -209,9 +209,9 @@ def compute_minimum_eigenvalue( prev_op_indices: list[int] = [] theta: list[float] = [] - max_grad: tuple[float, Optional[PauliSumOp]] = (0.0, None) + max_grad: tuple[complex, dict[str, Any] | None] = (0.0, None) self._excitation_list = [] - history: list[float] = [] + history: list[complex] = [] iteration = 0 while self.max_iterations is None or iteration < self.max_iterations: iteration += 1 @@ -294,10 +294,10 @@ class AdaptVQEResult(VQEResult): def __init__(self) -> None: super().__init__() - self._num_iterations: int = None - self._final_max_gradient: float = None + self._num_iterations: int | None = None + self._final_max_gradient: float | None = None self._termination_criterion: str = "" - self._eigenvalue_history: list[float] = None + self._eigenvalue_history: list[float] | None = None @property def num_iterations(self) -> int: diff --git a/qiskit/algorithms/minimum_eigensolvers/diagonal_estimator.py b/qiskit/algorithms/minimum_eigensolvers/diagonal_estimator.py index 63b09a87ff65..6af526f5ae85 100644 --- a/qiskit/algorithms/minimum_eigensolvers/diagonal_estimator.py +++ b/qiskit/algorithms/minimum_eigensolvers/diagonal_estimator.py @@ -191,7 +191,7 @@ def _evaluate_sparsepauli(state: int, observable: SparsePauliOp) -> complex: return np.sum(observable.coeffs * _PARITY[reduced]) -def _check_observable_is_diagonal(observable: SparsePauliOp) -> bool: +def _check_observable_is_diagonal(observable: SparsePauliOp) -> None: is_diagonal = not np.any(observable.paulis.x) if not is_diagonal: raise ValueError("The observable must be diagonal.") diff --git a/qiskit/algorithms/minimum_eigensolvers/minimum_eigensolver.py b/qiskit/algorithms/minimum_eigensolvers/minimum_eigensolver.py index ec3e280db0d3..26087c053aef 100644 --- a/qiskit/algorithms/minimum_eigensolvers/minimum_eigensolver.py +++ b/qiskit/algorithms/minimum_eigensolvers/minimum_eigensolver.py @@ -72,8 +72,8 @@ class MinimumEigensolverResult(AlgorithmResult): def __init__(self) -> None: super().__init__() - self._eigenvalue = None - self._aux_operators_evaluated = None + self._eigenvalue: complex | None = None + self._aux_operators_evaluated: ListOrDict[tuple[complex, dict[str, Any]]] | None = None @property def eigenvalue(self) -> complex | None: diff --git a/qiskit/algorithms/minimum_eigensolvers/numpy_minimum_eigensolver.py b/qiskit/algorithms/minimum_eigensolvers/numpy_minimum_eigensolver.py index d0664cad23ab..93dc328b282c 100644 --- a/qiskit/algorithms/minimum_eigensolvers/numpy_minimum_eigensolver.py +++ b/qiskit/algorithms/minimum_eigensolvers/numpy_minimum_eigensolver.py @@ -94,7 +94,7 @@ class NumPyMinimumEigensolverResult(MinimumEigensolverResult): def __init__(self) -> None: super().__init__() - self._eigenstate = None + self._eigenstate: Statevector | None = None @property def eigenstate(self) -> Statevector | None: diff --git a/qiskit/algorithms/minimum_eigensolvers/sampling_mes.py b/qiskit/algorithms/minimum_eigensolvers/sampling_mes.py index 6b04bdcf269a..e193f53ce15c 100644 --- a/qiskit/algorithms/minimum_eigensolvers/sampling_mes.py +++ b/qiskit/algorithms/minimum_eigensolvers/sampling_mes.py @@ -68,10 +68,10 @@ class SamplingMinimumEigensolverResult(AlgorithmResult): def __init__(self) -> None: super().__init__() - self._eigenvalue = None - self._eigenstate = None - self._aux_operator_values = None - self._best_measurement = None + self._eigenvalue: complex | None = None + self._eigenstate: QuasiDistribution | None = None + self._aux_operator_values: ListOrDict[tuple[complex, dict[str, Any]]] | None = None + self._best_measurement: Mapping[str, Any] | None = None @property def eigenvalue(self) -> complex | None: diff --git a/qiskit/algorithms/minimum_eigensolvers/sampling_vqe.py b/qiskit/algorithms/minimum_eigensolvers/sampling_vqe.py index 832abdca7998..2fb60355b2a5 100755 --- a/qiskit/algorithms/minimum_eigensolvers/sampling_vqe.py +++ b/qiskit/algorithms/minimum_eigensolvers/sampling_vqe.py @@ -258,7 +258,9 @@ def _get_evaluate_energy( operator: BaseOperator | PauliSumOp, ansatz: QuantumCircuit, return_best_measurement: bool = False, - ) -> tuple[Callable[[np.ndarray], np.ndarray | float], dict]: + ) -> Callable[[np.ndarray], np.ndarray | float] | tuple[ + Callable[[np.ndarray], np.ndarray | float], dict[str, Any] + ]: """Returns a function handle to evaluate the energy at given parameters. This is the objective function to be passed to the optimizer that is used for evaluation. @@ -297,7 +299,7 @@ def store_best_measurement(best): sampler=self.sampler, callback=store_best_measurement, aggregation=self.aggregation ) - def evaluate_energy(parameters): + def evaluate_energy(parameters: np.ndarray) -> np.ndarray | float: nonlocal eval_count # handle broadcasting: ensure parameters is of shape [array, array, ...] parameters = np.reshape(parameters, (-1, num_parameters)).tolist() @@ -351,7 +353,7 @@ class SamplingVQEResult(VariationalResult, SamplingMinimumEigensolverResult): def __init__(self) -> None: super().__init__() - self._cost_function_evals = None + self._cost_function_evals: int | None = None @property def cost_function_evals(self) -> int | None: diff --git a/qiskit/algorithms/minimum_eigensolvers/vqe.py b/qiskit/algorithms/minimum_eigensolvers/vqe.py index 266637253911..f26d3687971c 100644 --- a/qiskit/algorithms/minimum_eigensolvers/vqe.py +++ b/qiskit/algorithms/minimum_eigensolvers/vqe.py @@ -344,7 +344,7 @@ class VQEResult(VariationalResult, MinimumEigensolverResult): def __init__(self) -> None: super().__init__() - self._cost_function_evals = None + self._cost_function_evals: int | None = None @property def cost_function_evals(self) -> int | None: diff --git a/qiskit/algorithms/observables_evaluator.py b/qiskit/algorithms/observables_evaluator.py index 650570b6cae4..3e8f11dfd6e6 100644 --- a/qiskit/algorithms/observables_evaluator.py +++ b/qiskit/algorithms/observables_evaluator.py @@ -114,7 +114,7 @@ def _prepare_result( if isinstance(observables, list): # by construction, all None values will be overwritten - observables_eigenvalues = [None] * len(observables) + observables_eigenvalues: ListOrDict[tuple[complex, complex]] = [None] * len(observables) key_value_iterator = enumerate(observables_results) else: observables_eigenvalues = {} diff --git a/qiskit/algorithms/optimizers/adam_amsgrad.py b/qiskit/algorithms/optimizers/adam_amsgrad.py index e20bf559e0e2..422aa17a5f01 100644 --- a/qiskit/algorithms/optimizers/adam_amsgrad.py +++ b/qiskit/algorithms/optimizers/adam_amsgrad.py @@ -11,8 +11,10 @@ # that they have been altered from the originals. """The Adam and AMSGRAD optimizers.""" +from __future__ import annotations -from typing import Any, Optional, Callable, Dict, Tuple, List +from collections.abc import Callable +from typing import Any import os import csv @@ -73,7 +75,7 @@ def __init__( noise_factor: float = 1e-8, eps: float = 1e-10, amsgrad: bool = False, - snapshot_dir: Optional[str] = None, + snapshot_dir: str | None = None, ) -> None: """ Args: @@ -121,7 +123,7 @@ def __init__( writer.writeheader() @property - def settings(self) -> Dict[str, Any]: + def settings(self) -> dict[str, Any]: return { "maxiter": self._maxiter, "tol": self._tol, @@ -200,12 +202,12 @@ def minimize( self, fun: Callable[[POINT], float], x0: POINT, - jac: Optional[Callable[[POINT], POINT]] = None, - bounds: Optional[List[Tuple[float, float]]] = None, + jac: Callable[[POINT], POINT] | None = None, + bounds: list[tuple[float, float]] | None = None, # pylint:disable=unused-argument - objective_function: Optional[Callable[[np.ndarray], float]] = None, - initial_point: Optional[np.ndarray] = None, - gradient_function: Optional[Callable[[np.ndarray], float]] = None, + objective_function: Callable[[np.ndarray], float] | None = None, + initial_point: np.ndarray | None = None, + gradient_function: Callable[[np.ndarray], float] | None = None, # ) -> Tuple[np.ndarray, float, int]: ) -> OptimizerResult: # TODO find proper way to deprecate return type """Minimize the scalar function. diff --git a/qiskit/algorithms/optimizers/aqgd.py b/qiskit/algorithms/optimizers/aqgd.py index 5ffb0f849599..bedc52301f34 100644 --- a/qiskit/algorithms/optimizers/aqgd.py +++ b/qiskit/algorithms/optimizers/aqgd.py @@ -12,8 +12,10 @@ """Analytical Quantum Gradient Descent (AQGD) optimizer.""" +from __future__ import annotations import logging -from typing import Callable, Tuple, List, Dict, Union, Any, Optional +from collections.abc import Callable +from typing import Any import numpy as np from qiskit.utils.validation import validate_range_exclusive_max @@ -48,10 +50,10 @@ class AQGD(Optimizer): def __init__( self, - maxiter: Union[int, List[int]] = 1000, - eta: Union[float, List[float]] = 1.0, + maxiter: int | list[int] = 1000, + eta: float | list[float] = 1.0, tol: float = 1e-6, # this is tol - momentum: Union[float, List[float]] = 0.25, + momentum: float | list[float] = 0.25, param_tol: float = 1e-6, averaging: int = 10, ) -> None: @@ -97,13 +99,13 @@ def __init__( self._averaging = averaging # state - self._avg_objval = None - self._prev_param = None + self._avg_objval: float | None = None + self._prev_param: np.ndarray | None = None self._eval_count = 0 # function evaluations - self._prev_loss = [] # type: List[float] - self._prev_grad = [] # type: List[List[float]] + self._prev_loss: list[float] = [] + self._prev_grad: list[list[float]] = [] - def get_support_level(self) -> Dict[str, OptimizerSupportLevel]: + def get_support_level(self) -> dict[str, OptimizerSupportLevel]: """Support level dictionary Returns: @@ -117,7 +119,7 @@ def get_support_level(self) -> Dict[str, OptimizerSupportLevel]: } @property - def settings(self) -> Dict[str, Any]: + def settings(self) -> dict[str, Any]: return { "maxiter": self._maxiter, "eta": self._eta, @@ -128,8 +130,8 @@ def settings(self) -> Dict[str, Any]: } def _compute_objective_fn_and_gradient( - self, params: List[float], obj: Callable - ) -> Tuple[float, np.array]: + self, params: np.ndarray | list[float], obj: Callable + ) -> tuple[float, np.ndarray]: """ Obtains the objective function value for params and the analytical quantum derivatives of the objective function with respect to each parameter. Requires @@ -172,7 +174,7 @@ def _update( mprev: np.ndarray, step_size: float, momentum_coeff: float, - ) -> Tuple[List[float], List[float]]: + ) -> tuple[np.ndarray, np.ndarray]: """ Updates full parameter array based on a step that is a convex combination of the gradient and previous momentum @@ -232,7 +234,7 @@ def _converged_objective(self, objval: float, tol: float, window_size: int) -> b return True return False - def _converged_parameter(self, parameter: List[float], tol: float) -> bool: + def _converged_parameter(self, parameter: np.ndarray, tol: float) -> bool: """ Tests convergence based on change in parameter @@ -255,7 +257,7 @@ def _converged_parameter(self, parameter: List[float], tol: float) -> bool: return True return False - def _converged_alt(self, gradient: List[float], tol: float, window_size: int) -> bool: + def _converged_alt(self, gradient: list[float], tol: float, window_size: int) -> bool: """ Tests convergence from norm of windowed average of gradients @@ -295,8 +297,8 @@ def minimize( self, fun: Callable[[POINT], float], x0: POINT, - jac: Optional[Callable[[POINT], POINT]] = None, - bounds: Optional[List[Tuple[float, float]]] = None, + jac: Callable[[POINT], POINT] | None = None, + bounds: list[tuple[float, float]] | None = None, ) -> OptimizerResult: params = np.asarray(x0) momentum = np.zeros(shape=(params.size,)) diff --git a/qiskit/algorithms/optimizers/bobyqa.py b/qiskit/algorithms/optimizers/bobyqa.py index ecec5b529836..39250aef917a 100644 --- a/qiskit/algorithms/optimizers/bobyqa.py +++ b/qiskit/algorithms/optimizers/bobyqa.py @@ -12,7 +12,10 @@ """Bound Optimization BY Quadratic Approximation (BOBYQA) optimizer.""" -from typing import Any, Dict, Tuple, List, Callable, Optional +from __future__ import annotations + +from collections.abc import Callable +from typing import Any import numpy as np from qiskit.utils import optionals as _optionals @@ -54,15 +57,15 @@ def get_support_level(self): } @property - def settings(self) -> Dict[str, Any]: + def settings(self) -> dict[str, Any]: return {"maxiter": self._maxiter} def minimize( self, fun: Callable[[POINT], float], x0: POINT, - jac: Optional[Callable[[POINT], POINT]] = None, - bounds: Optional[List[Tuple[float, float]]] = None, + jac: Callable[[POINT], POINT] | None = None, + bounds: list[tuple[float, float]] | None = None, ) -> OptimizerResult: from skquant import opt as skq diff --git a/qiskit/algorithms/optimizers/cg.py b/qiskit/algorithms/optimizers/cg.py index cb287951fd0a..670b4ac33868 100644 --- a/qiskit/algorithms/optimizers/cg.py +++ b/qiskit/algorithms/optimizers/cg.py @@ -12,7 +12,7 @@ """Conjugate Gradient optimizer.""" -from typing import Optional +from __future__ import annotations from .scipy_optimizer import SciPyOptimizer @@ -39,9 +39,9 @@ def __init__( maxiter: int = 20, disp: bool = False, gtol: float = 1e-5, - tol: Optional[float] = None, + tol: float | None = None, eps: float = 1.4901161193847656e-08, - options: Optional[dict] = None, + options: dict | None = None, max_evals_grouped: int = 1, **kwargs, ) -> None: diff --git a/qiskit/algorithms/optimizers/cobyla.py b/qiskit/algorithms/optimizers/cobyla.py index 359d08a5e1b8..72a0938379e7 100644 --- a/qiskit/algorithms/optimizers/cobyla.py +++ b/qiskit/algorithms/optimizers/cobyla.py @@ -12,7 +12,7 @@ """Constrained Optimization By Linear Approximation optimizer.""" -from typing import Optional +from __future__ import annotations from .scipy_optimizer import SciPyOptimizer @@ -37,8 +37,8 @@ def __init__( maxiter: int = 1000, disp: bool = False, rhobeg: float = 1.0, - tol: Optional[float] = None, - options: Optional[dict] = None, + tol: float | None = None, + options: dict | None = None, **kwargs, ) -> None: """ diff --git a/qiskit/algorithms/optimizers/gradient_descent.py b/qiskit/algorithms/optimizers/gradient_descent.py index a354aa383a2b..03911c21c012 100644 --- a/qiskit/algorithms/optimizers/gradient_descent.py +++ b/qiskit/algorithms/optimizers/gradient_descent.py @@ -11,15 +11,17 @@ # that they have been altered from the originals. """A standard gradient descent optimizer.""" +from __future__ import annotations +from collections.abc import Generator from dataclasses import dataclass, field -from typing import Dict, Any, Union, Callable, Optional, Tuple, List, Iterator +from typing import Any, Callable, SupportsFloat import numpy as np from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT from .steppable_optimizer import AskData, TellData, OptimizerState, SteppableOptimizer from .optimizer_utils import LearningRate -CALLBACK = Callable[[int, np.ndarray, float, float], None] +CALLBACK = Callable[[int, np.ndarray, float, SupportsFloat], None] @dataclass @@ -29,7 +31,7 @@ class GradientDescentState(OptimizerState): Dataclass with all the information of an optimizer plus the learning_rate and the stepsize. """ - stepsize: Optional[float] + stepsize: float | None """Norm of the gradient on the last step.""" learning_rate: LearningRate = field(compare=False) @@ -175,10 +177,13 @@ def grad(x): def __init__( self, maxiter: int = 100, - learning_rate: Union[float, List[float], np.ndarray, Callable[[], Iterator]] = 0.01, + learning_rate: float + | list[float] + | np.ndarray + | Callable[[], Generator[float, None, None]] = 0.01, tol: float = 1e-7, - callback: Optional[CALLBACK] = None, - perturbation: Optional[float] = None, + callback: CALLBACK | None = None, + perturbation: float | None = None, ) -> None: """ Args: @@ -196,7 +201,7 @@ def __init__( """ super().__init__(maxiter=maxiter) self.callback = callback - self._state: Optional[GradientDescentState] = None + self._state: GradientDescentState | None = None self._perturbation = perturbation self._tol = tol # if learning rate is an array, check it is sufficiently long. @@ -231,7 +236,7 @@ def tol(self, tol: float) -> None: self._tol = tol @property - def perturbation(self) -> Optional[float]: + def perturbation(self) -> float | None: """Returns the perturbation. This is the perturbation used in the finite difference gradient approximation. @@ -239,7 +244,7 @@ def perturbation(self) -> Optional[float]: return self._perturbation @perturbation.setter - def perturbation(self, perturbation: Optional[float]) -> None: + def perturbation(self, perturbation: float | None) -> None: """Set the perturbation.""" self._perturbation = perturbation @@ -260,11 +265,13 @@ def _callback_wrapper(self) -> None: ) @property - def settings(self) -> Dict[str, Any]: + def settings(self) -> dict[str, Any]: # if learning rate or perturbation are custom iterators expand them if callable(self.learning_rate): iterator = self.learning_rate() - learning_rate = np.array([next(iterator) for _ in range(self.maxiter)]) + learning_rate: float | np.ndarray = np.array( + [next(iterator) for _ in range(self.maxiter)] + ) else: learning_rate = self.learning_rate @@ -355,8 +362,8 @@ def start( self, fun: Callable[[POINT], float], x0: POINT, - jac: Optional[Callable[[POINT], POINT]] = None, - bounds: Optional[List[Tuple[float, float]]] = None, + jac: Callable[[POINT], POINT] | None = None, + bounds: list[tuple[float, float]] | None = None, ) -> None: self.state = GradientDescentState( diff --git a/qiskit/algorithms/optimizers/gsls.py b/qiskit/algorithms/optimizers/gsls.py index 2649037f4170..6c1446f879cb 100644 --- a/qiskit/algorithms/optimizers/gsls.py +++ b/qiskit/algorithms/optimizers/gsls.py @@ -12,7 +12,10 @@ """Line search with Gaussian-smoothed samples on a sphere.""" -from typing import Dict, Optional, Tuple, List, Callable, Any +from __future__ import annotations + +from collections.abc import Callable +from typing import Any, SupportsFloat import numpy as np from qiskit.utils import algorithm_globals @@ -85,7 +88,7 @@ def __init__( if k in self._OPTIONS: self._options[k] = v - def get_support_level(self) -> Dict[str, int]: + def get_support_level(self) -> dict[str, int]: """Return support level dictionary. Returns: @@ -98,15 +101,15 @@ def get_support_level(self) -> Dict[str, int]: } @property - def settings(self) -> Dict[str, Any]: + def settings(self) -> dict[str, Any]: return {key: self._options.get(key, None) for key in self._OPTIONS} def minimize( self, fun: Callable[[POINT], float], x0: POINT, - jac: Optional[Callable[[POINT], POINT]] = None, - bounds: Optional[List[Tuple[float, float]]] = None, + jac: Callable[[POINT], POINT] | None = None, + bounds: list[tuple[float, float]] | None = None, ) -> OptimizerResult: if not isinstance(x0, np.ndarray): x0 = np.asarray(x0) @@ -118,24 +121,23 @@ def minimize( var_lb = np.array([l for (l, _) in bounds]) var_ub = np.array([u for (_, u) in bounds]) - x, fun, nfev, njev = self.ls_optimize(x0.size, fun, x0, var_lb, var_ub) + x, fun, nfev, _ = self.ls_optimize(x0.size, fun, x0, var_lb, var_ub) result = OptimizerResult() result.x = x result.fun = fun result.nfev = nfev - result.njev = njev return result def ls_optimize( self, n: int, - obj_fun: Callable, + obj_fun: Callable[[POINT], float], initial_point: np.ndarray, var_lb: np.ndarray, var_ub: np.ndarray, - ) -> Tuple[np.ndarray, float, int, float]: + ) -> tuple[np.ndarray, float, int, float]: """Run the line search optimization. Args: @@ -169,7 +171,7 @@ def ls_optimize( prev_directions, prev_sample_set_x, prev_sample_set_y = None, None, None consecutive_fail_iter = 0 alpha = self._options["initial_step_size"] - grad_norm = np.inf + grad_norm: SupportsFloat = np.inf sample_set_size = int(round(self._options["sample_size_factor"] * n)) # Initial point @@ -247,7 +249,7 @@ def ls_optimize( def sample_points( self, n: int, x: np.ndarray, num_points: int - ) -> Tuple[np.ndarray, np.ndarray]: + ) -> tuple[np.ndarray, np.ndarray]: """Sample ``num_points`` points around ``x`` on the ``n``-sphere of specified radius. The radius of the sphere is ``self._options['sampling_radius']``. @@ -269,7 +271,7 @@ def sample_points( def sample_set( self, n: int, x: np.ndarray, var_lb: np.ndarray, var_ub: np.ndarray, num_points: int - ) -> Tuple[np.ndarray, np.ndarray]: + ) -> tuple[np.ndarray, np.ndarray]: """Construct sample set of given size. Args: diff --git a/qiskit/algorithms/optimizers/imfil.py b/qiskit/algorithms/optimizers/imfil.py index be6c5a968bf6..2fca4da2c139 100644 --- a/qiskit/algorithms/optimizers/imfil.py +++ b/qiskit/algorithms/optimizers/imfil.py @@ -11,8 +11,10 @@ # that they have been altered from the originals. """IMplicit FILtering (IMFIL) optimizer.""" +from __future__ import annotations -from typing import Any, Dict, Callable, Optional, List, Tuple +from collections.abc import Callable +from typing import Any from qiskit.utils import optionals as _optionals from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT @@ -55,7 +57,7 @@ def get_support_level(self): } @property - def settings(self) -> Dict[str, Any]: + def settings(self) -> dict[str, Any]: return { "maxiter": self._maxiter, } @@ -64,8 +66,8 @@ def minimize( self, fun: Callable[[POINT], float], x0: POINT, - jac: Optional[Callable[[POINT], POINT]] = None, - bounds: Optional[List[Tuple[float, float]]] = None, + jac: Callable[[POINT], POINT] | None = None, + bounds: list[tuple[float, float]] | None = None, ) -> OptimizerResult: from skquant import opt as skq diff --git a/qiskit/algorithms/optimizers/l_bfgs_b.py b/qiskit/algorithms/optimizers/l_bfgs_b.py index 6b26ee80c6b4..3c2d7a619ef3 100644 --- a/qiskit/algorithms/optimizers/l_bfgs_b.py +++ b/qiskit/algorithms/optimizers/l_bfgs_b.py @@ -12,7 +12,8 @@ """Limited-memory BFGS Bound optimizer.""" -from typing import Optional +from __future__ import annotations +from typing import SupportsFloat import numpy as np @@ -50,10 +51,10 @@ def __init__( self, maxfun: int = 15000, maxiter: int = 15000, - ftol: float = 10 * np.finfo(float).eps, + ftol: SupportsFloat = 10 * np.finfo(float).eps, iprint: int = -1, eps: float = 1e-08, - options: Optional[dict] = None, + options: dict | None = None, max_evals_grouped: int = 1, **kwargs, ): diff --git a/qiskit/algorithms/optimizers/nelder_mead.py b/qiskit/algorithms/optimizers/nelder_mead.py index 318a5f8f63b1..ff9d8708763f 100644 --- a/qiskit/algorithms/optimizers/nelder_mead.py +++ b/qiskit/algorithms/optimizers/nelder_mead.py @@ -11,8 +11,8 @@ # that they have been altered from the originals. """Nelder-Mead optimizer.""" +from __future__ import annotations -from typing import Optional from .scipy_optimizer import SciPyOptimizer @@ -43,13 +43,13 @@ class NELDER_MEAD(SciPyOptimizer): # pylint: disable=invalid-name # pylint: disable=unused-argument def __init__( self, - maxiter: Optional[int] = None, + maxiter: int | None = None, maxfev: int = 1000, disp: bool = False, xatol: float = 0.0001, - tol: Optional[float] = None, + tol: float | None = None, adaptive: bool = False, - options: Optional[dict] = None, + options: dict | None = None, **kwargs, ) -> None: """ diff --git a/qiskit/algorithms/optimizers/nft.py b/qiskit/algorithms/optimizers/nft.py index 1c37665fcd25..07827007e79e 100644 --- a/qiskit/algorithms/optimizers/nft.py +++ b/qiskit/algorithms/optimizers/nft.py @@ -11,8 +11,8 @@ # that they have been altered from the originals. """Nakanishi-Fujii-Todo algorithm.""" +from __future__ import annotations -from typing import Optional import numpy as np from scipy.optimize import OptimizeResult @@ -32,11 +32,11 @@ class NFT(SciPyOptimizer): # pylint: disable=unused-argument def __init__( self, - maxiter: Optional[int] = None, + maxiter: int | None = None, maxfev: int = 1024, disp: bool = False, reset_interval: int = 32, - options: Optional[dict] = None, + options: dict | None = None, **kwargs, ) -> None: """ diff --git a/qiskit/algorithms/optimizers/nlopts/nloptimizer.py b/qiskit/algorithms/optimizers/nlopts/nloptimizer.py index 7bc20c972774..65f56b930482 100644 --- a/qiskit/algorithms/optimizers/nlopts/nloptimizer.py +++ b/qiskit/algorithms/optimizers/nlopts/nloptimizer.py @@ -11,8 +11,9 @@ # that they have been altered from the originals. """Minimize using objective function""" +from __future__ import annotations -from typing import List, Optional, Tuple, Callable +from collections.abc import Callable from enum import Enum from abc import abstractmethod import logging @@ -86,8 +87,8 @@ def minimize( self, fun: Callable[[POINT], float], x0: POINT, - jac: Optional[Callable[[POINT], POINT]] = None, - bounds: Optional[List[Tuple[float, float]]] = None, + jac: Callable[[POINT], POINT] | None = None, + bounds: list[tuple[float, float]] | None = None, ) -> OptimizerResult: import nlopt diff --git a/qiskit/algorithms/optimizers/optimizer.py b/qiskit/algorithms/optimizers/optimizer.py index c9201bd8d499..3f64384222d9 100644 --- a/qiskit/algorithms/optimizers/optimizer.py +++ b/qiskit/algorithms/optimizers/optimizer.py @@ -15,10 +15,11 @@ from __future__ import annotations from abc import ABC, abstractmethod +from collections.abc import Callable from enum import IntEnum import logging import sys -from typing import Any, Callable, Dict, List, Optional, Tuple, Union +from typing import Any, Union import numpy as np import scipy @@ -41,70 +42,70 @@ class OptimizerResult(AlgorithmResult): def __init__(self) -> None: super().__init__() - self._x = None - self._fun = None - self._jac = None - self._nfev = None - self._njev = None - self._nit = None + self._x: POINT | None = None + self._fun: float | None = None + self._jac: POINT | None = None + self._nfev: int | None = None + self._njev: int | None = None + self._nit: int | None = None @property - def x(self) -> Optional[POINT]: + def x(self) -> POINT | None: """The final point of the minimization.""" return self._x @x.setter - def x(self, x: Optional[POINT]) -> None: + def x(self, x: POINT | None) -> None: """Set the final point of the minimization.""" self._x = x @property - def fun(self) -> Optional[float]: + def fun(self) -> float | None: """The final value of the minimization.""" return self._fun @fun.setter - def fun(self, fun: Optional[float]) -> None: + def fun(self, fun: float | None) -> None: """Set the final value of the minimization.""" self._fun = fun @property - def jac(self) -> Optional[POINT]: + def jac(self) -> POINT | None: """The final gradient of the minimization.""" return self._jac @jac.setter - def jac(self, jac: Optional[POINT]) -> None: + def jac(self, jac: POINT | None) -> None: """Set the final gradient of the minimization.""" self._jac = jac @property - def nfev(self) -> Optional[int]: + def nfev(self) -> int | None: """The total number of function evaluations.""" return self._nfev @nfev.setter - def nfev(self, nfev: Optional[int]) -> None: + def nfev(self, nfev: int | None) -> None: """Set the total number of function evaluations.""" self._nfev = nfev @property - def njev(self) -> Optional[int]: + def njev(self) -> int | None: """The total number of gradient evaluations.""" return self._njev @njev.setter - def njev(self, njev: Optional[int]) -> None: + def njev(self, njev: int | None) -> None: """Set the total number of gradient evaluations.""" self._njev = njev @property - def nit(self) -> Optional[int]: + def nit(self) -> int | None: """The total number of iterations.""" return self._nit @nit.setter - def nit(self, nit: Optional[int]) -> None: + def nit(self, nit: int | None) -> None: """Set the total number of iterations.""" self._nit = nit @@ -287,7 +288,7 @@ def setting(self): return ret @property - def settings(self) -> Dict[str, Any]: + def settings(self) -> dict[str, Any]: """The optimizer settings in a dictionary format. The settings can for instance be used for JSON-serialization (if all settings are @@ -308,8 +309,8 @@ def minimize( self, fun: Callable[[POINT], float], x0: POINT, - jac: Optional[Callable[[POINT], POINT]] = None, - bounds: Optional[List[Tuple[float, float]]] = None, + jac: Callable[[POINT], POINT] | None = None, + bounds: list[tuple[float, float]] | None = None, ) -> OptimizerResult: """Minimize the scalar function. diff --git a/qiskit/algorithms/optimizers/optimizer_utils/learning_rate.py b/qiskit/algorithms/optimizers/optimizer_utils/learning_rate.py index 8ba8a0c69ca5..7bfea636ce2c 100644 --- a/qiskit/algorithms/optimizers/optimizer_utils/learning_rate.py +++ b/qiskit/algorithms/optimizers/optimizer_utils/learning_rate.py @@ -11,8 +11,9 @@ # that they have been altered from the originals. """A class to represent the Learning Rate.""" +from __future__ import annotations -from typing import Union, Callable, Optional, List, Iterator, Generator +from collections.abc import Generator, Callable from itertools import tee import numpy as np @@ -27,7 +28,11 @@ class LearningRate(Generator): """ def __init__( - self, learning_rate: Union[float, List[float], np.ndarray, Callable[[], Iterator]] + self, + learning_rate: float + | list[float] + | np.ndarray + | Callable[[], Generator[float, None, None]], ): """ Args: @@ -42,7 +47,7 @@ def __init__( else: self._gen = learning_rate() - self._current: Optional[float] = None + self._current: float | None = None def send(self, value): """Send a value into the generator. diff --git a/qiskit/algorithms/optimizers/p_bfgs.py b/qiskit/algorithms/optimizers/p_bfgs.py index e3aa811a4184..a59a15cbca64 100644 --- a/qiskit/algorithms/optimizers/p_bfgs.py +++ b/qiskit/algorithms/optimizers/p_bfgs.py @@ -11,12 +11,14 @@ # that they have been altered from the originals. """Parallelized Limited-memory BFGS optimizer""" +from __future__ import annotations import logging import multiprocessing import platform import sys -from typing import Optional, List, Tuple, Callable +from collections.abc import Callable +from typing import SupportsFloat import numpy as np @@ -49,10 +51,10 @@ class P_BFGS(SciPyOptimizer): # pylint: disable=invalid-name def __init__( self, maxfun: int = 1000, - ftol: float = 10 * np.finfo(float).eps, + ftol: SupportsFloat = 10 * np.finfo(float).eps, iprint: int = -1, - max_processes: Optional[int] = None, - options: Optional[dict] = None, + max_processes: int | None = None, + options: dict | None = None, max_evals_grouped: int = 1, **kwargs, ) -> None: @@ -91,8 +93,8 @@ def minimize( self, fun: Callable[[POINT], float], x0: POINT, - jac: Optional[Callable[[POINT], POINT]] = None, - bounds: Optional[List[Tuple[float, float]]] = None, + jac: Callable[[POINT], POINT] | None = None, + bounds: list[tuple[float, float]] | None = None, ) -> OptimizerResult: x0 = np.asarray(x0) @@ -119,7 +121,7 @@ def minimize( "For Windows, using only current process. Multiple core use not supported." ) - queue = multiprocessing.Queue() + queue: multiprocessing.queues.Queue[tuple[POINT, float, int]] = multiprocessing.Queue() # TODO: are automatic bounds a good idea? What if the circuit parameters are not # just from plain Pauli rotations but have a coefficient? @@ -170,7 +172,7 @@ def _optimize( initial_point, gradient_function=None, variable_bounds=None, - ): + ) -> tuple[POINT, float, int]: result = super().minimize( objective_function, initial_point, gradient_function, variable_bounds ) diff --git a/qiskit/algorithms/optimizers/powell.py b/qiskit/algorithms/optimizers/powell.py index 2cb70d03c613..de8cbb1b9d18 100644 --- a/qiskit/algorithms/optimizers/powell.py +++ b/qiskit/algorithms/optimizers/powell.py @@ -11,8 +11,7 @@ # that they have been altered from the originals. """Powell optimizer.""" - -from typing import Optional +from __future__ import annotations from .scipy_optimizer import SciPyOptimizer @@ -37,12 +36,12 @@ class POWELL(SciPyOptimizer): # pylint: disable=unused-argument def __init__( self, - maxiter: Optional[int] = None, + maxiter: int | None = None, maxfev: int = 1000, disp: bool = False, xtol: float = 0.0001, - tol: Optional[float] = None, - options: Optional[dict] = None, + tol: float | None = None, + options: dict | None = None, **kwargs, ) -> None: """ diff --git a/qiskit/algorithms/optimizers/qnspsa.py b/qiskit/algorithms/optimizers/qnspsa.py index 2bf6e4520ef2..93f79b18d1ce 100644 --- a/qiskit/algorithms/optimizers/qnspsa.py +++ b/qiskit/algorithms/optimizers/qnspsa.py @@ -13,7 +13,9 @@ """The QN-SPSA optimizer.""" from __future__ import annotations -from typing import Any, Iterator, Callable + +from collections.abc import Iterator +from typing import Any, Callable import numpy as np from qiskit.providers import Backend diff --git a/qiskit/algorithms/optimizers/scipy_optimizer.py b/qiskit/algorithms/optimizers/scipy_optimizer.py index b9bde78f8588..4c628a4ab5ec 100644 --- a/qiskit/algorithms/optimizers/scipy_optimizer.py +++ b/qiskit/algorithms/optimizers/scipy_optimizer.py @@ -11,8 +11,10 @@ # that they have been altered from the originals. """Wrapper class of scipy.optimize.minimize.""" +from __future__ import annotations -from typing import Any, Callable, Dict, Union, List, Optional, Tuple +from collections.abc import Callable +from typing import Any import numpy as np from scipy.optimize import minimize @@ -46,8 +48,8 @@ class SciPyOptimizer(Optimizer): def __init__( self, - method: Union[str, Callable], - options: Optional[Dict[str, Any]] = None, + method: str | Callable, + options: dict[str, Any] | None = None, max_evals_grouped: int = 1, **kwargs, ): @@ -84,7 +86,7 @@ def get_support_level(self): } @property - def settings(self) -> Dict[str, Any]: + def settings(self) -> dict[str, Any]: options = self._options.copy() if hasattr(self, "_OPTIONS"): # all _OPTIONS should be keys in self._options, but add a failsafe here @@ -112,8 +114,8 @@ def minimize( self, fun: Callable[[POINT], float], x0: POINT, - jac: Optional[Callable[[POINT], POINT]] = None, - bounds: Optional[List[Tuple[float, float]]] = None, + jac: Callable[[POINT], POINT] | None = None, + bounds: list[tuple[float, float]] | None = None, ) -> OptimizerResult: # Remove ignored parameters to supress the warning of scipy.optimize.minimize if self.is_bounds_ignored: diff --git a/qiskit/algorithms/optimizers/slsqp.py b/qiskit/algorithms/optimizers/slsqp.py index e9073ebbe44b..d02eb790afa9 100644 --- a/qiskit/algorithms/optimizers/slsqp.py +++ b/qiskit/algorithms/optimizers/slsqp.py @@ -11,8 +11,8 @@ # that they have been altered from the originals. """Sequential Least SQuares Programming optimizer""" +from __future__ import annotations -from typing import Optional from .scipy_optimizer import SciPyOptimizer @@ -42,9 +42,9 @@ def __init__( maxiter: int = 100, disp: bool = False, ftol: float = 1e-06, - tol: Optional[float] = None, + tol: float | None = None, eps: float = 1.4901161193847656e-08, - options: Optional[dict] = None, + options: dict | None = None, max_evals_grouped: int = 1, **kwargs, ) -> None: diff --git a/qiskit/algorithms/optimizers/snobfit.py b/qiskit/algorithms/optimizers/snobfit.py index 8c87fd60b41d..8d6a3bde1d07 100644 --- a/qiskit/algorithms/optimizers/snobfit.py +++ b/qiskit/algorithms/optimizers/snobfit.py @@ -11,8 +11,10 @@ # that they have been altered from the originals. """Stable Noisy Optimization by Branch and FIT algorithm (SNOBFIT) optimizer.""" +from __future__ import annotations -from typing import Any, Dict, Optional, Callable, Tuple, List +from collections.abc import Callable +from typing import Any import numpy as np from qiskit.exceptions import QiskitError @@ -77,7 +79,7 @@ def get_support_level(self): } @property - def settings(self) -> Dict[str, Any]: + def settings(self) -> dict[str, Any]: return { "maxiter": self._maxiter, "maxfail": self._maxfail, @@ -89,8 +91,8 @@ def minimize( self, fun: Callable[[POINT], float], x0: POINT, - jac: Optional[Callable[[POINT], POINT]] = None, - bounds: Optional[List[Tuple[float, float]]] = None, + jac: Callable[[POINT], POINT] | None = None, + bounds: list[tuple[float, float]] | None = None, ) -> OptimizerResult: import skquant.opt as skq from SQSnobFit import optset diff --git a/qiskit/algorithms/optimizers/spsa.py b/qiskit/algorithms/optimizers/spsa.py index 4f6c5a29212b..0226f8a00a1e 100644 --- a/qiskit/algorithms/optimizers/spsa.py +++ b/qiskit/algorithms/optimizers/spsa.py @@ -14,13 +14,15 @@ This implementation allows both, standard first-order as well as second-order SPSA. """ +from __future__ import annotations -from typing import Iterator, Optional, Union, Callable, Tuple, Dict, List, Any +from collections import deque +from collections.abc import Iterator +from typing import Callable, Any, SupportsFloat import logging import warnings from time import time -from collections import deque import scipy import numpy as np @@ -30,8 +32,8 @@ from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT # number of function evaluations, parameters, loss, stepsize, accepted -CALLBACK = Callable[[int, np.ndarray, float, float, bool], None] -TERMINATIONCHECKER = Callable[[int, np.ndarray, float, float, bool], bool] +CALLBACK = Callable[[int, np.ndarray, float, SupportsFloat, bool], None] +TERMINATIONCHECKER = Callable[[int, np.ndarray, float, SupportsFloat, bool], bool] logger = logging.getLogger(__name__) @@ -163,20 +165,20 @@ def __init__( self, maxiter: int = 100, blocking: bool = False, - allowed_increase: Optional[float] = None, + allowed_increase: float | None = None, trust_region: bool = False, - learning_rate: Optional[Union[float, np.array, Callable[[], Iterator]]] = None, - perturbation: Optional[Union[float, np.array, Callable[[], Iterator]]] = None, + learning_rate: float | np.ndarray | Callable[[], Iterator] | None = None, + perturbation: float | np.ndarray | Callable[[], Iterator] | None = None, last_avg: int = 1, - resamplings: Union[int, Dict[int, int]] = 1, - perturbation_dims: Optional[int] = None, + resamplings: int | dict[int, int] = 1, + perturbation_dims: int | None = None, second_order: bool = False, - regularization: Optional[float] = None, + regularization: float | None = None, hessian_delay: int = 0, - lse_solver: Optional[Callable[[np.ndarray, np.ndarray], np.ndarray]] = None, - initial_hessian: Optional[np.ndarray] = None, - callback: Optional[CALLBACK] = None, - termination_checker: Optional[TERMINATIONCHECKER] = None, + lse_solver: Callable[[np.ndarray, np.ndarray], np.ndarray] | None = None, + initial_hessian: np.ndarray | None = None, + callback: CALLBACK | None = None, + termination_checker: TERMINATIONCHECKER | None = None, ) -> None: r""" Args: @@ -275,8 +277,8 @@ def __init__( self.initial_hessian = initial_hessian # runtime arguments - self._nfev = None # the number of function evaluations - self._smoothed_hessian = None # smoothed average of the Hessians + self._nfev: int | None = None # the number of function evaluations + self._smoothed_hessian: np.ndarray | None = None # smoothed average of the Hessians @staticmethod def calibrate( @@ -284,12 +286,12 @@ def calibrate( initial_point: np.ndarray, c: float = 0.2, stability_constant: float = 0, - target_magnitude: Optional[float] = None, # 2 pi / 10 + target_magnitude: float | None = None, # 2 pi / 10 alpha: float = 0.602, gamma: float = 0.101, modelspace: bool = False, max_evals_grouped: int = 1, - ) -> Tuple[Iterator[float], Iterator[float]]: + ) -> tuple[Callable, Callable]: r"""Calibrate SPSA parameters with a powerseries as learning rate and perturbation coeffs. The powerseries are: @@ -332,7 +334,7 @@ def calibrate( losses = _batch_evaluate(loss, points, max_evals_grouped) - avg_magnitudes = 0 + avg_magnitudes = 0.0 for i in range(steps): delta = losses[2 * i] - losses[2 * i + 1] avg_magnitudes += np.abs(delta / (2 * c)) @@ -379,7 +381,7 @@ def estimate_stddev( return np.std(losses) @property - def settings(self) -> Dict[str, Any]: + def settings(self) -> dict[str, Any]: # if learning rate or perturbation are custom iterators expand them if callable(self.learning_rate): iterator = self.learning_rate() @@ -503,8 +505,8 @@ def minimize( self, fun: Callable[[POINT], float], x0: POINT, - jac: Optional[Callable[[POINT], POINT]] = None, - bounds: Optional[List[Tuple[float, float]]] = None, + jac: Callable[[POINT], POINT] | None = None, + bounds: list[tuple[float, float]] | None = None, ) -> OptimizerResult: # ensure learning rate and perturbation are correctly set: either none or both # this happens only here because for the calibration the loss function is required diff --git a/qiskit/algorithms/optimizers/steppable_optimizer.py b/qiskit/algorithms/optimizers/steppable_optimizer.py index 2330d65df01a..0c2e73ce5909 100644 --- a/qiskit/algorithms/optimizers/steppable_optimizer.py +++ b/qiskit/algorithms/optimizers/steppable_optimizer.py @@ -11,10 +11,11 @@ # that they have been altered from the originals. """SteppableOptimizer interface""" +from __future__ import annotations from abc import abstractmethod, ABC +from collections.abc import Callable from dataclasses import dataclass -from typing import Union, Callable, Optional, Tuple, List from .optimizer import Optimizer, POINT, OptimizerResult @@ -30,8 +31,8 @@ class AskData(ABC): """ - x_fun: Optional[Union[POINT, List[POINT]]] = None - x_jac: Optional[Union[POINT, List[POINT]]] = None + x_fun: POINT | list[POINT] | None = None + x_jac: POINT | list[POINT] | None = None @dataclass @@ -44,8 +45,8 @@ class TellData(ABC): """ - eval_fun: Union[float, List[float], None] = None - eval_jac: Union[POINT, List[POINT], None] = None + eval_fun: float | list[float] | None = None + eval_jac: POINT | list[POINT] | None = None @dataclass @@ -61,15 +62,15 @@ class OptimizerState: x: POINT """Current optimization parameters.""" - fun: Optional[Callable[[POINT], float]] + fun: Callable[[POINT], float] | None """Function being optimized.""" - jac: Optional[Callable[[POINT], POINT]] + jac: Callable[[POINT], POINT] | None """Jacobian of the function being optimized.""" - nfev: Optional[int] + nfev: int | None """Number of function evaluations so far in the optimization.""" - njev: Optional[int] + njev: int | None """Number of jacobian evaluations so far in the opimization.""" - nit: Optional[int] + nit: int | None """Number of optmization steps performed so far in the optimization.""" @@ -157,7 +158,7 @@ def __init__( maxiter: Number of steps in the optimization process before ending the loop. """ super().__init__() - self._state: Optional[OptimizerState] = None + self._state: OptimizerState | None = None self.maxiter = maxiter @property @@ -236,8 +237,8 @@ def start( self, fun: Callable[[POINT], float], x0: POINT, - jac: Optional[Callable[[POINT], POINT]] = None, - bounds: Optional[List[Tuple[float, float]]] = None, + jac: Callable[[POINT], POINT] | None = None, + bounds: list[tuple[float, float]] | None = None, ) -> None: """Populates the state of the optimizer with the data provided and sets all the counters to 0. @@ -254,8 +255,8 @@ def minimize( self, fun: Callable[[POINT], float], x0: POINT, - jac: Optional[Callable[[POINT], POINT]] = None, - bounds: Optional[List[Tuple[float, float]]] = None, + jac: Callable[[POINT], POINT] | None = None, + bounds: list[tuple[float, float]] | None = None, ) -> OptimizerResult: """Minimizes the function. diff --git a/qiskit/algorithms/optimizers/tnc.py b/qiskit/algorithms/optimizers/tnc.py index 11bca2638a89..06174e51ace9 100644 --- a/qiskit/algorithms/optimizers/tnc.py +++ b/qiskit/algorithms/optimizers/tnc.py @@ -11,8 +11,8 @@ # that they have been altered from the originals. """Truncated Newton (TNC) optimizer.""" +from __future__ import annotations -from typing import Optional from .scipy_optimizer import SciPyOptimizer @@ -42,9 +42,9 @@ def __init__( ftol: float = -1, xtol: float = -1, gtol: float = -1, - tol: Optional[float] = None, + tol: float | None = None, eps: float = 1e-08, - options: Optional[dict] = None, + options: dict | None = None, max_evals_grouped: int = 1, **kwargs, ) -> None: diff --git a/qiskit/algorithms/optimizers/umda.py b/qiskit/algorithms/optimizers/umda.py index b93df1b86e4f..3e84d59b7ad8 100644 --- a/qiskit/algorithms/optimizers/umda.py +++ b/qiskit/algorithms/optimizers/umda.py @@ -11,8 +11,10 @@ # that they have been altered from the originals. """Univariate Marginal Distribution Algorithm (Estimation-of-Distribution-Algorithm).""" +from __future__ import annotations -from typing import Callable, List, Optional, Tuple, Dict, Any +from collections.abc import Callable +from typing import Any import numpy as np from scipy.stats import norm from qiskit.utils import algorithm_globals @@ -131,22 +133,22 @@ def __init__(self, maxiter: int = 100, size_gen: int = 20, alpha: float = 0.5) - self.size_gen = size_gen self.maxiter = maxiter self.alpha = alpha - self._vector = None + self._vector: np.ndarray | None = None # initialization of generation - self._generation = None + self._generation: np.ndarray | None = None self._dead_iter = int(self._maxiter / 5) self._truncation_length = int(size_gen * alpha) super().__init__() - self._best_cost_global = None - self._best_ind_global = None - self._evaluations = None + self._best_cost_global: float | None = None + self._best_ind_global: int | None = None + self._evaluations: np.ndarray | None = None - self._n_variables = None + self._n_variables: int | None = None - def _initialization(self): + def _initialization(self) -> np.ndarray: vector = np.zeros((4, self._n_variables)) vector[0, :] = np.pi # mu @@ -196,14 +198,17 @@ def minimize( self, fun: Callable[[POINT], float], x0: POINT, - jac: Optional[Callable[[POINT], POINT]] = None, - bounds: Optional[List[Tuple[float, float]]] = None, + jac: Callable[[POINT], POINT] | None = None, + bounds: list[tuple[float, float]] | None = None, ) -> OptimizerResult: not_better_count = 0 result = OptimizerResult() + if isinstance(x0, float): + x0 = [x0] self._n_variables = len(x0) + self._best_cost_global = 999999999999 self._best_ind_global = 9999999 history = [] @@ -221,7 +226,7 @@ def minimize( self._truncation() self._update_vector() - best_mae_local = min(self._evaluations) + best_mae_local: float = min(self._evaluations) history.append(best_mae_local) best_ind_local = np.where(self._evaluations == best_mae_local)[0][0] @@ -311,7 +316,7 @@ def alpha(self, value: float): self._alpha = value @property - def settings(self) -> Dict[str, Any]: + def settings(self) -> dict[str, Any]: return { "maxiter": self.maxiter, "alpha": self.alpha, diff --git a/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation_result.py b/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation_result.py index b76924fcd2e5..ce844427b04a 100644 --- a/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation_result.py +++ b/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation_result.py @@ -13,7 +13,9 @@ """Result from running HamiltonianPhaseEstimation""" from __future__ import annotations -from typing import cast, Mapping + +from collections.abc import Mapping +from typing import cast from qiskit.algorithms.algorithm_result import AlgorithmResult from .phase_estimation_result import PhaseEstimationResult from .phase_estimation_scale import PhaseEstimationScale diff --git a/qiskit/algorithms/phase_estimators/phase_estimation_result.py b/qiskit/algorithms/phase_estimators/phase_estimation_result.py index 166ae162e0b6..8d87571af7e7 100644 --- a/qiskit/algorithms/phase_estimators/phase_estimation_result.py +++ b/qiskit/algorithms/phase_estimators/phase_estimation_result.py @@ -134,7 +134,7 @@ def filter_phases(self, cutoff: float = 0.0, as_float: bool = True) -> dict: # in reverse order. So, we reverse the bitstrings here. binary_phase_string = numpy.binary_repr(idx, self._num_evaluation_qubits)[::-1] if as_float: - _key = _bit_string_to_phase(binary_phase_string) + _key: str | float = _bit_string_to_phase(binary_phase_string) else: _key = binary_phase_string phases[_key] = amplitude diff --git a/qiskit/algorithms/phase_estimators/phase_estimation_scale.py b/qiskit/algorithms/phase_estimators/phase_estimation_scale.py index 268f1a9aae31..e22b3e18cd9e 100644 --- a/qiskit/algorithms/phase_estimators/phase_estimation_scale.py +++ b/qiskit/algorithms/phase_estimators/phase_estimation_scale.py @@ -141,7 +141,7 @@ def from_pauli_sum( bound = sum(abs(coeff) for coeff in pauli_sum.coeffs) return PhaseEstimationScale(bound) elif isinstance(pauli_sum, Operator): - bound = np.sum(np.abs(np.eigvalsh(pauli_sum))) + bound = np.sum(np.abs(np.linalg.eigvalsh(pauli_sum))) return PhaseEstimationScale(bound) elif isinstance(pauli_sum, BaseOperator): raise ValueError( diff --git a/qiskit/algorithms/state_fidelities/base_state_fidelity.py b/qiskit/algorithms/state_fidelities/base_state_fidelity.py index b6e12be764fc..9395889bc5da 100644 --- a/qiskit/algorithms/state_fidelities/base_state_fidelity.py +++ b/qiskit/algorithms/state_fidelities/base_state_fidelity.py @@ -44,7 +44,7 @@ class BaseStateFidelity(ABC): def __init__(self) -> None: # use cache for preventing unnecessary circuit compositions - self._circuit_cache: Mapping[(int, int), QuantumCircuit] = {} + self._circuit_cache: Mapping[tuple[int, int], QuantumCircuit] = {} @staticmethod def _preprocess_values( diff --git a/qiskit/algorithms/time_evolvers/classical_methods/evolve.py b/qiskit/algorithms/time_evolvers/classical_methods/evolve.py index c170a29e4404..18c679a03c8f 100644 --- a/qiskit/algorithms/time_evolvers/classical_methods/evolve.py +++ b/qiskit/algorithms/time_evolvers/classical_methods/evolve.py @@ -77,7 +77,7 @@ def _create_obs_final( """ aux_ops = evolution_problem.aux_operators - aux_ops_evaluated = [(op_ev, 0) for op_ev in ops_ev_mean] + aux_ops_evaluated: ListOrDict[tuple[complex, complex]] = [(op_ev, 0) for op_ev in ops_ev_mean] if isinstance(aux_ops, dict): aux_ops_evaluated = dict(zip(aux_ops.keys(), aux_ops_evaluated)) return aux_ops_evaluated @@ -86,11 +86,11 @@ def _create_obs_final( def _evaluate_aux_ops( aux_ops: list[csr_matrix], state: np.ndarray, -) -> tuple[np.ndarray, np.ndarray]: +) -> np.ndarray: """Evaluates the aux operators if they are provided and stores their value. Returns: - Tuple of the mean and standard deviation of the aux operators for a given state. + Mean of the aux operators for a given state. """ op_means = np.array([np.real(state.conjugate().dot(op.dot(state))) for op in aux_ops]) return op_means diff --git a/qiskit/algorithms/time_evolvers/pvqd/pvqd.py b/qiskit/algorithms/time_evolvers/pvqd/pvqd.py index 5fb82b586f6b..ea3d82dc4d74 100644 --- a/qiskit/algorithms/time_evolvers/pvqd/pvqd.py +++ b/qiskit/algorithms/time_evolvers/pvqd/pvqd.py @@ -14,7 +14,7 @@ from __future__ import annotations import logging -from typing import Callable +from collections.abc import Callable import numpy as np @@ -242,7 +242,7 @@ def get_loss( x = ParameterVector("w", ansatz.num_parameters) shifted = ansatz.assign_parameters(current_parameters + x) - def evaluate_loss(displacement: np.ndarray | list[np.ndarray]) -> float | list[float]: + def evaluate_loss(displacement: np.ndarray | list[np.ndarray]) -> float | np.ndarray: """Evaluate the overlap of the ansatz with the Trotterized evolution. Args: @@ -368,7 +368,7 @@ def evolve(self, evolution_problem: TimeEvolutionProblem) -> TimeEvolutionResult ) observable_values = [evaluate_observables(self.initial_parameters)] - fidelities = [1] + fidelities = [1.0] parameters = [self.initial_parameters] times = np.linspace(0, time, num_timesteps + 1).tolist() # +1 to include initial time 0 diff --git a/qiskit/algorithms/time_evolvers/pvqd/pvqd_result.py b/qiskit/algorithms/time_evolvers/pvqd/pvqd_result.py index 6f8a34cb89a9..65c2a8b18604 100644 --- a/qiskit/algorithms/time_evolvers/pvqd/pvqd_result.py +++ b/qiskit/algorithms/time_evolvers/pvqd/pvqd_result.py @@ -13,8 +13,9 @@ """Result object for p-VQD.""" from __future__ import annotations -import numpy as np +from collections.abc import Sequence +import numpy as np from qiskit.circuit import QuantumCircuit from ..time_evolution_result import TimeEvolutionResult @@ -28,7 +29,7 @@ def __init__( aux_ops_evaluated: list[tuple[complex, complex]] | None = None, times: list[float] | None = None, parameters: list[np.ndarray] | None = None, - fidelities: list[float] | None = None, + fidelities: Sequence[float] | None = None, estimated_error: float | None = None, observables: list[list[float]] | None = None, ): diff --git a/qiskit/algorithms/time_evolvers/pvqd/utils.py b/qiskit/algorithms/time_evolvers/pvqd/utils.py index 47e638d97011..9b3f330dd350 100644 --- a/qiskit/algorithms/time_evolvers/pvqd/utils.py +++ b/qiskit/algorithms/time_evolvers/pvqd/utils.py @@ -14,7 +14,7 @@ """Utilities for p-VQD.""" from __future__ import annotations import logging -from typing import Callable +from collections.abc import Callable import numpy as np diff --git a/qiskit/algorithms/time_evolvers/time_evolution_problem.py b/qiskit/algorithms/time_evolvers/time_evolution_problem.py index 8e767d92ca7d..87159558baf5 100644 --- a/qiskit/algorithms/time_evolvers/time_evolution_problem.py +++ b/qiskit/algorithms/time_evolvers/time_evolution_problem.py @@ -84,7 +84,7 @@ def __init__( circuit = QuantumCircuit(initial_state.num_qubits) circuit.prepare_state(initial_state.data) initial_state = circuit - self.initial_state = initial_state + self.initial_state: QuantumCircuit | None = initial_state self.aux_operators = aux_operators self.truncation_threshold = truncation_threshold diff --git a/qiskit/algorithms/time_evolvers/variational/solvers/ode/abstract_ode_function.py b/qiskit/algorithms/time_evolvers/variational/solvers/ode/abstract_ode_function.py index 373b736ff4d9..b94ded552a81 100644 --- a/qiskit/algorithms/time_evolvers/variational/solvers/ode/abstract_ode_function.py +++ b/qiskit/algorithms/time_evolvers/variational/solvers/ode/abstract_ode_function.py @@ -14,8 +14,7 @@ from __future__ import annotations from abc import ABC, abstractmethod -from collections.abc import Mapping -from typing import Iterable +from collections.abc import Mapping, Iterable from qiskit.circuit import Parameter diff --git a/qiskit/algorithms/time_evolvers/variational/solvers/ode/forward_euler_solver.py b/qiskit/algorithms/time_evolvers/variational/solvers/ode/forward_euler_solver.py index 62cacbb42479..d48ee5b6c4e1 100644 --- a/qiskit/algorithms/time_evolvers/variational/solvers/ode/forward_euler_solver.py +++ b/qiskit/algorithms/time_evolvers/variational/solvers/ode/forward_euler_solver.py @@ -10,8 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """Forward Euler ODE solver.""" - -from typing import Sequence +from collections.abc import Callable, Sequence import numpy as np from scipy.integrate import OdeSolver @@ -23,7 +22,7 @@ class ForwardEulerSolver(OdeSolver): def __init__( self, - function: callable, + function: Callable, t0: float, y0: Sequence, t_bound: float, diff --git a/qiskit/algorithms/time_evolvers/variational/solvers/ode/ode_function.py b/qiskit/algorithms/time_evolvers/variational/solvers/ode/ode_function.py index 49e28fa77b5e..a7d8453c29b8 100644 --- a/qiskit/algorithms/time_evolvers/variational/solvers/ode/ode_function.py +++ b/qiskit/algorithms/time_evolvers/variational/solvers/ode/ode_function.py @@ -11,7 +11,7 @@ # that they have been altered from the originals. """Class for generating ODE functions based on ODE gradients.""" -from typing import Iterable +from collections.abc import Iterable from .abstract_ode_function import AbstractOdeFunction diff --git a/qiskit/algorithms/time_evolvers/variational/solvers/ode/var_qte_ode_solver.py b/qiskit/algorithms/time_evolvers/variational/solvers/ode/var_qte_ode_solver.py index a0184c2c8962..aad1d96155ce 100644 --- a/qiskit/algorithms/time_evolvers/variational/solvers/ode/var_qte_ode_solver.py +++ b/qiskit/algorithms/time_evolvers/variational/solvers/ode/var_qte_ode_solver.py @@ -51,7 +51,9 @@ def __init__( self._ode_solver = ode_solver self._num_timesteps = num_timesteps - def run(self, evolution_time: float) -> Sequence[float]: + def run( + self, evolution_time: float + ) -> tuple[Sequence[float], Sequence[Sequence[float]], Sequence[float]]: """ Finds numerical solution with ODE Solver. diff --git a/qiskit/algorithms/time_evolvers/variational/solvers/var_qte_linear_solver.py b/qiskit/algorithms/time_evolvers/variational/solvers/var_qte_linear_solver.py index 12ff0c56c841..ec06fba0a685 100644 --- a/qiskit/algorithms/time_evolvers/variational/solvers/var_qte_linear_solver.py +++ b/qiskit/algorithms/time_evolvers/variational/solvers/var_qte_linear_solver.py @@ -13,8 +13,7 @@ """Class for solving linear equations for Quantum Time Evolution.""" from __future__ import annotations -from collections.abc import Mapping, Sequence -from typing import Callable +from collections.abc import Mapping, Sequence, Callable import numpy as np @@ -90,7 +89,7 @@ def solve_lse( self, param_dict: Mapping[Parameter, float], time_value: float | None = None, - ) -> tuple(np.ndarray, np.ndarray, np.ndarray): + ) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """ Solve the system of linear equations underlying McLachlan's variational principle for the calculation without error bounds. diff --git a/qiskit/algorithms/time_evolvers/variational/var_qte.py b/qiskit/algorithms/time_evolvers/variational/var_qte.py index 4d1ad1e76dbd..f0d33a6b8ae5 100644 --- a/qiskit/algorithms/time_evolvers/variational/var_qte.py +++ b/qiskit/algorithms/time_evolvers/variational/var_qte.py @@ -14,8 +14,8 @@ from __future__ import annotations from abc import ABC -from collections.abc import Mapping, Sequence -from typing import Type, Callable +from collections.abc import Mapping, Callable, Sequence +from typing import Type import numpy as np from scipy.integrate import OdeSolver @@ -188,7 +188,7 @@ def _evolve( hamiltonian: BaseOperator, time: float, t_param: Parameter | None = None, - ) -> QuantumCircuit: + ) -> tuple[QuantumCircuit | None, Sequence[Sequence[float]], Sequence[float]]: r""" Helper method for performing time evolution. Works both for imaginary and real case. @@ -257,7 +257,7 @@ def _create_init_state_param_dict( TypeError: If an unsupported type of ``param_values`` provided. """ if isinstance(param_values, Mapping): - init_state_parameter_values = [] + init_state_parameter_values: Sequence[float] = [] for param in init_state_parameters: if param in param_values.keys(): init_state_parameter_values.append(param_values[param]) diff --git a/qiskit/algorithms/utils/validate_bounds.py b/qiskit/algorithms/utils/validate_bounds.py index 65f00cfcbd79..747e68f78a52 100644 --- a/qiskit/algorithms/utils/validate_bounds.py +++ b/qiskit/algorithms/utils/validate_bounds.py @@ -17,7 +17,7 @@ from qiskit.circuit import QuantumCircuit -def validate_bounds(circuit: QuantumCircuit) -> list[tuple(float | None, float | None)]: +def validate_bounds(circuit: QuantumCircuit) -> list[tuple[float | None, float | None]]: """ Validate the bounds provided by a quantum circuit against its number of parameters. If no bounds are obtained, return ``None`` for all lower and upper bounds. diff --git a/qiskit/algorithms/variational_algorithm.py b/qiskit/algorithms/variational_algorithm.py index fc05d28af090..1b8b2ec6a164 100644 --- a/qiskit/algorithms/variational_algorithm.py +++ b/qiskit/algorithms/variational_algorithm.py @@ -26,7 +26,7 @@ (``qiskit.utils.algorithm_globals.random_seed = seed``). """ -from typing import Optional, Dict +from __future__ import annotations from abc import ABC, abstractmethod import numpy as np @@ -41,13 +41,13 @@ class VariationalAlgorithm(ABC): @property @abstractmethod - def initial_point(self) -> Optional[np.ndarray]: + def initial_point(self) -> np.ndarray | None: """Returns initial point.""" pass @initial_point.setter @abstractmethod - def initial_point(self, initial_point: Optional[np.ndarray]) -> None: + def initial_point(self, initial_point: np.ndarray | None) -> None: """Sets initial point.""" pass @@ -57,16 +57,16 @@ class VariationalResult(AlgorithmResult): def __init__(self) -> None: super().__init__() - self._optimizer_evals = None - self._optimizer_time = None - self._optimal_value = None - self._optimal_point = None - self._optimal_parameters = None - self._optimizer_result = None - self._optimal_circuit = None + self._optimizer_evals: int | None = None + self._optimizer_time: float | None = None + self._optimal_value: float | None = None + self._optimal_point: np.ndarray | None = None + self._optimal_parameters: dict | None = None + self._optimizer_result: OptimizerResult | None = None + self._optimal_circuit: QuantumCircuit | None = None @property - def optimizer_evals(self) -> Optional[int]: + def optimizer_evals(self) -> int | None: """Returns number of optimizer evaluations""" return self._optimizer_evals @@ -76,7 +76,7 @@ def optimizer_evals(self, value: int) -> None: self._optimizer_evals = value @property - def optimizer_time(self) -> Optional[float]: + def optimizer_time(self) -> float | None: """Returns time taken for optimization""" return self._optimizer_time @@ -86,7 +86,7 @@ def optimizer_time(self, value: float) -> None: self._optimizer_time = value @property - def optimal_value(self) -> Optional[float]: + def optimal_value(self) -> float | None: """Returns optimal value""" return self._optimal_value @@ -96,7 +96,7 @@ def optimal_value(self, value: int) -> None: self._optimal_value = value @property - def optimal_point(self) -> Optional[np.ndarray]: + def optimal_point(self) -> np.ndarray | None: """Returns optimal point""" return self._optimal_point @@ -106,17 +106,17 @@ def optimal_point(self, value: np.ndarray) -> None: self._optimal_point = value @property - def optimal_parameters(self) -> Optional[Dict]: + def optimal_parameters(self) -> dict | None: """Returns the optimal parameters in a dictionary""" return self._optimal_parameters @optimal_parameters.setter - def optimal_parameters(self, value: Dict) -> None: + def optimal_parameters(self, value: dict) -> None: """Sets optimal parameters""" self._optimal_parameters = value @property - def optimizer_result(self) -> Optional[OptimizerResult]: + def optimizer_result(self) -> OptimizerResult | None: """Returns the optimizer result""" return self._optimizer_result diff --git a/test/python/algorithms/test_aux_ops_evaluator.py b/test/python/algorithms/test_aux_ops_evaluator.py index f1727b88b19c..0f956bbb9adf 100644 --- a/test/python/algorithms/test_aux_ops_evaluator.py +++ b/test/python/algorithms/test_aux_ops_evaluator.py @@ -153,6 +153,16 @@ def test_eval_observables(self, observables: ListOrDict[OperatorBase]): quantum_instance, ) + with self.subTest(msg="Test QuantumCircuit with Backend."): + self._run_test( + expected_result, + bound_ansatz, + decimal, + expectation, + observables, + backend, + ) + with self.subTest(msg="Test Statevector."): statevector = Statevector(bound_ansatz) self._run_test( diff --git a/test/python/algorithms/test_grover.py b/test/python/algorithms/test_grover.py index be57e2dc987f..5658ad5a2323 100644 --- a/test/python/algorithms/test_grover.py +++ b/test/python/algorithms/test_grover.py @@ -120,6 +120,15 @@ def test_iterations_with_good_state(self, use_sampler, iterations): result = grover.amplify(problem) self.assertEqual(result.top_measurement, "111") + @idata(itertools.product(["shots", False], [[1, 2, 3], None, 2])) + @unpack + def test_iterations_with_good_state_sample_from_iterations(self, use_sampler, iterations): + """Test the algorithm with different iteration types and with good state""" + grover = self._prepare_grover(use_sampler, iterations, sample_from_iterations=True) + problem = AmplificationProblem(Statevector.from_label("111"), is_good_state=["111"]) + result = grover.amplify(problem) + self.assertEqual(result.top_measurement, "111") + @data("ideal", "shots", False) def test_fixed_iterations_without_good_state(self, use_sampler): """Test the algorithm with iterations as an int and without good state""" @@ -298,18 +307,31 @@ def test_sampler_setter(self): grover.sampler = self._sampler self.assertEqual(grover.sampler, self._sampler) - def _prepare_grover(self, use_sampler, iterations=None, growth_rate=None): + def _prepare_grover( + self, use_sampler, iterations=None, growth_rate=None, sample_from_iterations=False + ): """Prepare Grover instance for test""" if use_sampler == "ideal": - grover = Grover(sampler=self._sampler, iterations=iterations, growth_rate=growth_rate) + 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 + sampler=self._sampler_with_shots, + iterations=iterations, + growth_rate=growth_rate, + sample_from_iterations=sample_from_iterations, ) else: with self.assertWarns(PendingDeprecationWarning): grover = Grover( - quantum_instance=self.qasm, iterations=iterations, growth_rate=growth_rate + quantum_instance=self.qasm, + iterations=iterations, + growth_rate=growth_rate, + sample_from_iterations=sample_from_iterations, ) return grover diff --git a/test/python/algorithms/test_phase_estimator.py b/test/python/algorithms/test_phase_estimator.py index a061acf4f974..3153d87d7b79 100644 --- a/test/python/algorithms/test_phase_estimator.py +++ b/test/python/algorithms/test_phase_estimator.py @@ -18,9 +18,10 @@ 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 +from qiskit.quantum_info import Pauli, SparsePauliOp, Statevector, Operator from qiskit.synthesis import MatrixExponential, SuzukiTrotter from qiskit.primitives import Sampler +from qiskit.algorithms import PhaseEstimationScale from qiskit.algorithms.phase_estimators import ( PhaseEstimation, HamiltonianPhaseEstimation, @@ -550,6 +551,13 @@ def test_check_num_iterations_sampler(self): with self.assertRaises(ValueError): self.one_phase_sampler(unitary_circuit, state_preparation, num_iterations=-1) + def test_phase_estimation_scale_from_operator(self): + """test that PhaseEstimationScale from_pauli_sum works with Operator""" + circ = QuantumCircuit(2) + op = Operator(circ) + scale = PhaseEstimationScale.from_pauli_sum(op) + self.assertEqual(scale._bound, 4.0) + def phase_estimation_sampler( self, unitary_circuit,