From 5a05fe902f5bc9108a57f6201ffe2e57f7b3e594 Mon Sep 17 00:00:00 2001 From: Benoit Fanchon Date: Thu, 5 Sep 2024 18:49:08 +0200 Subject: [PATCH 1/4] PCVL-749 Remove python 3.8 --- .github/workflows/autotests.yml | 1 - README.md | 2 +- .../notebooks/Reinforcement_learning.ipynb | 3 +- docs/source/reference/statevector.rst | 4 +- perceval/algorithm/sampler.py | 15 +++--- perceval/backends/_naive_approx.py | 5 +- perceval/backends/_slos.py | 11 ++--- perceval/components/_mode_connector.py | 10 ++-- perceval/components/abstract_component.py | 10 ++-- perceval/components/abstract_processor.py | 33 +++++++------ perceval/components/component_catalog.py | 5 +- perceval/components/generic_interferometer.py | 10 ++-- perceval/components/linear_circuit.py | 47 +++++++++---------- perceval/components/port.py | 7 ++- perceval/components/processor.py | 13 +++-- perceval/components/source.py | 7 ++- .../components/tomography_exp_configurer.py | 5 +- perceval/error_mitigation/loss_mitigation.py | 5 +- .../scaleway/scaleway_rpc_handler.py | 5 +- perceval/rendering/canvas/canvas.py | 15 +++--- perceval/rendering/circuit/__init__.py | 4 +- perceval/rendering/circuit/abstract_skin.py | 9 ++-- perceval/rendering/circuit/renderer.py | 13 +++-- perceval/rendering/pdisplay.py | 3 +- perceval/runtime/_token_management.py | 9 ++-- perceval/runtime/job.py | 9 ++-- perceval/runtime/job_status.py | 9 ++-- perceval/runtime/local_job.py | 9 ++-- perceval/runtime/remote_job.py | 3 +- perceval/runtime/remote_processor.py | 9 ++-- .../serialization/_parameter_serialization.py | 4 +- perceval/serialization/deserialize.py | 5 +- perceval/simulators/_simulator_utils.py | 5 +- perceval/simulators/delay_simulator.py | 9 ++-- perceval/simulators/loss_simulator.py | 4 +- .../simulators/noisy_sampling_simulator.py | 15 +++--- perceval/simulators/simulator.py | 18 +++---- perceval/simulators/simulator_factory.py | 6 +-- perceval/simulators/simulator_interface.py | 5 +- perceval/simulators/stepper.py | 7 ++- .../utils/algorithms/circuit_optimizer.py | 6 +-- perceval/utils/algorithms/optimize.py | 6 +-- perceval/utils/algorithms/simplification.py | 4 +- perceval/utils/density_matrix.py | 25 +++++----- perceval/utils/density_matrix_utils.py | 10 ++-- perceval/utils/logical_state.py | 6 +-- perceval/utils/matrix.py | 8 ++-- perceval/utils/noise_model.py | 3 +- perceval/utils/parameter.py | 4 +- perceval/utils/persistent_data.py | 5 +- perceval/utils/polarization.py | 7 ++- perceval/utils/postselect.py | 11 ++--- perceval/utils/stategenerator.py | 3 +- perceval/utils/statevector.py | 13 +++-- setup.py | 3 +- tests/_test_utils.py | 3 +- tests/test_circuit_optimizer.py | 2 +- 57 files changed, 214 insertions(+), 273 deletions(-) diff --git a/.github/workflows/autotests.yml b/.github/workflows/autotests.yml index 014798d5d..27091c001 100644 --- a/.github/workflows/autotests.yml +++ b/.github/workflows/autotests.yml @@ -21,7 +21,6 @@ on: default: '3.10' type: choice options: - - '3.8' - '3.9' - '3.10' - '3.11' diff --git a/README.md b/README.md index 237ea5e46..c88312f8b 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ practitioners. Perceval requires: -* Above Python 3.8 and below Python 3.12 +* Above Python 3.9 and below Python 3.12 ## PIP We recommend installing it with `pip`: diff --git a/docs/source/notebooks/Reinforcement_learning.ipynb b/docs/source/notebooks/Reinforcement_learning.ipynb index 69ff2c3ed..57adcd99a 100644 --- a/docs/source/notebooks/Reinforcement_learning.ipynb +++ b/docs/source/notebooks/Reinforcement_learning.ipynb @@ -57,7 +57,6 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Union\n", "import math\n", "\n", "from ipywidgets import FloatProgress\n", @@ -310,7 +309,7 @@ "metadata": {}, "outputs": [], "source": [ - "def mzi(name:str, theta:Union[float, pcvl.Parameter], phi:Union[float, pcvl.Parameter],theta_2:Union[float, pcvl.Parameter]) -> pcvl.Circuit:\n", + "def mzi(name:str, theta:float | pcvl.Parameter, phi:float | pcvl.Parameter, theta_2:float | pcvl.Parameter) -> pcvl.Circuit:\n", " # For the mzi to be in the right shape:\n", " # theta_2 should be set to '- pi/2 - theta/2'\n", " # however we cannot pass a symbolic expression to the input of PS\n", diff --git a/docs/source/reference/statevector.rst b/docs/source/reference/statevector.rst index c50bdc70d..b6b5d89cd 100644 --- a/docs/source/reference/statevector.rst +++ b/docs/source/reference/statevector.rst @@ -8,7 +8,7 @@ StateVector class reference superposed state represented as a (complex) linear combination of :code:`BasicState` objects (its components), the complex coefficients being probability amplitudes. -* **Constructor** :code:`__init__(bs: BasicState or List[int] or str = None)` +* **Constructor** :code:`__init__(bs: BasicState or list[int] or str = None)` Initialize a StateVector from a BasicState or data to create a BasicState (list of integers, string representation) @@ -105,7 +105,7 @@ components and amplitudes. >>> print(sv.samples(10)) [|1,0>, |1,0>, |1,0>, |1,0>, |1,0>, |1,0>, |2,2>, |1,0>, |1,0>, |2,2>] -* **Method** :code:`measure(modes: List[int])` +* **Method** :code:`measure(modes: list[int])` Perform a measure on one or multiple modes and collapse the remaining :code:`StateVector`. The resulting states are not normalised by default. diff --git a/perceval/algorithm/sampler.py b/perceval/algorithm/sampler.py index 1bc5fd1dc..94913f4ba 100644 --- a/perceval/algorithm/sampler.py +++ b/perceval/algorithm/sampler.py @@ -26,7 +26,6 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from typing import Callable, List, Dict from numbers import Number from .abstract_algorithm import AAlgorithm @@ -140,7 +139,7 @@ def probs(self) -> Job: return self._create_job("probs") # Iterator construction methods - def _add_iteration(self, circuit_params: Dict = None, + def _add_iteration(self, circuit_params: dict = None, input_state: BasicState = None, min_detected_photons: int = None): it = {} @@ -170,13 +169,13 @@ def _check_iteration(self, iter_params): f"Iteration: input state and processor size mismatch (processor size is {self._processor.m})" self._processor.check_input(iter_params['input_state']) - def add_iteration(self, circuit_params: Dict = None, + def add_iteration(self, circuit_params: dict = None, input_state: BasicState = None, min_detected_photons: int = None): logger.info("Add 1 iteration to Sampler", channel.general) self._add_iteration(circuit_params, input_state, min_detected_photons) - def add_iteration_list(self, iterations: List[Dict]): + def add_iteration_list(self, iterations: list[dict]): logger.info(f"Add {len(iterations)} iterations to Sampler", channel.general) for iter_params in iterations: self._add_iteration(**iter_params) @@ -190,13 +189,13 @@ def clear_iterations(self): def n_iterations(self): return len(self._iterator) - def _probs_wrapper(self, progress_callback: Callable = None): + def _probs_wrapper(self, progress_callback: callable = None): # max_shots is used as the invert of the precision set in the probs computation # Rationale: mimic the fact that the more shots, the more accurate probability distributions are. precision = None if self._max_shots is None else min(1e-6, 1/self._max_shots) return self._processor.probs(precision, progress_callback) - def _samples_wrapper(self, max_samples: int = None, progress_callback: Callable = None): + def _samples_wrapper(self, max_samples: int = None, progress_callback: callable = None): if max_samples is None and self._max_shots is None: raise RuntimeError("Local sampling simumation requires max_samples and/or max_shots parameters") if max_samples is None: @@ -205,7 +204,7 @@ def _samples_wrapper(self, max_samples: int = None, progress_callback: Callable # Local iteration methods mimic remote iterations for interchangeability purpose - def _probs_iterate_locally(self, max_shots: int = None, progress_callback: Callable = None): + def _probs_iterate_locally(self, max_shots: int = None, progress_callback: callable = None): precision = None if max_shots is None else min(1e-6, 1 / max_shots) results = {'results_list':[]} for idx, it in enumerate(self._iterator): @@ -216,7 +215,7 @@ def _probs_iterate_locally(self, max_shots: int = None, progress_callback: Calla progress_callback((idx+1)/len(self._iterator)) return results - def _samples_iterate_locally(self, max_shots: int = None, max_samples: int = None, progress_callback: Callable = None): + def _samples_iterate_locally(self, max_shots: int = None, max_samples: int = None, progress_callback: callable = None): if max_samples is None and max_shots is None: raise RuntimeError("Local sampling simumation requires max_samples and/or max_shots parameters") if max_samples is None: diff --git a/perceval/backends/_naive_approx.py b/perceval/backends/_naive_approx.py index 377aca2d6..5b82ee9bf 100644 --- a/perceval/backends/_naive_approx.py +++ b/perceval/backends/_naive_approx.py @@ -28,7 +28,6 @@ # SOFTWARE. import math -from typing import List, Tuple import exqalibur as xq from . import NaiveBackend @@ -49,14 +48,14 @@ def _compute_permanent(self, M): permanent_with_error = xq.estimate_permanent_cx(M, self._gurvits_iterations, 0) return permanent_with_error[0] - def prob_amplitude_with_error(self, output_state: BasicState) -> Tuple[complex, float]: + def prob_amplitude_with_error(self, output_state: BasicState) -> tuple[complex, float]: M = self._compute_submatrix(output_state) permanent_with_error = xq.estimate_permanent_cx(M, self._gurvits_iterations, 0) normalization_coeff = math.sqrt(output_state.prodnfact() * self._input_state.prodnfact()) return (permanent_with_error[0]/normalization_coeff, permanent_with_error[1]/normalization_coeff) \ if M.size > 1 else (M[0, 0], 0) - def probability_confidence_interval(self, output_state: BasicState) -> List[float]: + def probability_confidence_interval(self, output_state: BasicState) -> list[float]: mean, err = self.prob_amplitude_with_error(output_state) min_prob = max((abs(mean) - err) ** 2, 0) max_prob = min((abs(mean) + err) ** 2, 1) diff --git a/perceval/backends/_slos.py b/perceval/backends/_slos.py index a66f83e24..6f57ca204 100644 --- a/perceval/backends/_slos.py +++ b/perceval/backends/_slos.py @@ -34,7 +34,6 @@ import exqalibur as xq import math import numpy as np -from typing import Dict, List class _Path: @@ -121,9 +120,9 @@ def name(self) -> str: def _reset(self): self._fsms = [[]] # xq.FSMask self._fsas = {} # xq.FSArray - self._mk_l: List[int] = [1] - self._path_roots: List[_Path] = [] - self._state_mapping: Dict[BasicState, _Path] = {} + self._mk_l: list[int] = [1] + self._path_roots: list[_Path] = [] + self._state_mapping: dict[BasicState, _Path] = {} self._mask = None # xq.FSMAsk self.clear_iterator_cache() @@ -151,7 +150,7 @@ def set_input_state(self, input_state: BasicState): self.preprocess([input_state]) super().set_input_state(input_state) - def _deploy(self, input_list: List[BasicState]): + def _deploy(self, input_list: list[BasicState]): # allocate the fsas and fsms for covering all the input_states respecting possible mask # after calculation, we only need to keep fsa for input_state n # during calculation we need to keep current fsa and previous fsa @@ -170,7 +169,7 @@ def _deploy(self, input_list: List[BasicState]): if n not in self._fsas: self._fsas[n] = current_fsa - def preprocess(self, input_list: List[BasicState]) -> bool: + def preprocess(self, input_list: list[BasicState]) -> bool: # now check if we have a path for the input states found_new = False for input_state in input_list: diff --git a/perceval/components/_mode_connector.py b/perceval/components/_mode_connector.py index 0c95a48a0..612ab8f8b 100644 --- a/perceval/components/_mode_connector.py +++ b/perceval/components/_mode_connector.py @@ -26,8 +26,6 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from typing import Dict, List, Union - from perceval.utils.logging import logger, channel from .abstract_component import AComponent @@ -35,7 +33,7 @@ class UnavailableModeException(Exception): - def __init__(self, mode: Union[int, List[int]], reason: str = None): + def __init__(self, mode: int | list[int], reason: str = None): because = '' if reason: because = f' because: {reason}' @@ -43,7 +41,7 @@ def __init__(self, mode: Union[int, List[int]], reason: str = None): class InvalidMappingException(Exception): - def __init__(self, mapping: Union[Dict, List], reason: str = None): + def __init__(self, mapping: dict | list, reason: str = None): because = '' if reason: because = f' because: {reason}' @@ -91,7 +89,7 @@ def _get_ordered_rmodes(self): r_list = list(range(self._ro.circuit_size)) return [x for x in r_list if x not in self._ro.heralds.keys()] - def resolve(self) -> Dict[int, int]: + def resolve(self) -> dict[int, int]: """ Resolves mode mapping (self._map) and checks if it is consistent. @@ -226,7 +224,7 @@ def add_heralded_modes(self, mapping): return new_mode_index - self._lp.circuit_size @staticmethod - def generate_permutation(mode_mapping: Dict[int, int]): + def generate_permutation(mode_mapping: dict[int, int]): """ Generate a PERM component given an already resolved mode mapping Returns a tuple containing: diff --git a/perceval/components/abstract_component.py b/perceval/components/abstract_component.py index e7d6ac6ac..48254db4f 100644 --- a/perceval/components/abstract_component.py +++ b/perceval/components/abstract_component.py @@ -28,7 +28,7 @@ # SOFTWARE. from abc import ABC -from typing import Dict, Union, List, Iterable +from collections.abc import Iterable import sympy as sp import copy @@ -72,7 +72,7 @@ def __init__(self, m: int, name: str = None): self._vars = {} @property - def vars(self) -> Dict[str, Parameter]: + def vars(self) -> dict[str, Parameter]: return {p.name: p for p in self._params.values() if not p.fixed} def assign(self, @@ -103,7 +103,7 @@ def param(self, param_name: str) -> Parameter: """Returns a `Parameter` object from its name""" return self._params[param_name] - def get_parameters(self, all_params: bool = False) -> List[Parameter]: + def get_parameters(self, all_params: bool = False) -> list[Parameter]: """Return the parameters of the circuit :param all_params: if False, only returns the variable parameters @@ -117,7 +117,7 @@ def reset_parameters(self) -> None: def _set_parameter(self, name: str, - p: Union[Parameter, float], + p: Parameter | float, min_v: float, max_v: float, periodic: bool = True) -> Parameter: @@ -174,7 +174,7 @@ def _populate_parameters(self, out_parameters: dict, pname: str, default_value: def get_variables(self): return {} - def copy(self, subs: Union[dict, list] = None): + def copy(self, subs: dict | list = None): nc = copy.deepcopy(self) if subs is None: diff --git a/perceval/components/abstract_processor.py b/perceval/components/abstract_processor.py index dc765197b..f7c854b5f 100644 --- a/perceval/components/abstract_processor.py +++ b/perceval/components/abstract_processor.py @@ -31,7 +31,6 @@ from abc import ABC, abstractmethod from enum import Enum from multipledispatch import dispatch -from typing import Any, Dict, List, Union, Tuple from perceval.components.linear_circuit import Circuit, ACircuit from perceval.utils import BasicState, Parameter, PostSelect, postselect_independent, LogicalState, NoiseModel @@ -53,26 +52,26 @@ class AProcessor(ABC): def __init__(self): self._input_state = None self.name: str = "" - self._parameters: Dict[str, Any] = {} + self._parameters: dict[str, any] = {} - self._noise: Union[NoiseModel, None] = None + self._noise: NoiseModel | None = None self._thresholded_output: bool = False - self._min_detected_photons: Union[int, None] = None + self._min_detected_photons: int | None = None self._reset_circuit() def _reset_circuit(self): - self._in_ports: Dict = {} - self._out_ports: Dict = {} - self._postselect: Union[PostSelect, None] = None + self._in_ports: dict = {} + self._out_ports: dict = {} + self._postselect: PostSelect | None = None self._is_unitary: bool = True self._has_td: bool = False self._n_heralds: int = 0 self._anon_herald_num: int = 0 # This is not a herald count! - self._components: List[Tuple[int, AComponent]] = [] # Any type of components, not only unitary ones + self._components: list[tuple[int, AComponent]] = [] # Any type of components, not only unitary ones self._n_moi = None # Number of modes of interest (moi) @@ -90,11 +89,11 @@ def is_remote(self) -> bool: def specs(self): return dict() - def set_parameters(self, params: Dict[str, Any]): + def set_parameters(self, params: dict[str, any]): for key, value in params.items(): self.set_parameter(key, value) - def set_parameter(self, key: str, value: Any): + def set_parameter(self, key: str, value: any): if not isinstance(key, str): raise TypeError(f"A parameter name has to be a string (got {type(key)})") self._parameters[key] = value @@ -147,7 +146,7 @@ def noise(self, nm: NoiseModel): @property @abstractmethod - def available_commands(self) -> List[str]: + def available_commands(self) -> list[str]: pass def postprocess_output(self, s: BasicState, keep_herald: bool = False) -> BasicState: @@ -190,7 +189,7 @@ def _state_selected(self, state: BasicState) -> bool: return self._postselect(state) return True - def copy(self, subs: Union[dict, list] = None): + def copy(self, subs: dict | list = None): logger.debug(f"Copy processor {self.name}", channel.general) new_proc = copy.copy(self) new_proc._components = [] @@ -253,7 +252,7 @@ def add(self, mode_mapping, component, keep_port=True): self._circuit_changed() return self - def _validate_postselect_composition(self, mode_mapping: Dict): + def _validate_postselect_composition(self, mode_mapping: dict): if self._postselect is not None and isinstance(self._postselect, PostSelect): impacted_modes = list(mode_mapping.keys()) # can_compose_with can take a bit of time so leave this test as an assert which can be removed by -O @@ -403,7 +402,7 @@ def linear_circuit(self, flatten: bool = False) -> Circuit: circuit.add(pos_m, component, merge=flatten) return circuit - def non_unitary_circuit(self, flatten: bool = False) -> List: + def non_unitary_circuit(self, flatten: bool = False) -> list: if self._has_td: # Inherited from the parent processor in this case return self.components @@ -436,7 +435,7 @@ def non_unitary_circuit(self, flatten: bool = False) -> List: return new_comp - def get_circuit_parameters(self) -> Dict[str, Parameter]: + def get_circuit_parameters(self) -> dict[str, Parameter]: return {p.name: p for _, c in self._components for p in c.get_parameters()} @property @@ -596,14 +595,14 @@ def with_input(self, input_state: BasicState) -> None: if self._min_detected_photons is None: self._deduce_min_detected_photons(expected_photons) - def flatten(self) -> List: + def flatten(self) -> list: """ :return: a component list where recursive circuits have been flattened """ return _flatten(self) -def _flatten(composite, starting_mode=0) -> List: +def _flatten(composite, starting_mode=0) -> list: component_list = [] for m_range, comp in composite._components: if isinstance(comp, Circuit): diff --git a/perceval/components/component_catalog.py b/perceval/components/component_catalog.py index 67d07c005..475d1b0a2 100644 --- a/perceval/components/component_catalog.py +++ b/perceval/components/component_catalog.py @@ -30,7 +30,6 @@ import importlib from abc import ABC, abstractmethod from enum import Enum -from typing import List from perceval.utils import Parameter from perceval.components import Processor, Circuit @@ -132,7 +131,7 @@ def build_processor(self, **kwargs) -> Processor: """Build the component as processor kwargs: - * backend(Union[ABackend, str]): Name or instance of a simulation backend. Default "SLOS" + * backend(ABackend | str): Name or instance of a simulation backend. Default "SLOS" :return: A Perceval processor """ @@ -159,7 +158,7 @@ def _add_sub_catalog(self, catalog): if isinstance(obj, CatalogItem): self._items[obj.name] = obj - def list(self) -> List[str]: + def list(self) -> list[str]: return list(self._items.keys()) def __contains__(self, item: str) -> bool: diff --git a/perceval/components/generic_interferometer.py b/perceval/components/generic_interferometer.py index c07d9ecca..3550e8884 100644 --- a/perceval/components/generic_interferometer.py +++ b/perceval/components/generic_interferometer.py @@ -28,7 +28,7 @@ # SOFTWARE. import math -from typing import Callable, List, Optional, Tuple +from collections.abc import Callable from .linear_circuit import ACircuit, Circuit @@ -57,7 +57,7 @@ def __init__(self, fun_gen: Callable[[int], ACircuit], shape: InterferometerShape = InterferometerShape.RECTANGLE, depth: int = None, - phase_shifter_fun_gen: Optional[Callable[[int], ACircuit]] = None, + phase_shifter_fun_gen: Callable[[int], ACircuit] | None = None, phase_at_output: bool = False): assert isinstance(shape, InterferometerShape),\ f"Wrong type for shape, expected InterferometerShape, got {type(shape)}" @@ -89,7 +89,7 @@ def __repr__(self): return f"Generic interferometer ({self.m} modes, {str(self._shape.name)}, {self.ncomponents()} components)" @property - def mzi_depths(self) -> List[int]: + def mzi_depths(self) -> list[int]: """Return a list of MZI depth, per mode""" return self._depth_per_mode @@ -156,7 +156,7 @@ def _compute_insertion_depth(start_col: int, m: int, param_count: int) -> int: cc += 1 return depth - def set_param_list(self, param_list: List[float], top_left_pos: Tuple[int, int], m: int): + def set_param_list(self, param_list: list[float], top_left_pos: tuple[int, int], m: int): """Insert parameters value starting from a given position in the interferometer. This method is designed to work on rectangular interferometers @@ -191,7 +191,7 @@ def set_param_list(self, param_list: List[float], top_left_pos: Tuple[int, int], break cc += 1 - def set_params_from_other(self, other: Circuit, top_left_pos: Tuple[int, int]): + def set_params_from_other(self, other: Circuit, top_left_pos: tuple[int, int]): """Retrieve parameter value from another interferometer :param other: Another interferometer diff --git a/perceval/components/linear_circuit.py b/perceval/components/linear_circuit.py index 868e8b67f..5081f31a6 100644 --- a/perceval/components/linear_circuit.py +++ b/perceval/components/linear_circuit.py @@ -33,7 +33,6 @@ import random from abc import ABC, abstractmethod -from typing import Callable, Optional, Union, Tuple, Type, List import numpy as np import sympy as sp @@ -72,7 +71,7 @@ def _compute_unitary(self, def compute_unitary(self, assign: dict = None, use_symbolic: bool = False, - use_polarization: Optional[bool] = None) -> Matrix: + use_polarization: bool | None = None) -> Matrix: """Compute the unitary matrix corresponding to the current circuit :param use_polarization: @@ -108,14 +107,14 @@ def definition(self): params = {name: Parameter(name) for name in self._params.keys()} return type(self)(**params).U - def add(self, port_range: Union[int, Tuple[int, ...]], + def add(self, port_range: int | tuple[int, ...], component: ACircuit, merge: bool = None) -> Circuit: return Circuit(self._m).add(0, self).add(port_range, component, merge) def __setitem__(self, key, value): self._params[key] = value - def __ifloordiv__(self, component: Union[ACircuit, Tuple[int, ACircuit]]) -> Circuit: + def __ifloordiv__(self, component: ACircuit | tuple[int, ACircuit]) -> Circuit: r"""Shortcut for ``.add`` >>> c //= b # equivalent to: `c.add((0:b.n),b)` @@ -132,7 +131,7 @@ def __ifloordiv__(self, component: Union[ACircuit, Tuple[int, ACircuit]]) -> Cir pos = 0 return self.add(tuple(range(pos, component._m+pos)), component, merge=True) - def __floordiv__(self, component: Union[ACircuit, Tuple[int, ACircuit]]) -> Circuit: + def __floordiv__(self, component: ACircuit | tuple[int, ACircuit]) -> Circuit: r"""Build a new circuit by adding `component` to the current circuit >>> c = a // b # equivalent to: `Circuit(n) // self // component` @@ -189,8 +188,8 @@ def identify(self, unitary_matrix, phases, precision=None, max_try=10, allow_err return None @staticmethod - def _match_unitary(circuit: Union[ACircuit, Matrix], pattern: ACircuit, match: Match = None, - actual_pos: Optional[int] = 0, actual_pattern_pos: Optional[int] = 0) -> Optional[Match]: + def _match_unitary(circuit: ACircuit | Matrix, pattern: ACircuit, match: Match = None, + actual_pos: int | None = 0, actual_pattern_pos: int | None = 0) -> Match | None: r"""match an elementary component by finding if possible the corresponding parameters. :param pattern: the circuit to match @@ -241,7 +240,7 @@ def g(*params): return None def match(self, pattern: ACircuit, pos: int = None, - pattern_pos: int = None, match: Match = None, actual_pos = 0, actual_pattern_pos=0) -> Optional[Match]: + pattern_pos: int = None, match: Match = None, actual_pos = 0, actual_pattern_pos=0) -> Match | None: # the component shape should match if pattern.name == "CPLX" or self._m != pattern._m or pos is not None or pattern_pos is not None: return None @@ -302,7 +301,7 @@ def __iter__(self): for range_comp, comp in c: yield tuple(pos + r[0] for pos in range_comp), comp - def getitem(self, idx: Tuple[int, int], only_parameterized: bool=False) -> ACircuit: + def getitem(self, idx: tuple[int, int], only_parameterized: bool=False) -> ACircuit: """ Direct access to components of the circuit :param idx: index of the component as (row, col) @@ -376,7 +375,7 @@ def _check_x_grid_consistency(self): raise ValueError("x_grid values are not consistent") x_grids[port] = component._x_grid - def group_components_by_xgrid(self) -> List[List[Tuple[int], ACircuit]]: + def group_components_by_xgrid(self) -> list[list[tuple[int], ACircuit]]: r"""Group the components according to their x_grid to facilitate rendering Grouping rule is simple: components without x_grid are singleton, components with similar x_grid are grouped @@ -437,7 +436,7 @@ def barrier(self): from perceval.components.unitary_components import Barrier return self.add(0, Barrier(self._m)) - def __imatmul__(self, component: Union[ACircuit, Tuple[int, ACircuit]]) -> Circuit: + def __imatmul__(self, component: ACircuit | tuple[int, ACircuit]) -> Circuit: r"""Add a barrier and a `component` to the current circuit :param component: the component to add, or a tuple (first_port, component) @@ -446,7 +445,7 @@ def __imatmul__(self, component: Union[ACircuit, Tuple[int, ACircuit]]) -> Circu self //= component return self - def __matmul__(self, component: Union[ACircuit, Tuple[int, ACircuit]]) -> Circuit: + def __matmul__(self, component: ACircuit | tuple[int, ACircuit]) -> Circuit: r"""Build a new circuit by adding a barrier and then `component` to the current circuit @@ -456,7 +455,7 @@ def __matmul__(self, component: Union[ACircuit, Tuple[int, ACircuit]]) -> Circui c @= component return c - def add(self, port_range: Union[int, Tuple[int, ...]], + def add(self, port_range: int | tuple[int, ...], component: ACircuit, merge: bool = False, x_grid: int = None) -> Circuit: r"""Add a component in a circuit @@ -560,7 +559,7 @@ def inverse(self, v=False, h=False): def compute_unitary(self, use_symbolic: bool = False, assign: dict = None, - use_polarization: Optional[bool] = None) -> Matrix: + use_polarization: bool | None = None) -> Matrix: r"""Compute the unitary matrix corresponding to the circuit :param assign: @@ -581,10 +580,10 @@ def compute_unitary(self, @staticmethod @deprecated(version="0.10.0", reason="Construct a GenericInterferometer object instead") def generic_interferometer(m: int, - fun_gen: Callable[[int], ACircuit], - shape: Union[str, InterferometerShape] = InterferometerShape.RECTANGLE, + fun_gen: callable[[int], ACircuit], + shape: str | InterferometerShape = InterferometerShape.RECTANGLE, depth: int = None, - phase_shifter_fun_gen: Optional[Callable[[int], ACircuit]] = None, + phase_shifter_fun_gen: callable[[int], ACircuit] | None = None, phase_at_output: bool = False) -> Circuit: from .generic_interferometer import GenericInterferometer # Import in method to avoir circular dependency if isinstance(shape, str): @@ -594,7 +593,7 @@ def generic_interferometer(m: int, raise ValueError(f"Unknown interferometer shape: {shape}") return GenericInterferometer(m, fun_gen, shape, depth, phase_shifter_fun_gen, phase_at_output) - def copy(self, subs: Union[dict,list] = None): + def copy(self, subs: dict | list = None): nc = copy.deepcopy(self) nc._params = {} nc._components = [] @@ -605,9 +604,9 @@ def copy(self, subs: Union[dict,list] = None): @staticmethod def decomposition(U: MatrixN, component: ACircuit, - phase_shifter_fn: Callable[[int], ACircuit] = None, - shape: Union[str, InterferometerShape] = InterferometerShape.TRIANGLE, - permutation: Type[ACircuit] = None, + phase_shifter_fn: callable[[int], ACircuit] = None, + shape: str | InterferometerShape = InterferometerShape.TRIANGLE, + permutation: type[ACircuit] = None, inverse_v: bool = False, inverse_h: bool = False, constraints=None, @@ -715,7 +714,7 @@ def transfer_from(self, source: ACircuit, force: bool = False): assert r_self[-1] < r[0] or r_self[0] > r[-1], \ "circuit structure does not match - missing %s at %s" % (str(c), str(r)) - def find_subnodes(self, pos: int) -> List[int]: + def find_subnodes(self, pos: int) -> list[int]: r"""find the subnodes of a given component (Udef for pos==None) :param pos: the position of the current node @@ -738,7 +737,7 @@ def find_subnodes(self, pos: int) -> List[int]: subnodes.append(found and (p, idx) or None) return subnodes - def isolate(self, lc: List[int], name=None, color=None): + def isolate(self, lc: list[int], name=None, color=None): nlc = [] rset = set() for idx in lc: @@ -788,7 +787,7 @@ def _check_brother_node(self, p0, p1): def match(self, pattern: ACircuit, pos: int = None, pattern_pos: int = 0, browse: bool = False, match: Match = None, - actual_pos: int = None, actual_pattern_pos: int = None, reverse: bool = False) -> Optional[Match]: + actual_pos: int = None, actual_pattern_pos: int = None, reverse: bool = False) -> Match | None: r"""match a sub-circuit at a given position :param match: the partial match diff --git a/perceval/components/port.py b/perceval/components/port.py index 1543b3cca..96422dc9d 100644 --- a/perceval/components/port.py +++ b/perceval/components/port.py @@ -28,7 +28,6 @@ # SOFTWARE. from abc import ABC, abstractmethod -from typing import List, Union from enum import Enum from perceval.utils import BasicState, Encoding, LogicalState @@ -96,7 +95,7 @@ def expected(self): return self._value -def get_basic_state_from_ports(ports: List[APort], state: LogicalState, add_herald_and_ancillary: bool = False) -> BasicState: +def get_basic_state_from_ports(ports: list[APort], state: LogicalState, add_herald_and_ancillary: bool = False) -> BasicState: """Convert a LogicalState to a BasicState by taking in account a port list :param ports: port list. @@ -114,7 +113,7 @@ def get_basic_state_from_ports(ports: List[APort], state: LogicalState, add_hera return get_basic_state_from_encoding(encodings, state) -def _to_fock(encoding: Encoding, qubit_state: List[int]) -> List[int]: +def _to_fock(encoding: Encoding, qubit_state: list[int]) -> list[int]: """Return the equivalent BasicState from the qubit state, as a list of integers :param encoding: a qubit encoding @@ -140,7 +139,7 @@ def _to_fock(encoding: Encoding, qubit_state: List[int]) -> List[int]: raise NotImplementedError -def get_basic_state_from_encoding(encoding: List[Union[Encoding, int]], logical: LogicalState) -> BasicState: +def get_basic_state_from_encoding(encoding: list[Encoding | int], logical: LogicalState) -> BasicState: fock = [] i = 0 for e in encoding: diff --git a/perceval/components/processor.py b/perceval/components/processor.py index c266b2190..b7dfddaf5 100644 --- a/perceval/components/processor.py +++ b/perceval/components/processor.py @@ -29,7 +29,6 @@ from multipledispatch import dispatch from numpy import inf -from typing import Dict, Callable, Union, List from perceval.backends import ABackend, ASamplingBackend, BACKEND_LIST from perceval.utils import SVDistribution, BSDistribution, BasicState, StateVector, LogicalState, NoiseModel @@ -60,14 +59,14 @@ class Processor(AProcessor): Note: source and noise are mutually exclusive :param name: a textual name for the processor (defaults to "Local processor") """ - def __init__(self, backend: Union[ABackend, str], m_circuit: Union[int, ACircuit] = None, source: Source = None, + def __init__(self, backend: ABackend | str, m_circuit: int | ACircuit = None, source: Source = None, noise: NoiseModel = None, name: str = "Local processor"): super().__init__() self._init_backend(backend) self._init_circuit(m_circuit) self._init_noise(noise, source) self.name = name - self._inputs_map: Union[SVDistribution, None] = None + self._inputs_map: SVDistribution | None = None self._simulator = None def _init_noise(self, noise: NoiseModel, source: Source): @@ -97,7 +96,7 @@ def noise(self, nm): self._generate_noisy_input() @property - def source_distribution(self) -> Union[SVDistribution, None]: + def source_distribution(self) -> SVDistribution | None: r""" Retrieve the computed input distribution. :return: the input SVDistribution if `with_input` was called previously, otherwise None. @@ -259,7 +258,7 @@ def linear_circuit(self, flatten: bool = False) -> Circuit: force=True) return circuit - def samples(self, max_samples: int, max_shots: int = None, progress_callback=None) -> Dict: + def samples(self, max_samples: int, max_shots: int = None, progress_callback=None) -> dict: from perceval.simulators import NoisySamplingSimulator assert isinstance(self.backend, ASamplingBackend), "A sampling backend is required to call samples method" sampling_simulator = NoisySamplingSimulator(self.backend) @@ -272,7 +271,7 @@ def samples(self, max_samples: int, max_shots: int = None, progress_callback=Non logger.info("Local sampling complete!", channel.general) return res - def probs(self, precision: float = None, progress_callback: Callable = None) -> Dict: + def probs(self, precision: float = None, progress_callback: callable = None) -> dict: # assert self._inputs_map is not None, "Input is missing, please call with_inputs()" if self._simulator is None: from perceval.simulators import SimulatorFactory # Avoids a circular import @@ -300,5 +299,5 @@ def probs(self, precision: float = None, progress_callback: Callable = None) -> return res @property - def available_commands(self) -> List[str]: + def available_commands(self) -> list[str]: return ["samples" if isinstance(self.backend, ASamplingBackend) else "probs"] diff --git a/perceval/components/source.py b/perceval/components/source.py index 37cc34002..f149594b2 100644 --- a/perceval/components/source.py +++ b/perceval/components/source.py @@ -31,7 +31,6 @@ from perceval.utils import SVDistribution, StateVector, BasicState, anonymize_annotations, NoiseModel, global_params from perceval.utils.logging import logger, channel -from typing import Dict, List, Union class Source: @@ -56,7 +55,7 @@ def __init__(self, indistinguishability: float = 1, losses: float = 0, multiphoton_model: str = "distinguishable", # Literal["distinguishable", "indistinguishable"] - context: Dict = None) -> None: + context: dict = None) -> None: assert 0 < emission_probability <= 1, "emission_probability must be in ]0;1]" assert 0 <= losses <= 1, "losses must be in [0;1]" @@ -111,7 +110,7 @@ def _get_probs(self): return p1to1, p2to1, p2to2 @staticmethod - def _merge_photon_distributions(d1: List, d2: List): + def _merge_photon_distributions(d1: list, d2: list): # Merges two lists of annotations (or unannotated photon count) following the tensor product rules if len(d1) == 0: return d2 @@ -128,7 +127,7 @@ def _merge_photon_distributions(d1: List, d2: List): return res @staticmethod - def _add(plist: List, annotations: Union[int, List], probability: float): + def _add(plist: list, annotations: int | list, probability: float): # Add an annotation list (or a number of unannotated photons) and its probability to the in/out # parameter `plist` if probability > 0: diff --git a/perceval/components/tomography_exp_configurer.py b/perceval/components/tomography_exp_configurer.py index 7a55170f9..04ca9b983 100644 --- a/perceval/components/tomography_exp_configurer.py +++ b/perceval/components/tomography_exp_configurer.py @@ -27,12 +27,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from typing import List from .processor import AProcessor from ._pauli import PauliType, PauliEigenStateType, get_pauli_eigen_state_prep_circ, get_pauli_basis_measurement_circuit -def _prep_state_circuit_preparer(prep_state_indices: List): +def _prep_state_circuit_preparer(prep_state_indices: list): """ Generates a layer of state preparation circuits (essentially 1-qubit pauli gates) for each qubit. The logical qubit state prepared will be one of the list: |0>,|1>,|+>,|+i> using Pauli Gates. @@ -42,7 +41,7 @@ def _prep_state_circuit_preparer(prep_state_indices: List): yield i * 2, get_pauli_eigen_state_prep_circ(pauli_type) -def _meas_state_circuit_preparer(pauli_indices: List): +def _meas_state_circuit_preparer(pauli_indices: list): """ Generates a layer of state measurement circuits (essentially measuring eigenstates of one of the pauli gates) for each qubit. diff --git a/perceval/error_mitigation/loss_mitigation.py b/perceval/error_mitigation/loss_mitigation.py index c26b6f7dd..316bd028c 100644 --- a/perceval/error_mitigation/loss_mitigation.py +++ b/perceval/error_mitigation/loss_mitigation.py @@ -30,13 +30,12 @@ import numpy as np from math import comb from scipy.optimize import curve_fit -from typing import Union from perceval.utils import BSCount, BSDistribution, BasicState from perceval.utils.logging import logger, channel from ._loss_mitigation_utils import _gen_lossy_dists, _get_avg_exp_from_uni_dist, _generate_one_photon_per_mode_mapping -def _validate_noisy_input(noisy_input: Union[BSCount, BSDistribution], ideal_photon_count: int): +def _validate_noisy_input(noisy_input: BSCount | BSDistribution, ideal_photon_count: int): if not isinstance(noisy_input, (BSCount, BSDistribution)): # check if the input type is correct raise TypeError('Noisy input should be of type BSCount or BSDistribution') @@ -53,7 +52,7 @@ def _validate_noisy_input(noisy_input: Union[BSCount, BSDistribution], ideal_pho "implementation requires states with n-1, n-2 photons for expected n-photon mitigation") -def photon_recycling(noisy_input: Union[BSCount, BSDistribution], ideal_photon_count: int) -> BSDistribution: +def photon_recycling(noisy_input: BSCount | BSDistribution, ideal_photon_count: int) -> BSDistribution: """ A classical technique to mitigate errors in the output distribution caused by photon loss in LO quantum circuits (ref: https://arxiv.org/abs/2405.02278) diff --git a/perceval/providers/scaleway/scaleway_rpc_handler.py b/perceval/providers/scaleway/scaleway_rpc_handler.py index 9aa6261ce..b0393fe9e 100644 --- a/perceval/providers/scaleway/scaleway_rpc_handler.py +++ b/perceval/providers/scaleway/scaleway_rpc_handler.py @@ -33,7 +33,6 @@ from datetime import datetime, timedelta from requests import HTTPError -from typing import Union from enum import Enum _PROVIDER_NAME = "quandela" @@ -189,7 +188,7 @@ def get_job_results(self, job_id: str) -> dict: def __build_endpoint(self, endpoint) -> str: return f"{self._url}{endpoint}" - def __to_date(self, date: Union[str, None]) -> Union[float, None]: + def __to_date(self, date: str | None) -> float | None: if not date or date == "": return None @@ -199,7 +198,7 @@ def __to_date(self, date: Union[str, None]) -> Union[float, None]: return datetime.fromisoformat(date).timestamp() - def __get_duration(self, start_time: Union[float, None]) -> Union[int, None]: + def __get_duration(self, start_time: float | None) -> int | None: return ( timedelta(seconds=time.time() - start_time).seconds if start_time else None ) diff --git a/perceval/rendering/canvas/canvas.py b/perceval/rendering/canvas/canvas.py index 00616fe5e..6434efb9e 100644 --- a/perceval/rendering/canvas/canvas.py +++ b/perceval/rendering/canvas/canvas.py @@ -29,7 +29,6 @@ from __future__ import annotations from abc import ABC -from typing import List, Union, Tuple class Canvas(ABC): @@ -49,7 +48,7 @@ def __init__(self, inverse_Y=False, **opts): self._inverse_Y = -1 if inverse_Y else 1 self._background_color = None - def set_offset(self, v: Tuple[float, float], width: float, height: float): + def set_offset(self, v: tuple[float, float], width: float, height: float): self._offset_x = v[0] self._offset_y = v[1] self.position = (0, 0) @@ -83,7 +82,7 @@ def width(self): return self._maxx - self._minx def add_mline(self, - points: List[float], + points: list[float], stroke: str = "black", stroke_width: float = 1, stroke_linejoin: str = "miter", @@ -103,7 +102,7 @@ def add_mline(self, return norm_points def add_polygon(self, - points: List[float], + points: list[float], stroke: str = "black", stroke_width: float = 1, fill: str = None, @@ -126,7 +125,7 @@ def add_polygon(self, return norm_points def add_rect(self, - points: Tuple[float, float], + points: tuple[float, float], width: float, height: float, **args): @@ -137,7 +136,7 @@ def add_rect(self, **args) def add_mpath(self, - points: List[Union[float, str]], + points: list[float | str], stroke: str = "black", stroke_width: float = 1, fill: str = None, @@ -228,7 +227,7 @@ def add_mpath(self, return norm_points def add_circle(self, - points: Tuple[float, float], + points: tuple[float, float], r: float, stroke: str = "black", stroke_width: float = 1, @@ -239,7 +238,7 @@ def add_circle(self, self.position = points return (self.position[0], self._inverse_Y * self.position[1]) - def add_text(self, points: Tuple[float, float], + def add_text(self, points: tuple[float, float], text: str, size: float, ta: str = "left", # Literal["left", "middle", "right"] fontstyle: str = "normal" # Literal["normal", "bold", "italic"] diff --git a/perceval/rendering/circuit/__init__.py b/perceval/rendering/circuit/__init__.py index 6641637c1..9060506f5 100644 --- a/perceval/rendering/circuit/__init__.py +++ b/perceval/rendering/circuit/__init__.py @@ -27,8 +27,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from typing import Type - from .abstract_skin import ASkin, ModeStyle from .phys_skin import PhysSkin from .symb_skin import SymbSkin @@ -40,7 +38,7 @@ class DisplayConfig: _selected_skin = PhysSkin # Default skin is PhysSkin @staticmethod - def select_skin(skin: Type[ASkin]) -> None: + def select_skin(skin: type[ASkin]) -> None: DisplayConfig._selected_skin = skin @staticmethod diff --git a/perceval/rendering/circuit/abstract_skin.py b/perceval/rendering/circuit/abstract_skin.py index 8b962f379..6a311b01f 100644 --- a/perceval/rendering/circuit/abstract_skin.py +++ b/perceval/rendering/circuit/abstract_skin.py @@ -29,7 +29,6 @@ from abc import ABC, abstractmethod from enum import Enum -from typing import Callable, Tuple from multipledispatch import dispatch from perceval.components import ACircuit, AProcessor, PERM @@ -66,7 +65,7 @@ def __init__(self, stroke_style, style_subcircuit, compact_display: bool = False self.style_subcircuit = style_subcircuit @dispatch((ACircuit, TD), bool) - def get_size(self, c: ACircuit, recursive: bool = False) -> Tuple[int, int]: + def get_size(self, c: ACircuit, recursive: bool = False) -> tuple[int, int]: """Gets the size of a circuit in arbitrary unit. If composite, it will take its components into account""" if not c.is_composite(): return self.measure(c) @@ -86,7 +85,7 @@ def get_size(self, c: ACircuit, recursive: bool = False) -> Tuple[int, int]: return max(w), c.m @dispatch(AProcessor, bool) - def get_size(self, p: AProcessor, recursive: bool = False) -> Tuple[int, int]: + def get_size(self, p: AProcessor, recursive: bool = False) -> tuple[int, int]: height = p.m # w represents the graph of the circuit. # Each value being the output of the rightmost component on the corresponding mode @@ -105,7 +104,7 @@ def get_size(self, p: AProcessor, recursive: bool = False) -> Tuple[int, int]: w[r] = [end_w] * comp.m return max(w), min(p.circuit_size, height+2) - def measure(self, c: AComponent) -> Tuple[int, int]: + def measure(self, c: AComponent) -> tuple[int, int]: """ Returns the measure (in arbitrary unit (AU) where the space between two modes = 1 AU) of a single component treated as a block (meaning that a composite circuit will not be @@ -118,5 +117,5 @@ def get_width(self, c) -> int: """Returns the width of component c""" @abstractmethod - def get_shape(self, c) -> Callable: + def get_shape(self, c) -> callable: """Returns the shape function of component c""" diff --git a/perceval/rendering/circuit/renderer.py b/perceval/rendering/circuit/renderer.py index c2f9a3ce3..a72bc672c 100644 --- a/perceval/rendering/circuit/renderer.py +++ b/perceval/rendering/circuit/renderer.py @@ -30,7 +30,6 @@ from abc import ABC, abstractmethod import copy import math -from typing import Any, Tuple from perceval.rendering.circuit import ASkin, ModeStyle from perceval.rendering.format import Format @@ -121,7 +120,7 @@ def render_circuit(self, self.extend_pos(0, circuit.m - 1) @abstractmethod - def get_circuit_size(self, circuit: ACircuit, recursive: bool = False) -> Tuple[int, int]: + def get_circuit_size(self, circuit: ACircuit, recursive: bool = False) -> tuple[int, int]: """ Returns the circuit size (in AU) """ @@ -152,32 +151,32 @@ def close(self) -> None: """ @abstractmethod - def open_subblock(self, lines: Tuple[int, ...], name: str, size: Tuple[int, int], color=None) -> None: + def open_subblock(self, lines: tuple[int, ...], name: str, size: tuple[int, int], color=None) -> None: """ Opens a visual area, highlighting a part of the circuit """ @abstractmethod - def close_subblock(self, lines: Tuple[int, ...]) -> None: + def close_subblock(self, lines: tuple[int, ...]) -> None: """ Close a visual area """ @abstractmethod - def draw(self) -> Any: + def draw(self) -> any: """ Finalize drawing, returns a fully drawn circuit (type is relative to the rendering method which was used). This should always be the last call. """ @abstractmethod - def append_subcircuit(self, lines: Tuple[int, ...], circuit: Circuit, content: str) -> None: + def append_subcircuit(self, lines: tuple[int, ...], circuit: Circuit, content: str) -> None: """ Add a composite circuit to the rendering. Render each subcomponent independently. """ @abstractmethod - def append_circuit(self, lines: Tuple[int, ...], circuit: ACircuit, content: str) -> None: + def append_circuit(self, lines: tuple[int, ...], circuit: ACircuit, content: str) -> None: """ Add a component (or a circuit treated as a single component) to the rendering, on modes 'lines' """ diff --git a/perceval/rendering/pdisplay.py b/perceval/rendering/pdisplay.py index 9b80fa546..fe19fb099 100644 --- a/perceval/rendering/pdisplay.py +++ b/perceval/rendering/pdisplay.py @@ -44,7 +44,6 @@ import networkx as nx import sympy as sp from tabulate import tabulate -from typing import Union from perceval.algorithm import Analyzer, AProcessTomography from perceval.components import ACircuit, Circuit, AProcessor, non_unitary_components as nl @@ -218,7 +217,7 @@ def pdisplay_analyzer(analyzer: Analyzer, output_format: Format = Format.TEXT, n tablefmt=_TABULATE_FMT_MAPPING[output_format]) -def pdisplay_state_distrib(sv: Union[StateVector, ProbabilityDistribution, BSCount], +def pdisplay_state_distrib(sv: StateVector | ProbabilityDistribution | BSCount, output_format: Format = Format.TEXT, nsimplify=True, precision=1e-6, max_v=None, sort=True): """ :meta private: diff --git a/perceval/runtime/_token_management.py b/perceval/runtime/_token_management.py index 6ad08db16..3a89ccd91 100644 --- a/perceval/runtime/_token_management.py +++ b/perceval/runtime/_token_management.py @@ -28,7 +28,6 @@ # SOFTWARE. import os -from typing import Union from perceval.utils.logging import logger, channel @@ -56,13 +55,13 @@ def __init__(self, env_var: str = "PCVL_CLOUD_TOKEN"): self._env_var = env_var self._persistent_data = PersistentData() - def _from_environment_variable(self) -> Union[str, None]: + def _from_environment_variable(self) -> str | None: if not self._env_var: return None TokenProvider._CACHED_TOKEN = os.getenv(self._env_var) return TokenProvider._CACHED_TOKEN - def _from_file(self) -> Union[str, None]: + def _from_file(self) -> str | None: token = None if self._persistent_data.has_file(_TOKEN_FILE_NAME): try: @@ -71,7 +70,7 @@ def _from_file(self) -> Union[str, None]: logger.warn("Cannot read token persistent file", channel.user) return token - def get_token(self) -> Union[str, None]: + def get_token(self) -> str | None: """Search for a token to provide :return: A token, or None if no token was found @@ -92,7 +91,7 @@ def clear_cache(): TokenProvider._CACHED_TOKEN = None @property - def cache(self) -> Union[str, None]: + def cache(self) -> str | None: return TokenProvider._CACHED_TOKEN @staticmethod diff --git a/perceval/runtime/job.py b/perceval/runtime/job.py index 220a1c37a..5fa6871ef 100644 --- a/perceval/runtime/job.py +++ b/perceval/runtime/job.py @@ -28,7 +28,6 @@ # SOFTWARE. from abc import ABC, abstractmethod -from typing import Dict, Callable from perceval.utils.logging import logger, channel @@ -36,7 +35,7 @@ class Job(ABC): - def __init__(self, result_mapping_function: Callable = None, delta_parameters=None, command_param_names=None): + def __init__(self, result_mapping_function: callable = None, delta_parameters=None, command_param_names=None): self._results = None self._result_mapping_function = result_mapping_function self._delta_parameters = delta_parameters or {"command": {}, "mapping": {}} @@ -77,7 +76,7 @@ def _handle_params(self, args, kwargs): if kwargs: raise RuntimeError(f"Unused parameters in user call ({list(kwargs.keys())})") - def __call__(self, *args, **kwargs) -> Dict: + def __call__(self, *args, **kwargs) -> dict: return self.execute_sync(*args, **kwargs) @property @@ -106,7 +105,7 @@ def is_running(self) -> bool: return self.status.running @abstractmethod - def execute_sync(self, *args, **kwargs) -> Dict: + def execute_sync(self, *args, **kwargs) -> dict: pass @abstractmethod @@ -121,7 +120,7 @@ def cancel(self): def _get_results(self): pass - def get_results(self) -> Dict: + def get_results(self) -> dict: job_status = self.status if not job_status.maybe_completed: diff --git a/perceval/runtime/job_status.py b/perceval/runtime/job_status.py index 97877c2b9..16319061a 100644 --- a/perceval/runtime/job_status.py +++ b/perceval/runtime/job_status.py @@ -29,7 +29,6 @@ from enum import Enum from time import time, sleep -from typing import Optional from perceval.utils.logging import logger, channel @@ -63,9 +62,9 @@ def __init__(self): self._duration = None self._completed_time = None self._running_progress: float = 0 - self._running_phase: Optional[str] = None + self._running_phase: str | None = None self._stop_message = None - self._waiting_progress: Optional[int] = None + self._waiting_progress: int | None = None self._last_progress_time: float = 0 def __call__(self): @@ -83,7 +82,7 @@ def start_run(self): self._running_time_start = time() self._status = RunningStatus.RUNNING - def stop_run(self, cause: RunningStatus = RunningStatus.SUCCESS, mesg: Optional[str] = None): + def stop_run(self, cause: RunningStatus = RunningStatus.SUCCESS, mesg: str | None = None): self._status = cause self._completed_time = time() self._duration = self._completed_time - self._init_time_start @@ -91,7 +90,7 @@ def stop_run(self, cause: RunningStatus = RunningStatus.SUCCESS, mesg: Optional[ self._running_progress = 1 self._stop_message = mesg - def update_progress(self, progress: float, phase: Optional[str] = None): + def update_progress(self, progress: float, phase: str | None = None): if self._status == RunningStatus.WAITING: self.start_run() self._running_progress = progress diff --git a/perceval/runtime/local_job.py b/perceval/runtime/local_job.py index fe183184f..0d38655ec 100644 --- a/perceval/runtime/local_job.py +++ b/perceval/runtime/local_job.py @@ -27,7 +27,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from typing import Callable, Optional import threading from perceval.utils.logging import logger, channel @@ -37,8 +36,8 @@ class LocalJob(Job): def __init__(self, - fn: Callable, - result_mapping_function: Callable = None, + fn: callable, + result_mapping_function: callable = None, delta_parameters: dict = None, command_param_names: list = None): """ @@ -58,7 +57,7 @@ def __init__(self, self._user_cb = None self._cancel_requested = False - def set_progress_callback(self, callback: Callable): # Signature must be (float, Optional[str]) + def set_progress_callback(self, callback: callable): # Signature must be (float, str | None) self._user_cb = callback @property @@ -68,7 +67,7 @@ def status(self) -> JobStatus: self._status.stop_run() return self._status - def _progress_cb(self, progress: float, phase: Optional[str] = None): + def _progress_cb(self, progress: float, phase: str | None = None): self._status.update_progress(progress, phase) if self._cancel_requested: return {'cancel_requested': True} diff --git a/perceval/runtime/remote_job.py b/perceval/runtime/remote_job.py index 3eede3b3d..59b49f78c 100644 --- a/perceval/runtime/remote_job.py +++ b/perceval/runtime/remote_job.py @@ -29,7 +29,6 @@ import json import time -from typing import Any from requests.exceptions import HTTPError, ConnectionError from .job import Job @@ -155,7 +154,7 @@ def status(self) -> JobStatus: return self._job_status - def execute_sync(self, *args, **kwargs) -> Any: + def execute_sync(self, *args, **kwargs) -> any: job = self.execute_async(*args, **kwargs) while not job.is_complete: time.sleep(self._refresh_progress_delay) diff --git a/perceval/runtime/remote_processor.py b/perceval/runtime/remote_processor.py index cc0e107b8..80f73d83c 100644 --- a/perceval/runtime/remote_processor.py +++ b/perceval/runtime/remote_processor.py @@ -27,7 +27,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import uuid -from typing import Dict, List, Any from multipledispatch import dispatch from perceval.components.abstract_processor import AProcessor, ProcessorType @@ -157,12 +156,12 @@ def performance(self): return self._perfs @property - def constraints(self) -> Dict: + def constraints(self) -> dict: if 'constraints' in self._specs: return self._specs['constraints'] return {} - def set_parameter(self, key: str, value: Any): + def set_parameter(self, key: str, value: any): super().set_parameter(key, value) if key in DEPRECATED_NOISE_PARAMS: logger.warn( @@ -216,11 +215,11 @@ def check_input(self, input_state: BasicState) -> None: raise RuntimeError(f"Input state and circuit size do not match ({input_state.m} != {self._n_moi})") @property - def available_commands(self) -> List[str]: + def available_commands(self) -> list[str]: return self._specs.get("available_commands", []) def prepare_job_payload(self, command: str, circuitless: bool = False, inputless: bool = False, **kwargs - ) -> Dict[str, Any]: + ) -> dict[str, any]: j = { 'platform_name': self.name, 'pcvl_version': PMetadata.short_version(), diff --git a/perceval/serialization/_parameter_serialization.py b/perceval/serialization/_parameter_serialization.py index 266eaed76..e77599920 100644 --- a/perceval/serialization/_parameter_serialization.py +++ b/perceval/serialization/_parameter_serialization.py @@ -26,13 +26,11 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from typing import Union - from perceval.utils import Parameter, Expression from perceval.serialization import _schema_circuit_pb2 as pb -def serialize_parameter(param: Union[Parameter, float]): +def serialize_parameter(param: Parameter | float): pb_param = pb.Parameter() if isinstance(param, float): pb_param.real_value = param diff --git a/perceval/serialization/deserialize.py b/perceval/serialization/deserialize.py index e58efa1bb..97ee083d9 100644 --- a/perceval/serialization/deserialize.py +++ b/perceval/serialization/deserialize.py @@ -28,7 +28,6 @@ # SOFTWARE. from base64 import b64decode from os import path -from typing import Union import json from zlib import decompress @@ -59,7 +58,7 @@ def deserialize_float(floatstring): return float(floatstring) -def deserialize_matrix(pb_mat: Union[str, pb.Matrix]) -> Matrix: +def deserialize_matrix(pb_mat: str | pb.Matrix) -> Matrix: if not isinstance(pb_mat, pb.Matrix): pb_binary_repr = pb_mat pb_mat = pb.Matrix() @@ -80,7 +79,7 @@ def matrix_from_file(filepath: str) -> Matrix: return deserialize_matrix(f.read()) -def deserialize_circuit(pb_circ: Union[str, bytes, pb.Circuit]) -> Circuit: +def deserialize_circuit(pb_circ: str | bytes | pb.Circuit) -> Circuit: if not isinstance(pb_circ, pb.Circuit): pb_binary_repr = pb_circ pb_circ = pb.Circuit() diff --git a/perceval/simulators/_simulator_utils.py b/perceval/simulators/_simulator_utils.py index e2fa9f84b..684aebaef 100644 --- a/perceval/simulators/_simulator_utils.py +++ b/perceval/simulators/_simulator_utils.py @@ -31,7 +31,6 @@ from perceval.components import Circuit from copy import copy from math import sqrt -from typing import List def _to_bsd(sv: StateVector) -> BSDistribution: @@ -76,11 +75,11 @@ def _annot_state_mapping(bs_with_annots: BasicState): return mapping -def _retrieve_mode_count(component_list: List) -> int: +def _retrieve_mode_count(component_list: list) -> int: return max([m for r in component_list for m in r[0]]) + 1 -def _unitary_components_to_circuit(component_list: List, m: int = 0): +def _unitary_components_to_circuit(component_list: list, m: int = 0): if not m: m = _retrieve_mode_count(component_list) circuit = Circuit(m) diff --git a/perceval/simulators/delay_simulator.py b/perceval/simulators/delay_simulator.py index 9d5669710..20d135b4b 100644 --- a/perceval/simulators/delay_simulator.py +++ b/perceval/simulators/delay_simulator.py @@ -33,7 +33,6 @@ from perceval.utils import BasicState, BSDistribution, StateVector, global_params from enum import Enum -from typing import List, Tuple class _CType(Enum): @@ -42,10 +41,10 @@ class _CType(Enum): OTHER = 2 -def _count_total_delay(component_list: List) -> int: +def _count_total_delay(component_list: list) -> int: return int(sum([c.get_variables()["t"] if isinstance(c, TD) else 0 for _, c in component_list])) -def _compute_depth(component_list: List, mode_count: int) -> int: +def _compute_depth(component_list: list, mode_count: int) -> int: depth = 1 count_per_mode = [[0, {i}] for i in range(mode_count)] for r, c in component_list: @@ -91,7 +90,7 @@ def _prepare_circuit(self, circuit): self._expanded_m = expanded_mode_count return expanded_circuit - def _mode_range(self) -> Tuple[int, int]: + def _mode_range(self) -> tuple[int, int]: return (self._depth - 1) * self._original_m, self._depth * self._original_m def _postprocess_bsd_impl(self, results): @@ -112,7 +111,7 @@ def _postprocess_sv_impl(self, results: StateVector) -> StateVector: output += probampli*reduced_out_state return output - def _expand_td(self, component_list: List): + def _expand_td(self, component_list: list): mode_count = self._original_m expanded = [] current_chunk = [] diff --git a/perceval/simulators/loss_simulator.py b/perceval/simulators/loss_simulator.py index 883c56a85..172cb0e98 100644 --- a/perceval/simulators/loss_simulator.py +++ b/perceval/simulators/loss_simulator.py @@ -32,8 +32,6 @@ from perceval.components import ACircuit, LC, PERM, BS from perceval.utils import BasicState, BSDistribution, StateVector -from typing import List - class LossSimulator(ASimulatorDecorator): @@ -59,7 +57,7 @@ def _postprocess_sv_impl(self, sv: StateVector) -> StateVector: output += probampli*reduced_out_state return output - def _simulate_losses_with_beam_splitters(self, components: List) -> ACircuit: + def _simulate_losses_with_beam_splitters(self, components: list) -> ACircuit: output = [] can_output_circuit = True next_free_mode = self._original_m diff --git a/perceval/simulators/noisy_sampling_simulator.py b/perceval/simulators/noisy_sampling_simulator.py index 09d21557d..a11419324 100644 --- a/perceval/simulators/noisy_sampling_simulator.py +++ b/perceval/simulators/noisy_sampling_simulator.py @@ -28,7 +28,6 @@ # SOFTWARE. import math import time -from typing import Callable, Dict, Tuple from perceval.backends import ASamplingBackend from perceval.components import ACircuit @@ -47,7 +46,7 @@ def __init__(self, sampling_backend: ASamplingBackend): self._min_samples = 100 # to be sampled at once self._max_samples = 2000 # to be sampled at once - def prepare(self, noisy_input: BSDistribution, n_samples: int, progress_callback: Callable = None): + def prepare(self, noisy_input: BSDistribution, n_samples: int, progress_callback: callable = None): for noisy_s, prob in noisy_input.items(): ns = min(math.ceil(prob * n_samples), self._max_samples) for bs in noisy_s.separate_state(keep_annotations=False): @@ -168,7 +167,7 @@ def _perfect_samples_no_selection( self, input_state: BasicState, n_samples: int, - progress_callback: Callable = None) -> Dict: + progress_callback: callable = None) -> dict: self._backend.set_input_state(input_state) samples_acquired = 0 results = BSSamples() @@ -197,7 +196,7 @@ def _noisy_sampling( provider: SamplesProvider, max_samples: int, max_shots: int, - progress_callback: Callable = None) -> Dict: + progress_callback: callable = None) -> dict: output = BSSamples() idx = 0 @@ -254,7 +253,7 @@ def _noisy_sampling( logical_perf = selected / (selected + not_selected) return {'results': output, 'physical_perf': physical_perf, 'logical_perf': logical_perf} - def _check_input_svd(self, svd: SVDistribution) -> Tuple[float, float]: + def _check_input_svd(self, svd: SVDistribution) -> tuple[float, float]: """ Check the mixed input state for its validity in the sampling case (no superposed states allowed) and compute both Zero Photon Probability (zpp) and MAX Probability of input states containing enough photons (max_p). @@ -275,7 +274,7 @@ def _check_input_svd(self, svd: SVDistribution) -> Tuple[float, float]: return zpp, max_p def _preprocess_input_state(self, svd: SVDistribution, max_p: float, n_threshold: int - ) -> Tuple[BSDistribution, float]: + ) -> tuple[BSDistribution, float]: """ Rework the input distribution to get rid of improbable states. Compute a first value for physical performance """ @@ -297,7 +296,7 @@ def samples(self, svd: SVDistribution, max_samples: int, max_shots: int = None, - progress_callback: Callable = None) -> Dict: + progress_callback: callable = None) -> dict: """ Run a noisy sampling simulation and retrieve the results @@ -341,7 +340,7 @@ def sample_count(self, svd: SVDistribution, max_samples: int, max_shots: int = None, - progress_callback: Callable = None) -> Dict: + progress_callback: callable = None) -> dict: sampling = self.samples(svd, max_samples, max_shots, progress_callback) sampling['results'] = samples_to_sample_count(sampling['results']) return sampling diff --git a/perceval/simulators/simulator.py b/perceval/simulators/simulator.py index a32337615..5fb2bf0c3 100644 --- a/perceval/simulators/simulator.py +++ b/perceval/simulators/simulator.py @@ -38,7 +38,7 @@ from copy import copy from multipledispatch import dispatch from numbers import Number -from typing import Callable, Set, Union, Optional, List +from collections.abc import Callable from scipy.sparse import csc_array, csr_array @@ -208,7 +208,7 @@ def _invalidate_cache(self): self.DEBUG_evolve_count = 0 self.DEBUG_merge_count = 0 - def _evolve_cache(self, input_list: Set[BasicState]): + def _evolve_cache(self, input_list: set[BasicState]): for state in input_list: if state not in self._evolve: self._backend.set_input_state(state) @@ -242,7 +242,7 @@ def probs(self, input_state: StateVector) -> BSDistribution: return self.probs(input_state[0]) return _to_bsd(self.evolve(input_state)) - def _probs_svd_generic(self, input_dist, p_threshold, progress_callback: Optional[Callable] = None): + def _probs_svd_generic(self, input_dist, p_threshold, progress_callback: Callable | None = None): decomposed_input = [] """decomposed input: From a SVD = { @@ -307,7 +307,7 @@ def _probs_svd_generic(self, input_dist, p_threshold, progress_callback: Optiona res.normalize() return res - def _probs_svd_fast(self, input_dist, p_threshold, progress_callback: Optional[Callable] = None): + def _probs_svd_fast(self, input_dist, p_threshold, progress_callback: Callable | None = None): decomposed_input = [] """decomposed input: From a SVD = { @@ -374,7 +374,7 @@ def _probs_svd_fast(self, input_dist, p_threshold, progress_callback: Optional[C res.normalize() return res - def probs_svd(self, input_dist: SVDistribution, progress_callback: Optional[Callable] = None): + def probs_svd(self, input_dist: SVDistribution, progress_callback: Callable | None = None): """ Compute the probability distribution from a SVDistribution input and as well as performance scores @@ -438,7 +438,7 @@ def probs_density_matrix(self, dm: DensityMatrix) -> dict: 'physical_perf': self._physical_perf, 'logical_perf': self._logical_perf * logical_perf_coeff} - def evolve(self, input_state: Union[BasicState, StateVector]) -> StateVector: + def evolve(self, input_state: BasicState | StateVector) -> StateVector: """ Evolve a state through the circuit @@ -476,8 +476,8 @@ def evolve(self, input_state: Union[BasicState, StateVector]) -> StateVector: return result_sv def evolve_svd(self, - svd: Union[SVDistribution, StateVector, BasicState], - progress_callback: Optional[Callable] = None) -> dict: + svd: SVDistribution | StateVector | BasicState, + progress_callback: Callable | None = None) -> dict: """ Compute the SVDistribution evolved through a Linear Optical circuit @@ -533,7 +533,7 @@ def evolve_density_matrix(self, dm: DensityMatrix) -> DensityMatrix: return DensityMatrix(out_matrix, index=dm.index, check_hermitian=False) - def _construct_evolve_operator(self, input_list: List[BasicState], dm: DensityMatrix) -> csc_array: + def _construct_evolve_operator(self, input_list: list[BasicState], dm: DensityMatrix) -> csc_array: """ construct the evolution operator needed to perform evolve_density_matrix. Stores it in a csc sparse_matrix diff --git a/perceval/simulators/simulator_factory.py b/perceval/simulators/simulator_factory.py index 493cba90c..bf99e18b4 100644 --- a/perceval/simulators/simulator_factory.py +++ b/perceval/simulators/simulator_factory.py @@ -36,8 +36,6 @@ from perceval.components import ACircuit, TD, LC, Processor from perceval.backends import ABackend, SLOSBackend, BACKEND_LIST -from typing import List, Union - class SimulatorFactory: """ @@ -47,8 +45,8 @@ class SimulatorFactory: """ @staticmethod - def build(circuit: Union[ACircuit, Processor, List], - backend: Union[ABackend, str] = None, + def build(circuit: ACircuit | Processor | list, + backend: ABackend | str = None, **kwargs) -> ISimulator: """ :param circuit: The optical circuit to build the simulation layers around. diff --git a/perceval/simulators/simulator_interface.py b/perceval/simulators/simulator_interface.py index 1480ab418..faf474080 100644 --- a/perceval/simulators/simulator_interface.py +++ b/perceval/simulators/simulator_interface.py @@ -28,7 +28,6 @@ # SOFTWARE. from abc import ABC, abstractmethod -from typing import Callable, Dict from perceval.components import ACircuit from perceval.utils import BSDistribution, StateVector, SVDistribution, PostSelect, post_select_distribution, \ @@ -45,7 +44,7 @@ def probs(self, input_state) -> BSDistribution: pass @abstractmethod - def probs_svd(self, svd: SVDistribution, progress_callback: Callable = None) -> Dict: + def probs_svd(self, svd: SVDistribution, progress_callback: callable = None) -> dict: pass @abstractmethod @@ -113,7 +112,7 @@ def probs(self, input_state) -> BSDistribution: results, _ = self._postprocess_bsd(results) return results - def probs_svd(self, svd: SVDistribution, progress_callback: Callable = None) -> Dict: + def probs_svd(self, svd: SVDistribution, progress_callback: callable = None) -> dict: probs = self._simulator.probs_svd(self._prepare_input(svd), progress_callback) probs['results'], logical_perf_coeff = self._postprocess_bsd(probs['results']) probs['logical_perf'] *= logical_perf_coeff diff --git a/perceval/simulators/stepper.py b/perceval/simulators/stepper.py index e57db7ed9..8ad93f046 100644 --- a/perceval/simulators/stepper.py +++ b/perceval/simulators/stepper.py @@ -27,7 +27,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. from collections import defaultdict -from typing import List, Union, Callable, Dict import copy from perceval.utils import StateVector, BasicState, BSDistribution, SVDistribution, allstate_iterator @@ -62,7 +61,7 @@ def set_circuit(self, circuit: ACircuit): def set_min_detected_photon_filter(self, value: int): self._min_detected_photons = value - def apply(self, sv: StateVector, r: List[int], c: ACircuit) -> StateVector: + def apply(self, sv: StateVector, r: list[int], c: ACircuit) -> StateVector: """Apply a circuit on a StateVector generating another StateVector :param sv: input StateVector :param r: range of port for the circuit corresponding to StateVector position @@ -101,7 +100,7 @@ def apply(self, sv: StateVector, r: List[int], c: ACircuit) -> StateVector: def probs(self, input_state) -> BSDistribution: return _to_bsd(self.evolve(input_state)) - def probs_svd(self, svd: SVDistribution, progress_callback: Callable = None) -> Dict: + def probs_svd(self, svd: SVDistribution, progress_callback: callable = None) -> dict: res_bsd = BSDistribution() for sv, p_sv in svd.items(): res = self.probs(sv) @@ -114,7 +113,7 @@ def evolve(self, input_state) -> StateVector: assert self._out.m == input_state.m, "Loss channels cannot be used with state amplitude" return self._out - def compile(self, input_states: Union[BasicState, StateVector]) -> bool: + def compile(self, input_states: BasicState | StateVector) -> bool: if isinstance(input_states, BasicState): sv = StateVector(input_states) else: diff --git a/perceval/utils/algorithms/circuit_optimizer.py b/perceval/utils/algorithms/circuit_optimizer.py index 5e700671f..8dfdd9ba0 100644 --- a/perceval/utils/algorithms/circuit_optimizer.py +++ b/perceval/utils/algorithms/circuit_optimizer.py @@ -27,7 +27,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from typing import Callable, Tuple, Union +from collections.abc import Callable import exqalibur as xq from perceval.components import ACircuit, Circuit, GenericInterferometer, BS, PS, catalog @@ -86,9 +86,9 @@ def max_eval_per_trial(self, value): self._max_eval_per_trial = value def optimize(self, - target: Union[ACircuit, Matrix], + target: ACircuit | Matrix, template: ACircuit - ) -> Tuple[ACircuit, float]: + ) -> tuple[ACircuit, float]: """ Optimize a template circuit unitary's fidelity with a target matrix or circuit. diff --git a/perceval/utils/algorithms/optimize.py b/perceval/utils/algorithms/optimize.py index 85e53bc5c..9bc1f77c5 100644 --- a/perceval/utils/algorithms/optimize.py +++ b/perceval/utils/algorithms/optimize.py @@ -27,14 +27,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from typing import Optional, Callable, List +from collections.abc import Callable from perceval.components.linear_circuit import ACircuit from perceval.utils import Matrix, P, global_params from scipy import optimize as scpy_optimize -def _min_fnc(c: ACircuit, params: List[P], x: List[int], v: Optional[Matrix], +def _min_fnc(c: ACircuit, params: list[P], x: list[int], v: Matrix | None, f: Callable[[Matrix, Matrix], float], sign: float): for idx, p in enumerate(x): params[idx].set_value(p) @@ -50,7 +50,7 @@ def _stop_criterion(f, f0, precision, accept): def optimize(c: ACircuit, - v: Optional[Matrix], + v: Matrix | None, f: Callable[[Matrix, Matrix], float], niter: int = 20, target_opt: float = 0, diff --git a/perceval/utils/algorithms/simplification.py b/perceval/utils/algorithms/simplification.py index 9b4a71b2a..518e1b7cf 100644 --- a/perceval/utils/algorithms/simplification.py +++ b/perceval/utils/algorithms/simplification.py @@ -26,14 +26,12 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from typing import Union - import numpy as np import perceval.components.unitary_components as comp from perceval.components.linear_circuit import ACircuit, Circuit -def simplify(circuit: Union[list, ACircuit], m: int=None, display: bool = False) -> Union[list, Circuit]: +def simplify(circuit: list | ACircuit, m: int=None, display: bool = False) -> list | Circuit: r""" Tries to simplify a circuit when simplifications are possible diff --git a/perceval/utils/density_matrix.py b/perceval/utils/density_matrix.py index 9e9bb975a..faa4473f3 100644 --- a/perceval/utils/density_matrix.py +++ b/perceval/utils/density_matrix.py @@ -30,7 +30,6 @@ import random from copy import copy from math import comb, sqrt -from typing import Union, Optional, List, Tuple import numpy as np from numpy import conj @@ -116,10 +115,10 @@ class DensityMatrix: :param n_max: optional maximum number of photon if index is not given """ def __init__(self, - mixed_state: Union[np.array, sparray], - index: Optional[FockBasis] = None, - m: Optional[int] = None, - n_max: Optional[int] = None, + mixed_state: np.ndarray | sparray, + index: FockBasis | None = None, + m: int | None = None, + n_max: int | None = None, check_hermitian: bool = True, precision: bool = 1e-6): """ @@ -155,7 +154,7 @@ def __init__(self, self.set_index(index) # index construction @staticmethod - def from_svd(svd: Union[SVDistribution, StateVector, BasicState], index: Optional[FockBasis] = None): + def from_svd(svd: SVDistribution | StateVector | BasicState, index: FockBasis | None = None): """ Construct a Density matrix from a SVDistribution. @@ -207,7 +206,7 @@ def set_index(self, index: dict): else: raise ValueError("the index size does not match the matrix size") - def __getitem__(self, key: Tuple[BasicState, BasicState]): + def __getitem__(self, key: tuple[BasicState, BasicState]): """key must be a BasicState tuple""" key1, key2 = key if not isinstance(key1, BasicState) or not isinstance(key2, BasicState): @@ -279,7 +278,7 @@ def _to_svd_large(self, threshold, batch_size): continue return SVDistribution(dic) - def to_svd(self, threshold: Optional[float] = None, batch_size: int = 1): + def to_svd(self, threshold: float | None = None, batch_size: int = 1): """ Gives back an SVDistribution from the density_matrix @@ -361,7 +360,7 @@ def __rmul__(self, other): else: return density_matrix_tensor_product(other, self) - def remove_low_amplitude(self, threshold: Optional[float] = None): + def remove_low_amplitude(self, threshold: float | None = None): """ Remove the lines and column where the amplitude is below a certain threshold """ @@ -400,7 +399,7 @@ def sample(self, count: int = 1) -> BSSamples: output.append(state) return output - def measure(self, modes: Union[List[int], int]): + def measure(self, modes: list[int] | int): """ Makes a measure on a list of modes. :param modes: a list of integer for the modes you want to measure @@ -422,7 +421,7 @@ def measure(self, modes: Union[List[int], int]): res[key_fs] = (prob, resulting_dm) return res - def _construct_projector_one_sample(self, modes, fock_state) -> Tuple[FockBasis, dok_array]: + def _construct_projector_one_sample(self, modes, fock_state) -> tuple[FockBasis, dok_array]: """ Construct the projection operator onto the subspace of some number photons on some mode """ @@ -439,7 +438,7 @@ def _construct_projector_one_sample(self, modes, fock_state) -> Tuple[FockBasis, return basis, projector - def _construct_all_projectors(self, modes: List[int]) -> dict: + def _construct_all_projectors(self, modes: list[int]) -> dict: """ construct all the projectors associated with some modes :return: a dictionary with for each measured state a list [fock_basis, projector, probability] @@ -513,7 +512,7 @@ def _construct_loss_operators(self, mode: int, p: float): p**n_photon_loss) return operators - def apply_loss(self, modes: Union[int, list], prob: float): + def apply_loss(self, modes: int | list, prob: float): """ Apply a loss on some mode according to some probability of losing a photon Everything works like if the mode was connected to some virtual mode with a beam splitter of reflectivity prob diff --git a/perceval/utils/density_matrix_utils.py b/perceval/utils/density_matrix_utils.py index adbadf052..fc904db83 100644 --- a/perceval/utils/density_matrix_utils.py +++ b/perceval/utils/density_matrix_utils.py @@ -28,14 +28,10 @@ # SOFTWARE. import sys -from typing import Union import numpy as np from scipy.sparse import csr_array -if sys.version.startswith('3.8.'): - from scipy.sparse import spmatrix as sparray -else: - from scipy.sparse import sparray +from scipy.sparse import sparray from perceval.utils.statevector import StateVector @@ -86,7 +82,7 @@ def statevector_to_array(sv: StateVector, index: dict): return vector -def array_to_statevector(vector: Union[np.ndarray, sparray], reverse_index: list): +def array_to_statevector(vector: np.ndarray | sparray, reverse_index: list): """ translate an array in a StateVector :param vector: an array @@ -102,7 +98,7 @@ def array_to_statevector(vector: Union[np.ndarray, sparray], reverse_index: list return sv -def is_hermitian(matrix: Union[sparray, np.ndarray]) -> bool: +def is_hermitian(matrix: sparray | np.ndarray) -> bool: n, m = matrix.shape diff --git a/perceval/utils/logical_state.py b/perceval/utils/logical_state.py index 08f757efb..f310a51ce 100644 --- a/perceval/utils/logical_state.py +++ b/perceval/utils/logical_state.py @@ -27,11 +27,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from typing import List - class LogicalState(list): - def __init__(self, state: List[int] or str = None): + def __init__(self, state: list[int] or str = None): """Represent a Logical state :param state: Can be either None, a list or a str, defaults to None @@ -61,7 +59,7 @@ def __str__(self): return ''.join([str(x) for x in self]) -def generate_all_logical_states(n : int) -> List[LogicalState]: +def generate_all_logical_states(n : int) -> list[LogicalState]: format_str = f"#0{n+2}b" logical_state_list = [] for i in range(2**n): diff --git a/perceval/utils/matrix.py b/perceval/utils/matrix.py index 8e7aa5008..fc04d228f 100644 --- a/perceval/utils/matrix.py +++ b/perceval/utils/matrix.py @@ -34,7 +34,7 @@ from abc import ABC, abstractmethod from scipy.linalg import sqrtm, block_diag, svd -from typing import Iterator, Optional, Union, Tuple +from collections.abc import Iterator import numpy as np import sympy as sp @@ -105,7 +105,7 @@ def eye(n: int, use_symbolic: bool = False) -> Matrix: return MatrixN(np.eye(n, dtype=complex)) @staticmethod - def zeros(shape: Tuple[int, int], use_symbolic: bool = False) -> Matrix: + def zeros(shape: tuple[int, int], use_symbolic: bool = False) -> Matrix: """Generate an empty matrix :param shape: 2D shape of the matrix @@ -141,7 +141,7 @@ def tosp(self): pass @staticmethod - def random_unitary(n: int, parameters: Optional[Union[np.ndarray, list]] = None) -> MatrixN: + def random_unitary(n: int, parameters: np.ndarray | list | None = None) -> MatrixN: r"""static method generating a random unitary matrix :param n: size of the Matrix @@ -157,7 +157,7 @@ def random_unitary(n: int, parameters: Optional[Union[np.ndarray, list]] = None) return Matrix._unitarize_matrix(n, u) @staticmethod - def parametrized_unitary(n: int, parameters: Union[np.ndarray,list]) -> MatrixN: + def parametrized_unitary(n: int, parameters: np.ndarray | list) -> MatrixN: r"""static method generating a parametrized unitary matrix :param n: size of the Matrix diff --git a/perceval/utils/noise_model.py b/perceval/utils/noise_model.py index e8144d52a..73dac4962 100644 --- a/perceval/utils/noise_model.py +++ b/perceval/utils/noise_model.py @@ -28,7 +28,6 @@ # SOFTWARE. from ._validated_params import AValidatedParam, ValidatedBool, ValidatedFloat -from typing import Dict class NoiseModel: @@ -54,7 +53,7 @@ def __init__(self, phase_imprecision: float = None ): # Source parameters - self._params: Dict[str, AValidatedParam] = {} + self._params: dict[str, AValidatedParam] = {} self._add_param(ValidatedFloat("brightness", brightness, 0, 1, 1)) self._add_param(ValidatedFloat("indistinguishability", indistinguishability, 0, 1, 1)) self._add_param(ValidatedFloat("g2", g2, 0, 1, 0)) diff --git a/perceval/utils/parameter.py b/perceval/utils/parameter.py index d20e00371..bdaf8a7e5 100644 --- a/perceval/utils/parameter.py +++ b/perceval/utils/parameter.py @@ -30,8 +30,6 @@ import random import sympy as sp -from typing import Tuple - class Parameter: r"""A Parameter is a used as a variable in a circuit definition @@ -173,7 +171,7 @@ def __repr__(self): self._max is not None and ", max_v="+str(self._max) or "") @property - def bounds(self) -> Tuple[float, float]: + def bounds(self) -> tuple[float, float]: r"""Minimal and maximal values for the parameter """ return self._min, self._max diff --git a/perceval/utils/persistent_data.py b/perceval/utils/persistent_data.py index 6797af986..909827d31 100644 --- a/perceval/utils/persistent_data.py +++ b/perceval/utils/persistent_data.py @@ -30,7 +30,6 @@ import os import json import warnings -from typing import Union from platformdirs import PlatformDirs from .metadata import PMetadata @@ -132,7 +131,7 @@ def delete_file(self, filename: str): except OSError: warnings.warn(UserWarning("Cannot delete persistent file {file_path}")) - def write_file(self, filename: str, data: Union[bytes, str], file_format: FileFormat): + def write_file(self, filename: str, data: bytes | str, file_format: FileFormat): """Write data into a file in persistent data directory :param filename: name of the file to write in (with extension) @@ -154,7 +153,7 @@ def write_file(self, filename: str, data: Union[bytes, str], file_format: FileFo else: warnings.warn(UserWarning(f"Can't save {filename}")) - def read_file(self, filename: str, file_format: FileFormat) -> Union[bytes, str]: + def read_file(self, filename: str, file_format: FileFormat) -> bytes | str: """Read data from a file in persistent data directory :param filename: name of the file to read (with extension) diff --git a/perceval/utils/polarization.py b/perceval/utils/polarization.py index a9407a079..5521853eb 100644 --- a/perceval/utils/polarization.py +++ b/perceval/utils/polarization.py @@ -32,7 +32,6 @@ import numpy as np import re import sympy as sp -from typing import Union, Tuple, Any from .statevector import BasicState from .matrix import Matrix @@ -48,7 +47,7 @@ class Polarization: :raise: `ValueError` if the parameters are out of range, or invalid """ def __init__(self, - v: Union[str, Any, Tuple[Any, Any]]): + v: str | any | tuple[any, any]): if isinstance(v, str): if v == "H": self.theta_phi = (0, 0) @@ -132,7 +131,7 @@ def parse(s: str) -> Polarization: raise ValueError("incorrect format - angle value should not contain variable in %s" % s) return Polarization((v, 0)) - def project_eh_ev(self, use_symbolic=False) -> Tuple[Any, Any]: + def project_eh_ev(self, use_symbolic=False) -> tuple[any, any]: r"""Build Jones vector corresponding to the current instance :return: a pair of numeric or symbolic expressions @@ -188,7 +187,7 @@ def _is_orthogonal(v1, v2, use_symbolic): def convert_polarized_state(state: BasicState, use_symbolic: bool = False, - inverse: bool = False) -> Tuple[BasicState, Matrix]: + inverse: bool = False) -> tuple[BasicState, Matrix]: r"""Convert a polarized BasicState into an expanded BasicState vector :param inverse: diff --git a/perceval/utils/postselect.py b/perceval/utils/postselect.py index b7ba756f5..98b50ebde 100644 --- a/perceval/utils/postselect.py +++ b/perceval/utils/postselect.py @@ -31,7 +31,6 @@ import json import re -from typing import Callable, List, Tuple class PostSelect: @@ -110,7 +109,7 @@ def le(self, indexes, value: int): self._add_condition(indexes, int.__le__, value) return self - def _add_condition(self, indexes, operator: Callable, value: int): + def _add_condition(self, indexes, operator: callable, value: int): indexes = (indexes,) if isinstance(indexes, int) else tuple(indexes) if operator not in self._conditions: self._conditions[operator] = [] @@ -149,7 +148,7 @@ def clear(self): """Clear all existing conditions""" self._conditions.clear() - def apply_permutation(self, perm_vector: List[int], first_mode: int = 0): + def apply_permutation(self, perm_vector: list[int], first_mode: int = 0): """ Apply a given permutation on the conditions. @@ -182,7 +181,7 @@ def shift_modes(self, shift: int): new_indexes = tuple(i + shift for i in indexes) cond[c] = (new_indexes, value) - def can_compose_with(self, modes: List[int]) -> bool: + def can_compose_with(self, modes: list[int]) -> bool: """ Check if all conditions are compatible with a composition on given modes @@ -226,7 +225,7 @@ def post_select_distribution( bsd: BSDistribution, postselect: PostSelect, heralds: dict = None, - keep_heralds: bool = True) -> Tuple[BSDistribution, float]: + keep_heralds: bool = True) -> tuple[BSDistribution, float]: if not (postselect.has_condition or heralds): bsd.normalize() return bsd, 1 @@ -254,7 +253,7 @@ def post_select_statevector( sv: StateVector, postselect: PostSelect, heralds: dict = None, - keep_heralds: bool = True) -> Tuple[StateVector, float]: + keep_heralds: bool = True) -> tuple[StateVector, float]: if not (postselect.has_condition or heralds): sv.normalize() return sv, 1 diff --git a/perceval/utils/stategenerator.py b/perceval/utils/stategenerator.py index b34deb429..5ba645837 100644 --- a/perceval/utils/stategenerator.py +++ b/perceval/utils/stategenerator.py @@ -27,7 +27,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from typing import List import networkx as nx from .statevector import BasicState, StateVector @@ -62,7 +61,7 @@ def __init__(self, encoding, polarization_base=(BasicState("|{P:H}>"), BasicStat else: raise ValueError("Only use RAW, DUAL_RAIL or POLARIZATION encoding.") - def logical_state(self, state: List[int]): + def logical_state(self, state: list[int]): """ Generate a StateVector from a list of logical state diff --git a/perceval/utils/statevector.py b/perceval/utils/statevector.py index 5002919a4..6514690f6 100644 --- a/perceval/utils/statevector.py +++ b/perceval/utils/statevector.py @@ -35,7 +35,6 @@ from collections import defaultdict from copy import copy from multipledispatch import dispatch -from typing import Dict, List, Union, Optional import exqalibur as xq from perceval.utils.logging import logger, channel @@ -47,7 +46,7 @@ StateVector = xq.StateVector -def allstate_iterator(input_state: Union[BasicState, StateVector], mask=None) -> BasicState: +def allstate_iterator(input_state: BasicState | StateVector, mask=None) -> BasicState: """Iterator on all possible output states compatible with mask generating StateVector :param input_state: a given input state vector @@ -80,7 +79,7 @@ def max_photon_state_iterator(m: int, n_max: int): yield output_state -def tensorproduct(states: List[Union[StateVector, BasicState]]): +def tensorproduct(states: list[StateVector | BasicState]): r""" Computes states[0] * states[1] * ... """ if len(states) == 1: @@ -128,7 +127,7 @@ def sample(self, count: int, non_null: bool = True): class SVDistribution(ProbabilityDistribution): r"""Time-Independent Probabilistic distribution of StateVectors """ - def __init__(self, sv: Optional[BasicState, StateVector, Dict] = None): + def __init__(self, sv: BasicState | StateVector | dict | None = None): super().__init__() self._n_max = 0 self._m = None @@ -187,7 +186,7 @@ def normalize(self): for sv in self.keys(): self[sv] /= sum_probs - def sample(self, count: int, non_null: bool = True) -> List[StateVector]: + def sample(self, count: int, non_null: bool = True) -> list[StateVector]: r""" Generate a sample StateVector from the `SVDistribution` :param non_null: excludes null states from the sample generation @@ -262,7 +261,7 @@ def anonymize_annotations(svd: SVDistribution, annot_tag: str = "a"): class BSDistribution(ProbabilityDistribution): r"""Time-Independent probabilistic distribution of Basic States """ - def __init__(self, d: Optional[BasicState, Dict] = None): + def __init__(self, d: BasicState | dict | None = None): super().__init__() self._m = None if d is not None: @@ -333,7 +332,7 @@ def m(self): class BSCount(defaultdict): r"""Container that counts basic state events """ - def __init__(self, d: Optional[Dict] = None): + def __init__(self, d: dict | None = None): super().__init__(int) if d is not None: for k, v in d.items(): diff --git a/setup.py b/setup.py index 3f5445aba..9e92bfee9 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,6 @@ "Tracker": "https://github.com/Quandela/Perceval/issues" }, classifiers=[ - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -41,6 +40,6 @@ "cqasm_bridge": ["libqasm~=0.6.6"], }, setup_requires=["scmver"], - python_requires=">=3.8", + python_requires=">=3.9", scmver=True ) diff --git a/tests/_test_utils.py b/tests/_test_utils.py index f40766793..db200f93b 100644 --- a/tests/_test_utils.py +++ b/tests/_test_utils.py @@ -31,7 +31,6 @@ import math import pytest -from typing import Type from pathlib import Path from unittest.mock import MagicMock @@ -164,7 +163,7 @@ def _check_qpt(test_path, ref_path): def _save_or_check(c, tmp_path, circuit_name, save_figs, recursive=False, compact=False, - skin_type: Type[ASkin] = PhysSkin) -> None: + skin_type: type[ASkin] = PhysSkin) -> None: img_path = (TEST_IMG_DIR if save_figs else tmp_path) / \ Path(circuit_name + ".svg") skin = skin_type(compact) diff --git a/tests/test_circuit_optimizer.py b/tests/test_circuit_optimizer.py index ebdf9c31c..503ec4f3b 100644 --- a/tests/test_circuit_optimizer.py +++ b/tests/test_circuit_optimizer.py @@ -26,7 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from typing import Callable +from collections.abc import Callable from perceval.utils.algorithms.circuit_optimizer import CircuitOptimizer from perceval.utils.algorithms import norm From ff92ad654eacbbe45508c744a9309269d90f060a Mon Sep 17 00:00:00 2001 From: Marion Fabre Date: Wed, 18 Sep 2024 17:56:37 +0200 Subject: [PATCH 2/4] fix python 3.9 --- perceval/components/_mode_connector.py | 2 ++ perceval/components/abstract_component.py | 1 + perceval/components/abstract_processor.py | 1 + perceval/components/component_catalog.py | 1 + perceval/components/generic_interferometer.py | 1 + perceval/components/linear_circuit.py | 7 ++++--- perceval/components/port.py | 1 + perceval/components/processor.py | 1 + perceval/components/source.py | 1 + perceval/error_mitigation/loss_mitigation.py | 1 + perceval/providers/scaleway/scaleway_rpc_handler.py | 2 ++ perceval/rendering/pdisplay.py | 1 + perceval/runtime/_token_management.py | 1 + perceval/runtime/job_status.py | 1 + perceval/runtime/local_job.py | 1 + perceval/serialization/_parameter_serialization.py | 2 ++ perceval/serialization/deserialize.py | 2 ++ perceval/simulators/simulator.py | 1 + perceval/simulators/simulator_factory.py | 1 + perceval/simulators/stepper.py | 2 ++ perceval/utils/algorithms/circuit_optimizer.py | 1 + perceval/utils/algorithms/optimize.py | 1 + perceval/utils/algorithms/simplification.py | 2 ++ perceval/utils/density_matrix.py | 1 + perceval/utils/density_matrix_utils.py | 1 + perceval/utils/persistent_data.py | 1 + 26 files changed, 35 insertions(+), 3 deletions(-) diff --git a/perceval/components/_mode_connector.py b/perceval/components/_mode_connector.py index 88a4387cc..8cce29259 100644 --- a/perceval/components/_mode_connector.py +++ b/perceval/components/_mode_connector.py @@ -26,6 +26,8 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations + from perceval.utils.logging import get_logger, channel from .abstract_component import AComponent diff --git a/perceval/components/abstract_component.py b/perceval/components/abstract_component.py index 48254db4f..51adbd0dd 100644 --- a/perceval/components/abstract_component.py +++ b/perceval/components/abstract_component.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations from abc import ABC from collections.abc import Iterable diff --git a/perceval/components/abstract_processor.py b/perceval/components/abstract_processor.py index ad9bf4406..956daba2e 100644 --- a/perceval/components/abstract_processor.py +++ b/perceval/components/abstract_processor.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations import copy from abc import ABC, abstractmethod diff --git a/perceval/components/component_catalog.py b/perceval/components/component_catalog.py index eb6cb08e5..99667a400 100644 --- a/perceval/components/component_catalog.py +++ b/perceval/components/component_catalog.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations import importlib from abc import ABC, abstractmethod diff --git a/perceval/components/generic_interferometer.py b/perceval/components/generic_interferometer.py index 12d47d205..0a025c416 100644 --- a/perceval/components/generic_interferometer.py +++ b/perceval/components/generic_interferometer.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations import math from collections.abc import Callable diff --git a/perceval/components/linear_circuit.py b/perceval/components/linear_circuit.py index ba7fa2024..73f53d321 100644 --- a/perceval/components/linear_circuit.py +++ b/perceval/components/linear_circuit.py @@ -33,6 +33,7 @@ import random from abc import ABC, abstractmethod +from collections.abc import Callable import numpy as np import sympy as sp @@ -580,10 +581,10 @@ def compute_unitary(self, @staticmethod @deprecated(version="0.10.0", reason="Construct a GenericInterferometer object instead") def generic_interferometer(m: int, - fun_gen: callable[[int], ACircuit], + fun_gen: Callable[[int], ACircuit], shape: str | InterferometerShape = InterferometerShape.RECTANGLE, depth: int = None, - phase_shifter_fun_gen: callable[[int], ACircuit] | None = None, + phase_shifter_fun_gen: Callable[[int], ACircuit] | None = None, phase_at_output: bool = False) -> Circuit: from .generic_interferometer import GenericInterferometer # Import in method to avoir circular dependency if isinstance(shape, str): @@ -604,7 +605,7 @@ def copy(self, subs: dict | list = None): @staticmethod def decomposition(U: MatrixN, component: ACircuit, - phase_shifter_fn: callable[[int], ACircuit] = None, + phase_shifter_fn: Callable[[int], ACircuit] = None, shape: str | InterferometerShape = InterferometerShape.TRIANGLE, permutation: type[ACircuit] = None, inverse_v: bool = False, diff --git a/perceval/components/port.py b/perceval/components/port.py index 96422dc9d..4ffd58dc1 100644 --- a/perceval/components/port.py +++ b/perceval/components/port.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations from abc import ABC, abstractmethod from enum import Enum diff --git a/perceval/components/processor.py b/perceval/components/processor.py index 5fadadaf4..11429e5dd 100644 --- a/perceval/components/processor.py +++ b/perceval/components/processor.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations import sys diff --git a/perceval/components/source.py b/perceval/components/source.py index 86944053d..8e4eede73 100644 --- a/perceval/components/source.py +++ b/perceval/components/source.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations import math diff --git a/perceval/error_mitigation/loss_mitigation.py b/perceval/error_mitigation/loss_mitigation.py index 6bd6fab62..1eab1fcb1 100644 --- a/perceval/error_mitigation/loss_mitigation.py +++ b/perceval/error_mitigation/loss_mitigation.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations import numpy as np from math import comb diff --git a/perceval/providers/scaleway/scaleway_rpc_handler.py b/perceval/providers/scaleway/scaleway_rpc_handler.py index b0393fe9e..71dd9b597 100644 --- a/perceval/providers/scaleway/scaleway_rpc_handler.py +++ b/perceval/providers/scaleway/scaleway_rpc_handler.py @@ -26,6 +26,8 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations + import urllib import time import requests diff --git a/perceval/rendering/pdisplay.py b/perceval/rendering/pdisplay.py index de8ca4884..730a3d574 100644 --- a/perceval/rendering/pdisplay.py +++ b/perceval/rendering/pdisplay.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations import copy import math diff --git a/perceval/runtime/_token_management.py b/perceval/runtime/_token_management.py index 7fdfb0a99..68151246d 100644 --- a/perceval/runtime/_token_management.py +++ b/perceval/runtime/_token_management.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations import os diff --git a/perceval/runtime/job_status.py b/perceval/runtime/job_status.py index 05f52f95a..f6e0e1116 100644 --- a/perceval/runtime/job_status.py +++ b/perceval/runtime/job_status.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations from enum import Enum from time import time, sleep diff --git a/perceval/runtime/local_job.py b/perceval/runtime/local_job.py index 1d3e4395b..5599e1f22 100644 --- a/perceval/runtime/local_job.py +++ b/perceval/runtime/local_job.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations import threading diff --git a/perceval/serialization/_parameter_serialization.py b/perceval/serialization/_parameter_serialization.py index e77599920..b42c24fcf 100644 --- a/perceval/serialization/_parameter_serialization.py +++ b/perceval/serialization/_parameter_serialization.py @@ -26,6 +26,8 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations + from perceval.utils import Parameter, Expression from perceval.serialization import _schema_circuit_pb2 as pb diff --git a/perceval/serialization/deserialize.py b/perceval/serialization/deserialize.py index 97ee083d9..167ba2222 100644 --- a/perceval/serialization/deserialize.py +++ b/perceval/serialization/deserialize.py @@ -26,6 +26,8 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations + from base64 import b64decode from os import path import json diff --git a/perceval/simulators/simulator.py b/perceval/simulators/simulator.py index 132db5193..e48029810 100644 --- a/perceval/simulators/simulator.py +++ b/perceval/simulators/simulator.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations import sys diff --git a/perceval/simulators/simulator_factory.py b/perceval/simulators/simulator_factory.py index 61448c0b9..108d5333c 100644 --- a/perceval/simulators/simulator_factory.py +++ b/perceval/simulators/simulator_factory.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations from .simulator_interface import ISimulator from .simulator import Simulator diff --git a/perceval/simulators/stepper.py b/perceval/simulators/stepper.py index 25679615d..3cd6e04d6 100644 --- a/perceval/simulators/stepper.py +++ b/perceval/simulators/stepper.py @@ -26,6 +26,8 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations + from collections import defaultdict import copy diff --git a/perceval/utils/algorithms/circuit_optimizer.py b/perceval/utils/algorithms/circuit_optimizer.py index 5c281bf56..ff63e943a 100644 --- a/perceval/utils/algorithms/circuit_optimizer.py +++ b/perceval/utils/algorithms/circuit_optimizer.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations from collections.abc import Callable diff --git a/perceval/utils/algorithms/optimize.py b/perceval/utils/algorithms/optimize.py index 9bc1f77c5..636bcf9b7 100644 --- a/perceval/utils/algorithms/optimize.py +++ b/perceval/utils/algorithms/optimize.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations from collections.abc import Callable from perceval.components.linear_circuit import ACircuit diff --git a/perceval/utils/algorithms/simplification.py b/perceval/utils/algorithms/simplification.py index 34f592192..2879089cb 100644 --- a/perceval/utils/algorithms/simplification.py +++ b/perceval/utils/algorithms/simplification.py @@ -26,6 +26,8 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations + import numpy as np import perceval.components.unitary_components as comp from perceval.components.linear_circuit import ACircuit, Circuit diff --git a/perceval/utils/density_matrix.py b/perceval/utils/density_matrix.py index faa4473f3..628028eee 100644 --- a/perceval/utils/density_matrix.py +++ b/perceval/utils/density_matrix.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations import random from copy import copy diff --git a/perceval/utils/density_matrix_utils.py b/perceval/utils/density_matrix_utils.py index fc904db83..a37a4e377 100644 --- a/perceval/utils/density_matrix_utils.py +++ b/perceval/utils/density_matrix_utils.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations import sys diff --git a/perceval/utils/persistent_data.py b/perceval/utils/persistent_data.py index 909827d31..dbe2eda9a 100644 --- a/perceval/utils/persistent_data.py +++ b/perceval/utils/persistent_data.py @@ -26,6 +26,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations import os import json From 678f1fec3478125ca6b9456904cd20c93f196374 Mon Sep 17 00:00:00 2001 From: Benoit Fanchon Date: Thu, 19 Sep 2024 18:16:41 +0200 Subject: [PATCH 3/4] Code review --- perceval/utils/persistent_data.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/perceval/utils/persistent_data.py b/perceval/utils/persistent_data.py index dbe2eda9a..2c7c247bc 100644 --- a/perceval/utils/persistent_data.py +++ b/perceval/utils/persistent_data.py @@ -39,19 +39,6 @@ _CONFIG_FILE_NAME = "config.json" -def _removesuffix(data, suffix): - """Replace the python 3.9 method removesuffix - - :param data: data on which remove suffix - :param suffix: suffix to remove - :return: data - """ - if data.endswith(suffix): - data = data[:-len(suffix)] - - return data - - class PersistentData: """PersistentData handle perceval persistent data On init, it creates a directory (if it doesn't exist) for storing perceval persistent data @@ -169,12 +156,12 @@ def read_file(self, filename: str, file_format: FileFormat) -> bytes | str: if file_format == FileFormat.BINARY: with open(file_path, "r+b") as file: data = file.read() - data = _removesuffix(data, b'\n') - data = _removesuffix(data, b' ') + data = data.removesuffix(b'\n') + data = data.removesuffix(b' ') elif file_format == FileFormat.TEXT: with open(file_path, "r+t", encoding="UTF-8") as file: data = str(file.read()) - data = _removesuffix(data, '\n').rstrip() + data = data.removesuffix('\n').rstrip() else: raise NotImplementedError(f"format {format} is not supported") return data From 956c81b732dddca23bec25f0db6be76254b5c3ee Mon Sep 17 00:00:00 2001 From: Eric Bertasi Date: Fri, 20 Sep 2024 18:32:38 +0200 Subject: [PATCH 4/4] Fast clean-up --- docs/source/notebooks/Reinforcement_learning.ipynb | 2 ++ perceval/components/component_catalog.py | 4 +--- perceval/components/generic_interferometer.py | 4 +--- perceval/converters/resources_estimator.py | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/source/notebooks/Reinforcement_learning.ipynb b/docs/source/notebooks/Reinforcement_learning.ipynb index 57adcd99a..4aa34992a 100644 --- a/docs/source/notebooks/Reinforcement_learning.ipynb +++ b/docs/source/notebooks/Reinforcement_learning.ipynb @@ -57,6 +57,8 @@ "metadata": {}, "outputs": [], "source": [ + "from __future__ import annotations\n", + "\n", "import math\n", "\n", "from ipywidgets import FloatProgress\n", diff --git a/perceval/components/component_catalog.py b/perceval/components/component_catalog.py index 99667a400..be5a46a31 100644 --- a/perceval/components/component_catalog.py +++ b/perceval/components/component_catalog.py @@ -26,8 +26,6 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from __future__ import annotations - import importlib from abc import ABC, abstractmethod from enum import Enum @@ -132,7 +130,7 @@ def build_processor(self, **kwargs) -> Processor: """Build the component as processor kwargs: - * backend(ABackend | str): Name or instance of a simulation backend. Default "SLOS" + * backend(ABackend or str): Name or instance of a simulation backend. Default "SLOS" :return: A Perceval processor """ diff --git a/perceval/components/generic_interferometer.py b/perceval/components/generic_interferometer.py index 0a025c416..474686cb2 100644 --- a/perceval/components/generic_interferometer.py +++ b/perceval/components/generic_interferometer.py @@ -26,8 +26,6 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from __future__ import annotations - import math from collections.abc import Callable @@ -58,7 +56,7 @@ def __init__(self, fun_gen: Callable[[int], ACircuit], shape: InterferometerShape = InterferometerShape.RECTANGLE, depth: int = None, - phase_shifter_fun_gen: Callable[[int], ACircuit] | None = None, + phase_shifter_fun_gen: Callable[[int], ACircuit] = None, phase_at_output: bool = False): assert isinstance(shape, InterferometerShape),\ f"Wrong type for shape, expected InterferometerShape, got {type(shape)}" diff --git a/perceval/converters/resources_estimator.py b/perceval/converters/resources_estimator.py index ef0bc394c..8cef39f19 100644 --- a/perceval/converters/resources_estimator.py +++ b/perceval/converters/resources_estimator.py @@ -27,7 +27,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from perceval.converters.circuit_to_graph_converter import CircuitToGraphConverter, gates_and_qubits +from .circuit_to_graph_converter import CircuitToGraphConverter, gates_and_qubits import numpy as np