You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

cache.py 3.7 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. # Python module wrapper for _functools C module
  2. # to allow utilities written in Python to be added
  3. # to the functools module.
  4. # Written by Nick Coghlan <ncoghlan at gmail.com>,
  5. # Raymond Hettinger <python at rcn.com>,
  6. # and Łukasz Langa <lukasz at langa.pl>.
  7. # Copyright (C) 2006-2013 Python Software Foundation.
  8. # See C source code for _functools credits/copyright
  9. # Modified from
  10. # https://github.com/python/cpython/blob/3.12/Lib/functools.py
  11. from typing import Callable, Generic, TypeVar
  12. K = TypeVar("K")
  13. T = TypeVar("T")
  14. PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields
  15. class Cache(Generic[K, T]):
  16. def __init__(self, func: Callable[[K], T]):
  17. """Create cache
  18. :param func: Function this cache evaluates
  19. :param cache: If true, do in memory caching.
  20. :param cache_root: If not None, cache to files at the provided path.
  21. :param key_func: Convert the key into a hashable object if needed
  22. """
  23. self.func = func
  24. self.has_init = False
  25. def __getitem__(self, obj, *args) -> T:
  26. return self.get_from_dict(obj, *args)
  27. def clear_cache(self):
  28. """Invalidate entire cache."""
  29. self.cache_dict.clear()
  30. def _init_cache(self, obj):
  31. if self.has_init:
  32. return
  33. self.cache = True
  34. self.cache_dict = dict()
  35. self.key_func = obj.key_func
  36. self.max_size = obj.cache_size
  37. self.hits, self.misses = 0, 0
  38. self.full = False
  39. self.root = [] # root of the circular doubly linked list
  40. self.root[:] = [self.root, self.root, None, None]
  41. self.has_init = True
  42. def get_from_dict(self, obj, *args) -> T:
  43. """Implements dict based cache."""
  44. # x is not used in cache key
  45. pred_pseudo_label, y, x, *res_args = args
  46. cache_key = (self.key_func(pred_pseudo_label), self.key_func(y), *res_args)
  47. link = self.cache_dict.get(cache_key)
  48. if link is not None:
  49. # Move the link to the front of the circular queue
  50. link_prev, link_next, _key, result = link
  51. link_prev[NEXT] = link_next
  52. link_next[PREV] = link_prev
  53. last = self.root[PREV]
  54. last[NEXT] = self.root[PREV] = link
  55. link[PREV] = last
  56. link[NEXT] = self.root
  57. self.hits += 1
  58. return result
  59. self.misses += 1
  60. result = self.func(obj, *args)
  61. if self.full:
  62. # Use the old root to store the new key and result.
  63. oldroot = self.root
  64. oldroot[KEY] = cache_key
  65. oldroot[RESULT] = result
  66. # Empty the oldest link and make it the new root.
  67. self.root = oldroot[NEXT]
  68. oldkey = self.root[KEY]
  69. self.root[KEY] = self.root[RESULT] = None
  70. # Now update the cache dictionary.
  71. del self.cache_dict[oldkey]
  72. self.cache_dict[cache_key] = oldroot
  73. else:
  74. # Put result in a new link at the front of the queue.
  75. last = self.root[PREV]
  76. link = [last, self.root, cache_key, result]
  77. last[NEXT] = self.root[PREV] = self.cache_dict[cache_key] = link
  78. if isinstance(self.max_size, int):
  79. self.full = len(self.cache_dict) >= self.max_size
  80. return result
  81. def abl_cache():
  82. def decorator(func):
  83. cache_instance = Cache(func)
  84. def wrapper(obj, *args):
  85. if obj.use_cache:
  86. cache_instance._init_cache(obj)
  87. return cache_instance.get_from_dict(obj, *args)
  88. else:
  89. return func(obj, *args)
  90. return wrapper
  91. return decorator

An efficient Python toolkit for Abductive Learning (ABL), a novel paradigm that integrates machine learning and logical reasoning in a unified framework.