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/node.pyi
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Hashable, Iterable, MutableMapping, Sequence
|
|
4
|
+
from typing import Any, Callable, Optional, Protocol, SupportsFloat, TypeVar
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from .mathematics import (
|
|
9
|
+
CoherenceOperator,
|
|
10
|
+
FrequencyOperator,
|
|
11
|
+
HilbertSpace,
|
|
12
|
+
NFRValidator,
|
|
13
|
+
StateProjector,
|
|
14
|
+
)
|
|
15
|
+
from .types import (
|
|
16
|
+
CouplingWeight,
|
|
17
|
+
DeltaNFR,
|
|
18
|
+
EPIValue,
|
|
19
|
+
NodeId,
|
|
20
|
+
Phase,
|
|
21
|
+
SecondDerivativeEPI,
|
|
22
|
+
SenseIndex,
|
|
23
|
+
StructuralFrequency,
|
|
24
|
+
TNFRGraph,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
T = TypeVar("T")
|
|
28
|
+
|
|
29
|
+
__all__ = ("NodeNX", "NodeProtocol", "add_edge")
|
|
30
|
+
|
|
31
|
+
class AttrSpec:
|
|
32
|
+
aliases: tuple[str, ...]
|
|
33
|
+
default: Any
|
|
34
|
+
getter: Callable[[MutableMapping[str, Any], tuple[str, ...], Any], Any]
|
|
35
|
+
setter: Callable[..., None]
|
|
36
|
+
to_python: Callable[[Any], Any]
|
|
37
|
+
to_storage: Callable[[Any], Any]
|
|
38
|
+
use_graph_setter: bool
|
|
39
|
+
|
|
40
|
+
def build_property(self) -> property: ...
|
|
41
|
+
|
|
42
|
+
ALIAS_EPI: tuple[str, ...]
|
|
43
|
+
ALIAS_VF: tuple[str, ...]
|
|
44
|
+
ALIAS_THETA: tuple[str, ...]
|
|
45
|
+
ALIAS_SI: tuple[str, ...]
|
|
46
|
+
ALIAS_EPI_KIND: tuple[str, ...]
|
|
47
|
+
ALIAS_DNFR: tuple[str, ...]
|
|
48
|
+
ALIAS_D2EPI: tuple[str, ...]
|
|
49
|
+
|
|
50
|
+
ATTR_SPECS: dict[str, AttrSpec]
|
|
51
|
+
|
|
52
|
+
def _add_edge_common(
|
|
53
|
+
n1: NodeId,
|
|
54
|
+
n2: NodeId,
|
|
55
|
+
weight: CouplingWeight | SupportsFloat | str,
|
|
56
|
+
) -> Optional[CouplingWeight]: ...
|
|
57
|
+
def add_edge(
|
|
58
|
+
graph: TNFRGraph,
|
|
59
|
+
n1: NodeId,
|
|
60
|
+
n2: NodeId,
|
|
61
|
+
weight: CouplingWeight | SupportsFloat | str,
|
|
62
|
+
overwrite: bool = ...,
|
|
63
|
+
) -> None: ...
|
|
64
|
+
|
|
65
|
+
class NodeProtocol(Protocol):
|
|
66
|
+
EPI: EPIValue
|
|
67
|
+
vf: StructuralFrequency
|
|
68
|
+
theta: Phase
|
|
69
|
+
Si: SenseIndex
|
|
70
|
+
epi_kind: str
|
|
71
|
+
dnfr: DeltaNFR
|
|
72
|
+
d2EPI: SecondDerivativeEPI
|
|
73
|
+
graph: MutableMapping[str, Any]
|
|
74
|
+
|
|
75
|
+
def neighbors(self) -> Iterable[NodeProtocol | Hashable]: ...
|
|
76
|
+
def _glyph_storage(self) -> MutableMapping[str, object]: ...
|
|
77
|
+
def has_edge(self, other: NodeProtocol) -> bool: ...
|
|
78
|
+
def add_edge(
|
|
79
|
+
self,
|
|
80
|
+
other: NodeProtocol,
|
|
81
|
+
weight: CouplingWeight,
|
|
82
|
+
*,
|
|
83
|
+
overwrite: bool = ...,
|
|
84
|
+
) -> None: ...
|
|
85
|
+
def offset(self) -> int: ...
|
|
86
|
+
def all_nodes(self) -> Iterable[NodeProtocol]: ...
|
|
87
|
+
|
|
88
|
+
class NodeNX(NodeProtocol):
|
|
89
|
+
G: TNFRGraph
|
|
90
|
+
n: NodeId
|
|
91
|
+
graph: MutableMapping[str, Any]
|
|
92
|
+
state_projector: StateProjector
|
|
93
|
+
hilbert_space: HilbertSpace
|
|
94
|
+
coherence_operator: CoherenceOperator | None
|
|
95
|
+
frequency_operator: FrequencyOperator | None
|
|
96
|
+
coherence_threshold: float | None
|
|
97
|
+
validator: NFRValidator | None
|
|
98
|
+
rng: np.random.Generator | None
|
|
99
|
+
|
|
100
|
+
def __init__(
|
|
101
|
+
self,
|
|
102
|
+
G: TNFRGraph,
|
|
103
|
+
n: NodeId,
|
|
104
|
+
*,
|
|
105
|
+
state_projector: StateProjector | None = ...,
|
|
106
|
+
enable_math_validation: Optional[bool] = ...,
|
|
107
|
+
hilbert_space: HilbertSpace | None = ...,
|
|
108
|
+
coherence_operator: CoherenceOperator | None = ...,
|
|
109
|
+
coherence_dim: int | None = ...,
|
|
110
|
+
coherence_spectrum: Sequence[float] | np.ndarray | None = ...,
|
|
111
|
+
coherence_c_min: float | None = ...,
|
|
112
|
+
frequency_operator: FrequencyOperator | None = ...,
|
|
113
|
+
frequency_matrix: Sequence[Sequence[complex]] | np.ndarray | None = ...,
|
|
114
|
+
coherence_threshold: float | None = ...,
|
|
115
|
+
validator: NFRValidator | None = ...,
|
|
116
|
+
rng: np.random.Generator | None = ...,
|
|
117
|
+
) -> None: ...
|
|
118
|
+
@classmethod
|
|
119
|
+
def from_graph(cls, G: TNFRGraph, n: NodeId) -> "NodeNX": ...
|
|
120
|
+
def _glyph_storage(self) -> MutableMapping[str, Any]: ...
|
|
121
|
+
@property
|
|
122
|
+
def EPI(self) -> EPIValue: ...
|
|
123
|
+
@EPI.setter
|
|
124
|
+
def EPI(self, value: EPIValue) -> None: ...
|
|
125
|
+
@property
|
|
126
|
+
def vf(self) -> StructuralFrequency: ...
|
|
127
|
+
@vf.setter
|
|
128
|
+
def vf(self, value: StructuralFrequency) -> None: ...
|
|
129
|
+
@property
|
|
130
|
+
def theta(self) -> Phase: ...
|
|
131
|
+
@theta.setter
|
|
132
|
+
def theta(self, value: Phase) -> None: ...
|
|
133
|
+
@property
|
|
134
|
+
def Si(self) -> SenseIndex: ...
|
|
135
|
+
@Si.setter
|
|
136
|
+
def Si(self, value: SenseIndex) -> None: ...
|
|
137
|
+
@property
|
|
138
|
+
def epi_kind(self) -> str: ...
|
|
139
|
+
@epi_kind.setter
|
|
140
|
+
def epi_kind(self, value: str) -> None: ...
|
|
141
|
+
@property
|
|
142
|
+
def dnfr(self) -> DeltaNFR: ...
|
|
143
|
+
@dnfr.setter
|
|
144
|
+
def dnfr(self, value: DeltaNFR) -> None: ...
|
|
145
|
+
@property
|
|
146
|
+
def d2EPI(self) -> SecondDerivativeEPI: ...
|
|
147
|
+
@d2EPI.setter
|
|
148
|
+
def d2EPI(self, value: SecondDerivativeEPI) -> None: ...
|
|
149
|
+
def neighbors(self) -> Iterable[NodeId]: ...
|
|
150
|
+
def has_edge(self, other: NodeProtocol) -> bool: ...
|
|
151
|
+
def add_edge(
|
|
152
|
+
self,
|
|
153
|
+
other: NodeProtocol,
|
|
154
|
+
weight: CouplingWeight,
|
|
155
|
+
*,
|
|
156
|
+
overwrite: bool = ...,
|
|
157
|
+
) -> None: ...
|
|
158
|
+
def offset(self) -> int: ...
|
|
159
|
+
def all_nodes(self) -> Iterable[NodeProtocol]: ...
|
|
160
|
+
def run_sequence_with_validation(
|
|
161
|
+
self,
|
|
162
|
+
ops: Iterable[Callable[[TNFRGraph, NodeId], None]],
|
|
163
|
+
*,
|
|
164
|
+
projector: StateProjector | None = ...,
|
|
165
|
+
hilbert_space: HilbertSpace | None = ...,
|
|
166
|
+
coherence_operator: CoherenceOperator | None = ...,
|
|
167
|
+
coherence_dim: int | None = ...,
|
|
168
|
+
coherence_spectrum: Sequence[float] | np.ndarray | None = ...,
|
|
169
|
+
coherence_c_min: float | None = ...,
|
|
170
|
+
coherence_threshold: float | None = ...,
|
|
171
|
+
frequency_operator: FrequencyOperator | None = ...,
|
|
172
|
+
frequency_matrix: Sequence[Sequence[complex]] | np.ndarray | None = ...,
|
|
173
|
+
validator: NFRValidator | None = ...,
|
|
174
|
+
enforce_frequency_positivity: bool | None = ...,
|
|
175
|
+
enable_validation: bool | None = ...,
|
|
176
|
+
rng: np.random.Generator | None = ...,
|
|
177
|
+
log_metrics: bool = ...,
|
|
178
|
+
) -> dict[str, Any]: ...
|
tnfr/observers.py
CHANGED
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
"""Observer management."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
import statistics
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from functools import partial
|
|
6
8
|
from statistics import StatisticsError, pvariance
|
|
7
9
|
|
|
8
|
-
from .
|
|
9
|
-
from .alias import get_attr
|
|
10
|
-
from .helpers.numeric import angle_diff
|
|
10
|
+
from .alias import get_theta_attr
|
|
11
11
|
from .callback_utils import CallbackEvent, callback_manager
|
|
12
|
+
from .config.constants import GLYPH_GROUPS
|
|
13
|
+
from .gamma import kuramoto_R_psi
|
|
12
14
|
from .glyph_history import (
|
|
13
|
-
ensure_history,
|
|
14
|
-
count_glyphs,
|
|
15
15
|
append_metric,
|
|
16
|
+
count_glyphs,
|
|
17
|
+
ensure_history,
|
|
16
18
|
)
|
|
17
|
-
from .
|
|
18
|
-
from .constants_glyphs import GLYPH_GROUPS
|
|
19
|
-
from .gamma import kuramoto_R_psi
|
|
20
|
-
from .logging_utils import get_logger
|
|
21
|
-
from .import_utils import get_numpy
|
|
19
|
+
from .utils import angle_diff
|
|
22
20
|
from .metrics.common import compute_coherence
|
|
23
|
-
from .
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
from .types import Glyph, GlyphLoadDistribution, TNFRGraph
|
|
22
|
+
from .utils import (
|
|
23
|
+
get_logger,
|
|
24
|
+
get_numpy,
|
|
25
|
+
mix_groups,
|
|
26
|
+
normalize_counter,
|
|
27
|
+
)
|
|
28
|
+
from .validation import validate_window
|
|
29
|
+
from .telemetry import ensure_nu_f_telemetry, record_nu_f_window
|
|
26
30
|
|
|
27
31
|
__all__ = (
|
|
28
32
|
"attach_standard_observer",
|
|
@@ -42,11 +46,10 @@ DEFAULT_GLYPH_LOAD_SPAN = 50
|
|
|
42
46
|
DEFAULT_WBAR_SPAN = 25
|
|
43
47
|
|
|
44
48
|
|
|
45
|
-
|
|
46
49
|
# -------------------------
|
|
47
|
-
#
|
|
50
|
+
# Standard Γ(R) observer
|
|
48
51
|
# -------------------------
|
|
49
|
-
def _std_log(kind: str, G, ctx:
|
|
52
|
+
def _std_log(kind: str, G: TNFRGraph, ctx: Mapping[str, object]) -> None:
|
|
50
53
|
"""Store compact events in ``history['events']``."""
|
|
51
54
|
h = ensure_history(G)
|
|
52
55
|
append_metric(h, "events", (kind, dict(ctx)))
|
|
@@ -56,25 +59,124 @@ _STD_CALLBACKS = {
|
|
|
56
59
|
CallbackEvent.BEFORE_STEP.value: partial(_std_log, "before"),
|
|
57
60
|
CallbackEvent.AFTER_STEP.value: partial(_std_log, "after"),
|
|
58
61
|
CallbackEvent.ON_REMESH.value: partial(_std_log, "remesh"),
|
|
62
|
+
CallbackEvent.CACHE_METRICS.value: partial(_std_log, "cache"),
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
_REORG_STATE_KEY = "_std_observer_reorg"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _resolve_reorg_state(G: TNFRGraph) -> dict[str, object]:
|
|
70
|
+
state = G.graph.get(_REORG_STATE_KEY)
|
|
71
|
+
if not isinstance(state, dict):
|
|
72
|
+
state = {}
|
|
73
|
+
G.graph[_REORG_STATE_KEY] = state
|
|
74
|
+
return state
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _before_step_reorg(G: TNFRGraph, ctx: Mapping[str, object] | None) -> None:
|
|
78
|
+
"""Capture structural time metadata before the step starts."""
|
|
79
|
+
|
|
80
|
+
ensure_nu_f_telemetry(G, confidence_level=None)
|
|
81
|
+
state = _resolve_reorg_state(G)
|
|
82
|
+
step_idx = ctx.get("step") if ctx else None
|
|
83
|
+
try:
|
|
84
|
+
state["step"] = int(step_idx) if step_idx is not None else None
|
|
85
|
+
except (TypeError, ValueError):
|
|
86
|
+
state["step"] = None
|
|
87
|
+
start_t = float(G.graph.get("_t", 0.0))
|
|
88
|
+
state["start_t"] = start_t
|
|
89
|
+
dt_raw = ctx.get("dt") if ctx else None
|
|
90
|
+
try:
|
|
91
|
+
state["dt"] = float(dt_raw) if dt_raw is not None else None
|
|
92
|
+
except (TypeError, ValueError):
|
|
93
|
+
state["dt"] = None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _after_step_reorg(G: TNFRGraph, ctx: Mapping[str, object] | None) -> None:
|
|
97
|
+
"""Record the reorganisation window for νf telemetry."""
|
|
98
|
+
|
|
99
|
+
state = _resolve_reorg_state(G)
|
|
100
|
+
pending_step = state.get("step")
|
|
101
|
+
ctx_step = ctx.get("step") if ctx else None
|
|
102
|
+
if pending_step is not None and ctx_step is not None and pending_step != ctx_step:
|
|
103
|
+
# Ignore mismatched callbacks to avoid double counting.
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
start_t = float(state.get("start_t", float(G.graph.get("_t", 0.0))))
|
|
108
|
+
except (TypeError, ValueError):
|
|
109
|
+
start_t = float(G.graph.get("_t", 0.0))
|
|
110
|
+
end_t = float(G.graph.get("_t", start_t))
|
|
111
|
+
dt_raw = state.get("dt")
|
|
112
|
+
try:
|
|
113
|
+
duration = float(dt_raw) if dt_raw is not None else end_t - start_t
|
|
114
|
+
except (TypeError, ValueError):
|
|
115
|
+
duration = end_t - start_t
|
|
116
|
+
if duration <= 0.0:
|
|
117
|
+
duration = end_t - start_t
|
|
118
|
+
if duration <= 0.0:
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
stable_frac = ctx.get("stable_frac") if ctx else None
|
|
122
|
+
if stable_frac is None:
|
|
123
|
+
hist = ensure_history(G)
|
|
124
|
+
series = hist.get("stable_frac", [])
|
|
125
|
+
stable_frac = series[-1] if series else None
|
|
126
|
+
try:
|
|
127
|
+
stable_frac_f = float(stable_frac) if stable_frac is not None else None
|
|
128
|
+
except (TypeError, ValueError):
|
|
129
|
+
stable_frac_f = None
|
|
130
|
+
total_nodes = G.number_of_nodes()
|
|
131
|
+
if stable_frac_f is None:
|
|
132
|
+
reorganisations = total_nodes
|
|
133
|
+
else:
|
|
134
|
+
frac = min(max(stable_frac_f, 0.0), 1.0)
|
|
135
|
+
stable_nodes = int(round(frac * total_nodes))
|
|
136
|
+
reorganisations = max(total_nodes - stable_nodes, 0)
|
|
137
|
+
|
|
138
|
+
record_nu_f_window(
|
|
139
|
+
G,
|
|
140
|
+
reorganisations,
|
|
141
|
+
duration,
|
|
142
|
+
start=start_t,
|
|
143
|
+
end=end_t,
|
|
144
|
+
)
|
|
145
|
+
state["last_duration"] = duration
|
|
146
|
+
state["last_reorganisations"] = reorganisations
|
|
147
|
+
state["last_end_t"] = end_t
|
|
148
|
+
state["step"] = None
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def attach_standard_observer(G: TNFRGraph) -> TNFRGraph:
|
|
63
152
|
"""Register standard callbacks: before_step, after_step, on_remesh."""
|
|
64
153
|
if G.graph.get("_STD_OBSERVER"):
|
|
65
154
|
return G
|
|
66
155
|
for event, fn in _STD_CALLBACKS.items():
|
|
67
156
|
callback_manager.register_callback(G, event, fn)
|
|
157
|
+
callback_manager.register_callback(
|
|
158
|
+
G,
|
|
159
|
+
CallbackEvent.BEFORE_STEP.value,
|
|
160
|
+
_before_step_reorg,
|
|
161
|
+
name="std_reorg_before",
|
|
162
|
+
)
|
|
163
|
+
callback_manager.register_callback(
|
|
164
|
+
G,
|
|
165
|
+
CallbackEvent.AFTER_STEP.value,
|
|
166
|
+
_after_step_reorg,
|
|
167
|
+
name="std_reorg_after",
|
|
168
|
+
)
|
|
169
|
+
ensure_nu_f_telemetry(G, confidence_level=None)
|
|
68
170
|
G.graph["_STD_OBSERVER"] = "attached"
|
|
69
171
|
return G
|
|
70
172
|
|
|
71
173
|
|
|
72
|
-
def _ensure_nodes(G) -> bool:
|
|
174
|
+
def _ensure_nodes(G: TNFRGraph) -> bool:
|
|
73
175
|
"""Return ``True`` when the graph has nodes."""
|
|
74
176
|
return bool(G.number_of_nodes())
|
|
75
177
|
|
|
76
178
|
|
|
77
|
-
def kuramoto_metrics(G) -> tuple[float, float]:
|
|
179
|
+
def kuramoto_metrics(G: TNFRGraph) -> tuple[float, float]:
|
|
78
180
|
"""Return Kuramoto order ``R`` and mean phase ``ψ``.
|
|
79
181
|
|
|
80
182
|
Delegates to :func:`kuramoto_R_psi` and performs the computation exactly
|
|
@@ -83,7 +185,13 @@ def kuramoto_metrics(G) -> tuple[float, float]:
|
|
|
83
185
|
return kuramoto_R_psi(G)
|
|
84
186
|
|
|
85
187
|
|
|
86
|
-
def phase_sync(
|
|
188
|
+
def phase_sync(
|
|
189
|
+
G: TNFRGraph,
|
|
190
|
+
R: float | None = None,
|
|
191
|
+
psi: float | None = None,
|
|
192
|
+
) -> float:
|
|
193
|
+
"""Return a [0, 1] synchrony index derived from phase dispersion."""
|
|
194
|
+
|
|
87
195
|
if not _ensure_nodes(G):
|
|
88
196
|
return 1.0
|
|
89
197
|
if R is None or psi is None:
|
|
@@ -92,10 +200,12 @@ def phase_sync(G, R: float | None = None, psi: float | None = None) -> float:
|
|
|
92
200
|
R = R_calc
|
|
93
201
|
if psi is None:
|
|
94
202
|
psi = psi_calc
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
203
|
+
|
|
204
|
+
def _theta(nd: Mapping[str, object]) -> float:
|
|
205
|
+
value = get_theta_attr(nd, 0.0)
|
|
206
|
+
return float(value) if value is not None else 0.0
|
|
207
|
+
|
|
208
|
+
diffs = (angle_diff(_theta(data), psi) for _, data in G.nodes(data=True))
|
|
99
209
|
# Try NumPy for a vectorised population variance
|
|
100
210
|
np = get_numpy()
|
|
101
211
|
if np is not None:
|
|
@@ -110,7 +220,7 @@ def phase_sync(G, R: float | None = None, psi: float | None = None) -> float:
|
|
|
110
220
|
|
|
111
221
|
|
|
112
222
|
def kuramoto_order(
|
|
113
|
-
G, R: float | None = None, psi: float | None = None
|
|
223
|
+
G: TNFRGraph, R: float | None = None, psi: float | None = None
|
|
114
224
|
) -> float:
|
|
115
225
|
"""R in [0,1], 1 means perfectly aligned phases."""
|
|
116
226
|
if not _ensure_nodes(G):
|
|
@@ -120,7 +230,7 @@ def kuramoto_order(
|
|
|
120
230
|
return float(R)
|
|
121
231
|
|
|
122
232
|
|
|
123
|
-
def glyph_load(G, window: int | None = None) ->
|
|
233
|
+
def glyph_load(G: TNFRGraph, window: int | None = None) -> GlyphLoadDistribution:
|
|
124
234
|
"""Return distribution of glyphs applied in the network.
|
|
125
235
|
|
|
126
236
|
- ``window``: if provided, count only the last ``window`` events per node;
|
|
@@ -128,21 +238,28 @@ def glyph_load(G, window: int | None = None) -> dict:
|
|
|
128
238
|
Returns a dict with proportions per glyph and useful aggregates.
|
|
129
239
|
"""
|
|
130
240
|
if window == 0:
|
|
131
|
-
return {"_count": 0}
|
|
241
|
+
return {"_count": 0.0}
|
|
132
242
|
if window is None:
|
|
133
243
|
window_int = DEFAULT_GLYPH_LOAD_SPAN
|
|
134
244
|
else:
|
|
135
245
|
window_int = validate_window(window, positive=True)
|
|
136
246
|
total = count_glyphs(G, window=window_int, last_only=(window_int == 1))
|
|
137
|
-
|
|
247
|
+
dist_raw, count = normalize_counter(total)
|
|
138
248
|
if count == 0:
|
|
139
|
-
return {"_count": 0}
|
|
140
|
-
dist = mix_groups(
|
|
141
|
-
|
|
142
|
-
|
|
249
|
+
return {"_count": 0.0}
|
|
250
|
+
dist = mix_groups(dist_raw, GLYPH_GROUPS)
|
|
251
|
+
glyph_dist: GlyphLoadDistribution = {}
|
|
252
|
+
for key, value in dist.items():
|
|
253
|
+
try:
|
|
254
|
+
glyph_key: Glyph | str = Glyph(key)
|
|
255
|
+
except ValueError:
|
|
256
|
+
glyph_key = key
|
|
257
|
+
glyph_dist[glyph_key] = value
|
|
258
|
+
glyph_dist["_count"] = float(count)
|
|
259
|
+
return glyph_dist
|
|
143
260
|
|
|
144
261
|
|
|
145
|
-
def wbar(G, window: int | None = None) -> float:
|
|
262
|
+
def wbar(G: TNFRGraph, window: int | None = None) -> float:
|
|
146
263
|
"""Return W̄ = mean of ``C(t)`` over a recent window.
|
|
147
264
|
|
|
148
265
|
Uses :func:`ensure_history` to obtain ``G.graph['history']`` and falls back
|
|
@@ -151,7 +268,7 @@ def wbar(G, window: int | None = None) -> float:
|
|
|
151
268
|
hist = ensure_history(G)
|
|
152
269
|
cs = list(hist.get("C_steps", []))
|
|
153
270
|
if not cs:
|
|
154
|
-
# fallback:
|
|
271
|
+
# fallback: instantaneous coherence
|
|
155
272
|
return compute_coherence(G)
|
|
156
273
|
w_param = DEFAULT_WBAR_SPAN if window is None else window
|
|
157
274
|
w = validate_window(w_param, positive=True)
|
tnfr/observers.pyi
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
def _std_log(kind: str, G: TNFRGraph, ctx: Mapping[str, object]) -> None: ...
|
|
14
|
+
def attach_standard_observer(G: TNFRGraph) -> TNFRGraph: ...
|
|
15
|
+
def _ensure_nodes(G: TNFRGraph) -> bool: ...
|
|
16
|
+
def kuramoto_metrics(G: TNFRGraph) -> tuple[float, float]: ...
|
|
17
|
+
def phase_sync(
|
|
18
|
+
G: TNFRGraph,
|
|
19
|
+
R: float | None = ...,
|
|
20
|
+
psi: float | None = ...,
|
|
21
|
+
) -> float: ...
|
|
22
|
+
def kuramoto_order(
|
|
23
|
+
G: TNFRGraph,
|
|
24
|
+
R: float | None = ...,
|
|
25
|
+
psi: float | None = ...,
|
|
26
|
+
) -> float: ...
|
|
27
|
+
def glyph_load(
|
|
28
|
+
G: TNFRGraph,
|
|
29
|
+
window: int | None = ...,
|
|
30
|
+
) -> GlyphLoadDistribution: ...
|
|
31
|
+
def wbar(G: TNFRGraph, window: int | None = ...) -> float: ...
|
tnfr/ontosim.py
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
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
|
|
|
7
8
|
from .callback_utils import CallbackEvent
|
|
8
|
-
from .constants import METRIC_DEFAULTS,
|
|
9
|
-
from .dynamics import step as _step, run as _run
|
|
9
|
+
from .constants import METRIC_DEFAULTS, get_param, inject_defaults
|
|
10
10
|
from .dynamics import default_compute_delta_nfr
|
|
11
|
-
from .
|
|
11
|
+
from .dynamics import run as _run
|
|
12
|
+
from .dynamics import step as _step
|
|
12
13
|
from .glyph_history import append_metric
|
|
13
|
-
from .
|
|
14
|
+
from .initialization import init_node_attrs
|
|
15
|
+
from .utils import cached_import
|
|
14
16
|
|
|
15
17
|
if TYPE_CHECKING: # pragma: no cover
|
|
16
|
-
import networkx as nx
|
|
18
|
+
import networkx as nx
|
|
17
19
|
|
|
18
|
-
# API
|
|
19
|
-
__all__ = ("
|
|
20
|
+
# High-level API exports
|
|
21
|
+
__all__ = ("prepare_network", "step", "run")
|
|
20
22
|
|
|
21
23
|
|
|
22
|
-
def
|
|
24
|
+
def prepare_network(
|
|
23
25
|
G: "nx.Graph",
|
|
24
26
|
*,
|
|
25
27
|
init_attrs: bool = True,
|
|
@@ -43,11 +45,9 @@ def preparar_red(
|
|
|
43
45
|
from .constants import merge_overrides
|
|
44
46
|
|
|
45
47
|
merge_overrides(G, **overrides)
|
|
46
|
-
#
|
|
48
|
+
# Initialize history buffers
|
|
47
49
|
ph_len = int(
|
|
48
|
-
G.graph.get(
|
|
49
|
-
"PHASE_HISTORY_MAXLEN", METRIC_DEFAULTS["PHASE_HISTORY_MAXLEN"]
|
|
50
|
-
)
|
|
50
|
+
G.graph.get("PHASE_HISTORY_MAXLEN", METRIC_DEFAULTS["PHASE_HISTORY_MAXLEN"])
|
|
51
51
|
)
|
|
52
52
|
hist_keys = [
|
|
53
53
|
"C_steps",
|
|
@@ -59,7 +59,7 @@ def preparar_red(
|
|
|
59
59
|
"sense_sigma_mag",
|
|
60
60
|
"sense_sigma_angle",
|
|
61
61
|
"iota",
|
|
62
|
-
"
|
|
62
|
+
"glyph_load_stabilizers",
|
|
63
63
|
"glyph_load_disr",
|
|
64
64
|
"Si_mean",
|
|
65
65
|
"Si_hi_frac",
|
|
@@ -76,12 +76,12 @@ def preparar_red(
|
|
|
76
76
|
"phase_disr": deque(maxlen=ph_len),
|
|
77
77
|
}
|
|
78
78
|
)
|
|
79
|
-
G.graph.setdefault("history", history)
|
|
80
|
-
#
|
|
79
|
+
history_ref = G.graph.setdefault("history", history)
|
|
80
|
+
# Global REMESH memory
|
|
81
81
|
tau = int(get_param(G, "REMESH_TAU_GLOBAL"))
|
|
82
82
|
maxlen = max(2 * tau + 5, 64)
|
|
83
83
|
G.graph.setdefault("_epi_hist", deque(maxlen=maxlen))
|
|
84
|
-
# Auto-attach
|
|
84
|
+
# Auto-attach the standard observer when requested
|
|
85
85
|
if G.graph.get("ATTACH_STD_OBSERVER", False):
|
|
86
86
|
attach_standard_observer = cached_import(
|
|
87
87
|
"tnfr.observers",
|
|
@@ -95,7 +95,7 @@ def preparar_red(
|
|
|
95
95
|
"_callback_errors",
|
|
96
96
|
{"event": "attach_std_observer", "error": "ImportError"},
|
|
97
97
|
)
|
|
98
|
-
#
|
|
98
|
+
# Explicit hook for ΔNFR (can later be replaced with
|
|
99
99
|
# dynamics.set_delta_nfr_hook)
|
|
100
100
|
G.graph.setdefault("compute_delta_nfr", default_compute_delta_nfr)
|
|
101
101
|
G.graph.setdefault("_dnfr_hook_name", "default_compute_delta_nfr")
|
|
@@ -110,8 +110,8 @@ def preparar_red(
|
|
|
110
110
|
)
|
|
111
111
|
G.graph.setdefault(
|
|
112
112
|
"_CALLBACKS_DOC",
|
|
113
|
-
"
|
|
114
|
-
"
|
|
113
|
+
"Γ(R) interface: register (name, func) pairs with signature (G, ctx) "
|
|
114
|
+
"in callbacks['before_step'|'after_step'|'on_remesh']",
|
|
115
115
|
)
|
|
116
116
|
|
|
117
117
|
if init_attrs:
|
|
@@ -126,6 +126,8 @@ def step(
|
|
|
126
126
|
use_Si: bool = True,
|
|
127
127
|
apply_glyphs: bool = True,
|
|
128
128
|
) -> None:
|
|
129
|
+
"""Advance the ontosim runtime by a single step."""
|
|
130
|
+
|
|
129
131
|
_step(G, dt=dt, use_Si=use_Si, apply_glyphs=apply_glyphs)
|
|
130
132
|
|
|
131
133
|
|
|
@@ -137,4 +139,6 @@ def run(
|
|
|
137
139
|
use_Si: bool = True,
|
|
138
140
|
apply_glyphs: bool = True,
|
|
139
141
|
) -> None:
|
|
142
|
+
"""Advance the ontosim runtime ``steps`` times with optional overrides."""
|
|
143
|
+
|
|
140
144
|
_run(G, steps=steps, dt=dt, use_Si=use_Si, apply_glyphs=apply_glyphs)
|
tnfr/ontosim.pyi
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .types import TNFRConfigValue, TNFRGraph
|
|
4
|
+
|
|
5
|
+
__all__: tuple[str, ...]
|
|
6
|
+
|
|
7
|
+
def prepare_network(
|
|
8
|
+
G: TNFRGraph,
|
|
9
|
+
*,
|
|
10
|
+
init_attrs: bool = True,
|
|
11
|
+
override_defaults: bool = False,
|
|
12
|
+
**overrides: TNFRConfigValue,
|
|
13
|
+
) -> TNFRGraph: ...
|
|
14
|
+
def step(
|
|
15
|
+
G: TNFRGraph,
|
|
16
|
+
*,
|
|
17
|
+
dt: float | None = None,
|
|
18
|
+
use_Si: bool = True,
|
|
19
|
+
apply_glyphs: bool = True,
|
|
20
|
+
) -> None: ...
|
|
21
|
+
def run(
|
|
22
|
+
G: TNFRGraph,
|
|
23
|
+
steps: int,
|
|
24
|
+
*,
|
|
25
|
+
dt: float | None = None,
|
|
26
|
+
use_Si: bool = True,
|
|
27
|
+
apply_glyphs: bool = True,
|
|
28
|
+
) -> None: ...
|