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.

Files changed (195) hide show
  1. tnfr/__init__.py +275 -51
  2. tnfr/__init__.pyi +33 -0
  3. tnfr/_compat.py +10 -0
  4. tnfr/_generated_version.py +34 -0
  5. tnfr/_version.py +49 -0
  6. tnfr/_version.pyi +7 -0
  7. tnfr/alias.py +117 -31
  8. tnfr/alias.pyi +108 -0
  9. tnfr/cache.py +6 -572
  10. tnfr/cache.pyi +16 -0
  11. tnfr/callback_utils.py +16 -38
  12. tnfr/callback_utils.pyi +79 -0
  13. tnfr/cli/__init__.py +34 -14
  14. tnfr/cli/__init__.pyi +26 -0
  15. tnfr/cli/arguments.py +211 -28
  16. tnfr/cli/arguments.pyi +27 -0
  17. tnfr/cli/execution.py +470 -50
  18. tnfr/cli/execution.pyi +70 -0
  19. tnfr/cli/utils.py +18 -3
  20. tnfr/cli/utils.pyi +8 -0
  21. tnfr/config/__init__.py +13 -0
  22. tnfr/config/__init__.pyi +10 -0
  23. tnfr/{constants_glyphs.py → config/constants.py} +26 -20
  24. tnfr/config/constants.pyi +12 -0
  25. tnfr/config/feature_flags.py +83 -0
  26. tnfr/{config.py → config/init.py} +11 -7
  27. tnfr/config/init.pyi +8 -0
  28. tnfr/config/operator_names.py +93 -0
  29. tnfr/config/operator_names.pyi +28 -0
  30. tnfr/config/presets.py +84 -0
  31. tnfr/config/presets.pyi +7 -0
  32. tnfr/constants/__init__.py +80 -29
  33. tnfr/constants/__init__.pyi +92 -0
  34. tnfr/constants/aliases.py +31 -0
  35. tnfr/constants/core.py +4 -4
  36. tnfr/constants/core.pyi +17 -0
  37. tnfr/constants/init.py +1 -1
  38. tnfr/constants/init.pyi +12 -0
  39. tnfr/constants/metric.py +7 -15
  40. tnfr/constants/metric.pyi +19 -0
  41. tnfr/dynamics/__init__.py +165 -633
  42. tnfr/dynamics/__init__.pyi +82 -0
  43. tnfr/dynamics/adaptation.py +267 -0
  44. tnfr/dynamics/aliases.py +23 -0
  45. tnfr/dynamics/coordination.py +385 -0
  46. tnfr/dynamics/dnfr.py +2283 -400
  47. tnfr/dynamics/dnfr.pyi +24 -0
  48. tnfr/dynamics/integrators.py +406 -98
  49. tnfr/dynamics/integrators.pyi +34 -0
  50. tnfr/dynamics/runtime.py +881 -0
  51. tnfr/dynamics/sampling.py +10 -5
  52. tnfr/dynamics/sampling.pyi +7 -0
  53. tnfr/dynamics/selectors.py +719 -0
  54. tnfr/execution.py +70 -48
  55. tnfr/execution.pyi +45 -0
  56. tnfr/flatten.py +13 -9
  57. tnfr/flatten.pyi +21 -0
  58. tnfr/gamma.py +66 -53
  59. tnfr/gamma.pyi +34 -0
  60. tnfr/glyph_history.py +110 -52
  61. tnfr/glyph_history.pyi +35 -0
  62. tnfr/glyph_runtime.py +16 -0
  63. tnfr/glyph_runtime.pyi +9 -0
  64. tnfr/immutable.py +69 -28
  65. tnfr/immutable.pyi +34 -0
  66. tnfr/initialization.py +16 -16
  67. tnfr/initialization.pyi +65 -0
  68. tnfr/io.py +6 -240
  69. tnfr/io.pyi +16 -0
  70. tnfr/locking.pyi +7 -0
  71. tnfr/mathematics/__init__.py +81 -0
  72. tnfr/mathematics/backend.py +426 -0
  73. tnfr/mathematics/dynamics.py +398 -0
  74. tnfr/mathematics/epi.py +254 -0
  75. tnfr/mathematics/generators.py +222 -0
  76. tnfr/mathematics/metrics.py +119 -0
  77. tnfr/mathematics/operators.py +233 -0
  78. tnfr/mathematics/operators_factory.py +71 -0
  79. tnfr/mathematics/projection.py +78 -0
  80. tnfr/mathematics/runtime.py +173 -0
  81. tnfr/mathematics/spaces.py +247 -0
  82. tnfr/mathematics/transforms.py +292 -0
  83. tnfr/metrics/__init__.py +10 -10
  84. tnfr/metrics/__init__.pyi +20 -0
  85. tnfr/metrics/coherence.py +993 -324
  86. tnfr/metrics/common.py +23 -16
  87. tnfr/metrics/common.pyi +46 -0
  88. tnfr/metrics/core.py +251 -35
  89. tnfr/metrics/core.pyi +13 -0
  90. tnfr/metrics/diagnosis.py +708 -111
  91. tnfr/metrics/diagnosis.pyi +85 -0
  92. tnfr/metrics/export.py +27 -15
  93. tnfr/metrics/glyph_timing.py +232 -42
  94. tnfr/metrics/reporting.py +33 -22
  95. tnfr/metrics/reporting.pyi +12 -0
  96. tnfr/metrics/sense_index.py +987 -43
  97. tnfr/metrics/sense_index.pyi +9 -0
  98. tnfr/metrics/trig.py +214 -23
  99. tnfr/metrics/trig.pyi +13 -0
  100. tnfr/metrics/trig_cache.py +115 -22
  101. tnfr/metrics/trig_cache.pyi +10 -0
  102. tnfr/node.py +542 -136
  103. tnfr/node.pyi +178 -0
  104. tnfr/observers.py +152 -35
  105. tnfr/observers.pyi +31 -0
  106. tnfr/ontosim.py +23 -19
  107. tnfr/ontosim.pyi +28 -0
  108. tnfr/operators/__init__.py +601 -82
  109. tnfr/operators/__init__.pyi +45 -0
  110. tnfr/operators/definitions.py +513 -0
  111. tnfr/operators/definitions.pyi +78 -0
  112. tnfr/operators/grammar.py +760 -0
  113. tnfr/operators/jitter.py +107 -38
  114. tnfr/operators/jitter.pyi +11 -0
  115. tnfr/operators/registry.py +75 -0
  116. tnfr/operators/registry.pyi +13 -0
  117. tnfr/operators/remesh.py +149 -88
  118. tnfr/py.typed +0 -0
  119. tnfr/rng.py +46 -143
  120. tnfr/rng.pyi +14 -0
  121. tnfr/schemas/__init__.py +8 -0
  122. tnfr/schemas/grammar.json +94 -0
  123. tnfr/selector.py +25 -19
  124. tnfr/selector.pyi +19 -0
  125. tnfr/sense.py +72 -62
  126. tnfr/sense.pyi +23 -0
  127. tnfr/structural.py +522 -262
  128. tnfr/structural.pyi +69 -0
  129. tnfr/telemetry/__init__.py +35 -0
  130. tnfr/telemetry/cache_metrics.py +226 -0
  131. tnfr/telemetry/nu_f.py +423 -0
  132. tnfr/telemetry/nu_f.pyi +123 -0
  133. tnfr/telemetry/verbosity.py +37 -0
  134. tnfr/tokens.py +1 -3
  135. tnfr/tokens.pyi +36 -0
  136. tnfr/trace.py +270 -113
  137. tnfr/trace.pyi +40 -0
  138. tnfr/types.py +574 -6
  139. tnfr/types.pyi +331 -0
  140. tnfr/units.py +69 -0
  141. tnfr/units.pyi +16 -0
  142. tnfr/utils/__init__.py +217 -0
  143. tnfr/utils/__init__.pyi +202 -0
  144. tnfr/utils/cache.py +2395 -0
  145. tnfr/utils/cache.pyi +468 -0
  146. tnfr/utils/chunks.py +104 -0
  147. tnfr/utils/chunks.pyi +21 -0
  148. tnfr/{collections_utils.py → utils/data.py} +147 -90
  149. tnfr/utils/data.pyi +64 -0
  150. tnfr/utils/graph.py +85 -0
  151. tnfr/utils/graph.pyi +10 -0
  152. tnfr/utils/init.py +770 -0
  153. tnfr/utils/init.pyi +78 -0
  154. tnfr/utils/io.py +456 -0
  155. tnfr/{helpers → utils}/numeric.py +51 -24
  156. tnfr/utils/numeric.pyi +21 -0
  157. tnfr/validation/__init__.py +113 -0
  158. tnfr/validation/__init__.pyi +77 -0
  159. tnfr/validation/compatibility.py +95 -0
  160. tnfr/validation/compatibility.pyi +6 -0
  161. tnfr/validation/grammar.py +71 -0
  162. tnfr/validation/grammar.pyi +40 -0
  163. tnfr/validation/graph.py +138 -0
  164. tnfr/validation/graph.pyi +17 -0
  165. tnfr/validation/rules.py +281 -0
  166. tnfr/validation/rules.pyi +55 -0
  167. tnfr/validation/runtime.py +263 -0
  168. tnfr/validation/runtime.pyi +31 -0
  169. tnfr/validation/soft_filters.py +170 -0
  170. tnfr/validation/soft_filters.pyi +37 -0
  171. tnfr/validation/spectral.py +159 -0
  172. tnfr/validation/spectral.pyi +46 -0
  173. tnfr/validation/syntax.py +40 -0
  174. tnfr/validation/syntax.pyi +10 -0
  175. tnfr/validation/window.py +39 -0
  176. tnfr/validation/window.pyi +1 -0
  177. tnfr/viz/__init__.py +9 -0
  178. tnfr/viz/matplotlib.py +246 -0
  179. tnfr-7.0.0.dist-info/METADATA +179 -0
  180. tnfr-7.0.0.dist-info/RECORD +185 -0
  181. {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/licenses/LICENSE.md +1 -1
  182. tnfr/grammar.py +0 -344
  183. tnfr/graph_utils.py +0 -84
  184. tnfr/helpers/__init__.py +0 -71
  185. tnfr/import_utils.py +0 -228
  186. tnfr/json_utils.py +0 -162
  187. tnfr/logging_utils.py +0 -116
  188. tnfr/presets.py +0 -60
  189. tnfr/validators.py +0 -84
  190. tnfr/value_utils.py +0 -59
  191. tnfr-4.5.2.dist-info/METADATA +0 -379
  192. tnfr-4.5.2.dist-info/RECORD +0 -67
  193. {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/WHEEL +0 -0
  194. {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/entry_points.txt +0 -0
  195. {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,113 @@
1
+ """Unified validation interface consolidating grammar, graph and spectral checks.
2
+
3
+ This package re-exports the canonical grammar helpers implemented in
4
+ ``tnfr.operators.grammar`` so downstream code can rely on a single import path for
5
+ structural validation primitives.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass
11
+ from typing import Any, Generic, Mapping, Protocol, TypeVar, runtime_checkable
12
+
13
+ SubjectT = TypeVar("SubjectT")
14
+
15
+
16
+ @dataclass(slots=True)
17
+ class ValidationOutcome(Generic[SubjectT]):
18
+ """Result emitted by all canonical TNFR validators."""
19
+
20
+ subject: SubjectT
21
+ """The validated subject in canonical form."""
22
+
23
+ passed: bool
24
+ """Whether the validation succeeded without invariant violations."""
25
+
26
+ summary: Mapping[str, Any]
27
+ """Structured diagnostics describing the performed checks."""
28
+
29
+ artifacts: Mapping[str, Any] | None = None
30
+ """Optional artefacts (e.g. clamped nodes, normalised vectors)."""
31
+
32
+
33
+ @runtime_checkable
34
+ class Validator(Protocol[SubjectT]):
35
+ """Contract implemented by runtime and spectral validators."""
36
+
37
+ def validate(self, subject: SubjectT, /, **kwargs: Any) -> ValidationOutcome[SubjectT]:
38
+ """Validate ``subject`` returning a :class:`ValidationOutcome`."""
39
+
40
+ def report(self, outcome: "ValidationOutcome[SubjectT]") -> str:
41
+ """Produce a concise textual explanation for ``outcome``."""
42
+
43
+
44
+ from .compatibility import CANON_COMPAT, CANON_FALLBACK
45
+ from ..operators.grammar import (
46
+ GrammarContext,
47
+ MutationPreconditionError,
48
+ RepeatWindowError,
49
+ SequenceValidationResult,
50
+ SequenceSyntaxError,
51
+ StructuralGrammarError,
52
+ TholClosureError,
53
+ TransitionCompatibilityError,
54
+ apply_glyph_with_grammar,
55
+ enforce_canonical_grammar,
56
+ on_applied_glyph,
57
+ record_grammar_violation,
58
+ )
59
+ from .graph import GRAPH_VALIDATORS, run_validators
60
+ from .window import validate_window
61
+ from .runtime import GraphCanonicalValidator, apply_canonical_clamps, validate_canon
62
+ from .rules import coerce_glyph, get_norm, glyph_fallback, normalized_dnfr
63
+ from .soft_filters import (
64
+ acceleration_norm,
65
+ check_repeats,
66
+ maybe_force,
67
+ soft_grammar_filters,
68
+ )
69
+ from ..operators.grammar import validate_sequence
70
+
71
+ __all__ = (
72
+ "ValidationOutcome",
73
+ "Validator",
74
+ "validate_sequence",
75
+ "GrammarContext",
76
+ "StructuralGrammarError",
77
+ "RepeatWindowError",
78
+ "MutationPreconditionError",
79
+ "TholClosureError",
80
+ "TransitionCompatibilityError",
81
+ "SequenceValidationResult",
82
+ "SequenceSyntaxError",
83
+ "apply_glyph_with_grammar",
84
+ "enforce_canonical_grammar",
85
+ "on_applied_glyph",
86
+ "record_grammar_violation",
87
+ "validate_window",
88
+ "run_validators",
89
+ "GRAPH_VALIDATORS",
90
+ "coerce_glyph",
91
+ "glyph_fallback",
92
+ "normalized_dnfr",
93
+ "get_norm",
94
+ "acceleration_norm",
95
+ "check_repeats",
96
+ "maybe_force",
97
+ "soft_grammar_filters",
98
+ "CANON_COMPAT",
99
+ "CANON_FALLBACK",
100
+ "GraphCanonicalValidator",
101
+ "apply_canonical_clamps",
102
+ "validate_canon",
103
+ "NFRValidator",
104
+ )
105
+
106
+
107
+ def __getattr__(name: str) -> Any:
108
+ if name == "NFRValidator":
109
+ from .spectral import NFRValidator as _NFRValidator
110
+
111
+ return _NFRValidator
112
+ raise AttributeError(name)
113
+
@@ -0,0 +1,77 @@
1
+ from collections.abc import Mapping
2
+ from typing import Any, Generic, Protocol, TypeVar
3
+
4
+ from ..types import Glyph, TNFRGraph
5
+ from .compatibility import CANON_COMPAT as CANON_COMPAT, CANON_FALLBACK as CANON_FALLBACK
6
+ from ..operators.grammar import (
7
+ GrammarContext,
8
+ MutationPreconditionError,
9
+ RepeatWindowError,
10
+ SequenceSyntaxError,
11
+ SequenceValidationResult,
12
+ StructuralGrammarError,
13
+ TholClosureError,
14
+ TransitionCompatibilityError,
15
+ apply_glyph_with_grammar,
16
+ enforce_canonical_grammar,
17
+ on_applied_glyph,
18
+ record_grammar_violation,
19
+ )
20
+ from .graph import GRAPH_VALIDATORS, run_validators
21
+ from .window import validate_window
22
+ from .rules import coerce_glyph, get_norm, glyph_fallback, normalized_dnfr
23
+ from .soft_filters import (acceleration_norm, check_repeats, maybe_force, soft_grammar_filters)
24
+ from .runtime import GraphCanonicalValidator, apply_canonical_clamps, validate_canon
25
+ from .spectral import NFRValidator
26
+ from ..operators.grammar import validate_sequence
27
+
28
+ SubjectT = TypeVar("SubjectT")
29
+
30
+
31
+ class ValidationOutcome(Generic[SubjectT]):
32
+ subject: SubjectT
33
+ passed: bool
34
+ summary: Mapping[str, Any]
35
+ artifacts: Mapping[str, Any] | None
36
+
37
+
38
+ class Validator(Protocol[SubjectT]):
39
+ def validate(self, subject: SubjectT, /, **kwargs: Any) -> ValidationOutcome[SubjectT]: ...
40
+
41
+ def report(self, outcome: ValidationOutcome[SubjectT]) -> str: ...
42
+
43
+
44
+ __all__ = (
45
+ "ValidationOutcome",
46
+ "Validator",
47
+ "validate_sequence",
48
+ "GrammarContext",
49
+ "StructuralGrammarError",
50
+ "RepeatWindowError",
51
+ "MutationPreconditionError",
52
+ "TholClosureError",
53
+ "TransitionCompatibilityError",
54
+ "SequenceSyntaxError",
55
+ "SequenceValidationResult",
56
+ "apply_glyph_with_grammar",
57
+ "enforce_canonical_grammar",
58
+ "on_applied_glyph",
59
+ "record_grammar_violation",
60
+ "validate_window",
61
+ "run_validators",
62
+ "GRAPH_VALIDATORS",
63
+ "coerce_glyph",
64
+ "glyph_fallback",
65
+ "normalized_dnfr",
66
+ "get_norm",
67
+ "acceleration_norm",
68
+ "check_repeats",
69
+ "maybe_force",
70
+ "soft_grammar_filters",
71
+ "CANON_COMPAT",
72
+ "CANON_FALLBACK",
73
+ "GraphCanonicalValidator",
74
+ "apply_canonical_clamps",
75
+ "validate_canon",
76
+ "NFRValidator",
77
+ )
@@ -0,0 +1,95 @@
1
+ """Canonical TNFR compatibility tables expressed via structural operators."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..config.operator_names import (
6
+ COHERENCE,
7
+ CONTRACTION,
8
+ COUPLING,
9
+ DISSONANCE,
10
+ EMISSION,
11
+ EXPANSION,
12
+ MUTATION,
13
+ RECEPTION,
14
+ RESONANCE,
15
+ SELF_ORGANIZATION,
16
+ SILENCE,
17
+ TRANSITION,
18
+ )
19
+ from ..operators import grammar as _grammar
20
+ from ..types import Glyph
21
+
22
+ __all__ = ["CANON_COMPAT", "CANON_FALLBACK"]
23
+
24
+ # Canonical compatibilities (allowed next operators) expressed via structural names
25
+ _STRUCTURAL_COMPAT: dict[str, set[str]] = {
26
+ # Opening / initiation
27
+ EMISSION: {RECEPTION, RESONANCE, TRANSITION, EXPANSION, COUPLING},
28
+ RECEPTION: {COHERENCE, COUPLING, RESONANCE, TRANSITION},
29
+ # Stabilisation / diffusion / coupling
30
+ COHERENCE: {RESONANCE, EXPANSION, COUPLING, SILENCE},
31
+ COUPLING: {RESONANCE, COHERENCE, EXPANSION, TRANSITION},
32
+ RESONANCE: {COHERENCE, EXPANSION, COUPLING, TRANSITION},
33
+ EXPANSION: {COUPLING, RESONANCE, COHERENCE, TRANSITION},
34
+ # Dissonance → transition → mutation
35
+ DISSONANCE: {MUTATION, TRANSITION},
36
+ MUTATION: {COHERENCE, TRANSITION},
37
+ TRANSITION: {DISSONANCE, MUTATION, RESONANCE, COHERENCE, COUPLING},
38
+ # Closures / latent states
39
+ SILENCE: {EMISSION, RECEPTION},
40
+ CONTRACTION: {EMISSION, COHERENCE},
41
+ # Self-organising blocks
42
+ SELF_ORGANIZATION: {
43
+ DISSONANCE,
44
+ MUTATION,
45
+ TRANSITION,
46
+ RESONANCE,
47
+ COHERENCE,
48
+ COUPLING,
49
+ SILENCE,
50
+ CONTRACTION,
51
+ },
52
+ }
53
+
54
+
55
+ def _name_to_glyph(name: str) -> Glyph:
56
+ glyph = _grammar.function_name_to_glyph(name)
57
+ if glyph is None:
58
+ raise KeyError(f"No glyph mapped to structural operator '{name}'")
59
+ return glyph
60
+
61
+
62
+ def _translate_structural() -> tuple[dict[Glyph, set[Glyph]], dict[Glyph, Glyph]]:
63
+ compat: dict[Glyph, set[Glyph]] = {}
64
+ for src, targets in _STRUCTURAL_COMPAT.items():
65
+ src_glyph = _name_to_glyph(src)
66
+ compat[src_glyph] = {_name_to_glyph(target) for target in targets}
67
+ fallback: dict[Glyph, Glyph] = {}
68
+ for src, target in _STRUCTURAL_FALLBACK.items():
69
+ fallback[_name_to_glyph(src)] = _name_to_glyph(target)
70
+ return compat, fallback
71
+
72
+
73
+ # Canonical fallbacks when a transition is not allowed (structural names)
74
+ _STRUCTURAL_FALLBACK: dict[str, str] = {
75
+ EMISSION: RECEPTION,
76
+ RECEPTION: COHERENCE,
77
+ COHERENCE: RESONANCE,
78
+ TRANSITION: RESONANCE,
79
+ CONTRACTION: EMISSION,
80
+ DISSONANCE: MUTATION,
81
+ RESONANCE: COHERENCE,
82
+ SILENCE: EMISSION,
83
+ SELF_ORGANIZATION: TRANSITION,
84
+ COUPLING: RESONANCE,
85
+ EXPANSION: RESONANCE,
86
+ MUTATION: COHERENCE,
87
+ }
88
+
89
+
90
+ CANON_COMPAT, CANON_FALLBACK = _translate_structural()
91
+
92
+ # Re-export structural tables for internal consumers that operate on functional
93
+ # identifiers without exposing them as part of the public API.
94
+ _STRUCTURAL_COMPAT_TABLE = _STRUCTURAL_COMPAT
95
+ _STRUCTURAL_FALLBACK_TABLE = _STRUCTURAL_FALLBACK
@@ -0,0 +1,6 @@
1
+ from ..types import Glyph
2
+
3
+ __all__ = ("CANON_COMPAT", "CANON_FALLBACK")
4
+
5
+ CANON_COMPAT: dict[Glyph, set[Glyph]]
6
+ CANON_FALLBACK: dict[Glyph, Glyph]
@@ -0,0 +1,71 @@
1
+ """Deprecated compatibility wrapper for :mod:`tnfr.operators.grammar`.
2
+
3
+ Import :mod:`tnfr.operators.grammar` directly to access the canonical grammar
4
+ interfaces. This module remains as a thin shim for historical callers and will
5
+ be removed once downstream packages adopt the canonical entry point.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import warnings
11
+ from typing import TYPE_CHECKING
12
+
13
+ from ..operators.grammar import (
14
+ GrammarContext,
15
+ MutationPreconditionError,
16
+ RepeatWindowError,
17
+ record_grammar_violation as _canonical_record_violation,
18
+ SequenceSyntaxError,
19
+ SequenceValidationResult,
20
+ StructuralGrammarError,
21
+ TholClosureError,
22
+ TransitionCompatibilityError,
23
+ _gram_state,
24
+ apply_glyph_with_grammar,
25
+ enforce_canonical_grammar,
26
+ on_applied_glyph,
27
+ parse_sequence,
28
+ validate_sequence,
29
+ )
30
+
31
+ if TYPE_CHECKING: # pragma: no cover - typing only
32
+ from ..types import NodeId, TNFRGraph
33
+
34
+ __all__ = [
35
+ "GrammarContext",
36
+ "StructuralGrammarError",
37
+ "RepeatWindowError",
38
+ "MutationPreconditionError",
39
+ "TholClosureError",
40
+ "TransitionCompatibilityError",
41
+ "SequenceSyntaxError",
42
+ "SequenceValidationResult",
43
+ "record_grammar_violation",
44
+ "_gram_state",
45
+ "apply_glyph_with_grammar",
46
+ "enforce_canonical_grammar",
47
+ "on_applied_glyph",
48
+ "parse_sequence",
49
+ "validate_sequence",
50
+ ]
51
+
52
+ warnings.warn(
53
+ "'tnfr.validation.grammar' is deprecated; import from 'tnfr.operators.grammar' "
54
+ "for the canonical grammar interface.",
55
+ DeprecationWarning,
56
+ stacklevel=2,
57
+ )
58
+
59
+
60
+ def record_grammar_violation(
61
+ G: "TNFRGraph", node: "NodeId", error: StructuralGrammarError, *, stage: str
62
+ ) -> None:
63
+ """Bridge to :func:`tnfr.operators.grammar.record_grammar_violation`."""
64
+
65
+ warnings.warn(
66
+ "'tnfr.validation.grammar.record_grammar_violation' is deprecated; "
67
+ "use 'tnfr.operators.grammar.record_grammar_violation' instead.",
68
+ DeprecationWarning,
69
+ stacklevel=2,
70
+ )
71
+ _canonical_record_violation(G, node, error, stage=stage)
@@ -0,0 +1,40 @@
1
+ from ..operators.grammar import (
2
+ GrammarContext,
3
+ StructuralGrammarError,
4
+ RepeatWindowError,
5
+ MutationPreconditionError,
6
+ TholClosureError,
7
+ TransitionCompatibilityError,
8
+ SequenceSyntaxError,
9
+ SequenceValidationResult,
10
+ _gram_state,
11
+ apply_glyph_with_grammar,
12
+ enforce_canonical_grammar,
13
+ on_applied_glyph,
14
+ parse_sequence,
15
+ validate_sequence,
16
+ )
17
+ from ..types import NodeId, TNFRGraph
18
+
19
+ __all__ = (
20
+ "GrammarContext",
21
+ "StructuralGrammarError",
22
+ "RepeatWindowError",
23
+ "MutationPreconditionError",
24
+ "TholClosureError",
25
+ "TransitionCompatibilityError",
26
+ "SequenceSyntaxError",
27
+ "SequenceValidationResult",
28
+ "record_grammar_violation",
29
+ "_gram_state",
30
+ "apply_glyph_with_grammar",
31
+ "enforce_canonical_grammar",
32
+ "on_applied_glyph",
33
+ "parse_sequence",
34
+ "validate_sequence",
35
+ )
36
+
37
+
38
+ def record_grammar_violation(
39
+ G: TNFRGraph, node: NodeId, error: StructuralGrammarError, *, stage: str
40
+ ) -> None: ...
@@ -0,0 +1,138 @@
1
+ """Graph-level validation helpers enforcing TNFR invariants."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from collections.abc import Sequence
7
+
8
+ import numpy as np
9
+
10
+ from ..alias import get_attr
11
+ from ..glyph_runtime import last_glyph
12
+ from ..config.constants import GLYPHS_CANONICAL_SET
13
+ from ..constants import get_param
14
+ from ..constants.aliases import ALIAS_EPI, ALIAS_VF
15
+ from ..utils import within_range
16
+ from ..types import (
17
+ EPIValue,
18
+ NodeAttrMap,
19
+ NodeId,
20
+ StructuralFrequency,
21
+ TNFRGraph,
22
+ ValidatorFunc,
23
+ ensure_bepi,
24
+ )
25
+ NodeData = NodeAttrMap
26
+ """Read-only node attribute mapping used by validators."""
27
+
28
+ AliasSequence = Sequence[str]
29
+ """Sequence of accepted attribute aliases."""
30
+
31
+ __all__ = ("run_validators", "GRAPH_VALIDATORS")
32
+
33
+
34
+ def _materialize_node_mapping(data: NodeData) -> dict[str, object]:
35
+ if isinstance(data, dict):
36
+ return data
37
+ return dict(data)
38
+
39
+
40
+ def _require_attr(
41
+ data: NodeData, alias: AliasSequence, node: NodeId, name: str
42
+ ) -> float:
43
+ """Return scalar attribute value or raise if missing."""
44
+
45
+ mapping = _materialize_node_mapping(data)
46
+ val = get_attr(mapping, alias, None)
47
+ if val is None:
48
+ raise ValueError(f"Missing {name} attribute in node {node}")
49
+ return float(val)
50
+
51
+
52
+ def _require_epi(data: NodeData, node: NodeId) -> EPIValue:
53
+ """Return a validated BEPI element stored in ``data``."""
54
+
55
+ mapping = _materialize_node_mapping(data)
56
+ value = get_attr(mapping, ALIAS_EPI, None, conv=lambda obj: obj)
57
+ if value is None:
58
+ raise ValueError(f"Missing EPI attribute in node {node}")
59
+ try:
60
+ return ensure_bepi(value)
61
+ except (TypeError, ValueError) as exc:
62
+ raise ValueError(f"Invalid EPI payload in node {node}: {exc}") from exc
63
+
64
+
65
+ def _validate_sigma(graph: TNFRGraph) -> None:
66
+ from ..sense import sigma_vector_from_graph
67
+
68
+ sv = sigma_vector_from_graph(graph)
69
+ if sv.get("mag", 0.0) > 1.0 + sys.float_info.epsilon:
70
+ raise ValueError("σ norm exceeds 1")
71
+
72
+
73
+ GRAPH_VALIDATORS: tuple[ValidatorFunc, ...] = (_validate_sigma,)
74
+ """Ordered collection of graph-level validators."""
75
+
76
+
77
+ def _max_abs(values: np.ndarray) -> float:
78
+ if values.size == 0:
79
+ return 0.0
80
+ return float(np.max(np.abs(values)))
81
+
82
+
83
+ def _check_epi(
84
+ epi: EPIValue,
85
+ epi_min: float,
86
+ epi_max: float,
87
+ node: NodeId,
88
+ ) -> None:
89
+ continuous_max = _max_abs(epi.f_continuous)
90
+ discrete_max = _max_abs(epi.a_discrete)
91
+ _check_range(continuous_max, epi_min, epi_max, "EPI continuous", node)
92
+ _check_range(discrete_max, epi_min, epi_max, "EPI discrete", node)
93
+
94
+ spacings = np.diff(epi.x_grid)
95
+ if np.any(spacings <= 0.0):
96
+ raise ValueError(f"EPI grid must be strictly increasing for node {node}")
97
+ if not np.allclose(spacings, spacings[0], rtol=1e-9, atol=1e-12):
98
+ raise ValueError(f"EPI grid must be uniform for node {node}")
99
+
100
+
101
+ def _out_of_range_msg(name: str, node: NodeId, val: float) -> str:
102
+ return f"{name} out of range in node {node}: {val}"
103
+
104
+
105
+ def _check_range(
106
+ val: float,
107
+ lower: float,
108
+ upper: float,
109
+ name: str,
110
+ node: NodeId,
111
+ tol: float = 1e-9,
112
+ ) -> None:
113
+ if not within_range(val, lower, upper, tol):
114
+ raise ValueError(_out_of_range_msg(name, node, val))
115
+
116
+
117
+ def _check_glyph(glyph: str | None, node: NodeId) -> None:
118
+ if glyph and glyph not in GLYPHS_CANONICAL_SET:
119
+ raise KeyError(f"Invalid glyph {glyph} in node {node}")
120
+
121
+
122
+ def run_validators(graph: TNFRGraph) -> None:
123
+ """Run all invariant validators on ``graph`` with a single node pass."""
124
+
125
+ epi_min = float(get_param(graph, "EPI_MIN"))
126
+ epi_max = float(get_param(graph, "EPI_MAX"))
127
+ vf_min = float(get_param(graph, "VF_MIN"))
128
+ vf_max = float(get_param(graph, "VF_MAX"))
129
+
130
+ for node, data in graph.nodes(data=True):
131
+ epi = _require_epi(data, node)
132
+ vf = StructuralFrequency(_require_attr(data, ALIAS_VF, node, "VF"))
133
+ _check_epi(epi, epi_min, epi_max, node)
134
+ _check_range(vf, vf_min, vf_max, "VF", node)
135
+ _check_glyph(last_glyph(data), node)
136
+
137
+ for validator in GRAPH_VALIDATORS:
138
+ validator(graph)
@@ -0,0 +1,17 @@
1
+ from collections.abc import Sequence
2
+ from typing import Tuple
3
+
4
+ from ..types import (
5
+ EPIValue,
6
+ NodeAttrMap,
7
+ NodeId,
8
+ StructuralFrequency,
9
+ TNFRGraph,
10
+ ValidatorFunc,
11
+ )
12
+ NodeData = NodeAttrMap
13
+ AliasSequence = Sequence[str]
14
+
15
+ GRAPH_VALIDATORS: Tuple[ValidatorFunc, ...]
16
+
17
+ def run_validators(graph: TNFRGraph) -> None: ...