# Path: pyphi/cache.py
# def cache(cache={}, maxmem=config.MAXIMUM_CACHE_MEMORY_PERCENTAGE, typed=False):
#     """Memory-limited cache decorator.
# 
#     ``maxmem`` is a float between 0 and 100, inclusive, specifying the maximum
#     percentage of physical memory that the cache can use.
# 
#     If ``typed`` is ``True``, arguments of different types will be cached
#     separately. For example, f(3.0) and f(3) will be treated as distinct calls
#     with distinct results.
# 
#     Arguments to the cached function must be hashable.
# 
#     View the cache statistics named tuple (hits, misses, currsize)
#     with f.cache_info(). Clear the cache and statistics with f.cache_clear().
#     Access the underlying function with f.__wrapped__.
#     """
#     # Constants shared by all lru cache instances:
#     # Unique object used to signal cache misses.
#     sentinel = object()
#     # Build a key from the function arguments.
#     make_key = _make_key
# 
#     def decorating_function(user_function, hits=0, misses=0):
#         full = False
#         # Bound method to look up a key or return None.
#         cache_get = cache.get
# 
#         if not maxmem:
# 
#             def wrapper(*args, **kwds):
#                 # Simple caching without memory limit.
#                 nonlocal hits, misses
#                 key = make_key(args, kwds, typed)
#                 result = cache_get(key, sentinel)
#                 if result is not sentinel:
#                     hits += 1
#                     return result
#                 result = user_function(*args, **kwds)
#                 cache[key] = result
#                 misses += 1
#                 return result
# 
#         else:
# 
#             def wrapper(*args, **kwds):
#                 # Memory-limited caching.
#                 nonlocal hits, misses, full
#                 key = make_key(args, kwds, typed)
#                 result = cache_get(key)
#                 if result is not None:
#                     hits += 1
#                     return result
#                 result = user_function(*args, **kwds)
#                 if not full:
#                     cache[key] = result
#                     # Cache is full if the total recursive usage is greater
#                     # than the maximum allowed percentage.
#                     current_process = psutil.Process(os.getpid())
#                     full = current_process.memory_percent() > maxmem
#                 misses += 1
#                 return result
# 
#         def cache_info():
#             """Report cache statistics."""
#             return _CacheInfo(hits, misses, len(cache))
# 
#         def cache_clear():
#             """Clear the cache and cache statistics."""
#             nonlocal hits, misses, full
#             cache.clear()
#             hits = misses = 0
#             full = False
# 
#         wrapper.cache_info = cache_info
#         wrapper.cache_clear = cache_clear
#         return update_wrapper(wrapper, user_function)
# 
#     return decorating_function


import json
import numpy as np
import pyphi
from pyphi import cache
    def __init__(self, *args, **kwargs):
        kwargs["object_hook"] = self._load_object
        super().__init__(*args, **kwargs)

        # Memoize available models
        self._models = _loadable_models()

        # Cache for loaded objects
        self._object_cache = _ObjectCache()

    def _load_object(self, obj):
        """Recursively load a PyPhi object.

        PyPhi models are recursively loaded, using the model metadata to
        recreate the original object relations. Lists are cast to tuples
        because most objects in PyPhi which are serialized to lists (eg.
        mechanisms and purviews) are ultimately tuples. Other lists (tpms,
        repertoires) should be cast to the correct type in init methods.
        """
        if isinstance(obj, dict):
            obj = {k: self._load_object(v) for k, v in obj.items()}
            # Load a serialized PyPhi model
            if _is_model(obj):
                return self._load_model(obj)

        elif isinstance(obj, list):
            return tuple(self._load_object(item) for item in obj)

        return obj


# Path: pyphi/cache.py
# def cache(cache={}, maxmem=config.MAXIMUM_CACHE_MEMORY_PERCENTAGE, typed=False):
#     """Memory-limited cache decorator.
# 
#     ``maxmem`` is a float between 0 and 100, inclusive, specifying the maximum
#     percentage of physical memory that the cache can use.
# 
#     If ``typed`` is ``True``, arguments of different types will be cached
#     separately. For example, f(3.0) and f(3) will be treated as distinct calls
#     with distinct results.
# 
#     Arguments to the cached function must be hashable.
# 
#     View the cache statistics named tuple (hits, misses, currsize)
#     with f.cache_info(). Clear the cache and statistics with f.cache_clear().
#     Access the underlying function with f.__wrapped__.
#     """
#     # Constants shared by all lru cache instances:
#     # Unique object used to signal cache misses.
#     sentinel = object()
#     # Build a key from the function arguments.
#     make_key = _make_key
# 
#     def decorating_function(user_function, hits=0, misses=0):
#         full = False
#         # Bound method to look up a key or return None.
#         cache_get = cache.get
# 
#         if not maxmem:
# 
#             def wrapper(*args, **kwds):
#                 # Simple caching without memory limit.
#                 nonlocal hits, misses
#                 key = make_key(args, kwds, typed)
#                 result = cache_get(key, sentinel)
#                 if result is not sentinel:
#                     hits += 1
#                     return result
#                 result = user_function(*args, **kwds)
#                 cache[key] = result
#                 misses += 1
#                 return result
# 
#         else:
# 
#             def wrapper(*args, **kwds):
#                 # Memory-limited caching.
#                 nonlocal hits, misses, full
#                 key = make_key(args, kwds, typed)
#                 result = cache_get(key)
#                 if result is not None:
#                     hits += 1
#                     return result
#                 result = user_function(*args, **kwds)
#                 if not full:
#                     cache[key] = result
#                     # Cache is full if the total recursive usage is greater
#                     # than the maximum allowed percentage.
#                     current_process = psutil.Process(os.getpid())
#                     full = current_process.memory_percent() > maxmem
#                 misses += 1
#                 return result
# 
#         def cache_info():
#             """Report cache statistics."""
#             return _CacheInfo(hits, misses, len(cache))
# 
#         def cache_clear():
#             """Clear the cache and cache statistics."""
#             nonlocal hits, misses, full
#             cache.clear()
#             hits = misses = 0
#             full = False
# 
#         wrapper.cache_info = cache_info
#         wrapper.cache_clear = cache_clear
#         return update_wrapper(wrapper, user_function)
# 
#     return decorating_function


# Path: pyphi/jsonify.py
import json
import numpy as np
import pyphi
from pyphi import cache

    def __init__(self, *args, **kwargs):
        kwargs["object_hook"] = self._load_object
        super().__init__(*args, **kwargs)

        # Memoize available models
        self._models = _loadable_models()

        # Cache for loaded objects
        self._object_cache = _ObjectCache()

    def _load_object(self, obj):
        """Recursively load a PyPhi object.

        PyPhi models are recursively loaded, using the model metadata to
        recreate the original object relations. Lists are cast to tuples
        because most objects in PyPhi which are serialized to lists (eg.
        mechanisms and purviews) are ultimately tuples. Other lists (tpms,
        repertoires) should be cast to the correct type in init methods.
        """
        if isinstance(obj, dict):
            obj = {k: self._load_object(v) for k, v in obj.items()}
            # Load a serialized PyPhi model
            if _is_model(obj):
                return self._load_model(obj)

        elif isinstance(obj, list):
            return tuple(self._load_object(item) for item in obj)

        return obj


    @cache.method("_object_cache")