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
|
@@ -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,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: ...
|
tnfr/validation/graph.py
ADDED
|
@@ -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: ...
|