aboutsummaryrefslogtreecommitdiffstats
path: root/resources/libraries/python/MLRsearch/trimmed_stat.py
blob: 088e8beaf8649b30711d6fc3d79fbd30938c5ce3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# Copyright (c) 2023 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Module defining TrimmedStat class."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Optional

from .discrete_load import DiscreteLoad
from .load_stats import LoadStats
from .target_spec import TargetSpec


@dataclass
class TrimmedStat(LoadStats):
    """Load stats trimmed to a single target.

    Useful mainly for reporting the overall results.
    """

    def __post_init__(self) -> None:
        """Initialize load value and check there is one target to track."""
        super().__post_init__()
        if len(self.target_to_stat) != 1:
            raise ValueError(f"No single target: {self.target_to_stat!r}")

    @staticmethod
    def for_target(stats: LoadStats, target: TargetSpec) -> TrimmedStat:
        """Return new instance with only one target in the mapping.

        :param stats: The load stats instance to trim.
        :param target: The one target which should remain in the mapping.
        :type stats: LoadStats
        :type target: TargetSpec
        :return: Newly created instance.
        :rtype: TrimmedStat
        """
        return TrimmedStat(
            rounding=stats.rounding,
            int_load=stats.int_load,
            target_to_stat={target: stats.target_to_stat[target]},
        )

    @property
    def conditional_throughput(self) -> Optional[DiscreteLoad]:
        """Compute conditional throughput from the load.

        The conditional throughput has the same semantics as load,
        so if load is unicirectional and user wants bidirectional
        throughput, the manager has to compensate.

        This only works correctly if the self load is a lower bound
        for the self target, but this method does not check that.
        Its should not matter, as MLRsearch controller only returns
        the relevant lower bounds to the manager.

        :return: Conditional throughput assuming self is a relevant lower bound.
        :rtype: Optional[DiscreteLoad]
        :raises RuntimeError: If target is unclear or load is spurious.
        """
        target = list(self.target_to_stat.keys())[0]
        stat = self.target_to_stat[target]
        loss_ratio = stat.pessimistic_loss_ratio()
        ret = self * (1.0 - loss_ratio)
        return ret