tnfr 6.0.0__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.

Files changed (176) hide show
  1. tnfr/__init__.py +50 -5
  2. tnfr/__init__.pyi +0 -7
  3. tnfr/_compat.py +0 -1
  4. tnfr/_generated_version.py +34 -0
  5. tnfr/_version.py +44 -2
  6. tnfr/alias.py +14 -13
  7. tnfr/alias.pyi +5 -37
  8. tnfr/cache.py +9 -729
  9. tnfr/cache.pyi +8 -224
  10. tnfr/callback_utils.py +16 -31
  11. tnfr/callback_utils.pyi +3 -29
  12. tnfr/cli/__init__.py +17 -11
  13. tnfr/cli/__init__.pyi +0 -21
  14. tnfr/cli/arguments.py +175 -14
  15. tnfr/cli/arguments.pyi +5 -11
  16. tnfr/cli/execution.py +434 -48
  17. tnfr/cli/execution.pyi +14 -24
  18. tnfr/cli/utils.py +20 -3
  19. tnfr/cli/utils.pyi +5 -5
  20. tnfr/config/__init__.py +2 -1
  21. tnfr/config/__init__.pyi +2 -0
  22. tnfr/config/feature_flags.py +83 -0
  23. tnfr/config/init.py +1 -1
  24. tnfr/config/operator_names.py +1 -14
  25. tnfr/config/presets.py +6 -26
  26. tnfr/constants/__init__.py +10 -13
  27. tnfr/constants/__init__.pyi +10 -22
  28. tnfr/constants/aliases.py +31 -0
  29. tnfr/constants/core.py +4 -3
  30. tnfr/constants/init.py +1 -1
  31. tnfr/constants/metric.py +3 -3
  32. tnfr/dynamics/__init__.py +64 -10
  33. tnfr/dynamics/__init__.pyi +3 -4
  34. tnfr/dynamics/adaptation.py +79 -13
  35. tnfr/dynamics/aliases.py +10 -9
  36. tnfr/dynamics/coordination.py +77 -35
  37. tnfr/dynamics/dnfr.py +575 -274
  38. tnfr/dynamics/dnfr.pyi +1 -10
  39. tnfr/dynamics/integrators.py +47 -33
  40. tnfr/dynamics/integrators.pyi +0 -1
  41. tnfr/dynamics/runtime.py +489 -129
  42. tnfr/dynamics/sampling.py +2 -0
  43. tnfr/dynamics/selectors.py +101 -62
  44. tnfr/execution.py +15 -8
  45. tnfr/execution.pyi +5 -25
  46. tnfr/flatten.py +7 -3
  47. tnfr/flatten.pyi +1 -8
  48. tnfr/gamma.py +22 -26
  49. tnfr/gamma.pyi +0 -6
  50. tnfr/glyph_history.py +37 -26
  51. tnfr/glyph_history.pyi +1 -19
  52. tnfr/glyph_runtime.py +16 -0
  53. tnfr/glyph_runtime.pyi +9 -0
  54. tnfr/immutable.py +20 -15
  55. tnfr/immutable.pyi +4 -7
  56. tnfr/initialization.py +5 -7
  57. tnfr/initialization.pyi +1 -9
  58. tnfr/io.py +6 -305
  59. tnfr/io.pyi +13 -8
  60. tnfr/mathematics/__init__.py +81 -0
  61. tnfr/mathematics/backend.py +426 -0
  62. tnfr/mathematics/dynamics.py +398 -0
  63. tnfr/mathematics/epi.py +254 -0
  64. tnfr/mathematics/generators.py +222 -0
  65. tnfr/mathematics/metrics.py +119 -0
  66. tnfr/mathematics/operators.py +233 -0
  67. tnfr/mathematics/operators_factory.py +71 -0
  68. tnfr/mathematics/projection.py +78 -0
  69. tnfr/mathematics/runtime.py +173 -0
  70. tnfr/mathematics/spaces.py +247 -0
  71. tnfr/mathematics/transforms.py +292 -0
  72. tnfr/metrics/__init__.py +10 -10
  73. tnfr/metrics/coherence.py +123 -94
  74. tnfr/metrics/common.py +22 -13
  75. tnfr/metrics/common.pyi +42 -11
  76. tnfr/metrics/core.py +72 -14
  77. tnfr/metrics/diagnosis.py +48 -57
  78. tnfr/metrics/diagnosis.pyi +3 -7
  79. tnfr/metrics/export.py +3 -5
  80. tnfr/metrics/glyph_timing.py +41 -31
  81. tnfr/metrics/reporting.py +13 -6
  82. tnfr/metrics/sense_index.py +884 -114
  83. tnfr/metrics/trig.py +167 -11
  84. tnfr/metrics/trig.pyi +1 -0
  85. tnfr/metrics/trig_cache.py +112 -15
  86. tnfr/node.py +400 -17
  87. tnfr/node.pyi +55 -38
  88. tnfr/observers.py +111 -8
  89. tnfr/observers.pyi +0 -15
  90. tnfr/ontosim.py +9 -6
  91. tnfr/ontosim.pyi +0 -5
  92. tnfr/operators/__init__.py +529 -42
  93. tnfr/operators/__init__.pyi +14 -0
  94. tnfr/operators/definitions.py +350 -18
  95. tnfr/operators/definitions.pyi +0 -14
  96. tnfr/operators/grammar.py +760 -0
  97. tnfr/operators/jitter.py +28 -22
  98. tnfr/operators/registry.py +7 -12
  99. tnfr/operators/registry.pyi +0 -2
  100. tnfr/operators/remesh.py +38 -61
  101. tnfr/rng.py +17 -300
  102. tnfr/schemas/__init__.py +8 -0
  103. tnfr/schemas/grammar.json +94 -0
  104. tnfr/selector.py +3 -4
  105. tnfr/selector.pyi +1 -1
  106. tnfr/sense.py +22 -24
  107. tnfr/sense.pyi +0 -7
  108. tnfr/structural.py +504 -21
  109. tnfr/structural.pyi +41 -18
  110. tnfr/telemetry/__init__.py +23 -1
  111. tnfr/telemetry/cache_metrics.py +226 -0
  112. tnfr/telemetry/nu_f.py +423 -0
  113. tnfr/telemetry/nu_f.pyi +123 -0
  114. tnfr/tokens.py +1 -4
  115. tnfr/tokens.pyi +1 -6
  116. tnfr/trace.py +20 -53
  117. tnfr/trace.pyi +9 -37
  118. tnfr/types.py +244 -15
  119. tnfr/types.pyi +200 -14
  120. tnfr/units.py +69 -0
  121. tnfr/units.pyi +16 -0
  122. tnfr/utils/__init__.py +107 -48
  123. tnfr/utils/__init__.pyi +80 -11
  124. tnfr/utils/cache.py +1705 -65
  125. tnfr/utils/cache.pyi +370 -58
  126. tnfr/utils/chunks.py +104 -0
  127. tnfr/utils/chunks.pyi +21 -0
  128. tnfr/utils/data.py +95 -5
  129. tnfr/utils/data.pyi +8 -17
  130. tnfr/utils/graph.py +2 -4
  131. tnfr/utils/init.py +31 -7
  132. tnfr/utils/init.pyi +4 -11
  133. tnfr/utils/io.py +313 -14
  134. tnfr/{helpers → utils}/numeric.py +50 -24
  135. tnfr/utils/numeric.pyi +21 -0
  136. tnfr/validation/__init__.py +92 -4
  137. tnfr/validation/__init__.pyi +77 -17
  138. tnfr/validation/compatibility.py +79 -43
  139. tnfr/validation/compatibility.pyi +4 -6
  140. tnfr/validation/grammar.py +55 -133
  141. tnfr/validation/grammar.pyi +37 -8
  142. tnfr/validation/graph.py +138 -0
  143. tnfr/validation/graph.pyi +17 -0
  144. tnfr/validation/rules.py +161 -74
  145. tnfr/validation/rules.pyi +55 -18
  146. tnfr/validation/runtime.py +263 -0
  147. tnfr/validation/runtime.pyi +31 -0
  148. tnfr/validation/soft_filters.py +170 -0
  149. tnfr/validation/soft_filters.pyi +37 -0
  150. tnfr/validation/spectral.py +159 -0
  151. tnfr/validation/spectral.pyi +46 -0
  152. tnfr/validation/syntax.py +28 -139
  153. tnfr/validation/syntax.pyi +7 -4
  154. tnfr/validation/window.py +39 -0
  155. tnfr/validation/window.pyi +1 -0
  156. tnfr/viz/__init__.py +9 -0
  157. tnfr/viz/matplotlib.py +246 -0
  158. {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/METADATA +63 -19
  159. tnfr-7.0.0.dist-info/RECORD +185 -0
  160. {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/licenses/LICENSE.md +1 -1
  161. tnfr/constants_glyphs.py +0 -16
  162. tnfr/constants_glyphs.pyi +0 -12
  163. tnfr/grammar.py +0 -25
  164. tnfr/grammar.pyi +0 -13
  165. tnfr/helpers/__init__.py +0 -151
  166. tnfr/helpers/__init__.pyi +0 -66
  167. tnfr/helpers/numeric.pyi +0 -12
  168. tnfr/presets.py +0 -15
  169. tnfr/presets.pyi +0 -7
  170. tnfr/utils/io.pyi +0 -10
  171. tnfr/utils/validators.py +0 -130
  172. tnfr/utils/validators.pyi +0 -19
  173. tnfr-6.0.0.dist-info/RECORD +0 -157
  174. {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/WHEEL +0 -0
  175. {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/entry_points.txt +0 -0
  176. {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/top_level.txt +0 -0
tnfr/cli/execution.pyi CHANGED
@@ -6,75 +6,65 @@ from typing import Any, Optional
6
6
 
7
7
  import networkx as nx
8
8
 
9
+ from ..config import apply_config
10
+ from ..config.presets import get_preset
9
11
  from ..constants import METRIC_DEFAULTS
10
12
  from ..dynamics import (
11
- run,
12
13
  default_glyph_selector,
13
14
  parametric_glyph_selector,
15
+ run,
14
16
  validate_canon,
15
17
  )
16
18
  from ..execution import CANONICAL_PRESET_NAME, play, seq
17
19
  from ..flatten import parse_program_tokens
18
20
  from ..glyph_history import ensure_history
19
- from ..io import read_structured_file, safe_write, StructuredFileError
20
21
  from ..metrics import (
21
- register_metrics_callbacks,
22
- glyph_top,
23
- export_metrics,
24
22
  build_metrics_summary,
23
+ export_metrics,
24
+ glyph_top,
25
+ register_metrics_callbacks,
25
26
  )
26
27
  from ..metrics.core import _metrics_step
27
28
  from ..ontosim import prepare_network
28
29
  from ..sense import register_sigma_callback
29
30
  from ..trace import register_trace
30
31
  from ..types import Glyph, ProgramTokens
31
- from ..utils import get_logger, json_dumps
32
- from ..config import apply_config
33
- from ..config.presets import get_preset
34
-
32
+ from ..utils import (
33
+ StructuredFileError,
34
+ get_logger,
35
+ json_dumps,
36
+ read_structured_file,
37
+ safe_write,
38
+ )
35
39
  from .arguments import _args_to_dict
36
40
 
37
41
  DEFAULT_SUMMARY_SERIES_LIMIT: int
38
42
  logger: Any
39
43
 
40
-
41
44
  def _save_json(path: str, data: Any) -> None: ...
42
-
43
45
  def _attach_callbacks(G: nx.Graph) -> None: ...
44
-
45
46
  def _persist_history(G: nx.Graph, args: argparse.Namespace) -> None: ...
46
-
47
47
  def build_basic_graph(args: argparse.Namespace) -> nx.Graph: ...
48
-
49
48
  def apply_cli_config(G: nx.Graph, args: argparse.Namespace) -> None: ...
50
-
51
49
  def register_callbacks_and_observer(G: nx.Graph) -> None: ...
52
-
53
50
  def _build_graph_from_args(args: argparse.Namespace) -> nx.Graph: ...
54
-
55
51
  def _load_sequence(path: Path) -> ProgramTokens: ...
56
-
57
52
  def resolve_program(
58
53
  args: argparse.Namespace, default: Optional[ProgramTokens] = ...
59
54
  ) -> Optional[ProgramTokens]: ...
60
-
61
55
  def run_program(
62
56
  G: Optional[nx.Graph],
63
57
  program: Optional[ProgramTokens],
64
58
  args: argparse.Namespace,
65
59
  ) -> nx.Graph: ...
66
-
67
60
  def _run_cli_program(
68
61
  args: argparse.Namespace,
69
62
  *,
70
63
  default_program: Optional[ProgramTokens] = ...,
71
64
  graph: Optional[nx.Graph] = ...,
72
65
  ) -> tuple[int, Optional[nx.Graph]]: ...
73
-
74
66
  def _log_run_summaries(G: nx.Graph, args: argparse.Namespace) -> None: ...
75
-
76
67
  def cmd_run(args: argparse.Namespace) -> int: ...
77
-
78
68
  def cmd_sequence(args: argparse.Namespace) -> int: ...
79
-
80
69
  def cmd_metrics(args: argparse.Namespace) -> int: ...
70
+ def cmd_profile_si(args: argparse.Namespace) -> int: ...
tnfr/cli/utils.py CHANGED
@@ -2,8 +2,11 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from collections.abc import Iterable
5
6
  from typing import Any
6
7
 
8
+ from ..utils import normalize_optional_int
9
+
7
10
 
8
11
  def spec(opt: str, /, **kwargs: Any) -> tuple[str, dict[str, Any]]:
9
12
  """Create an argument specification pair.
@@ -27,8 +30,22 @@ def spec(opt: str, /, **kwargs: Any) -> tuple[str, dict[str, Any]]:
27
30
  """
28
31
 
29
32
  kwargs = dict(kwargs)
30
- kwargs.setdefault(
31
- "dest", opt.lstrip("-").replace("-", "_").replace(".", "_")
32
- )
33
+ kwargs.setdefault("dest", opt.lstrip("-").replace("-", "_").replace(".", "_"))
33
34
  kwargs.setdefault("default", None)
34
35
  return opt, kwargs
36
+
37
+
38
+ def _parse_cli_variants(values: Iterable[Any] | None) -> list[int | None]:
39
+ """Return a stable list of integer/``None`` variants for the CLI options."""
40
+
41
+ if values is None:
42
+ return [None]
43
+ parsed: list[int | None] = []
44
+ seen: set[int | None] = set()
45
+ for raw in values:
46
+ coerced = normalize_optional_int(raw, strict=True)
47
+ if coerced in seen:
48
+ continue
49
+ seen.add(coerced)
50
+ parsed.append(coerced)
51
+ return parsed or [None]
tnfr/cli/utils.pyi CHANGED
@@ -1,8 +1,8 @@
1
- from typing import Any
1
+ from __future__ import annotations
2
2
 
3
- __all__: Any
3
+ from collections.abc import Iterable
4
+ from typing import Any
4
5
 
5
- def __getattr__(name: str) -> Any: ...
6
+ def spec(opt: str, /, **kwargs: Any) -> tuple[str, dict[str, Any]]: ...
6
7
 
7
- annotations: Any
8
- spec: Any
8
+ def _parse_cli_variants(values: Iterable[Any] | None) -> list[int | None]: ...
tnfr/config/__init__.py CHANGED
@@ -7,6 +7,7 @@ previous module level functions so downstream importers remain stable.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ from .feature_flags import context_flags, get_flags
10
11
  from .init import apply_config, load_config
11
12
 
12
- __all__ = ("load_config", "apply_config")
13
+ __all__ = ("load_config", "apply_config", "get_flags", "context_flags")
tnfr/config/__init__.pyi CHANGED
@@ -6,3 +6,5 @@ def __getattr__(name: str) -> Any: ...
6
6
 
7
7
  apply_config: Any
8
8
  load_config: Any
9
+ get_flags: Any
10
+ context_flags: Any
@@ -0,0 +1,83 @@
1
+ """Math feature flag configuration helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from contextlib import contextmanager
7
+ from dataclasses import dataclass, replace
8
+ from typing import Iterator
9
+
10
+ __all__ = ("MathFeatureFlags", "get_flags", "context_flags")
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class MathFeatureFlags:
15
+ """Toggle optional mathematical behaviours in the engine."""
16
+
17
+ enable_math_validation: bool = False
18
+ enable_math_dynamics: bool = False
19
+ log_performance: bool = False
20
+ math_backend: str = "numpy"
21
+
22
+
23
+ _TRUE_VALUES = {"1", "true", "on", "yes", "y", "t"}
24
+ _FALSE_VALUES = {"0", "false", "off", "no", "n", "f"}
25
+
26
+ _BASE_FLAGS: MathFeatureFlags | None = None
27
+ _FLAGS_STACK: list[MathFeatureFlags] = []
28
+
29
+
30
+ def _parse_env_flag(name: str, default: bool) -> bool:
31
+ value = os.getenv(name)
32
+ if value is None:
33
+ return default
34
+ lowered = value.strip().lower()
35
+ if lowered in _TRUE_VALUES:
36
+ return True
37
+ if lowered in _FALSE_VALUES:
38
+ return False
39
+ return default
40
+
41
+
42
+ def _load_base_flags() -> MathFeatureFlags:
43
+ global _BASE_FLAGS
44
+ if _BASE_FLAGS is None:
45
+ backend = os.getenv("TNFR_MATH_BACKEND")
46
+ backend_choice = backend.strip() if backend else "numpy"
47
+ _BASE_FLAGS = MathFeatureFlags(
48
+ enable_math_validation=_parse_env_flag(
49
+ "TNFR_ENABLE_MATH_VALIDATION", False
50
+ ),
51
+ enable_math_dynamics=_parse_env_flag(
52
+ "TNFR_ENABLE_MATH_DYNAMICS", False
53
+ ),
54
+ log_performance=_parse_env_flag("TNFR_LOG_PERF", False),
55
+ math_backend=backend_choice or "numpy",
56
+ )
57
+ return _BASE_FLAGS
58
+
59
+
60
+ def get_flags() -> MathFeatureFlags:
61
+ """Return the currently active feature flags."""
62
+
63
+ if _FLAGS_STACK:
64
+ return _FLAGS_STACK[-1]
65
+ return _load_base_flags()
66
+
67
+
68
+ @contextmanager
69
+ def context_flags(**overrides: bool) -> Iterator[MathFeatureFlags]:
70
+ """Temporarily override math feature flags."""
71
+
72
+ invalid = set(overrides) - set(MathFeatureFlags.__annotations__)
73
+ if invalid:
74
+ invalid_names = ", ".join(sorted(invalid))
75
+ raise TypeError(f"Unknown flag overrides: {invalid_names}")
76
+
77
+ previous = get_flags()
78
+ next_flags = replace(previous, **overrides)
79
+ _FLAGS_STACK.append(next_flags)
80
+ try:
81
+ yield next_flags
82
+ finally:
83
+ _FLAGS_STACK.pop()
tnfr/config/init.py CHANGED
@@ -7,7 +7,7 @@ from pathlib import Path
7
7
  from typing import TYPE_CHECKING, Any
8
8
 
9
9
  from ..constants import inject_defaults
10
- from ..io import read_structured_file
10
+ from ..utils import read_structured_file
11
11
 
12
12
  if TYPE_CHECKING: # pragma: no cover - only for type checkers
13
13
  import networkx as nx
@@ -4,7 +4,6 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Any
6
6
 
7
-
8
7
  # Canonical operator identifiers (English tokens)
9
8
  EMISSION = "emission"
10
9
  RECEPTION = "reception"
@@ -50,13 +49,6 @@ VALID_END_OPERATORS = frozenset({SILENCE, TRANSITION, RECURSIVITY})
50
49
  SELF_ORGANIZATION_CLOSURES = frozenset({SILENCE, CONTRACTION})
51
50
 
52
51
 
53
- _LEGACY_COLLECTION_ALIASES: dict[str, str] = {
54
- "INICIO_VALIDOS": "VALID_START_OPERATORS",
55
- "TRAMO_INTERMEDIO": "INTERMEDIATE_OPERATORS",
56
- "CIERRE_VALIDO": "VALID_END_OPERATORS",
57
- "AUTOORGANIZACION_CIERRES": "SELF_ORGANIZATION_CLOSURES",
58
- }
59
-
60
52
  def canonical_operator_name(name: str) -> str:
61
53
  """Return the canonical operator token for ``name``."""
62
54
 
@@ -96,11 +88,6 @@ __all__ = [
96
88
 
97
89
 
98
90
  def __getattr__(name: str) -> Any:
99
- """Provide guidance for legacy operator collection aliases."""
91
+ """Provide a consistent ``AttributeError`` when names are missing."""
100
92
 
101
- canonical = _LEGACY_COLLECTION_ALIASES.get(name)
102
- if canonical is not None:
103
- raise AttributeError(
104
- f"module '{__name__}' has no attribute '{name}'; use '{canonical}' instead."
105
- )
106
93
  raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
tnfr/config/presets.py CHANGED
@@ -65,39 +65,19 @@ PREFERRED_PRESET_NAMES: tuple[str, ...] = tuple(_PRIMARY_PRESETS.keys())
65
65
  _PRESETS: dict[str, PresetTokens] = {**_PRIMARY_PRESETS}
66
66
 
67
67
 
68
- _LEGACY_PRESET_ALIASES: dict[str, str] = {
69
- "arranque_resonante": "resonant_bootstrap",
70
- "mutacion_contenida": "contained_mutation",
71
- "exploracion_acople": "coupling_exploration",
72
- "ejemplo_canonico": CANONICAL_PRESET_NAME,
73
- }
74
-
75
-
76
68
  def legacy_preset_guidance(name: str) -> str | None:
77
- """Return CLI guidance for historical preset aliases.
69
+ """Return CLI guidance for preset lookups.
78
70
 
79
- Parameters
80
- ----------
81
- name:
82
- Identifier received from the CLI.
83
-
84
- Returns
85
- -------
86
- str | None
87
- A human readable guidance string when ``name`` matches a removed
88
- alias. ``None`` when no dedicated guidance is available.
71
+ Legacy aliases were removed; the function now always returns ``None``.
72
+ ``name`` is accepted to preserve the public helper signature.
89
73
  """
90
74
 
91
- canonical = _LEGACY_PRESET_ALIASES.get(name)
92
- if canonical is None:
93
- return None
94
- return (
95
- f"Legacy preset identifier '{name}' was removed in TNFR 9.0. "
96
- f"Use '{canonical}' instead."
97
- )
75
+ return None
98
76
 
99
77
 
100
78
  def get_preset(name: str) -> PresetTokens:
79
+ """Return the preset token sequence identified by ``name``."""
80
+
101
81
  try:
102
82
  return _PRESETS[name]
103
83
  except KeyError:
@@ -7,30 +7,28 @@ from collections.abc import Mapping
7
7
  from types import MappingProxyType
8
8
  from typing import Callable, TypeVar, cast
9
9
 
10
+ from ..immutable import _is_immutable
11
+ from ..types import GraphLike, TNFRConfigValue
10
12
  from .core import CORE_DEFAULTS, REMESH_DEFAULTS
11
13
  from .init import INIT_DEFAULTS
12
14
  from .metric import (
15
+ COHERENCE,
16
+ DIAGNOSIS,
17
+ GRAMMAR_CANON,
13
18
  METRIC_DEFAULTS,
19
+ METRICS,
14
20
  SIGMA,
15
21
  TRACE,
16
- METRICS,
17
- GRAMMAR_CANON,
18
- COHERENCE,
19
- DIAGNOSIS,
20
22
  )
21
23
 
22
- from ..immutable import _is_immutable
23
- from ..types import GraphLike, TNFRConfigValue
24
-
25
24
  T = TypeVar("T")
26
25
 
27
26
  STATE_STABLE = "stable"
28
27
  STATE_TRANSITION = "transition"
29
28
  STATE_DISSONANT = "dissonant"
30
29
 
31
- CANONICAL_STATE_TOKENS = frozenset(
32
- {STATE_STABLE, STATE_TRANSITION, STATE_DISSONANT}
33
- )
30
+ CANONICAL_STATE_TOKENS = frozenset({STATE_STABLE, STATE_TRANSITION, STATE_DISSONANT})
31
+
34
32
 
35
33
  def normalise_state_token(token: str) -> str:
36
34
  """Return the canonical English token for ``token``.
@@ -56,6 +54,7 @@ def normalise_state_token(token: str) -> str:
56
54
  "state token must be one of 'stable', 'transition', or 'dissonant'"
57
55
  )
58
56
 
57
+
59
58
  try: # pragma: no cover - optional dependency
60
59
  from ..utils import ensure_node_offset_map as _ensure_node_offset_map
61
60
  except ImportError: # noqa: BLE001 - allow any import error
@@ -103,9 +102,7 @@ def inject_defaults(
103
102
  for k, v in defaults.items():
104
103
  if override or k not in G.graph:
105
104
  G.graph[k] = (
106
- v
107
- if _is_immutable(v)
108
- else cast(TNFRConfigValue, copy.deepcopy(v))
105
+ v if _is_immutable(v) else cast(TNFRConfigValue, copy.deepcopy(v))
109
106
  )
110
107
  G.graph["_tnfr_defaults_attached"] = True
111
108
  if ensure_node_offset_map is not None:
@@ -3,18 +3,17 @@ from __future__ import annotations
3
3
  from collections.abc import Mapping
4
4
  from typing import Callable, TypeVar
5
5
 
6
- from .core import CORE_DEFAULTS as CORE_DEFAULTS, REMESH_DEFAULTS as REMESH_DEFAULTS
7
- from .init import INIT_DEFAULTS as INIT_DEFAULTS
8
- from .metric import (
9
- COHERENCE as COHERENCE,
10
- DIAGNOSIS as DIAGNOSIS,
11
- GRAMMAR_CANON as GRAMMAR_CANON,
12
- METRICS as METRICS,
13
- METRIC_DEFAULTS as METRIC_DEFAULTS,
14
- SIGMA as SIGMA,
15
- TRACE as TRACE,
16
- )
17
6
  from ..types import GraphLike, TNFRConfigValue
7
+ from .core import CORE_DEFAULTS as CORE_DEFAULTS
8
+ from .core import REMESH_DEFAULTS as REMESH_DEFAULTS
9
+ from .init import INIT_DEFAULTS as INIT_DEFAULTS
10
+ from .metric import COHERENCE as COHERENCE
11
+ from .metric import DIAGNOSIS as DIAGNOSIS
12
+ from .metric import GRAMMAR_CANON as GRAMMAR_CANON
13
+ from .metric import METRIC_DEFAULTS as METRIC_DEFAULTS
14
+ from .metric import METRICS as METRICS
15
+ from .metric import SIGMA as SIGMA
16
+ from .metric import TRACE as TRACE
18
17
 
19
18
  T = TypeVar("T")
20
19
 
@@ -79,26 +78,15 @@ STATE_TRANSITION: str
79
78
  STATE_DISSONANT: str
80
79
  CANONICAL_STATE_TOKENS: frozenset[str]
81
80
 
82
-
83
81
  def inject_defaults(
84
82
  G: GraphLike,
85
83
  defaults: Mapping[str, TNFRConfigValue] = ...,
86
84
  override: bool = ...,
87
85
  ) -> None: ...
88
-
89
-
90
86
  def merge_overrides(G: GraphLike, **overrides: TNFRConfigValue) -> None: ...
91
-
92
-
93
87
  def get_param(G: GraphLike, key: str) -> TNFRConfigValue: ...
94
-
95
-
96
88
  def get_graph_param(
97
89
  G: GraphLike, key: str, cast: Callable[[object], T] = ...
98
90
  ) -> T | None: ...
99
-
100
-
101
91
  def get_aliases(key: str) -> tuple[str, ...]: ...
102
-
103
-
104
92
  def normalise_state_token(token: str) -> str: ...
@@ -0,0 +1,31 @@
1
+ """Shared alias constants for TNFR attributes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from . import get_aliases
6
+
7
+ ALIAS_VF = get_aliases("VF")
8
+ ALIAS_THETA = get_aliases("THETA")
9
+ ALIAS_DNFR = get_aliases("DNFR")
10
+ ALIAS_EPI = get_aliases("EPI")
11
+ ALIAS_EPI_KIND = get_aliases("EPI_KIND")
12
+ ALIAS_SI = get_aliases("SI")
13
+ ALIAS_DEPI = get_aliases("DEPI")
14
+ ALIAS_D2EPI = get_aliases("D2EPI")
15
+ ALIAS_DVF = get_aliases("DVF")
16
+ ALIAS_D2VF = get_aliases("D2VF")
17
+ ALIAS_DSI = get_aliases("DSI")
18
+
19
+ __all__ = [
20
+ "ALIAS_VF",
21
+ "ALIAS_THETA",
22
+ "ALIAS_DNFR",
23
+ "ALIAS_EPI",
24
+ "ALIAS_EPI_KIND",
25
+ "ALIAS_SI",
26
+ "ALIAS_DEPI",
27
+ "ALIAS_D2EPI",
28
+ "ALIAS_DVF",
29
+ "ALIAS_D2VF",
30
+ "ALIAS_DSI",
31
+ ]
tnfr/constants/core.py CHANGED
@@ -2,10 +2,9 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from dataclasses import dataclass, asdict, field
6
- from typing import Any, Mapping
5
+ from dataclasses import asdict, dataclass, field
7
6
  from types import MappingProxyType
8
-
7
+ from typing import Any, Mapping
9
8
 
10
9
  SELECTOR_THRESHOLD_DEFAULTS: Mapping[str, float] = MappingProxyType(
11
10
  {
@@ -72,6 +71,7 @@ class CoreDefaults:
72
71
  GLYPH_SELECTOR_MARGIN: float = 0.05
73
72
  VF_ADAPT_TAU: int = 5
74
73
  VF_ADAPT_MU: float = 0.1
74
+ HZ_STR_BRIDGE: float = 1.0
75
75
  GLYPH_FACTORS: dict[str, float] = field(
76
76
  default_factory=lambda: {
77
77
  "AL_boost": 0.05,
@@ -151,6 +151,7 @@ class RemeshDefaults:
151
151
  REMESH_ALPHA: float = 0.5
152
152
  REMESH_ALPHA_HARD: bool = False
153
153
 
154
+
154
155
  _core_defaults = asdict(CoreDefaults())
155
156
  _remesh_defaults = asdict(RemeshDefaults())
156
157
 
tnfr/constants/init.py CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from dataclasses import dataclass, asdict
6
5
  import math
6
+ from dataclasses import asdict, dataclass
7
7
 
8
8
 
9
9
  @dataclass(frozen=True, slots=True)
tnfr/constants/metric.py CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from dataclasses import dataclass, asdict, field
6
- from typing import Any
5
+ from dataclasses import asdict, dataclass, field
7
6
  from types import MappingProxyType
7
+ from typing import Any
8
8
 
9
9
 
10
10
  @dataclass(frozen=True, slots=True)
@@ -27,7 +27,7 @@ class MetricDefaults:
27
27
  default_factory=lambda: {
28
28
  "enabled": True,
29
29
  "weight": "Si", # "Si" | "EPI" | "1"
30
- "smooth": 0.0, # EMA sobre el vector global (0=off)
30
+ "smooth": 0.0, # EMA over the global vector (0=off)
31
31
  "history_key": "sigma_global",
32
32
  "per_node": False,
33
33
  }
tnfr/dynamics/__init__.py CHANGED
@@ -1,9 +1,71 @@
1
- """Facade for TNFR dynamics submodules."""
1
+ """Facade that keeps ΔNFR, νf and phase orchestration coherent across TNFR dynamics.
2
+
3
+ Attributes
4
+ ----------
5
+ run : callable
6
+ Callable that fully manages the evolution loop, integrating the nodal
7
+ equation while enforcing ΔNFR hooks, νf adaptation and phase coordination
8
+ on every step.
9
+ step : callable
10
+ Callable entry point for a single iteration that reuses the
11
+ ΔNFR/νf/phase pipeline while letting callers interleave bespoke telemetry
12
+ or operator injections.
13
+ set_delta_nfr_hook : callable
14
+ Callable used to install custom ΔNFR supervision under
15
+ ``G.graph['compute_delta_nfr']`` so each operator reorganization stays
16
+ coupled to νf drift and phase targets.
17
+ default_glyph_selector, parametric_glyph_selector : AbstractSelector
18
+ Selector implementations that choose glyphs according to ΔNFR trends,
19
+ νf ranges and phase synchrony, ensuring operator firing reinforces
20
+ coherence.
21
+ coordination, dnfr, integrators : module
22
+ Re-exported modules providing explicit control over phase alignment,
23
+ ΔNFR caches and integrator lifecycles to centralize orchestration.
24
+ ProcessPoolExecutor, apply_glyph, compute_Si : callable
25
+ Re-exported utilities for parallel selector evaluation, explicit glyph
26
+ execution and Si telemetry so ΔNFR, νf and phase traces remain observable.
27
+
28
+ Notes
29
+ -----
30
+ The facade aggregates runtime helpers that preserve canonical TNFR dynamics:
31
+ ``dnfr`` manages ΔNFR preparation and caching, ``integrators`` drives the
32
+ numerical updates of νf and EPI, and ``coordination`` synchronizes global and
33
+ local phase. Complementary exports such as
34
+ :func:`~tnfr.dynamics.adaptation.adapt_vf_by_coherence` and
35
+ :func:`~tnfr.dynamics.coordination.coordinate_global_local_phase` allow custom
36
+ feedback loops without breaking operator closure.
37
+
38
+ Examples
39
+ --------
40
+ >>> from tnfr.constants import DNFR_PRIMARY, EPI_PRIMARY, VF_PRIMARY
41
+ >>> from tnfr.structural import Coherence, Emission, Resonance, create_nfr, run_sequence
42
+ >>> from tnfr.dynamics import parametric_glyph_selector, run, set_delta_nfr_hook, step
43
+ >>> G, node = create_nfr("seed", epi=0.22, vf=1.0)
44
+ >>> def regulate_delta(graph, *, n_jobs=None):
45
+ ... for _, nd in graph.nodes(data=True):
46
+ ... delta = nd[VF_PRIMARY] * 0.08
47
+ ... nd[DNFR_PRIMARY] = delta
48
+ ... nd[EPI_PRIMARY] += delta
49
+ ... nd[VF_PRIMARY] += delta * 0.05
50
+ ... return None
51
+ >>> set_delta_nfr_hook(G, regulate_delta, note="ΔNFR guided by νf")
52
+ >>> G.graph["glyph_selector"] = parametric_glyph_selector
53
+ >>> run_sequence(G, node, [Emission(), Resonance(), Coherence()])
54
+ >>> run(G, steps=2, dt=0.05)
55
+ >>> # Automatic integration keeps ΔNFR, νf and phase co-modulated.
56
+ >>> step(G, dt=0.05)
57
+ >>> # Manual control reuses the selector state to consolidate coherence traces.
58
+ """
2
59
 
3
60
  from __future__ import annotations
4
61
 
5
62
  from concurrent.futures import ProcessPoolExecutor
6
63
 
64
+ from ..metrics.sense_index import compute_Si
65
+ from ..operators import apply_glyph, enforce_canonical_grammar, on_applied_glyph
66
+ from ..types import GlyphCode
67
+ from ..utils import get_numpy
68
+ from ..validation import apply_canonical_clamps, validate_canon
7
69
  from . import coordination, dnfr, integrators
8
70
  from .adaptation import adapt_vf_by_coherence
9
71
  from .aliases import (
@@ -43,18 +105,14 @@ from .runtime import (
43
105
  _run_validators,
44
106
  _update_epi_hist,
45
107
  _update_nodes,
46
- apply_canonical_clamps,
47
108
  run,
48
109
  step,
49
- validate_canon,
50
110
  )
51
111
  from .sampling import update_node_sample as _update_node_sample
52
112
  from .selectors import (
53
113
  AbstractSelector,
54
114
  DefaultGlyphSelector,
55
- GlyphCode,
56
115
  ParametricGlyphSelector,
57
- _SelectorPreselection,
58
116
  _apply_glyphs,
59
117
  _apply_selector,
60
118
  _choose_glyph,
@@ -63,13 +121,10 @@ from .selectors import (
63
121
  _prepare_selector_preselection,
64
122
  _resolve_preselected_glyph,
65
123
  _selector_parallel_jobs,
124
+ _SelectorPreselection,
66
125
  default_glyph_selector,
67
126
  parametric_glyph_selector,
68
127
  )
69
- from ..operators import apply_glyph
70
- from ..metrics.sense_index import compute_Si
71
- from ..utils import get_numpy
72
- from ..validation.grammar import enforce_canonical_grammar, on_applied_glyph
73
128
 
74
129
  __all__ = (
75
130
  "coordination",
@@ -133,4 +188,3 @@ __all__ = (
133
188
  "update_epi_via_nodal_equation",
134
189
  "validate_canon",
135
190
  )
136
-
@@ -1,6 +1,7 @@
1
1
  from typing import Any, Literal, Sequence
2
2
 
3
- from tnfr.types import TNFRGraph
3
+ from tnfr.types import GlyphCode, TNFRGraph
4
+ from tnfr.validation import ValidationOutcome
4
5
 
5
6
  __all__: tuple[str, ...]
6
7
 
@@ -16,7 +17,6 @@ ALIAS_VF: Sequence[str]
16
17
 
17
18
  AbstractSelector: Any
18
19
  DefaultGlyphSelector: Any
19
- GlyphCode: Any
20
20
  ParametricGlyphSelector: Any
21
21
  _SelectorPreselection: Any
22
22
  _apply_glyphs: Any
@@ -79,5 +79,4 @@ def update_epi_via_nodal_equation(
79
79
  n_jobs: int | None = ...,
80
80
  ) -> None: ...
81
81
 
82
- validate_canon: Any
83
-
82
+ def validate_canon(G: TNFRGraph) -> ValidationOutcome[TNFRGraph]: ...