From fa3de8e8cef73300243f4b6e6928b41255bf4a1a Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 10 Jan 2024 11:23:35 +0400 Subject: [PATCH] refactor: Implement suggestion from @AleCandido * Introduce `Outcome` type * Generalization validation beyond chi2 * Remove comments from tests --- src/qibocal/auto/validation.py | 32 +++++++++++++------------- src/qibocal/auto/validators/chi2.py | 18 ++++++++------- tests/test_task_options.py | 35 ++++++++++++++++------------- 3 files changed, 44 insertions(+), 41 deletions(-) diff --git a/src/qibocal/auto/validation.py b/src/qibocal/auto/validation.py index 4315a8a0e..5e4976dec 100644 --- a/src/qibocal/auto/validation.py +++ b/src/qibocal/auto/validation.py @@ -11,9 +11,10 @@ ValidatorId = NewType("ValidatorId", str) """Identifier for validator object.""" - Target = Union[QubitId, QubitPairId, list[QubitId]] """Protocol target.""" +Outcome = tuple[str, Optional[dict]] +"""Validation outcome tuple of nodes with parameters or Status.""" @dataclass @@ -24,7 +25,7 @@ class Validator: """Validator present in validators module.""" parameters: Optional[dict] = field(default_factory=dict) """"Validator parameters.""" - outcomes: Optional[list[tuple[str, dict]]] = field(default_factory=list) + outcomes: Optional[list[Outcome]] = field(default_factory=list) """Depending on the validation we jump into one of the possible nodes.""" # TODO: think of a better name @@ -38,7 +39,7 @@ def method(self) -> Callable[[Results, Target], Union[Status, str]]: def validate( self, results: Results, target: Target - ) -> tuple[Union[Status, str], Optional[dict]]: + ) -> Union[Outcome, tuple[Status, None]]: """Perform validation of target in results. Possible Returns are: @@ -47,18 +48,15 @@ def validate( - (task, dict) which moves the head to task using parameters in dict. """ index = self.method(results=results, target=target, **self.parameters) - # If index is None -> status is Failure - # if index is 0 -> Normal Status - # else: jump to corresponding outcomes - if index is None: - log.error("Stopping execution due to error in validation.") - return Failure(), None - elif index == 0: - # for chi2 (to be generalized for other validators): - # if chi2 is less than first threshold the status is normal - return Normal(), None - # else we return outcomes [index-1] since outcomes outcomes[i] is - # the output of thresholds[index+1], given that for the first threshold - # the status is Normal. - return self.outcomes[index - 1] + if index == -1: + # -1 denotes Normal() + return Normal(), None + else: + try: + return self.outcomes[index] + except (TypeError, IndexError): + # TypeError to handle the case where index is None + # IndexError to handle the case where index not in outcomes + log.error("Stopping execution due to error in validation.") + return Failure(), None diff --git a/src/qibocal/auto/validators/chi2.py b/src/qibocal/auto/validators/chi2.py index 9ff645878..b08857abb 100644 --- a/src/qibocal/auto/validators/chi2.py +++ b/src/qibocal/auto/validators/chi2.py @@ -1,6 +1,7 @@ """Chi2 validation""" -from typing import Union +from typing import Optional, Union +import numpy as np from qibolab.qubits import QubitId, QubitPairId from qibocal.config import log @@ -14,20 +15,21 @@ def check_chi2( results: Results, target: Union[QubitId, QubitPairId, list[QubitId]], - thresholds: list = [CHI2_MAX], -): + thresholds: Optional[list] = None, +) -> Optional[float]: """Performs validation of results using chi2. - It assesses that chi2 is below the chi2_max_value threshold. + Find the threshold of the chi2 among thresholds. """ + if thresholds is None: + thresholds = [CHI2_MAX] + try: chi2 = results.chi2[target][0] - for threshold in sorted(thresholds): - if chi2 < threshold: - return thresholds.index(threshold) + idx = np.searchsorted(thresholds, chi2) + return idx - 1 - return None except AttributeError: log.error(f"Chi2 validation not available for {type(results)}") return None diff --git a/tests/test_task_options.py b/tests/test_task_options.py index 744ed416f..37298cfc0 100644 --- a/tests/test_task_options.py +++ b/tests/test_task_options.py @@ -7,6 +7,7 @@ from qibocal.auto.execute import Executor from qibocal.auto.operation import DEFAULT_PARENT_PARAMETERS from qibocal.auto.runcard import Runcard +from qibocal.auto.task import Task from qibocal.cli.report import ExecutionMode from qibocal.protocols.characterization.classification import ( SingleShotClassificationParameters, @@ -14,10 +15,12 @@ from qibocal.protocols.characterization.readout_mitigation_matrix import ( ReadoutMitigationMatrixParameters, ) +from qibocal.utils import allocate_single_qubits PLATFORM = create_platform("dummy") QUBITS = list(PLATFORM.qubits) DUMMY_CARD = { + "backend": "numpy", "qubits": QUBITS, "actions": [ { @@ -44,22 +47,22 @@ def modify_card(card, qubits=None, update=None): return card -# @pytest.mark.parametrize("platform", [None, PLATFORM]) -# @pytest.mark.parametrize("local_qubits", [[], [0, 1]]) -# def test_qubits_argument(platform, local_qubits): -# """Test possible qubits combinations between global and local.""" -# runcard = Runcard.load(modify_card(DUMMY_CARD, qubits=local_qubits)) -# task = Task(runcard.actions[0]) -# global_qubits = ( -# allocate_single_qubits(platform, QUBITS) if platform is not None else QUBITS -# ) -# task._allocate_local_qubits(global_qubits, platform) - -# _, _ = task.operation.acquisition(task.parameters, platform, global_qubits) -# if local_qubits: -# assert task.qubits == local_qubits -# else: -# assert task.qubits == QUBITS +@pytest.mark.parametrize("platform", [None, PLATFORM]) +@pytest.mark.parametrize("local_qubits", [[], [0, 1]]) +def test_qubits_argument(platform, local_qubits): + """Test possible qubits combinations between global and local.""" + runcard = Runcard.load(modify_card(DUMMY_CARD, qubits=local_qubits)) + task = Task(runcard.actions[0]) + global_qubits = ( + allocate_single_qubits(platform, QUBITS) if platform is not None else QUBITS + ) + task._allocate_local_qubits(global_qubits, platform) + + _, _ = task.operation.acquisition(task.parameters, platform, global_qubits) + if local_qubits: + assert task.qubits == local_qubits + else: + assert task.qubits == QUBITS UPDATE_CARD = {