tnfr 3.0.3__tar.gz → 4.0.0__tar.gz
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-3.0.3/LICENSE.txt → tnfr-4.0.0/LICENSE.md +1 -1
- tnfr-4.0.0/PKG-INFO +101 -0
- tnfr-4.0.0/README.md +74 -0
- {tnfr-3.0.3 → tnfr-4.0.0}/pyproject.toml +10 -2
- tnfr-4.0.0/src/tnfr/__init__.py +57 -0
- tnfr-4.0.0/src/tnfr/cli.py +177 -0
- {tnfr-3.0.3 → tnfr-4.0.0}/src/tnfr/constants.py +41 -11
- {tnfr-3.0.3 → tnfr-4.0.0}/src/tnfr/dynamics.py +87 -31
- tnfr-4.0.0/src/tnfr/gamma.py +91 -0
- tnfr-4.0.0/src/tnfr/grammar.py +149 -0
- {tnfr-3.0.3 → tnfr-4.0.0}/src/tnfr/helpers.py +43 -15
- {tnfr-3.0.3 → tnfr-4.0.0}/src/tnfr/main.py +20 -10
- tnfr-4.0.0/src/tnfr/metrics.py +211 -0
- {tnfr-3.0.3 → tnfr-4.0.0}/src/tnfr/observers.py +19 -7
- {tnfr-3.0.3 → tnfr-4.0.0}/src/tnfr/ontosim.py +12 -9
- {tnfr-3.0.3 → tnfr-4.0.0}/src/tnfr/operators.py +23 -6
- tnfr-4.0.0/src/tnfr/presets.py +24 -0
- tnfr-4.0.0/src/tnfr/program.py +168 -0
- tnfr-4.0.0/src/tnfr/scenarios.py +28 -0
- tnfr-4.0.0/src/tnfr/sense.py +215 -0
- tnfr-4.0.0/src/tnfr/trace.py +145 -0
- tnfr-4.0.0/src/tnfr/types.py +17 -0
- tnfr-4.0.0/src/tnfr.egg-info/PKG-INFO +101 -0
- tnfr-4.0.0/src/tnfr.egg-info/SOURCES.txt +35 -0
- tnfr-4.0.0/src/tnfr.egg-info/entry_points.txt +2 -0
- tnfr-4.0.0/tests/test_cli_sanity.py +12 -0
- tnfr-4.0.0/tests/test_edge_cases.py +29 -0
- tnfr-4.0.0/tests/test_gamma.py +20 -0
- tnfr-4.0.0/tests/test_grammar.py +56 -0
- tnfr-4.0.0/tests/test_history.py +15 -0
- tnfr-4.0.0/tests/test_invariants.py +84 -0
- tnfr-4.0.0/tests/test_program.py +17 -0
- tnfr-4.0.0/tests/test_remesh.py +29 -0
- tnfr-3.0.3/PKG-INFO +0 -35
- tnfr-3.0.3/README.md +0 -8
- tnfr-3.0.3/src/tnfr/__init__.py +0 -56
- tnfr-3.0.3/src/tnfr.egg-info/PKG-INFO +0 -35
- tnfr-3.0.3/src/tnfr.egg-info/SOURCES.txt +0 -16
- {tnfr-3.0.3 → tnfr-4.0.0}/setup.cfg +0 -0
- {tnfr-3.0.3 → tnfr-4.0.0}/src/tnfr.egg-info/dependency_links.txt +0 -0
- {tnfr-3.0.3 → tnfr-4.0.0}/src/tnfr.egg-info/requires.txt +0 -0
- {tnfr-3.0.3 → tnfr-4.0.0}/src/tnfr.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2025 TNFR - Teoría de la naturaleza fractral resonante
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
tnfr-4.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tnfr
|
|
3
|
+
Version: 4.0.0
|
|
4
|
+
Summary: TNFR canónica: dinámica glífica modular sobre redes.
|
|
5
|
+
Author: fmg
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://pypi.org/project/tnfr/
|
|
8
|
+
Project-URL: Repository, https://github.com/fermga/Teoria-de-la-naturaleza-fractal-resonante-TNFR-
|
|
9
|
+
Keywords: TNFR,fractal resonante,resonancia,glifos,networkx,dinámica,coherencia,EPI,Kuramoto
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Intended Audience :: Science/Research
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE.md
|
|
25
|
+
Requires-Dist: networkx>=2.6
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# General Project Structure
|
|
29
|
+
|
|
30
|
+
* **Package entry point.** `__init__.py` registers modules under short names to avoid circular imports and exposes the public API: `preparar_red`, `step`, `run`, and observation utilities.
|
|
31
|
+
|
|
32
|
+
* **Configuration & constants.** `constants.py` centralizes default parameters (discretization, EPI and νf ranges, mixing weights, re-mesh limits, etc.) and provides utilities to inject them into the network (`attach_defaults`, `merge_overrides`), along with standardized aliases for node attributes.
|
|
33
|
+
|
|
34
|
+
* **Cross-cutting utilities.** `helpers.py` offers core numeric helpers, alias-based attribute accessors, neighborhood statistics, glyph history, a callback system, and computation of the sense index `Si` for each node.
|
|
35
|
+
|
|
36
|
+
* **Dynamics engine.** `dynamics.py` implements the simulation loop: ΔNFR field computation, nodal equation integration, glyph selection/application, clamps, phase coordination, history updates, and conditional re-mesh (`step` and `run`).
|
|
37
|
+
|
|
38
|
+
* **Glyph operators.** `operators.py` defines the 13 glyphs as local transformations, a dispatcher `aplicar_glifo`, and both direct and stability-conditioned re-mesh utilities.
|
|
39
|
+
|
|
40
|
+
* **Observers & metrics.** `observers.py` registers standard callbacks and computes global coherence, phase synchrony, Kuramoto order, glyph distribution, and the sense vector `Σ⃗`, among others.
|
|
41
|
+
|
|
42
|
+
* **Simulation orchestration.** `ontosim.py` prepares a NetworkX graph, attaches configuration, and initializes attributes (EPI, phases, frequencies) before delegating dynamics to `dynamics.step`/`run`.
|
|
43
|
+
|
|
44
|
+
* **Demo CLI.** `main.py` generates an Erdős–Rényi network, lets you tweak basic parameters, and runs the simulation while displaying final metrics.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Key Concepts to Grasp
|
|
49
|
+
|
|
50
|
+
* **Aliased dependency tree.** Modules import each other via global aliases to simplify access and prevent cycles—essential for navigating the code unambiguously.
|
|
51
|
+
|
|
52
|
+
* **Normalized node attributes.** All data (EPI, phase `θ`, frequency `νf`, `ΔNFR`, etc.) live in `G.nodes[n]` under compatible alias names, making extensions and custom hooks straightforward.
|
|
53
|
+
|
|
54
|
+
* **Sense Index (`Si`).** Combines normalized frequency, phase dispersion, and field magnitude to evaluate each node’s “sense,” influencing glyph selection.
|
|
55
|
+
|
|
56
|
+
* **Step-wise engine.** `dynamics.step` orchestrates eight phases: field computation, `Si`, glyph selection & application, integration, clamps, phase coordination, history update, and conditioned re-mesh.
|
|
57
|
+
|
|
58
|
+
* **Glyphs as operators.** Each glyph applies a smooth transformation to node attributes (emission, diffusion, coupling, dissonance, etc.), dispatched by a configurable, typographic name.
|
|
59
|
+
|
|
60
|
+
* **Network re-mesh.** Mixes the current state with a past one (memory `τ`) to stabilize the network, with clear precedence for `α` and conditions based on recent stability and synchrony history.
|
|
61
|
+
|
|
62
|
+
* **Callbacks & observers.** The `Γ(R)` system lets you hook functions before/after each step and after re-mesh, enabling monitoring or external intervention.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Recommendations for Going Deeper
|
|
67
|
+
|
|
68
|
+
* **NetworkX & the Graph API.** Get comfortable with how NetworkX handles attributes and topology; all dynamics operate on `Graph` objects and their properties.
|
|
69
|
+
|
|
70
|
+
* **Extending the ΔNFR field.** Explore `set_delta_nfr_hook` to implement alternative nodal fields and learn how metadata and mixing weights are recorded.
|
|
71
|
+
|
|
72
|
+
* **Designing new glyphs.** Review `operators.py` to add operators or adjust factors in `DEFAULTS['GLYPH_FACTORS']`.
|
|
73
|
+
|
|
74
|
+
* **Custom observers.** Implement your own metrics via `register_callback` or by extending `observers.py` to measure phenomena specific to your study.
|
|
75
|
+
|
|
76
|
+
* **Theoretical reading.** For conceptual background, see the included PDFs (`TNFR.pdf`, *El Pulso que nos Atraviesa*), which deepen the fractal-resonant framework.
|
|
77
|
+
|
|
78
|
+
* **Advanced parameters.** Experiment with adaptive phase coordination, stability criteria, and the glyph grammar to observe their impact on network self-organization.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
**Mastering these pieces will let you extend the simulation, build analysis pipelines and connect the theory with computational applications.**
|
|
83
|
+
|
|
84
|
+
## Optional Node environment
|
|
85
|
+
The repository includes a minimal `package.json` and `netlify.toml` used for an experimental Remix web demo. They are not required for the core Python package; feel free to ignore them unless you plan to build the demo via `npm run build`.
|
|
86
|
+
|
|
87
|
+
## Testing
|
|
88
|
+
|
|
89
|
+
Install the dependencies and project in editable mode before running the test suite with `pytest`:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
pip install networkx
|
|
93
|
+
pip install -e .
|
|
94
|
+
pytest
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Installation
|
|
99
|
+
```
|
|
100
|
+
pip install tnfr
|
|
101
|
+
```
|
tnfr-4.0.0/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# General Project Structure
|
|
2
|
+
|
|
3
|
+
* **Package entry point.** `__init__.py` registers modules under short names to avoid circular imports and exposes the public API: `preparar_red`, `step`, `run`, and observation utilities.
|
|
4
|
+
|
|
5
|
+
* **Configuration & constants.** `constants.py` centralizes default parameters (discretization, EPI and νf ranges, mixing weights, re-mesh limits, etc.) and provides utilities to inject them into the network (`attach_defaults`, `merge_overrides`), along with standardized aliases for node attributes.
|
|
6
|
+
|
|
7
|
+
* **Cross-cutting utilities.** `helpers.py` offers core numeric helpers, alias-based attribute accessors, neighborhood statistics, glyph history, a callback system, and computation of the sense index `Si` for each node.
|
|
8
|
+
|
|
9
|
+
* **Dynamics engine.** `dynamics.py` implements the simulation loop: ΔNFR field computation, nodal equation integration, glyph selection/application, clamps, phase coordination, history updates, and conditional re-mesh (`step` and `run`).
|
|
10
|
+
|
|
11
|
+
* **Glyph operators.** `operators.py` defines the 13 glyphs as local transformations, a dispatcher `aplicar_glifo`, and both direct and stability-conditioned re-mesh utilities.
|
|
12
|
+
|
|
13
|
+
* **Observers & metrics.** `observers.py` registers standard callbacks and computes global coherence, phase synchrony, Kuramoto order, glyph distribution, and the sense vector `Σ⃗`, among others.
|
|
14
|
+
|
|
15
|
+
* **Simulation orchestration.** `ontosim.py` prepares a NetworkX graph, attaches configuration, and initializes attributes (EPI, phases, frequencies) before delegating dynamics to `dynamics.step`/`run`.
|
|
16
|
+
|
|
17
|
+
* **Demo CLI.** `main.py` generates an Erdős–Rényi network, lets you tweak basic parameters, and runs the simulation while displaying final metrics.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Key Concepts to Grasp
|
|
22
|
+
|
|
23
|
+
* **Aliased dependency tree.** Modules import each other via global aliases to simplify access and prevent cycles—essential for navigating the code unambiguously.
|
|
24
|
+
|
|
25
|
+
* **Normalized node attributes.** All data (EPI, phase `θ`, frequency `νf`, `ΔNFR`, etc.) live in `G.nodes[n]` under compatible alias names, making extensions and custom hooks straightforward.
|
|
26
|
+
|
|
27
|
+
* **Sense Index (`Si`).** Combines normalized frequency, phase dispersion, and field magnitude to evaluate each node’s “sense,” influencing glyph selection.
|
|
28
|
+
|
|
29
|
+
* **Step-wise engine.** `dynamics.step` orchestrates eight phases: field computation, `Si`, glyph selection & application, integration, clamps, phase coordination, history update, and conditioned re-mesh.
|
|
30
|
+
|
|
31
|
+
* **Glyphs as operators.** Each glyph applies a smooth transformation to node attributes (emission, diffusion, coupling, dissonance, etc.), dispatched by a configurable, typographic name.
|
|
32
|
+
|
|
33
|
+
* **Network re-mesh.** Mixes the current state with a past one (memory `τ`) to stabilize the network, with clear precedence for `α` and conditions based on recent stability and synchrony history.
|
|
34
|
+
|
|
35
|
+
* **Callbacks & observers.** The `Γ(R)` system lets you hook functions before/after each step and after re-mesh, enabling monitoring or external intervention.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Recommendations for Going Deeper
|
|
40
|
+
|
|
41
|
+
* **NetworkX & the Graph API.** Get comfortable with how NetworkX handles attributes and topology; all dynamics operate on `Graph` objects and their properties.
|
|
42
|
+
|
|
43
|
+
* **Extending the ΔNFR field.** Explore `set_delta_nfr_hook` to implement alternative nodal fields and learn how metadata and mixing weights are recorded.
|
|
44
|
+
|
|
45
|
+
* **Designing new glyphs.** Review `operators.py` to add operators or adjust factors in `DEFAULTS['GLYPH_FACTORS']`.
|
|
46
|
+
|
|
47
|
+
* **Custom observers.** Implement your own metrics via `register_callback` or by extending `observers.py` to measure phenomena specific to your study.
|
|
48
|
+
|
|
49
|
+
* **Theoretical reading.** For conceptual background, see the included PDFs (`TNFR.pdf`, *El Pulso que nos Atraviesa*), which deepen the fractal-resonant framework.
|
|
50
|
+
|
|
51
|
+
* **Advanced parameters.** Experiment with adaptive phase coordination, stability criteria, and the glyph grammar to observe their impact on network self-organization.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
**Mastering these pieces will let you extend the simulation, build analysis pipelines and connect the theory with computational applications.**
|
|
56
|
+
|
|
57
|
+
## Optional Node environment
|
|
58
|
+
The repository includes a minimal `package.json` and `netlify.toml` used for an experimental Remix web demo. They are not required for the core Python package; feel free to ignore them unless you plan to build the demo via `npm run build`.
|
|
59
|
+
|
|
60
|
+
## Testing
|
|
61
|
+
|
|
62
|
+
Install the dependencies and project in editable mode before running the test suite with `pytest`:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
pip install networkx
|
|
66
|
+
pip install -e .
|
|
67
|
+
pytest
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Installation
|
|
72
|
+
```
|
|
73
|
+
pip install tnfr
|
|
74
|
+
```
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tnfr"
|
|
3
|
-
version = "
|
|
3
|
+
version = "4.0.0"
|
|
4
4
|
description = "TNFR canónica: dinámica glífica modular sobre redes."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.9"
|
|
7
7
|
license = { text = "MIT" }
|
|
8
|
-
authors = [{ name = "
|
|
8
|
+
authors = [{ name = "fmg" }]
|
|
9
9
|
keywords = [
|
|
10
10
|
"TNFR", "fractal resonante", "resonancia", "glifos",
|
|
11
11
|
"networkx", "dinámica", "coherencia", "EPI", "Kuramoto"
|
|
@@ -24,8 +24,16 @@ classifiers = [
|
|
|
24
24
|
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
25
25
|
"Topic :: Scientific/Engineering :: Information Analysis"
|
|
26
26
|
]
|
|
27
|
+
|
|
27
28
|
dependencies = ["networkx>=2.6"]
|
|
28
29
|
|
|
30
|
+
[project.scripts]
|
|
31
|
+
tnfr = "tnfr.cli:main"
|
|
32
|
+
|
|
29
33
|
[project.urls]
|
|
30
34
|
Homepage = "https://pypi.org/project/tnfr/"
|
|
31
35
|
Repository = "https://github.com/fermga/Teoria-de-la-naturaleza-fractal-resonante-TNFR-"
|
|
36
|
+
|
|
37
|
+
[build-system]
|
|
38
|
+
requires = ["setuptools>=61", "wheel"]
|
|
39
|
+
build-backend = "setuptools.build_meta"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
"""
|
|
4
|
+
TNFR — Teoría de la Naturaleza Fractal Resonante
|
|
5
|
+
API pública del paquete.
|
|
6
|
+
|
|
7
|
+
Ecuación nodal:
|
|
8
|
+
∂EPI/∂t = νf · ΔNFR(t)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
__version__ = "4.0.0"
|
|
12
|
+
|
|
13
|
+
# Re-exports de la API pública
|
|
14
|
+
from .dynamics import step, run, set_delta_nfr_hook
|
|
15
|
+
from .ontosim import preparar_red
|
|
16
|
+
from .observers import attach_standard_observer, coherencia_global, orden_kuramoto
|
|
17
|
+
from .gamma import GAMMA_REGISTRY, eval_gamma, kuramoto_R_psi
|
|
18
|
+
from .grammar import enforce_canonical_grammar, on_applied_glifo
|
|
19
|
+
from .sense import (
|
|
20
|
+
GLYPHS_CANONICAL, glyph_angle, glyph_unit,
|
|
21
|
+
sigma_vector_node, sigma_vector_global,
|
|
22
|
+
push_sigma_snapshot, sigma_series, sigma_rose,
|
|
23
|
+
register_sigma_callback,
|
|
24
|
+
)
|
|
25
|
+
from .metrics import (
|
|
26
|
+
register_metrics_callbacks,
|
|
27
|
+
Tg_global, Tg_by_node,
|
|
28
|
+
latency_series, glifogram_series,
|
|
29
|
+
glyph_top, glyph_dwell_stats,
|
|
30
|
+
)
|
|
31
|
+
from .trace import register_trace
|
|
32
|
+
from .program import play, seq, block, target, wait, THOL, TARGET, WAIT
|
|
33
|
+
from .cli import main as cli_main
|
|
34
|
+
from .scenarios import build_graph
|
|
35
|
+
from .presets import get_preset
|
|
36
|
+
from .types import NodeState
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"preparar_red",
|
|
40
|
+
"step", "run", "set_delta_nfr_hook",
|
|
41
|
+
"attach_standard_observer", "coherencia_global", "orden_kuramoto",
|
|
42
|
+
"GAMMA_REGISTRY", "eval_gamma", "kuramoto_R_psi",
|
|
43
|
+
"enforce_canonical_grammar", "on_applied_glifo",
|
|
44
|
+
"GLYPHS_CANONICAL", "glyph_angle", "glyph_unit",
|
|
45
|
+
"sigma_vector_node", "sigma_vector_global",
|
|
46
|
+
"push_sigma_snapshot", "sigma_series", "sigma_rose",
|
|
47
|
+
"register_sigma_callback",
|
|
48
|
+
"register_metrics_callbacks",
|
|
49
|
+
"register_trace",
|
|
50
|
+
"Tg_global", "Tg_by_node",
|
|
51
|
+
"latency_series", "glifogram_series",
|
|
52
|
+
"glyph_top", "glyph_dwell_stats",
|
|
53
|
+
"play", "seq", "block", "target", "wait", "THOL", "TARGET", "WAIT",
|
|
54
|
+
"__version__",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
__all__ += ["cli_main", "build_graph", "get_preset", "NodeState"]
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import argparse
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
try: # pragma: no cover - opcional
|
|
7
|
+
import yaml # type: ignore
|
|
8
|
+
except Exception: # pragma: no cover - yaml es opcional
|
|
9
|
+
yaml = None
|
|
10
|
+
|
|
11
|
+
import networkx as nx
|
|
12
|
+
|
|
13
|
+
from .constants import inject_defaults, DEFAULTS
|
|
14
|
+
from .sense import register_sigma_callback, sigma_series, sigma_rose
|
|
15
|
+
from .metrics import (
|
|
16
|
+
register_metrics_callbacks,
|
|
17
|
+
Tg_global,
|
|
18
|
+
latency_series,
|
|
19
|
+
glifogram_series,
|
|
20
|
+
glyph_top,
|
|
21
|
+
)
|
|
22
|
+
from .trace import register_trace
|
|
23
|
+
from .program import play, seq, block, wait, target
|
|
24
|
+
from .dynamics import step, _update_history
|
|
25
|
+
from .scenarios import build_graph
|
|
26
|
+
from .presets import get_preset
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _save_json(path: str, data: Any) -> None:
|
|
30
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
31
|
+
json.dump(data, f, ensure_ascii=False, indent=2)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _load_sequence(path: str) -> List[Any]:
|
|
35
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
36
|
+
text = f.read()
|
|
37
|
+
if path.endswith(".yaml") or path.endswith(".yml"):
|
|
38
|
+
if not yaml:
|
|
39
|
+
raise RuntimeError("pyyaml no está instalado, usa JSON o instala pyyaml")
|
|
40
|
+
data = yaml.safe_load(text)
|
|
41
|
+
else:
|
|
42
|
+
data = json.loads(text)
|
|
43
|
+
|
|
44
|
+
def parse_token(tok: Any):
|
|
45
|
+
if isinstance(tok, str):
|
|
46
|
+
return tok
|
|
47
|
+
if isinstance(tok, dict):
|
|
48
|
+
if "WAIT" in tok:
|
|
49
|
+
return wait(int(tok["WAIT"]))
|
|
50
|
+
if "TARGET" in tok:
|
|
51
|
+
return target(tok["TARGET"])
|
|
52
|
+
if "THOL" in tok:
|
|
53
|
+
spec = tok["THOL"] or {}
|
|
54
|
+
b = [_parse_inner(x) for x in spec.get("body", [])]
|
|
55
|
+
return block(*b, repeat=int(spec.get("repeat", 1)), close=spec.get("close"))
|
|
56
|
+
raise ValueError(f"Token inválido: {tok}")
|
|
57
|
+
|
|
58
|
+
def _parse_inner(x: Any):
|
|
59
|
+
return parse_token(x)
|
|
60
|
+
|
|
61
|
+
return [parse_token(t) for t in data]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _attach_callbacks(G: nx.Graph) -> None:
|
|
65
|
+
inject_defaults(G, DEFAULTS)
|
|
66
|
+
register_sigma_callback(G)
|
|
67
|
+
register_metrics_callbacks(G)
|
|
68
|
+
register_trace(G)
|
|
69
|
+
_update_history(G)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def cmd_run(args: argparse.Namespace) -> int:
|
|
73
|
+
G = build_graph(n=args.nodes, topology=args.topology, seed=args.seed)
|
|
74
|
+
_attach_callbacks(G)
|
|
75
|
+
|
|
76
|
+
if args.preset:
|
|
77
|
+
program = get_preset(args.preset)
|
|
78
|
+
play(G, program)
|
|
79
|
+
else:
|
|
80
|
+
steps = int(args.steps or 100)
|
|
81
|
+
for _ in range(steps):
|
|
82
|
+
step(G)
|
|
83
|
+
|
|
84
|
+
if args.save_history:
|
|
85
|
+
_save_json(args.save_history, G.graph.get("history", {}))
|
|
86
|
+
|
|
87
|
+
if args.summary:
|
|
88
|
+
tg = Tg_global(G, normalize=True)
|
|
89
|
+
lat = latency_series(G)
|
|
90
|
+
print("Top glifos por Tg:", glyph_top(G, k=5))
|
|
91
|
+
if lat["value"]:
|
|
92
|
+
print("Latencia media:", sum(lat["value"]) / max(1, len(lat["value"])) )
|
|
93
|
+
return 0
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def cmd_sequence(args: argparse.Namespace) -> int:
|
|
97
|
+
G = build_graph(n=args.nodes, topology=args.topology, seed=args.seed)
|
|
98
|
+
_attach_callbacks(G)
|
|
99
|
+
|
|
100
|
+
if args.preset:
|
|
101
|
+
program = get_preset(args.preset)
|
|
102
|
+
elif args.sequence_file:
|
|
103
|
+
program = _load_sequence(args.sequence_file)
|
|
104
|
+
else:
|
|
105
|
+
program = seq("A’L", "E’N", "I’L", block("O’Z", "Z’HIR", "I’L", repeat=1), "R’A", "SH’A")
|
|
106
|
+
|
|
107
|
+
play(G, program)
|
|
108
|
+
|
|
109
|
+
if args.save_history:
|
|
110
|
+
_save_json(args.save_history, G.graph.get("history", {}))
|
|
111
|
+
return 0
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def cmd_metrics(args: argparse.Namespace) -> int:
|
|
115
|
+
G = build_graph(n=args.nodes, topology=args.topology, seed=args.seed)
|
|
116
|
+
_attach_callbacks(G)
|
|
117
|
+
for _ in range(int(args.steps or 200)):
|
|
118
|
+
step(G)
|
|
119
|
+
|
|
120
|
+
tg = Tg_global(G, normalize=True)
|
|
121
|
+
lat = latency_series(G)
|
|
122
|
+
rose = sigma_rose(G)
|
|
123
|
+
glifo = glifogram_series(G)
|
|
124
|
+
|
|
125
|
+
out = {
|
|
126
|
+
"Tg_global": tg,
|
|
127
|
+
"latency_mean": (sum(lat["value"]) / max(1, len(lat["value"])) ) if lat["value"] else 0.0,
|
|
128
|
+
"rose": rose,
|
|
129
|
+
"glifogram": {k: v[:10] for k, v in glifo.items()},
|
|
130
|
+
}
|
|
131
|
+
if args.save:
|
|
132
|
+
_save_json(args.save, out)
|
|
133
|
+
else:
|
|
134
|
+
print(json.dumps(out, ensure_ascii=False, indent=2))
|
|
135
|
+
return 0
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def main(argv: Optional[List[str]] = None) -> int:
|
|
139
|
+
p = argparse.ArgumentParser(prog="tnfr")
|
|
140
|
+
sub = p.add_subparsers(dest="cmd")
|
|
141
|
+
|
|
142
|
+
p_run = sub.add_parser("run", help="Correr escenario libre o preset y opcionalmente exportar history")
|
|
143
|
+
p_run.add_argument("--nodes", type=int, default=24)
|
|
144
|
+
p_run.add_argument("--topology", choices=["ring", "complete", "erdos"], default="ring")
|
|
145
|
+
p_run.add_argument("--steps", type=int, default=200)
|
|
146
|
+
p_run.add_argument("--seed", type=int, default=1)
|
|
147
|
+
p_run.add_argument("--preset", type=str, default=None)
|
|
148
|
+
p_run.add_argument("--save-history", dest="save_history", type=str, default=None)
|
|
149
|
+
p_run.add_argument("--summary", action="store_true")
|
|
150
|
+
p_run.set_defaults(func=cmd_run)
|
|
151
|
+
|
|
152
|
+
p_seq = sub.add_parser("sequence", help="Ejecutar una secuencia (preset o YAML/JSON)")
|
|
153
|
+
p_seq.add_argument("--nodes", type=int, default=24)
|
|
154
|
+
p_seq.add_argument("--topology", choices=["ring", "complete", "erdos"], default="ring")
|
|
155
|
+
p_seq.add_argument("--seed", type=int, default=1)
|
|
156
|
+
p_seq.add_argument("--preset", type=str, default=None)
|
|
157
|
+
p_seq.add_argument("--sequence-file", type=str, default=None)
|
|
158
|
+
p_seq.add_argument("--save-history", dest="save_history", type=str, default=None)
|
|
159
|
+
p_seq.set_defaults(func=cmd_sequence)
|
|
160
|
+
|
|
161
|
+
p_met = sub.add_parser("metrics", help="Correr breve y volcar métricas clave")
|
|
162
|
+
p_met.add_argument("--nodes", type=int, default=24)
|
|
163
|
+
p_met.add_argument("--topology", choices=["ring", "complete", "erdos"], default="ring")
|
|
164
|
+
p_met.add_argument("--steps", type=int, default=300)
|
|
165
|
+
p_met.add_argument("--seed", type=int, default=1)
|
|
166
|
+
p_met.add_argument("--save", type=str, default=None)
|
|
167
|
+
p_met.set_defaults(func=cmd_metrics)
|
|
168
|
+
|
|
169
|
+
args = p.parse_args(argv)
|
|
170
|
+
if not hasattr(args, "func"):
|
|
171
|
+
p.print_help()
|
|
172
|
+
return 1
|
|
173
|
+
return int(args.func(args))
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
if __name__ == "__main__": # pragma: no cover
|
|
177
|
+
raise SystemExit(main())
|
|
@@ -10,7 +10,7 @@ from typing import Dict, Any
|
|
|
10
10
|
# -------------------------
|
|
11
11
|
# Parámetros canónicos
|
|
12
12
|
# -------------------------
|
|
13
|
-
DEFAULTS: Dict[str, Any] = {
|
|
13
|
+
DEFAULTS: Dict[str, Any] = {
|
|
14
14
|
# Discretización
|
|
15
15
|
"DT": 1.0,
|
|
16
16
|
|
|
@@ -147,22 +147,52 @@ DEFAULTS: Dict[str, Any] = {
|
|
|
147
147
|
"dnfr_hi": 0.50, "dnfr_lo": 0.10,
|
|
148
148
|
"accel_hi": 0.50, "accel_lo": 0.10
|
|
149
149
|
},
|
|
150
|
-
# Callbacks Γ(R)
|
|
151
|
-
"
|
|
152
|
-
|
|
150
|
+
# Callbacks Γ(R)
|
|
151
|
+
"GAMMA": {
|
|
152
|
+
"type": "none", # 'none' | 'kuramoto_linear' | 'kuramoto_bandpass'
|
|
153
|
+
"beta": 0.0,
|
|
154
|
+
"R0": 0.0,
|
|
155
|
+
},
|
|
156
|
+
"CALLBACKS_STRICT": False, # si True, un error en callback detiene; si False, se loguea y continúa
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
# Gramática glífica canónica
|
|
160
|
+
DEFAULTS.setdefault("GRAMMAR_CANON", {
|
|
161
|
+
"enabled": True, # activar la gramática canónica
|
|
162
|
+
"zhir_requires_oz_window": 3, # cuántos pasos atrás buscamos O’Z
|
|
163
|
+
"zhir_dnfr_min": 0.05, # si |ΔNFR|_norm < este valor, no permitimos Z’HIR sin O’Z
|
|
164
|
+
"thol_min_len": 2,
|
|
165
|
+
"thol_max_len": 6,
|
|
166
|
+
"thol_close_dnfr": 0.15, # si el campo calma, cerramos con SH’A/NU’L
|
|
167
|
+
"si_high": 0.66, # umbral para elegir NU’L vs SH’A al cerrar
|
|
168
|
+
})
|
|
153
169
|
|
|
154
170
|
|
|
155
171
|
# -------------------------
|
|
156
172
|
# Utilidades
|
|
157
173
|
# -------------------------
|
|
158
174
|
|
|
159
|
-
def attach_defaults(G, override: bool = False) -> None:
|
|
160
|
-
"""Escribe DEFAULTS en G.graph (sin sobreescribir si override=False)."""
|
|
161
|
-
G.graph.setdefault("_tnfr_defaults_attached", False)
|
|
162
|
-
for k, v in DEFAULTS.items():
|
|
163
|
-
if override or k not in G.graph:
|
|
164
|
-
G.graph[k] = v
|
|
165
|
-
G.graph["_tnfr_defaults_attached"] = True
|
|
175
|
+
def attach_defaults(G, override: bool = False) -> None:
|
|
176
|
+
"""Escribe DEFAULTS en G.graph (sin sobreescribir si override=False)."""
|
|
177
|
+
G.graph.setdefault("_tnfr_defaults_attached", False)
|
|
178
|
+
for k, v in DEFAULTS.items():
|
|
179
|
+
if override or k not in G.graph:
|
|
180
|
+
G.graph[k] = v
|
|
181
|
+
G.graph["_tnfr_defaults_attached"] = True
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def inject_defaults(G, defaults: Dict[str, Any] = DEFAULTS, override: bool = False) -> None:
|
|
185
|
+
"""Alias de conveniencia para inyectar ``DEFAULTS`` en ``G.graph``.
|
|
186
|
+
|
|
187
|
+
Permite pasar un diccionario de *defaults* alternativo y mantiene la
|
|
188
|
+
semántica de ``attach_defaults`` existente. Si ``override`` es ``True`` se
|
|
189
|
+
sobreescriben valores ya presentes.
|
|
190
|
+
"""
|
|
191
|
+
G.graph.setdefault("_tnfr_defaults_attached", False)
|
|
192
|
+
for k, v in defaults.items():
|
|
193
|
+
if override or k not in G.graph:
|
|
194
|
+
G.graph[k] = v
|
|
195
|
+
G.graph["_tnfr_defaults_attached"] = True
|
|
166
196
|
|
|
167
197
|
|
|
168
198
|
def merge_overrides(G, **overrides) -> None:
|