X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=resources%2Flibraries%2Fpython%2FMLRsearch%2Fgoal_result.py;fp=resources%2Flibraries%2Fpython%2FMLRsearch%2Fgoal_result.py;h=91dccec0bbfc24a244d4b66aa9262db534599cc6;hb=8993ddb4f38f2754ae3af1c61e69a2e747f32a67;hp=0000000000000000000000000000000000000000;hpb=ee28e8ae476c6b0c098cd3895c586316feb4bdb9;p=csit.git diff --git a/resources/libraries/python/MLRsearch/goal_result.py b/resources/libraries/python/MLRsearch/goal_result.py new file mode 100644 index 0000000000..91dccec0bb --- /dev/null +++ b/resources/libraries/python/MLRsearch/goal_result.py @@ -0,0 +1,72 @@ +# 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 GoalResult class.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Optional + +from .discrete_load import DiscreteLoad +from .relevant_bounds import RelevantBounds +from .trimmed_stat import TrimmedStat + + +@dataclass +class GoalResult: + """Composite to be mapped for each search goal at the end of the search. + + The values are stored as trimmed stats, + the conditional throughput is returned as a discrete loads. + Thus, users interested only in float values have to convert explicitly. + + Irregular goal results are supported as instances with a bound missing. + """ + + relevant_lower_bound: Optional[TrimmedStat] + """The relevant lower bound for the search goal.""" + relevant_upper_bound: Optional[TrimmedStat] + """The relevant lower upper for the search goal.""" + + @staticmethod + def from_bounds(bounds: RelevantBounds) -> GoalResult: + """Factory, so that the call site can be shorter. + + :param bounds: The relevant bounds as found in measurement database. + :type bounds: RelevantBounds + :returns: Newly created instance based on the bounds. + :rtype: GoalResult + """ + return GoalResult( + relevant_lower_bound=bounds.clo, + relevant_upper_bound=bounds.chi, + ) + + @property + def conditional_throughput(self) -> Optional[DiscreteLoad]: + """Compute conditional throughput from the relevant lower bound. + + If the relevant lower bound is missing, None is returned. + + The conditional throughput has the same semantics as load, + so if load is unidirectional and user wants bidirectional + throughput, the manager has to compensate. + + :return: Conditional throughput at the relevant lower bound. + :rtype: Optional[DiscreteLoad] + """ + if not (rlb := self.relevant_lower_bound): + return None + stat = next(iter(rlb.target_to_stat.values())) + return rlb * (1.0 - stat.pessimistic_loss_ratio)