Skip to content

Commit

Permalink
refactor: Implement suggestion from @alecandido
Browse files Browse the repository at this point in the history
* Introduce `Outcome` type
* Generalization validation beyond chi2
* Remove comments from tests
  • Loading branch information
andrea-pasquale committed Jan 10, 2024
1 parent 9f62bfb commit fa3de8e
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 41 deletions.
32 changes: 15 additions & 17 deletions src/qibocal/auto/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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
18 changes: 10 additions & 8 deletions src/qibocal/auto/validators/chi2.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
35 changes: 19 additions & 16 deletions tests/test_task_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@
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,
)
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": [
{
Expand All @@ -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 = {
Expand Down

0 comments on commit fa3de8e

Please sign in to comment.