tnfr 4.5.2__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 (195) hide show
  1. tnfr/__init__.py +275 -51
  2. tnfr/__init__.pyi +33 -0
  3. tnfr/_compat.py +10 -0
  4. tnfr/_generated_version.py +34 -0
  5. tnfr/_version.py +49 -0
  6. tnfr/_version.pyi +7 -0
  7. tnfr/alias.py +117 -31
  8. tnfr/alias.pyi +108 -0
  9. tnfr/cache.py +6 -572
  10. tnfr/cache.pyi +16 -0
  11. tnfr/callback_utils.py +16 -38
  12. tnfr/callback_utils.pyi +79 -0
  13. tnfr/cli/__init__.py +34 -14
  14. tnfr/cli/__init__.pyi +26 -0
  15. tnfr/cli/arguments.py +211 -28
  16. tnfr/cli/arguments.pyi +27 -0
  17. tnfr/cli/execution.py +470 -50
  18. tnfr/cli/execution.pyi +70 -0
  19. tnfr/cli/utils.py +18 -3
  20. tnfr/cli/utils.pyi +8 -0
  21. tnfr/config/__init__.py +13 -0
  22. tnfr/config/__init__.pyi +10 -0
  23. tnfr/{constants_glyphs.py → config/constants.py} +26 -20
  24. tnfr/config/constants.pyi +12 -0
  25. tnfr/config/feature_flags.py +83 -0
  26. tnfr/{config.py → config/init.py} +11 -7
  27. tnfr/config/init.pyi +8 -0
  28. tnfr/config/operator_names.py +93 -0
  29. tnfr/config/operator_names.pyi +28 -0
  30. tnfr/config/presets.py +84 -0
  31. tnfr/config/presets.pyi +7 -0
  32. tnfr/constants/__init__.py +80 -29
  33. tnfr/constants/__init__.pyi +92 -0
  34. tnfr/constants/aliases.py +31 -0
  35. tnfr/constants/core.py +4 -4
  36. tnfr/constants/core.pyi +17 -0
  37. tnfr/constants/init.py +1 -1
  38. tnfr/constants/init.pyi +12 -0
  39. tnfr/constants/metric.py +7 -15
  40. tnfr/constants/metric.pyi +19 -0
  41. tnfr/dynamics/__init__.py +165 -633
  42. tnfr/dynamics/__init__.pyi +82 -0
  43. tnfr/dynamics/adaptation.py +267 -0
  44. tnfr/dynamics/aliases.py +23 -0
  45. tnfr/dynamics/coordination.py +385 -0
  46. tnfr/dynamics/dnfr.py +2283 -400
  47. tnfr/dynamics/dnfr.pyi +24 -0
  48. tnfr/dynamics/integrators.py +406 -98
  49. tnfr/dynamics/integrators.pyi +34 -0
  50. tnfr/dynamics/runtime.py +881 -0
  51. tnfr/dynamics/sampling.py +10 -5
  52. tnfr/dynamics/sampling.pyi +7 -0
  53. tnfr/dynamics/selectors.py +719 -0
  54. tnfr/execution.py +70 -48
  55. tnfr/execution.pyi +45 -0
  56. tnfr/flatten.py +13 -9
  57. tnfr/flatten.pyi +21 -0
  58. tnfr/gamma.py +66 -53
  59. tnfr/gamma.pyi +34 -0
  60. tnfr/glyph_history.py +110 -52
  61. tnfr/glyph_history.pyi +35 -0
  62. tnfr/glyph_runtime.py +16 -0
  63. tnfr/glyph_runtime.pyi +9 -0
  64. tnfr/immutable.py +69 -28
  65. tnfr/immutable.pyi +34 -0
  66. tnfr/initialization.py +16 -16
  67. tnfr/initialization.pyi +65 -0
  68. tnfr/io.py +6 -240
  69. tnfr/io.pyi +16 -0
  70. tnfr/locking.pyi +7 -0
  71. tnfr/mathematics/__init__.py +81 -0
  72. tnfr/mathematics/backend.py +426 -0
  73. tnfr/mathematics/dynamics.py +398 -0
  74. tnfr/mathematics/epi.py +254 -0
  75. tnfr/mathematics/generators.py +222 -0
  76. tnfr/mathematics/metrics.py +119 -0
  77. tnfr/mathematics/operators.py +233 -0
  78. tnfr/mathematics/operators_factory.py +71 -0
  79. tnfr/mathematics/projection.py +78 -0
  80. tnfr/mathematics/runtime.py +173 -0
  81. tnfr/mathematics/spaces.py +247 -0
  82. tnfr/mathematics/transforms.py +292 -0
  83. tnfr/metrics/__init__.py +10 -10
  84. tnfr/metrics/__init__.pyi +20 -0
  85. tnfr/metrics/coherence.py +993 -324
  86. tnfr/metrics/common.py +23 -16
  87. tnfr/metrics/common.pyi +46 -0
  88. tnfr/metrics/core.py +251 -35
  89. tnfr/metrics/core.pyi +13 -0
  90. tnfr/metrics/diagnosis.py +708 -111
  91. tnfr/metrics/diagnosis.pyi +85 -0
  92. tnfr/metrics/export.py +27 -15
  93. tnfr/metrics/glyph_timing.py +232 -42
  94. tnfr/metrics/reporting.py +33 -22
  95. tnfr/metrics/reporting.pyi +12 -0
  96. tnfr/metrics/sense_index.py +987 -43
  97. tnfr/metrics/sense_index.pyi +9 -0
  98. tnfr/metrics/trig.py +214 -23
  99. tnfr/metrics/trig.pyi +13 -0
  100. tnfr/metrics/trig_cache.py +115 -22
  101. tnfr/metrics/trig_cache.pyi +10 -0
  102. tnfr/node.py +542 -136
  103. tnfr/node.pyi +178 -0
  104. tnfr/observers.py +152 -35
  105. tnfr/observers.pyi +31 -0
  106. tnfr/ontosim.py +23 -19
  107. tnfr/ontosim.pyi +28 -0
  108. tnfr/operators/__init__.py +601 -82
  109. tnfr/operators/__init__.pyi +45 -0
  110. tnfr/operators/definitions.py +513 -0
  111. tnfr/operators/definitions.pyi +78 -0
  112. tnfr/operators/grammar.py +760 -0
  113. tnfr/operators/jitter.py +107 -38
  114. tnfr/operators/jitter.pyi +11 -0
  115. tnfr/operators/registry.py +75 -0
  116. tnfr/operators/registry.pyi +13 -0
  117. tnfr/operators/remesh.py +149 -88
  118. tnfr/py.typed +0 -0
  119. tnfr/rng.py +46 -143
  120. tnfr/rng.pyi +14 -0
  121. tnfr/schemas/__init__.py +8 -0
  122. tnfr/schemas/grammar.json +94 -0
  123. tnfr/selector.py +25 -19
  124. tnfr/selector.pyi +19 -0
  125. tnfr/sense.py +72 -62
  126. tnfr/sense.pyi +23 -0
  127. tnfr/structural.py +522 -262
  128. tnfr/structural.pyi +69 -0
  129. tnfr/telemetry/__init__.py +35 -0
  130. tnfr/telemetry/cache_metrics.py +226 -0
  131. tnfr/telemetry/nu_f.py +423 -0
  132. tnfr/telemetry/nu_f.pyi +123 -0
  133. tnfr/telemetry/verbosity.py +37 -0
  134. tnfr/tokens.py +1 -3
  135. tnfr/tokens.pyi +36 -0
  136. tnfr/trace.py +270 -113
  137. tnfr/trace.pyi +40 -0
  138. tnfr/types.py +574 -6
  139. tnfr/types.pyi +331 -0
  140. tnfr/units.py +69 -0
  141. tnfr/units.pyi +16 -0
  142. tnfr/utils/__init__.py +217 -0
  143. tnfr/utils/__init__.pyi +202 -0
  144. tnfr/utils/cache.py +2395 -0
  145. tnfr/utils/cache.pyi +468 -0
  146. tnfr/utils/chunks.py +104 -0
  147. tnfr/utils/chunks.pyi +21 -0
  148. tnfr/{collections_utils.py → utils/data.py} +147 -90
  149. tnfr/utils/data.pyi +64 -0
  150. tnfr/utils/graph.py +85 -0
  151. tnfr/utils/graph.pyi +10 -0
  152. tnfr/utils/init.py +770 -0
  153. tnfr/utils/init.pyi +78 -0
  154. tnfr/utils/io.py +456 -0
  155. tnfr/{helpers → utils}/numeric.py +51 -24
  156. tnfr/utils/numeric.pyi +21 -0
  157. tnfr/validation/__init__.py +113 -0
  158. tnfr/validation/__init__.pyi +77 -0
  159. tnfr/validation/compatibility.py +95 -0
  160. tnfr/validation/compatibility.pyi +6 -0
  161. tnfr/validation/grammar.py +71 -0
  162. tnfr/validation/grammar.pyi +40 -0
  163. tnfr/validation/graph.py +138 -0
  164. tnfr/validation/graph.pyi +17 -0
  165. tnfr/validation/rules.py +281 -0
  166. tnfr/validation/rules.pyi +55 -0
  167. tnfr/validation/runtime.py +263 -0
  168. tnfr/validation/runtime.pyi +31 -0
  169. tnfr/validation/soft_filters.py +170 -0
  170. tnfr/validation/soft_filters.pyi +37 -0
  171. tnfr/validation/spectral.py +159 -0
  172. tnfr/validation/spectral.pyi +46 -0
  173. tnfr/validation/syntax.py +40 -0
  174. tnfr/validation/syntax.pyi +10 -0
  175. tnfr/validation/window.py +39 -0
  176. tnfr/validation/window.pyi +1 -0
  177. tnfr/viz/__init__.py +9 -0
  178. tnfr/viz/matplotlib.py +246 -0
  179. tnfr-7.0.0.dist-info/METADATA +179 -0
  180. tnfr-7.0.0.dist-info/RECORD +185 -0
  181. {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/licenses/LICENSE.md +1 -1
  182. tnfr/grammar.py +0 -344
  183. tnfr/graph_utils.py +0 -84
  184. tnfr/helpers/__init__.py +0 -71
  185. tnfr/import_utils.py +0 -228
  186. tnfr/json_utils.py +0 -162
  187. tnfr/logging_utils.py +0 -116
  188. tnfr/presets.py +0 -60
  189. tnfr/validators.py +0 -84
  190. tnfr/value_utils.py +0 -59
  191. tnfr-4.5.2.dist-info/METADATA +0 -379
  192. tnfr-4.5.2.dist-info/RECORD +0 -67
  193. {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/WHEEL +0 -0
  194. {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/entry_points.txt +0 -0
  195. {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/top_level.txt +0 -0
tnfr/utils/cache.pyi ADDED
@@ -0,0 +1,468 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import threading
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
16
+
17
+ import networkx as nx
18
+ from cachetools import LRUCache
19
+
20
+ from ..types import GraphLike, NodeId, TimingContext, TNFRGraph
21
+
22
+
23
+ K = TypeVar("K", bound=Hashable)
24
+ V = TypeVar("V")
25
+ T = TypeVar("T")
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
+
216
+ __all__ = (
217
+ "CacheLayer",
218
+ "CacheManager",
219
+ "CacheCapacityConfig",
220
+ "CacheStatistics",
221
+ "InstrumentedLRUCache",
222
+ "ManagedLRUCache",
223
+ "MappingCacheLayer",
224
+ "RedisCacheLayer",
225
+ "ShelveCacheLayer",
226
+ "prune_lock_mapping",
227
+ "EdgeCacheManager",
228
+ "NODE_SET_CHECKSUM_KEY",
229
+ "cached_node_list",
230
+ "cached_nodes_and_A",
231
+ "clear_node_repr_cache",
232
+ "edge_version_cache",
233
+ "edge_version_update",
234
+ "ensure_node_index_map",
235
+ "ensure_node_offset_map",
236
+ "get_graph_version",
237
+ "increment_edge_version",
238
+ "increment_graph_version",
239
+ "node_set_checksum",
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",
252
+ )
253
+
254
+
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
314
+
315
+
316
+ class EdgeCacheState:
317
+ cache: MutableMapping[Hashable, Any]
318
+ locks: defaultdict[Hashable, threading.RLock]
319
+ max_entries: int | None
320
+ dirty: bool
321
+
322
+
323
+ def new_dnfr_cache() -> DnfrCache: ...
324
+
325
+
326
+ class DnfrPrepState:
327
+ cache: DnfrCache
328
+ cache_lock: threading.RLock
329
+ vector_lock: threading.RLock
330
+
331
+
332
+ class EdgeCacheManager:
333
+ _STATE_KEY: str
334
+
335
+ def __init__(self, graph: MutableMapping[str, Any]) -> None: ...
336
+ def record_hit(self) -> None: ...
337
+ def record_miss(self, *, track_metrics: bool = ...) -> None: ...
338
+ def record_eviction(self, *, track_metrics: bool = ...) -> None: ...
339
+ def timer(self) -> TimingContext: ...
340
+ def _default_state(self) -> EdgeCacheState: ...
341
+ def resolve_max_entries(self, max_entries: int | None | object) -> int | None: ...
342
+ def _build_state(self, max_entries: int | None) -> EdgeCacheState: ...
343
+ def _ensure_state(
344
+ self, state: EdgeCacheState | None, max_entries: int | None | object
345
+ ) -> EdgeCacheState: ...
346
+ def _reset_state(self, state: EdgeCacheState | None) -> EdgeCacheState: ...
347
+ def get_cache(
348
+ self,
349
+ max_entries: int | None | object,
350
+ *,
351
+ create: bool = ...,
352
+ ) -> EdgeCacheState | None: ...
353
+ def flush_state(self, state: EdgeCacheState) -> None: ...
354
+ def clear(self) -> None: ...
355
+
356
+ def get_graph_version(graph: Any, key: str, default: int = ...) -> int: ...
357
+ def increment_graph_version(graph: Any, key: str) -> int: ...
358
+ def stable_json(obj: Any) -> str: ...
359
+ def clear_node_repr_cache() -> None: ...
360
+ def configure_global_cache_layers(
361
+ *,
362
+ shelve: Mapping[str, Any] | None = ...,
363
+ redis: Mapping[str, Any] | None = ...,
364
+ replace: bool = ...,
365
+ ) -> None: ...
366
+ def node_set_checksum(
367
+ G: nx.Graph,
368
+ nodes: Iterable[Any] | None = ...,
369
+ *,
370
+ presorted: bool = ...,
371
+ store: bool = ...,
372
+ ) -> str: ...
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: ...
381
+ def cached_node_list(G: nx.Graph) -> tuple[Any, ...]: ...
382
+ def ensure_node_index_map(G: TNFRGraph) -> dict[NodeId, int]: ...
383
+ def ensure_node_offset_map(G: TNFRGraph) -> dict[NodeId, int]: ...
384
+ def configure_graph_cache_limits(
385
+ G: GraphLike | TNFRGraph | MutableMapping[str, Any],
386
+ *,
387
+ default_capacity: int | None | object = CacheManager._MISSING,
388
+ overrides: Mapping[str, int | None] | None = ...,
389
+ replace_overrides: bool = ...,
390
+ ) -> CacheCapacityConfig: ...
391
+ def increment_edge_version(G: Any) -> None: ...
392
+ def edge_version_cache(
393
+ G: Any,
394
+ key: Hashable,
395
+ builder: Callable[[], T],
396
+ *,
397
+ max_entries: int | None | object = CacheManager._MISSING,
398
+ ) -> T: ...
399
+ def cached_nodes_and_A(
400
+ G: nx.Graph,
401
+ *,
402
+ cache_size: int | None = ...,
403
+ require_numpy: bool = ...,
404
+ prefer_sparse: bool = ...,
405
+ nodes: tuple[Any, ...] | None = ...,
406
+ ) -> tuple[tuple[Any, ...], Any]: ...
407
+ def edge_version_update(G: TNFRGraph) -> ContextManager[None]: ...
408
+
409
+
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: ...