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.
- tnfr/__init__.py +275 -51
- tnfr/__init__.pyi +33 -0
- tnfr/_compat.py +10 -0
- tnfr/_generated_version.py +34 -0
- tnfr/_version.py +49 -0
- tnfr/_version.pyi +7 -0
- tnfr/alias.py +117 -31
- tnfr/alias.pyi +108 -0
- tnfr/cache.py +6 -572
- tnfr/cache.pyi +16 -0
- tnfr/callback_utils.py +16 -38
- tnfr/callback_utils.pyi +79 -0
- tnfr/cli/__init__.py +34 -14
- tnfr/cli/__init__.pyi +26 -0
- tnfr/cli/arguments.py +211 -28
- tnfr/cli/arguments.pyi +27 -0
- tnfr/cli/execution.py +470 -50
- tnfr/cli/execution.pyi +70 -0
- tnfr/cli/utils.py +18 -3
- tnfr/cli/utils.pyi +8 -0
- tnfr/config/__init__.py +13 -0
- tnfr/config/__init__.pyi +10 -0
- tnfr/{constants_glyphs.py → config/constants.py} +26 -20
- tnfr/config/constants.pyi +12 -0
- tnfr/config/feature_flags.py +83 -0
- tnfr/{config.py → config/init.py} +11 -7
- tnfr/config/init.pyi +8 -0
- tnfr/config/operator_names.py +93 -0
- tnfr/config/operator_names.pyi +28 -0
- tnfr/config/presets.py +84 -0
- tnfr/config/presets.pyi +7 -0
- tnfr/constants/__init__.py +80 -29
- tnfr/constants/__init__.pyi +92 -0
- tnfr/constants/aliases.py +31 -0
- tnfr/constants/core.py +4 -4
- tnfr/constants/core.pyi +17 -0
- tnfr/constants/init.py +1 -1
- tnfr/constants/init.pyi +12 -0
- tnfr/constants/metric.py +7 -15
- tnfr/constants/metric.pyi +19 -0
- tnfr/dynamics/__init__.py +165 -633
- tnfr/dynamics/__init__.pyi +82 -0
- tnfr/dynamics/adaptation.py +267 -0
- tnfr/dynamics/aliases.py +23 -0
- tnfr/dynamics/coordination.py +385 -0
- tnfr/dynamics/dnfr.py +2283 -400
- tnfr/dynamics/dnfr.pyi +24 -0
- tnfr/dynamics/integrators.py +406 -98
- tnfr/dynamics/integrators.pyi +34 -0
- tnfr/dynamics/runtime.py +881 -0
- tnfr/dynamics/sampling.py +10 -5
- tnfr/dynamics/sampling.pyi +7 -0
- tnfr/dynamics/selectors.py +719 -0
- tnfr/execution.py +70 -48
- tnfr/execution.pyi +45 -0
- tnfr/flatten.py +13 -9
- tnfr/flatten.pyi +21 -0
- tnfr/gamma.py +66 -53
- tnfr/gamma.pyi +34 -0
- tnfr/glyph_history.py +110 -52
- tnfr/glyph_history.pyi +35 -0
- tnfr/glyph_runtime.py +16 -0
- tnfr/glyph_runtime.pyi +9 -0
- tnfr/immutable.py +69 -28
- tnfr/immutable.pyi +34 -0
- tnfr/initialization.py +16 -16
- tnfr/initialization.pyi +65 -0
- tnfr/io.py +6 -240
- tnfr/io.pyi +16 -0
- tnfr/locking.pyi +7 -0
- tnfr/mathematics/__init__.py +81 -0
- tnfr/mathematics/backend.py +426 -0
- tnfr/mathematics/dynamics.py +398 -0
- tnfr/mathematics/epi.py +254 -0
- tnfr/mathematics/generators.py +222 -0
- tnfr/mathematics/metrics.py +119 -0
- tnfr/mathematics/operators.py +233 -0
- tnfr/mathematics/operators_factory.py +71 -0
- tnfr/mathematics/projection.py +78 -0
- tnfr/mathematics/runtime.py +173 -0
- tnfr/mathematics/spaces.py +247 -0
- tnfr/mathematics/transforms.py +292 -0
- tnfr/metrics/__init__.py +10 -10
- tnfr/metrics/__init__.pyi +20 -0
- tnfr/metrics/coherence.py +993 -324
- tnfr/metrics/common.py +23 -16
- tnfr/metrics/common.pyi +46 -0
- tnfr/metrics/core.py +251 -35
- tnfr/metrics/core.pyi +13 -0
- tnfr/metrics/diagnosis.py +708 -111
- tnfr/metrics/diagnosis.pyi +85 -0
- tnfr/metrics/export.py +27 -15
- tnfr/metrics/glyph_timing.py +232 -42
- tnfr/metrics/reporting.py +33 -22
- tnfr/metrics/reporting.pyi +12 -0
- tnfr/metrics/sense_index.py +987 -43
- tnfr/metrics/sense_index.pyi +9 -0
- tnfr/metrics/trig.py +214 -23
- tnfr/metrics/trig.pyi +13 -0
- tnfr/metrics/trig_cache.py +115 -22
- tnfr/metrics/trig_cache.pyi +10 -0
- tnfr/node.py +542 -136
- tnfr/node.pyi +178 -0
- tnfr/observers.py +152 -35
- tnfr/observers.pyi +31 -0
- tnfr/ontosim.py +23 -19
- tnfr/ontosim.pyi +28 -0
- tnfr/operators/__init__.py +601 -82
- tnfr/operators/__init__.pyi +45 -0
- tnfr/operators/definitions.py +513 -0
- tnfr/operators/definitions.pyi +78 -0
- tnfr/operators/grammar.py +760 -0
- tnfr/operators/jitter.py +107 -38
- tnfr/operators/jitter.pyi +11 -0
- tnfr/operators/registry.py +75 -0
- tnfr/operators/registry.pyi +13 -0
- tnfr/operators/remesh.py +149 -88
- tnfr/py.typed +0 -0
- tnfr/rng.py +46 -143
- tnfr/rng.pyi +14 -0
- tnfr/schemas/__init__.py +8 -0
- tnfr/schemas/grammar.json +94 -0
- tnfr/selector.py +25 -19
- tnfr/selector.pyi +19 -0
- tnfr/sense.py +72 -62
- tnfr/sense.pyi +23 -0
- tnfr/structural.py +522 -262
- tnfr/structural.pyi +69 -0
- tnfr/telemetry/__init__.py +35 -0
- tnfr/telemetry/cache_metrics.py +226 -0
- tnfr/telemetry/nu_f.py +423 -0
- tnfr/telemetry/nu_f.pyi +123 -0
- tnfr/telemetry/verbosity.py +37 -0
- tnfr/tokens.py +1 -3
- tnfr/tokens.pyi +36 -0
- tnfr/trace.py +270 -113
- tnfr/trace.pyi +40 -0
- tnfr/types.py +574 -6
- tnfr/types.pyi +331 -0
- tnfr/units.py +69 -0
- tnfr/units.pyi +16 -0
- tnfr/utils/__init__.py +217 -0
- tnfr/utils/__init__.pyi +202 -0
- tnfr/utils/cache.py +2395 -0
- tnfr/utils/cache.pyi +468 -0
- tnfr/utils/chunks.py +104 -0
- tnfr/utils/chunks.pyi +21 -0
- tnfr/{collections_utils.py → utils/data.py} +147 -90
- tnfr/utils/data.pyi +64 -0
- tnfr/utils/graph.py +85 -0
- tnfr/utils/graph.pyi +10 -0
- tnfr/utils/init.py +770 -0
- tnfr/utils/init.pyi +78 -0
- tnfr/utils/io.py +456 -0
- tnfr/{helpers → utils}/numeric.py +51 -24
- tnfr/utils/numeric.pyi +21 -0
- tnfr/validation/__init__.py +113 -0
- tnfr/validation/__init__.pyi +77 -0
- tnfr/validation/compatibility.py +95 -0
- tnfr/validation/compatibility.pyi +6 -0
- tnfr/validation/grammar.py +71 -0
- tnfr/validation/grammar.pyi +40 -0
- tnfr/validation/graph.py +138 -0
- tnfr/validation/graph.pyi +17 -0
- tnfr/validation/rules.py +281 -0
- tnfr/validation/rules.pyi +55 -0
- tnfr/validation/runtime.py +263 -0
- tnfr/validation/runtime.pyi +31 -0
- tnfr/validation/soft_filters.py +170 -0
- tnfr/validation/soft_filters.pyi +37 -0
- tnfr/validation/spectral.py +159 -0
- tnfr/validation/spectral.pyi +46 -0
- tnfr/validation/syntax.py +40 -0
- tnfr/validation/syntax.pyi +10 -0
- tnfr/validation/window.py +39 -0
- tnfr/validation/window.pyi +1 -0
- tnfr/viz/__init__.py +9 -0
- tnfr/viz/matplotlib.py +246 -0
- tnfr-7.0.0.dist-info/METADATA +179 -0
- tnfr-7.0.0.dist-info/RECORD +185 -0
- {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/licenses/LICENSE.md +1 -1
- tnfr/grammar.py +0 -344
- tnfr/graph_utils.py +0 -84
- tnfr/helpers/__init__.py +0 -71
- tnfr/import_utils.py +0 -228
- tnfr/json_utils.py +0 -162
- tnfr/logging_utils.py +0 -116
- tnfr/presets.py +0 -60
- tnfr/validators.py +0 -84
- tnfr/value_utils.py +0 -59
- tnfr-4.5.2.dist-info/METADATA +0 -379
- tnfr-4.5.2.dist-info/RECORD +0 -67
- {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/WHEEL +0 -0
- {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/entry_points.txt +0 -0
- {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/top_level.txt +0 -0
tnfr/rng.py
CHANGED
|
@@ -2,17 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import random
|
|
6
5
|
import hashlib
|
|
6
|
+
import random
|
|
7
7
|
import struct
|
|
8
|
-
from
|
|
9
|
-
from typing import Any, Generic, Hashable, TypeVar
|
|
8
|
+
from typing import Any, cast
|
|
10
9
|
|
|
10
|
+
from cachetools import cached # type: ignore[import-untyped]
|
|
11
11
|
|
|
12
|
-
from cachetools import LRUCache, cached
|
|
13
12
|
from .constants import DEFAULTS, get_param
|
|
14
|
-
from .graph_utils import get_graph
|
|
15
13
|
from .locking import get_lock
|
|
14
|
+
from .types import GraphLike, TNFRGraph
|
|
15
|
+
from .utils import (
|
|
16
|
+
ScopedCounterCache,
|
|
17
|
+
_SeedHashCache,
|
|
18
|
+
build_cache_manager,
|
|
19
|
+
get_graph,
|
|
20
|
+
)
|
|
16
21
|
|
|
17
22
|
MASK64 = 0xFFFFFFFFFFFFFFFF
|
|
18
23
|
|
|
@@ -21,147 +26,43 @@ _DEFAULT_CACHE_MAXSIZE = int(DEFAULTS.get("JITTER_CACHE_SIZE", 128))
|
|
|
21
26
|
_CACHE_MAXSIZE = _DEFAULT_CACHE_MAXSIZE
|
|
22
27
|
_CACHE_LOCKED = False
|
|
23
28
|
|
|
24
|
-
K = TypeVar("K", bound=Hashable)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class _SeedHashCache(MutableMapping[tuple[int, int], int]):
|
|
28
|
-
"""Mutable mapping proxy exposing a configurable LRU cache."""
|
|
29
|
-
|
|
30
|
-
def __init__(self, maxsize: int) -> None:
|
|
31
|
-
self._maxsize = 0
|
|
32
|
-
self._cache: LRUCache[tuple[int, int], int] | None = None
|
|
33
|
-
self.configure(maxsize)
|
|
34
|
-
|
|
35
|
-
def configure(self, maxsize: int) -> None:
|
|
36
|
-
"""Configure internal cache size, clearing previous entries."""
|
|
37
|
-
|
|
38
|
-
self._maxsize = int(maxsize)
|
|
39
|
-
if self._maxsize <= 0:
|
|
40
|
-
self._cache = None
|
|
41
|
-
else:
|
|
42
|
-
self._cache = LRUCache(maxsize=self._maxsize)
|
|
43
|
-
|
|
44
|
-
def __getitem__(self, key: tuple[int, int]) -> int:
|
|
45
|
-
if self._cache is None:
|
|
46
|
-
raise KeyError(key)
|
|
47
|
-
return self._cache[key]
|
|
48
|
-
|
|
49
|
-
def __setitem__(self, key: tuple[int, int], value: int) -> None:
|
|
50
|
-
if self._cache is not None:
|
|
51
|
-
self._cache[key] = value
|
|
52
|
-
|
|
53
|
-
def __delitem__(self, key: tuple[int, int]) -> None:
|
|
54
|
-
if self._cache is None:
|
|
55
|
-
raise KeyError(key)
|
|
56
|
-
del self._cache[key]
|
|
57
|
-
|
|
58
|
-
def __iter__(self) -> Iterator[tuple[int, int]]:
|
|
59
|
-
if self._cache is None:
|
|
60
|
-
return iter(())
|
|
61
|
-
return iter(self._cache)
|
|
62
|
-
|
|
63
|
-
def __len__(self) -> int:
|
|
64
|
-
if self._cache is None:
|
|
65
|
-
return 0
|
|
66
|
-
return len(self._cache)
|
|
67
|
-
|
|
68
|
-
def clear(self) -> None: # type: ignore[override]
|
|
69
|
-
if self._cache is not None:
|
|
70
|
-
self._cache.clear()
|
|
71
|
-
|
|
72
|
-
@property
|
|
73
|
-
def maxsize(self) -> int:
|
|
74
|
-
return self._maxsize
|
|
75
|
-
|
|
76
|
-
@property
|
|
77
|
-
def enabled(self) -> bool:
|
|
78
|
-
return self._cache is not None
|
|
79
|
-
|
|
80
|
-
@property
|
|
81
|
-
def data(self) -> LRUCache[tuple[int, int], int] | None:
|
|
82
|
-
"""Expose the underlying cache for diagnostics/tests."""
|
|
83
|
-
|
|
84
|
-
return self._cache
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
class ScopedCounterCache(Generic[K]):
|
|
88
|
-
"""Thread-safe LRU cache storing monotonic counters by ``key``."""
|
|
89
|
-
|
|
90
|
-
def __init__(self, name: str, max_entries: int) -> None:
|
|
91
|
-
if max_entries < 0:
|
|
92
|
-
raise ValueError("max_entries must be non-negative")
|
|
93
|
-
self._lock = get_lock(name)
|
|
94
|
-
self._max_entries = int(max_entries)
|
|
95
|
-
self._cache: LRUCache[K, int] = LRUCache(maxsize=self._max_entries)
|
|
96
|
-
|
|
97
|
-
@property
|
|
98
|
-
def lock(self):
|
|
99
|
-
"""Return the lock guarding access to the underlying cache."""
|
|
100
|
-
|
|
101
|
-
return self._lock
|
|
102
|
-
|
|
103
|
-
@property
|
|
104
|
-
def max_entries(self) -> int:
|
|
105
|
-
"""Return the configured maximum number of cached entries."""
|
|
106
29
|
|
|
107
|
-
|
|
30
|
+
_RNG_CACHE_MANAGER = build_cache_manager(default_capacity=_DEFAULT_CACHE_MAXSIZE)
|
|
108
31
|
|
|
109
|
-
@property
|
|
110
|
-
def cache(self) -> LRUCache[K, int]:
|
|
111
|
-
"""Expose the underlying ``LRUCache`` for inspection."""
|
|
112
32
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
) -> None:
|
|
118
|
-
"""Resize or reset the cache keeping previous settings."""
|
|
119
|
-
|
|
120
|
-
size = self._max_entries if max_entries is None else int(max_entries)
|
|
121
|
-
if size < 0:
|
|
122
|
-
raise ValueError("max_entries must be non-negative")
|
|
123
|
-
with self._lock:
|
|
124
|
-
if size != self._max_entries:
|
|
125
|
-
self._max_entries = size
|
|
126
|
-
force = True
|
|
127
|
-
if force:
|
|
128
|
-
self._cache = LRUCache(maxsize=self._max_entries)
|
|
129
|
-
|
|
130
|
-
def clear(self) -> None:
|
|
131
|
-
"""Clear stored counters preserving ``max_entries``."""
|
|
132
|
-
|
|
133
|
-
self.configure(force=True)
|
|
134
|
-
|
|
135
|
-
def bump(self, key: K) -> int:
|
|
136
|
-
"""Return current counter for ``key`` and increment it atomically."""
|
|
33
|
+
_seed_hash_cache = _SeedHashCache(
|
|
34
|
+
manager=_RNG_CACHE_MANAGER,
|
|
35
|
+
default_maxsize=_DEFAULT_CACHE_MAXSIZE,
|
|
36
|
+
)
|
|
137
37
|
|
|
138
|
-
with self._lock:
|
|
139
|
-
value = int(self._cache.get(key, 0))
|
|
140
|
-
self._cache[key] = value + 1
|
|
141
|
-
return value
|
|
142
38
|
|
|
143
|
-
|
|
144
|
-
|
|
39
|
+
def _compute_seed_hash(seed_int: int, key_int: int) -> int:
|
|
40
|
+
seed_bytes = struct.pack(
|
|
41
|
+
">QQ",
|
|
42
|
+
seed_int & MASK64,
|
|
43
|
+
key_int & MASK64,
|
|
44
|
+
)
|
|
45
|
+
return int.from_bytes(hashlib.blake2b(seed_bytes, digest_size=8).digest(), "big")
|
|
145
46
|
|
|
146
47
|
|
|
147
|
-
_seed_hash_cache =
|
|
48
|
+
@cached(cache=_seed_hash_cache, lock=_RNG_LOCK)
|
|
49
|
+
def _cached_seed_hash(seed_int: int, key_int: int) -> int:
|
|
50
|
+
return _compute_seed_hash(seed_int, key_int)
|
|
148
51
|
|
|
149
52
|
|
|
150
|
-
@cached(cache=_seed_hash_cache, lock=_RNG_LOCK)
|
|
151
53
|
def seed_hash(seed_int: int, key_int: int) -> int:
|
|
152
54
|
"""Return a 64-bit hash derived from ``seed_int`` and ``key_int``."""
|
|
153
55
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
)
|
|
159
|
-
return int.from_bytes(
|
|
160
|
-
hashlib.blake2b(seed_bytes, digest_size=8).digest(), "big"
|
|
161
|
-
)
|
|
56
|
+
if _CACHE_MAXSIZE <= 0 or not _seed_hash_cache.enabled:
|
|
57
|
+
return _compute_seed_hash(seed_int, key_int)
|
|
58
|
+
return _cached_seed_hash(seed_int, key_int)
|
|
59
|
+
|
|
162
60
|
|
|
61
|
+
seed_hash.cache_clear = cast(Any, _cached_seed_hash).cache_clear # type: ignore[attr-defined]
|
|
62
|
+
seed_hash.cache = _seed_hash_cache # type: ignore[attr-defined]
|
|
163
63
|
|
|
164
|
-
|
|
64
|
+
|
|
65
|
+
def _sync_cache_size(G: TNFRGraph | GraphLike | None) -> None:
|
|
165
66
|
"""Synchronise cache size with ``G`` when needed."""
|
|
166
67
|
|
|
167
68
|
global _CACHE_MAXSIZE
|
|
@@ -169,12 +70,14 @@ def _sync_cache_size(G: Any | None) -> None:
|
|
|
169
70
|
return
|
|
170
71
|
size = get_cache_maxsize(G)
|
|
171
72
|
with _RNG_LOCK:
|
|
172
|
-
if size !=
|
|
73
|
+
if size != _seed_hash_cache.maxsize:
|
|
173
74
|
_seed_hash_cache.configure(size)
|
|
174
|
-
_CACHE_MAXSIZE =
|
|
75
|
+
_CACHE_MAXSIZE = _seed_hash_cache.maxsize
|
|
175
76
|
|
|
176
77
|
|
|
177
|
-
def make_rng(
|
|
78
|
+
def make_rng(
|
|
79
|
+
seed: int, key: int, G: TNFRGraph | GraphLike | None = None
|
|
80
|
+
) -> random.Random:
|
|
178
81
|
"""Return a ``random.Random`` for ``seed`` and ``key``.
|
|
179
82
|
|
|
180
83
|
When ``G`` is provided, ``JITTER_CACHE_SIZE`` is read from ``G`` and the
|
|
@@ -188,17 +91,17 @@ def make_rng(seed: int, key: int, G: Any | None = None) -> random.Random:
|
|
|
188
91
|
|
|
189
92
|
def clear_rng_cache() -> None:
|
|
190
93
|
"""Clear cached seed hashes."""
|
|
191
|
-
if
|
|
94
|
+
if _seed_hash_cache.maxsize <= 0 or not _seed_hash_cache.enabled:
|
|
192
95
|
return
|
|
193
|
-
seed_hash.cache_clear()
|
|
96
|
+
seed_hash.cache_clear() # type: ignore[attr-defined]
|
|
194
97
|
|
|
195
98
|
|
|
196
|
-
def get_cache_maxsize(G:
|
|
99
|
+
def get_cache_maxsize(G: TNFRGraph | GraphLike) -> int:
|
|
197
100
|
"""Return RNG cache maximum size for ``G``."""
|
|
198
101
|
return int(get_param(G, "JITTER_CACHE_SIZE"))
|
|
199
102
|
|
|
200
103
|
|
|
201
|
-
def cache_enabled(G:
|
|
104
|
+
def cache_enabled(G: TNFRGraph | GraphLike | None = None) -> bool:
|
|
202
105
|
"""Return ``True`` if RNG caching is enabled.
|
|
203
106
|
|
|
204
107
|
When ``G`` is provided, the cache size is synchronised with
|
|
@@ -207,12 +110,12 @@ def cache_enabled(G: Any | None = None) -> bool:
|
|
|
207
110
|
# Only synchronise the cache size with ``G`` when caching is enabled. This
|
|
208
111
|
# preserves explicit calls to :func:`set_cache_maxsize(0)` which are used in
|
|
209
112
|
# tests to temporarily disable caching regardless of graph defaults.
|
|
210
|
-
if
|
|
113
|
+
if _seed_hash_cache.maxsize > 0:
|
|
211
114
|
_sync_cache_size(G)
|
|
212
|
-
return
|
|
115
|
+
return _seed_hash_cache.maxsize > 0
|
|
213
116
|
|
|
214
117
|
|
|
215
|
-
def base_seed(G:
|
|
118
|
+
def base_seed(G: TNFRGraph | GraphLike) -> int:
|
|
216
119
|
"""Return base RNG seed stored in ``G.graph``."""
|
|
217
120
|
graph = get_graph(G)
|
|
218
121
|
return int(graph.get("RANDOM_SEED", 0))
|
|
@@ -238,7 +141,7 @@ def set_cache_maxsize(size: int) -> None:
|
|
|
238
141
|
raise ValueError("size must be non-negative")
|
|
239
142
|
with _RNG_LOCK:
|
|
240
143
|
_seed_hash_cache.configure(new_size)
|
|
241
|
-
_CACHE_MAXSIZE =
|
|
144
|
+
_CACHE_MAXSIZE = _seed_hash_cache.maxsize
|
|
242
145
|
_CACHE_LOCKED = new_size != _DEFAULT_CACHE_MAXSIZE
|
|
243
146
|
|
|
244
147
|
|
tnfr/rng.pyi
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
__all__: Any
|
|
4
|
+
|
|
5
|
+
def __getattr__(name: str) -> Any: ...
|
|
6
|
+
|
|
7
|
+
ScopedCounterCache: Any
|
|
8
|
+
base_seed: Any
|
|
9
|
+
cache_enabled: Any
|
|
10
|
+
clear_rng_cache: Any
|
|
11
|
+
get_cache_maxsize: Any
|
|
12
|
+
make_rng: Any
|
|
13
|
+
seed_hash: Any
|
|
14
|
+
set_cache_maxsize: Any
|
tnfr/schemas/__init__.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://tnfr.io/schemas/grammar.json",
|
|
4
|
+
"title": "TNFR Grammar Configuration",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"definitions": {
|
|
7
|
+
"glyphCode": {
|
|
8
|
+
"description": "Canonical glyph identifier in uppercase notation.",
|
|
9
|
+
"type": "string",
|
|
10
|
+
"pattern": "^[A-Z][A-Z_]*$"
|
|
11
|
+
},
|
|
12
|
+
"probability": {
|
|
13
|
+
"description": "Threshold constrained to the [0, 1] interval.",
|
|
14
|
+
"type": "number",
|
|
15
|
+
"minimum": 0.0,
|
|
16
|
+
"maximum": 1.0
|
|
17
|
+
},
|
|
18
|
+
"cfg_soft": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"description": "Soft grammar preferences applied before canonical automaton rules.",
|
|
21
|
+
"properties": {
|
|
22
|
+
"window": {
|
|
23
|
+
"description": "History window inspected to avoid short-term glyph repetitions.",
|
|
24
|
+
"type": "integer",
|
|
25
|
+
"minimum": 0
|
|
26
|
+
},
|
|
27
|
+
"avoid_repeats": {
|
|
28
|
+
"description": "Glyph codes that should be substituted when repeated inside the window.",
|
|
29
|
+
"type": "array",
|
|
30
|
+
"items": { "$ref": "#/definitions/glyphCode" },
|
|
31
|
+
"uniqueItems": true
|
|
32
|
+
},
|
|
33
|
+
"fallbacks": {
|
|
34
|
+
"description": "Mapping of glyph codes to their explicit substitution when repetition is detected.",
|
|
35
|
+
"type": "object",
|
|
36
|
+
"additionalProperties": { "$ref": "#/definitions/glyphCode" }
|
|
37
|
+
},
|
|
38
|
+
"force_dnfr": {
|
|
39
|
+
"description": "Minimum |ΔNFR| normalised score that bypasses soft filtering.",
|
|
40
|
+
"$ref": "#/definitions/probability"
|
|
41
|
+
},
|
|
42
|
+
"force_accel": {
|
|
43
|
+
"description": "Minimum acceleration normalised score that bypasses soft filtering.",
|
|
44
|
+
"$ref": "#/definitions/probability"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"additionalProperties": true
|
|
48
|
+
},
|
|
49
|
+
"cfg_canon": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"description": "Canonical grammar thresholds enforced by the automaton.",
|
|
52
|
+
"properties": {
|
|
53
|
+
"enabled": {
|
|
54
|
+
"description": "Toggle canonical grammar enforcement during metric runs.",
|
|
55
|
+
"type": "boolean"
|
|
56
|
+
},
|
|
57
|
+
"zhir_requires_oz_window": {
|
|
58
|
+
"description": "Window requiring a DISSONANCE glyph before a MUTATION.",
|
|
59
|
+
"type": "integer",
|
|
60
|
+
"minimum": 0
|
|
61
|
+
},
|
|
62
|
+
"zhir_dnfr_min": {
|
|
63
|
+
"description": "Minimum normalised |ΔNFR| required to allow MUTATION without recent DISSONANCE.",
|
|
64
|
+
"type": "number",
|
|
65
|
+
"minimum": 0.0
|
|
66
|
+
},
|
|
67
|
+
"thol_min_len": {
|
|
68
|
+
"description": "Minimum number of THOL glyphs before canonical closure is allowed.",
|
|
69
|
+
"type": "integer",
|
|
70
|
+
"minimum": 0
|
|
71
|
+
},
|
|
72
|
+
"thol_max_len": {
|
|
73
|
+
"description": "Maximum number of THOL glyphs tolerated before forcing closure.",
|
|
74
|
+
"type": "integer",
|
|
75
|
+
"minimum": 0
|
|
76
|
+
},
|
|
77
|
+
"thol_close_dnfr": {
|
|
78
|
+
"description": "Upper bound on normalised |ΔNFR| that triggers THOL closure.",
|
|
79
|
+
"$ref": "#/definitions/probability"
|
|
80
|
+
},
|
|
81
|
+
"si_high": {
|
|
82
|
+
"description": "Sense index threshold: Si at or above this resolves THOL closures with silence; lower Si forces contraction.",
|
|
83
|
+
"$ref": "#/definitions/probability"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"additionalProperties": true
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"properties": {
|
|
90
|
+
"cfg_soft": { "$ref": "#/definitions/cfg_soft" },
|
|
91
|
+
"cfg_canon": { "$ref": "#/definitions/cfg_canon" }
|
|
92
|
+
},
|
|
93
|
+
"additionalProperties": false
|
|
94
|
+
}
|
tnfr/selector.py
CHANGED
|
@@ -8,32 +8,33 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
import threading
|
|
10
10
|
from operator import itemgetter
|
|
11
|
-
from typing import Any, Mapping,
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Mapping, cast
|
|
12
12
|
from weakref import WeakKeyDictionary
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING: # pragma: no cover
|
|
15
|
-
import networkx as nx
|
|
15
|
+
import networkx as nx
|
|
16
16
|
|
|
17
17
|
from .constants import DEFAULTS
|
|
18
18
|
from .constants.core import SELECTOR_THRESHOLD_DEFAULTS
|
|
19
|
-
from .
|
|
19
|
+
from .utils import clamp01
|
|
20
20
|
from .metrics.common import compute_dnfr_accel_max
|
|
21
|
-
from .
|
|
22
|
-
|
|
21
|
+
from .types import SelectorNorms, SelectorThresholds, SelectorWeights
|
|
22
|
+
from .utils import is_non_string_sequence
|
|
23
23
|
|
|
24
24
|
HYSTERESIS_GLYPHS: set[str] = {"IL", "OZ", "ZHIR", "THOL", "NAV", "RA"}
|
|
25
25
|
|
|
26
26
|
__all__ = (
|
|
27
27
|
"_selector_thresholds",
|
|
28
|
-
"
|
|
28
|
+
"_selector_norms",
|
|
29
29
|
"_calc_selector_score",
|
|
30
30
|
"_apply_selector_hysteresis",
|
|
31
31
|
)
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
_SelectorThresholdItems = tuple[tuple[str, float], ...]
|
|
34
35
|
_SelectorThresholdCacheEntry = tuple[
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
_SelectorThresholdItems,
|
|
37
|
+
SelectorThresholds,
|
|
37
38
|
]
|
|
38
39
|
_SELECTOR_THRESHOLD_CACHE: WeakKeyDictionary[
|
|
39
40
|
"nx.Graph",
|
|
@@ -42,7 +43,7 @@ _SELECTOR_THRESHOLD_CACHE: WeakKeyDictionary[
|
|
|
42
43
|
_SELECTOR_THRESHOLD_CACHE_LOCK = threading.Lock()
|
|
43
44
|
|
|
44
45
|
|
|
45
|
-
def _sorted_items(mapping: Mapping[str, float]) ->
|
|
46
|
+
def _sorted_items(mapping: Mapping[str, float]) -> _SelectorThresholdItems:
|
|
46
47
|
"""Return mapping items sorted by key.
|
|
47
48
|
|
|
48
49
|
Parameters
|
|
@@ -59,8 +60,8 @@ def _sorted_items(mapping: Mapping[str, float]) -> tuple[tuple[str, float], ...]
|
|
|
59
60
|
|
|
60
61
|
|
|
61
62
|
def _compute_selector_thresholds(
|
|
62
|
-
thr_sel_items:
|
|
63
|
-
) ->
|
|
63
|
+
thr_sel_items: _SelectorThresholdItems,
|
|
64
|
+
) -> SelectorThresholds:
|
|
64
65
|
"""Construct selector thresholds for a graph.
|
|
65
66
|
|
|
66
67
|
Parameters
|
|
@@ -79,10 +80,10 @@ def _compute_selector_thresholds(
|
|
|
79
80
|
for key, default in SELECTOR_THRESHOLD_DEFAULTS.items():
|
|
80
81
|
val = thr_sel.get(key, default)
|
|
81
82
|
out[key] = clamp01(float(val))
|
|
82
|
-
return out
|
|
83
|
+
return cast(SelectorThresholds, out)
|
|
83
84
|
|
|
84
85
|
|
|
85
|
-
def _selector_thresholds(G: "nx.Graph") ->
|
|
86
|
+
def _selector_thresholds(G: "nx.Graph") -> SelectorThresholds:
|
|
86
87
|
"""Return normalised thresholds for Si, ΔNFR and acceleration.
|
|
87
88
|
|
|
88
89
|
Parameters
|
|
@@ -114,8 +115,8 @@ def _selector_thresholds(G: "nx.Graph") -> dict[str, float]:
|
|
|
114
115
|
return thresholds
|
|
115
116
|
|
|
116
117
|
|
|
117
|
-
def
|
|
118
|
-
"""Compute and cache norms for ΔNFR and acceleration.
|
|
118
|
+
def _selector_norms(G: "nx.Graph") -> SelectorNorms:
|
|
119
|
+
"""Compute and cache selector norms for ΔNFR and acceleration.
|
|
119
120
|
|
|
120
121
|
Parameters
|
|
121
122
|
----------
|
|
@@ -134,7 +135,7 @@ def _norms_para_selector(G: "nx.Graph") -> dict:
|
|
|
134
135
|
|
|
135
136
|
|
|
136
137
|
def _calc_selector_score(
|
|
137
|
-
Si: float, dnfr: float, accel: float, weights:
|
|
138
|
+
Si: float, dnfr: float, accel: float, weights: SelectorWeights
|
|
138
139
|
) -> float:
|
|
139
140
|
"""Compute weighted selector score.
|
|
140
141
|
|
|
@@ -167,7 +168,7 @@ def _apply_selector_hysteresis(
|
|
|
167
168
|
dnfr: float,
|
|
168
169
|
accel: float,
|
|
169
170
|
thr: dict[str, float],
|
|
170
|
-
margin: float,
|
|
171
|
+
margin: float | None,
|
|
171
172
|
) -> str | None:
|
|
172
173
|
"""Apply hysteresis when values are near thresholds.
|
|
173
174
|
|
|
@@ -183,8 +184,10 @@ def _apply_selector_hysteresis(
|
|
|
183
184
|
Normalised acceleration.
|
|
184
185
|
thr : dict[str, float]
|
|
185
186
|
Thresholds returned by :func:`_selector_thresholds`.
|
|
186
|
-
margin : float
|
|
187
|
-
|
|
187
|
+
margin : float or None
|
|
188
|
+
When positive, distance from thresholds below which the previous
|
|
189
|
+
glyph is reused. Falsy margins disable hysteresis entirely, letting
|
|
190
|
+
selectors bypass the reuse logic.
|
|
188
191
|
|
|
189
192
|
Returns
|
|
190
193
|
-------
|
|
@@ -192,6 +195,9 @@ def _apply_selector_hysteresis(
|
|
|
192
195
|
Previous glyph if hysteresis applies, otherwise ``None``.
|
|
193
196
|
"""
|
|
194
197
|
# Batch extraction reduces dictionary lookups inside loops.
|
|
198
|
+
if not margin:
|
|
199
|
+
return None
|
|
200
|
+
|
|
195
201
|
si_hi, si_lo, dnfr_hi, dnfr_lo, accel_hi, accel_lo = itemgetter(
|
|
196
202
|
"si_hi", "si_lo", "dnfr_hi", "dnfr_lo", "accel_hi", "accel_lo"
|
|
197
203
|
)(thr)
|
tnfr/selector.pyi
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Mapping
|
|
4
|
+
|
|
5
|
+
__all__: Any
|
|
6
|
+
|
|
7
|
+
def __getattr__(name: str) -> Any: ...
|
|
8
|
+
def _apply_selector_hysteresis(
|
|
9
|
+
nd: dict[str, Any],
|
|
10
|
+
Si: float,
|
|
11
|
+
dnfr: float,
|
|
12
|
+
accel: float,
|
|
13
|
+
thr: Mapping[str, float],
|
|
14
|
+
margin: float | None,
|
|
15
|
+
) -> str | None: ...
|
|
16
|
+
|
|
17
|
+
_calc_selector_score: Any
|
|
18
|
+
_selector_norms: Any
|
|
19
|
+
_selector_thresholds: Any
|