tnfr 6.0.0__py3-none-any.whl → 7.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tnfr might be problematic. Click here for more details.

Files changed (176) hide show
  1. tnfr/__init__.py +50 -5
  2. tnfr/__init__.pyi +0 -7
  3. tnfr/_compat.py +0 -1
  4. tnfr/_generated_version.py +34 -0
  5. tnfr/_version.py +44 -2
  6. tnfr/alias.py +14 -13
  7. tnfr/alias.pyi +5 -37
  8. tnfr/cache.py +9 -729
  9. tnfr/cache.pyi +8 -224
  10. tnfr/callback_utils.py +16 -31
  11. tnfr/callback_utils.pyi +3 -29
  12. tnfr/cli/__init__.py +17 -11
  13. tnfr/cli/__init__.pyi +0 -21
  14. tnfr/cli/arguments.py +175 -14
  15. tnfr/cli/arguments.pyi +5 -11
  16. tnfr/cli/execution.py +434 -48
  17. tnfr/cli/execution.pyi +14 -24
  18. tnfr/cli/utils.py +20 -3
  19. tnfr/cli/utils.pyi +5 -5
  20. tnfr/config/__init__.py +2 -1
  21. tnfr/config/__init__.pyi +2 -0
  22. tnfr/config/feature_flags.py +83 -0
  23. tnfr/config/init.py +1 -1
  24. tnfr/config/operator_names.py +1 -14
  25. tnfr/config/presets.py +6 -26
  26. tnfr/constants/__init__.py +10 -13
  27. tnfr/constants/__init__.pyi +10 -22
  28. tnfr/constants/aliases.py +31 -0
  29. tnfr/constants/core.py +4 -3
  30. tnfr/constants/init.py +1 -1
  31. tnfr/constants/metric.py +3 -3
  32. tnfr/dynamics/__init__.py +64 -10
  33. tnfr/dynamics/__init__.pyi +3 -4
  34. tnfr/dynamics/adaptation.py +79 -13
  35. tnfr/dynamics/aliases.py +10 -9
  36. tnfr/dynamics/coordination.py +77 -35
  37. tnfr/dynamics/dnfr.py +575 -274
  38. tnfr/dynamics/dnfr.pyi +1 -10
  39. tnfr/dynamics/integrators.py +47 -33
  40. tnfr/dynamics/integrators.pyi +0 -1
  41. tnfr/dynamics/runtime.py +489 -129
  42. tnfr/dynamics/sampling.py +2 -0
  43. tnfr/dynamics/selectors.py +101 -62
  44. tnfr/execution.py +15 -8
  45. tnfr/execution.pyi +5 -25
  46. tnfr/flatten.py +7 -3
  47. tnfr/flatten.pyi +1 -8
  48. tnfr/gamma.py +22 -26
  49. tnfr/gamma.pyi +0 -6
  50. tnfr/glyph_history.py +37 -26
  51. tnfr/glyph_history.pyi +1 -19
  52. tnfr/glyph_runtime.py +16 -0
  53. tnfr/glyph_runtime.pyi +9 -0
  54. tnfr/immutable.py +20 -15
  55. tnfr/immutable.pyi +4 -7
  56. tnfr/initialization.py +5 -7
  57. tnfr/initialization.pyi +1 -9
  58. tnfr/io.py +6 -305
  59. tnfr/io.pyi +13 -8
  60. tnfr/mathematics/__init__.py +81 -0
  61. tnfr/mathematics/backend.py +426 -0
  62. tnfr/mathematics/dynamics.py +398 -0
  63. tnfr/mathematics/epi.py +254 -0
  64. tnfr/mathematics/generators.py +222 -0
  65. tnfr/mathematics/metrics.py +119 -0
  66. tnfr/mathematics/operators.py +233 -0
  67. tnfr/mathematics/operators_factory.py +71 -0
  68. tnfr/mathematics/projection.py +78 -0
  69. tnfr/mathematics/runtime.py +173 -0
  70. tnfr/mathematics/spaces.py +247 -0
  71. tnfr/mathematics/transforms.py +292 -0
  72. tnfr/metrics/__init__.py +10 -10
  73. tnfr/metrics/coherence.py +123 -94
  74. tnfr/metrics/common.py +22 -13
  75. tnfr/metrics/common.pyi +42 -11
  76. tnfr/metrics/core.py +72 -14
  77. tnfr/metrics/diagnosis.py +48 -57
  78. tnfr/metrics/diagnosis.pyi +3 -7
  79. tnfr/metrics/export.py +3 -5
  80. tnfr/metrics/glyph_timing.py +41 -31
  81. tnfr/metrics/reporting.py +13 -6
  82. tnfr/metrics/sense_index.py +884 -114
  83. tnfr/metrics/trig.py +167 -11
  84. tnfr/metrics/trig.pyi +1 -0
  85. tnfr/metrics/trig_cache.py +112 -15
  86. tnfr/node.py +400 -17
  87. tnfr/node.pyi +55 -38
  88. tnfr/observers.py +111 -8
  89. tnfr/observers.pyi +0 -15
  90. tnfr/ontosim.py +9 -6
  91. tnfr/ontosim.pyi +0 -5
  92. tnfr/operators/__init__.py +529 -42
  93. tnfr/operators/__init__.pyi +14 -0
  94. tnfr/operators/definitions.py +350 -18
  95. tnfr/operators/definitions.pyi +0 -14
  96. tnfr/operators/grammar.py +760 -0
  97. tnfr/operators/jitter.py +28 -22
  98. tnfr/operators/registry.py +7 -12
  99. tnfr/operators/registry.pyi +0 -2
  100. tnfr/operators/remesh.py +38 -61
  101. tnfr/rng.py +17 -300
  102. tnfr/schemas/__init__.py +8 -0
  103. tnfr/schemas/grammar.json +94 -0
  104. tnfr/selector.py +3 -4
  105. tnfr/selector.pyi +1 -1
  106. tnfr/sense.py +22 -24
  107. tnfr/sense.pyi +0 -7
  108. tnfr/structural.py +504 -21
  109. tnfr/structural.pyi +41 -18
  110. tnfr/telemetry/__init__.py +23 -1
  111. tnfr/telemetry/cache_metrics.py +226 -0
  112. tnfr/telemetry/nu_f.py +423 -0
  113. tnfr/telemetry/nu_f.pyi +123 -0
  114. tnfr/tokens.py +1 -4
  115. tnfr/tokens.pyi +1 -6
  116. tnfr/trace.py +20 -53
  117. tnfr/trace.pyi +9 -37
  118. tnfr/types.py +244 -15
  119. tnfr/types.pyi +200 -14
  120. tnfr/units.py +69 -0
  121. tnfr/units.pyi +16 -0
  122. tnfr/utils/__init__.py +107 -48
  123. tnfr/utils/__init__.pyi +80 -11
  124. tnfr/utils/cache.py +1705 -65
  125. tnfr/utils/cache.pyi +370 -58
  126. tnfr/utils/chunks.py +104 -0
  127. tnfr/utils/chunks.pyi +21 -0
  128. tnfr/utils/data.py +95 -5
  129. tnfr/utils/data.pyi +8 -17
  130. tnfr/utils/graph.py +2 -4
  131. tnfr/utils/init.py +31 -7
  132. tnfr/utils/init.pyi +4 -11
  133. tnfr/utils/io.py +313 -14
  134. tnfr/{helpers → utils}/numeric.py +50 -24
  135. tnfr/utils/numeric.pyi +21 -0
  136. tnfr/validation/__init__.py +92 -4
  137. tnfr/validation/__init__.pyi +77 -17
  138. tnfr/validation/compatibility.py +79 -43
  139. tnfr/validation/compatibility.pyi +4 -6
  140. tnfr/validation/grammar.py +55 -133
  141. tnfr/validation/grammar.pyi +37 -8
  142. tnfr/validation/graph.py +138 -0
  143. tnfr/validation/graph.pyi +17 -0
  144. tnfr/validation/rules.py +161 -74
  145. tnfr/validation/rules.pyi +55 -18
  146. tnfr/validation/runtime.py +263 -0
  147. tnfr/validation/runtime.pyi +31 -0
  148. tnfr/validation/soft_filters.py +170 -0
  149. tnfr/validation/soft_filters.pyi +37 -0
  150. tnfr/validation/spectral.py +159 -0
  151. tnfr/validation/spectral.pyi +46 -0
  152. tnfr/validation/syntax.py +28 -139
  153. tnfr/validation/syntax.pyi +7 -4
  154. tnfr/validation/window.py +39 -0
  155. tnfr/validation/window.pyi +1 -0
  156. tnfr/viz/__init__.py +9 -0
  157. tnfr/viz/matplotlib.py +246 -0
  158. {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/METADATA +63 -19
  159. tnfr-7.0.0.dist-info/RECORD +185 -0
  160. {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/licenses/LICENSE.md +1 -1
  161. tnfr/constants_glyphs.py +0 -16
  162. tnfr/constants_glyphs.pyi +0 -12
  163. tnfr/grammar.py +0 -25
  164. tnfr/grammar.pyi +0 -13
  165. tnfr/helpers/__init__.py +0 -151
  166. tnfr/helpers/__init__.pyi +0 -66
  167. tnfr/helpers/numeric.pyi +0 -12
  168. tnfr/presets.py +0 -15
  169. tnfr/presets.pyi +0 -7
  170. tnfr/utils/io.pyi +0 -10
  171. tnfr/utils/validators.py +0 -130
  172. tnfr/utils/validators.pyi +0 -19
  173. tnfr-6.0.0.dist-info/RECORD +0 -157
  174. {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/WHEEL +0 -0
  175. {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/entry_points.txt +0 -0
  176. {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/top_level.txt +0 -0
tnfr/utils/cache.pyi CHANGED
@@ -1,25 +1,234 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import logging
3
4
  import threading
4
- from collections.abc import Callable, Hashable, Iterable, Iterator, Mapping, MutableMapping
5
- from typing import Any, ContextManager, Generic, TypeVar
5
+ from collections import defaultdict
6
+ from collections.abc import (
7
+ Callable,
8
+ Hashable,
9
+ Iterable,
10
+ Iterator,
11
+ Mapping,
12
+ MutableMapping,
13
+ )
14
+ from dataclasses import dataclass
15
+ from typing import Any, ClassVar, ContextManager, Generic, TypeVar
6
16
 
7
17
  import networkx as nx
18
+ from cachetools import LRUCache
19
+
20
+ from ..types import GraphLike, NodeId, TimingContext, TNFRGraph
8
21
 
9
- from ..cache import CacheCapacityConfig, CacheManager
10
- from ..types import GraphLike, NodeId, TNFRGraph, TimingContext
11
22
 
12
23
  K = TypeVar("K", bound=Hashable)
13
24
  V = TypeVar("V")
14
25
  T = TypeVar("T")
15
26
 
27
+ @dataclass(frozen=True)
28
+ class CacheCapacityConfig:
29
+ default_capacity: int | None
30
+ overrides: dict[str, int | None]
31
+
32
+ @dataclass(frozen=True)
33
+ class CacheStatistics:
34
+ hits: int = ...
35
+ misses: int = ...
36
+ evictions: int = ...
37
+ total_time: float = ...
38
+ timings: int = ...
39
+
40
+ def merge(self, other: CacheStatistics) -> CacheStatistics: ...
41
+
42
+ class CacheLayer:
43
+ def load(self, name: str) -> Any: ...
44
+ def store(self, name: str, value: Any) -> None: ...
45
+ def delete(self, name: str) -> None: ...
46
+ def clear(self) -> None: ...
47
+ def close(self) -> None: ...
48
+
49
+ class MappingCacheLayer(CacheLayer):
50
+ def __init__(self, storage: MutableMapping[str, Any] | None = ...) -> None: ...
51
+
52
+ class ShelveCacheLayer(CacheLayer):
53
+ def __init__(
54
+ self,
55
+ path: str,
56
+ *,
57
+ flag: str = ...,
58
+ protocol: int | None = ...,
59
+ writeback: bool = ...,
60
+ ) -> None: ...
61
+
62
+ class RedisCacheLayer(CacheLayer):
63
+ def __init__(self, client: Any | None = ..., *, namespace: str = ...) -> None: ...
64
+
65
+ class CacheManager:
66
+ _MISSING: ClassVar[object]
67
+
68
+ def __init__(
69
+ self,
70
+ storage: MutableMapping[str, Any] | None = ...,
71
+ *,
72
+ default_capacity: int | None = ...,
73
+ overrides: Mapping[str, int | None] | None = ...,
74
+ layers: Iterable[CacheLayer] | None = ...,
75
+ ) -> None: ...
76
+ @staticmethod
77
+ def _normalise_capacity(value: int | None) -> int | None: ...
78
+ def register(
79
+ self,
80
+ name: str,
81
+ factory: Callable[[], Any],
82
+ *,
83
+ lock_factory: Callable[[], threading.Lock | threading.RLock] | None = ...,
84
+ reset: Callable[[Any], Any] | None = ...,
85
+ create: bool = ...,
86
+ encoder: Callable[[Any], Any] | None = ...,
87
+ decoder: Callable[[Any], Any] | None = ...,
88
+ ) -> None: ...
89
+ def configure(
90
+ self,
91
+ *,
92
+ default_capacity: int | None | object = ...,
93
+ overrides: Mapping[str, int | None] | None = ...,
94
+ replace_overrides: bool = ...,
95
+ ) -> None: ...
96
+ def configure_from_mapping(self, config: Mapping[str, Any]) -> None: ...
97
+ def export_config(self) -> CacheCapacityConfig: ...
98
+ def get_capacity(
99
+ self,
100
+ name: str,
101
+ *,
102
+ requested: int | None = ...,
103
+ fallback: int | None = ...,
104
+ use_default: bool = ...,
105
+ ) -> int | None: ...
106
+ def has_override(self, name: str) -> bool: ...
107
+ def get_lock(self, name: str) -> threading.Lock | threading.RLock: ...
108
+ def names(self) -> Iterator[str]: ...
109
+ def get(self, name: str, *, create: bool = ...) -> Any: ...
110
+ def peek(self, name: str) -> Any: ...
111
+ def store(self, name: str, value: Any) -> None: ...
112
+ def update(
113
+ self,
114
+ name: str,
115
+ updater: Callable[[Any], Any],
116
+ *,
117
+ create: bool = ...,
118
+ ) -> Any: ...
119
+ def clear(self, name: str | None = ...) -> None: ...
120
+ def increment_hit(
121
+ self,
122
+ name: str,
123
+ *,
124
+ amount: int = ...,
125
+ duration: float | None = ...,
126
+ ) -> None: ...
127
+ def increment_miss(
128
+ self,
129
+ name: str,
130
+ *,
131
+ amount: int = ...,
132
+ duration: float | None = ...,
133
+ ) -> None: ...
134
+ def increment_eviction(self, name: str, *, amount: int = ...) -> None: ...
135
+ def record_timing(self, name: str, duration: float) -> None: ...
136
+ def timer(self, name: str) -> TimingContext: ...
137
+ def get_metrics(self, name: str) -> CacheStatistics: ...
138
+ def iter_metrics(self) -> Iterator[tuple[str, CacheStatistics]]: ...
139
+ def aggregate_metrics(self) -> CacheStatistics: ...
140
+ def register_metrics_publisher(
141
+ self, publisher: Callable[[str, CacheStatistics], None]
142
+ ) -> None: ...
143
+ def publish_metrics(
144
+ self,
145
+ *,
146
+ publisher: Callable[[str, CacheStatistics], None] | None = ...,
147
+ ) -> None: ...
148
+ def log_metrics(self, logger: logging.Logger, *, level: int = ...) -> None: ...
149
+
150
+ class InstrumentedLRUCache(MutableMapping[K, V], Generic[K, V]):
151
+ _MISSING: ClassVar[object]
152
+
153
+ def __init__(
154
+ self,
155
+ maxsize: int,
156
+ *,
157
+ manager: CacheManager | None = ...,
158
+ metrics_key: str | None = ...,
159
+ telemetry_callbacks: (
160
+ Iterable[Callable[[K, V], None]] | Callable[[K, V], None] | None
161
+ ) = ...,
162
+ eviction_callbacks: (
163
+ Iterable[Callable[[K, V], None]] | Callable[[K, V], None] | None
164
+ ) = ...,
165
+ locks: MutableMapping[K, Any] | None = ...,
166
+ getsizeof: Callable[[V], int] | None = ...,
167
+ count_overwrite_hit: bool = ...,
168
+ ) -> None: ...
169
+ @property
170
+ def telemetry_callbacks(self) -> tuple[Callable[[K, V], None], ...]: ...
171
+ @property
172
+ def eviction_callbacks(self) -> tuple[Callable[[K, V], None], ...]: ...
173
+ def set_telemetry_callbacks(
174
+ self,
175
+ callbacks: Iterable[Callable[[K, V], None]] | Callable[[K, V], None] | None,
176
+ *,
177
+ append: bool = ...,
178
+ ) -> None: ...
179
+ def set_eviction_callbacks(
180
+ self,
181
+ callbacks: Iterable[Callable[[K, V], None]] | Callable[[K, V], None] | None,
182
+ *,
183
+ append: bool = ...,
184
+ ) -> None: ...
185
+ def pop(self, key: K, default: Any = ...) -> V: ...
186
+ def popitem(self) -> tuple[K, V]: ...
187
+ def clear(self) -> None: ...
188
+ @property
189
+ def maxsize(self) -> int: ...
190
+ @property
191
+ def currsize(self) -> int: ...
192
+ def get(self, key: K, default: V | None = ...) -> V | None: ...
193
+
194
+ class ManagedLRUCache(LRUCache[K, V], Generic[K, V]):
195
+ def __init__(
196
+ self,
197
+ maxsize: int,
198
+ *,
199
+ manager: CacheManager | None = ...,
200
+ metrics_key: str | None = ...,
201
+ eviction_callbacks: (
202
+ Iterable[Callable[[K, V], None]] | Callable[[K, V], None] | None
203
+ ) = ...,
204
+ telemetry_callbacks: (
205
+ Iterable[Callable[[K, V], None]] | Callable[[K, V], None] | None
206
+ ) = ...,
207
+ locks: MutableMapping[K, Any] | None = ...,
208
+ ) -> None: ...
209
+ def popitem(self) -> tuple[K, V]: ...
210
+
211
+ def prune_lock_mapping(
212
+ cache: Mapping[K, Any] | MutableMapping[K, Any] | None,
213
+ locks: MutableMapping[K, Any] | None,
214
+ ) -> None: ...
215
+
16
216
  __all__ = (
217
+ "CacheLayer",
218
+ "CacheManager",
219
+ "CacheCapacityConfig",
220
+ "CacheStatistics",
221
+ "InstrumentedLRUCache",
222
+ "ManagedLRUCache",
223
+ "MappingCacheLayer",
224
+ "RedisCacheLayer",
225
+ "ShelveCacheLayer",
226
+ "prune_lock_mapping",
17
227
  "EdgeCacheManager",
18
228
  "NODE_SET_CHECKSUM_KEY",
19
229
  "cached_node_list",
20
230
  "cached_nodes_and_A",
21
231
  "clear_node_repr_cache",
22
- "configure_graph_cache_limits",
23
232
  "edge_version_cache",
24
233
  "edge_version_update",
25
234
  "ensure_node_index_map",
@@ -29,81 +238,131 @@ __all__ = (
29
238
  "increment_graph_version",
30
239
  "node_set_checksum",
31
240
  "stable_json",
241
+ "configure_graph_cache_limits",
242
+ "DNFR_PREP_STATE_KEY",
243
+ "DnfrCache",
244
+ "DnfrPrepState",
245
+ "new_dnfr_cache",
246
+ "build_cache_manager",
247
+ "configure_global_cache_layers",
248
+ "reset_global_cache_manager",
249
+ "_GRAPH_CACHE_LAYERS_KEY",
250
+ "_SeedHashCache",
251
+ "ScopedCounterCache",
32
252
  )
33
253
 
34
- NODE_SET_CHECKSUM_KEY: str
35
-
36
-
37
- class LRUCache(MutableMapping[K, V], Generic[K, V]):
38
- def __init__(self, maxsize: int = ...) -> None: ...
39
254
 
40
- def __getitem__(self, __key: K) -> V: ...
255
+ NODE_SET_CHECKSUM_KEY: str
256
+ _GRAPH_CACHE_LAYERS_KEY: str
257
+ DNFR_PREP_STATE_KEY: str
258
+
259
+ class DnfrCache:
260
+ idx: dict[Any, int]
261
+ theta: list[float]
262
+ epi: list[float]
263
+ vf: list[float]
264
+ cos_theta: list[float]
265
+ sin_theta: list[float]
266
+ neighbor_x: list[float]
267
+ neighbor_y: list[float]
268
+ neighbor_epi_sum: list[float]
269
+ neighbor_vf_sum: list[float]
270
+ neighbor_count: list[float]
271
+ neighbor_deg_sum: list[float] | None
272
+ th_bar: list[float] | None
273
+ epi_bar: list[float] | None
274
+ vf_bar: list[float] | None
275
+ deg_bar: list[float] | None
276
+ degs: dict[Any, float] | None
277
+ deg_list: list[float] | None
278
+ theta_np: Any | None
279
+ epi_np: Any | None
280
+ vf_np: Any | None
281
+ cos_theta_np: Any | None
282
+ sin_theta_np: Any | None
283
+ deg_array: Any | None
284
+ edge_src: Any | None
285
+ edge_dst: Any | None
286
+ checksum: Any | None
287
+ neighbor_x_np: Any | None
288
+ neighbor_y_np: Any | None
289
+ neighbor_epi_sum_np: Any | None
290
+ neighbor_vf_sum_np: Any | None
291
+ neighbor_count_np: Any | None
292
+ neighbor_deg_sum_np: Any | None
293
+ th_bar_np: Any | None
294
+ epi_bar_np: Any | None
295
+ vf_bar_np: Any | None
296
+ deg_bar_np: Any | None
297
+ grad_phase_np: Any | None
298
+ grad_epi_np: Any | None
299
+ grad_vf_np: Any | None
300
+ grad_topo_np: Any | None
301
+ grad_total_np: Any | None
302
+ dense_components_np: Any | None
303
+ dense_accum_np: Any | None
304
+ dense_degree_np: Any | None
305
+ neighbor_accum_np: Any | None
306
+ neighbor_inv_count_np: Any | None
307
+ neighbor_cos_avg_np: Any | None
308
+ neighbor_sin_avg_np: Any | None
309
+ neighbor_mean_tmp_np: Any | None
310
+ neighbor_mean_length_np: Any | None
311
+ edge_signature: Any | None
312
+ neighbor_accum_signature: Any | None
313
+ neighbor_edge_values_np: Any | None
41
314
 
42
- def __setitem__(self, __key: K, __value: V) -> None: ...
43
315
 
44
- def __delitem__(self, __key: K) -> None: ...
316
+ class EdgeCacheState:
317
+ cache: MutableMapping[Hashable, Any]
318
+ locks: defaultdict[Hashable, threading.RLock]
319
+ max_entries: int | None
320
+ dirty: bool
45
321
 
46
- def __iter__(self) -> Iterator[K]: ...
47
322
 
48
- def __len__(self) -> int: ...
323
+ def new_dnfr_cache() -> DnfrCache: ...
49
324
 
50
325
 
51
- class EdgeCacheState:
52
- cache: MutableMapping[Hashable, Any]
53
- locks: MutableMapping[Hashable, threading.RLock]
54
- max_entries: int | None
326
+ class DnfrPrepState:
327
+ cache: DnfrCache
328
+ cache_lock: threading.RLock
329
+ vector_lock: threading.RLock
55
330
 
56
331
 
57
332
  class EdgeCacheManager:
58
333
  _STATE_KEY: str
59
334
 
60
335
  def __init__(self, graph: MutableMapping[str, Any]) -> None: ...
61
-
62
336
  def record_hit(self) -> None: ...
63
-
64
337
  def record_miss(self, *, track_metrics: bool = ...) -> None: ...
65
-
66
338
  def record_eviction(self, *, track_metrics: bool = ...) -> None: ...
67
-
68
339
  def timer(self) -> TimingContext: ...
69
-
70
340
  def _default_state(self) -> EdgeCacheState: ...
71
-
72
341
  def resolve_max_entries(self, max_entries: int | None | object) -> int | None: ...
73
-
74
342
  def _build_state(self, max_entries: int | None) -> EdgeCacheState: ...
75
-
76
343
  def _ensure_state(
77
344
  self, state: EdgeCacheState | None, max_entries: int | None | object
78
345
  ) -> EdgeCacheState: ...
79
-
80
346
  def _reset_state(self, state: EdgeCacheState | None) -> EdgeCacheState: ...
81
-
82
347
  def get_cache(
83
348
  self,
84
349
  max_entries: int | None | object,
85
350
  *,
86
351
  create: bool = ...,
87
- ) -> tuple[
88
- MutableMapping[Hashable, Any] | None,
89
- MutableMapping[Hashable, threading.RLock] | None,
90
- ]: ...
91
-
352
+ ) -> EdgeCacheState | None: ...
353
+ def flush_state(self, state: EdgeCacheState) -> None: ...
92
354
  def clear(self) -> None: ...
93
355
 
94
-
95
356
  def get_graph_version(graph: Any, key: str, default: int = ...) -> int: ...
96
-
97
-
98
357
  def increment_graph_version(graph: Any, key: str) -> int: ...
99
-
100
-
101
358
  def stable_json(obj: Any) -> str: ...
102
-
103
-
104
359
  def clear_node_repr_cache() -> None: ...
105
-
106
-
360
+ def configure_global_cache_layers(
361
+ *,
362
+ shelve: Mapping[str, Any] | None = ...,
363
+ redis: Mapping[str, Any] | None = ...,
364
+ replace: bool = ...,
365
+ ) -> None: ...
107
366
  def node_set_checksum(
108
367
  G: nx.Graph,
109
368
  nodes: Iterable[Any] | None = ...,
@@ -111,17 +370,17 @@ def node_set_checksum(
111
370
  presorted: bool = ...,
112
371
  store: bool = ...,
113
372
  ) -> str: ...
114
-
115
-
373
+ def reset_global_cache_manager() -> None: ...
374
+ def build_cache_manager(
375
+ *,
376
+ graph: MutableMapping[str, Any] | None = ...,
377
+ storage: MutableMapping[str, Any] | None = ...,
378
+ default_capacity: int | None = ...,
379
+ overrides: Mapping[str, int | None] | None = ...,
380
+ ) -> CacheManager: ...
116
381
  def cached_node_list(G: nx.Graph) -> tuple[Any, ...]: ...
117
-
118
-
119
382
  def ensure_node_index_map(G: TNFRGraph) -> dict[NodeId, int]: ...
120
-
121
-
122
383
  def ensure_node_offset_map(G: TNFRGraph) -> dict[NodeId, int]: ...
123
-
124
-
125
384
  def configure_graph_cache_limits(
126
385
  G: GraphLike | TNFRGraph | MutableMapping[str, Any],
127
386
  *,
@@ -129,11 +388,7 @@ def configure_graph_cache_limits(
129
388
  overrides: Mapping[str, int | None] | None = ...,
130
389
  replace_overrides: bool = ...,
131
390
  ) -> CacheCapacityConfig: ...
132
-
133
-
134
391
  def increment_edge_version(G: Any) -> None: ...
135
-
136
-
137
392
  def edge_version_cache(
138
393
  G: Any,
139
394
  key: Hashable,
@@ -141,8 +396,6 @@ def edge_version_cache(
141
396
  *,
142
397
  max_entries: int | None | object = CacheManager._MISSING,
143
398
  ) -> T: ...
144
-
145
-
146
399
  def cached_nodes_and_A(
147
400
  G: nx.Graph,
148
401
  *,
@@ -151,6 +404,65 @@ def cached_nodes_and_A(
151
404
  prefer_sparse: bool = ...,
152
405
  nodes: tuple[Any, ...] | None = ...,
153
406
  ) -> tuple[tuple[Any, ...], Any]: ...
407
+ def edge_version_update(G: TNFRGraph) -> ContextManager[None]: ...
154
408
 
155
409
 
156
- def edge_version_update(G: TNFRGraph) -> ContextManager[None]: ...
410
+ class _SeedCacheState:
411
+ cache: InstrumentedLRUCache[tuple[int, int], int] | None
412
+ maxsize: int
413
+
414
+
415
+ class _CounterState(Generic[K]):
416
+ cache: InstrumentedLRUCache[K, int]
417
+ locks: dict[K, threading.RLock]
418
+ max_entries: int
419
+
420
+
421
+ class _SeedHashCache(MutableMapping[tuple[int, int], int]):
422
+ _state_key: str
423
+
424
+ def __init__(
425
+ self,
426
+ *,
427
+ manager: CacheManager | None = ...,
428
+ state_key: str = ...,
429
+ default_maxsize: int = ...,
430
+ ) -> None: ...
431
+ def configure(self, maxsize: int) -> None: ...
432
+ def __getitem__(self, key: tuple[int, int]) -> int: ...
433
+ def __setitem__(self, key: tuple[int, int], value: int) -> None: ...
434
+ def __delitem__(self, key: tuple[int, int]) -> None: ...
435
+ def __iter__(self) -> Iterator[tuple[int, int]]: ...
436
+ def __len__(self) -> int: ...
437
+ def clear(self) -> None: ...
438
+ @property
439
+ def maxsize(self) -> int: ...
440
+ @property
441
+ def enabled(self) -> bool: ...
442
+ @property
443
+ def data(self) -> InstrumentedLRUCache[tuple[int, int], int] | None: ...
444
+
445
+
446
+ class ScopedCounterCache(Generic[K]):
447
+ _state_key: str
448
+
449
+ def __init__(
450
+ self,
451
+ name: str,
452
+ max_entries: int | None = ...,
453
+ *,
454
+ manager: CacheManager | None = ...,
455
+ default_max_entries: int = ...,
456
+ ) -> None: ...
457
+ def configure(self, *, force: bool = ..., max_entries: int | None = ...) -> None: ...
458
+ def clear(self) -> None: ...
459
+ def bump(self, key: K) -> int: ...
460
+ def __len__(self) -> int: ...
461
+ @property
462
+ def lock(self) -> threading.Lock | threading.RLock: ...
463
+ @property
464
+ def max_entries(self) -> int: ...
465
+ @property
466
+ def cache(self) -> InstrumentedLRUCache[K, int]: ...
467
+ @property
468
+ def locks(self) -> dict[K, threading.RLock]: ...
tnfr/utils/chunks.py ADDED
@@ -0,0 +1,104 @@
1
+ """Chunk sizing heuristics for batching structural computations.
2
+
3
+ The helpers in this module determine how large each processing block should be
4
+ when splitting work across workers or vectorised loops. They take into account
5
+ the number of items involved, approximate memory pressure, and available CPU
6
+ parallelism so the caller can balance throughput with deterministic behaviour.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import math
12
+ import os
13
+ from typing import Final
14
+
15
+ DEFAULT_APPROX_BYTES_PER_ITEM: Final[int] = 64
16
+ DEFAULT_CHUNK_CLAMP: Final[int] | None = 131_072
17
+
18
+
19
+ def _estimate_available_memory() -> int | None:
20
+ """Best-effort estimation of free memory available to the process."""
21
+
22
+ try:
23
+ page_size = os.sysconf("SC_PAGE_SIZE")
24
+ avail_pages = os.sysconf("SC_AVPHYS_PAGES")
25
+ except (AttributeError, ValueError, OSError): # pragma: no cover - platform specific
26
+ return None
27
+ if page_size <= 0 or avail_pages <= 0:
28
+ return None
29
+ return int(page_size) * int(avail_pages)
30
+
31
+
32
+ def auto_chunk_size(
33
+ total_items: int,
34
+ *,
35
+ minimum: int = 1,
36
+ approx_bytes_per_item: int = DEFAULT_APPROX_BYTES_PER_ITEM,
37
+ clamp_to: int | None = DEFAULT_CHUNK_CLAMP,
38
+ ) -> int:
39
+ """Infer a safe chunk length when the caller does not specify one."""
40
+
41
+ if total_items <= 0:
42
+ return 0
43
+
44
+ minimum = max(1, minimum)
45
+ approx_bytes_per_item = max(1, approx_bytes_per_item)
46
+
47
+ available_memory = _estimate_available_memory()
48
+ if available_memory is not None and available_memory > 0:
49
+ safe_bytes = max(approx_bytes_per_item * minimum, available_memory // 8)
50
+ mem_bound = max(minimum, min(total_items, safe_bytes // approx_bytes_per_item))
51
+ else:
52
+ mem_bound = total_items
53
+
54
+ if clamp_to is not None:
55
+ mem_bound = min(mem_bound, clamp_to)
56
+
57
+ cpu_count = os.cpu_count() or 1
58
+ target_chunks = max(1, cpu_count * 4)
59
+ cpu_chunk = max(minimum, math.ceil(total_items / target_chunks))
60
+ baseline = max(minimum, min(total_items, 1024))
61
+ target = max(cpu_chunk, baseline)
62
+
63
+ chunk = min(mem_bound, target)
64
+ chunk = max(minimum, min(total_items, chunk))
65
+ return chunk
66
+
67
+
68
+ def resolve_chunk_size(
69
+ chunk_size: int | None,
70
+ total_items: int,
71
+ *,
72
+ minimum: int = 1,
73
+ approx_bytes_per_item: int = DEFAULT_APPROX_BYTES_PER_ITEM,
74
+ clamp_to: int | None = DEFAULT_CHUNK_CLAMP,
75
+ ) -> int:
76
+ """Return a sanitised chunk size honouring automatic fallbacks."""
77
+
78
+ if total_items <= 0:
79
+ return 0
80
+
81
+ resolved: int | None
82
+ if chunk_size is None:
83
+ resolved = None
84
+ else:
85
+ try:
86
+ resolved = int(chunk_size)
87
+ except (TypeError, ValueError):
88
+ resolved = None
89
+ else:
90
+ if resolved <= 0:
91
+ resolved = None
92
+
93
+ if resolved is None:
94
+ resolved = auto_chunk_size(
95
+ total_items,
96
+ minimum=minimum,
97
+ approx_bytes_per_item=approx_bytes_per_item,
98
+ clamp_to=clamp_to,
99
+ )
100
+
101
+ return max(minimum, min(total_items, resolved))
102
+
103
+
104
+ __all__ = ["auto_chunk_size", "resolve_chunk_size"]
tnfr/utils/chunks.pyi ADDED
@@ -0,0 +1,21 @@
1
+ from typing import Final
2
+
3
+ DEFAULT_APPROX_BYTES_PER_ITEM: Final[int]
4
+ DEFAULT_CHUNK_CLAMP: Final[int | None]
5
+
6
+ def auto_chunk_size(
7
+ total_items: int,
8
+ *,
9
+ minimum: int = ...,
10
+ approx_bytes_per_item: int = ...,
11
+ clamp_to: int | None = ...,
12
+ ) -> int: ...
13
+
14
+ def resolve_chunk_size(
15
+ chunk_size: int | None,
16
+ total_items: int,
17
+ *,
18
+ minimum: int = ...,
19
+ approx_bytes_per_item: int = ...,
20
+ clamp_to: int | None = ...,
21
+ ) -> int: ...