diff options
Diffstat (limited to 'resources/libraries/python/jumpavg/BitCountingGroup.py')
-rw-r--r-- | resources/libraries/python/jumpavg/BitCountingGroup.py | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/resources/libraries/python/jumpavg/BitCountingGroup.py b/resources/libraries/python/jumpavg/BitCountingGroup.py new file mode 100644 index 0000000000..0c1aabba30 --- /dev/null +++ b/resources/libraries/python/jumpavg/BitCountingGroup.py @@ -0,0 +1,173 @@ +# Copyright (c) 2019 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 holding BitCountingGroup class.""" + +import copy + +from .AvgStdevStats import AvgStdevStats +from .BitCountingStats import BitCountingStats + + +class BitCountingGroup: + # TODO: Inherit from collections.abc.Sequence in Python 3. + """Group of runs which tracks bit count in an efficient manner. + + This class contains methods that mutate the internal state, + use copy() method to save the previous state. + + The Sequence-like access is related to the list of runs, + for example group[0] returns the first run in the list. + Writable list-like methods are not implemented. + + As the group bit count depends on previous average + and overall maximal value, those values are assumed + to be known beforehand (and immutable). + + As the caller is allowed to divide runs into groups in any way, + a method to add a single run in an efficient manner is provided. + """ + + def __init__(self, run_list=None, stats=None, bits=None, + max_value=None, prev_avg=None, comment="unknown"): + """Set the internal state and partially the stats. + + A "group" stands for an Iterable of runs, where "run" is either + a float value, or a stats-like object (only size, avg and stdev + are accessed). Run is a hypothetical abstract class, + defining it in Python 2 is too much hassle. + + Only a copy of the run list argument value is stored in the instance, + so it is not a problem if the value object is mutated afterwards. + + It is not verified whether the user provided values are valid, + e.g. whether the stats and bits values reflect the runs. + + :param run_list: List of run to compose into this group. Default: empty. + :param stats: Stats object used for computing bits. + :param bits: Cached value of information content. + :param max_value: Maximal sample value to be used for computing. + :param prev_avg: Average of the previous group, affects bits. + :param comment: Any string giving more info, e.g. "regression". + :type run_list: Iterable[Run] + :type stats: Optional[AvgStdevStats] + :type bits: Optional[float] + :type max_value: float + :type prev_avg: Optional[float] + :type comment: str + """ + self.run_list = copy.deepcopy(run_list) if run_list else list() + self.stats = stats + self.cached_bits = bits + self.max_value = max_value + self.prev_avg = prev_avg + self.comment = comment + if self.stats is None: + self.stats = AvgStdevStats.for_runs(self.run_list) + + def __str__(self): + """Return string with human readable description of the group. + + :returns: Readable description. + :rtype: str + """ + return f"stats={self.stats} bits={self.cached_bits}" + + def __repr__(self): + """Return string executable as Python constructor call. + + :returns: Executable constructor call. + :rtype: str + """ + return ( + f"BitCountingGroup(run_list={self.run_list!r},stats={self.stats!r}" + f",bits={self.cached_bits!r},max_value={self.max_value!r}" + f",prev_avg={self.prev_avg!r},comment={self.comment!r})" + ) + + def __getitem__(self, index): + """Return the run at the index. + + :param index: Index of the run to return. + :type index: int + :returns: The run at the index. + :rtype: Run + """ + return self.run_list[index] + + def __len__(self): + """Return the number of runs in the group. + + :returns: The Length of run_list. + :rtype: int + """ + return len(self.run_list) + + def copy(self): + """Return a new instance with copied internal state. + + :returns: The copied instance. + :rtype: BitCountingGroup + """ + stats = AvgStdevStats.for_runs([self.stats]) + return self.__class__( + run_list=self.run_list, stats=stats, bits=self.cached_bits, + max_value=self.max_value, prev_avg=self.prev_avg, + comment=self.comment) + + @property + def bits(self): + """Return overall bit content of the group list. + + If not cached, compute from stats and cache. + + :returns: The overall information content in bits. + :rtype: float + """ + if self.cached_bits is None: + self.cached_bits = BitCountingStats.for_runs( + [self.stats], self.max_value, self.prev_avg).bits + return self.cached_bits + + def append(self, run): + """Mutate to add the new run, return self. + + Stats are updated, but old bits value is deleted from cache. + + :param run: The run value to add to the group. + :type value: Run + :returns: The updated self. + :rtype: BitCountingGroup + """ + self.run_list.append(run) + self.stats = AvgStdevStats.for_runs([self.stats, run]) + self.cached_bits = None + return self + + def extend(self, runs): + """Mutate to add the new runs, return self. + + This is saves small amount of computation + compared to adding runs one by one in a loop. + + Stats are updated, but old bits value is deleted from cache. + + :param runs: The runs to add to the group. + :type value: Iterable[Run] + :returns: The updated self. + :rtype: BitCountingGroup + """ + self.run_list.extend(runs) + self.stats = AvgStdevStats.for_runs([self.stats] + runs) + self.cached_bits = None + return self |