qlat_utils.cache — Hierarchical In-Memory Cache

Source: qlat-utils/qlat_utils/cache.py

Note: Update this document when updating the source file.

Outline

  1. Overview

  2. Cache Class

  3. Global Cache & Management Functions

  4. Examples


Overview

The qlat_utils.cache module provides a hierarchical, dict-based caching system built on the Cache class (a dict subclass). Each Cache node carries a cache_keys attribute recording its path from the root, enabling introspection and targeted cleanup.

A module-level root cache object serves as the top of the hierarchy. Sub-caches are created and retrieved with mk_cache. Values can be cleaned recursively without destroying structure, and the entire Python + C++ cache stack can be cleared with clear_all_caches.


Cache Class

class Cache(dict)

A dict subclass that records a hierarchical key path.

Attribute

Type

Description

cache_keys

tuple[str, ...]

Chain of keys from the root cache to this node

Cache behaves exactly like a regular dict for item access (__getitem__, __setitem__, pop, items, etc.). The cache_keys tuple is purely informational and used by management functions for logging.


Global Cache & Management Functions

cache

The module-level root Cache() instance. All sub-caches are descendants of this object.

mk_cache(*keys, ca=cache)

Create a nested chain of Cache nodes under ca for each key in keys. If a sub-cache already exists at any level, it is reused (not recreated).

Returns the innermost Cache node.

Parameter

Type

Default

Description

*keys

str

Hierarchical key path

ca

Cache

cache

Parent cache to search/create under

clean_cache(ca=cache)

Recursively remove all leaf values (non-Cache entries) from ca and its sub-caches, preserving the Cache structure itself. Timed with @timer.

rm_cache(*keys, ca=cache)

Remove the sub-cache identified by keys from ca if it exists. Timed with @timer.

list_cache(ca=cache)

Return a nested dict mirroring the cache hierarchy. Leaf values are omitted; only Cache sub-nodes are included. Timed with @timer.

show_cache_keys(keys)

Return a human-readable string representation of a cache_keys tuple, e.g. "['fields_io']['gauge_field']".

clear_all_caches()

Clean the Python-level cache (clean_cache + cache.clear()) and then clear the C+±level cache via c.clear_all_caches(). Timed with @timer.


Examples

Creating and Using a Sub-Cache

import qlat_utils as q

# Create a sub-cache for field I/O handles
cache_fields_io = q.mk_cache("fields_io")

# Store a value keyed by object id
my_obj = object()
cache_fields_io[id(my_obj)] = ("fsel_data", "sbs_data")

# Retrieve
assert id(my_obj) in cache_fields_io
c_fsel, c_sbs = cache_fields_io[id(my_obj)]
assert c_fsel == "fsel_data"
assert c_sbs == "sbs_data"

# Remove
cache_fields_io.pop(id(my_obj), None)
assert id(my_obj) not in cache_fields_io

Hierarchical Sub-Caches

import qlat_utils as q

cache_gauge = q.mk_cache("fields_io", "gauge_field")
# Equivalent to:
#   cache["fields_io"]["gauge_field"]
# created step by step, reusing existing nodes.

# Store and retrieve a value
cache_gauge["config_100"] = {"beta": 6.0, "L": 8}
assert cache_gauge["config_100"]["beta"] == 6.0

# mk_cache reuses existing nodes (does not overwrite)
cache_gauge2 = q.mk_cache("fields_io", "gauge_field")
assert cache_gauge is cache_gauge2

Removing a Sub-Cache

import qlat_utils as q

# Set up a nested cache
q.mk_cache("fields_io", "gauge_field")
q.mk_cache("fields_io", "prop_field")

# Remove one sub-cache (keeps sibling)
q.rm_cache("fields_io", "gauge_field")

# Verify it is gone
assert "gauge_field" not in q.cache.get("fields_io", {})
# Sibling still exists
assert "prop_field" in q.cache["fields_io"]

Cleaning Values (Preserving Structure)

import qlat_utils as q

# Set up cache with values
cache_io = q.mk_cache("fields_io")
cache_io["key_a"] = "value_a"
cache_io["key_b"] = "value_b"

# clean_cache removes leaf values but keeps Cache sub-nodes
q.clean_cache()
assert len(cache_io) == 0
# The Cache node itself still exists in the hierarchy
assert "fields_io" in q.cache

Clearing All Caches

import qlat_utils as q

# Set up some caches with values
cache_io = q.mk_cache("fields_io")
cache_io["key"] = "value"

# clear_all_caches clears both Python-level and C++-level caches
q.clear_all_caches()

# Python cache is empty after clearing
assert len(q.cache) == 0

Inspecting the Cache

import qlat_utils as q

# Create some nested caches
q.mk_cache("fields_io", "gauge_field")
q.mk_cache("fields_io", "prop_field")

structure = q.list_cache()
# Returns a nested dict with Cache sub-nodes only (no leaf values)
assert "fields_io" in structure
assert "gauge_field" in structure["fields_io"]
assert "prop_field" in structure["fields_io"]

Multiple Independent Cache Trees

import qlat_utils as q

# You can create independent cache trees under different roots
root_a = q.Cache()
root_b = q.Cache()

ca = q.mk_cache("group", "sub", ca=root_a)
cb = q.mk_cache("group", "sub", ca=root_b)

ca["from_a"] = 1
cb["from_b"] = 2

assert ca["from_a"] == 1
assert cb["from_b"] == 2
# They are independent
assert "from_b" not in ca
assert "from_a" not in cb

Cache Keys Introspection

import qlat_utils as q

cache_gauge = q.mk_cache("fields_io", "gauge_field")
# cache_keys records the path from the root
assert cache_gauge.cache_keys == ("fields_io", "gauge_field")

# show_cache_keys produces a human-readable string
print(q.show_cache_keys(cache_gauge.cache_keys))
# Output: ['fields_io']['gauge_field']