From a63c6aaa0a46d8a4c5b8770d7fb30f0dfe9dbbfc Mon Sep 17 00:00:00 2001 From: Benoit Fanchon Date: Mon, 20 Jul 2026 14:32:33 +0200 Subject: [PATCH 1/8] PCVL 1267 EMT - Dectector loss balancing --- perceval/runtime/error_mitigation/__init__.py | 1 + .../error_mitigation/detector_balancing.py | 97 ++++++++++++++++++ .../test_detector_balancing.py | 99 +++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 perceval/runtime/error_mitigation/detector_balancing.py create mode 100644 tests/runtime/error_mitigation/test_detector_balancing.py diff --git a/perceval/runtime/error_mitigation/__init__.py b/perceval/runtime/error_mitigation/__init__.py index 077c009a1..bfe6617b3 100644 --- a/perceval/runtime/error_mitigation/__init__.py +++ b/perceval/runtime/error_mitigation/__init__.py @@ -30,3 +30,4 @@ from .loss_mitigation import photon_recycling, PhotonRecycling from .abstract_mitigation import AbstractMitigation from .compilation_averaging import CompilationAveraging +from .detector_balancing import DetectorBalancing diff --git a/perceval/runtime/error_mitigation/detector_balancing.py b/perceval/runtime/error_mitigation/detector_balancing.py new file mode 100644 index 000000000..549d262db --- /dev/null +++ b/perceval/runtime/error_mitigation/detector_balancing.py @@ -0,0 +1,97 @@ +# MIT License +# +# Copyright (c) 2022 Quandela +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# As a special exception, the copyright holders of exqalibur library give you +# permission to combine exqalibur with code included in the standard release of +# Perceval under the MIT license (or modified versions of such code). You may +# copy and distribute such a combined system following the terms of the MIT +# license for both exqalibur and Perceval. This exception for the usage of +# exqalibur is limited to the python bindings used by Perceval. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# 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 copy import deepcopy, copy +import math +import warnings + +from .abstract_mitigation import AbstractMitigation +from ..computation import Computation + +from perceval.utils import NoiseModel +from perceval.utils.constants import KEY_RESULTS + +class DetectorBalancing(AbstractMitigation): + + APPLY_MIN_PHOTONS = True + APPLY_LOGICAL_SELECTION = True + + def __init__(self): + """ + A mitigation process that adjusts the probabilities of each output state based on the output + loss and number of photons in each mode. + """ + + @staticmethod + def _validate_ratios(ratios: list[float] | None, m: int) -> list[float]: + if ratios is None: + warnings.warn( + "DetectorBalancing was created without specifying loss ratios: " + "defaulting to 1.", + RuntimeWarning, + ) + ratios = [1] * m + + if len(ratios) != m: + raise ValueError( + "Size of detector loss ratios must match raw processor mode count " + f"(expected {m}, got {len(ratios)})." + ) + + def valid(v): + res = math.isfinite(v) and v > 0. + if not res and not valid.warned: + warnings.warn( + "Calibrated detector loss ratios contain non-positive or non-finite " + "values. Replacing invalid entries with 1.0.", + RuntimeWarning, + ) + valid.warned = True + return res + valid.warned = False + + return [v if valid(v) else 1. for v in ratios] + + def extend_computation(self, computation: Computation, noise: NoiseModel) -> list[Computation]: + comp = deepcopy(computation) + comp.command = "probs" + return [comp] + + def _parse_results(self, computation: Computation, results: list[dict], noise: NoiseModel) -> dict: + ratios = self._validate_ratios(noise.loss_ratios, computation.experiment.m) + + def _balance(k, v): + return math.prod([(1 / ratios[k.photon2mode(i)]) for i in range(k.n)], start = v) + + res = copy(results[0]) # We are going to modify this to keep custom fields as much as we can + assert len(ratios) == computation.experiment.m, "Loss ratios do not match the distribution lengths." + + for k, v in res[KEY_RESULTS].items(): + res[KEY_RESULTS][k] = _balance(k, v) + return res diff --git a/tests/runtime/error_mitigation/test_detector_balancing.py b/tests/runtime/error_mitigation/test_detector_balancing.py new file mode 100644 index 000000000..3135fd00b --- /dev/null +++ b/tests/runtime/error_mitigation/test_detector_balancing.py @@ -0,0 +1,99 @@ +# MIT License +# +# Copyright (c) 2022 Quandela +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# As a special exception, the copyright holders of exqalibur library give you +# permission to combine exqalibur with code included in the standard release of +# Perceval under the MIT license (or modified versions of such code). You may +# copy and distribute such a combined system following the terms of the MIT +# license for both exqalibur and Perceval. This exception for the usage of +# exqalibur is limited to the python bindings used by Perceval. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# 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 copy import copy +import random + +import pytest +from exqalibur import FockState +from exqalibur.exqalibur import PostSelect + +from perceval import DetectorBalancing, Computation, CommandFactory, Experiment, NoiseModel, Command, BSCount, \ + apply_min_photons, apply_post_select +from perceval.utils.bsdistribution import BSDistribution +from perceval.utils.constants import KEY_SHOTS_USED, KEY_RESULTS + + +def test_computation_extension(): + computation = Computation(CommandFactory.samples, Experiment()) + averaging = DetectorBalancing() + comp_list = averaging.extend_computation(computation, NoiseModel()) + + assert len(comp_list) == 1 + + assert all(comp.command == "probs" for comp in comp_list) + + + +def prepare_test(): + raw_results = BSDistribution({FockState([0, 1]): 1, + FockState([1, 0]): 1, + FockState([1, 1]): 1, + FockState([2, 0]): 1, + FockState([0, 2]): 1}) + raw_results.normalize() + + shots_used = 1_000_000 + min_photons = 2 + post_select = PostSelect("[1] >= 1") + heralds = {} + + raw_results, phys_perf = apply_min_photons(raw_results, min_photons) + raw_results, log_perf = apply_post_select(raw_results, post_select, heralds, True) + + sub_results = [] + + sub_results.append({"results": copy(raw_results), + "physical_perf": phys_perf, + "logical_perf": log_perf, + "global_perf": phys_perf * log_perf, + KEY_SHOTS_USED: shots_used}) + + expected = {"results": raw_results, + "physical_perf": phys_perf, + "logical_perf": log_perf, + "global_perf": phys_perf * log_perf, + KEY_SHOTS_USED: shots_used} + + return expected, sub_results + + +def test_recombination(): + expected, sub_results = prepare_test() + noise = NoiseModel() + noise.loss_ratios = [1., 1.] + + averaging = DetectorBalancing() + + computation = Computation(CommandFactory.probs, Experiment(2)) + computation.add_params(max_shots = 50000, max_samples = 10000) + + res = averaging.parse_results(computation, sub_results, noise) + + assert res[KEY_RESULTS]._container == pytest.approx(expected[KEY_RESULTS]._container) From b5ce2da43256f6328311d87734cdd5325d16c3df Mon Sep 17 00:00:00 2001 From: Benoit Fanchon Date: Mon, 20 Jul 2026 14:54:40 +0200 Subject: [PATCH 2/8] fix test --- tests/runtime/error_mitigation/test_detector_balancing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/runtime/error_mitigation/test_detector_balancing.py b/tests/runtime/error_mitigation/test_detector_balancing.py index 3135fd00b..f1adc6145 100644 --- a/tests/runtime/error_mitigation/test_detector_balancing.py +++ b/tests/runtime/error_mitigation/test_detector_balancing.py @@ -96,4 +96,4 @@ def test_recombination(): res = averaging.parse_results(computation, sub_results, noise) - assert res[KEY_RESULTS]._container == pytest.approx(expected[KEY_RESULTS]._container) + assert res == expected From d5c3ed8a10eceb431f299519129d3059896ab27e Mon Sep 17 00:00:00 2001 From: Benoit Fanchon Date: Wed, 22 Jul 2026 15:36:15 +0200 Subject: [PATCH 3/8] code review --- .../error_mitigation/detector_balancing.py | 52 +++++-------------- perceval/utils/noise_model.py | 30 ++++++++++- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/perceval/runtime/error_mitigation/detector_balancing.py b/perceval/runtime/error_mitigation/detector_balancing.py index 549d262db..64d7723a8 100644 --- a/perceval/runtime/error_mitigation/detector_balancing.py +++ b/perceval/runtime/error_mitigation/detector_balancing.py @@ -39,7 +39,7 @@ class DetectorBalancing(AbstractMitigation): - APPLY_MIN_PHOTONS = True + APPLY_MIN_PHOTONS = False APPLY_LOGICAL_SELECTION = True def __init__(self): @@ -48,50 +48,24 @@ def __init__(self): loss and number of photons in each mode. """ - @staticmethod - def _validate_ratios(ratios: list[float] | None, m: int) -> list[float]: - if ratios is None: - warnings.warn( - "DetectorBalancing was created without specifying loss ratios: " - "defaulting to 1.", - RuntimeWarning, - ) - ratios = [1] * m - - if len(ratios) != m: - raise ValueError( - "Size of detector loss ratios must match raw processor mode count " - f"(expected {m}, got {len(ratios)})." - ) - - def valid(v): - res = math.isfinite(v) and v > 0. - if not res and not valid.warned: - warnings.warn( - "Calibrated detector loss ratios contain non-positive or non-finite " - "values. Replacing invalid entries with 1.0.", - RuntimeWarning, - ) - valid.warned = True - return res - valid.warned = False - - return [v if valid(v) else 1. for v in ratios] - def extend_computation(self, computation: Computation, noise: NoiseModel) -> list[Computation]: comp = deepcopy(computation) - comp.command = "probs" + comp.command.name = "probs" return [comp] def _parse_results(self, computation: Computation, results: list[dict], noise: NoiseModel) -> dict: - ratios = self._validate_ratios(noise.loss_ratios, computation.experiment.m) - - def _balance(k, v): - return math.prod([(1 / ratios[k.photon2mode(i)]) for i in range(k.n)], start = v) + ratios = noise.loss_ratios + if len(ratios) < computation.experiment.m: + warnings.warn( + "Not enough loss ratio for DetectorBalancing: " + "defaulting missing ones to 1.", + RuntimeWarning, + ) + ratios.extend([1.] * (computation.experiment.m - len(ratios))) res = copy(results[0]) # We are going to modify this to keep custom fields as much as we can - assert len(ratios) == computation.experiment.m, "Loss ratios do not match the distribution lengths." + for k in res[KEY_RESULTS].keys(): + res[KEY_RESULTS][k] /= math.prod([ratios[k.photon2mode(i)] for i in range(k.n)]) + res[KEY_RESULTS].normalize() - for k, v in res[KEY_RESULTS].items(): - res[KEY_RESULTS][k] = _balance(k, v) return res diff --git a/perceval/utils/noise_model.py b/perceval/utils/noise_model.py index 1475e4d17..3bc4c8e66 100644 --- a/perceval/utils/noise_model.py +++ b/perceval/utils/noise_model.py @@ -26,6 +26,9 @@ # 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. +import math +import warnings + from ._validated_params import AValidatedParam, ValidatedBool, ValidatedFloat from math import pi @@ -70,7 +73,8 @@ def __init__(self, g2_distinguishable: bool = None, transmittance: float = None, phase_imprecision: float = None, - phase_error: float = None + phase_error: float = None, + loss_ratios: list[float] = None ): self.brightness = brightness self.indistinguishability = indistinguishability @@ -79,6 +83,26 @@ def __init__(self, self.transmittance = transmittance self.phase_imprecision = phase_imprecision self.phase_error = phase_error + self.loss_ratios = loss_ratios + + @property + def loss_ratios(self) -> list[float] | None: + return self._loss_ratios + + @loss_ratios.setter + def loss_ratios(self, values: list[float] | None): + if values: + valids = [math.isfinite(v) and v > 0. for v in values] + if not all(valids): + warnings.warn( + "Calibrated detector loss ratios contain non-positive or non-finite " + "values. Replacing invalid entries with 1.0.", + RuntimeWarning, + ) + self._loss_ratios = [ v if valid else 1. for v, valid in zip(values, valids) ] + #TODO: divide by max(valid_ratios) ? + else: + self._loss_ratios = [] def __deepcopy__(self, memo): return NoiseModel(**self.__dict__()) @@ -97,6 +121,8 @@ def __dict__(self) -> dict: v = getattr(self, attr) if v != cls.__dict__[attr].default_value: res[attr] = v + if self.loss_ratios: + res["loss_ratio"] = self.loss_ratios return res def __eq__(self, other) -> bool: @@ -116,10 +142,12 @@ def perf_dict_to_noise(perfs: dict[str, float]) -> NoiseModel: nm.indistinguishability = perfs[INDISTINGUISHABILITY_KEY] / 100 if G2_KEY in perfs: nm.g2 = perfs[G2_KEY] / 100 + #TODO loss ratios return nm def noise_to_perf_dict(nm: NoiseModel) -> dict: + #TODO loss ratios return { INDISTINGUISHABILITY_KEY: nm.indistinguishability * 100, TRANSMITTANCE_KEY: nm.brightness * nm.transmittance * 100, From e98a875a056b772a3bd556e40b32438a1da53f64 Mon Sep 17 00:00:00 2001 From: Benoit Fanchon Date: Wed, 22 Jul 2026 15:39:23 +0200 Subject: [PATCH 4/8] tests --- .../error_mitigation/detector_balancing.py | 1 + .../test_detector_balancing.py | 42 +++++++++++++------ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/perceval/runtime/error_mitigation/detector_balancing.py b/perceval/runtime/error_mitigation/detector_balancing.py index 64d7723a8..fdbf639a9 100644 --- a/perceval/runtime/error_mitigation/detector_balancing.py +++ b/perceval/runtime/error_mitigation/detector_balancing.py @@ -38,6 +38,7 @@ from perceval.utils.constants import KEY_RESULTS class DetectorBalancing(AbstractMitigation): + # Note: we do not know if it behaves correctly with Feef-Forward APPLY_MIN_PHOTONS = False APPLY_LOGICAL_SELECTION = True diff --git a/tests/runtime/error_mitigation/test_detector_balancing.py b/tests/runtime/error_mitigation/test_detector_balancing.py index f1adc6145..e2e589c02 100644 --- a/tests/runtime/error_mitigation/test_detector_balancing.py +++ b/tests/runtime/error_mitigation/test_detector_balancing.py @@ -36,20 +36,10 @@ from perceval import DetectorBalancing, Computation, CommandFactory, Experiment, NoiseModel, Command, BSCount, \ apply_min_photons, apply_post_select +from perceval.runtime.simulated_computer import SimulatedComputer from perceval.utils.bsdistribution import BSDistribution -from perceval.utils.constants import KEY_SHOTS_USED, KEY_RESULTS - - -def test_computation_extension(): - computation = Computation(CommandFactory.samples, Experiment()) - averaging = DetectorBalancing() - comp_list = averaging.extend_computation(computation, NoiseModel()) - - assert len(comp_list) == 1 - - assert all(comp.command == "probs" for comp in comp_list) - - +from perceval.utils.constants import KEY_SHOTS_USED +from perceval.utils.states import BasicState def prepare_test(): raw_results = BSDistribution({FockState([0, 1]): 1, @@ -84,6 +74,16 @@ def prepare_test(): return expected, sub_results +def test_computation_extension(): + computation = Computation(CommandFactory.samples, Experiment()) + averaging = DetectorBalancing() + comp_list = averaging.extend_computation(computation, NoiseModel()) + + assert len(comp_list) == 1 + + assert all(comp.command.name == "probs" for comp in comp_list) + + def test_recombination(): expected, sub_results = prepare_test() noise = NoiseModel() @@ -97,3 +97,19 @@ def test_recombination(): res = averaging.parse_results(computation, sub_results, noise) assert res == expected + + +def test_run_through(): + computer = SimulatedComputer("SLOS") + computer.mitigations = [ DetectorBalancing() ] + noise = NoiseModel() + noise.loss_ratios = [1., .5] + computer.noise = noise + + e = Experiment(2) + e.min_detected_photons_filter(1) + e.with_input(BasicState([1, 0])) + computation = Computation(CommandFactory.probs, e) + computation.add_params(max_shots = 50000, max_samples = 10000) + + computer.execute(computation) From 77f23f3f4dec83890ff456cd0444a339d7e35b6d Mon Sep 17 00:00:00 2001 From: Benoit Fanchon Date: Thu, 23 Jul 2026 10:16:14 +0200 Subject: [PATCH 5/8] code review --- perceval/runtime/error_mitigation/detector_balancing.py | 6 ++++-- perceval/utils/noise_model.py | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/perceval/runtime/error_mitigation/detector_balancing.py b/perceval/runtime/error_mitigation/detector_balancing.py index fdbf639a9..89814e99a 100644 --- a/perceval/runtime/error_mitigation/detector_balancing.py +++ b/perceval/runtime/error_mitigation/detector_balancing.py @@ -31,6 +31,8 @@ import math import warnings +from perceval.utils.logging import get_logger + from .abstract_mitigation import AbstractMitigation from ..computation import Computation @@ -41,7 +43,7 @@ class DetectorBalancing(AbstractMitigation): # Note: we do not know if it behaves correctly with Feef-Forward APPLY_MIN_PHOTONS = False - APPLY_LOGICAL_SELECTION = True + APPLY_LOGICAL_SELECTION = False def __init__(self): """ @@ -57,7 +59,7 @@ def extend_computation(self, computation: Computation, noise: NoiseModel) -> lis def _parse_results(self, computation: Computation, results: list[dict], noise: NoiseModel) -> dict: ratios = noise.loss_ratios if len(ratios) < computation.experiment.m: - warnings.warn( + get_logger().warnings.warn( "Not enough loss ratio for DetectorBalancing: " "defaulting missing ones to 1.", RuntimeWarning, diff --git a/perceval/utils/noise_model.py b/perceval/utils/noise_model.py index 3bc4c8e66..823cf2ba9 100644 --- a/perceval/utils/noise_model.py +++ b/perceval/utils/noise_model.py @@ -29,6 +29,8 @@ import math import warnings +from perceval.utils.logging import get_logger + from ._validated_params import AValidatedParam, ValidatedBool, ValidatedFloat from math import pi @@ -94,7 +96,7 @@ def loss_ratios(self, values: list[float] | None): if values: valids = [math.isfinite(v) and v > 0. for v in values] if not all(valids): - warnings.warn( + get_logger().warnings.warn( "Calibrated detector loss ratios contain non-positive or non-finite " "values. Replacing invalid entries with 1.0.", RuntimeWarning, From 272f23f92afd464513ef190bcf16e596538e1115 Mon Sep 17 00:00:00 2001 From: Benoit Fanchon Date: Mon, 27 Jul 2026 11:32:32 +0200 Subject: [PATCH 6/8] fix --- perceval/runtime/error_mitigation/detector_balancing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/perceval/runtime/error_mitigation/detector_balancing.py b/perceval/runtime/error_mitigation/detector_balancing.py index 89814e99a..da9d6bb18 100644 --- a/perceval/runtime/error_mitigation/detector_balancing.py +++ b/perceval/runtime/error_mitigation/detector_balancing.py @@ -59,7 +59,7 @@ def extend_computation(self, computation: Computation, noise: NoiseModel) -> lis def _parse_results(self, computation: Computation, results: list[dict], noise: NoiseModel) -> dict: ratios = noise.loss_ratios if len(ratios) < computation.experiment.m: - get_logger().warnings.warn( + get_logger().warn( "Not enough loss ratio for DetectorBalancing: " "defaulting missing ones to 1.", RuntimeWarning, From 5505db9a240f290d049fca1d080f558c2b6ad6a8 Mon Sep 17 00:00:00 2001 From: Benoit Fanchon Date: Mon, 27 Jul 2026 11:33:17 +0200 Subject: [PATCH 7/8] temp --- perceval/runtime/remote_config.py | 2 +- perceval/utils/bsdistribution.py | 2 +- perceval/utils/noise_model.py | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/perceval/runtime/remote_config.py b/perceval/runtime/remote_config.py index c8b703140..42a0a2b63 100644 --- a/perceval/runtime/remote_config.py +++ b/perceval/runtime/remote_config.py @@ -53,7 +53,7 @@ class RemoteConfig: _token = None _url = None - def __init__(self, persistent_data: PersistentData = PersistentData()): + def __init__(self, persistent_data: PersistentData = PersistentData()): # !! default value is evaluated during file load, so test_remote_config_env_var_vs_cache cannot mock it !! self._persistent_data = persistent_data def _get_remote_config(self, key) -> str | dict[str, str] | None: diff --git a/perceval/utils/bsdistribution.py b/perceval/utils/bsdistribution.py index 31b361001..6c595e81c 100644 --- a/perceval/utils/bsdistribution.py +++ b/perceval/utils/bsdistribution.py @@ -36,7 +36,7 @@ import exqalibur -class BSDistribution(): +class BSDistribution(Mapping): """ Basic state distribution holding measured states (i.e. perfect Fock states), of the same size (number of modes). diff --git a/perceval/utils/noise_model.py b/perceval/utils/noise_model.py index 823cf2ba9..39aca3f14 100644 --- a/perceval/utils/noise_model.py +++ b/perceval/utils/noise_model.py @@ -77,6 +77,8 @@ def __init__(self, phase_imprecision: float = None, phase_error: float = None, loss_ratios: list[float] = None + , transmitance_ratios_input: list[float] = None + , transmitance_ratios_output: list[float] = None ): self.brightness = brightness self.indistinguishability = indistinguishability @@ -96,7 +98,7 @@ def loss_ratios(self, values: list[float] | None): if values: valids = [math.isfinite(v) and v > 0. for v in values] if not all(valids): - get_logger().warnings.warn( + get_logger().warn( "Calibrated detector loss ratios contain non-positive or non-finite " "values. Replacing invalid entries with 1.0.", RuntimeWarning, From 0356b8c8bf4cee84841cf74e3875179699cb026c Mon Sep 17 00:00:00 2001 From: Benoit Fanchon Date: Tue, 28 Jul 2026 15:33:05 +0200 Subject: [PATCH 8/8] fix --- perceval/runtime/error_mitigation/detector_balancing.py | 3 +-- perceval/utils/noise_model.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/perceval/runtime/error_mitigation/detector_balancing.py b/perceval/runtime/error_mitigation/detector_balancing.py index da9d6bb18..3db4350c6 100644 --- a/perceval/runtime/error_mitigation/detector_balancing.py +++ b/perceval/runtime/error_mitigation/detector_balancing.py @@ -61,8 +61,7 @@ def _parse_results(self, computation: Computation, results: list[dict], noise: N if len(ratios) < computation.experiment.m: get_logger().warn( "Not enough loss ratio for DetectorBalancing: " - "defaulting missing ones to 1.", - RuntimeWarning, + "defaulting missing ones to 1." ) ratios.extend([1.] * (computation.experiment.m - len(ratios))) diff --git a/perceval/utils/noise_model.py b/perceval/utils/noise_model.py index 39aca3f14..badfbc004 100644 --- a/perceval/utils/noise_model.py +++ b/perceval/utils/noise_model.py @@ -100,8 +100,7 @@ def loss_ratios(self, values: list[float] | None): if not all(valids): get_logger().warn( "Calibrated detector loss ratios contain non-positive or non-finite " - "values. Replacing invalid entries with 1.0.", - RuntimeWarning, + "values. Replacing invalid entries with 1.0." ) self._loss_ratios = [ v if valid else 1. for v, valid in zip(values, valids) ] #TODO: divide by max(valid_ratios) ?