Source code for pyzeal.algorithms.estimators.estimator_cache

"""
This module provides a simple cache to store and retrieve intermediate
results during computation.

Authors:\n
- Philipp Schuette
"""

from logging import DEBUG
from typing import Dict, Optional, Tuple

from pyzeal.pyzeal_logging.loggable import Loggable


[docs]class EstimatorCache(Loggable): """ A simple in-memory cache that can store and retrieve total argument changes along horizontal and vertical lines in the complex plane. """ __slots__ = ("_cache", "cacheHits", "cacheMisses")
[docs] def __init__(self) -> None: """ Initializes a new `EstimatorCache`. """ self._cache: Dict[int, Dict[Tuple[complex, complex], complex]] = {} self.logger.info("initialized a new argument estimator cache...") # cache hits and misses are only recorded if logging is set to DEBUG self.cacheHits = 0 self.cacheMisses = 0
[docs] def store( self, order: int, zStart: complex, zEnd: complex, argument: complex, ) -> None: """ Store the total argument change associated with a horizontally or vertically oriented range of complex numbers. :param order: Order of the moment to be stored :param zStart: Starting point of the line :param zEnd: End point of the line :param argument: Total argument change """ if (orderCache := self._cache.get(order, None)) is None: self._cache[order] = {(zStart, zEnd): argument} else: orderCache[(zStart, zEnd)] = argument self.logger.debug( "stored value %s under key (%s, %d) in estimator cache!", str(argument), str((order, (zStart, zEnd))), order, )
[docs] def retrieve( self, order: int, zStart: complex, zEnd: complex, ) -> Optional[complex]: """ Retrieve the total argument change associated with a horizontally or vertically oriented range of complex numbers. Returns `None` if the requested entry is not present. :param order: Order of the moment to be retrieved :param zStart: Starting point of the line :param zEnd: End point of the line :return: Total argument change if the cache contains a value, else None is returned. """ if (orderCache := self._cache.get(order, None)) is None: value = None else: value = orderCache.get((zStart, zEnd), None) self.logger.debug( "retrieved value %s under key %s from estimator cache!", str(value), str((zStart, zEnd)), ) if self.logger.isEnabledFor(DEBUG): self.cacheHits += 1 if value else 0 self.cacheMisses += 1 if not value else 0 return value
[docs] def remove( self, order: int, zStart: complex, zEnd: complex, ) -> None: """ Remove the total argument change associated with a horizontally or vertically oriented range of complex numbers. :param order: Order of the moment to remove :param zStart: Starting point of the line :param zEnd: End point of the line """ if order in self._cache: value = self._cache[order].pop((zStart, zEnd), None) self.logger.debug( "removed value %s under key %s from estimator cache!", str(value), str((zStart, zEnd)), )
[docs] def dirty(self) -> bool: """ Returns `True` if the cache contains anything. :return: `True` if the cache contains anything. """ return len(self._cache) > 0
[docs] def reset(self) -> None: """ Resets the cache by clearing all stored values and resetting the hit and miss counters. """ self._cache.clear() self.cacheHits = 0 self.cacheMisses = 0