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/observers.pyi
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from typing import Final
|
|
5
|
+
|
|
6
|
+
from .types import GlyphLoadDistribution, TNFRGraph
|
|
7
|
+
|
|
8
|
+
__all__: tuple[str, ...]
|
|
9
|
+
|
|
10
|
+
DEFAULT_GLYPH_LOAD_SPAN: Final[int]
|
|
11
|
+
DEFAULT_WBAR_SPAN: Final[int]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _std_log(kind: str, G: TNFRGraph, ctx: Mapping[str, object]) -> None: ...
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def attach_standard_observer(G: TNFRGraph) -> TNFRGraph: ...
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _ensure_nodes(G: TNFRGraph) -> bool: ...
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def kuramoto_metrics(G: TNFRGraph) -> tuple[float, float]: ...
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def phase_sync(
|
|
27
|
+
G: TNFRGraph,
|
|
28
|
+
R: float | None = ...,
|
|
29
|
+
psi: float | None = ...,
|
|
30
|
+
) -> float: ...
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def kuramoto_order(
|
|
34
|
+
G: TNFRGraph,
|
|
35
|
+
R: float | None = ...,
|
|
36
|
+
psi: float | None = ...,
|
|
37
|
+
) -> float: ...
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def glyph_load(
|
|
41
|
+
G: TNFRGraph,
|
|
42
|
+
window: int | None = ...,
|
|
43
|
+
) -> GlyphLoadDistribution: ...
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def wbar(G: TNFRGraph, window: int | None = ...) -> float: ...
|
tnfr/ontosim.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Orchestrate the canonical simulation."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
|
+
|
|
4
5
|
from collections import deque
|
|
5
6
|
from typing import TYPE_CHECKING
|
|
6
7
|
|
|
@@ -10,16 +11,16 @@ from .dynamics import step as _step, run as _run
|
|
|
10
11
|
from .dynamics import default_compute_delta_nfr
|
|
11
12
|
from .initialization import init_node_attrs
|
|
12
13
|
from .glyph_history import append_metric
|
|
13
|
-
from .
|
|
14
|
+
from .utils import cached_import
|
|
14
15
|
|
|
15
16
|
if TYPE_CHECKING: # pragma: no cover
|
|
16
|
-
import networkx as nx
|
|
17
|
+
import networkx as nx
|
|
17
18
|
|
|
18
|
-
# API
|
|
19
|
-
__all__ = ("
|
|
19
|
+
# High-level API exports
|
|
20
|
+
__all__ = ("prepare_network", "step", "run")
|
|
20
21
|
|
|
21
22
|
|
|
22
|
-
def
|
|
23
|
+
def prepare_network(
|
|
23
24
|
G: "nx.Graph",
|
|
24
25
|
*,
|
|
25
26
|
init_attrs: bool = True,
|
|
@@ -43,7 +44,7 @@ def preparar_red(
|
|
|
43
44
|
from .constants import merge_overrides
|
|
44
45
|
|
|
45
46
|
merge_overrides(G, **overrides)
|
|
46
|
-
#
|
|
47
|
+
# Initialize history buffers
|
|
47
48
|
ph_len = int(
|
|
48
49
|
G.graph.get(
|
|
49
50
|
"PHASE_HISTORY_MAXLEN", METRIC_DEFAULTS["PHASE_HISTORY_MAXLEN"]
|
|
@@ -59,7 +60,7 @@ def preparar_red(
|
|
|
59
60
|
"sense_sigma_mag",
|
|
60
61
|
"sense_sigma_angle",
|
|
61
62
|
"iota",
|
|
62
|
-
"
|
|
63
|
+
"glyph_load_stabilizers",
|
|
63
64
|
"glyph_load_disr",
|
|
64
65
|
"Si_mean",
|
|
65
66
|
"Si_hi_frac",
|
|
@@ -76,12 +77,12 @@ def preparar_red(
|
|
|
76
77
|
"phase_disr": deque(maxlen=ph_len),
|
|
77
78
|
}
|
|
78
79
|
)
|
|
79
|
-
G.graph.setdefault("history", history)
|
|
80
|
-
#
|
|
80
|
+
history_ref = G.graph.setdefault("history", history)
|
|
81
|
+
# Global REMESH memory
|
|
81
82
|
tau = int(get_param(G, "REMESH_TAU_GLOBAL"))
|
|
82
83
|
maxlen = max(2 * tau + 5, 64)
|
|
83
84
|
G.graph.setdefault("_epi_hist", deque(maxlen=maxlen))
|
|
84
|
-
# Auto-attach
|
|
85
|
+
# Auto-attach the standard observer when requested
|
|
85
86
|
if G.graph.get("ATTACH_STD_OBSERVER", False):
|
|
86
87
|
attach_standard_observer = cached_import(
|
|
87
88
|
"tnfr.observers",
|
|
@@ -95,7 +96,7 @@ def preparar_red(
|
|
|
95
96
|
"_callback_errors",
|
|
96
97
|
{"event": "attach_std_observer", "error": "ImportError"},
|
|
97
98
|
)
|
|
98
|
-
#
|
|
99
|
+
# Explicit hook for ΔNFR (can later be replaced with
|
|
99
100
|
# dynamics.set_delta_nfr_hook)
|
|
100
101
|
G.graph.setdefault("compute_delta_nfr", default_compute_delta_nfr)
|
|
101
102
|
G.graph.setdefault("_dnfr_hook_name", "default_compute_delta_nfr")
|
|
@@ -110,8 +111,8 @@ def preparar_red(
|
|
|
110
111
|
)
|
|
111
112
|
G.graph.setdefault(
|
|
112
113
|
"_CALLBACKS_DOC",
|
|
113
|
-
"
|
|
114
|
-
"
|
|
114
|
+
"Γ(R) interface: register (name, func) pairs with signature (G, ctx) "
|
|
115
|
+
"in callbacks['before_step'|'after_step'|'on_remesh']",
|
|
115
116
|
)
|
|
116
117
|
|
|
117
118
|
if init_attrs:
|
tnfr/ontosim.pyi
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .types import TNFRConfigValue, TNFRGraph
|
|
4
|
+
|
|
5
|
+
__all__: tuple[str, ...]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def prepare_network(
|
|
9
|
+
G: TNFRGraph,
|
|
10
|
+
*,
|
|
11
|
+
init_attrs: bool = True,
|
|
12
|
+
override_defaults: bool = False,
|
|
13
|
+
**overrides: TNFRConfigValue,
|
|
14
|
+
) -> TNFRGraph: ...
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def step(
|
|
18
|
+
G: TNFRGraph,
|
|
19
|
+
*,
|
|
20
|
+
dt: float | None = None,
|
|
21
|
+
use_Si: bool = True,
|
|
22
|
+
apply_glyphs: bool = True,
|
|
23
|
+
) -> None: ...
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def run(
|
|
27
|
+
G: TNFRGraph,
|
|
28
|
+
steps: int,
|
|
29
|
+
*,
|
|
30
|
+
dt: float | None = None,
|
|
31
|
+
use_Si: bool = True,
|
|
32
|
+
apply_glyphs: bool = True,
|
|
33
|
+
) -> None: ...
|
tnfr/operators/__init__.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""Network operators."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable, Iterator
|
|
6
|
+
from typing import Any, TYPE_CHECKING
|
|
5
7
|
import math
|
|
6
8
|
import heapq
|
|
7
9
|
from itertools import islice
|
|
@@ -12,11 +14,12 @@ from ..constants import DEFAULTS, get_aliases, get_param
|
|
|
12
14
|
|
|
13
15
|
from ..helpers.numeric import angle_diff
|
|
14
16
|
from ..metrics.trig import neighbor_phase_mean
|
|
15
|
-
from ..
|
|
17
|
+
from ..utils import get_nodenx
|
|
16
18
|
from ..rng import make_rng
|
|
17
19
|
from tnfr import glyph_history
|
|
18
|
-
from ..types import Glyph
|
|
20
|
+
from ..types import EPIValue, Glyph, NodeId, TNFRGraph
|
|
19
21
|
|
|
22
|
+
from . import definitions as _definitions
|
|
20
23
|
from .jitter import (
|
|
21
24
|
JitterCache,
|
|
22
25
|
JitterCacheManager,
|
|
@@ -24,14 +27,38 @@ from .jitter import (
|
|
|
24
27
|
reset_jitter_manager,
|
|
25
28
|
random_jitter,
|
|
26
29
|
)
|
|
30
|
+
from .registry import OPERATORS, discover_operators, get_operator_class
|
|
27
31
|
from .remesh import (
|
|
28
32
|
apply_network_remesh,
|
|
29
33
|
apply_topological_remesh,
|
|
30
34
|
apply_remesh_if_globally_stable,
|
|
31
35
|
)
|
|
32
36
|
|
|
37
|
+
_remesh_doc = (
|
|
38
|
+
"Trigger a remesh once the stability window is satisfied.\n\n"
|
|
39
|
+
"Parameters\n----------\n"
|
|
40
|
+
"stable_step_window : int | None\n"
|
|
41
|
+
" Number of consecutive stable steps required before remeshing.\n"
|
|
42
|
+
" Only the English keyword 'stable_step_window' is supported."
|
|
43
|
+
)
|
|
44
|
+
if apply_remesh_if_globally_stable.__doc__:
|
|
45
|
+
apply_remesh_if_globally_stable.__doc__ += "\n\n" + _remesh_doc
|
|
46
|
+
else:
|
|
47
|
+
apply_remesh_if_globally_stable.__doc__ = _remesh_doc
|
|
48
|
+
|
|
49
|
+
discover_operators()
|
|
50
|
+
|
|
51
|
+
_DEFINITION_EXPORTS = {
|
|
52
|
+
name: getattr(_definitions, name)
|
|
53
|
+
for name in getattr(_definitions, "__all__", ())
|
|
54
|
+
}
|
|
55
|
+
globals().update(_DEFINITION_EXPORTS)
|
|
56
|
+
|
|
33
57
|
if TYPE_CHECKING: # pragma: no cover - type checking only
|
|
34
|
-
from ..node import
|
|
58
|
+
from ..node import NodeProtocol
|
|
59
|
+
|
|
60
|
+
GlyphFactors = dict[str, Any]
|
|
61
|
+
GlyphOperation = Callable[["NodeProtocol", GlyphFactors], None]
|
|
35
62
|
|
|
36
63
|
ALIAS_EPI = get_aliases("EPI")
|
|
37
64
|
|
|
@@ -49,15 +76,20 @@ __all__ = [
|
|
|
49
76
|
"apply_network_remesh",
|
|
50
77
|
"apply_topological_remesh",
|
|
51
78
|
"apply_remesh_if_globally_stable",
|
|
79
|
+
"OPERATORS",
|
|
80
|
+
"discover_operators",
|
|
81
|
+
"get_operator_class",
|
|
52
82
|
]
|
|
53
83
|
|
|
84
|
+
__all__.extend(_DEFINITION_EXPORTS.keys())
|
|
85
|
+
|
|
54
86
|
|
|
55
|
-
def get_glyph_factors(node:
|
|
87
|
+
def get_glyph_factors(node: NodeProtocol) -> GlyphFactors:
|
|
56
88
|
"""Return glyph factors for ``node`` with defaults."""
|
|
57
89
|
return node.graph.get("GLYPH_FACTORS", DEFAULTS["GLYPH_FACTORS"].copy())
|
|
58
90
|
|
|
59
91
|
|
|
60
|
-
def get_factor(gf:
|
|
92
|
+
def get_factor(gf: GlyphFactors, key: str, default: float) -> float:
|
|
61
93
|
"""Return ``gf[key]`` as ``float`` with ``default`` fallback."""
|
|
62
94
|
return float(gf.get(key, default))
|
|
63
95
|
|
|
@@ -67,7 +99,7 @@ def get_factor(gf: dict[str, Any], key: str, default: float) -> float:
|
|
|
67
99
|
# -------------------------
|
|
68
100
|
|
|
69
101
|
|
|
70
|
-
def get_neighbor_epi(node:
|
|
102
|
+
def get_neighbor_epi(node: NodeProtocol) -> tuple[list[NodeProtocol], EPIValue]:
|
|
71
103
|
"""Return neighbour list and their mean ``EPI`` without mutating ``node``."""
|
|
72
104
|
|
|
73
105
|
epi = node.EPI
|
|
@@ -98,11 +130,11 @@ def get_neighbor_epi(node: NodoProtocol) -> tuple[list[NodoProtocol], float]:
|
|
|
98
130
|
return [], epi
|
|
99
131
|
epi_bar = total / count if count else float(epi)
|
|
100
132
|
if needs_conversion:
|
|
101
|
-
|
|
102
|
-
if
|
|
103
|
-
raise ImportError("
|
|
133
|
+
NodeNX = get_nodenx()
|
|
134
|
+
if NodeNX is None:
|
|
135
|
+
raise ImportError("NodeNX is unavailable")
|
|
104
136
|
neigh = [
|
|
105
|
-
v if hasattr(v, "EPI") else
|
|
137
|
+
v if hasattr(v, "EPI") else NodeNX.from_graph(node.G, v)
|
|
106
138
|
for v in neigh
|
|
107
139
|
]
|
|
108
140
|
else:
|
|
@@ -115,7 +147,7 @@ def get_neighbor_epi(node: NodoProtocol) -> tuple[list[NodoProtocol], float]:
|
|
|
115
147
|
|
|
116
148
|
|
|
117
149
|
def _determine_dominant(
|
|
118
|
-
neigh: list[
|
|
150
|
+
neigh: list[NodeProtocol], default_kind: str
|
|
119
151
|
) -> tuple[str, float]:
|
|
120
152
|
"""Return dominant ``epi_kind`` among ``neigh`` and its absolute ``EPI``."""
|
|
121
153
|
best_kind: str | None = None
|
|
@@ -131,7 +163,7 @@ def _determine_dominant(
|
|
|
131
163
|
|
|
132
164
|
|
|
133
165
|
def _mix_epi_with_neighbors(
|
|
134
|
-
node:
|
|
166
|
+
node: NodeProtocol, mix: float, default_glyph: Glyph | str
|
|
135
167
|
) -> tuple[float, str]:
|
|
136
168
|
"""Mix ``EPI`` of ``node`` with the mean of its neighbours."""
|
|
137
169
|
default_kind = (
|
|
@@ -156,22 +188,22 @@ def _mix_epi_with_neighbors(
|
|
|
156
188
|
return epi_bar, final
|
|
157
189
|
|
|
158
190
|
|
|
159
|
-
def _op_AL(node:
|
|
191
|
+
def _op_AL(node: NodeProtocol, gf: GlyphFactors) -> None: # AL — Emission
|
|
160
192
|
f = get_factor(gf, "AL_boost", 0.05)
|
|
161
193
|
node.EPI = node.EPI + f
|
|
162
194
|
|
|
163
195
|
|
|
164
|
-
def _op_EN(node:
|
|
196
|
+
def _op_EN(node: NodeProtocol, gf: GlyphFactors) -> None: # EN — Reception
|
|
165
197
|
mix = get_factor(gf, "EN_mix", 0.25)
|
|
166
198
|
_mix_epi_with_neighbors(node, mix, Glyph.EN)
|
|
167
199
|
|
|
168
200
|
|
|
169
|
-
def _op_IL(node:
|
|
201
|
+
def _op_IL(node: NodeProtocol, gf: GlyphFactors) -> None: # IL — Coherence
|
|
170
202
|
factor = get_factor(gf, "IL_dnfr_factor", 0.7)
|
|
171
203
|
node.dnfr = factor * getattr(node, "dnfr", 0.0)
|
|
172
204
|
|
|
173
205
|
|
|
174
|
-
def _op_OZ(node:
|
|
206
|
+
def _op_OZ(node: NodeProtocol, gf: GlyphFactors) -> None: # OZ — Dissonance
|
|
175
207
|
factor = get_factor(gf, "OZ_dnfr_factor", 1.3)
|
|
176
208
|
dnfr = getattr(node, "dnfr", 0.0)
|
|
177
209
|
if bool(node.graph.get("OZ_NOISE_MODE", False)):
|
|
@@ -184,13 +216,13 @@ def _op_OZ(node: NodoProtocol, gf: dict[str, Any]) -> None: # OZ — Disonancia
|
|
|
184
216
|
node.dnfr = factor * dnfr if abs(dnfr) > 1e-9 else 0.1
|
|
185
217
|
|
|
186
218
|
|
|
187
|
-
def _um_candidate_iter(node:
|
|
219
|
+
def _um_candidate_iter(node: NodeProtocol) -> Iterator[NodeProtocol]:
|
|
188
220
|
sample_ids = node.graph.get("_node_sample")
|
|
189
221
|
if sample_ids is not None and hasattr(node, "G"):
|
|
190
|
-
|
|
191
|
-
if
|
|
192
|
-
raise ImportError("
|
|
193
|
-
base = (
|
|
222
|
+
NodeNX = get_nodenx()
|
|
223
|
+
if NodeNX is None:
|
|
224
|
+
raise ImportError("NodeNX is unavailable")
|
|
225
|
+
base = (NodeNX.from_graph(node.G, j) for j in sample_ids)
|
|
194
226
|
else:
|
|
195
227
|
base = node.all_nodes()
|
|
196
228
|
for j in base:
|
|
@@ -203,12 +235,12 @@ def _um_candidate_iter(node: NodoProtocol):
|
|
|
203
235
|
|
|
204
236
|
|
|
205
237
|
def _um_select_candidates(
|
|
206
|
-
node:
|
|
207
|
-
candidates,
|
|
238
|
+
node: NodeProtocol,
|
|
239
|
+
candidates: Iterator[NodeProtocol],
|
|
208
240
|
limit: int,
|
|
209
241
|
mode: str,
|
|
210
242
|
th: float,
|
|
211
|
-
):
|
|
243
|
+
) -> list[NodeProtocol]:
|
|
212
244
|
"""Select a subset of ``candidates`` for UM coupling."""
|
|
213
245
|
rng = make_rng(int(node.graph.get("RANDOM_SEED", 0)), node.offset(), node.G)
|
|
214
246
|
|
|
@@ -232,7 +264,7 @@ def _um_select_candidates(
|
|
|
232
264
|
return reservoir
|
|
233
265
|
|
|
234
266
|
|
|
235
|
-
def _op_UM(node:
|
|
267
|
+
def _op_UM(node: NodeProtocol, gf: GlyphFactors) -> None: # UM — Coupling
|
|
236
268
|
k = get_factor(gf, "UM_theta_push", 0.25)
|
|
237
269
|
th = node.theta
|
|
238
270
|
thL = neighbor_phase_mean(node)
|
|
@@ -269,12 +301,12 @@ def _op_UM(node: NodoProtocol, gf: dict[str, Any]) -> None: # UM — Coupling
|
|
|
269
301
|
node.add_edge(j, compat)
|
|
270
302
|
|
|
271
303
|
|
|
272
|
-
def _op_RA(node:
|
|
304
|
+
def _op_RA(node: NodeProtocol, gf: GlyphFactors) -> None: # RA — Resonance
|
|
273
305
|
diff = get_factor(gf, "RA_epi_diff", 0.15)
|
|
274
306
|
_mix_epi_with_neighbors(node, diff, Glyph.RA)
|
|
275
307
|
|
|
276
308
|
|
|
277
|
-
def _op_SHA(node:
|
|
309
|
+
def _op_SHA(node: NodeProtocol, gf: GlyphFactors) -> None: # SHA — Silence
|
|
278
310
|
factor = get_factor(gf, "SHA_vf_factor", 0.85)
|
|
279
311
|
node.vf = factor * node.vf
|
|
280
312
|
|
|
@@ -284,12 +316,12 @@ factor_nul = 0.85
|
|
|
284
316
|
_SCALE_FACTORS = {Glyph.VAL: factor_val, Glyph.NUL: factor_nul}
|
|
285
317
|
|
|
286
318
|
|
|
287
|
-
def _op_scale(node:
|
|
319
|
+
def _op_scale(node: NodeProtocol, factor: float) -> None:
|
|
288
320
|
node.vf *= factor
|
|
289
321
|
|
|
290
322
|
|
|
291
|
-
def _make_scale_op(glyph: Glyph):
|
|
292
|
-
def _op(node:
|
|
323
|
+
def _make_scale_op(glyph: Glyph) -> GlyphOperation:
|
|
324
|
+
def _op(node: NodeProtocol, gf: GlyphFactors) -> None:
|
|
293
325
|
key = "VAL_scale" if glyph is Glyph.VAL else "NUL_scale"
|
|
294
326
|
default = _SCALE_FACTORS[glyph]
|
|
295
327
|
factor = get_factor(gf, key, default)
|
|
@@ -299,22 +331,22 @@ def _make_scale_op(glyph: Glyph):
|
|
|
299
331
|
|
|
300
332
|
|
|
301
333
|
def _op_THOL(
|
|
302
|
-
node:
|
|
303
|
-
) -> None: # THOL —
|
|
334
|
+
node: NodeProtocol, gf: GlyphFactors
|
|
335
|
+
) -> None: # THOL — Self-organization
|
|
304
336
|
a = get_factor(gf, "THOL_accel", 0.10)
|
|
305
337
|
node.dnfr = node.dnfr + a * getattr(node, "d2EPI", 0.0)
|
|
306
338
|
|
|
307
339
|
|
|
308
340
|
def _op_ZHIR(
|
|
309
|
-
node:
|
|
310
|
-
) -> None: # ZHIR —
|
|
341
|
+
node: NodeProtocol, gf: GlyphFactors
|
|
342
|
+
) -> None: # ZHIR — Mutation
|
|
311
343
|
shift = get_factor(gf, "ZHIR_theta_shift", math.pi / 2)
|
|
312
344
|
node.theta = node.theta + shift
|
|
313
345
|
|
|
314
346
|
|
|
315
347
|
def _op_NAV(
|
|
316
|
-
node:
|
|
317
|
-
) -> None: # NAV —
|
|
348
|
+
node: NodeProtocol, gf: GlyphFactors
|
|
349
|
+
) -> None: # NAV — Transition
|
|
318
350
|
dnfr = node.dnfr
|
|
319
351
|
vf = node.vf
|
|
320
352
|
eta = get_factor(gf, "NAV_eta", 0.5)
|
|
@@ -334,14 +366,14 @@ def _op_NAV(
|
|
|
334
366
|
|
|
335
367
|
|
|
336
368
|
def _op_REMESH(
|
|
337
|
-
node:
|
|
338
|
-
) -> None: # REMESH —
|
|
369
|
+
node: NodeProtocol, gf: GlyphFactors | None = None
|
|
370
|
+
) -> None: # REMESH — advisory
|
|
339
371
|
step_idx = glyph_history.current_step_idx(node)
|
|
340
372
|
last_warn = node.graph.get("_remesh_warn_step", None)
|
|
341
373
|
if last_warn != step_idx:
|
|
342
374
|
msg = (
|
|
343
|
-
"REMESH
|
|
344
|
-
"stable(G)
|
|
375
|
+
"REMESH operates at network scale. Use apply_remesh_if_globally_"
|
|
376
|
+
"stable(G) or apply_network_remesh(G)."
|
|
345
377
|
)
|
|
346
378
|
hist = glyph_history.ensure_history(node)
|
|
347
379
|
glyph_history.append_metric(
|
|
@@ -357,7 +389,7 @@ def _op_REMESH(
|
|
|
357
389
|
# Dispatcher
|
|
358
390
|
# -------------------------
|
|
359
391
|
|
|
360
|
-
GLYPH_OPERATIONS: dict[Glyph,
|
|
392
|
+
GLYPH_OPERATIONS: dict[Glyph, GlyphOperation] = {
|
|
361
393
|
Glyph.AL: _op_AL,
|
|
362
394
|
Glyph.EN: _op_EN,
|
|
363
395
|
Glyph.IL: _op_IL,
|
|
@@ -375,9 +407,9 @@ GLYPH_OPERATIONS: dict[Glyph, Callable[["NodoProtocol", dict[str, Any]], None]]
|
|
|
375
407
|
|
|
376
408
|
|
|
377
409
|
def apply_glyph_obj(
|
|
378
|
-
node:
|
|
410
|
+
node: NodeProtocol, glyph: Glyph | str, *, window: int | None = None
|
|
379
411
|
) -> None:
|
|
380
|
-
"""Apply ``glyph`` to an object satisfying :class:`
|
|
412
|
+
"""Apply ``glyph`` to an object satisfying :class:`NodeProtocol`."""
|
|
381
413
|
|
|
382
414
|
try:
|
|
383
415
|
g = glyph if isinstance(glyph, Glyph) else Glyph(str(glyph))
|
|
@@ -392,15 +424,15 @@ def apply_glyph_obj(
|
|
|
392
424
|
{
|
|
393
425
|
"step": step_idx,
|
|
394
426
|
"node": getattr(node, "n", None),
|
|
395
|
-
"msg": f"glyph
|
|
427
|
+
"msg": f"unknown glyph: {glyph}",
|
|
396
428
|
},
|
|
397
429
|
),
|
|
398
430
|
)
|
|
399
|
-
raise ValueError(f"glyph
|
|
431
|
+
raise ValueError(f"unknown glyph: {glyph}")
|
|
400
432
|
|
|
401
433
|
op = GLYPH_OPERATIONS.get(g)
|
|
402
434
|
if op is None:
|
|
403
|
-
raise ValueError(f"glyph
|
|
435
|
+
raise ValueError(f"glyph has no registered operator: {g}")
|
|
404
436
|
if window is None:
|
|
405
437
|
window = int(get_param(node, "GLYPH_HYSTERESIS_WINDOW"))
|
|
406
438
|
gf = get_glyph_factors(node)
|
|
@@ -410,11 +442,11 @@ def apply_glyph_obj(
|
|
|
410
442
|
|
|
411
443
|
|
|
412
444
|
def apply_glyph(
|
|
413
|
-
G, n, glyph: Glyph | str, *, window: int | None = None
|
|
445
|
+
G: TNFRGraph, n: NodeId, glyph: Glyph | str, *, window: int | None = None
|
|
414
446
|
) -> None:
|
|
415
447
|
"""Adapter to operate on ``networkx`` graphs."""
|
|
416
|
-
|
|
417
|
-
if
|
|
418
|
-
raise ImportError("
|
|
419
|
-
node =
|
|
448
|
+
NodeNX = get_nodenx()
|
|
449
|
+
if NodeNX is None:
|
|
450
|
+
raise ImportError("NodeNX is unavailable")
|
|
451
|
+
node = NodeNX(G, n)
|
|
420
452
|
apply_glyph_obj(node, glyph, window=window)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
Operator: Any
|
|
4
|
+
Emission: Any
|
|
5
|
+
Reception: Any
|
|
6
|
+
Coherence: Any
|
|
7
|
+
Dissonance: Any
|
|
8
|
+
Coupling: Any
|
|
9
|
+
Resonance: Any
|
|
10
|
+
Silence: Any
|
|
11
|
+
Expansion: Any
|
|
12
|
+
Contraction: Any
|
|
13
|
+
SelfOrganization: Any
|
|
14
|
+
Mutation: Any
|
|
15
|
+
Transition: Any
|
|
16
|
+
Recursivity: Any
|
|
17
|
+
GLYPH_OPERATIONS: Any
|
|
18
|
+
JitterCache: Any
|
|
19
|
+
JitterCacheManager: Any
|
|
20
|
+
OPERATORS: Any
|
|
21
|
+
apply_glyph: Any
|
|
22
|
+
apply_glyph_obj: Any
|
|
23
|
+
apply_network_remesh: Any
|
|
24
|
+
apply_remesh_if_globally_stable: Any
|
|
25
|
+
apply_topological_remesh: Any
|
|
26
|
+
discover_operators: Any
|
|
27
|
+
get_glyph_factors: Any
|
|
28
|
+
get_jitter_manager: Any
|
|
29
|
+
get_neighbor_epi: Any
|
|
30
|
+
random_jitter: Any
|
|
31
|
+
reset_jitter_manager: Any
|