diff --git a/qiskit_optimization/algorithms/admm_optimizer.py b/qiskit_optimization/algorithms/admm_optimizer.py index 74ca638c4..e33eef502 100644 --- a/qiskit_optimization/algorithms/admm_optimizer.py +++ b/qiskit_optimization/algorithms/admm_optimizer.py @@ -17,7 +17,7 @@ import copy import logging import time -from typing import List, Optional, Tuple, cast +from typing import cast import numpy as np @@ -182,7 +182,7 @@ def __init__( # pylint: disable=too-many-positional-arguments self, x: np.ndarray, fval: float, - variables: List[Variable], + variables: list[Variable], state: ADMMState, status: OptimizationResultStatus, ) -> None: @@ -215,9 +215,9 @@ class ADMMOptimizer(OptimizationAlgorithm): def __init__( self, - qubo_optimizer: Optional[OptimizationAlgorithm] = None, - continuous_optimizer: Optional[OptimizationAlgorithm] = None, - params: Optional[ADMMParameters] = None, + qubo_optimizer: OptimizationAlgorithm | None = None, + continuous_optimizer: OptimizationAlgorithm | None = None, + params: ADMMParameters | None = None, ) -> None: """ Args: @@ -400,7 +400,7 @@ def solve(self, problem: QuadraticProgram) -> ADMMOptimizationResult: ) @staticmethod - def _get_variable_indices(op: QuadraticProgram, var_type: VarType) -> List[int]: + def _get_variable_indices(op: QuadraticProgram, var_type: VarType) -> list[int]: """Returns a list of indices of the variables of the specified type. Args: @@ -483,7 +483,7 @@ def _convert_problem_representation(self) -> None: # equality constraints with binary vars only self._state.a0, self._state.b0 = self._get_a0_b0() - def _get_step1_indices(self) -> Tuple[List[int], List[int]]: + def _get_step1_indices(self) -> tuple[list[int], list[int]]: """ Constructs two arrays of absolute (pointing to the original problem) and relative (pointing to the list of all binary variables) indices of the variables considered @@ -550,7 +550,7 @@ def _get_step1_indices(self) -> Tuple[List[int], List[int]]: return step1_absolute_indices, step1_relative_indices - def _get_q(self, variable_indices: List[int]) -> np.ndarray: + def _get_q(self, variable_indices: list[int]) -> np.ndarray: """Constructs a quadratic matrix for the variables with the specified indices from the quadratic terms in the objective. @@ -573,7 +573,7 @@ def _get_q(self, variable_indices: List[int]) -> np.ndarray: return q - def _get_a0_b0(self) -> Tuple[np.ndarray, np.ndarray]: + def _get_a0_b0(self) -> tuple[np.ndarray, np.ndarray]: """Constructs a matrix and a vector from the constraints in a form of Ax = b, where x is a vector of binary variables. @@ -705,7 +705,7 @@ def _update_x0(self, op1: QuadraticProgram) -> np.ndarray: x0_all_binaries[self._state.step1_relative_indices] = x0_qubo return x0_all_binaries - def _update_x1(self, op2: QuadraticProgram) -> Tuple[np.ndarray, np.ndarray]: + def _update_x1(self, op2: QuadraticProgram) -> tuple[np.ndarray, np.ndarray]: """Solves the Step2 QuadraticProgram via the continuous optimizer. Args: @@ -734,7 +734,7 @@ def _update_y(self, op3: QuadraticProgram) -> np.ndarray: """ return np.asarray(self._continuous_optimizer.solve(op3).x) - def _get_best_merit_solution(self) -> Tuple[np.ndarray, np.ndarray, float]: + def _get_best_merit_solution(self) -> tuple[np.ndarray, np.ndarray, float]: """The ADMM solution is that for which the merit value is the min * sol: Iterate with the min merit value * sol_val: Value of sol, according to the original objective @@ -825,7 +825,7 @@ def _get_objective_value(self) -> float: """ return self._state.op.objective.evaluate(self._get_current_solution()) - def _get_solution_residuals(self, iteration: int) -> Tuple[float, float]: + def _get_solution_residuals(self, iteration: int) -> tuple[float, float]: """Compute primal and dual residual. Args: diff --git a/qiskit_optimization/algorithms/amplitude_amplifiers/amplification_problem.py b/qiskit_optimization/algorithms/amplitude_amplifiers/amplification_problem.py index 175fbcd83..60abc8a88 100644 --- a/qiskit_optimization/algorithms/amplitude_amplifiers/amplification_problem.py +++ b/qiskit_optimization/algorithms/amplitude_amplifiers/amplification_problem.py @@ -14,7 +14,7 @@ from __future__ import annotations from collections.abc import Callable -from typing import Any, List, cast +from typing import Any, cast from qiskit.circuit import QuantumCircuit from qiskit.circuit.library import GroverOperator @@ -168,10 +168,10 @@ def is_good_state(self) -> Callable[[str], bool]: return self._is_good_state # returns None if no is_good_state arg has been set elif isinstance(self._is_good_state, list): if all(isinstance(good_bitstr, str) for good_bitstr in self._is_good_state): - return lambda bitstr: bitstr in cast(List[str], self._is_good_state) + return lambda bitstr: bitstr in cast(list[str], self._is_good_state) else: return lambda bitstr: all( - bitstr[good_index] == "1" for good_index in cast(List[int], self._is_good_state) + bitstr[good_index] == "1" for good_index in cast(list[int], self._is_good_state) ) return lambda bitstr: bitstr in cast(Statevector, self._is_good_state).probabilities_dict() diff --git a/qiskit_optimization/algorithms/cobyla_optimizer.py b/qiskit_optimization/algorithms/cobyla_optimizer.py index 55253eaab..5f4a41277 100644 --- a/qiskit_optimization/algorithms/cobyla_optimizer.py +++ b/qiskit_optimization/algorithms/cobyla_optimizer.py @@ -11,8 +11,9 @@ # that they have been altered from the originals. """The COBYLA optimizer wrapped to be used within Qiskit optimization module.""" +from __future__ import annotations -from typing import Optional, cast, List, Tuple, Any +from typing import cast, Any import numpy as np from scipy.optimize import fmin_cobyla @@ -48,7 +49,7 @@ def __init__( # pylint: disable=too-many-positional-arguments rhobeg: float = 1.0, rhoend: float = 1e-4, maxfun: int = 1000, - disp: Optional[int] = None, + disp: int | None = None, catol: float = 2e-4, trials: int = 1, clip: float = 100.0, @@ -145,8 +146,8 @@ def ub_constraint(x, u_b=upperbound, j=i): # pylint: disable=no-member # add linear and quadratic constraints - for constraint in cast(List[Constraint], problem.linear_constraints) + cast( - List[Constraint], problem.quadratic_constraints + for constraint in cast(list[Constraint], problem.linear_constraints) + cast( + list[Constraint], problem.quadratic_constraints ): rhs = constraint.rhs sense = constraint.sense @@ -164,7 +165,7 @@ def ub_constraint(x, u_b=upperbound, j=i): raise QiskitOptimizationError("Unsupported constraint type!") # actual minimization function to be called by multi_start_solve - def _minimize(x_0: np.ndarray) -> Tuple[np.ndarray, Any]: + def _minimize(x_0: np.ndarray) -> tuple[np.ndarray, Any]: x = fmin_cobyla( problem.objective.evaluate, x_0, diff --git a/qiskit_optimization/algorithms/cplex_optimizer.py b/qiskit_optimization/algorithms/cplex_optimizer.py index 57709c0b7..4a2bd6f33 100644 --- a/qiskit_optimization/algorithms/cplex_optimizer.py +++ b/qiskit_optimization/algorithms/cplex_optimizer.py @@ -11,8 +11,9 @@ # that they have been altered from the originals. """The CPLEX optimizer wrapped to be used within Qiskit optimization module.""" +from __future__ import annotations -from typing import Any, Dict, Optional +from typing import Any from warnings import warn from qiskit_optimization.problems.quadratic_program import QuadraticProgram @@ -41,9 +42,7 @@ class CplexOptimizer(OptimizationAlgorithm): >>> if optimizer: result = optimizer.solve(problem) """ - def __init__( - self, disp: bool = False, cplex_parameters: Optional[Dict[str, Any]] = None - ) -> None: + def __init__(self, disp: bool = False, cplex_parameters: dict[str, Any] | None = None) -> None: """Initializes the CplexOptimizer. Args: @@ -77,12 +76,12 @@ def disp(self, disp: bool): self._disp = disp @property - def cplex_parameters(self) -> Optional[Dict[str, Any]]: + def cplex_parameters(self) -> dict[str, Any] | None: """Returns parameters for CPLEX""" return self._cplex_parameters @cplex_parameters.setter - def cplex_parameters(self, parameters: Optional[Dict[str, Any]]): + def cplex_parameters(self, parameters: dict[str, Any] | None): """Set parameters for CPLEX Args: parameters: The parameters for CPLEX diff --git a/qiskit_optimization/algorithms/goemans_williamson_optimizer.py b/qiskit_optimization/algorithms/goemans_williamson_optimizer.py index 94c067454..3f6e52712 100644 --- a/qiskit_optimization/algorithms/goemans_williamson_optimizer.py +++ b/qiskit_optimization/algorithms/goemans_williamson_optimizer.py @@ -14,8 +14,9 @@ Implementation of the Goemans-Williamson algorithm as an optimizer. Requires CVXPY to run. """ +from __future__ import annotations import logging -from typing import Optional, List, Tuple, Union, cast +from typing import cast import numpy as np @@ -41,12 +42,12 @@ class GoemansWilliamsonOptimizationResult(OptimizationResult): def __init__( # pylint: disable=too-many-positional-arguments self, - x: Optional[Union[List[float], np.ndarray]], + x: list[float] | np.ndarray | None, fval: float, - variables: List[Variable], + variables: list[Variable], status: OptimizationResultStatus, - samples: Optional[List[SolutionSample]], - sdp_solution: Optional[np.ndarray] = None, + samples: list[SolutionSample] | None, + sdp_solution: np.ndarray | None = None, ) -> None: """ Args: @@ -61,7 +62,7 @@ def __init__( # pylint: disable=too-many-positional-arguments self._sdp_solution = sdp_solution @property - def sdp_solution(self) -> Optional[np.ndarray]: + def sdp_solution(self) -> np.ndarray | None: """ Returns: Returns an SDP solution of the problem. @@ -188,8 +189,8 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: ) def _get_unique_cuts( - self, solutions: List[Tuple[np.ndarray, float]] - ) -> List[Tuple[np.ndarray, float]]: + self, solutions: list[tuple[np.ndarray, float]] + ) -> list[tuple[np.ndarray, float]]: """ Returns: Unique Goemans-Williamson cuts. diff --git a/qiskit_optimization/algorithms/grover_optimizer.py b/qiskit_optimization/algorithms/grover_optimizer.py index a6ccd1d61..7513c3719 100644 --- a/qiskit_optimization/algorithms/grover_optimizer.py +++ b/qiskit_optimization/algorithms/grover_optimizer.py @@ -11,12 +11,13 @@ # that they have been altered from the originals. """GroverOptimizer module""" +from __future__ import annotations import logging import math import warnings from copy import deepcopy -from typing import Dict, List, Optional, Union, cast +from typing import cast import numpy as np from qiskit import QuantumCircuit, QuantumRegister @@ -46,12 +47,10 @@ def __init__( # pylint: disable=too-many-positional-arguments self, num_value_qubits: int, num_iterations: int = 3, - converters: Optional[ - Union[QuadraticProgramConverter, List[QuadraticProgramConverter]] - ] = None, - penalty: Optional[float] = None, - sampler: Optional[Union[BaseSamplerV1, BaseSamplerV2]] = None, - pass_manager: Optional[BasePassManager] = None, + converters: QuadraticProgramConverter | list[QuadraticProgramConverter] | None = None, + penalty: float | None = None, + sampler: BaseSamplerV1 | BaseSamplerV2 | None = None, + pass_manager: BasePassManager | None = None, ) -> None: """ Args: @@ -303,7 +302,7 @@ def _measure(self, circuit: QuantumCircuit) -> str: # Pick a random outcome. return algorithm_globals.random.choice(list(probs.keys()), 1, p=list(probs.values()))[0] - def _get_prob_dist(self, qc: QuantumCircuit) -> Dict[str, float]: + def _get_prob_dist(self, qc: QuantumCircuit) -> dict[str, float]: """Gets probabilities from a given backend.""" if self._pass_manager: qc = self._pass_manager.run(qc) @@ -345,17 +344,17 @@ class GroverOptimizationResult(OptimizationResult): def __init__( # pylint: disable=too-many-positional-arguments self, - x: Union[List[float], np.ndarray], + x: list[float] | np.ndarray, fval: float, - variables: List[Variable], - operation_counts: Dict[int, Dict[str, int]], + variables: list[Variable], + operation_counts: dict[int, dict[str, int]], n_input_qubits: int, n_output_qubits: int, intermediate_fval: float, threshold: float, status: OptimizationResultStatus, - samples: Optional[List[SolutionSample]] = None, - raw_samples: Optional[List[SolutionSample]] = None, + samples: list[SolutionSample] | None = None, + raw_samples: list[SolutionSample] | None = None, ) -> None: """ Constructs a result object with the specific Grover properties. @@ -392,7 +391,7 @@ def __init__( # pylint: disable=too-many-positional-arguments self._threshold = threshold @property - def operation_counts(self) -> Dict[int, Dict[str, int]]: + def operation_counts(self) -> dict[int, dict[str, int]]: """Get the operation counts. Returns: @@ -437,7 +436,7 @@ def threshold(self) -> float: return self._threshold @property - def raw_samples(self) -> Optional[List[SolutionSample]]: + def raw_samples(self) -> list[SolutionSample] | None: """Returns the list of raw solution samples of ``GroverOptimizer``. Returns: diff --git a/qiskit_optimization/algorithms/minimum_eigen_optimizer.py b/qiskit_optimization/algorithms/minimum_eigen_optimizer.py index 8ed8cb14c..1747e550d 100644 --- a/qiskit_optimization/algorithms/minimum_eigen_optimizer.py +++ b/qiskit_optimization/algorithms/minimum_eigen_optimizer.py @@ -11,7 +11,8 @@ # that they have been altered from the originals. """A wrapper for minimum eigen solvers to be used within the optimization module.""" -from typing import List, Optional, Union, cast +from __future__ import annotations +from typing import Union, cast import numpy as np from qiskit.quantum_info import SparsePauliOp @@ -41,13 +42,13 @@ class MinimumEigenOptimizationResult(OptimizationResult): def __init__( # pylint: disable=too-many-positional-arguments self, - x: Optional[Union[List[float], np.ndarray]], - fval: Optional[float], - variables: List[Variable], + x: list[float] | np.ndarray | None, + fval: float | None, + variables: list[Variable], status: OptimizationResultStatus, - samples: Optional[List[SolutionSample]] = None, - min_eigen_solver_result: Optional[MinimumEigensolverResult] = None, - raw_samples: Optional[List[SolutionSample]] = None, + samples: list[SolutionSample] | None = None, + min_eigen_solver_result: MinimumEigensolverResult | None = None, + raw_samples: list[SolutionSample] | None = None, ) -> None: """ Args: @@ -79,7 +80,7 @@ def min_eigen_solver_result(self) -> MinimumEigensolverResult: return self._min_eigen_solver_result @property - def raw_samples(self) -> Optional[List[SolutionSample]]: + def raw_samples(self) -> list[SolutionSample] | None: """Returns the list of raw solution samples of ``SamplingMinimumEigensolver`` or ``NumPyMinimumEigensolver``. @@ -123,10 +124,8 @@ class MinimumEigenOptimizer(OptimizationAlgorithm): def __init__( self, min_eigen_solver: MinimumEigensolver, - penalty: Optional[float] = None, - converters: Optional[ - Union[QuadraticProgramConverter, List[QuadraticProgramConverter]] - ] = None, + penalty: float | None = None, + converters: QuadraticProgramConverter | list[QuadraticProgramConverter] | None = None, ) -> None: """ This initializer takes the minimum eigen solver to be used to approximate the ground state @@ -212,7 +211,7 @@ def _solve_internal( original_problem: QuadraticProgram, ): # only try to solve non-empty Ising Hamiltonians - eigen_result: Optional[MinimumEigensolverResult] = None + eigen_result: MinimumEigensolverResult | None = None if operator.num_qubits > 0: # approximate ground state of operator using min eigen solver eigen_result = self._min_eigen_solver.compute_minimum_eigenvalue(operator) diff --git a/qiskit_optimization/algorithms/multistart_optimizer.py b/qiskit_optimization/algorithms/multistart_optimizer.py index 43664ee70..ca61a4937 100644 --- a/qiskit_optimization/algorithms/multistart_optimizer.py +++ b/qiskit_optimization/algorithms/multistart_optimizer.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2020, 2023. +# (C) Copyright IBM 2020, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -13,11 +13,12 @@ """Defines an abstract class for multi start optimizers. A multi start optimizer is an optimizer that may run minimization algorithm for the several time with different initial guesses to achieve better results. This implementation is suitable for local optimizers.""" +from __future__ import annotations import logging import time from abc import ABC -from typing import Callable, Tuple, Any, Optional +from typing import Callable, Any import numpy as np from scipy.stats import uniform @@ -63,7 +64,7 @@ def __init__(self, trials: int = 1, clip: float = 100.0) -> None: def multi_start_solve( self, - minimize: Callable[[np.ndarray], Tuple[np.ndarray, Any]], + minimize: Callable[[np.ndarray], tuple[np.ndarray, Any]], problem: QuadraticProgram, ) -> OptimizationResult: """Applies a multi start method given a local optimizer. @@ -76,8 +77,8 @@ def multi_start_solve( The result of the multi start algorithm applied to the problem. """ fval_sol = INFINITY - x_sol: Optional[np.ndarray] = None - rest_sol: Optional[Tuple] = None + x_sol: np.ndarray | None = None + rest_sol: tuple | None = None # we deal with minimization in the optimizer, so turn the problem to minimization max2min = MaximizeToMinimize() diff --git a/qiskit_optimization/algorithms/optimization_algorithm.py b/qiskit_optimization/algorithms/optimization_algorithm.py index b91737871..81f8490fe 100644 --- a/qiskit_optimization/algorithms/optimization_algorithm.py +++ b/qiskit_optimization/algorithms/optimization_algorithm.py @@ -17,7 +17,7 @@ from dataclasses import dataclass from enum import Enum from logging import getLogger -from typing import Any, Dict, List, Tuple, Type, Union, cast +from typing import Any, cast import numpy as np from qiskit.quantum_info import Statevector @@ -99,12 +99,12 @@ class OptimizationResult: def __init__( # pylint: disable=too-many-positional-arguments self, - x: Union[List[float], np.ndarray] | None, + x: list[float] | np.ndarray | None, fval: float | None, - variables: List[Variable], + variables: list[Variable], status: OptimizationResultStatus, raw_results: Any | None = None, - samples: List[SolutionSample] | None = None, + samples: list[SolutionSample] | None = None, ) -> None: """ Args: @@ -168,7 +168,7 @@ def prettyprint(self) -> str: f"status: {self._status.name}" ) - def __getitem__(self, key: Union[int, str]) -> float: + def __getitem__(self, key: int | str) -> float: """Returns the value of the variable whose index or name is equal to ``key``. The key can be an integer or a string. @@ -256,7 +256,7 @@ def status(self) -> OptimizationResultStatus: return self._status @property - def variables(self) -> List[Variable]: + def variables(self) -> list[Variable]: """Returns the list of variables of the optimization problem. Returns: @@ -265,7 +265,7 @@ def variables(self) -> List[Variable]: return self._variables @property - def variables_dict(self) -> Dict[str, float]: + def variables_dict(self) -> dict[str, float]: """Returns the variable values as a dictionary of the variable name and corresponding value. Returns: @@ -274,7 +274,7 @@ def variables_dict(self) -> Dict[str, float]: return self._variables_dict @property - def variable_names(self) -> List[str]: + def variable_names(self) -> list[str]: """Returns the list of variable names of the optimization problem. Returns: @@ -283,7 +283,7 @@ def variable_names(self) -> List[str]: return self._variable_names @property - def samples(self) -> List[SolutionSample]: + def samples(self) -> list[SolutionSample]: """Returns the list of solution samples Returns: @@ -320,7 +320,7 @@ def is_compatible(self, problem: QuadraticProgram) -> bool: return len(self.get_compatibility_msg(problem)) == 0 @abstractmethod - def solve(self, problem: QuadraticProgram) -> "OptimizationResult": + def solve(self, problem: QuadraticProgram) -> OptimizationResult: """Tries to solves the given problem using the optimizer. Runs the optimizer to try to solve the optimization problem. @@ -358,7 +358,7 @@ def _verify_compatibility(self, problem: QuadraticProgram) -> None: @staticmethod def _get_feasibility_status( - problem: QuadraticProgram, x: Union[List[float], np.ndarray] + problem: QuadraticProgram, x: list[float] | np.ndarray ) -> OptimizationResultStatus: """Returns whether the input result is feasible or not for the given problem. @@ -377,9 +377,9 @@ def _get_feasibility_status( @staticmethod def _prepare_converters( - converters: Union[QuadraticProgramConverter, List[QuadraticProgramConverter]] | None, + converters: QuadraticProgramConverter | list[QuadraticProgramConverter] | None, penalty: float | None = None, - ) -> List[QuadraticProgramConverter]: + ) -> list[QuadraticProgramConverter]: """Prepare a list of converters from the input. Args: @@ -410,7 +410,7 @@ def _prepare_converters( @staticmethod def _convert( problem: QuadraticProgram, - converters: Union[QuadraticProgramConverter, List[QuadraticProgramConverter]], + converters: QuadraticProgramConverter | list[QuadraticProgramConverter], ) -> QuadraticProgram: """Convert the problem with the converters @@ -433,8 +433,8 @@ def _convert( @staticmethod def _check_converters( - converters: Union[QuadraticProgramConverter, List[QuadraticProgramConverter]] | None, - ) -> List[QuadraticProgramConverter]: + converters: QuadraticProgramConverter | list[QuadraticProgramConverter] | None, + ) -> list[QuadraticProgramConverter]: if converters is None: converters = [] if not isinstance(converters, list): @@ -448,8 +448,8 @@ def _interpret( cls, x: np.ndarray, problem: QuadraticProgram, - converters: Union[QuadraticProgramConverter, List[QuadraticProgramConverter]] | None = None, - result_class: Type[OptimizationResult] = OptimizationResult, + converters: QuadraticProgramConverter | list[QuadraticProgramConverter] | None = None, + result_class: type[OptimizationResult] = OptimizationResult, **kwargs, ) -> OptimizationResult: """Convert back the result of the converted problem to the result of the original problem. @@ -490,13 +490,13 @@ def _interpret( def _interpret_samples( cls, problem: QuadraticProgram, - raw_samples: List[SolutionSample], + raw_samples: list[SolutionSample], converters: QuadraticProgramConverter | list[QuadraticProgramConverter] | None = None, - ) -> Tuple[List[SolutionSample], SolutionSample]: + ) -> tuple[list[SolutionSample], SolutionSample]: """Interpret and sort all samples and return the raw sample corresponding to the best one""" converters = cls._check_converters(converters) - prob: Dict[Tuple, float] = {} + prob: dict[tuple, float] = {} array = {} index = {} for i, sample in enumerate(raw_samples): @@ -524,10 +524,10 @@ def _interpret_samples( @staticmethod def _eigenvector_to_solutions( - eigenvector: Union[QuasiDistribution, Statevector, dict, np.ndarray], + eigenvector: QuasiDistribution | Statevector | dict | np.ndarray, qubo: QuadraticProgram, min_probability: float = _MIN_PROBABILITY, - ) -> List[SolutionSample]: + ) -> list[SolutionSample]: """Convert the eigenvector to the bitstrings and corresponding eigenvalues. Args: diff --git a/qiskit_optimization/algorithms/qrao/quantum_random_access_optimizer.py b/qiskit_optimization/algorithms/qrao/quantum_random_access_optimizer.py index ac34310c0..bec13b9f1 100644 --- a/qiskit_optimization/algorithms/qrao/quantum_random_access_optimizer.py +++ b/qiskit_optimization/algorithms/qrao/quantum_random_access_optimizer.py @@ -13,7 +13,7 @@ """Quantum Random Access Optimizer class.""" from __future__ import annotations -from typing import List, cast +from typing import cast import numpy as np from qiskit import QuantumCircuit @@ -227,7 +227,7 @@ def solve_relaxed( expectation_values: list[complex] | None = None if relaxed_result.aux_operators_evaluated is not None: expectation_values = cast( - List[complex], [v[0] for v in relaxed_result.aux_operators_evaluated] + list[complex], [v[0] for v in relaxed_result.aux_operators_evaluated] ) # Get the circuit corresponding to the relaxed solution. diff --git a/qiskit_optimization/algorithms/recursive_minimum_eigen_optimizer.py b/qiskit_optimization/algorithms/recursive_minimum_eigen_optimizer.py index 8e392e1c7..2297279d6 100644 --- a/qiskit_optimization/algorithms/recursive_minimum_eigen_optimizer.py +++ b/qiskit_optimization/algorithms/recursive_minimum_eigen_optimizer.py @@ -11,10 +11,11 @@ # that they have been altered from the originals. """A recursive minimal eigen optimizer in Qiskit optimization module.""" +from __future__ import annotations from copy import deepcopy from enum import Enum -from typing import Dict, List, Optional, Tuple, Union, cast +from typing import cast import numpy as np @@ -54,12 +55,12 @@ class RecursiveMinimumEigenOptimizationResult(OptimizationResult): def __init__( # pylint: disable=too-many-positional-arguments self, - x: Union[List[float], np.ndarray], + x: list[float] | np.ndarray, fval: float, - variables: List[Variable], + variables: list[Variable], status: OptimizationResultStatus, - replacements: Dict[str, Tuple[str, int]], - history: Tuple[List[MinimumEigenOptimizationResult], OptimizationResult], + replacements: dict[str, tuple[str, int]], + history: tuple[list[MinimumEigenOptimizationResult], OptimizationResult], ) -> None: """ Constructs an instance of the result class. @@ -83,7 +84,7 @@ def __init__( # pylint: disable=too-many-positional-arguments self._history = history @property - def replacements(self) -> Dict[str, Tuple[str, int]]: + def replacements(self) -> dict[str, tuple[str, int]]: """ Returns a dictionary of substituted variables. Key is a variable being substituted, value is a tuple of substituting variable and a weight, either 1 or -1.""" @@ -92,7 +93,7 @@ def replacements(self) -> Dict[str, Tuple[str, int]]: @property def history( self, - ) -> Tuple[List[MinimumEigenOptimizationResult], OptimizationResult]: + ) -> tuple[list[MinimumEigenOptimizationResult], OptimizationResult]: """ Returns intermediate results. The first element is a list of :class:`~qiskit_optimization.algorithms.MinimumEigenOptimizerResult` obtained by invoking @@ -141,12 +142,10 @@ def __init__( # pylint: disable=too-many-positional-arguments self, optimizer: OptimizationAlgorithm, min_num_vars: int = 1, - min_num_vars_optimizer: Optional[OptimizationAlgorithm] = None, - penalty: Optional[float] = None, - history: Optional[IntermediateResult] = IntermediateResult.LAST_ITERATION, - converters: Optional[ - Union[QuadraticProgramConverter, List[QuadraticProgramConverter]] - ] = None, + min_num_vars_optimizer: OptimizationAlgorithm | None = None, + penalty: float | None = None, + history: IntermediateResult | None = IntermediateResult.LAST_ITERATION, + converters: QuadraticProgramConverter | list[QuadraticProgramConverter] | None = None, ) -> None: """Initializes the recursive minimum eigen optimizer. @@ -224,8 +223,8 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: problem_ref = deepcopy(problem_) # run recursive optimization until the resulting problem is small enough - replacements = {} # type: Dict[str, Tuple[str, int]] - optimization_results = [] # type: List[OptimizationResult] + replacements: dict[str, tuple[str, int]] = {} + optimization_results: list[OptimizationResult] = [] while problem_.get_num_vars() > self._min_num_vars: # solve current problem with optimizer diff --git a/qiskit_optimization/algorithms/slsqp_optimizer.py b/qiskit_optimization/algorithms/slsqp_optimizer.py index 70adc7454..ba57e5615 100644 --- a/qiskit_optimization/algorithms/slsqp_optimizer.py +++ b/qiskit_optimization/algorithms/slsqp_optimizer.py @@ -11,7 +11,8 @@ # that they have been altered from the originals. """The SLSQP optimizer wrapped to be used within Qiskit optimization module.""" -from typing import List, cast, Tuple, Any, Union, Optional +from __future__ import annotations +from typing import cast, Any import numpy as np from scipy.optimize import fmin_slsqp @@ -32,14 +33,14 @@ class SlsqpOptimizationResult(OptimizationResult): def __init__( # pylint: disable=too-many-positional-arguments self, - x: Union[List[float], np.ndarray], + x: list[float] | np.ndarray, fval: float, - variables: List[Variable], + variables: list[Variable], status: OptimizationResultStatus, - fx: Optional[np.ndarray] = None, - its: Optional[int] = None, - imode: Optional[int] = None, - smode: Optional[str] = None, + fx: np.ndarray | None = None, + its: int | None = None, + imode: int | None = None, + smode: str | None = None, ) -> None: """ Constructs a result object with properties specific to SLSQP. @@ -63,22 +64,22 @@ def __init__( # pylint: disable=too-many-positional-arguments # pylint:disable=invalid-name @property - def fx(self) -> Optional[np.ndarray]: + def fx(self) -> np.ndarray | None: """Returns the final value of the objective function being actually optimized.""" return self._fx @property - def its(self) -> Optional[int]: + def its(self) -> int | None: """Returns the number of iterations""" return self._its @property - def imode(self) -> Optional[int]: + def imode(self) -> int | None: """Returns the exit mode from the optimizer.""" return self._imode @property - def smode(self) -> Optional[str]: + def smode(self) -> str | None: """Returns message describing the exit mode from the optimizer.""" return self._smode @@ -199,8 +200,8 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: # pylint: disable=no-member # add linear and quadratic constraints - for constraint in cast(List[Constraint], problem.linear_constraints) + cast( - List[Constraint], problem.quadratic_constraints + for constraint in cast(list[Constraint], problem.linear_constraints) + cast( + list[Constraint], problem.quadratic_constraints ): rhs = constraint.rhs sense = constraint.sense @@ -215,7 +216,7 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: raise QiskitOptimizationError("Unsupported constraint type!") # actual minimization function to be called by multi_start_solve - def _minimize(x_0: np.ndarray) -> Tuple[np.ndarray, Any]: + def _minimize(x_0: np.ndarray) -> tuple[np.ndarray, Any]: output = fmin_slsqp( problem.objective.evaluate, x_0, diff --git a/qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py b/qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py index acaf4c94d..a4866fe2e 100644 --- a/qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py +++ b/qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py @@ -11,10 +11,11 @@ # that they have been altered from the originals. """Implementation of the warm start QAOA optimizer.""" +from __future__ import annotations import copy from abc import ABC, abstractmethod -from typing import Dict, List, Optional, Tuple, Union, cast +from typing import cast import numpy as np from qiskit import QuantumCircuit @@ -33,7 +34,7 @@ class BaseAggregator(ABC): """A base abstract class for aggregates results""" @abstractmethod - def aggregate(self, results: List[MinimumEigenOptimizationResult]) -> List[SolutionSample]: + def aggregate(self, results: list[MinimumEigenOptimizationResult]) -> list[SolutionSample]: """ Aggregates the results. @@ -49,7 +50,7 @@ def aggregate(self, results: List[MinimumEigenOptimizationResult]) -> List[Solut class MeanAggregator(BaseAggregator): """Aggregates the results by averaging the probability of each sample.""" - def aggregate(self, results: List[MinimumEigenOptimizationResult]) -> List[SolutionSample]: + def aggregate(self, results: list[MinimumEigenOptimizationResult]) -> list[SolutionSample]: """ Args: results: List of result objects that need to be combined. @@ -60,7 +61,7 @@ def aggregate(self, results: List[MinimumEigenOptimizationResult]) -> List[Solut # Use a dict for fast solution look-up # Key: sample code, value: tuple of fval, probability - dict_samples: Dict[str, Tuple[float, float]] = {} + dict_samples: dict[str, tuple[float, float]] = {} def _to_string(x: np.ndarray) -> str: return "".join(str(int(v)) for v in x) @@ -124,7 +125,7 @@ def __init__(self, epsilon: float) -> None: ) self._epsilon = epsilon - def create_initial_variables(self, solution: np.ndarray) -> List[float]: + def create_initial_variables(self, solution: np.ndarray) -> list[float]: """ Creates initial variable values to warm start QAOA. @@ -146,7 +147,7 @@ def create_initial_variables(self, solution: np.ndarray) -> List[float]: return initial_variables - def create_initial_state(self, initial_variables: List[float]) -> QuantumCircuit: + def create_initial_state(self, initial_variables: list[float]) -> QuantumCircuit: """ Creates an initial state quantum circuit to warm start QAOA. @@ -164,7 +165,7 @@ def create_initial_state(self, initial_variables: List[float]) -> QuantumCircuit return circuit - def create_mixer(self, initial_variables: List[float]) -> QuantumCircuit: + def create_mixer(self, initial_variables: list[float]) -> QuantumCircuit: """ Creates an evolved mixer circuit as Ry(theta)Rz(-2beta)Ry(-theta). @@ -205,12 +206,10 @@ def __init__( # pylint: disable=too-many-positional-arguments qaoa: QAOA, epsilon: float = 0.25, num_initial_solutions: int = 1, - warm_start_factory: Optional[WarmStartQAOAFactory] = None, - aggregator: Optional[BaseAggregator] = None, - penalty: Optional[float] = None, - converters: Optional[ - Union[QuadraticProgramConverter, List[QuadraticProgramConverter]] - ] = None, + warm_start_factory: WarmStartQAOAFactory | None = None, + aggregator: BaseAggregator | None = None, + penalty: float | None = None, + converters: QuadraticProgramConverter | list[QuadraticProgramConverter] | None = None, ) -> None: """Initializes the optimizer. For correct initialization either ``epsilon`` or ``circuit_factory`` must be passed. If only ``epsilon`` is specified @@ -312,7 +311,7 @@ def solve(self, problem: QuadraticProgram) -> MinimumEigenOptimizationResult: # construct operator and offset operator, offset = converted_problem.to_ising() - results: List[MinimumEigenOptimizationResult] = [] + results: list[MinimumEigenOptimizationResult] = [] for pre_solution in pre_solutions: # Set the solver using the result of the pre-solver. initial_variables = self._warm_start_factory.create_initial_variables(pre_solution.x) diff --git a/qiskit_optimization/applications/bin_packing.py b/qiskit_optimization/applications/bin_packing.py index 38b94803a..405f37ef9 100755 --- a/qiskit_optimization/applications/bin_packing.py +++ b/qiskit_optimization/applications/bin_packing.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2021, 2023. +# (C) Copyright IBM 2021, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,8 +11,8 @@ # that they have been altered from the originals. """An application class for the bin packing.""" +from __future__ import annotations -from typing import List, Union, Optional import numpy as np from docplex.mp.model import Model @@ -45,7 +45,7 @@ class BinPacking(OptimizationApplication): """ def __init__( - self, weights: List[int], max_weight: int, max_number_of_bins: Optional[int] = None + self, weights: list[int], max_weight: int, max_number_of_bins: int | None = None ) -> None: """ Args: @@ -86,7 +86,7 @@ def to_quadratic_program(self) -> QuadraticProgram: op = from_docplex_mp(mdl) return op - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[int]]: + def interpret(self, result: OptimizationResult | np.ndarray) -> list[list[int]]: """Interpret a result as item indices Args: @@ -106,7 +106,7 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[ return items_in_bins @_optionals.HAS_MATPLOTLIB.require_in_call - def get_figure(self, result: Union[OptimizationResult, np.ndarray]) -> Figure: + def get_figure(self, result: OptimizationResult | np.ndarray) -> Figure: """Get plot of the solution of the Bin Packing Problem. Args: diff --git a/qiskit_optimization/applications/clique.py b/qiskit_optimization/applications/clique.py index 8d0b76af3..d2ffc2111 100644 --- a/qiskit_optimization/applications/clique.py +++ b/qiskit_optimization/applications/clique.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2018, 2023. +# (C) Copyright IBM 2018, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,7 +11,7 @@ # that they have been altered from the originals. """An application class for the clique.""" -from typing import Optional, Union, List, Dict +from __future__ import annotations import networkx as nx import numpy as np @@ -32,9 +32,7 @@ class Clique(GraphOptimizationApplication): `_ """ - def __init__( - self, graph: Union[nx.Graph, np.ndarray, List], size: Optional[int] = None - ) -> None: + def __init__(self, graph: nx.Graph | np.ndarray | list, size: int | None = None) -> None: """ Args: graph: A graph representing a problem. It can be specified directly as a @@ -70,7 +68,7 @@ def to_quadratic_program(self) -> QuadraticProgram: op = from_docplex_mp(mdl) return op - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[int]: + def interpret(self, result: OptimizationResult | np.ndarray) -> list[int]: """Interpret a result as a list of node indices Args: @@ -88,8 +86,8 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[int]: def _draw_result( self, - result: Union[OptimizationResult, np.ndarray], - pos: Optional[Dict[int, np.ndarray]] = None, + result: OptimizationResult | np.ndarray, + pos: dict[int, np.ndarray] | None = None, ) -> None: """Draw the result with colors @@ -100,14 +98,14 @@ def _draw_result( x = self._result_to_x(result) nx.draw(self._graph, node_color=self._node_colors(x), pos=pos, with_labels=True) - def _node_colors(self, x: np.ndarray) -> List[str]: + def _node_colors(self, x: np.ndarray) -> list[str]: # Return a list of strings for draw. # Color a node with red when the corresponding variable is 1. # Otherwise color it with dark gray. return ["r" if x[node] else "darkgrey" for node in self._graph.nodes] @property - def size(self) -> Optional[int]: + def size(self) -> int | None: """Getter of size Returns: @@ -116,7 +114,7 @@ def size(self) -> Optional[int]: return self._size @size.setter - def size(self, size: Optional[int]) -> None: + def size(self, size: int | None) -> None: """Setter of size Args: diff --git a/qiskit_optimization/applications/exact_cover.py b/qiskit_optimization/applications/exact_cover.py index 79f68ae57..8f340e40e 100644 --- a/qiskit_optimization/applications/exact_cover.py +++ b/qiskit_optimization/applications/exact_cover.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2018, 2023. +# (C) Copyright IBM 2018, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,7 +11,8 @@ # that they have been altered from the originals. """An application class for the exact cover.""" -from typing import List, Union, cast +from __future__ import annotations +from typing import cast import numpy as np from docplex.mp.model import Model @@ -30,7 +31,7 @@ class ExactCover(OptimizationApplication): https://en.wikipedia.org/wiki/Exact_cover """ - def __init__(self, subsets: List[List[int]]) -> None: + def __init__(self, subsets: list[list[int]]) -> None: """ Args: subsets: A list of subsets @@ -39,7 +40,7 @@ def __init__(self, subsets: List[List[int]]) -> None: self._set = [] for sub in self._subsets: self._set.extend(sub) - self._set = cast(List, np.unique(self._set)) + self._set = cast(list, np.unique(self._set)) def to_quadratic_program(self) -> QuadraticProgram: """Convert an exact cover instance into a @@ -59,7 +60,7 @@ def to_quadratic_program(self) -> QuadraticProgram: op = from_docplex_mp(mdl) return op - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[int]]: + def interpret(self, result: OptimizationResult | np.ndarray) -> list[list[int]]: """Interpret a result as a list of subsets Args: diff --git a/qiskit_optimization/applications/graph_optimization_application.py b/qiskit_optimization/applications/graph_optimization_application.py index 027c01662..4ae7ae3ec 100644 --- a/qiskit_optimization/applications/graph_optimization_application.py +++ b/qiskit_optimization/applications/graph_optimization_application.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2018, 2023. +# (C) Copyright IBM 2018, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,9 +11,9 @@ # that they have been altered from the originals. """An abstract class for graph optimization application classes.""" +from __future__ import annotations from abc import abstractmethod -from typing import Dict, List, Optional, Union import networkx as nx import numpy as np @@ -29,7 +29,7 @@ class GraphOptimizationApplication(OptimizationApplication): An abstract class for graph optimization applications. """ - def __init__(self, graph: Union[nx.Graph, np.ndarray, List]) -> None: + def __init__(self, graph: nx.Graph | np.ndarray | list) -> None: """ Args: graph: A graph representing a problem. It can be specified directly as a @@ -42,8 +42,8 @@ def __init__(self, graph: Union[nx.Graph, np.ndarray, List]) -> None: @_optionals.HAS_MATPLOTLIB.require_in_call def draw( self, - result: Optional[Union[OptimizationResult, np.ndarray]] = None, - pos: Optional[Dict[int, np.ndarray]] = None, + result: OptimizationResult | np.ndarray | None = None, + pos: dict[int, np.ndarray] | None = None, ) -> None: """Draw a graph with the result. When the result is None, draw an original graph without colors. @@ -60,8 +60,8 @@ def draw( @abstractmethod def _draw_result( self, - result: Union[OptimizationResult, np.ndarray], - pos: Optional[Dict[int, np.ndarray]] = None, + result: OptimizationResult | np.ndarray, + pos: dict[int, np.ndarray] | None = None, ) -> None: """Draw the result with colors diff --git a/qiskit_optimization/applications/graph_partition.py b/qiskit_optimization/applications/graph_partition.py index b5a44eb55..d1c32e86d 100644 --- a/qiskit_optimization/applications/graph_partition.py +++ b/qiskit_optimization/applications/graph_partition.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2018, 2023. +# (C) Copyright IBM 2018, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,8 +11,8 @@ # that they have been altered from the originals. """An application class for the graph partitioning.""" +from __future__ import annotations -from typing import Dict, List, Optional, Union import networkx as nx import numpy as np @@ -53,7 +53,7 @@ def to_quadratic_program(self) -> QuadraticProgram: op = from_docplex_mp(mdl) return op - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[int]]: + def interpret(self, result: OptimizationResult | np.ndarray) -> list[list[int]]: """Interpret a result as a list of node indices Args: @@ -63,7 +63,7 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[ A list of node indices divided into two groups. """ x = self._result_to_x(result) - partition = [[], []] # type: List[List[int]] + partition: list[list[int]] = [[], []] for i, value in enumerate(x): if value == 0: partition[0].append(i) @@ -73,8 +73,8 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[ def _draw_result( self, - result: Union[OptimizationResult, np.ndarray], - pos: Optional[Dict[int, np.ndarray]] = None, + result: OptimizationResult | np.ndarray, + pos: dict[int, np.ndarray] | None = None, ) -> None: """Draw the result with colors @@ -85,7 +85,7 @@ def _draw_result( x = self._result_to_x(result) nx.draw(self._graph, node_color=self._node_colors(x), pos=pos, with_labels=True) - def _node_colors(self, x: np.ndarray) -> List[str]: + def _node_colors(self, x: np.ndarray) -> list[str]: # Return a list of strings for draw. # Color a node with red when the corresponding variable is 1. # Otherwise color it with blue. diff --git a/qiskit_optimization/applications/knapsack.py b/qiskit_optimization/applications/knapsack.py index 2d5b8d2ff..22bcfa5b0 100644 --- a/qiskit_optimization/applications/knapsack.py +++ b/qiskit_optimization/applications/knapsack.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2018, 2023. +# (C) Copyright IBM 2018, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,7 +11,7 @@ # that they have been altered from the originals. """An application class for the Knapsack problem""" -from typing import List, Union +from __future__ import annotations import numpy as np from docplex.mp.model import Model @@ -30,7 +30,7 @@ class Knapsack(OptimizationApplication): https://en.wikipedia.org/wiki/Knapsack_problem """ - def __init__(self, values: List[int], weights: List[int], max_weight: int) -> None: + def __init__(self, values: list[int], weights: list[int], max_weight: int) -> None: """ Args: values: A list of the values of items @@ -56,7 +56,7 @@ def to_quadratic_program(self) -> QuadraticProgram: op = from_docplex_mp(mdl) return op - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[int]: + def interpret(self, result: OptimizationResult | np.ndarray) -> list[int]: """Interpret a result as item indices Args: diff --git a/qiskit_optimization/applications/max_cut.py b/qiskit_optimization/applications/max_cut.py index a38ce3f31..fe669b46e 100644 --- a/qiskit_optimization/applications/max_cut.py +++ b/qiskit_optimization/applications/max_cut.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2018, 2024. +# (C) Copyright IBM 2018, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -12,8 +12,8 @@ """An application class for the Max-cut.""" +from __future__ import annotations -from typing import List, Dict, Optional, Union import networkx as nx import numpy as np from docplex.mp.model import Model @@ -55,8 +55,8 @@ def to_quadratic_program(self) -> QuadraticProgram: def _draw_result( self, - result: Union[OptimizationResult, np.ndarray], - pos: Optional[Dict[int, np.ndarray]] = None, + result: OptimizationResult | np.ndarray, + pos: dict[int, np.ndarray] | None = None, ) -> None: """Draw the result with colors @@ -67,7 +67,7 @@ def _draw_result( x = self._result_to_x(result) nx.draw(self._graph, node_color=self._node_color(x), pos=pos, with_labels=True) - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[int]]: + def interpret(self, result: OptimizationResult | np.ndarray) -> list[list[int]]: """Interpret a result as two lists of node indices Args: @@ -77,7 +77,7 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[ Two lists of node indices correspond to two node sets for the Max-cut """ x = self._result_to_x(result) - cut = [[], []] # type: List[List[int]] + cut: list[list[int]] = [[], []] for i, value in enumerate(x): if value == 0: cut[0].append(i) @@ -85,7 +85,7 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[ cut[1].append(i) return cut - def _node_color(self, x: np.ndarray) -> List[str]: + def _node_color(self, x: np.ndarray) -> list[str]: # Return a list of strings for draw. # Color a node with red when the corresponding variable is 1. # Otherwise color it with blue. @@ -124,7 +124,7 @@ def parse_gset_format(filename: str) -> np.ndarray: return w @staticmethod - def get_gset_result(x: np.ndarray) -> Dict[int, int]: + def get_gset_result(x: np.ndarray) -> dict[int, int]: """Get graph solution in Gset format from binary string. Args: diff --git a/qiskit_optimization/applications/number_partition.py b/qiskit_optimization/applications/number_partition.py index d7a4c019d..e7598c0c6 100644 --- a/qiskit_optimization/applications/number_partition.py +++ b/qiskit_optimization/applications/number_partition.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2018, 2023. +# (C) Copyright IBM 2018, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,8 +11,8 @@ # that they have been altered from the originals. """An application class for the number partitioning.""" +from __future__ import annotations -from typing import List, Union import numpy as np from docplex.mp.model import Model @@ -31,7 +31,7 @@ class NumberPartition(OptimizationApplication): https://en.wikipedia.org/wiki/Partition_problem """ - def __init__(self, number_set: List[int]) -> None: + def __init__(self, number_set: list[int]) -> None: """ Args: number_set: A list of integers @@ -54,7 +54,7 @@ def to_quadratic_program(self) -> QuadraticProgram: op = from_docplex_mp(mdl) return op - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[int]]: + def interpret(self, result: OptimizationResult | np.ndarray) -> list[list[int]]: """Interpret a result as a list of subsets Args: @@ -64,7 +64,7 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[ A list of subsets whose sum is the half of the total. """ x = self._result_to_x(result) - num_subsets = [[], []] # type: List[List[int]] + num_subsets: list[list[int]] = [[], []] for i, value in enumerate(x): if value == 0: num_subsets[0].append(self._number_set[i]) diff --git a/qiskit_optimization/applications/optimization_application.py b/qiskit_optimization/applications/optimization_application.py index 6208c4f3a..e5977af2b 100644 --- a/qiskit_optimization/applications/optimization_application.py +++ b/qiskit_optimization/applications/optimization_application.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2018, 2023. +# (C) Copyright IBM 2018, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,9 +11,9 @@ # that they have been altered from the originals. """An abstract class for optimization application classes.""" +from __future__ import annotations from abc import ABC, abstractmethod from collections import OrderedDict -from typing import Dict, Union import numpy as np from qiskit.quantum_info import Statevector @@ -36,7 +36,7 @@ def to_quadratic_program(self) -> QuadraticProgram: pass @abstractmethod - def interpret(self, result: Union[OptimizationResult, np.ndarray]): + def interpret(self, result: OptimizationResult | np.ndarray): """Convert the calculation result of the problem (:class:`~qiskit_optimization.algorithms.OptimizationResult` or a binary array using np.ndarray) to the answer of the problem in an easy-to-understand format. @@ -46,7 +46,7 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]): """ pass - def _result_to_x(self, result: Union[OptimizationResult, np.ndarray]) -> np.ndarray: + def _result_to_x(self, result: OptimizationResult | np.ndarray) -> np.ndarray: # Return result.x for OptimizationResult and return result itself for np.ndarray if isinstance(result, OptimizationResult): x = result.x @@ -61,7 +61,7 @@ def _result_to_x(self, result: Union[OptimizationResult, np.ndarray]) -> np.ndar @staticmethod def sample_most_likely( - state_vector: Union[QuasiDistribution, Statevector, np.ndarray, Dict] + state_vector: QuasiDistribution | Statevector | np.ndarray | dict, ) -> np.ndarray: """Compute the most likely binary string from state vector. diff --git a/qiskit_optimization/applications/set_packing.py b/qiskit_optimization/applications/set_packing.py index bed16e663..3707fb610 100644 --- a/qiskit_optimization/applications/set_packing.py +++ b/qiskit_optimization/applications/set_packing.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2018, 2023. +# (C) Copyright IBM 2018, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,8 +11,9 @@ # that they have been altered from the originals. """An application class for the set packing.""" +from __future__ import annotations -from typing import List, Union, cast +from typing import cast import numpy as np from docplex.mp.model import Model @@ -31,7 +32,7 @@ class SetPacking(OptimizationApplication): https://en.wikipedia.org/wiki/Set_packing """ - def __init__(self, subsets: List[List[int]]) -> None: + def __init__(self, subsets: list[list[int]]) -> None: """ Args: subsets: A list of subsets @@ -40,7 +41,7 @@ def __init__(self, subsets: List[List[int]]) -> None: self._set = [] for sub in self._subsets: self._set.extend(sub) - self._set = cast(List, np.unique(self._set)) + self._set = cast(list, np.unique(self._set)) def to_quadratic_program(self) -> QuadraticProgram: """Convert a set packing instance into a @@ -60,7 +61,7 @@ def to_quadratic_program(self) -> QuadraticProgram: op = from_docplex_mp(mdl) return op - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[int]]: + def interpret(self, result: OptimizationResult | np.ndarray) -> list[list[int]]: """Interpret a result as a list of subsets Args: diff --git a/qiskit_optimization/applications/sk_model.py b/qiskit_optimization/applications/sk_model.py index c0d1369a8..dd020edd2 100644 --- a/qiskit_optimization/applications/sk_model.py +++ b/qiskit_optimization/applications/sk_model.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2021, 2023. +# (C) Copyright IBM 2021, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,8 +11,8 @@ # that they have been altered from the originals. """An application class for the Sherrington Kirkpatrick (SK) model.""" +from __future__ import annotations -from typing import Union, List, Optional import networkx as nx import numpy as np @@ -39,9 +39,7 @@ class SKModel(OptimizationApplication): https://arxiv.org/abs/1211.1094 """ - def __init__( - self, num_sites: int, rng_or_seed: Optional[Union[np.random.Generator, int]] = None - ): + def __init__(self, num_sites: int, rng_or_seed: np.random.Generator | int | None = None): """ Args: num_sites: number of sites @@ -87,7 +85,7 @@ def to_quadratic_program(self) -> QuadraticProgram: mdl.minimize(objective) return from_docplex_mp(mdl) - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[int]: + def interpret(self, result: OptimizationResult | np.ndarray) -> list[int]: """Interpret a result as configuration of spins. Args: diff --git a/qiskit_optimization/applications/stable_set.py b/qiskit_optimization/applications/stable_set.py index 0c3bd4d6a..6cd23f7a9 100644 --- a/qiskit_optimization/applications/stable_set.py +++ b/qiskit_optimization/applications/stable_set.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2018, 2023. +# (C) Copyright IBM 2018, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,8 +11,8 @@ # that they have been altered from the originals. """An application class for the stable set.""" +from __future__ import annotations -from typing import Dict, List, Optional, Union import networkx as nx import numpy as np @@ -53,7 +53,7 @@ def to_quadratic_program(self) -> QuadraticProgram: op = from_docplex_mp(mdl) return op - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[int]: + def interpret(self, result: OptimizationResult | np.ndarray) -> list[int]: """Interpret a result as a list of node indices Args: @@ -71,8 +71,8 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[int]: def _draw_result( self, - result: Union[OptimizationResult, np.ndarray], - pos: Optional[Dict[int, np.ndarray]] = None, + result: OptimizationResult | np.ndarray, + pos: dict[int, np.ndarray] | None = None, ) -> None: """Draw the result with colors diff --git a/qiskit_optimization/applications/tsp.py b/qiskit_optimization/applications/tsp.py index c23abe507..fb5e777b2 100644 --- a/qiskit_optimization/applications/tsp.py +++ b/qiskit_optimization/applications/tsp.py @@ -11,7 +11,7 @@ # that they have been altered from the originals. """An application class for Traveling salesman problem (TSP).""" -from typing import Dict, List, Optional, Union +from __future__ import annotations import networkx as nx import numpy as np @@ -72,9 +72,7 @@ def to_quadratic_program(self) -> QuadraticProgram: op = from_docplex_mp(mdl) return op - def interpret( - self, result: Union[OptimizationResult, np.ndarray] - ) -> List[Union[int, List[int]]]: + def interpret(self, result: OptimizationResult | np.ndarray) -> list[int | list[int]]: """Interpret a result as a list of node indices Args: @@ -85,7 +83,7 @@ def interpret( """ x = self._result_to_x(result) n = self._graph.number_of_nodes() - route = [] # type: List[Union[int, List[int]]] + route: list[int | list[int]] = [] for p__ in range(n): p_step = [] for i in range(n): @@ -99,8 +97,8 @@ def interpret( def _draw_result( self, - result: Union[OptimizationResult, np.ndarray], - pos: Optional[Dict[int, np.ndarray]] = None, + result: OptimizationResult | np.ndarray, + pos: dict[int, np.ndarray] | None = None, ) -> None: """Draw the result with colors @@ -126,7 +124,7 @@ def _edgelist(self, x: np.ndarray): @staticmethod # pylint: disable=undefined-variable - def create_random_instance(n: int, low: int = 0, high: int = 100, seed: int = None) -> "Tsp": + def create_random_instance(n: int, low: int = 0, high: int = 100, seed: int = None) -> Tsp: """Create a random instance of the traveling salesman problem Args: @@ -149,7 +147,7 @@ def create_random_instance(n: int, low: int = 0, high: int = 100, seed: int = No return Tsp(graph) @staticmethod - def parse_tsplib_format(filename: str) -> "Tsp": + def parse_tsplib_format(filename: str) -> Tsp: """Read a graph in TSPLIB format from file and return a Tsp instance. Only the EUC_2D edge weight format is supported. @@ -213,7 +211,7 @@ def parse_tsplib_format(filename: str) -> "Tsp": return Tsp(graph) @staticmethod - def tsp_value(z: List[int], adj_matrix: np.ndarray) -> float: + def tsp_value(z: list[int], adj_matrix: np.ndarray) -> float: """Compute the TSP value of a solution. Args: z: list of cities. diff --git a/qiskit_optimization/applications/vehicle_routing.py b/qiskit_optimization/applications/vehicle_routing.py index 86d7b87a2..8f1ca61a7 100644 --- a/qiskit_optimization/applications/vehicle_routing.py +++ b/qiskit_optimization/applications/vehicle_routing.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2018, 2024. +# (C) Copyright IBM 2018, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,10 +11,10 @@ # that they have been altered from the originals. """An application class for the vehicle routing problem.""" +from __future__ import annotations import itertools import random -from typing import List, Dict, Union, Optional import networkx as nx import numpy as np @@ -35,7 +35,7 @@ class VehicleRouting(GraphOptimizationApplication): def __init__( self, - graph: Union[nx.Graph, np.ndarray, List], + graph: nx.Graph | np.ndarray | list, num_vehicles: int = 2, depot: int = 0, ) -> None: @@ -103,7 +103,7 @@ def to_quadratic_program(self) -> QuadraticProgram: op = from_docplex_mp(mdl) return op - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[List[int]]]: + def interpret(self, result: OptimizationResult | np.ndarray) -> list[list[list[int]]]: """Interpret a result as a list of the routes for each vehicle Args: @@ -122,7 +122,7 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[ if x[idx]: edge_list.append([i, j]) idx += 1 - route_list = [] # type: List[List[List[int]]] + route_list: list[list[list[int]]] = [] for k in range(self.num_vehicles): i = 0 start = self.depot @@ -146,8 +146,8 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[ def _draw_result( self, - result: Union[OptimizationResult, np.ndarray], - pos: Optional[Dict[int, np.ndarray]] = None, + result: OptimizationResult | np.ndarray, + pos: dict[int, np.ndarray] | None = None, ) -> None: """Draw the result with colors @@ -169,12 +169,12 @@ def _draw_result( edge_cmap=mpl.colormaps["plasma"], ) - def _edgelist(self, route_list: List[List[List[int]]]): + def _edgelist(self, route_list: list[list[list[int]]]): # Arrange route_list and return the list of the edges for the edge list of # nx.draw_networkx_edges return [edge for k in range(len(route_list)) for edge in route_list[k]] - def _edge_color(self, route_list: List[List[List[int]]]): + def _edge_color(self, route_list: list[list[list[int]]]): # Arrange route_list and return the list of the colors of each route # for edge_color of nx.draw_networkx_edges return [k / len(route_list) for k in range(len(route_list)) for edge in route_list[k]] @@ -221,10 +221,10 @@ def create_random_instance( # pylint: disable=too-many-positional-arguments n: int, low: int = 0, high: int = 100, - seed: Optional[int] = None, + seed: int | None = None, num_vehicle: int = 2, depot: int = 0, - ) -> "VehicleRouting": + ) -> VehicleRouting: """Create a random instance of the vehicle routing problem. Args: diff --git a/qiskit_optimization/applications/vertex_cover.py b/qiskit_optimization/applications/vertex_cover.py index 92b474d57..2477af0a5 100644 --- a/qiskit_optimization/applications/vertex_cover.py +++ b/qiskit_optimization/applications/vertex_cover.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2018, 2023. +# (C) Copyright IBM 2018, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,8 +11,8 @@ # that they have been altered from the originals. """An application class for the vertex cover.""" +from __future__ import annotations -from typing import Dict, List, Optional, Union import networkx as nx import numpy as np @@ -49,7 +49,7 @@ def to_quadratic_program(self) -> QuadraticProgram: op = from_docplex_mp(mdl) return op - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[int]: + def interpret(self, result: OptimizationResult | np.ndarray) -> list[int]: """Interpret a result as a list of node indices Args: @@ -67,8 +67,8 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[int]: def _draw_result( self, - result: Union[OptimizationResult, np.ndarray], - pos: Optional[Dict[int, np.ndarray]] = None, + result: OptimizationResult | np.ndarray, + pos: dict[int, np.ndarray] | None = None, ) -> None: """Draw the result with colors @@ -79,7 +79,7 @@ def _draw_result( x = self._result_to_x(result) nx.draw(self._graph, node_color=self._node_colors(x), pos=pos, with_labels=True) - def _node_colors(self, x: np.ndarray) -> List[str]: + def _node_colors(self, x: np.ndarray) -> list[str]: # Return a list of strings for draw. # Color a node with red when the corresponding variable is 1. # Otherwise color it with dark gray. diff --git a/qiskit_optimization/converters/flip_problem_sense.py b/qiskit_optimization/converters/flip_problem_sense.py index 5b089611d..05405a05f 100644 --- a/qiskit_optimization/converters/flip_problem_sense.py +++ b/qiskit_optimization/converters/flip_problem_sense.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2021, 2023. +# (C) Copyright IBM 2021, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,9 +11,9 @@ # that they have been altered from the originals. """Converters to flip problem sense, e.g. maximization to minimization and vice versa.""" +from __future__ import annotations import copy -from typing import Optional, List, Union import numpy as np @@ -28,7 +28,7 @@ class _FlipProblemSense(QuadraticProgramConverter): vice versa, regardless of the current sense.""" def __init__(self) -> None: - self._src_num_vars: Optional[int] = None + self._src_num_vars: int | None = None def convert(self, problem: QuadraticProgram) -> QuadraticProgram: """Flip the sense of a problem. @@ -72,7 +72,7 @@ def _get_desired_sense(self, problem: QuadraticProgram) -> ObjSense: else: return ObjSense.MAXIMIZE - def interpret(self, x: Union[np.ndarray, List[float]]) -> np.ndarray: + def interpret(self, x: np.ndarray | list[float]) -> np.ndarray: """Convert the result of the converted problem back to that of the original problem. Args: diff --git a/qiskit_optimization/converters/inequality_to_equality.py b/qiskit_optimization/converters/inequality_to_equality.py index 4898e4e6f..6dea4d6d9 100644 --- a/qiskit_optimization/converters/inequality_to_equality.py +++ b/qiskit_optimization/converters/inequality_to_equality.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2020, 2023. +# (C) Copyright IBM 2020, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -10,10 +10,10 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """The inequality to equality converter.""" +from __future__ import annotations import copy import math -from typing import List, Optional, Union import numpy as np @@ -50,8 +50,8 @@ def __init__(self, mode: str = "auto") -> None: - 'continuous': All slack variables will be continuous variables. - 'auto': Use integer variables if possible, otherwise use continuous variables. """ - self._src: Optional[QuadraticProgram] = None - self._dst: Optional[QuadraticProgram] = None + self._src: QuadraticProgram | None = None + self._dst: QuadraticProgram | None = None self._mode = mode def convert(self, problem: QuadraticProgram) -> QuadraticProgram: @@ -252,7 +252,7 @@ def _add_slack_var_quadratic_constraint(self, constraint: QuadraticConstraint): new_linear[slack_name] = sign return new_linear, quadratic.coefficients, "==", new_rhs, name - def interpret(self, x: Union[np.ndarray, List[float]]) -> np.ndarray: + def interpret(self, x: np.ndarray | list[float]) -> np.ndarray: """Convert a result of a converted problem into that of the original problem. Args: diff --git a/qiskit_optimization/converters/integer_to_binary.py b/qiskit_optimization/converters/integer_to_binary.py index e36f3aa2a..a5f97a85b 100644 --- a/qiskit_optimization/converters/integer_to_binary.py +++ b/qiskit_optimization/converters/integer_to_binary.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2020, 2024. +# (C) Copyright IBM 2020, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,9 +11,9 @@ # that they have been altered from the originals. """The converter to map integer variables in a quadratic program to binary variables.""" +from __future__ import annotations import copy -from typing import Dict, List, Optional, Tuple, Union import numpy as np @@ -46,9 +46,9 @@ class IntegerToBinary(QuadraticProgramConverter): _delimiter = "@" # users are supposed not to use this character in variable names def __init__(self) -> None: - self._src: Optional[QuadraticProgram] = None - self._dst: Optional[QuadraticProgram] = None - self._conv: Dict[Variable, List[Tuple[str, int]]] = {} + self._src: QuadraticProgram | None = None + self._dst: QuadraticProgram | None = None + self._conv: dict[Variable, list[tuple[str, int]]] = {} # e.g., self._conv = {x: [('x@1', 1), ('x@2', 2)]} def convert(self, problem: QuadraticProgram) -> QuadraticProgram: @@ -97,7 +97,7 @@ def convert(self, problem: QuadraticProgram) -> QuadraticProgram: def _convert_var( self, name: str, lowerbound: float, upperbound: float - ) -> List[Tuple[str, int]]: + ) -> list[tuple[str, int]]: var_range = upperbound - lowerbound power = int(np.log2(var_range)) if var_range > 0 else 0 bounded_coef = var_range - (2**power - 1) @@ -106,10 +106,10 @@ def _convert_var( return [(name + self._delimiter + str(i), coef) for i, coef in enumerate(coeffs)] def _convert_linear_coefficients_dict( - self, coefficients: Dict[str, float] - ) -> Tuple[Dict[str, float], float]: + self, coefficients: dict[str, float] + ) -> tuple[dict[str, float], float]: constant = 0.0 - linear: Dict[str, float] = {} + linear: dict[str, float] = {} for name, v in coefficients.items(): x = self._src.get_variable(name) if x in self._conv: @@ -122,10 +122,10 @@ def _convert_linear_coefficients_dict( return linear, constant def _convert_quadratic_coefficients_dict( - self, coefficients: Dict[Tuple[str, str], float] - ) -> Tuple[Dict[Tuple[str, str], float], Dict[str, float], float]: + self, coefficients: dict[tuple[str, str], float] + ) -> tuple[dict[tuple[str, str], float], dict[str, float], float]: constant = 0.0 - linear: Dict[str, float] = {} + linear: dict[str, float] = {} quadratic = {} for (name_i, name_j), v in coefficients.items(): x = self._src.get_variable(name_i) @@ -211,7 +211,7 @@ def _substitute_int_var(self): constraint.name, ) - def interpret(self, x: Union[np.ndarray, List[float]]) -> np.ndarray: + def interpret(self, x: np.ndarray | list[float]) -> np.ndarray: """Convert back the converted problem (binary variables) to the original (integer variables). diff --git a/qiskit_optimization/converters/linear_equality_to_penalty.py b/qiskit_optimization/converters/linear_equality_to_penalty.py index e820d2763..8418ea9ae 100644 --- a/qiskit_optimization/converters/linear_equality_to_penalty.py +++ b/qiskit_optimization/converters/linear_equality_to_penalty.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2020, 2023. +# (C) Copyright IBM 2020, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,9 +11,10 @@ # that they have been altered from the originals. """Converter to convert a problem with equality constraints to unconstrained with penalty terms.""" +from __future__ import annotations import logging -from typing import Optional, cast, Union, Tuple, List +from typing import cast, Union import numpy as np @@ -30,15 +31,15 @@ class LinearEqualityToPenalty(QuadraticProgramConverter): """Convert a problem with only equality constraints to unconstrained with penalty terms.""" - def __init__(self, penalty: Optional[float] = None) -> None: + def __init__(self, penalty: float | None = None) -> None: """ Args: penalty: Penalty factor to scale equality constraints that are added to objective. If None is passed, a penalty factor will be automatically calculated on every conversion. """ - self._src_num_vars: Optional[int] = None - self._penalty: Optional[float] = penalty + self._src_num_vars: int | None = None + self._penalty: float | None = penalty self._should_define_penalty: bool = penalty is None def convert(self, problem: QuadraticProgram) -> QuadraticProgram: @@ -111,7 +112,7 @@ def convert(self, problem: QuadraticProgram) -> QuadraticProgram: # according to implementation of quadratic terms in OptimizationModel, # don't need to multiply by 2, since loops run over (x, y) and (y, x). - tup = cast(Union[Tuple[int, int], Tuple[str, str]], (j, k)) + tup = cast(Union[tuple[int, int], tuple[str, str]], (j, k)) quadratic[tup] = quadratic.get(tup, 0.0) + sense * penalty * coef_1 * coef_2 if problem.objective.sense == QuadraticObjective.Sense.MINIMIZE: @@ -156,7 +157,7 @@ def _auto_define_penalty(problem: QuadraticProgram) -> float: quad_b = problem.objective.quadratic.bounds return 1.0 + (lin_b.upperbound - lin_b.lowerbound) + (quad_b.upperbound - quad_b.lowerbound) - def interpret(self, x: Union[np.ndarray, List[float]]) -> np.ndarray: + def interpret(self, x: np.ndarray | list[float]) -> np.ndarray: """Convert the result of the converted problem back to that of the original problem Args: @@ -177,7 +178,7 @@ def interpret(self, x: Union[np.ndarray, List[float]]) -> np.ndarray: return np.asarray(x) @property - def penalty(self) -> Optional[float]: + def penalty(self) -> float | None: """Returns the penalty factor used in conversion. Returns: @@ -186,7 +187,7 @@ def penalty(self) -> Optional[float]: return self._penalty @penalty.setter - def penalty(self, penalty: Optional[float]) -> None: + def penalty(self, penalty: float | None) -> None: """Set a new penalty factor. Args: diff --git a/qiskit_optimization/converters/linear_inequality_to_penalty.py b/qiskit_optimization/converters/linear_inequality_to_penalty.py index 154851c32..19e29a04b 100644 --- a/qiskit_optimization/converters/linear_inequality_to_penalty.py +++ b/qiskit_optimization/converters/linear_inequality_to_penalty.py @@ -11,9 +11,9 @@ # that they have been altered from the originals. """Converter to convert a problem with inequality constraints to unconstrained with penalty terms.""" +from __future__ import annotations import logging -from typing import Optional, Union, Tuple, List, Dict import numpy as np @@ -56,7 +56,7 @@ class LinearInequalityToPenalty(QuadraticProgramConverter): """ - def __init__(self, penalty: Optional[float] = None) -> None: + def __init__(self, penalty: float | None = None) -> None: """ Args: penalty: Penalty factor to scale equality constraints that are added to objective. @@ -64,9 +64,9 @@ def __init__(self, penalty: Optional[float] = None) -> None: every conversion. """ - self._src_num_vars: Optional[int] = None - self._dst: Optional[QuadraticProgram] = None - self._penalty: Optional[float] = penalty + self._src_num_vars: int | None = None + self._dst: QuadraticProgram | None = None + self._penalty: float | None = penalty self._should_define_penalty: bool = penalty is None def convert(self, problem: QuadraticProgram) -> QuadraticProgram: @@ -184,7 +184,7 @@ def convert(self, problem: QuadraticProgram) -> QuadraticProgram: @staticmethod def _conversion_table( constraint, - ) -> Tuple[int, np.ndarray, np.ndarray, Dict[int, int]]: + ) -> tuple[int, np.ndarray, np.ndarray, dict[int, int]]: """Construct conversion matrix for special constraint. Returns: @@ -310,7 +310,7 @@ def _auto_define_penalty(problem) -> float: quad_b = problem.objective.quadratic.bounds return 1.0 + (lin_b.upperbound - lin_b.lowerbound) + (quad_b.upperbound - quad_b.lowerbound) - def interpret(self, x: Union[np.ndarray, List[float]]) -> np.ndarray: + def interpret(self, x: np.ndarray | list[float]) -> np.ndarray: """Convert the result of the converted problem back to that of the original problem Args: @@ -332,7 +332,7 @@ def interpret(self, x: Union[np.ndarray, List[float]]) -> np.ndarray: return np.asarray(x) @property - def penalty(self) -> Optional[float]: + def penalty(self) -> float | None: """Returns the penalty factor used in conversion. Returns: @@ -342,7 +342,7 @@ def penalty(self) -> Optional[float]: return self._penalty @penalty.setter - def penalty(self, penalty: Optional[float]) -> None: + def penalty(self, penalty: float | None) -> None: """Set a new penalty factor. Args: diff --git a/qiskit_optimization/converters/quadratic_program_converter.py b/qiskit_optimization/converters/quadratic_program_converter.py index 05cb01329..08d334af8 100644 --- a/qiskit_optimization/converters/quadratic_program_converter.py +++ b/qiskit_optimization/converters/quadratic_program_converter.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2020, 2023. +# (C) Copyright IBM 2020, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,9 +11,9 @@ # that they have been altered from the originals. """An abstract class for optimization algorithms in Qiskit optimization module.""" +from __future__ import annotations from abc import ABC, abstractmethod -from typing import List, Union import numpy as np @@ -33,6 +33,6 @@ def convert(self, problem: QuadraticProgram) -> QuadraticProgram: raise NotImplementedError @abstractmethod - def interpret(self, x: Union[np.ndarray, List[float]]) -> np.ndarray: + def interpret(self, x: np.ndarray | list[float]) -> np.ndarray: """Interpret a result into another form using the information of conversion""" raise NotImplementedError diff --git a/qiskit_optimization/converters/quadratic_program_to_qubo.py b/qiskit_optimization/converters/quadratic_program_to_qubo.py index e4d5fb43f..46f8c078d 100644 --- a/qiskit_optimization/converters/quadratic_program_to_qubo.py +++ b/qiskit_optimization/converters/quadratic_program_to_qubo.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2020, 2023. +# (C) Copyright IBM 2020, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,8 +11,9 @@ # that they have been altered from the originals. """A converter from quadratic program to a QUBO.""" +from __future__ import annotations -from typing import List, Optional, Union, cast +from typing import cast import numpy as np @@ -38,7 +39,7 @@ class QuadraticProgramToQubo(QuadraticProgramConverter): >>> problem2 = conv.convert(problem) """ - def __init__(self, penalty: Optional[float] = None) -> None: + def __init__(self, penalty: float | None = None) -> None: """ Args: penalty: Penalty factor to scale equality constraints that are added to objective. @@ -77,7 +78,7 @@ def convert(self, problem: QuadraticProgram) -> QuadraticProgram: problem = conv.convert(problem) return problem - def interpret(self, x: Union[np.ndarray, List[float]]) -> np.ndarray: + def interpret(self, x: np.ndarray | list[float]) -> np.ndarray: """Convert a result of a converted problem into that of the original problem. Args: @@ -145,7 +146,7 @@ def is_compatible(self, problem: QuadraticProgram) -> bool: return len(self.get_compatibility_msg(problem)) == 0 @property - def penalty(self) -> Optional[float]: + def penalty(self) -> float | None: """Returns the penalty factor used in conversion. Returns: @@ -154,7 +155,7 @@ def penalty(self) -> Optional[float]: return self._penalize_lin_eq_constraints.penalty @penalty.setter - def penalty(self, penalty: Optional[float]) -> None: + def penalty(self, penalty: float | None) -> None: """Set a new penalty factor. Args: diff --git a/qiskit_optimization/minimum_eigensolvers/eigensolver.py b/qiskit_optimization/minimum_eigensolvers/eigensolver.py index c56da9070..2f2ddc189 100644 --- a/qiskit_optimization/minimum_eigensolvers/eigensolver.py +++ b/qiskit_optimization/minimum_eigensolvers/eigensolver.py @@ -37,7 +37,7 @@ def compute_eigenvalues( self, operator: BaseOperator, aux_operators: ListOrDict[BaseOperator] | None = None, - ) -> "EigensolverResult": + ) -> EigensolverResult: """ Computes the minimum eigenvalue. The ``operator`` and ``aux_operators`` are supplied here. While an ``operator`` is required by algorithms, ``aux_operators`` are optional. diff --git a/qiskit_optimization/minimum_eigensolvers/list_or_dict.py b/qiskit_optimization/minimum_eigensolvers/list_or_dict.py index 16822edb5..15447c57b 100644 --- a/qiskit_optimization/minimum_eigensolvers/list_or_dict.py +++ b/qiskit_optimization/minimum_eigensolvers/list_or_dict.py @@ -12,7 +12,7 @@ """Introduced new type to maintain readability.""" -from typing import Dict, List, Optional, TypeVar, Union +from typing import Optional, TypeVar, Union _T = TypeVar("_T") # Pylint does not allow single character class names. -ListOrDict = Union[List[Optional[_T]], Dict[str, _T]] +ListOrDict = Union[list[Optional[_T]], dict[str, _T]] diff --git a/qiskit_optimization/minimum_eigensolvers/minimum_eigensolver.py b/qiskit_optimization/minimum_eigensolvers/minimum_eigensolver.py index dddf12788..4b24497df 100644 --- a/qiskit_optimization/minimum_eigensolvers/minimum_eigensolver.py +++ b/qiskit_optimization/minimum_eigensolvers/minimum_eigensolver.py @@ -35,7 +35,7 @@ def compute_minimum_eigenvalue( self, operator: BaseOperator, aux_operators: ListOrDict[BaseOperator] | None = None, - ) -> "MinimumEigensolverResult": + ) -> MinimumEigensolverResult: """ Computes the minimum eigenvalue. The ``operator`` and ``aux_operators`` are supplied here. While an ``operator`` is required by algorithms, ``aux_operators`` are optional. diff --git a/qiskit_optimization/minimum_eigensolvers/numpy_eigensolver.py b/qiskit_optimization/minimum_eigensolvers/numpy_eigensolver.py index 44ec8202a..01d1e7d9f 100644 --- a/qiskit_optimization/minimum_eigensolvers/numpy_eigensolver.py +++ b/qiskit_optimization/minimum_eigensolvers/numpy_eigensolver.py @@ -16,7 +16,7 @@ import logging from collections.abc import Iterable -from typing import Callable, Dict, List, Optional, Tuple, Union, cast +from typing import Callable, Optional, Union, cast import numpy as np from qiskit.quantum_info import SparsePauliOp, Statevector @@ -31,7 +31,7 @@ logger = logging.getLogger(__name__) FilterType = Callable[ - [Union[List, np.ndarray], float, Optional[ListOrDict[Tuple[float, Dict[str, float]]]]], bool + [Union[list, np.ndarray], float, Optional[ListOrDict[tuple[float, dict[str, float]]]]], bool ] @@ -162,9 +162,9 @@ def _solve_sparse(op_matrix: scisparse.csr_matrix, k: int) -> tuple[np.ndarray, def _solve_dense(op_matrix: np.ndarray) -> tuple[np.ndarray, np.ndarray]: if op_matrix.all() == op_matrix.conj().T.all(): # Operator is Hermitian - return cast(Tuple[np.ndarray, np.ndarray], np.linalg.eigh(op_matrix)) + return cast(tuple[np.ndarray, np.ndarray], np.linalg.eigh(op_matrix)) else: - return cast(Tuple[np.ndarray, np.ndarray], np.linalg.eig(op_matrix)) + return cast(tuple[np.ndarray, np.ndarray], np.linalg.eig(op_matrix)) @staticmethod def _eval_aux_operators( diff --git a/qiskit_optimization/minimum_eigensolvers/numpy_minimum_eigensolver.py b/qiskit_optimization/minimum_eigensolvers/numpy_minimum_eigensolver.py index 8694b9c96..471cbceee 100644 --- a/qiskit_optimization/minimum_eigensolvers/numpy_minimum_eigensolver.py +++ b/qiskit_optimization/minimum_eigensolvers/numpy_minimum_eigensolver.py @@ -15,7 +15,7 @@ from __future__ import annotations import logging -from typing import Callable, Dict, List, Optional, Tuple, Union +from typing import Callable, Optional, Union import numpy as np from qiskit.quantum_info import Statevector @@ -29,7 +29,7 @@ # future type annotations not supported in type aliases in 3.8 FilterType = Callable[ - [Union[List, np.ndarray], float, Optional[ListOrDict[Tuple[float, Dict[str, float]]]]], bool + [Union[list, np.ndarray], float, Optional[ListOrDict[tuple[float, dict[str, float]]]]], bool ] diff --git a/qiskit_optimization/minimum_eigensolvers/sampling_mes.py b/qiskit_optimization/minimum_eigensolvers/sampling_mes.py index ab856b370..bcea09edf 100644 --- a/qiskit_optimization/minimum_eigensolvers/sampling_mes.py +++ b/qiskit_optimization/minimum_eigensolvers/sampling_mes.py @@ -33,7 +33,7 @@ def compute_minimum_eigenvalue( self, operator: BaseOperator, aux_operators: ListOrDict[BaseOperator] | None = None, - ) -> "SamplingMinimumEigensolverResult": + ) -> SamplingMinimumEigensolverResult: """Compute the minimum eigenvalue of a diagonal operator. Args: diff --git a/qiskit_optimization/problems/constraint.py b/qiskit_optimization/problems/constraint.py index 1ce616222..9fbabeb70 100644 --- a/qiskit_optimization/problems/constraint.py +++ b/qiskit_optimization/problems/constraint.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2019, 2023. +# (C) Copyright IBM 2019, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,10 +11,11 @@ # that they have been altered from the originals. """Abstract Constraint.""" +from __future__ import annotations from abc import abstractmethod from enum import Enum -from typing import Union, List, Dict, Any +from typing import Any from numpy import ndarray @@ -31,7 +32,7 @@ class ConstraintSense(Enum): EQ = 2 @staticmethod - def convert(sense: Union[str, "ConstraintSense"]) -> "ConstraintSense": + def convert(sense: str | ConstraintSense) -> ConstraintSense: """Convert a string into a corresponding sense of constraints Args: @@ -150,7 +151,7 @@ def rhs(self, rhs: float) -> None: self._rhs = rhs @abstractmethod - def evaluate(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> float: + def evaluate(self, x: ndarray | list | dict[int | str, float]) -> float: """Evaluate left-hand-side of constraint for given values of variables. Args: diff --git a/qiskit_optimization/problems/linear_constraint.py b/qiskit_optimization/problems/linear_constraint.py index e144289f9..fca8e0717 100644 --- a/qiskit_optimization/problems/linear_constraint.py +++ b/qiskit_optimization/problems/linear_constraint.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2019, 2024. +# (C) Copyright IBM 2019, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,8 +11,9 @@ # that they have been altered from the originals. """Linear Constraint.""" +from __future__ import annotations -from typing import Union, List, Dict, Any +from typing import Any from numpy import ndarray from scipy.sparse import spmatrix @@ -31,7 +32,7 @@ def __init__( # pylint: disable=too-many-positional-arguments self, quadratic_program: Any, name: str, - linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]], + linear: ndarray | spmatrix | list[float] | dict[str | int, float], sense: ConstraintSense, rhs: float, ) -> None: @@ -58,7 +59,7 @@ def linear(self) -> LinearExpression: @linear.setter def linear( self, - linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]], + linear: ndarray | spmatrix | list[float] | dict[str | int, float], ) -> None: """Sets the linear expression corresponding to the left-hand-side of the constraint. The coefficients can either be given by an array, a (sparse) 1d matrix, a list or a @@ -69,7 +70,7 @@ def linear( """ self._linear = LinearExpression(self.quadratic_program, linear) - def evaluate(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> float: + def evaluate(self, x: ndarray | list | dict[int | str, float]) -> float: """Evaluate the left-hand-side of the constraint. Args: diff --git a/qiskit_optimization/problems/linear_expression.py b/qiskit_optimization/problems/linear_expression.py index 983ef5f0b..9fc6936a0 100644 --- a/qiskit_optimization/problems/linear_expression.py +++ b/qiskit_optimization/problems/linear_expression.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2019, 2023. +# (C) Copyright IBM 2019, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,8 +11,9 @@ # that they have been altered from the originals. """Linear expression interface.""" +from __future__ import annotations -from typing import List, Union, Dict, Any +from typing import Any from dataclasses import dataclass from numpy import ndarray @@ -40,7 +41,7 @@ class LinearExpression(QuadraticProgramElement): def __init__( self, quadratic_program: Any, - coefficients: Union[ndarray, spmatrix, List[float], Dict[Union[int, str], float]], + coefficients: ndarray | spmatrix | list[float] | dict[int | str, float], ) -> None: """Creates a new linear expression. @@ -56,7 +57,7 @@ def __init__( super().__init__(quadratic_program) self.coefficients = coefficients - def __getitem__(self, i: Union[int, str]) -> float: + def __getitem__(self, i: int | str) -> float: """Returns the i-th coefficient where i can be a variable name or index. Args: @@ -69,13 +70,13 @@ def __getitem__(self, i: Union[int, str]) -> float: i = self.quadratic_program.variables_index[i] return self.coefficients[0, i] - def __setitem__(self, i: Union[int, str], value: float) -> None: + def __setitem__(self, i: int | str, value: float) -> None: if isinstance(i, str): i = self.quadratic_program.variables_index[i] self._coefficients[0, i] = value def _coeffs_to_dok_matrix( - self, coefficients: Union[ndarray, spmatrix, List, Dict[Union[int, str], float]] + self, coefficients: ndarray | spmatrix | list | dict[int | str, float] ) -> dok_matrix: """Maps given 1d-coefficients to a dok_matrix. @@ -119,7 +120,7 @@ def coefficients(self) -> dok_matrix: @coefficients.setter def coefficients( self, - coefficients: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]], + coefficients: ndarray | spmatrix | list[float] | dict[str | int, float], ) -> None: """Sets the coefficients of the linear expression. @@ -136,7 +137,7 @@ def to_array(self) -> ndarray: """ return self._coefficients.toarray()[0] - def to_dict(self, use_name: bool = False) -> Dict[Union[int, str], float]: + def to_dict(self, use_name: bool = False) -> dict[int | str, float]: """Returns the coefficients of the linear expression as dictionary, either using variable names or indices as keys. @@ -154,7 +155,7 @@ def to_dict(self, use_name: bool = False) -> Dict[Union[int, str], float]: else: return {k: v for (_, k), v in self._coefficients.items()} - def evaluate(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> float: + def evaluate(self, x: ndarray | list | dict[int | str, float]) -> float: """Evaluate the linear expression for given variables. Args: @@ -173,7 +174,7 @@ def evaluate(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> flo return val # pylint: disable=unused-argument - def evaluate_gradient(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> ndarray: + def evaluate_gradient(self, x: ndarray | list | dict[int | str, float]) -> ndarray: """Evaluate the gradient of the linear expression for given variables. Args: diff --git a/qiskit_optimization/problems/quadratic_constraint.py b/qiskit_optimization/problems/quadratic_constraint.py index ee044f36e..a23f35e08 100644 --- a/qiskit_optimization/problems/quadratic_constraint.py +++ b/qiskit_optimization/problems/quadratic_constraint.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2019, 2024. +# (C) Copyright IBM 2019, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,8 +11,9 @@ # that they have been altered from the originals. """Quadratic Constraint.""" +from __future__ import annotations -from typing import Union, List, Dict, Tuple, Any +from typing import Any from numpy import ndarray from scipy.sparse import spmatrix @@ -32,13 +33,10 @@ def __init__( # pylint: disable=too-many-positional-arguments self, quadratic_program: Any, name: str, - linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]], - quadratic: Union[ - ndarray, - spmatrix, - List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float], - ], + linear: ndarray | spmatrix | list[float] | dict[str | int, float], + quadratic: ( + ndarray | spmatrix | list[list[float]] | dict[tuple[int | str, int | str], float] + ), sense: ConstraintSense, rhs: float, ) -> None: @@ -68,7 +66,7 @@ def linear(self) -> LinearExpression: @linear.setter def linear( self, - linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]], + linear: ndarray | spmatrix | list[float] | dict[str | int, float], ) -> None: """Sets the linear expression corresponding to the left-hand-side of the constraint. The coefficients can either be given by an array, a (sparse) 1d matrix, a list or a @@ -92,12 +90,9 @@ def quadratic(self) -> QuadraticExpression: @quadratic.setter def quadratic( self, - quadratic: Union[ - ndarray, - spmatrix, - List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float], - ], + quadratic: ( + ndarray | spmatrix | list[list[float]] | dict[tuple[int | str, int | str], float] + ), ) -> None: """Sets the quadratic expression corresponding to the left-hand-side of the constraint. The coefficients can either be given by an array, a (sparse) matrix, a list or a @@ -108,7 +103,7 @@ def quadratic( """ self._quadratic = QuadraticExpression(self.quadratic_program, quadratic) - def evaluate(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> float: + def evaluate(self, x: ndarray | list | dict[int | str, float]) -> float: """Evaluate the left-hand-side of the constraint. Args: diff --git a/qiskit_optimization/problems/quadratic_expression.py b/qiskit_optimization/problems/quadratic_expression.py index 2e1501260..c75ffef6d 100644 --- a/qiskit_optimization/problems/quadratic_expression.py +++ b/qiskit_optimization/problems/quadratic_expression.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2019, 2023. +# (C) Copyright IBM 2019, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,8 +11,9 @@ # that they have been altered from the originals. """Quadratic expression interface.""" +from __future__ import annotations -from typing import List, Union, Dict, Tuple, Any +from typing import Any import numpy as np from numpy import ndarray @@ -30,12 +31,9 @@ class QuadraticExpression(QuadraticProgramElement): def __init__( self, quadratic_program: Any, - coefficients: Union[ - ndarray, - spmatrix, - List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float], - ], + coefficients: ( + ndarray | spmatrix | list[list[float]] | dict[tuple[int | str, int | str], float] + ), ) -> None: """Creates a new quadratic expression. @@ -52,7 +50,7 @@ def __init__( super().__init__(quadratic_program) self.coefficients = coefficients - def __getitem__(self, key: Tuple[Union[int, str], Union[int, str]]) -> float: + def __getitem__(self, key: tuple[int | str, int | str]) -> float: """Returns the coefficient where i, j can be a variable names or indices. Args: @@ -68,7 +66,7 @@ def __getitem__(self, key: Tuple[Union[int, str], Union[int, str]]) -> float: j = self.quadratic_program.variables_index[j] return self.coefficients[min(i, j), max(i, j)] - def __setitem__(self, key: Tuple[Union[int, str], Union[int, str]], value: float) -> None: + def __setitem__(self, key: tuple[int | str, int | str], value: float) -> None: """Sets the coefficient where i, j can be a variable names or indices. Args: @@ -84,12 +82,9 @@ def __setitem__(self, key: Tuple[Union[int, str], Union[int, str]], value: float def _coeffs_to_dok_matrix( self, - coefficients: Union[ - ndarray, - spmatrix, - List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float], - ], + coefficients: ( + ndarray | spmatrix | list[list[float]] | dict[tuple[int | str, int | str], float] + ), ) -> dok_matrix: """Maps given coefficients to a dok_matrix. @@ -142,12 +137,9 @@ def coefficients(self) -> dok_matrix: @coefficients.setter def coefficients( self, - coefficients: Union[ - ndarray, - spmatrix, - List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float], - ], + coefficients: ( + ndarray | spmatrix | list[list[float]] | dict[tuple[int | str, int | str], float] + ), ) -> None: """Sets the coefficients of the quadratic expression. @@ -170,7 +162,7 @@ def to_array(self, symmetric: bool = False) -> ndarray: def to_dict( self, symmetric: bool = False, use_name: bool = False - ) -> Dict[Union[Tuple[int, int], Tuple[str, str]], float]: + ) -> dict[tuple[int, int] | tuple[str, str], float]: """Returns the coefficients of the quadratic expression as dictionary, either using tuples of variable names or indices as keys. @@ -193,7 +185,7 @@ def to_dict( else: return {(int(i), int(j)): v for (i, j), v in coeffs.items()} - def evaluate(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> float: + def evaluate(self, x: ndarray | list | dict[int | str, float]) -> float: """Evaluate the quadratic expression for given variables: x * Q * x. Args: @@ -210,7 +202,7 @@ def evaluate(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> flo # return the result return val - def evaluate_gradient(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> ndarray: + def evaluate_gradient(self, x: ndarray | list | dict[int | str, float]) -> ndarray: """Evaluate the gradient of the quadratic expression for given variables. Args: @@ -227,9 +219,7 @@ def evaluate_gradient(self, x: Union[ndarray, List, Dict[Union[int, str], float] # return the result return val - def _cast_as_array( - self, x: Union[ndarray, List, Dict[Union[int, str], float]] - ) -> Union[dok_matrix, np.ndarray]: + def _cast_as_array(self, x: ndarray | list | dict[int | str, float]) -> dok_matrix | np.ndarray: """Converts input to an array if it is a dictionary or list.""" if isinstance(x, dict): x_aux = np.zeros(self.quadratic_program.get_num_vars()) diff --git a/qiskit_optimization/problems/quadratic_objective.py b/qiskit_optimization/problems/quadratic_objective.py index 385735df9..817d59c5e 100644 --- a/qiskit_optimization/problems/quadratic_objective.py +++ b/qiskit_optimization/problems/quadratic_objective.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2019, 2024. +# (C) Copyright IBM 2019, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,9 +11,10 @@ # that they have been altered from the originals. """Quadratic Objective.""" +from __future__ import annotations from enum import Enum -from typing import Union, List, Dict, Tuple, Any, Optional +from typing import Any from numpy import ndarray from scipy.sparse import spmatrix @@ -42,17 +43,10 @@ def __init__( # pylint: disable=too-many-positional-arguments self, quadratic_program: Any, constant: float = 0.0, - linear: Optional[ - Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]] - ] = None, - quadratic: Optional[ - Union[ - ndarray, - spmatrix, - List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float], - ] - ] = None, + linear: ndarray | spmatrix | list[float] | dict[str | int, float] | None = None, + quadratic: ( + ndarray | spmatrix | list[list[float]] | dict[tuple[int | str, int | str], float] | None + ) = None, sense: ObjSense = ObjSense.MINIMIZE, ) -> None: """Constructs a quadratic objective function. @@ -104,7 +98,7 @@ def linear(self) -> LinearExpression: @linear.setter def linear( self, - linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]], + linear: ndarray | spmatrix | list[float] | dict[str | int, float], ) -> None: """Sets the coefficients of the linear part of the objective function. @@ -126,12 +120,9 @@ def quadratic(self) -> QuadraticExpression: @quadratic.setter def quadratic( self, - quadratic: Union[ - ndarray, - spmatrix, - List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float], - ], + quadratic: ( + ndarray | spmatrix | list[list[float]] | dict[tuple[int | str, int | str], float] + ), ) -> None: """Sets the coefficients of the quadratic part of the objective function. @@ -159,7 +150,7 @@ def sense(self, sense: ObjSense) -> None: """ self._sense = sense - def evaluate(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> float: + def evaluate(self, x: ndarray | list | dict[int | str, float]) -> float: """Evaluate the quadratic objective for given variable values. Args: @@ -180,7 +171,7 @@ def evaluate(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> flo ) return self.constant + self.linear.evaluate(x) + self.quadratic.evaluate(x) - def evaluate_gradient(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> ndarray: + def evaluate_gradient(self, x: ndarray | list | dict[int | str, float]) -> ndarray: """Evaluate the gradient of the quadratic objective for given variable values. Args: diff --git a/qiskit_optimization/problems/quadratic_program.py b/qiskit_optimization/problems/quadratic_program.py index 2c2e4a2d6..8744525db 100644 --- a/qiskit_optimization/problems/quadratic_program.py +++ b/qiskit_optimization/problems/quadratic_program.py @@ -11,12 +11,13 @@ # that they have been altered from the originals. """Quadratic Program.""" +from __future__ import annotations import logging from collections.abc import Sequence from enum import Enum from math import isclose -from typing import Dict, List, Optional, Tuple, Union, cast +from typing import cast from warnings import warn import numpy as np @@ -68,14 +69,14 @@ def __init__(self, name: str = "") -> None: self.name = name self._status = QuadraticProgram.Status.VALID - self._variables: List[Variable] = [] - self._variables_index: Dict[str, int] = {} + self._variables: list[Variable] = [] + self._variables_index: dict[str, int] = {} - self._linear_constraints: List[LinearConstraint] = [] - self._linear_constraints_index: Dict[str, int] = {} + self._linear_constraints: list[LinearConstraint] = [] + self._linear_constraints_index: dict[str, int] = {} - self._quadratic_constraints: List[QuadraticConstraint] = [] - self._quadratic_constraints_index: Dict[str, int] = {} + self._quadratic_constraints: list[QuadraticConstraint] = [] + self._quadratic_constraints_index: dict[str, int] = {} self._objective = QuadraticObjective(self) @@ -155,7 +156,7 @@ def status(self) -> QuadraticProgramStatus: return self._status @property - def variables(self) -> List[Variable]: + def variables(self) -> list[Variable]: """Returns the list of variables of the quadratic program. Returns: @@ -164,7 +165,7 @@ def variables(self) -> List[Variable]: return self._variables @property - def variables_index(self) -> Dict[str, int]: + def variables_index(self) -> dict[str, int]: """Returns the dictionary that maps the name of a variable to its index. Returns: @@ -174,10 +175,10 @@ def variables_index(self) -> Dict[str, int]: def _add_variable( self, - lowerbound: Union[float, int], - upperbound: Union[float, int], + lowerbound: float | int, + upperbound: float | int, vartype: VarType, - name: Optional[str], + name: str | None, ) -> Variable: if not name: name = "x" @@ -188,13 +189,13 @@ def _add_variable( def _add_variables( # pylint: disable=too-many-positional-arguments self, - keys: Union[int, Sequence], - lowerbound: Union[float, int], - upperbound: Union[float, int], + keys: int | Sequence, + lowerbound: float | int, + upperbound: float | int, vartype: VarType, - name: Optional[str], + name: str | None, key_format: str, - ) -> Tuple[List[str], List[Variable]]: + ) -> tuple[list[str], list[Variable]]: if isinstance(keys, int) and keys < 1: raise QiskitOptimizationError(f"Cannot create non-positive number of variables: {keys}") if not name: @@ -242,13 +243,13 @@ def _find_name(name, key_format, k): def _var_dict( # pylint: disable=too-many-positional-arguments self, - keys: Union[int, Sequence], - lowerbound: Union[float, int], - upperbound: Union[float, int], + keys: int | Sequence, + lowerbound: float | int, + upperbound: float | int, vartype: VarType, - name: Optional[str], + name: str | None, key_format: str, - ) -> Dict[str, Variable]: + ) -> dict[str, Variable]: """ Adds a positive number of variables to the variable list and index and returns a dictionary mapping the variable names to their instances. If 'key_format' is present, @@ -280,13 +281,13 @@ def _var_dict( # pylint: disable=too-many-positional-arguments def _var_list( # pylint: disable=too-many-positional-arguments self, - keys: Union[int, Sequence], - lowerbound: Union[float, int], - upperbound: Union[float, int], + keys: int | Sequence, + lowerbound: float | int, + upperbound: float | int, vartype: VarType, - name: Optional[str], + name: str | None, key_format: str, - ) -> List[Variable]: + ) -> list[Variable]: """ Adds a positive number of variables to the variable list and index and returns a list of variable instances. @@ -314,9 +315,9 @@ def _var_list( # pylint: disable=too-many-positional-arguments def continuous_var( self, - lowerbound: Union[float, int] = 0, - upperbound: Union[float, int] = INFINITY, - name: Optional[str] = None, + lowerbound: float | int = 0, + upperbound: float | int = INFINITY, + name: str | None = None, ) -> Variable: """Adds a continuous variable to the quadratic program. @@ -336,12 +337,12 @@ def continuous_var( def continuous_var_dict( # pylint: disable=too-many-positional-arguments self, - keys: Union[int, Sequence], - lowerbound: Union[float, int] = 0, - upperbound: Union[float, int] = INFINITY, - name: Optional[str] = None, + keys: int | Sequence, + lowerbound: float | int = 0, + upperbound: float | int = INFINITY, + name: str | None = None, key_format: str = "{}", - ) -> Dict[str, Variable]: + ) -> dict[str, Variable]: """ Uses 'var_dict' to construct a dictionary of continuous variables @@ -375,12 +376,12 @@ def continuous_var_dict( # pylint: disable=too-many-positional-arguments def continuous_var_list( # pylint: disable=too-many-positional-arguments self, - keys: Union[int, Sequence], - lowerbound: Union[float, int] = 0, - upperbound: Union[float, int] = INFINITY, - name: Optional[str] = None, + keys: int | Sequence, + lowerbound: float | int = 0, + upperbound: float | int = INFINITY, + name: str | None = None, key_format: str = "{}", - ) -> List[Variable]: + ) -> list[Variable]: """ Uses 'var_list' to construct a list of continuous variables @@ -407,7 +408,7 @@ def continuous_var_list( # pylint: disable=too-many-positional-arguments keys, lowerbound, upperbound, Variable.Type.CONTINUOUS, name, key_format ) - def binary_var(self, name: Optional[str] = None) -> Variable: + def binary_var(self, name: str | None = None) -> Variable: """Adds a binary variable to the quadratic program. Args: @@ -424,10 +425,10 @@ def binary_var(self, name: Optional[str] = None) -> Variable: def binary_var_dict( self, - keys: Union[int, Sequence], - name: Optional[str] = None, + keys: int | Sequence, + name: str | None = None, key_format: str = "{}", - ) -> Dict[str, Variable]: + ) -> dict[str, Variable]: """ Uses 'var_dict' to construct a dictionary of binary variables @@ -459,10 +460,10 @@ def binary_var_dict( def binary_var_list( self, - keys: Union[int, Sequence], - name: Optional[str] = None, + keys: int | Sequence, + name: str | None = None, key_format: str = "{}", - ) -> List[Variable]: + ) -> list[Variable]: """ Uses 'var_list' to construct a list of binary variables @@ -487,9 +488,9 @@ def binary_var_list( def integer_var( self, - lowerbound: Union[float, int] = 0, - upperbound: Union[float, int] = INFINITY, - name: Optional[str] = None, + lowerbound: float | int = 0, + upperbound: float | int = INFINITY, + name: str | None = None, ) -> Variable: """Adds an integer variable to the quadratic program. @@ -509,12 +510,12 @@ def integer_var( def integer_var_dict( # pylint: disable=too-many-positional-arguments self, - keys: Union[int, Sequence], - lowerbound: Union[float, int] = 0, - upperbound: Union[float, int] = INFINITY, - name: Optional[str] = None, + keys: int | Sequence, + lowerbound: float | int = 0, + upperbound: float | int = INFINITY, + name: str | None = None, key_format: str = "{}", - ) -> Dict[str, Variable]: + ) -> dict[str, Variable]: """ Uses 'var_dict' to construct a dictionary of integer variables @@ -548,12 +549,12 @@ def integer_var_dict( # pylint: disable=too-many-positional-arguments def integer_var_list( # pylint: disable=too-many-positional-arguments self, - keys: Union[int, Sequence], - lowerbound: Union[float, int] = 0, - upperbound: Union[float, int] = INFINITY, - name: Optional[str] = None, + keys: int | Sequence, + lowerbound: float | int = 0, + upperbound: float | int = INFINITY, + name: str | None = None, key_format: str = "{}", - ) -> List[Variable]: + ) -> list[Variable]: """ Uses 'var_list' to construct a list of integer variables @@ -578,7 +579,7 @@ def integer_var_list( # pylint: disable=too-many-positional-arguments """ return self._var_list(keys, lowerbound, upperbound, Variable.Type.INTEGER, name, key_format) - def get_variable(self, i: Union[int, str]) -> Variable: + def get_variable(self, i: int | str) -> Variable: """Returns a variable for a given name or index. Args: @@ -592,7 +593,7 @@ def get_variable(self, i: Union[int, str]) -> Variable: else: return self.variables[self._variables_index[i]] - def get_num_vars(self, vartype: Optional[VarType] = None) -> int: + def get_num_vars(self, vartype: VarType | None = None) -> int: """Returns the total number of variables or the number of variables of the specified type. Args: @@ -631,7 +632,7 @@ def get_num_integer_vars(self) -> int: return self.get_num_vars(Variable.Type.INTEGER) @property - def linear_constraints(self) -> List[LinearConstraint]: + def linear_constraints(self) -> list[LinearConstraint]: """Returns the list of linear constraints of the quadratic program. Returns: @@ -640,7 +641,7 @@ def linear_constraints(self) -> List[LinearConstraint]: return self._linear_constraints @property - def linear_constraints_index(self) -> Dict[str, int]: + def linear_constraints_index(self) -> dict[str, int]: """Returns the dictionary that maps the name of a linear constraint to its index. Returns: @@ -650,10 +651,10 @@ def linear_constraints_index(self) -> Dict[str, int]: def linear_constraint( self, - linear: Union[ndarray, spmatrix, List[float], Dict[Union[int, str], float]] = None, - sense: Union[str, ConstraintSense] = "<=", + linear: ndarray | spmatrix | list[float] | dict[int | str, float] | None = None, + sense: str | ConstraintSense = "<=", rhs: float = 0.0, - name: Optional[str] = None, + name: str | None = None, ) -> LinearConstraint: """Adds a linear equality constraint to the quadratic program of the form: ``(linear * x) sense rhs``. @@ -693,7 +694,7 @@ def linear_constraint( self.linear_constraints.append(constraint) return constraint - def get_linear_constraint(self, i: Union[int, str]) -> LinearConstraint: + def get_linear_constraint(self, i: int | str) -> LinearConstraint: """Returns a linear constraint for a given name or index. Args: @@ -720,7 +721,7 @@ def get_num_linear_constraints(self) -> int: return len(self._linear_constraints) @property - def quadratic_constraints(self) -> List[QuadraticConstraint]: + def quadratic_constraints(self) -> list[QuadraticConstraint]: """Returns the list of quadratic constraints of the quadratic program. Returns: @@ -729,7 +730,7 @@ def quadratic_constraints(self) -> List[QuadraticConstraint]: return self._quadratic_constraints @property - def quadratic_constraints_index(self) -> Dict[str, int]: + def quadratic_constraints_index(self) -> dict[str, int]: """Returns the dictionary that maps the name of a quadratic constraint to its index. Returns: @@ -739,16 +740,13 @@ def quadratic_constraints_index(self) -> Dict[str, int]: def quadratic_constraint( # pylint: disable=too-many-positional-arguments self, - linear: Union[ndarray, spmatrix, List[float], Dict[Union[int, str], float]] = None, - quadratic: Union[ - ndarray, - spmatrix, - List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float], - ] = None, - sense: Union[str, ConstraintSense] = "<=", + linear: ndarray | spmatrix | list[float] | dict[int | str, float] | None = None, + quadratic: ( + ndarray | spmatrix | list[list[float]] | dict[tuple[int | str, int | str], float] | None + ) = None, + sense: str | ConstraintSense = "<=", rhs: float = 0.0, - name: Optional[str] = None, + name: str | None = None, ) -> QuadraticConstraint: """Adds a quadratic equality constraint to the quadratic program of the form: ``(x * quadratic * x + linear * x) sense rhs``. @@ -792,7 +790,7 @@ def quadratic_constraint( # pylint: disable=too-many-positional-arguments self.quadratic_constraints.append(constraint) return constraint - def get_quadratic_constraint(self, i: Union[int, str]) -> QuadraticConstraint: + def get_quadratic_constraint(self, i: int | str) -> QuadraticConstraint: """Returns a quadratic constraint for a given name or index. Args: @@ -818,7 +816,7 @@ def get_num_quadratic_constraints(self) -> int: """ return len(self._quadratic_constraints) - def remove_linear_constraint(self, i: Union[str, int]) -> None: + def remove_linear_constraint(self, i: str | int) -> None: """Remove a linear constraint Args: @@ -835,7 +833,7 @@ def remove_linear_constraint(self, i: Union[str, int]) -> None: cst.name: j for j, cst in enumerate(self._linear_constraints) } - def remove_quadratic_constraint(self, i: Union[str, int]) -> None: + def remove_quadratic_constraint(self, i: str | int) -> None: """Remove a quadratic constraint Args: @@ -864,13 +862,10 @@ def objective(self) -> QuadraticObjective: def minimize( self, constant: float = 0.0, - linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]] = None, - quadratic: Union[ - ndarray, - spmatrix, - List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float], - ] = None, + linear: ndarray | spmatrix | list[float] | dict[str | int, float] | None = None, + quadratic: ( + ndarray | spmatrix | list[list[float]] | dict[tuple[int | str, int | str], float] | None + ) = None, ) -> None: """Sets a quadratic objective to be minimized. @@ -889,13 +884,10 @@ def minimize( def maximize( self, constant: float = 0.0, - linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]] = None, - quadratic: Union[ - ndarray, - spmatrix, - List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float], - ] = None, + linear: ndarray | spmatrix | list[float] | dict[str | int, float] | None = None, + quadratic: ( + ndarray | spmatrix | list[list[float]] | dict[tuple[int | str, int | str], float] | None + ) = None, ) -> None: """Sets a quadratic objective to be maximized. @@ -911,7 +903,7 @@ def maximize( self, constant, linear, quadratic, QuadraticObjective.Sense.MAXIMIZE ) - def _copy_from(self, other: "QuadraticProgram", include_name: bool) -> None: + def _copy_from(self, other: QuadraticProgram, include_name: bool) -> None: """Copy another QuadraticProgram to this updating QuadraticProgramElement Note: this breaks the consistency of `other`. You cannot use `other` after the copy. @@ -1000,9 +992,9 @@ def write_to_lp_file(self, filename: str) -> None: def substitute_variables( self, - constants: Optional[Dict[Union[str, int], float]] = None, - variables: Optional[Dict[Union[str, int], Tuple[Union[str, int], float]]] = None, - ) -> "QuadraticProgram": + constants: dict[str | int, float] | None = None, + variables: dict[str | int, tuple[str | int, float]] | None = None, + ) -> QuadraticProgram: """Substitutes variables with constants or other variables. Args: @@ -1031,7 +1023,7 @@ def substitute_variables( return substitute_variables(self, constants, variables) - def to_ising(self) -> Tuple[SparsePauliOp, float]: + def to_ising(self) -> tuple[SparsePauliOp, float]: """Return the Ising Hamiltonian of this problem. Variables are mapped to qubits in the same order, i.e., @@ -1083,8 +1075,8 @@ def from_ising( self._copy_from(other, include_name=False) def get_feasibility_info( - self, x: Union[List[float], np.ndarray] - ) -> Tuple[bool, List[Variable], List[Constraint]]: + self, x: list[float] | np.ndarray + ) -> tuple[bool, list[Variable], list[Constraint]]: """Returns whether a solution is feasible or not along with the violations. Args: x: a solution value, such as returned in an optimizer result. @@ -1112,8 +1104,8 @@ def get_feasibility_info( # check whether the input satisfy the constraints of the problem violated_constraints = [] - for constraint in cast(List[Constraint], self._linear_constraints) + cast( - List[Constraint], self._quadratic_constraints + for constraint in cast(list[Constraint], self._linear_constraints) + cast( + list[Constraint], self._quadratic_constraints ): lhs = constraint.evaluate(x) if constraint.sense == ConstraintSense.LE and lhs > constraint.rhs: @@ -1127,7 +1119,7 @@ def get_feasibility_info( return feasible, violated_variables, violated_constraints - def is_feasible(self, x: Union[List[float], np.ndarray]) -> bool: + def is_feasible(self, x: list[float] | np.ndarray) -> bool: """Returns whether a solution is feasible or not. Args: diff --git a/qiskit_optimization/problems/substitute_variables.py b/qiskit_optimization/problems/substitute_variables.py index 69da1d818..1979fc50e 100644 --- a/qiskit_optimization/problems/substitute_variables.py +++ b/qiskit_optimization/problems/substitute_variables.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2019, 2023. +# (C) Copyright IBM 2019, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,12 +11,13 @@ # that they have been altered from the originals. """Substitute variables of QuadraticProgram.""" +from __future__ import annotations import logging from collections import defaultdict from dataclasses import dataclass from math import isclose -from typing import Dict, Optional, Tuple, Union, cast +from typing import cast from ..exceptions import QiskitOptimizationError from ..infinity import INFINITY @@ -40,14 +41,14 @@ class SubstitutionExpression: """Constant value""" coeff: float = 0.0 """Coefficient of the new variable""" - variable: Optional[str] = None + variable: str | None = None """Variable name or `None`""" def substitute_variables( quadratic_program: QuadraticProgram, - constants: Optional[Dict[Union[str, int], float]] = None, - variables: Optional[Dict[Union[str, int], Tuple[Union[str, int], float]]] = None, + constants: dict[str | int, float] | None = None, + variables: dict[str | int, tuple[str | int, float]] | None = None, ) -> QuadraticProgram: """Substitutes variables with constants or other variables. @@ -115,12 +116,12 @@ class _SubstituteVariables: variables""" def __init__(self) -> None: - self._src: Optional[QuadraticProgram] = None - self._dst: Optional[QuadraticProgram] = None - self._subs: Dict[str, SubstitutionExpression] = {} + self._src: QuadraticProgram | None = None + self._dst: QuadraticProgram | None = None + self._subs: dict[str, SubstitutionExpression] = {} def substitute_variables( - self, quadratic_program: QuadraticProgram, subs: Dict[str, SubstitutionExpression] + self, quadratic_program: QuadraticProgram, subs: dict[str, SubstitutionExpression] ) -> QuadraticProgram: """Substitutes variables with constants or other variables. @@ -220,9 +221,9 @@ def _variables(self) -> bool: return feasible - def _linear_expression(self, lin_expr: LinearExpression) -> Tuple[float, LinearExpression]: + def _linear_expression(self, lin_expr: LinearExpression) -> tuple[float, LinearExpression]: const = 0.0 - lin_dict: Dict[str, float] = defaultdict(float) + lin_dict: dict[str, float] = defaultdict(float) for i, w_i in lin_expr.to_dict(use_name=True).items(): i = cast(str, i) expr_i = self._subs.get(i, SubstitutionExpression(coeff=1, variable=i)) @@ -236,10 +237,10 @@ def _linear_expression(self, lin_expr: LinearExpression) -> Tuple[float, LinearE def _quadratic_expression( self, quad_expr: QuadraticExpression - ) -> Tuple[float, Optional[LinearExpression], Optional[QuadraticExpression]]: + ) -> tuple[float, LinearExpression | None, QuadraticExpression | None]: const = 0.0 - lin_dict: Dict[str, float] = defaultdict(float) - quad_dict: Dict[Tuple[str, str], float] = defaultdict(float) + lin_dict: dict[str, float] = defaultdict(float) + quad_dict: dict[tuple[str, str], float] = defaultdict(float) for (i, j), w_ij in quad_expr.to_dict(use_name=True).items(): i = cast(str, i) j = cast(str, j) diff --git a/qiskit_optimization/problems/variable.py b/qiskit_optimization/problems/variable.py index 06d8e0484..4fa83eba0 100644 --- a/qiskit_optimization/problems/variable.py +++ b/qiskit_optimization/problems/variable.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2019, 2024. +# (C) Copyright IBM 2019, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,9 +11,10 @@ # that they have been altered from the originals. """Variable interface""" +from __future__ import annotations from enum import Enum -from typing import Tuple, Union, Any +from typing import Any from .quadratic_program_element import QuadraticProgramElement from ..exceptions import QiskitOptimizationError @@ -37,8 +38,8 @@ def __init__( # pylint: disable=too-many-positional-arguments self, quadratic_program: Any, name: str, - lowerbound: Union[float, int] = 0, - upperbound: Union[float, int] = INFINITY, + lowerbound: float | int = 0, + upperbound: float | int = INFINITY, vartype: VarType = VarType.CONTINUOUS, ) -> None: """Creates a new Variable. @@ -76,7 +77,7 @@ def name(self) -> str: return self._name @property - def lowerbound(self) -> Union[float, int]: + def lowerbound(self) -> float | int: """Returns the lowerbound of the variable. Returns: @@ -85,7 +86,7 @@ def lowerbound(self) -> Union[float, int]: return self._lowerbound @lowerbound.setter - def lowerbound(self, lowerbound: Union[float, int]) -> None: + def lowerbound(self, lowerbound: float | int) -> None: """Sets the lowerbound of the variable. Args: @@ -99,7 +100,7 @@ def lowerbound(self, lowerbound: Union[float, int]) -> None: self._lowerbound = lowerbound @property - def upperbound(self) -> Union[float, int]: + def upperbound(self) -> float | int: """Returns the upperbound of the variable. Returns: @@ -108,7 +109,7 @@ def upperbound(self) -> Union[float, int]: return self._upperbound @upperbound.setter - def upperbound(self, upperbound: Union[float, int]) -> None: + def upperbound(self, upperbound: float | int) -> None: """Sets the upperbound of the variable. Args: @@ -140,7 +141,7 @@ def vartype(self, vartype: VarType) -> None: """ self._vartype = vartype - def as_tuple(self) -> Tuple[str, Union[float, int], Union[float, int], VarType]: + def as_tuple(self) -> tuple[str, float | int, float | int, VarType]: """Returns a tuple corresponding to this variable. Returns: diff --git a/qiskit_optimization/translators/docplex_mp.py b/qiskit_optimization/translators/docplex_mp.py index 117f98515..6e1cffdf3 100644 --- a/qiskit_optimization/translators/docplex_mp.py +++ b/qiskit_optimization/translators/docplex_mp.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2021, 2023. +# (C) Copyright IBM 2021, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,9 +11,10 @@ # that they have been altered from the originals. """Translator between a docplex.mp model and a quadratic program""" +from __future__ import annotations from math import isclose -from typing import Any, Dict, Optional, Tuple, cast +from typing import Any, cast from warnings import warn from docplex.mp.basic import Expr @@ -144,8 +145,8 @@ def __init__(self, model: Model): """ self._model: Model = model self._quadratic_program: QuadraticProgram = QuadraticProgram() - self._var_names: Dict[Var, str] = {} - self._var_bounds: Dict[str, Tuple[float, float]] = {} + self._var_names: dict[Var, str] = {} + self._var_bounds: dict[str, tuple[float, float]] = {} def _variables(self): # keep track of names separately, since docplex allows to have None names. @@ -161,7 +162,7 @@ def _variables(self): self._var_names[x] = x_new.name self._var_bounds[x.name] = (x_new.lowerbound, x_new.upperbound) - def _linear_expr(self, expr: AbstractLinearExpr) -> Dict[str, float]: + def _linear_expr(self, expr: AbstractLinearExpr) -> dict[str, float]: # AbstractLinearExpr is a parent of LinearExpr, ConstantExpr, and ZeroExpr linear = {} for x, coeff in expr.iter_terms(): @@ -170,7 +171,7 @@ def _linear_expr(self, expr: AbstractLinearExpr) -> Dict[str, float]: def _quadratic_expr( self, expr: QuadExpr - ) -> Tuple[Dict[str, float], Dict[Tuple[str, str], float]]: + ) -> tuple[dict[str, float], dict[tuple[str, str], float]]: linear = self._linear_expr(expr.get_linear_part()) quad = {} for x, y, coeff in expr.iter_quad_triplets(): @@ -179,7 +180,7 @@ def _quadratic_expr( quad[i, j] = coeff return linear, quad - def quadratic_program(self, indicator_big_m: Optional[float]) -> QuadraticProgram: + def quadratic_program(self, indicator_big_m: float | None) -> QuadraticProgram: """Generate a quadratic program corresponding to the input Docplex model. Args: @@ -245,7 +246,7 @@ def quadratic_program(self, indicator_big_m: Optional[float]) -> QuadraticProgra return self._quadratic_program @staticmethod - def _subtract(dict1: Dict[Any, float], dict2: Dict[Any, float]) -> Dict[Any, float]: + def _subtract(dict1: dict[Any, float], dict2: dict[Any, float]) -> dict[Any, float]: """Calculate dict1 - dict2""" ret = dict1.copy() for key, val2 in dict2.items(): @@ -261,7 +262,7 @@ def _subtract(dict1: Dict[Any, float], dict2: Dict[Any, float]) -> Dict[Any, flo def _linear_constraint( self, constraint: LinearConstraint - ) -> Tuple[Dict[str, float], str, float]: + ) -> tuple[dict[str, float], str, float]: left_expr = constraint.get_left_expr() right_expr = constraint.get_right_expr() # for linear constraints we may get an instance of Var instead of expression, @@ -289,7 +290,7 @@ def _linear_constraint( def _quadratic_constraint( self, constraint: QuadraticConstraint - ) -> Tuple[Dict[str, float], Dict[Tuple[str, str], float], str, float]: + ) -> tuple[dict[str, float], dict[tuple[str, str], float], str, float]: left_expr = constraint.get_left_expr() right_expr = constraint.get_right_expr() if not isinstance(left_expr, (Expr, Var)): @@ -322,7 +323,7 @@ def _quadratic_constraint( rhs = right_expr.constant - left_expr.constant return linear, quadratic, self._sense_dict[constraint.sense], rhs - def _linear_bounds(self, linear: Dict[str, float]): + def _linear_bounds(self, linear: dict[str, float]): linear_lb = 0.0 linear_ub = 0.0 for var_name, val in linear.items(): @@ -337,7 +338,7 @@ def _indicator_constraints( self, constraint: IndicatorConstraint, name: str, - indicator_big_m: Optional[float] = None, + indicator_big_m: float | None = None, ): binary_var = constraint.binary_var active_value = constraint.active_value @@ -376,7 +377,7 @@ def _indicator_constraints( return ret -def from_docplex_mp(model: Model, indicator_big_m: Optional[float] = None) -> QuadraticProgram: +def from_docplex_mp(model: Model, indicator_big_m: float | None = None) -> QuadraticProgram: """Translate a docplex.mp model into a quadratic program. Note that this supports the following features of docplex: diff --git a/qiskit_optimization/translators/ising.py b/qiskit_optimization/translators/ising.py index e99a0d3d1..bd55d87f1 100644 --- a/qiskit_optimization/translators/ising.py +++ b/qiskit_optimization/translators/ising.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2019, 2023. +# (C) Copyright IBM 2019, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -13,7 +13,6 @@ """Translator between an Ising Hamiltonian and a quadratic program""" import math -from typing import Tuple import numpy as np from qiskit.quantum_info import Pauli, SparsePauliOp @@ -23,7 +22,7 @@ from qiskit_optimization.problems.quadratic_program import QuadraticProgram -def to_ising(quad_prog: QuadraticProgram) -> Tuple[SparsePauliOp, float]: +def to_ising(quad_prog: QuadraticProgram) -> tuple[SparsePauliOp, float]: """Return the Ising Hamiltonian of this problem. Variables are mapped to qubits in the same order, i.e., diff --git a/qiskit_optimization/translators/prettyprint.py b/qiskit_optimization/translators/prettyprint.py index 2b4f97b67..ffd91d183 100644 --- a/qiskit_optimization/translators/prettyprint.py +++ b/qiskit_optimization/translators/prettyprint.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2022, 2024. +# (C) Copyright IBM 2022, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,10 +11,11 @@ # that they have been altered from the originals. """Translate ``QuadraticProgram`` into a pretty-printed string""" +from __future__ import annotations from io import StringIO from math import isclose -from typing import List, Optional, Union, cast +from typing import cast import numpy as np @@ -30,7 +31,7 @@ DEFAULT_TRUNCATE = 50 -def _int_if_close(val: Union[int, float, np.integer, np.floating]) -> Union[int, float]: +def _int_if_close(val: int | float | np.integer | np.floating) -> int | float: """Convert a value into an integer if possible Note: if abs(val) is too large, int(val) is not correct @@ -98,7 +99,7 @@ def _check_name(name: str, name_type: str) -> None: raise QiskitOptimizationError(f"{name_type} name is not printable: {repr(name)}") -def _concatenate_terms(terms: List[str], wrap: int, indent: int) -> str: +def _concatenate_terms(terms: list[str], wrap: int, indent: int) -> str: ind = " " * indent if wrap == 0: return ind + " ".join(terms) @@ -119,8 +120,8 @@ def _concatenate_terms(terms: List[str], wrap: int, indent: int) -> str: def expr2str( # pylint: disable=too-many-positional-arguments constant: float = 0.0, - linear: Optional[LinearExpression] = None, - quadratic: Optional[QuadraticExpression] = None, + linear: LinearExpression | None = None, + quadratic: QuadraticExpression | None = None, truncate: int = 0, suffix: str = "", wrap: int = 0, diff --git a/qiskit_optimization/utils/validation.py b/qiskit_optimization/utils/validation.py index 73abb0db1..76f66e9ec 100644 --- a/qiskit_optimization/utils/validation.py +++ b/qiskit_optimization/utils/validation.py @@ -14,10 +14,8 @@ Validation module """ -from typing import Set - -def validate_in_set(name: str, value: object, values: Set[object]) -> None: +def validate_in_set(name: str, value: object, values: set[object]) -> None: """ Args: name: value name. diff --git a/test/algorithms_test_case.py b/test/algorithms_test_case.py index aeae60cbd..ff2d0d2ae 100644 --- a/test/algorithms_test_case.py +++ b/test/algorithms_test_case.py @@ -11,8 +11,8 @@ # that they have been altered from the originals. """Algorithms Test Case""" +from __future__ import annotations -from typing import Optional from abc import ABC import warnings import inspect @@ -75,7 +75,7 @@ def setUpClass(cls) -> None: level = logging._nameToLevel.get(os.getenv("LOG_LEVEL"), logging.INFO) cls.log.setLevel(level) - def get_resource_path(self, filename: str, path: Optional[str] = None) -> str: + def get_resource_path(self, filename: str, path: str | None = None) -> str: """Get the absolute path to a resource. Args: filename: filename or relative path to the resource. diff --git a/test/optimization_test_case.py b/test/optimization_test_case.py index 689b8e47c..10d48933a 100755 --- a/test/optimization_test_case.py +++ b/test/optimization_test_case.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2018, 2023. +# (C) Copyright IBM 2018, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,8 +11,8 @@ # that they have been altered from the originals. """Optimization Test Case""" +from __future__ import annotations -from typing import Optional from abc import ABC import warnings import inspect @@ -72,7 +72,7 @@ def setUpClass(cls) -> None: level = logging._nameToLevel.get(os.getenv("LOG_LEVEL"), logging.INFO) cls.log.setLevel(level) - def get_resource_path(self, filename: str, path: Optional[str] = None) -> str: + def get_resource_path(self, filename: str, path: str | None = None) -> str: """Get the absolute path to a resource. Args: filename: filename or relative path to the resource. diff --git a/test/optimizers/test_optimizers.py b/test/optimizers/test_optimizers.py index 318858dc8..765f9c950 100644 --- a/test/optimizers/test_optimizers.py +++ b/test/optimizers/test_optimizers.py @@ -11,11 +11,11 @@ # that they have been altered from the originals. """Test Optimizers""" +from __future__ import annotations import unittest from test import QiskitAlgorithmsTestCase -from typing import Optional, List, Tuple from ddt import ddt, data, unpack import numpy as np from scipy.optimize import rosen, rosen_der @@ -47,7 +47,7 @@ def run_optimizer( optimizer: Optimizer, max_nfev: int, grad: bool = False, - bounds: Optional[List[Tuple[float, float]]] = None, + bounds: list[tuple[float, float]] | None = None, ): """Test the optimizer. diff --git a/tools/check_copyright.py b/tools/check_copyright.py index 6085e0a17..33f1cd4b4 100644 --- a/tools/check_copyright.py +++ b/tools/check_copyright.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2020, 2023. +# (C) Copyright IBM 2020, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,8 +11,8 @@ # that they have been altered from the originals. """ Fix copyright year in header """ +from __future__ import annotations -from typing import Tuple, Union, List import builtins import sys import os @@ -47,7 +47,7 @@ def _get_year_from_date(date) -> int: return int(date[:4]) - def _cmd_execute(self, args: List[str]) -> Tuple[str, Union[None, str]]: + def _cmd_execute(self, args: list[str]) -> tuple[str, None | str]: # execute command env = {} for k in ["SYSTEMROOT", "PATH"]: @@ -73,7 +73,7 @@ def _cmd_execute(self, args: List[str]) -> Tuple[str, Union[None, str]]: err_str = err_str if err_str else None return out_str, err_str - def _get_changed_files(self) -> List[str]: + def _get_changed_files(self) -> list[str]: out_str, err_str = self._cmd_execute(["git", "diff", "--name-only", "HEAD"]) if err_str: raise builtins.Exception(err_str) @@ -98,7 +98,7 @@ def _get_file_last_year(self, relative_path: str) -> int: return last_year - def check_copyright(self, file_path) -> Tuple[bool, bool, bool]: + def check_copyright(self, file_path) -> tuple[bool, bool, bool]: """check copyright for a file""" file_with_utf8 = False file_with_invalid_year = False @@ -177,11 +177,11 @@ def check_copyright(self, file_path) -> Tuple[bool, bool, bool]: return file_with_utf8, file_with_invalid_year, file_has_header - def check(self) -> Tuple[int, int, int]: + def check(self) -> tuple[int, int, int]: """check copyright""" return self._check_copyright(self._root_dir) - def _check_copyright(self, path: str) -> Tuple[int, int, int]: + def _check_copyright(self, path: str) -> tuple[int, int, int]: files_with_utf8 = 0 files_with_invalid_year = 0 files_with_header = 0 diff --git a/tools/extract_deprecation.py b/tools/extract_deprecation.py index 5a61d982b..cef9cc653 100644 --- a/tools/extract_deprecation.py +++ b/tools/extract_deprecation.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2021, 2023. +# (C) Copyright IBM 2021, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -12,7 +12,8 @@ """ Extract deprecation messages from input """ -from typing import List +from __future__ import annotations + import sys import os import argparse @@ -24,7 +25,7 @@ class DeprecationExtractor: def __init__(self, in_file: str, out_file: str) -> None: self._input_filename = in_file self._output_filename = out_file - self._messages = None # type: List[str] + self._messages: list[str] | None = None def extract_messages(self) -> bool: """ diff --git a/tools/generate_spell_dict.py b/tools/generate_spell_dict.py index e20d1eec2..fbc53a28d 100644 --- a/tools/generate_spell_dict.py +++ b/tools/generate_spell_dict.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2021, 2023. +# (C) Copyright IBM 2021, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -12,7 +12,6 @@ """ Generates spelling dictionaries for Sphinx and Pylint and combine them. """ -from typing import Set, List import sys import os import argparse @@ -46,10 +45,10 @@ def __init__(self, root_dir: str, out_file: str) -> None: self._jupyter_execute_dir = os.path.join( self._docs_dir, SpellDictGenerator._JUPYTER_EXECUTE_DIR ) - self._sphinx_words: Set[str] = set() - self._pylint_words: Set[str] = set() + self._sphinx_words: set[str] = set() + self._pylint_words: set[str] = set() - def generate_sphinx_spell_words(self) -> Set[str]: + def generate_sphinx_spell_words(self) -> set[str]: """ Generates Sphinx spelling dictionary @@ -89,7 +88,7 @@ def generate_sphinx_spell_words(self) -> Set[str]: shutil.rmtree(self._jupyter_execute_dir) @staticmethod - def _get_sphinx_spell_words(path: str) -> Set[str]: + def _get_sphinx_spell_words(path: str) -> set[str]: words = set() for item in os.listdir(path): fullpath = os.path.join(path, item) @@ -104,7 +103,7 @@ def _get_sphinx_spell_words(path: str) -> Set[str]: return words @staticmethod - def _extract_sphinx_spell_words(file_path: str) -> Set[str]: + def _extract_sphinx_spell_words(file_path: str) -> set[str]: words = set() with open(file_path, "rt", encoding="utf8") as file: for line in file: @@ -117,7 +116,7 @@ def _extract_sphinx_spell_words(file_path: str) -> Set[str]: words.add(word) return words - def generate_pylint_spell_words(self) -> Set[str]: + def generate_pylint_spell_words(self) -> set[str]: """ Generates Pylint spelling dictionary @@ -158,7 +157,7 @@ def generate_pylint_spell_words(self) -> Set[str]: self._pylint_words.update(words) return self._pylint_words - def merge_sort_dict_to_output(self) -> List[str]: + def merge_sort_dict_to_output(self) -> list[str]: """Merge and sort Sphinx and Pylint dicts""" word_set = set(w.lower() for w in self._sphinx_words) word_set.update(w.lower() for w in self._pylint_words)