tnfr 4.5.2__py3-none-any.whl → 6.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.
- tnfr/__init__.py +228 -49
- tnfr/__init__.pyi +40 -0
- tnfr/_compat.py +11 -0
- tnfr/_version.py +7 -0
- tnfr/_version.pyi +7 -0
- tnfr/alias.py +106 -21
- tnfr/alias.pyi +140 -0
- tnfr/cache.py +666 -512
- tnfr/cache.pyi +232 -0
- tnfr/callback_utils.py +2 -9
- tnfr/callback_utils.pyi +105 -0
- tnfr/cli/__init__.py +21 -7
- tnfr/cli/__init__.pyi +47 -0
- tnfr/cli/arguments.py +42 -20
- tnfr/cli/arguments.pyi +33 -0
- tnfr/cli/execution.py +54 -20
- tnfr/cli/execution.pyi +80 -0
- tnfr/cli/utils.py +0 -2
- tnfr/cli/utils.pyi +8 -0
- tnfr/config/__init__.py +12 -0
- tnfr/config/__init__.pyi +8 -0
- tnfr/config/constants.py +104 -0
- tnfr/config/constants.pyi +12 -0
- tnfr/{config.py → config/init.py} +11 -7
- tnfr/config/init.pyi +8 -0
- tnfr/config/operator_names.py +106 -0
- tnfr/config/operator_names.pyi +28 -0
- tnfr/config/presets.py +104 -0
- tnfr/config/presets.pyi +7 -0
- tnfr/constants/__init__.py +78 -24
- tnfr/constants/__init__.pyi +104 -0
- tnfr/constants/core.py +1 -2
- tnfr/constants/core.pyi +17 -0
- tnfr/constants/init.pyi +12 -0
- tnfr/constants/metric.py +4 -12
- tnfr/constants/metric.pyi +19 -0
- tnfr/constants_glyphs.py +9 -91
- tnfr/constants_glyphs.pyi +12 -0
- tnfr/dynamics/__init__.py +112 -634
- tnfr/dynamics/__init__.pyi +83 -0
- tnfr/dynamics/adaptation.py +201 -0
- tnfr/dynamics/aliases.py +22 -0
- tnfr/dynamics/coordination.py +343 -0
- tnfr/dynamics/dnfr.py +1936 -354
- tnfr/dynamics/dnfr.pyi +33 -0
- tnfr/dynamics/integrators.py +369 -75
- tnfr/dynamics/integrators.pyi +35 -0
- tnfr/dynamics/runtime.py +521 -0
- tnfr/dynamics/sampling.py +8 -5
- tnfr/dynamics/sampling.pyi +7 -0
- tnfr/dynamics/selectors.py +680 -0
- tnfr/execution.py +56 -41
- tnfr/execution.pyi +65 -0
- tnfr/flatten.py +7 -7
- tnfr/flatten.pyi +28 -0
- tnfr/gamma.py +54 -37
- tnfr/gamma.pyi +40 -0
- tnfr/glyph_history.py +85 -38
- tnfr/glyph_history.pyi +53 -0
- tnfr/grammar.py +19 -338
- tnfr/grammar.pyi +13 -0
- tnfr/helpers/__init__.py +110 -30
- tnfr/helpers/__init__.pyi +66 -0
- tnfr/helpers/numeric.py +1 -0
- tnfr/helpers/numeric.pyi +12 -0
- tnfr/immutable.py +55 -19
- tnfr/immutable.pyi +37 -0
- tnfr/initialization.py +12 -10
- tnfr/initialization.pyi +73 -0
- tnfr/io.py +99 -34
- tnfr/io.pyi +11 -0
- tnfr/locking.pyi +7 -0
- tnfr/metrics/__init__.pyi +20 -0
- tnfr/metrics/coherence.py +934 -294
- tnfr/metrics/common.py +1 -3
- tnfr/metrics/common.pyi +15 -0
- tnfr/metrics/core.py +192 -34
- tnfr/metrics/core.pyi +13 -0
- tnfr/metrics/diagnosis.py +707 -101
- tnfr/metrics/diagnosis.pyi +89 -0
- tnfr/metrics/export.py +27 -13
- tnfr/metrics/glyph_timing.py +218 -38
- tnfr/metrics/reporting.py +22 -18
- tnfr/metrics/reporting.pyi +12 -0
- tnfr/metrics/sense_index.py +199 -25
- tnfr/metrics/sense_index.pyi +9 -0
- tnfr/metrics/trig.py +53 -18
- tnfr/metrics/trig.pyi +12 -0
- tnfr/metrics/trig_cache.py +3 -7
- tnfr/metrics/trig_cache.pyi +10 -0
- tnfr/node.py +148 -125
- tnfr/node.pyi +161 -0
- tnfr/observers.py +44 -30
- tnfr/observers.pyi +46 -0
- tnfr/ontosim.py +14 -13
- tnfr/ontosim.pyi +33 -0
- tnfr/operators/__init__.py +84 -52
- tnfr/operators/__init__.pyi +31 -0
- tnfr/operators/definitions.py +181 -0
- tnfr/operators/definitions.pyi +92 -0
- tnfr/operators/jitter.py +86 -23
- tnfr/operators/jitter.pyi +11 -0
- tnfr/operators/registry.py +80 -0
- tnfr/operators/registry.pyi +15 -0
- tnfr/operators/remesh.py +141 -57
- tnfr/presets.py +9 -54
- tnfr/presets.pyi +7 -0
- tnfr/py.typed +0 -0
- tnfr/rng.py +259 -73
- tnfr/rng.pyi +14 -0
- tnfr/selector.py +24 -17
- tnfr/selector.pyi +19 -0
- tnfr/sense.py +55 -43
- tnfr/sense.pyi +30 -0
- tnfr/structural.py +44 -267
- tnfr/structural.pyi +46 -0
- tnfr/telemetry/__init__.py +13 -0
- tnfr/telemetry/verbosity.py +37 -0
- tnfr/tokens.py +3 -2
- tnfr/tokens.pyi +41 -0
- tnfr/trace.py +272 -82
- tnfr/trace.pyi +68 -0
- tnfr/types.py +345 -6
- tnfr/types.pyi +145 -0
- tnfr/utils/__init__.py +158 -0
- tnfr/utils/__init__.pyi +133 -0
- tnfr/utils/cache.py +755 -0
- tnfr/utils/cache.pyi +156 -0
- tnfr/{collections_utils.py → utils/data.py} +57 -90
- tnfr/utils/data.pyi +73 -0
- tnfr/utils/graph.py +87 -0
- tnfr/utils/graph.pyi +10 -0
- tnfr/utils/init.py +746 -0
- tnfr/utils/init.pyi +85 -0
- tnfr/{json_utils.py → utils/io.py} +13 -18
- tnfr/utils/io.pyi +10 -0
- tnfr/utils/validators.py +130 -0
- tnfr/utils/validators.pyi +19 -0
- tnfr/validation/__init__.py +25 -0
- tnfr/validation/__init__.pyi +17 -0
- tnfr/validation/compatibility.py +59 -0
- tnfr/validation/compatibility.pyi +8 -0
- tnfr/validation/grammar.py +149 -0
- tnfr/validation/grammar.pyi +11 -0
- tnfr/validation/rules.py +194 -0
- tnfr/validation/rules.pyi +18 -0
- tnfr/validation/syntax.py +151 -0
- tnfr/validation/syntax.pyi +7 -0
- tnfr-6.0.0.dist-info/METADATA +135 -0
- tnfr-6.0.0.dist-info/RECORD +157 -0
- tnfr/graph_utils.py +0 -84
- tnfr/import_utils.py +0 -228
- tnfr/logging_utils.py +0 -116
- 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-6.0.0.dist-info}/WHEEL +0 -0
- {tnfr-4.5.2.dist-info → tnfr-6.0.0.dist-info}/entry_points.txt +0 -0
- {tnfr-4.5.2.dist-info → tnfr-6.0.0.dist-info}/licenses/LICENSE.md +0 -0
- {tnfr-4.5.2.dist-info → tnfr-6.0.0.dist-info}/top_level.txt +0 -0
tnfr/execution.py
CHANGED
|
@@ -4,28 +4,25 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from collections import deque
|
|
6
6
|
from collections.abc import Callable, Iterable, Sequence
|
|
7
|
-
from typing import Any, Optional
|
|
7
|
+
from typing import Any, Optional, cast
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
from ._compat import TypeAlias
|
|
10
10
|
|
|
11
|
-
from .
|
|
12
|
-
MAX_MATERIALIZE_DEFAULT,
|
|
13
|
-
ensure_collection,
|
|
14
|
-
is_non_string_sequence,
|
|
15
|
-
)
|
|
11
|
+
from .utils import MAX_MATERIALIZE_DEFAULT, ensure_collection, is_non_string_sequence
|
|
16
12
|
from .constants import get_param
|
|
17
13
|
from .dynamics import step
|
|
18
14
|
from .flatten import _flatten
|
|
19
15
|
from .glyph_history import ensure_history
|
|
20
|
-
from .grammar import apply_glyph_with_grammar
|
|
16
|
+
from .validation.grammar import apply_glyph_with_grammar
|
|
21
17
|
from .tokens import OpTag, TARGET, THOL, WAIT, Token
|
|
22
|
-
from .types import Glyph
|
|
18
|
+
from .types import Glyph, NodeId, TNFRGraph
|
|
23
19
|
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
AdvanceFn = Callable[[TNFRGraph], None]
|
|
21
|
+
TraceEntry = dict[str, Any]
|
|
22
|
+
ProgramTrace: TypeAlias = deque[TraceEntry]
|
|
26
23
|
HandlerFn = Callable[
|
|
27
|
-
[
|
|
28
|
-
Optional[
|
|
24
|
+
[TNFRGraph, Any, Optional[Sequence[NodeId]], ProgramTrace, AdvanceFn],
|
|
25
|
+
Optional[Sequence[NodeId]],
|
|
29
26
|
]
|
|
30
27
|
|
|
31
28
|
__all__ = [
|
|
@@ -45,7 +42,7 @@ __all__ = [
|
|
|
45
42
|
]
|
|
46
43
|
|
|
47
44
|
|
|
48
|
-
CANONICAL_PRESET_NAME = "
|
|
45
|
+
CANONICAL_PRESET_NAME = "canonical_example"
|
|
49
46
|
CANONICAL_PROGRAM_TOKENS: tuple[Token, ...] = (
|
|
50
47
|
Glyph.SHA,
|
|
51
48
|
Glyph.AL,
|
|
@@ -56,13 +53,13 @@ CANONICAL_PROGRAM_TOKENS: tuple[Token, ...] = (
|
|
|
56
53
|
)
|
|
57
54
|
|
|
58
55
|
|
|
59
|
-
def _window(G) -> int:
|
|
56
|
+
def _window(G: TNFRGraph) -> int:
|
|
60
57
|
return int(get_param(G, "GLYPH_HYSTERESIS_WINDOW"))
|
|
61
58
|
|
|
62
59
|
|
|
63
60
|
def _apply_glyph_to_targets(
|
|
64
|
-
G, g: Glyph | str, nodes: Optional[Iterable[
|
|
65
|
-
):
|
|
61
|
+
G: TNFRGraph, g: Glyph | str, nodes: Optional[Iterable[NodeId]] = None
|
|
62
|
+
) -> None:
|
|
66
63
|
"""Apply ``g`` to ``nodes`` (or all nodes) respecting the grammar."""
|
|
67
64
|
|
|
68
65
|
nodes_iter = G.nodes() if nodes is None else nodes
|
|
@@ -70,22 +67,22 @@ def _apply_glyph_to_targets(
|
|
|
70
67
|
apply_glyph_with_grammar(G, nodes_iter, g, w)
|
|
71
68
|
|
|
72
69
|
|
|
73
|
-
def _advance(G, step_fn: AdvanceFn):
|
|
70
|
+
def _advance(G: TNFRGraph, step_fn: AdvanceFn) -> None:
|
|
74
71
|
step_fn(G)
|
|
75
72
|
|
|
76
73
|
|
|
77
|
-
def _record_trace(trace:
|
|
74
|
+
def _record_trace(trace: ProgramTrace, G: TNFRGraph, op: OpTag, **data: Any) -> None:
|
|
78
75
|
trace.append({"t": float(G.graph.get("_t", 0.0)), "op": op.name, **data})
|
|
79
76
|
|
|
80
77
|
|
|
81
78
|
def _advance_and_record(
|
|
82
|
-
G,
|
|
83
|
-
trace:
|
|
79
|
+
G: TNFRGraph,
|
|
80
|
+
trace: ProgramTrace,
|
|
84
81
|
label: OpTag,
|
|
85
82
|
step_fn: AdvanceFn,
|
|
86
83
|
*,
|
|
87
84
|
times: int = 1,
|
|
88
|
-
**data,
|
|
85
|
+
**data: Any,
|
|
89
86
|
) -> None:
|
|
90
87
|
for _ in range(times):
|
|
91
88
|
_advance(G, step_fn)
|
|
@@ -93,40 +90,55 @@ def _advance_and_record(
|
|
|
93
90
|
|
|
94
91
|
|
|
95
92
|
def _handle_target(
|
|
96
|
-
G
|
|
97
|
-
|
|
93
|
+
G: TNFRGraph,
|
|
94
|
+
payload: TARGET,
|
|
95
|
+
_curr_target: Optional[Sequence[NodeId]],
|
|
96
|
+
trace: ProgramTrace,
|
|
97
|
+
_step_fn: AdvanceFn,
|
|
98
|
+
) -> Sequence[NodeId]:
|
|
98
99
|
"""Handle a ``TARGET`` token and return the active node set."""
|
|
99
100
|
|
|
100
101
|
nodes_src = G.nodes() if payload.nodes is None else payload.nodes
|
|
101
102
|
nodes = ensure_collection(nodes_src, max_materialize=None)
|
|
102
|
-
|
|
103
|
+
if is_non_string_sequence(nodes):
|
|
104
|
+
curr_target = cast(Sequence[NodeId], nodes)
|
|
105
|
+
else:
|
|
106
|
+
curr_target = tuple(nodes)
|
|
103
107
|
_record_trace(trace, G, OpTag.TARGET, n=len(curr_target))
|
|
104
108
|
return curr_target
|
|
105
109
|
|
|
106
110
|
|
|
107
111
|
def _handle_wait(
|
|
108
|
-
G
|
|
109
|
-
|
|
112
|
+
G: TNFRGraph,
|
|
113
|
+
steps: int,
|
|
114
|
+
curr_target: Optional[Sequence[NodeId]],
|
|
115
|
+
trace: ProgramTrace,
|
|
116
|
+
step_fn: AdvanceFn,
|
|
117
|
+
) -> Optional[Sequence[NodeId]]:
|
|
110
118
|
_advance_and_record(G, trace, OpTag.WAIT, step_fn, times=steps, k=steps)
|
|
111
119
|
return curr_target
|
|
112
120
|
|
|
113
121
|
|
|
114
122
|
def _handle_glyph(
|
|
115
|
-
G,
|
|
116
|
-
g: str,
|
|
117
|
-
curr_target,
|
|
118
|
-
trace:
|
|
123
|
+
G: TNFRGraph,
|
|
124
|
+
g: Glyph | str,
|
|
125
|
+
curr_target: Optional[Sequence[NodeId]],
|
|
126
|
+
trace: ProgramTrace,
|
|
119
127
|
step_fn: AdvanceFn,
|
|
120
128
|
label: OpTag = OpTag.GLYPH,
|
|
121
|
-
):
|
|
129
|
+
) -> Optional[Sequence[NodeId]]:
|
|
122
130
|
_apply_glyph_to_targets(G, g, curr_target)
|
|
123
131
|
_advance_and_record(G, trace, label, step_fn, g=g)
|
|
124
132
|
return curr_target
|
|
125
133
|
|
|
126
134
|
|
|
127
135
|
def _handle_thol(
|
|
128
|
-
G
|
|
129
|
-
|
|
136
|
+
G: TNFRGraph,
|
|
137
|
+
g: Glyph | str | None,
|
|
138
|
+
curr_target: Optional[Sequence[NodeId]],
|
|
139
|
+
trace: ProgramTrace,
|
|
140
|
+
step_fn: AdvanceFn,
|
|
141
|
+
) -> Optional[Sequence[NodeId]]:
|
|
130
142
|
return _handle_glyph(
|
|
131
143
|
G, g or Glyph.THOL.value, curr_target, trace, step_fn, label=OpTag.THOL
|
|
132
144
|
)
|
|
@@ -141,20 +153,23 @@ HANDLERS: dict[OpTag, HandlerFn] = {
|
|
|
141
153
|
|
|
142
154
|
|
|
143
155
|
def play(
|
|
144
|
-
G, sequence: Sequence[Token], step_fn: Optional[AdvanceFn] = None
|
|
156
|
+
G: TNFRGraph, sequence: Sequence[Token], step_fn: Optional[AdvanceFn] = None
|
|
145
157
|
) -> None:
|
|
146
158
|
"""Execute a canonical sequence on graph ``G``."""
|
|
147
159
|
|
|
148
160
|
step_fn = step_fn or step
|
|
149
161
|
|
|
150
|
-
curr_target: Optional[
|
|
162
|
+
curr_target: Optional[Sequence[NodeId]] = None
|
|
151
163
|
|
|
152
164
|
history = ensure_history(G)
|
|
153
165
|
maxlen = int(get_param(G, "PROGRAM_TRACE_MAXLEN"))
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
166
|
+
trace_obj = history.get("program_trace")
|
|
167
|
+
trace: ProgramTrace
|
|
168
|
+
if not isinstance(trace_obj, deque) or trace_obj.maxlen != maxlen:
|
|
169
|
+
trace = cast(ProgramTrace, deque(trace_obj or [], maxlen=maxlen))
|
|
157
170
|
history["program_trace"] = trace
|
|
171
|
+
else:
|
|
172
|
+
trace = cast(ProgramTrace, trace_obj)
|
|
158
173
|
|
|
159
174
|
for op, payload in _flatten(sequence):
|
|
160
175
|
handler: HandlerFn | None = HANDLERS.get(op)
|
|
@@ -183,7 +198,7 @@ def block(
|
|
|
183
198
|
return THOL(body=list(tokens), repeat=repeat, force_close=close)
|
|
184
199
|
|
|
185
200
|
|
|
186
|
-
def target(nodes: Optional[Iterable[
|
|
201
|
+
def target(nodes: Optional[Iterable[NodeId]] = None) -> TARGET:
|
|
187
202
|
return TARGET(nodes=nodes)
|
|
188
203
|
|
|
189
204
|
|
|
@@ -195,7 +210,7 @@ def basic_canonical_example() -> list[Token]:
|
|
|
195
210
|
"""Reference canonical sequence.
|
|
196
211
|
|
|
197
212
|
Returns a copy of the canonical preset tokens to keep CLI defaults aligned
|
|
198
|
-
with :func:`tnfr.presets.get_preset`.
|
|
213
|
+
with :func:`tnfr.config.presets.get_preset`.
|
|
199
214
|
"""
|
|
200
215
|
|
|
201
216
|
return list(CANONICAL_PROGRAM_TOKENS)
|
tnfr/execution.pyi
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections import deque
|
|
4
|
+
from collections.abc import Callable, Iterable, Sequence
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
|
|
7
|
+
from ._compat import TypeAlias
|
|
8
|
+
|
|
9
|
+
from .tokens import OpTag, TARGET, THOL, WAIT, Token
|
|
10
|
+
from .types import Glyph, NodeId, TNFRGraph
|
|
11
|
+
|
|
12
|
+
__all__: list[str]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def __getattr__(name: str) -> Any: ...
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
AdvanceFn = Callable[[TNFRGraph], None]
|
|
19
|
+
TraceEntry = dict[str, Any]
|
|
20
|
+
ProgramTrace: TypeAlias = deque[TraceEntry]
|
|
21
|
+
HandlerFn = Callable[
|
|
22
|
+
[TNFRGraph, Any, Sequence[NodeId] | None, ProgramTrace, AdvanceFn],
|
|
23
|
+
Sequence[NodeId] | None,
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
CANONICAL_PRESET_NAME: str
|
|
27
|
+
CANONICAL_PROGRAM_TOKENS: tuple[Token, ...]
|
|
28
|
+
HANDLERS: dict[OpTag, HandlerFn]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _apply_glyph_to_targets(
|
|
32
|
+
G: TNFRGraph, g: Glyph | str, nodes: Iterable[NodeId] | None = ...
|
|
33
|
+
) -> None: ...
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _record_trace(trace: ProgramTrace, G: TNFRGraph, op: OpTag, **data: Any) -> None: ...
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def compile_sequence(
|
|
40
|
+
sequence: Iterable[Token] | Sequence[Token] | Any,
|
|
41
|
+
*,
|
|
42
|
+
max_materialize: int | None = ...,
|
|
43
|
+
) -> list[tuple[OpTag, Any]]: ...
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def play(
|
|
47
|
+
G: TNFRGraph, sequence: Sequence[Token], step_fn: Optional[AdvanceFn] = ...
|
|
48
|
+
) -> None: ...
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def seq(*tokens: Token) -> list[Token]: ...
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def block(
|
|
55
|
+
*tokens: Token, repeat: int = ..., close: Glyph | None = ...
|
|
56
|
+
) -> THOL: ...
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def target(nodes: Iterable[NodeId] | None = ...) -> TARGET: ...
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def wait(steps: int = ...) -> WAIT: ...
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def basic_canonical_example() -> list[Token]: ...
|
tnfr/flatten.py
CHANGED
|
@@ -7,14 +7,14 @@ from dataclasses import dataclass
|
|
|
7
7
|
from itertools import chain
|
|
8
8
|
from typing import Any, Callable
|
|
9
9
|
|
|
10
|
-
from .
|
|
10
|
+
from .utils import (
|
|
11
11
|
MAX_MATERIALIZE_DEFAULT,
|
|
12
|
+
STRING_TYPES,
|
|
12
13
|
ensure_collection,
|
|
13
14
|
flatten_structure,
|
|
14
|
-
STRING_TYPES,
|
|
15
15
|
normalize_materialize_limit,
|
|
16
16
|
)
|
|
17
|
-
from .
|
|
17
|
+
from .config.constants import GLYPHS_CANONICAL_SET
|
|
18
18
|
from .tokens import THOL, TARGET, WAIT, OpTag, THOL_SENTINEL, Token
|
|
19
19
|
from .types import Glyph
|
|
20
20
|
|
|
@@ -126,7 +126,7 @@ class THOLEvaluator:
|
|
|
126
126
|
def __iter__(self) -> "THOLEvaluator":
|
|
127
127
|
return self
|
|
128
128
|
|
|
129
|
-
def __next__(self):
|
|
129
|
+
def __next__(self) -> Token | object:
|
|
130
130
|
if not self._started:
|
|
131
131
|
self._started = True
|
|
132
132
|
return THOL_SENTINEL
|
|
@@ -211,7 +211,7 @@ def _coerce_mapping_token(
|
|
|
211
211
|
if isinstance(close, str):
|
|
212
212
|
close_enum = Glyph.__members__.get(close)
|
|
213
213
|
if close_enum is None:
|
|
214
|
-
raise ValueError(f"
|
|
214
|
+
raise ValueError(f"Unknown closing glyph: {close!r}")
|
|
215
215
|
close = close_enum
|
|
216
216
|
elif close is not None and not isinstance(close, Glyph):
|
|
217
217
|
raise TypeError("THOL close glyph must be a Glyph or string name")
|
|
@@ -235,7 +235,7 @@ def parse_program_tokens(
|
|
|
235
235
|
|
|
236
236
|
sequence = _iter_source(obj, max_materialize=max_materialize)
|
|
237
237
|
|
|
238
|
-
def _expand(item: Any):
|
|
238
|
+
def _expand(item: Any) -> Iterable[Any] | None:
|
|
239
239
|
if isinstance(item, Mapping):
|
|
240
240
|
return (_coerce_mapping_token(item, max_materialize=max_materialize),)
|
|
241
241
|
return None
|
|
@@ -259,7 +259,7 @@ def _flatten(
|
|
|
259
259
|
ops: list[tuple[OpTag, Any]] = []
|
|
260
260
|
sequence = _iter_source(seq, max_materialize=max_materialize)
|
|
261
261
|
|
|
262
|
-
def _expand(item: Any):
|
|
262
|
+
def _expand(item: Any) -> Iterable[Any] | None:
|
|
263
263
|
if isinstance(item, THOL):
|
|
264
264
|
return THOLEvaluator(item, max_materialize=max_materialize)
|
|
265
265
|
if isinstance(item, Mapping):
|
tnfr/flatten.pyi
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable, Iterator, Sequence
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .tokens import THOL, Token
|
|
7
|
+
|
|
8
|
+
__all__: list[str]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def __getattr__(name: str) -> Any: ...
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class THOLEvaluator(Iterator[Token | object]):
|
|
15
|
+
def __init__(
|
|
16
|
+
self, item: THOL, *, max_materialize: int | None = ...
|
|
17
|
+
) -> None: ...
|
|
18
|
+
|
|
19
|
+
def __iter__(self) -> THOLEvaluator: ...
|
|
20
|
+
|
|
21
|
+
def __next__(self) -> Token | object: ...
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def parse_program_tokens(
|
|
25
|
+
obj: Iterable[Any] | Sequence[Any] | Any,
|
|
26
|
+
*,
|
|
27
|
+
max_materialize: int | None = ...,
|
|
28
|
+
) -> list[Token]: ...
|
tnfr/gamma.py
CHANGED
|
@@ -9,16 +9,18 @@ from collections.abc import Mapping
|
|
|
9
9
|
from functools import lru_cache
|
|
10
10
|
from types import MappingProxyType
|
|
11
11
|
|
|
12
|
-
from .constants import DEFAULTS
|
|
13
|
-
from .alias import
|
|
14
|
-
from .
|
|
15
|
-
from .
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
from .constants import DEFAULTS
|
|
13
|
+
from .alias import get_theta_attr
|
|
14
|
+
from .types import GammaSpec, NodeId, TNFRGraph
|
|
15
|
+
from .utils import (
|
|
16
|
+
edge_version_cache,
|
|
17
|
+
get_graph_mapping,
|
|
18
|
+
get_logger,
|
|
19
|
+
json_dumps,
|
|
20
|
+
node_set_checksum,
|
|
21
|
+
)
|
|
18
22
|
from .metrics.trig_cache import get_trig_cache
|
|
19
23
|
|
|
20
|
-
ALIAS_THETA = get_aliases("THETA")
|
|
21
|
-
|
|
22
24
|
|
|
23
25
|
logger = get_logger(__name__)
|
|
24
26
|
|
|
@@ -44,7 +46,7 @@ def _default_gamma_spec() -> tuple[bytes, str]:
|
|
|
44
46
|
return dumped, hash_
|
|
45
47
|
|
|
46
48
|
|
|
47
|
-
def _ensure_kuramoto_cache(G, t) -> None:
|
|
49
|
+
def _ensure_kuramoto_cache(G: TNFRGraph, t: float | int) -> None:
|
|
48
50
|
"""Cache ``(R, ψ)`` for the current step ``t`` using
|
|
49
51
|
``edge_version_cache``."""
|
|
50
52
|
checksum = G.graph.get("_dnfr_nodes_checksum")
|
|
@@ -63,7 +65,7 @@ def _ensure_kuramoto_cache(G, t) -> None:
|
|
|
63
65
|
G.graph["_kuramoto_cache"] = entry
|
|
64
66
|
|
|
65
67
|
|
|
66
|
-
def kuramoto_R_psi(G) -> tuple[float, float]:
|
|
68
|
+
def kuramoto_R_psi(G: TNFRGraph) -> tuple[float, float]:
|
|
67
69
|
"""Return ``(R, ψ)`` for Kuramoto order using θ from all nodes."""
|
|
68
70
|
max_steps = int(G.graph.get("KURAMOTO_CACHE_STEPS", 1))
|
|
69
71
|
trig = get_trig_cache(G, cache_size=max_steps)
|
|
@@ -78,7 +80,9 @@ def kuramoto_R_psi(G) -> tuple[float, float]:
|
|
|
78
80
|
return R, psi
|
|
79
81
|
|
|
80
82
|
|
|
81
|
-
def _kuramoto_common(
|
|
83
|
+
def _kuramoto_common(
|
|
84
|
+
G: TNFRGraph, node: NodeId, _cfg: GammaSpec
|
|
85
|
+
) -> tuple[float, float, float]:
|
|
82
86
|
"""Return ``(θ_i, R, ψ)`` for Kuramoto-based Γ functions.
|
|
83
87
|
|
|
84
88
|
Reads cached global order ``R`` and mean phase ``ψ`` and obtains node
|
|
@@ -88,11 +92,12 @@ def _kuramoto_common(G, node, _cfg):
|
|
|
88
92
|
cache = G.graph.get("_kuramoto_cache", {})
|
|
89
93
|
R = float(cache.get("R", 0.0))
|
|
90
94
|
psi = float(cache.get("psi", 0.0))
|
|
91
|
-
|
|
95
|
+
th_val = get_theta_attr(G.nodes[node], 0.0)
|
|
96
|
+
th_i = float(th_val if th_val is not None else 0.0)
|
|
92
97
|
return th_i, R, psi
|
|
93
98
|
|
|
94
99
|
|
|
95
|
-
def _read_gamma_raw(G) ->
|
|
100
|
+
def _read_gamma_raw(G: TNFRGraph) -> GammaSpec | None:
|
|
96
101
|
"""Return raw Γ specification from ``G.graph['GAMMA']``.
|
|
97
102
|
|
|
98
103
|
The returned value is the direct contents of ``G.graph['GAMMA']`` when
|
|
@@ -104,11 +109,13 @@ def _read_gamma_raw(G) -> Mapping[str, Any] | None:
|
|
|
104
109
|
if raw is None or isinstance(raw, Mapping):
|
|
105
110
|
return raw
|
|
106
111
|
return get_graph_mapping(
|
|
107
|
-
G,
|
|
112
|
+
G,
|
|
113
|
+
"GAMMA",
|
|
114
|
+
"G.graph['GAMMA'] is not a mapping; using {'type': 'none'}",
|
|
108
115
|
)
|
|
109
116
|
|
|
110
117
|
|
|
111
|
-
def _get_gamma_spec(G) ->
|
|
118
|
+
def _get_gamma_spec(G: TNFRGraph) -> GammaSpec:
|
|
112
119
|
"""Return validated Γ specification caching results.
|
|
113
120
|
|
|
114
121
|
The raw value from ``G.graph['GAMMA']`` is cached together with the
|
|
@@ -122,7 +129,7 @@ def _get_gamma_spec(G) -> Mapping[str, Any]:
|
|
|
122
129
|
cached_spec = G.graph.get("_gamma_spec")
|
|
123
130
|
cached_hash = G.graph.get("_gamma_spec_hash")
|
|
124
131
|
|
|
125
|
-
def _hash_mapping(mapping:
|
|
132
|
+
def _hash_mapping(mapping: GammaSpec) -> str:
|
|
126
133
|
dumped = json_dumps(mapping, sort_keys=True, to_bytes=True)
|
|
127
134
|
return hashlib.blake2b(dumped, digest_size=16).hexdigest()
|
|
128
135
|
|
|
@@ -166,7 +173,7 @@ def _get_gamma_spec(G) -> Mapping[str, Any]:
|
|
|
166
173
|
|
|
167
174
|
|
|
168
175
|
def _gamma_params(
|
|
169
|
-
cfg:
|
|
176
|
+
cfg: GammaSpec, **defaults: float
|
|
170
177
|
) -> tuple[float, ...]:
|
|
171
178
|
"""Return normalized Γ parameters from ``cfg``.
|
|
172
179
|
|
|
@@ -185,18 +192,20 @@ def _gamma_params(
|
|
|
185
192
|
|
|
186
193
|
|
|
187
194
|
# -----------------
|
|
188
|
-
# Γi(R)
|
|
195
|
+
# Canonical Γi(R)
|
|
189
196
|
# -----------------
|
|
190
197
|
|
|
191
198
|
|
|
192
|
-
def gamma_none(
|
|
199
|
+
def gamma_none(
|
|
200
|
+
G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec
|
|
201
|
+
) -> float:
|
|
193
202
|
return 0.0
|
|
194
203
|
|
|
195
204
|
|
|
196
205
|
def _gamma_kuramoto(
|
|
197
|
-
G,
|
|
198
|
-
node,
|
|
199
|
-
cfg:
|
|
206
|
+
G: TNFRGraph,
|
|
207
|
+
node: NodeId,
|
|
208
|
+
cfg: GammaSpec,
|
|
200
209
|
builder: Callable[..., float],
|
|
201
210
|
**defaults: float,
|
|
202
211
|
) -> float:
|
|
@@ -224,7 +233,9 @@ def _builder_tanh(th_i: float, R: float, psi: float, beta: float, k: float, R0:
|
|
|
224
233
|
return beta * math.tanh(k * (R - R0)) * math.cos(th_i - psi)
|
|
225
234
|
|
|
226
235
|
|
|
227
|
-
def gamma_kuramoto_linear(
|
|
236
|
+
def gamma_kuramoto_linear(
|
|
237
|
+
G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec
|
|
238
|
+
) -> float:
|
|
228
239
|
"""Linear Kuramoto coupling for Γi(R).
|
|
229
240
|
|
|
230
241
|
Formula: Γ = β · (R - R0) · cos(θ_i - ψ)
|
|
@@ -239,13 +250,17 @@ def gamma_kuramoto_linear(G, node, t, cfg: dict[str, Any]) -> float:
|
|
|
239
250
|
return _gamma_kuramoto(G, node, cfg, _builder_linear, beta=0.0, R0=0.0)
|
|
240
251
|
|
|
241
252
|
|
|
242
|
-
def gamma_kuramoto_bandpass(
|
|
253
|
+
def gamma_kuramoto_bandpass(
|
|
254
|
+
G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec
|
|
255
|
+
) -> float:
|
|
243
256
|
"""Γ = β · R(1-R) · sign(cos(θ_i - ψ))"""
|
|
244
257
|
|
|
245
258
|
return _gamma_kuramoto(G, node, cfg, _builder_bandpass, beta=0.0)
|
|
246
259
|
|
|
247
260
|
|
|
248
|
-
def gamma_kuramoto_tanh(
|
|
261
|
+
def gamma_kuramoto_tanh(
|
|
262
|
+
G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec
|
|
263
|
+
) -> float:
|
|
249
264
|
"""Saturating tanh coupling for Γi(R).
|
|
250
265
|
|
|
251
266
|
Formula: Γ = β · tanh(k·(R - R0)) · cos(θ_i - ψ)
|
|
@@ -257,7 +272,9 @@ def gamma_kuramoto_tanh(G, node, t, cfg: dict[str, Any]) -> float:
|
|
|
257
272
|
return _gamma_kuramoto(G, node, cfg, _builder_tanh, beta=0.0, k=1.0, R0=0.0)
|
|
258
273
|
|
|
259
274
|
|
|
260
|
-
def gamma_harmonic(
|
|
275
|
+
def gamma_harmonic(
|
|
276
|
+
G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec
|
|
277
|
+
) -> float:
|
|
261
278
|
"""Harmonic forcing aligned with the global phase field.
|
|
262
279
|
|
|
263
280
|
Formula: Γ = β · sin(ω·t + φ) · cos(θ_i - ψ)
|
|
@@ -271,13 +288,13 @@ def gamma_harmonic(G, node, t, cfg: dict[str, Any]) -> float:
|
|
|
271
288
|
|
|
272
289
|
|
|
273
290
|
class GammaEntry(NamedTuple):
|
|
274
|
-
fn: Callable[[
|
|
291
|
+
fn: Callable[[TNFRGraph, NodeId, float | int, GammaSpec], float]
|
|
275
292
|
needs_kuramoto: bool
|
|
276
293
|
|
|
277
294
|
|
|
278
|
-
# ``GAMMA_REGISTRY``
|
|
279
|
-
# ``
|
|
280
|
-
#
|
|
295
|
+
# ``GAMMA_REGISTRY`` associates each coupling name with a ``GammaEntry`` where
|
|
296
|
+
# ``fn`` is the evaluation function and ``needs_kuramoto`` indicates whether
|
|
297
|
+
# the global phase order must be precomputed.
|
|
281
298
|
GAMMA_REGISTRY: dict[str, GammaEntry] = {
|
|
282
299
|
"none": GammaEntry(gamma_none, False),
|
|
283
300
|
"kuramoto_linear": GammaEntry(gamma_kuramoto_linear, True),
|
|
@@ -288,9 +305,9 @@ GAMMA_REGISTRY: dict[str, GammaEntry] = {
|
|
|
288
305
|
|
|
289
306
|
|
|
290
307
|
def eval_gamma(
|
|
291
|
-
G,
|
|
292
|
-
node,
|
|
293
|
-
t,
|
|
308
|
+
G: TNFRGraph,
|
|
309
|
+
node: NodeId,
|
|
310
|
+
t: float | int,
|
|
294
311
|
*,
|
|
295
312
|
strict: bool = False,
|
|
296
313
|
log_level: int | None = None,
|
|
@@ -300,8 +317,8 @@ def eval_gamma(
|
|
|
300
317
|
|
|
301
318
|
If ``strict`` is ``True`` exceptions raised during evaluation are
|
|
302
319
|
propagated instead of returning ``0.0``. Likewise, if the specified
|
|
303
|
-
Γ type is not registered a warning is emitted (
|
|
304
|
-
|
|
320
|
+
Γ type is not registered a warning is emitted (or ``ValueError`` in
|
|
321
|
+
strict mode) and ``gamma_none`` is used.
|
|
305
322
|
|
|
306
323
|
``log_level`` controls the logging level for captured errors when
|
|
307
324
|
``strict`` is ``False``. If omitted, ``logging.ERROR`` is used in
|
|
@@ -311,7 +328,7 @@ def eval_gamma(
|
|
|
311
328
|
spec_type = spec.get("type", "none")
|
|
312
329
|
reg_entry = GAMMA_REGISTRY.get(spec_type)
|
|
313
330
|
if reg_entry is None:
|
|
314
|
-
msg = f"
|
|
331
|
+
msg = f"Unknown GAMMA type: {spec_type}"
|
|
315
332
|
if strict:
|
|
316
333
|
raise ValueError(msg)
|
|
317
334
|
logger.warning(msg)
|
|
@@ -330,7 +347,7 @@ def eval_gamma(
|
|
|
330
347
|
)
|
|
331
348
|
logger.log(
|
|
332
349
|
level,
|
|
333
|
-
"
|
|
350
|
+
"Failed to evaluate Γi for node %s at t=%s: %s: %s",
|
|
334
351
|
node,
|
|
335
352
|
t,
|
|
336
353
|
exc.__class__.__name__,
|
tnfr/gamma.pyi
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from typing import Callable, NamedTuple
|
|
2
|
+
|
|
3
|
+
from .types import GammaSpec, NodeId, TNFRGraph
|
|
4
|
+
|
|
5
|
+
__all__: tuple[str, ...]
|
|
6
|
+
|
|
7
|
+
class GammaEntry(NamedTuple):
|
|
8
|
+
fn: Callable[[TNFRGraph, NodeId, float | int, GammaSpec], float]
|
|
9
|
+
needs_kuramoto: bool
|
|
10
|
+
|
|
11
|
+
GAMMA_REGISTRY: dict[str, GammaEntry]
|
|
12
|
+
|
|
13
|
+
def kuramoto_R_psi(G: TNFRGraph) -> tuple[float, float]: ...
|
|
14
|
+
|
|
15
|
+
def gamma_none(G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec) -> float: ...
|
|
16
|
+
|
|
17
|
+
def gamma_kuramoto_linear(
|
|
18
|
+
G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec
|
|
19
|
+
) -> float: ...
|
|
20
|
+
|
|
21
|
+
def gamma_kuramoto_bandpass(
|
|
22
|
+
G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec
|
|
23
|
+
) -> float: ...
|
|
24
|
+
|
|
25
|
+
def gamma_kuramoto_tanh(
|
|
26
|
+
G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec
|
|
27
|
+
) -> float: ...
|
|
28
|
+
|
|
29
|
+
def gamma_harmonic(
|
|
30
|
+
G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec
|
|
31
|
+
) -> float: ...
|
|
32
|
+
|
|
33
|
+
def eval_gamma(
|
|
34
|
+
G: TNFRGraph,
|
|
35
|
+
node: NodeId,
|
|
36
|
+
t: float | int,
|
|
37
|
+
*,
|
|
38
|
+
strict: bool = ...,
|
|
39
|
+
log_level: int | None = ...,
|
|
40
|
+
) -> float: ...
|