Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pcvl 749 Remove python 3.8 #459

Merged
merged 5 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/autotests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ on:
default: '3.10'
type: choice
options:
- '3.8'
- '3.9'
- '3.10'
- '3.11'
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`:
Expand Down
3 changes: 1 addition & 2 deletions docs/source/notebooks/Reinforcement_learning.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
"metadata": {},
"outputs": [],
"source": [
"from typing import Union\n",
"import math\n",
"\n",
"from ipywidgets import FloatProgress\n",
Expand Down Expand Up @@ -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",
Copy link
Contributor

Choose a reason for hiding this comment

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

| is only working from python 3.10 and we still support python 3.9.
https://docs.python.org/3.10/whatsnew/3.10.html#pep-604-new-type-union-operator
But do not loose hope, you can add at the top of the import from __future__ import annotations (it's very important to put it as the first import) and we will delete this import in 3.10 (it's already elsewhere in the code)

Copy link
Contributor

Choose a reason for hiding this comment

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

So, is this still to be fixed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Marion did it

Copy link
Contributor

Choose a reason for hiding this comment

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

We're inside the pull request regarding this issue, you tell me Marion did it, but where is the diff showing the fix?
I expect from __future__ import annotations to be added somewhere on top of this file. I might be blind but I can't find it.

" # 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",
Expand Down
4 changes: 2 additions & 2 deletions docs/source/reference/statevector.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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.
Expand Down
15 changes: 7 additions & 8 deletions perceval/algorithm/sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 = {}
Expand Down Expand Up @@ -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)
Expand All @@ -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:
Expand All @@ -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):
Expand All @@ -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:
Expand Down
5 changes: 2 additions & 3 deletions perceval/backends/_naive_approx.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
# SOFTWARE.

import math
from typing import List, Tuple

import exqalibur as xq
from . import NaiveBackend
Expand All @@ -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)
Expand Down
11 changes: 5 additions & 6 deletions perceval/backends/_slos.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import exqalibur as xq
import math
import numpy as np
from typing import Dict, List


class _Path:
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down
10 changes: 4 additions & 6 deletions perceval/components/_mode_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,22 @@
# 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
from .unitary_components import PERM


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}'
super().__init__(f"Mode(s) {mode} not available{because}")


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}'
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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:
Expand Down
10 changes: 5 additions & 5 deletions perceval/components/abstract_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
33 changes: 16 additions & 17 deletions perceval/components/abstract_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 = []
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down
Loading
Loading