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/structural.py
CHANGED
|
@@ -1,20 +1,75 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Maintain TNFR structural coherence for nodes and operator sequences.
|
|
2
|
+
|
|
3
|
+
This module exposes the canonical entry points used by the engine to
|
|
4
|
+
instantiate coherent TNFR nodes and to orchestrate structural operator
|
|
5
|
+
pipelines while keeping the nodal equation
|
|
6
|
+
``∂EPI/∂t = νf · ΔNFR(t)`` balanced.
|
|
7
|
+
|
|
8
|
+
Public API
|
|
9
|
+
----------
|
|
10
|
+
create_nfr
|
|
11
|
+
Initialise a node with canonical EPI, νf and phase attributes plus a
|
|
12
|
+
ΔNFR hook that propagates reorganisations through the graph.
|
|
13
|
+
run_sequence
|
|
14
|
+
Validate and execute operator trajectories so that ΔNFR hooks can
|
|
15
|
+
update EPI, νf and phase coherently after each step.
|
|
16
|
+
OPERATORS
|
|
17
|
+
Registry of canonical structural operators ready to be composed into
|
|
18
|
+
validated sequences.
|
|
19
|
+
validate_sequence
|
|
20
|
+
Grammar guard that ensures operator trajectories stay within TNFR
|
|
21
|
+
closure rules before execution.
|
|
22
|
+
"""
|
|
2
23
|
|
|
3
24
|
from __future__ import annotations
|
|
4
|
-
from typing import Iterable
|
|
5
|
-
import networkx as nx # type: ignore[import-untyped]
|
|
6
25
|
|
|
26
|
+
from copy import deepcopy
|
|
27
|
+
from typing import Iterable, Mapping, Sequence
|
|
28
|
+
|
|
29
|
+
import networkx as nx
|
|
30
|
+
|
|
31
|
+
from .constants import EPI_PRIMARY, THETA_PRIMARY, VF_PRIMARY
|
|
7
32
|
from .dynamics import (
|
|
8
|
-
set_delta_nfr_hook,
|
|
9
33
|
dnfr_epi_vf_mixed,
|
|
34
|
+
set_delta_nfr_hook,
|
|
35
|
+
)
|
|
36
|
+
from .mathematics import (
|
|
37
|
+
BasicStateProjector,
|
|
38
|
+
CoherenceOperator,
|
|
39
|
+
FrequencyOperator,
|
|
40
|
+
HilbertSpace,
|
|
41
|
+
MathematicalDynamicsEngine,
|
|
42
|
+
NFRValidator,
|
|
43
|
+
make_coherence_operator,
|
|
44
|
+
make_frequency_operator,
|
|
45
|
+
)
|
|
46
|
+
from .validation import validate_sequence
|
|
47
|
+
from .operators.definitions import (
|
|
48
|
+
Coherence,
|
|
49
|
+
Contraction,
|
|
50
|
+
Coupling,
|
|
51
|
+
Dissonance,
|
|
52
|
+
Emission,
|
|
53
|
+
Expansion,
|
|
54
|
+
Mutation,
|
|
55
|
+
Operator,
|
|
56
|
+
Reception,
|
|
57
|
+
Recursivity,
|
|
58
|
+
Resonance,
|
|
59
|
+
SelfOrganization,
|
|
60
|
+
Silence,
|
|
61
|
+
Transition,
|
|
10
62
|
)
|
|
11
|
-
from .
|
|
12
|
-
from .types import
|
|
13
|
-
from .constants import EPI_PRIMARY, VF_PRIMARY, THETA_PRIMARY
|
|
63
|
+
from .operators.registry import OPERATORS
|
|
64
|
+
from .types import DeltaNFRHook, NodeId, TNFRGraph
|
|
14
65
|
|
|
66
|
+
try: # pragma: no cover - optional dependency path exercised in CI extras
|
|
67
|
+
import numpy as np
|
|
68
|
+
except ImportError: # pragma: no cover - optional dependency path exercised in CI extras
|
|
69
|
+
np = None # type: ignore[assignment]
|
|
15
70
|
|
|
16
71
|
# ---------------------------------------------------------------------------
|
|
17
|
-
# 1)
|
|
72
|
+
# 1) NFR factory
|
|
18
73
|
# ---------------------------------------------------------------------------
|
|
19
74
|
|
|
20
75
|
|
|
@@ -24,13 +79,85 @@ def create_nfr(
|
|
|
24
79
|
epi: float = 0.0,
|
|
25
80
|
vf: float = 1.0,
|
|
26
81
|
theta: float = 0.0,
|
|
27
|
-
graph:
|
|
28
|
-
dnfr_hook=dnfr_epi_vf_mixed,
|
|
29
|
-
) -> tuple[
|
|
30
|
-
"""
|
|
31
|
-
|
|
32
|
-
|
|
82
|
+
graph: TNFRGraph | None = None,
|
|
83
|
+
dnfr_hook: DeltaNFRHook = dnfr_epi_vf_mixed,
|
|
84
|
+
) -> tuple[TNFRGraph, str]:
|
|
85
|
+
"""Anchor a TNFR node by seeding EPI, νf, phase and ΔNFR coupling.
|
|
86
|
+
|
|
87
|
+
The factory secures the structural state of a node: it stores canonical
|
|
88
|
+
values for the Primary Information Structure (EPI), structural frequency
|
|
89
|
+
(νf) and phase, then installs a ΔNFR hook so that later operator
|
|
90
|
+
sequences can reorganise the node without breaking the nodal equation.
|
|
91
|
+
|
|
92
|
+
Parameters
|
|
93
|
+
----------
|
|
94
|
+
name : str
|
|
95
|
+
Identifier for the new node. The identifier is stored as the node key
|
|
96
|
+
and must remain hashable by :mod:`networkx`.
|
|
97
|
+
epi : float, optional
|
|
98
|
+
Initial Primary Information Structure (EPI) assigned to the node. The
|
|
99
|
+
value provides the baseline form that subsequent ΔNFR hooks reorganise
|
|
100
|
+
through the nodal equation.
|
|
101
|
+
vf : float, optional
|
|
102
|
+
Structural frequency (νf, expressed in Hz_str) used as the starting
|
|
103
|
+
reorganisation rate for the node.
|
|
104
|
+
theta : float, optional
|
|
105
|
+
Initial phase of the node in radians, used to keep phase alignment with
|
|
106
|
+
neighbouring coherence structures.
|
|
107
|
+
graph : TNFRGraph, optional
|
|
108
|
+
Existing graph where the node will be registered. When omitted a new
|
|
109
|
+
:class:`networkx.Graph` instance is created.
|
|
110
|
+
dnfr_hook : DeltaNFRHook, optional
|
|
111
|
+
Callable responsible for computing ΔNFR and updating EPI/νf after each
|
|
112
|
+
operator application. By default the canonical ``dnfr_epi_vf_mixed``
|
|
113
|
+
hook is installed, which keeps the nodal equation coherent with TNFR
|
|
114
|
+
invariants.
|
|
115
|
+
|
|
116
|
+
Returns
|
|
117
|
+
-------
|
|
118
|
+
tuple[TNFRGraph, str]
|
|
119
|
+
The graph that stores the node together with the node identifier. The
|
|
120
|
+
tuple form allows immediate reuse with :func:`run_sequence`.
|
|
121
|
+
|
|
122
|
+
Notes
|
|
123
|
+
-----
|
|
124
|
+
The factory does not introduce additional TNFR-specific errors. Any
|
|
125
|
+
exceptions raised by :mod:`networkx` when adding nodes propagate unchanged.
|
|
126
|
+
|
|
127
|
+
Examples
|
|
128
|
+
--------
|
|
129
|
+
Create a node, connect a ΔNFR hook and launch a coherent operator
|
|
130
|
+
trajectory while tracking the evolving metrics.
|
|
131
|
+
|
|
132
|
+
>>> from tnfr.constants import DNFR_PRIMARY, EPI_PRIMARY, THETA_PRIMARY, VF_PRIMARY
|
|
133
|
+
>>> from tnfr.dynamics import set_delta_nfr_hook
|
|
134
|
+
>>> from tnfr.structural import (
|
|
135
|
+
... Coherence,
|
|
136
|
+
... Emission,
|
|
137
|
+
... Reception,
|
|
138
|
+
... Resonance,
|
|
139
|
+
... Silence,
|
|
140
|
+
... create_nfr,
|
|
141
|
+
... run_sequence,
|
|
142
|
+
... )
|
|
143
|
+
>>> G, node = create_nfr("seed", epi=1.0, vf=2.0, theta=0.1)
|
|
144
|
+
>>> def synchronise_delta(graph):
|
|
145
|
+
... delta = graph.nodes[node][VF_PRIMARY] * 0.2
|
|
146
|
+
... graph.nodes[node][DNFR_PRIMARY] = delta
|
|
147
|
+
... graph.nodes[node][EPI_PRIMARY] += delta
|
|
148
|
+
... graph.nodes[node][VF_PRIMARY] += delta * 0.05
|
|
149
|
+
... graph.nodes[node][THETA_PRIMARY] += 0.01
|
|
150
|
+
>>> set_delta_nfr_hook(G, synchronise_delta)
|
|
151
|
+
>>> run_sequence(G, node, [Emission(), Reception(), Coherence(), Resonance(), Silence()]) # doctest: +SKIP
|
|
152
|
+
>>> (
|
|
153
|
+
... G.nodes[node][EPI_PRIMARY],
|
|
154
|
+
... G.nodes[node][VF_PRIMARY],
|
|
155
|
+
... G.nodes[node][THETA_PRIMARY],
|
|
156
|
+
... G.nodes[node][DNFR_PRIMARY],
|
|
157
|
+
... ) # doctest: +SKIP
|
|
158
|
+
(..., ..., ..., ...)
|
|
33
159
|
"""
|
|
160
|
+
|
|
34
161
|
G = graph if graph is not None else nx.Graph()
|
|
35
162
|
G.add_node(
|
|
36
163
|
name,
|
|
@@ -44,280 +171,413 @@ def create_nfr(
|
|
|
44
171
|
return G, name
|
|
45
172
|
|
|
46
173
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
174
|
+
def _resolve_dimension(
|
|
175
|
+
G: TNFRGraph,
|
|
176
|
+
*,
|
|
177
|
+
dimension: int | None,
|
|
178
|
+
hilbert_space: HilbertSpace | None,
|
|
179
|
+
existing_cfg: Mapping[str, object] | None,
|
|
180
|
+
) -> int:
|
|
181
|
+
if hilbert_space is not None:
|
|
182
|
+
resolved = int(getattr(hilbert_space, "dimension", 0) or 0)
|
|
183
|
+
if resolved <= 0:
|
|
184
|
+
raise ValueError("Hilbert space dimension must be positive.")
|
|
185
|
+
return resolved
|
|
186
|
+
|
|
187
|
+
if dimension is None and existing_cfg:
|
|
188
|
+
candidate = existing_cfg.get("dimension")
|
|
189
|
+
if isinstance(candidate, int) and candidate > 0:
|
|
190
|
+
dimension = candidate
|
|
191
|
+
|
|
192
|
+
if dimension is None:
|
|
193
|
+
if hasattr(G, "number_of_nodes"):
|
|
194
|
+
count = int(G.number_of_nodes())
|
|
195
|
+
else:
|
|
196
|
+
count = len(tuple(G.nodes))
|
|
197
|
+
dimension = max(1, count)
|
|
198
|
+
|
|
199
|
+
resolved = int(dimension)
|
|
200
|
+
if resolved <= 0:
|
|
201
|
+
raise ValueError("Hilbert space dimension must be positive.")
|
|
202
|
+
return resolved
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _ensure_coherence_operator(
|
|
206
|
+
*,
|
|
207
|
+
operator: CoherenceOperator | None,
|
|
208
|
+
dimension: int,
|
|
209
|
+
spectrum: Sequence[float] | None,
|
|
210
|
+
c_min: float | None,
|
|
211
|
+
) -> CoherenceOperator:
|
|
212
|
+
if operator is not None:
|
|
213
|
+
return operator
|
|
214
|
+
|
|
215
|
+
kwargs: dict[str, object] = {}
|
|
216
|
+
if spectrum is not None:
|
|
217
|
+
spectrum_array = np.asarray(spectrum, dtype=np.complex128)
|
|
218
|
+
if spectrum_array.ndim != 1:
|
|
219
|
+
raise ValueError("Coherence spectrum must be one-dimensional.")
|
|
220
|
+
kwargs["spectrum"] = spectrum_array
|
|
221
|
+
if c_min is not None:
|
|
222
|
+
kwargs["c_min"] = float(c_min)
|
|
223
|
+
return make_coherence_operator(dimension, **kwargs)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _ensure_frequency_operator(
|
|
227
|
+
*,
|
|
228
|
+
operator: FrequencyOperator | None,
|
|
229
|
+
dimension: int,
|
|
230
|
+
diagonal: Sequence[float] | None,
|
|
231
|
+
) -> FrequencyOperator:
|
|
232
|
+
if operator is not None:
|
|
233
|
+
return operator
|
|
234
|
+
|
|
235
|
+
if diagonal is None:
|
|
236
|
+
matrix = np.eye(dimension, dtype=float)
|
|
237
|
+
else:
|
|
238
|
+
diag_array = np.asarray(diagonal, dtype=float)
|
|
239
|
+
if diag_array.ndim != 1:
|
|
240
|
+
raise ValueError("Frequency diagonal must be one-dimensional.")
|
|
241
|
+
if diag_array.shape[0] != int(dimension):
|
|
242
|
+
raise ValueError("Frequency diagonal size must match Hilbert dimension.")
|
|
243
|
+
matrix = np.diag(diag_array)
|
|
244
|
+
return make_frequency_operator(np.asarray(matrix, dtype=np.complex128))
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _ensure_generator_matrix(
|
|
248
|
+
*,
|
|
249
|
+
dimension: int,
|
|
250
|
+
diagonal: Sequence[float] | None,
|
|
251
|
+
) -> "np.ndarray":
|
|
252
|
+
if diagonal is None:
|
|
253
|
+
return np.zeros((dimension, dimension), dtype=np.complex128)
|
|
254
|
+
diag_array = np.asarray(diagonal, dtype=np.complex128)
|
|
255
|
+
if diag_array.ndim != 1:
|
|
256
|
+
raise ValueError("Generator diagonal must be one-dimensional.")
|
|
257
|
+
if diag_array.shape[0] != int(dimension):
|
|
258
|
+
raise ValueError("Generator diagonal size must match Hilbert dimension.")
|
|
259
|
+
return np.diag(diag_array)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def create_math_nfr(
|
|
263
|
+
name: str,
|
|
264
|
+
*,
|
|
265
|
+
epi: float = 0.0,
|
|
266
|
+
vf: float = 1.0,
|
|
267
|
+
theta: float = 0.0,
|
|
268
|
+
graph: TNFRGraph | None = None,
|
|
269
|
+
dnfr_hook: DeltaNFRHook = dnfr_epi_vf_mixed,
|
|
270
|
+
dimension: int | None = None,
|
|
271
|
+
hilbert_space: HilbertSpace | None = None,
|
|
272
|
+
coherence_operator: CoherenceOperator | None = None,
|
|
273
|
+
coherence_spectrum: Sequence[float] | None = None,
|
|
274
|
+
coherence_c_min: float | None = None,
|
|
275
|
+
coherence_threshold: float | None = None,
|
|
276
|
+
frequency_operator: FrequencyOperator | None = None,
|
|
277
|
+
frequency_diagonal: Sequence[float] | None = None,
|
|
278
|
+
generator_diagonal: Sequence[float] | None = None,
|
|
279
|
+
state_projector: BasicStateProjector | None = None,
|
|
280
|
+
dynamics_engine: MathematicalDynamicsEngine | None = None,
|
|
281
|
+
validator: NFRValidator | None = None,
|
|
282
|
+
) -> tuple[TNFRGraph, str]:
|
|
283
|
+
"""Create a TNFR node with canonical mathematical validation attached.
|
|
284
|
+
|
|
285
|
+
The helper wraps :func:`create_nfr` while projecting the structural state
|
|
286
|
+
into a Hilbert space so coherence, νf and norm invariants can be tracked via
|
|
287
|
+
the mathematical runtime. It installs operators and validation metadata on
|
|
288
|
+
both the node and the hosting graph so that the
|
|
289
|
+
:class:`~tnfr.mathematics.MathematicalDynamicsEngine` can consume them
|
|
290
|
+
directly.
|
|
291
|
+
|
|
292
|
+
Parameters
|
|
293
|
+
----------
|
|
294
|
+
name : str
|
|
295
|
+
Identifier for the new node.
|
|
296
|
+
epi, vf, theta : float, optional
|
|
297
|
+
Canonical TNFR scalars forwarded to :func:`create_nfr`.
|
|
298
|
+
dimension : int, optional
|
|
299
|
+
Hilbert space dimension. When omitted it is inferred from the graph size
|
|
300
|
+
(at least one).
|
|
301
|
+
hilbert_space : HilbertSpace, optional
|
|
302
|
+
Pre-built Hilbert space to reuse. Its dimension supersedes ``dimension``.
|
|
303
|
+
coherence_operator, frequency_operator : optional
|
|
304
|
+
Custom operators to install. When omitted they are derived from
|
|
305
|
+
``coherence_spectrum``/``coherence_c_min`` and
|
|
306
|
+
``frequency_diagonal`` respectively.
|
|
307
|
+
coherence_threshold : float, optional
|
|
308
|
+
Validation floor. Defaults to ``coherence_operator.c_min``.
|
|
309
|
+
generator_diagonal : sequence of float, optional
|
|
310
|
+
Diagonal entries for the unitary generator used by the mathematical
|
|
311
|
+
dynamics engine. Defaults to a null generator.
|
|
312
|
+
state_projector : BasicStateProjector, optional
|
|
313
|
+
Projector used to build the canonical spectral state for validation.
|
|
314
|
+
|
|
315
|
+
Returns
|
|
316
|
+
-------
|
|
317
|
+
tuple[TNFRGraph, str]
|
|
318
|
+
The graph and node identifier, mirroring :func:`create_nfr`.
|
|
319
|
+
|
|
320
|
+
Examples
|
|
321
|
+
--------
|
|
322
|
+
>>> G, node = create_math_nfr("math-seed", epi=0.4, vf=1.2, theta=0.05, dimension=3)
|
|
323
|
+
>>> metrics = G.nodes[node]["math_metrics"]
|
|
324
|
+
>>> round(metrics["norm"], 6)
|
|
325
|
+
1.0
|
|
326
|
+
>>> metrics["coherence_passed"], metrics["frequency_passed"]
|
|
327
|
+
(True, True)
|
|
328
|
+
>>> metrics["coherence_value"] >= metrics["coherence_threshold"]
|
|
329
|
+
True
|
|
330
|
+
|
|
331
|
+
Notes
|
|
332
|
+
-----
|
|
333
|
+
The helper mutates/extends ``G.graph['MATH_ENGINE']`` so subsequent calls to
|
|
334
|
+
:mod:`tnfr.dynamics.runtime` can advance the mathematical engine without
|
|
335
|
+
further configuration.
|
|
58
336
|
"""
|
|
59
337
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def __call__(self, G: nx.Graph, node, **kw) -> None:
|
|
64
|
-
if self.glyph is None:
|
|
65
|
-
raise NotImplementedError("Operador sin glyph asignado")
|
|
66
|
-
apply_glyph_with_grammar(G, [node], self.glyph, kw.get("window"))
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
class Emision(Operador):
|
|
70
|
-
"""Aplicación del operador de emisión (símbolo ``AL``)."""
|
|
71
|
-
|
|
72
|
-
__slots__ = ()
|
|
73
|
-
name = "emision"
|
|
74
|
-
glyph = Glyph.AL.value
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
class Recepcion(Operador):
|
|
78
|
-
"""Operador de recepción (símbolo ``EN``)."""
|
|
79
|
-
|
|
80
|
-
__slots__ = ()
|
|
81
|
-
name = "recepcion"
|
|
82
|
-
glyph = Glyph.EN.value
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
class Coherencia(Operador):
|
|
86
|
-
"""Operador de coherencia (símbolo ``IL``)."""
|
|
87
|
-
|
|
88
|
-
__slots__ = ()
|
|
89
|
-
name = "coherencia"
|
|
90
|
-
glyph = Glyph.IL.value
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
class Disonancia(Operador):
|
|
94
|
-
"""Operador de disonancia (símbolo ``OZ``)."""
|
|
95
|
-
|
|
96
|
-
__slots__ = ()
|
|
97
|
-
name = "disonancia"
|
|
98
|
-
glyph = Glyph.OZ.value
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
class Acoplamiento(Operador):
|
|
102
|
-
"""Operador de acoplamiento (símbolo ``UM``)."""
|
|
103
|
-
|
|
104
|
-
__slots__ = ()
|
|
105
|
-
name = "acoplamiento"
|
|
106
|
-
glyph = Glyph.UM.value
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
class Resonancia(Operador):
|
|
110
|
-
"""Operador de resonancia (símbolo ``RA``)."""
|
|
111
|
-
|
|
112
|
-
__slots__ = ()
|
|
113
|
-
name = "resonancia"
|
|
114
|
-
glyph = Glyph.RA.value
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
class Silencio(Operador):
|
|
118
|
-
"""Operador de silencio (símbolo ``SHA``)."""
|
|
119
|
-
|
|
120
|
-
__slots__ = ()
|
|
121
|
-
name = "silencio"
|
|
122
|
-
glyph = Glyph.SHA.value
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
class Expansion(Operador):
|
|
126
|
-
"""Operador de expansión (símbolo ``VAL``)."""
|
|
127
|
-
|
|
128
|
-
__slots__ = ()
|
|
129
|
-
name = "expansion"
|
|
130
|
-
glyph = Glyph.VAL.value
|
|
338
|
+
if np is None:
|
|
339
|
+
raise ImportError("create_math_nfr requires NumPy; install the 'tnfr[math]' extras.")
|
|
131
340
|
|
|
341
|
+
G, node = create_nfr(
|
|
342
|
+
name,
|
|
343
|
+
epi=epi,
|
|
344
|
+
vf=vf,
|
|
345
|
+
theta=theta,
|
|
346
|
+
graph=graph,
|
|
347
|
+
dnfr_hook=dnfr_hook,
|
|
348
|
+
)
|
|
132
349
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
glyph = Glyph.THOL.value
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
class Mutacion(Operador):
|
|
150
|
-
"""Operador de mutación (símbolo ``ZHIR``)."""
|
|
151
|
-
|
|
152
|
-
__slots__ = ()
|
|
153
|
-
name = "mutacion"
|
|
154
|
-
glyph = Glyph.ZHIR.value
|
|
350
|
+
existing_cfg = G.graph.get("MATH_ENGINE")
|
|
351
|
+
mapping_cfg: Mapping[str, object] | None
|
|
352
|
+
if isinstance(existing_cfg, Mapping):
|
|
353
|
+
mapping_cfg = existing_cfg
|
|
354
|
+
else:
|
|
355
|
+
mapping_cfg = None
|
|
356
|
+
|
|
357
|
+
resolved_dimension = _resolve_dimension(
|
|
358
|
+
G,
|
|
359
|
+
dimension=dimension,
|
|
360
|
+
hilbert_space=hilbert_space,
|
|
361
|
+
existing_cfg=mapping_cfg,
|
|
362
|
+
)
|
|
155
363
|
|
|
364
|
+
hilbert = hilbert_space or HilbertSpace(resolved_dimension)
|
|
365
|
+
resolved_dimension = int(getattr(hilbert, "dimension", resolved_dimension))
|
|
156
366
|
|
|
157
|
-
|
|
158
|
-
|
|
367
|
+
coherence = _ensure_coherence_operator(
|
|
368
|
+
operator=coherence_operator,
|
|
369
|
+
dimension=resolved_dimension,
|
|
370
|
+
spectrum=coherence_spectrum,
|
|
371
|
+
c_min=coherence_c_min,
|
|
372
|
+
)
|
|
373
|
+
threshold = float(
|
|
374
|
+
coherence_threshold if coherence_threshold is not None else coherence.c_min
|
|
375
|
+
)
|
|
159
376
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
377
|
+
frequency = _ensure_frequency_operator(
|
|
378
|
+
operator=frequency_operator,
|
|
379
|
+
dimension=resolved_dimension,
|
|
380
|
+
diagonal=frequency_diagonal,
|
|
381
|
+
)
|
|
163
382
|
|
|
383
|
+
projector = state_projector or BasicStateProjector()
|
|
164
384
|
|
|
165
|
-
|
|
166
|
-
|
|
385
|
+
generator_matrix = _ensure_generator_matrix(
|
|
386
|
+
dimension=resolved_dimension,
|
|
387
|
+
diagonal=generator_diagonal,
|
|
388
|
+
)
|
|
389
|
+
engine = dynamics_engine or MathematicalDynamicsEngine(
|
|
390
|
+
generator_matrix,
|
|
391
|
+
hilbert_space=hilbert,
|
|
392
|
+
)
|
|
167
393
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
394
|
+
enforce_frequency = frequency is not None
|
|
395
|
+
spectral_validator = validator or NFRValidator(
|
|
396
|
+
hilbert,
|
|
397
|
+
coherence,
|
|
398
|
+
threshold,
|
|
399
|
+
frequency_operator=frequency if enforce_frequency else None,
|
|
400
|
+
)
|
|
171
401
|
|
|
402
|
+
state = projector(
|
|
403
|
+
epi=float(epi),
|
|
404
|
+
nu_f=float(vf),
|
|
405
|
+
theta=float(theta),
|
|
406
|
+
dim=resolved_dimension,
|
|
407
|
+
)
|
|
408
|
+
norm_value = float(hilbert.norm(state))
|
|
409
|
+
outcome = spectral_validator.validate(
|
|
410
|
+
state,
|
|
411
|
+
enforce_frequency_positivity=enforce_frequency,
|
|
412
|
+
)
|
|
413
|
+
summary_raw = outcome.summary
|
|
414
|
+
summary = {key: deepcopy(value) for key, value in summary_raw.items()}
|
|
415
|
+
|
|
416
|
+
coherence_summary = summary.get("coherence")
|
|
417
|
+
frequency_summary = summary.get("frequency")
|
|
418
|
+
|
|
419
|
+
math_metrics = {
|
|
420
|
+
"norm": norm_value,
|
|
421
|
+
"normalized": bool(summary.get("normalized", False)),
|
|
422
|
+
"coherence_value": float(coherence_summary.get("value", 0.0))
|
|
423
|
+
if isinstance(coherence_summary, Mapping)
|
|
424
|
+
else 0.0,
|
|
425
|
+
"coherence_threshold": float(
|
|
426
|
+
coherence_summary.get("threshold", threshold)
|
|
427
|
+
)
|
|
428
|
+
if isinstance(coherence_summary, Mapping)
|
|
429
|
+
else threshold,
|
|
430
|
+
"coherence_passed": bool(coherence_summary.get("passed", False))
|
|
431
|
+
if isinstance(coherence_summary, Mapping)
|
|
432
|
+
else False,
|
|
433
|
+
"frequency_value": float(frequency_summary.get("value", 0.0))
|
|
434
|
+
if isinstance(frequency_summary, Mapping)
|
|
435
|
+
else 0.0,
|
|
436
|
+
"frequency_passed": bool(frequency_summary.get("passed", False))
|
|
437
|
+
if isinstance(frequency_summary, Mapping)
|
|
438
|
+
else True,
|
|
439
|
+
"frequency_spectrum_min": float(
|
|
440
|
+
frequency_summary.get("spectrum_min", 0.0)
|
|
441
|
+
)
|
|
442
|
+
if isinstance(frequency_summary, Mapping)
|
|
443
|
+
and "spectrum_min" in frequency_summary
|
|
444
|
+
else None,
|
|
445
|
+
"unitary_passed": bool(
|
|
446
|
+
summary.get("unitary_stability", {}).get("passed", False)
|
|
447
|
+
),
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
node_context = {
|
|
451
|
+
"hilbert_space": hilbert,
|
|
452
|
+
"coherence_operator": coherence,
|
|
453
|
+
"frequency_operator": frequency,
|
|
454
|
+
"coherence_threshold": threshold,
|
|
455
|
+
"dimension": resolved_dimension,
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
node_data = G.nodes[node]
|
|
459
|
+
node_data["math_metrics"] = math_metrics
|
|
460
|
+
node_data["math_summary"] = summary
|
|
461
|
+
node_data["math_context"] = node_context
|
|
462
|
+
|
|
463
|
+
cfg = dict(mapping_cfg) if mapping_cfg is not None else {}
|
|
464
|
+
cfg.update(
|
|
465
|
+
{
|
|
466
|
+
"enabled": True,
|
|
467
|
+
"dimension": resolved_dimension,
|
|
468
|
+
"hilbert_space": hilbert,
|
|
469
|
+
"coherence_operator": coherence,
|
|
470
|
+
"coherence_threshold": threshold,
|
|
471
|
+
"frequency_operator": frequency,
|
|
472
|
+
"state_projector": projector,
|
|
473
|
+
"validator": spectral_validator,
|
|
474
|
+
"generator_matrix": generator_matrix,
|
|
475
|
+
"dynamics_engine": engine,
|
|
476
|
+
}
|
|
477
|
+
)
|
|
478
|
+
G.graph["MATH_ENGINE"] = cfg
|
|
172
479
|
|
|
173
|
-
|
|
174
|
-
Emision.name: Emision,
|
|
175
|
-
Recepcion.name: Recepcion,
|
|
176
|
-
Coherencia.name: Coherencia,
|
|
177
|
-
Disonancia.name: Disonancia,
|
|
178
|
-
Acoplamiento.name: Acoplamiento,
|
|
179
|
-
Resonancia.name: Resonancia,
|
|
180
|
-
Silencio.name: Silencio,
|
|
181
|
-
Expansion.name: Expansion,
|
|
182
|
-
Contraccion.name: Contraccion,
|
|
183
|
-
Autoorganizacion.name: Autoorganizacion,
|
|
184
|
-
Mutacion.name: Mutacion,
|
|
185
|
-
Transicion.name: Transicion,
|
|
186
|
-
Recursividad.name: Recursividad,
|
|
187
|
-
}
|
|
480
|
+
return G, node
|
|
188
481
|
|
|
189
482
|
|
|
190
483
|
__all__ = (
|
|
191
484
|
"create_nfr",
|
|
192
|
-
"
|
|
193
|
-
"
|
|
194
|
-
"
|
|
195
|
-
"
|
|
196
|
-
"
|
|
197
|
-
"
|
|
198
|
-
"
|
|
199
|
-
"
|
|
485
|
+
"create_math_nfr",
|
|
486
|
+
"Operator",
|
|
487
|
+
"Emission",
|
|
488
|
+
"Reception",
|
|
489
|
+
"Coherence",
|
|
490
|
+
"Dissonance",
|
|
491
|
+
"Coupling",
|
|
492
|
+
"Resonance",
|
|
493
|
+
"Silence",
|
|
200
494
|
"Expansion",
|
|
201
|
-
"
|
|
202
|
-
"
|
|
203
|
-
"
|
|
204
|
-
"
|
|
205
|
-
"
|
|
206
|
-
"
|
|
495
|
+
"Contraction",
|
|
496
|
+
"SelfOrganization",
|
|
497
|
+
"Mutation",
|
|
498
|
+
"Transition",
|
|
499
|
+
"Recursivity",
|
|
500
|
+
"OPERATORS",
|
|
207
501
|
"validate_sequence",
|
|
208
502
|
"run_sequence",
|
|
209
503
|
)
|
|
210
|
-
# ---------------------------------------------------------------------------
|
|
211
|
-
# 3) Motor de secuencias + validador sintáctico
|
|
212
|
-
# ---------------------------------------------------------------------------
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
_INICIO_VALIDOS = {"emision", "recursividad"}
|
|
216
|
-
_TRAMO_INTERMEDIO = {"disonancia", "acoplamiento", "resonancia"}
|
|
217
|
-
_CIERRE_VALIDO = {"silencio", "transicion", "recursividad"}
|
|
218
|
-
|
|
219
504
|
|
|
220
|
-
def _validate_start(token: str) -> tuple[bool, str]:
|
|
221
|
-
"""Ensure the sequence begins with a valid structural operator."""
|
|
222
505
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
if n == "autoorganizacion":
|
|
290
|
-
open_thol = True
|
|
291
|
-
elif open_thol and n in {"silencio", "contraccion"}:
|
|
292
|
-
open_thol = False
|
|
293
|
-
|
|
294
|
-
ok, msg = _validate_known_tokens(nombres_set)
|
|
295
|
-
if not ok:
|
|
296
|
-
return False, msg
|
|
297
|
-
ok, msg = _validate_intermediate(found_recepcion, found_coherencia, seen_intermedio)
|
|
298
|
-
if not ok:
|
|
299
|
-
return False, msg
|
|
300
|
-
ok, msg = _validate_end(nombres[-1], open_thol)
|
|
301
|
-
if not ok:
|
|
302
|
-
return False, msg
|
|
303
|
-
return True, "ok"
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
def validate_sequence(nombres: list[str]) -> tuple[bool, str]:
|
|
307
|
-
"""Validate minimal TNFR syntax rules."""
|
|
308
|
-
return _validate_token_sequence(nombres)
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
def run_sequence(G: nx.Graph, node, ops: Iterable[Operador]) -> None:
|
|
312
|
-
"""Execute a sequence of operators on ``node`` after validation."""
|
|
506
|
+
def run_sequence(G: TNFRGraph, node: NodeId, ops: Iterable[Operator]) -> None:
|
|
507
|
+
"""Drive structural sequences that rebalance EPI, νf, phase and ΔNFR.
|
|
508
|
+
|
|
509
|
+
The function enforces the canonical operator grammar, then executes each
|
|
510
|
+
operator so that the configured ΔNFR hook can update the nodal equation in
|
|
511
|
+
place. Each step is expected to express the structural effect of the
|
|
512
|
+
operator, while the hook keeps EPI, νf and phase consistent with the
|
|
513
|
+
resulting ΔNFR variations.
|
|
514
|
+
|
|
515
|
+
Parameters
|
|
516
|
+
----------
|
|
517
|
+
G : TNFRGraph
|
|
518
|
+
Graph that stores the node and its ΔNFR orchestration hook. The hook is
|
|
519
|
+
read from ``G.graph['compute_delta_nfr']`` and is responsible for
|
|
520
|
+
keeping the nodal equation up to date after each operator.
|
|
521
|
+
node : NodeId
|
|
522
|
+
Identifier of the node that will receive the operators. The node must
|
|
523
|
+
already contain the canonical attributes ``EPI``, ``νf`` and ``θ``.
|
|
524
|
+
ops : Iterable[Operator]
|
|
525
|
+
Iterable of canonical structural operators to apply. Their
|
|
526
|
+
concatenation must respect the validated TNFR grammar.
|
|
527
|
+
|
|
528
|
+
Returns
|
|
529
|
+
-------
|
|
530
|
+
None
|
|
531
|
+
The function mutates ``G`` in-place by updating the node attributes.
|
|
532
|
+
|
|
533
|
+
Raises
|
|
534
|
+
------
|
|
535
|
+
ValueError
|
|
536
|
+
Raised when the provided operator names do not satisfy the canonical
|
|
537
|
+
sequence validation rules.
|
|
538
|
+
|
|
539
|
+
Examples
|
|
540
|
+
--------
|
|
541
|
+
Run a validated trajectory that highlights the ΔNFR-driven evolution of the
|
|
542
|
+
node metrics.
|
|
543
|
+
|
|
544
|
+
>>> from tnfr.constants import DNFR_PRIMARY, EPI_PRIMARY, THETA_PRIMARY, VF_PRIMARY
|
|
545
|
+
>>> from tnfr.dynamics import set_delta_nfr_hook
|
|
546
|
+
>>> from tnfr.structural import (
|
|
547
|
+
... Coherence,
|
|
548
|
+
... Emission,
|
|
549
|
+
... Reception,
|
|
550
|
+
... Resonance,
|
|
551
|
+
... Silence,
|
|
552
|
+
... create_nfr,
|
|
553
|
+
... run_sequence,
|
|
554
|
+
... )
|
|
555
|
+
>>> G, node = create_nfr("seed", epi=0.8, vf=1.5, theta=0.0)
|
|
556
|
+
>>> def amplify_delta(graph):
|
|
557
|
+
... delta = graph.nodes[node][VF_PRIMARY] * 0.15
|
|
558
|
+
... graph.nodes[node][DNFR_PRIMARY] = delta
|
|
559
|
+
... graph.nodes[node][EPI_PRIMARY] += delta * 0.8
|
|
560
|
+
... graph.nodes[node][VF_PRIMARY] += delta * 0.1
|
|
561
|
+
... graph.nodes[node][THETA_PRIMARY] += 0.02
|
|
562
|
+
>>> set_delta_nfr_hook(G, amplify_delta)
|
|
563
|
+
>>> run_sequence(G, node, [Emission(), Reception(), Coherence(), Resonance(), Silence()]) # doctest: +SKIP
|
|
564
|
+
>>> (
|
|
565
|
+
... G.nodes[node][EPI_PRIMARY],
|
|
566
|
+
... G.nodes[node][VF_PRIMARY],
|
|
567
|
+
... G.nodes[node][THETA_PRIMARY],
|
|
568
|
+
... G.nodes[node][DNFR_PRIMARY],
|
|
569
|
+
... ) # doctest: +SKIP
|
|
570
|
+
(..., ..., ..., ...)
|
|
571
|
+
"""
|
|
313
572
|
|
|
314
573
|
compute = G.graph.get("compute_delta_nfr")
|
|
315
574
|
ops_list = list(ops)
|
|
316
|
-
|
|
575
|
+
names = [op.name for op in ops_list]
|
|
317
576
|
|
|
318
|
-
|
|
319
|
-
if not
|
|
320
|
-
|
|
577
|
+
outcome = validate_sequence(names)
|
|
578
|
+
if not outcome.passed:
|
|
579
|
+
summary_message = outcome.summary.get("message", "validation failed")
|
|
580
|
+
raise ValueError(f"Invalid sequence: {summary_message}")
|
|
321
581
|
|
|
322
582
|
for op in ops_list:
|
|
323
583
|
op(G, node)
|