tnfr 3.0.3__py3-none-any.whl → 8.5.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 +375 -56
- 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 +723 -0
- tnfr/alias.pyi +108 -0
- tnfr/backends/__init__.py +354 -0
- tnfr/backends/jax_backend.py +173 -0
- tnfr/backends/numpy_backend.py +238 -0
- tnfr/backends/optimized_numpy.py +420 -0
- tnfr/backends/torch_backend.py +408 -0
- tnfr/cache.py +171 -0
- tnfr/cache.pyi +13 -0
- tnfr/cli/__init__.py +110 -0
- tnfr/cli/__init__.pyi +26 -0
- tnfr/cli/arguments.py +489 -0
- tnfr/cli/arguments.pyi +29 -0
- tnfr/cli/execution.py +914 -0
- tnfr/cli/execution.pyi +70 -0
- tnfr/cli/interactive_validator.py +614 -0
- tnfr/cli/utils.py +51 -0
- tnfr/cli/utils.pyi +7 -0
- tnfr/cli/validate.py +236 -0
- tnfr/compat/__init__.py +85 -0
- tnfr/compat/dataclass.py +136 -0
- tnfr/compat/jsonschema_stub.py +61 -0
- tnfr/compat/matplotlib_stub.py +73 -0
- tnfr/compat/numpy_stub.py +155 -0
- tnfr/config/__init__.py +224 -0
- tnfr/config/__init__.pyi +10 -0
- tnfr/config/constants.py +104 -0
- tnfr/config/constants.pyi +12 -0
- tnfr/config/defaults.py +54 -0
- tnfr/config/defaults_core.py +212 -0
- tnfr/config/defaults_init.py +33 -0
- tnfr/config/defaults_metric.py +104 -0
- tnfr/config/feature_flags.py +81 -0
- tnfr/config/feature_flags.pyi +16 -0
- tnfr/config/glyph_constants.py +31 -0
- tnfr/config/init.py +77 -0
- tnfr/config/init.pyi +8 -0
- tnfr/config/operator_names.py +254 -0
- tnfr/config/operator_names.pyi +36 -0
- tnfr/config/physics_derivation.py +354 -0
- tnfr/config/presets.py +83 -0
- tnfr/config/presets.pyi +7 -0
- tnfr/config/security.py +927 -0
- tnfr/config/thresholds.py +114 -0
- tnfr/config/tnfr_config.py +498 -0
- tnfr/constants/__init__.py +92 -0
- tnfr/constants/__init__.pyi +92 -0
- tnfr/constants/aliases.py +33 -0
- tnfr/constants/aliases.pyi +27 -0
- tnfr/constants/init.py +33 -0
- tnfr/constants/init.pyi +12 -0
- tnfr/constants/metric.py +104 -0
- tnfr/constants/metric.pyi +19 -0
- tnfr/core/__init__.py +33 -0
- tnfr/core/container.py +226 -0
- tnfr/core/default_implementations.py +329 -0
- tnfr/core/interfaces.py +279 -0
- tnfr/dynamics/__init__.py +238 -0
- tnfr/dynamics/__init__.pyi +83 -0
- tnfr/dynamics/adaptation.py +267 -0
- tnfr/dynamics/adaptation.pyi +7 -0
- tnfr/dynamics/adaptive_sequences.py +189 -0
- tnfr/dynamics/adaptive_sequences.pyi +14 -0
- tnfr/dynamics/aliases.py +23 -0
- tnfr/dynamics/aliases.pyi +19 -0
- tnfr/dynamics/bifurcation.py +232 -0
- tnfr/dynamics/canonical.py +229 -0
- tnfr/dynamics/canonical.pyi +48 -0
- tnfr/dynamics/coordination.py +385 -0
- tnfr/dynamics/coordination.pyi +25 -0
- tnfr/dynamics/dnfr.py +3034 -0
- tnfr/dynamics/dnfr.pyi +26 -0
- tnfr/dynamics/dynamic_limits.py +225 -0
- tnfr/dynamics/feedback.py +252 -0
- tnfr/dynamics/feedback.pyi +24 -0
- tnfr/dynamics/fused_dnfr.py +454 -0
- tnfr/dynamics/homeostasis.py +157 -0
- tnfr/dynamics/homeostasis.pyi +14 -0
- tnfr/dynamics/integrators.py +661 -0
- tnfr/dynamics/integrators.pyi +36 -0
- tnfr/dynamics/learning.py +310 -0
- tnfr/dynamics/learning.pyi +33 -0
- tnfr/dynamics/metabolism.py +254 -0
- tnfr/dynamics/nbody.py +796 -0
- tnfr/dynamics/nbody_tnfr.py +783 -0
- tnfr/dynamics/propagation.py +326 -0
- tnfr/dynamics/runtime.py +908 -0
- tnfr/dynamics/runtime.pyi +77 -0
- tnfr/dynamics/sampling.py +36 -0
- tnfr/dynamics/sampling.pyi +7 -0
- tnfr/dynamics/selectors.py +711 -0
- tnfr/dynamics/selectors.pyi +85 -0
- tnfr/dynamics/structural_clip.py +207 -0
- tnfr/errors/__init__.py +37 -0
- tnfr/errors/contextual.py +492 -0
- tnfr/execution.py +223 -0
- tnfr/execution.pyi +45 -0
- tnfr/extensions/__init__.py +205 -0
- tnfr/extensions/__init__.pyi +18 -0
- tnfr/extensions/base.py +173 -0
- tnfr/extensions/base.pyi +35 -0
- tnfr/extensions/business/__init__.py +71 -0
- tnfr/extensions/business/__init__.pyi +11 -0
- tnfr/extensions/business/cookbook.py +88 -0
- tnfr/extensions/business/cookbook.pyi +8 -0
- tnfr/extensions/business/health_analyzers.py +202 -0
- tnfr/extensions/business/health_analyzers.pyi +9 -0
- tnfr/extensions/business/patterns.py +183 -0
- tnfr/extensions/business/patterns.pyi +8 -0
- tnfr/extensions/medical/__init__.py +73 -0
- tnfr/extensions/medical/__init__.pyi +11 -0
- tnfr/extensions/medical/cookbook.py +88 -0
- tnfr/extensions/medical/cookbook.pyi +8 -0
- tnfr/extensions/medical/health_analyzers.py +181 -0
- tnfr/extensions/medical/health_analyzers.pyi +9 -0
- tnfr/extensions/medical/patterns.py +163 -0
- tnfr/extensions/medical/patterns.pyi +8 -0
- tnfr/flatten.py +262 -0
- tnfr/flatten.pyi +21 -0
- tnfr/gamma.py +354 -0
- tnfr/gamma.pyi +36 -0
- tnfr/glyph_history.py +377 -0
- tnfr/glyph_history.pyi +35 -0
- tnfr/glyph_runtime.py +19 -0
- tnfr/glyph_runtime.pyi +8 -0
- tnfr/immutable.py +218 -0
- tnfr/immutable.pyi +36 -0
- tnfr/initialization.py +203 -0
- tnfr/initialization.pyi +65 -0
- tnfr/io.py +10 -0
- tnfr/io.pyi +13 -0
- tnfr/locking.py +37 -0
- tnfr/locking.pyi +7 -0
- tnfr/mathematics/__init__.py +79 -0
- tnfr/mathematics/backend.py +453 -0
- tnfr/mathematics/backend.pyi +99 -0
- tnfr/mathematics/dynamics.py +408 -0
- tnfr/mathematics/dynamics.pyi +90 -0
- tnfr/mathematics/epi.py +391 -0
- tnfr/mathematics/epi.pyi +65 -0
- tnfr/mathematics/generators.py +242 -0
- tnfr/mathematics/generators.pyi +29 -0
- tnfr/mathematics/metrics.py +119 -0
- tnfr/mathematics/metrics.pyi +16 -0
- tnfr/mathematics/operators.py +239 -0
- tnfr/mathematics/operators.pyi +59 -0
- tnfr/mathematics/operators_factory.py +124 -0
- tnfr/mathematics/operators_factory.pyi +11 -0
- tnfr/mathematics/projection.py +87 -0
- tnfr/mathematics/projection.pyi +33 -0
- tnfr/mathematics/runtime.py +182 -0
- tnfr/mathematics/runtime.pyi +64 -0
- tnfr/mathematics/spaces.py +256 -0
- tnfr/mathematics/spaces.pyi +83 -0
- tnfr/mathematics/transforms.py +305 -0
- tnfr/mathematics/transforms.pyi +62 -0
- tnfr/metrics/__init__.py +79 -0
- tnfr/metrics/__init__.pyi +20 -0
- tnfr/metrics/buffer_cache.py +163 -0
- tnfr/metrics/buffer_cache.pyi +24 -0
- tnfr/metrics/cache_utils.py +214 -0
- tnfr/metrics/coherence.py +2009 -0
- tnfr/metrics/coherence.pyi +129 -0
- tnfr/metrics/common.py +158 -0
- tnfr/metrics/common.pyi +35 -0
- tnfr/metrics/core.py +316 -0
- tnfr/metrics/core.pyi +13 -0
- tnfr/metrics/diagnosis.py +833 -0
- tnfr/metrics/diagnosis.pyi +86 -0
- tnfr/metrics/emergence.py +245 -0
- tnfr/metrics/export.py +179 -0
- tnfr/metrics/export.pyi +7 -0
- tnfr/metrics/glyph_timing.py +379 -0
- tnfr/metrics/glyph_timing.pyi +81 -0
- tnfr/metrics/learning_metrics.py +280 -0
- tnfr/metrics/learning_metrics.pyi +21 -0
- tnfr/metrics/phase_coherence.py +351 -0
- tnfr/metrics/phase_compatibility.py +349 -0
- tnfr/metrics/reporting.py +183 -0
- tnfr/metrics/reporting.pyi +25 -0
- tnfr/metrics/sense_index.py +1203 -0
- tnfr/metrics/sense_index.pyi +9 -0
- tnfr/metrics/trig.py +373 -0
- tnfr/metrics/trig.pyi +13 -0
- tnfr/metrics/trig_cache.py +233 -0
- tnfr/metrics/trig_cache.pyi +10 -0
- tnfr/multiscale/__init__.py +32 -0
- tnfr/multiscale/hierarchical.py +517 -0
- tnfr/node.py +763 -0
- tnfr/node.pyi +139 -0
- tnfr/observers.py +255 -130
- tnfr/observers.pyi +31 -0
- tnfr/ontosim.py +144 -137
- tnfr/ontosim.pyi +28 -0
- tnfr/operators/__init__.py +1672 -0
- tnfr/operators/__init__.pyi +31 -0
- tnfr/operators/algebra.py +277 -0
- tnfr/operators/canonical_patterns.py +420 -0
- tnfr/operators/cascade.py +267 -0
- tnfr/operators/cycle_detection.py +358 -0
- tnfr/operators/definitions.py +4108 -0
- tnfr/operators/definitions.pyi +78 -0
- tnfr/operators/grammar.py +1164 -0
- tnfr/operators/grammar.pyi +140 -0
- tnfr/operators/hamiltonian.py +710 -0
- tnfr/operators/health_analyzer.py +809 -0
- tnfr/operators/jitter.py +272 -0
- tnfr/operators/jitter.pyi +11 -0
- tnfr/operators/lifecycle.py +314 -0
- tnfr/operators/metabolism.py +618 -0
- tnfr/operators/metrics.py +2138 -0
- tnfr/operators/network_analysis/__init__.py +27 -0
- tnfr/operators/network_analysis/source_detection.py +186 -0
- tnfr/operators/nodal_equation.py +395 -0
- tnfr/operators/pattern_detection.py +660 -0
- tnfr/operators/patterns.py +669 -0
- tnfr/operators/postconditions/__init__.py +38 -0
- tnfr/operators/postconditions/mutation.py +236 -0
- tnfr/operators/preconditions/__init__.py +1226 -0
- tnfr/operators/preconditions/coherence.py +305 -0
- tnfr/operators/preconditions/dissonance.py +236 -0
- tnfr/operators/preconditions/emission.py +128 -0
- tnfr/operators/preconditions/mutation.py +580 -0
- tnfr/operators/preconditions/reception.py +125 -0
- tnfr/operators/preconditions/resonance.py +364 -0
- tnfr/operators/registry.py +74 -0
- tnfr/operators/registry.pyi +9 -0
- tnfr/operators/remesh.py +1809 -0
- tnfr/operators/remesh.pyi +26 -0
- tnfr/operators/structural_units.py +268 -0
- tnfr/operators/unified_grammar.py +105 -0
- tnfr/parallel/__init__.py +54 -0
- tnfr/parallel/auto_scaler.py +234 -0
- tnfr/parallel/distributed.py +384 -0
- tnfr/parallel/engine.py +238 -0
- tnfr/parallel/gpu_engine.py +420 -0
- tnfr/parallel/monitoring.py +248 -0
- tnfr/parallel/partitioner.py +459 -0
- tnfr/py.typed +0 -0
- tnfr/recipes/__init__.py +22 -0
- tnfr/recipes/cookbook.py +743 -0
- tnfr/rng.py +178 -0
- tnfr/rng.pyi +26 -0
- tnfr/schemas/__init__.py +8 -0
- tnfr/schemas/grammar.json +94 -0
- tnfr/sdk/__init__.py +107 -0
- tnfr/sdk/__init__.pyi +19 -0
- tnfr/sdk/adaptive_system.py +173 -0
- tnfr/sdk/adaptive_system.pyi +21 -0
- tnfr/sdk/builders.py +370 -0
- tnfr/sdk/builders.pyi +51 -0
- tnfr/sdk/fluent.py +1121 -0
- tnfr/sdk/fluent.pyi +74 -0
- tnfr/sdk/templates.py +342 -0
- tnfr/sdk/templates.pyi +41 -0
- tnfr/sdk/utils.py +341 -0
- tnfr/secure_config.py +46 -0
- tnfr/security/__init__.py +70 -0
- tnfr/security/database.py +514 -0
- tnfr/security/subprocess.py +503 -0
- tnfr/security/validation.py +290 -0
- tnfr/selector.py +247 -0
- tnfr/selector.pyi +19 -0
- tnfr/sense.py +378 -0
- tnfr/sense.pyi +23 -0
- tnfr/services/__init__.py +17 -0
- tnfr/services/orchestrator.py +325 -0
- tnfr/sparse/__init__.py +39 -0
- tnfr/sparse/representations.py +492 -0
- tnfr/structural.py +705 -0
- tnfr/structural.pyi +83 -0
- tnfr/telemetry/__init__.py +35 -0
- tnfr/telemetry/cache_metrics.py +226 -0
- tnfr/telemetry/cache_metrics.pyi +64 -0
- tnfr/telemetry/nu_f.py +422 -0
- tnfr/telemetry/nu_f.pyi +108 -0
- tnfr/telemetry/verbosity.py +36 -0
- tnfr/telemetry/verbosity.pyi +15 -0
- tnfr/tokens.py +58 -0
- tnfr/tokens.pyi +36 -0
- tnfr/tools/__init__.py +20 -0
- tnfr/tools/domain_templates.py +478 -0
- tnfr/tools/sequence_generator.py +846 -0
- tnfr/topology/__init__.py +13 -0
- tnfr/topology/asymmetry.py +151 -0
- tnfr/trace.py +543 -0
- tnfr/trace.pyi +42 -0
- tnfr/tutorials/__init__.py +38 -0
- tnfr/tutorials/autonomous_evolution.py +285 -0
- tnfr/tutorials/interactive.py +1576 -0
- tnfr/tutorials/structural_metabolism.py +238 -0
- tnfr/types.py +775 -0
- tnfr/types.pyi +357 -0
- tnfr/units.py +68 -0
- tnfr/units.pyi +13 -0
- tnfr/utils/__init__.py +282 -0
- tnfr/utils/__init__.pyi +215 -0
- tnfr/utils/cache.py +4223 -0
- tnfr/utils/cache.pyi +470 -0
- tnfr/utils/callbacks.py +375 -0
- tnfr/utils/callbacks.pyi +49 -0
- tnfr/utils/chunks.py +108 -0
- tnfr/utils/chunks.pyi +22 -0
- tnfr/utils/data.py +428 -0
- tnfr/utils/data.pyi +74 -0
- tnfr/utils/graph.py +85 -0
- tnfr/utils/graph.pyi +10 -0
- tnfr/utils/init.py +821 -0
- tnfr/utils/init.pyi +80 -0
- tnfr/utils/io.py +559 -0
- tnfr/utils/io.pyi +66 -0
- tnfr/utils/numeric.py +114 -0
- tnfr/utils/numeric.pyi +21 -0
- tnfr/validation/__init__.py +257 -0
- tnfr/validation/__init__.pyi +85 -0
- tnfr/validation/compatibility.py +460 -0
- tnfr/validation/compatibility.pyi +6 -0
- tnfr/validation/config.py +73 -0
- tnfr/validation/graph.py +139 -0
- tnfr/validation/graph.pyi +18 -0
- tnfr/validation/input_validation.py +755 -0
- tnfr/validation/invariants.py +712 -0
- tnfr/validation/rules.py +253 -0
- tnfr/validation/rules.pyi +44 -0
- tnfr/validation/runtime.py +279 -0
- tnfr/validation/runtime.pyi +28 -0
- tnfr/validation/sequence_validator.py +162 -0
- tnfr/validation/soft_filters.py +170 -0
- tnfr/validation/soft_filters.pyi +32 -0
- tnfr/validation/spectral.py +164 -0
- tnfr/validation/spectral.pyi +42 -0
- tnfr/validation/validator.py +1266 -0
- tnfr/validation/window.py +39 -0
- tnfr/validation/window.pyi +1 -0
- tnfr/visualization/__init__.py +98 -0
- tnfr/visualization/cascade_viz.py +256 -0
- tnfr/visualization/hierarchy.py +284 -0
- tnfr/visualization/sequence_plotter.py +784 -0
- tnfr/viz/__init__.py +60 -0
- tnfr/viz/matplotlib.py +278 -0
- tnfr/viz/matplotlib.pyi +35 -0
- tnfr-8.5.0.dist-info/METADATA +573 -0
- tnfr-8.5.0.dist-info/RECORD +353 -0
- tnfr-8.5.0.dist-info/entry_points.txt +3 -0
- tnfr-3.0.3.dist-info/licenses/LICENSE.txt → tnfr-8.5.0.dist-info/licenses/LICENSE.md +1 -1
- tnfr/constants.py +0 -183
- tnfr/dynamics.py +0 -543
- tnfr/helpers.py +0 -198
- tnfr/main.py +0 -37
- tnfr/operators.py +0 -296
- tnfr-3.0.3.dist-info/METADATA +0 -35
- tnfr-3.0.3.dist-info/RECORD +0 -13
- {tnfr-3.0.3.dist-info → tnfr-8.5.0.dist-info}/WHEEL +0 -0
- {tnfr-3.0.3.dist-info → tnfr-8.5.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1164 @@
|
|
|
1
|
+
"""TNFR Canonical Grammar - Single Source of Truth.
|
|
2
|
+
|
|
3
|
+
This module implements the canonical TNFR grammar constraints that emerge
|
|
4
|
+
inevitably from TNFR physics.
|
|
5
|
+
|
|
6
|
+
All rules derive from the nodal equation ∂EPI/∂t = νf · ΔNFR(t), canonical
|
|
7
|
+
invariants, and formal contracts. No organizational conventions.
|
|
8
|
+
|
|
9
|
+
Canonical Constraints (U1-U4)
|
|
10
|
+
------------------------------
|
|
11
|
+
U1: STRUCTURAL INITIATION & CLOSURE
|
|
12
|
+
U1a: Start with generators when needed
|
|
13
|
+
U1b: End with closure operators
|
|
14
|
+
Basis: ∂EPI/∂t undefined at EPI=0, sequences need coherent endpoints
|
|
15
|
+
|
|
16
|
+
U2: CONVERGENCE & BOUNDEDNESS
|
|
17
|
+
If destabilizers, then include stabilizers
|
|
18
|
+
Basis: ∫νf·ΔNFR dt must converge (integral convergence theorem)
|
|
19
|
+
|
|
20
|
+
U3: RESONANT COUPLING
|
|
21
|
+
If coupling/resonance, then verify phase compatibility
|
|
22
|
+
Basis: AGENTS.md Invariant #5 + resonance physics
|
|
23
|
+
|
|
24
|
+
U4: BIFURCATION DYNAMICS
|
|
25
|
+
U4a: If bifurcation triggers, then include handlers
|
|
26
|
+
U4b: If transformers, then recent destabilizer (+ prior IL for ZHIR)
|
|
27
|
+
Basis: Contract OZ + bifurcation theory
|
|
28
|
+
|
|
29
|
+
For complete derivations and physics basis, see UNIFIED_GRAMMAR_RULES.md
|
|
30
|
+
|
|
31
|
+
References
|
|
32
|
+
----------
|
|
33
|
+
- UNIFIED_GRAMMAR_RULES.md: Complete physics derivations and mappings
|
|
34
|
+
- AGENTS.md: Canonical invariants and formal contracts
|
|
35
|
+
- TNFR.pdf: Nodal equation and bifurcation theory
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
from __future__ import annotations
|
|
39
|
+
|
|
40
|
+
from enum import Enum
|
|
41
|
+
from typing import TYPE_CHECKING, Any, List
|
|
42
|
+
|
|
43
|
+
if TYPE_CHECKING:
|
|
44
|
+
from ..types import NodeId, TNFRGraph, Glyph
|
|
45
|
+
from .definitions import Operator
|
|
46
|
+
else:
|
|
47
|
+
from ..types import Glyph
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class StructuralPattern(Enum):
|
|
51
|
+
"""Classification of structural patterns in TNFR sequences.
|
|
52
|
+
|
|
53
|
+
Used by canonical_patterns module for backward compatibility.
|
|
54
|
+
Deprecated - use pattern_detection module for new code.
|
|
55
|
+
"""
|
|
56
|
+
BIFURCATED = "bifurcated"
|
|
57
|
+
THERAPEUTIC = "therapeutic"
|
|
58
|
+
EDUCATIONAL = "educational"
|
|
59
|
+
COMPLEX = "complex"
|
|
60
|
+
COMPRESS = "compress"
|
|
61
|
+
EXPLORE = "explore"
|
|
62
|
+
RESONATE = "resonate"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# ============================================================================
|
|
66
|
+
# Glyph-Function Name Mappings
|
|
67
|
+
# ============================================================================
|
|
68
|
+
|
|
69
|
+
# Mapping from Glyph to canonical function name
|
|
70
|
+
GLYPH_TO_FUNCTION = {
|
|
71
|
+
Glyph.AL: "emission",
|
|
72
|
+
Glyph.EN: "reception",
|
|
73
|
+
Glyph.IL: "coherence",
|
|
74
|
+
Glyph.OZ: "dissonance",
|
|
75
|
+
Glyph.UM: "coupling",
|
|
76
|
+
Glyph.RA: "resonance",
|
|
77
|
+
Glyph.SHA: "silence",
|
|
78
|
+
Glyph.VAL: "expansion",
|
|
79
|
+
Glyph.NUL: "contraction",
|
|
80
|
+
Glyph.THOL: "self_organization",
|
|
81
|
+
Glyph.ZHIR: "mutation",
|
|
82
|
+
Glyph.NAV: "transition",
|
|
83
|
+
Glyph.REMESH: "recursivity",
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Reverse mapping from function name to Glyph
|
|
87
|
+
FUNCTION_TO_GLYPH = {v: k for k, v in GLYPH_TO_FUNCTION.items()}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def glyph_function_name(
|
|
91
|
+
val: Any,
|
|
92
|
+
*,
|
|
93
|
+
default: Any = None,
|
|
94
|
+
) -> Any:
|
|
95
|
+
"""Convert glyph to canonical function name.
|
|
96
|
+
|
|
97
|
+
Parameters
|
|
98
|
+
----------
|
|
99
|
+
val : Glyph | str | None
|
|
100
|
+
Glyph enum, glyph string value ('IL', 'OZ'), or function name to convert
|
|
101
|
+
default : str | None, optional
|
|
102
|
+
Default value if conversion fails
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
str | None
|
|
107
|
+
Canonical function name or default
|
|
108
|
+
|
|
109
|
+
Notes
|
|
110
|
+
-----
|
|
111
|
+
Glyph enum inherits from str, so we must check for Enum type
|
|
112
|
+
BEFORE checking isinstance(val, str), otherwise Glyph instances
|
|
113
|
+
will be returned unchanged instead of being converted.
|
|
114
|
+
|
|
115
|
+
The function handles three input types:
|
|
116
|
+
1. Glyph enum (e.g., Glyph.IL) → function name (e.g., 'coherence')
|
|
117
|
+
2. Glyph string value (e.g., 'IL') → function name (e.g., 'coherence')
|
|
118
|
+
3. Function name (e.g., 'coherence') → returned as-is
|
|
119
|
+
"""
|
|
120
|
+
if val is None:
|
|
121
|
+
return default
|
|
122
|
+
# Check for Glyph/Enum BEFORE str (Glyph inherits from str)
|
|
123
|
+
if isinstance(val, Enum):
|
|
124
|
+
return GLYPH_TO_FUNCTION.get(val, default)
|
|
125
|
+
if isinstance(val, str):
|
|
126
|
+
# Check if it's a glyph string value ('IL', 'OZ', etc)
|
|
127
|
+
# Build reverse lookup on first use
|
|
128
|
+
if not hasattr(glyph_function_name, '_glyph_value_map'):
|
|
129
|
+
glyph_function_name._glyph_value_map = {
|
|
130
|
+
g.value: func for g, func in GLYPH_TO_FUNCTION.items()
|
|
131
|
+
}
|
|
132
|
+
# Try to convert glyph value to function name
|
|
133
|
+
func_name = glyph_function_name._glyph_value_map.get(val)
|
|
134
|
+
if func_name:
|
|
135
|
+
return func_name
|
|
136
|
+
# Otherwise assume it's already a function name
|
|
137
|
+
return val
|
|
138
|
+
return GLYPH_TO_FUNCTION.get(val, default)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def function_name_to_glyph(
|
|
142
|
+
val: Any,
|
|
143
|
+
*,
|
|
144
|
+
default: Any = None,
|
|
145
|
+
) -> Any:
|
|
146
|
+
"""Convert function name to glyph.
|
|
147
|
+
|
|
148
|
+
Parameters
|
|
149
|
+
----------
|
|
150
|
+
val : str | Glyph | None
|
|
151
|
+
Function name or glyph to convert
|
|
152
|
+
default : Glyph | None, optional
|
|
153
|
+
Default value if conversion fails
|
|
154
|
+
|
|
155
|
+
Returns
|
|
156
|
+
-------
|
|
157
|
+
Glyph | None
|
|
158
|
+
Glyph or default
|
|
159
|
+
"""
|
|
160
|
+
if val is None:
|
|
161
|
+
return default
|
|
162
|
+
if isinstance(val, Glyph):
|
|
163
|
+
return val
|
|
164
|
+
return FUNCTION_TO_GLYPH.get(val, default)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
__all__ = [
|
|
168
|
+
"GrammarValidator",
|
|
169
|
+
"GrammarContext",
|
|
170
|
+
"validate_grammar",
|
|
171
|
+
"StructuralPattern",
|
|
172
|
+
# Error classes
|
|
173
|
+
"StructuralGrammarError",
|
|
174
|
+
"RepeatWindowError",
|
|
175
|
+
"MutationPreconditionError",
|
|
176
|
+
"TholClosureError",
|
|
177
|
+
"TransitionCompatibilityError",
|
|
178
|
+
"SequenceSyntaxError",
|
|
179
|
+
"GrammarConfigurationError",
|
|
180
|
+
"record_grammar_violation",
|
|
181
|
+
# Glyph mappings
|
|
182
|
+
"GLYPH_TO_FUNCTION",
|
|
183
|
+
"FUNCTION_TO_GLYPH",
|
|
184
|
+
"glyph_function_name",
|
|
185
|
+
"function_name_to_glyph",
|
|
186
|
+
# Grammar application functions
|
|
187
|
+
"apply_glyph_with_grammar",
|
|
188
|
+
"on_applied_glyph",
|
|
189
|
+
"enforce_canonical_grammar",
|
|
190
|
+
# Sequence validation
|
|
191
|
+
"validate_sequence",
|
|
192
|
+
"parse_sequence",
|
|
193
|
+
# Operator sets
|
|
194
|
+
"GENERATORS",
|
|
195
|
+
"CLOSURES",
|
|
196
|
+
"STABILIZERS",
|
|
197
|
+
"DESTABILIZERS",
|
|
198
|
+
"COUPLING_RESONANCE",
|
|
199
|
+
"BIFURCATION_TRIGGERS",
|
|
200
|
+
"BIFURCATION_HANDLERS",
|
|
201
|
+
"TRANSFORMERS",
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# ============================================================================
|
|
206
|
+
# Operator Sets (Derived from TNFR Physics)
|
|
207
|
+
# ============================================================================
|
|
208
|
+
|
|
209
|
+
# U1a: Generators - Create EPI from null/dormant states
|
|
210
|
+
GENERATORS = frozenset({"emission", "transition", "recursivity"})
|
|
211
|
+
|
|
212
|
+
# U1b: Closures - Leave system in coherent attractor states
|
|
213
|
+
CLOSURES = frozenset({"silence", "transition", "recursivity", "dissonance"})
|
|
214
|
+
|
|
215
|
+
# U2: Stabilizers - Provide negative feedback for convergence
|
|
216
|
+
STABILIZERS = frozenset({"coherence", "self_organization"})
|
|
217
|
+
|
|
218
|
+
# U2: Destabilizers - Increase |ΔNFR| (positive feedback)
|
|
219
|
+
DESTABILIZERS = frozenset({"dissonance", "mutation", "expansion"})
|
|
220
|
+
|
|
221
|
+
# U3: Coupling/Resonance - Require phase verification
|
|
222
|
+
COUPLING_RESONANCE = frozenset({"coupling", "resonance"})
|
|
223
|
+
|
|
224
|
+
# U4a: Bifurcation triggers - May initiate phase transitions
|
|
225
|
+
BIFURCATION_TRIGGERS = frozenset({"dissonance", "mutation"})
|
|
226
|
+
|
|
227
|
+
# U4a: Bifurcation handlers - Manage reorganization when ∂²EPI/∂t² > τ
|
|
228
|
+
BIFURCATION_HANDLERS = frozenset({"self_organization", "coherence"})
|
|
229
|
+
|
|
230
|
+
# U4b: Transformers - Execute structural bifurcations
|
|
231
|
+
TRANSFORMERS = frozenset({"mutation", "self_organization"})
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
# ============================================================================
|
|
235
|
+
# Grammar Errors
|
|
236
|
+
# ============================================================================
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class StructuralGrammarError(RuntimeError):
|
|
240
|
+
"""Base class for structural grammar violations.
|
|
241
|
+
|
|
242
|
+
Attributes
|
|
243
|
+
----------
|
|
244
|
+
rule : str
|
|
245
|
+
Grammar rule that was violated
|
|
246
|
+
candidate : str
|
|
247
|
+
Operator/glyph that caused violation
|
|
248
|
+
message : str
|
|
249
|
+
Error description
|
|
250
|
+
window : int | None
|
|
251
|
+
Grammar window if applicable
|
|
252
|
+
threshold : float | None
|
|
253
|
+
Threshold value if applicable
|
|
254
|
+
order : Sequence[str] | None
|
|
255
|
+
Operator sequence if applicable
|
|
256
|
+
context : dict
|
|
257
|
+
Additional context information
|
|
258
|
+
"""
|
|
259
|
+
|
|
260
|
+
def __init__(
|
|
261
|
+
self,
|
|
262
|
+
*,
|
|
263
|
+
rule: str,
|
|
264
|
+
candidate: str,
|
|
265
|
+
message: str,
|
|
266
|
+
window: int | None = None,
|
|
267
|
+
threshold: float | None = None,
|
|
268
|
+
order: list[str] | None = None,
|
|
269
|
+
context: dict[str, Any] | None = None,
|
|
270
|
+
):
|
|
271
|
+
self.rule = rule
|
|
272
|
+
self.candidate = candidate
|
|
273
|
+
self.message = message
|
|
274
|
+
self.window = window
|
|
275
|
+
self.threshold = threshold
|
|
276
|
+
self.order = order
|
|
277
|
+
self.context = context or {}
|
|
278
|
+
super().__init__(message)
|
|
279
|
+
|
|
280
|
+
def attach_context(self, **context: Any) -> "StructuralGrammarError":
|
|
281
|
+
"""Attach additional context to error.
|
|
282
|
+
|
|
283
|
+
Parameters
|
|
284
|
+
----------
|
|
285
|
+
**context : Any
|
|
286
|
+
Additional context key-value pairs
|
|
287
|
+
|
|
288
|
+
Returns
|
|
289
|
+
-------
|
|
290
|
+
StructuralGrammarError
|
|
291
|
+
Self for chaining
|
|
292
|
+
"""
|
|
293
|
+
self.context.update(context)
|
|
294
|
+
return self
|
|
295
|
+
|
|
296
|
+
def to_payload(self) -> dict[str, Any]:
|
|
297
|
+
"""Convert error to dictionary payload.
|
|
298
|
+
|
|
299
|
+
Returns
|
|
300
|
+
-------
|
|
301
|
+
dict
|
|
302
|
+
Error information as dictionary
|
|
303
|
+
"""
|
|
304
|
+
return {
|
|
305
|
+
"rule": self.rule,
|
|
306
|
+
"candidate": self.candidate,
|
|
307
|
+
"message": self.message,
|
|
308
|
+
"window": self.window,
|
|
309
|
+
"threshold": self.threshold,
|
|
310
|
+
"order": self.order,
|
|
311
|
+
"context": self.context,
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
class RepeatWindowError(StructuralGrammarError):
|
|
316
|
+
"""Error for repeated operator within window."""
|
|
317
|
+
pass
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
class MutationPreconditionError(StructuralGrammarError):
|
|
321
|
+
"""Error for mutation without proper preconditions."""
|
|
322
|
+
pass
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class TholClosureError(StructuralGrammarError):
|
|
326
|
+
"""Error for THOL without proper closure."""
|
|
327
|
+
pass
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class TransitionCompatibilityError(StructuralGrammarError):
|
|
331
|
+
"""Error for incompatible transition."""
|
|
332
|
+
pass
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
class SequenceSyntaxError(ValueError):
|
|
336
|
+
"""Error in sequence syntax.
|
|
337
|
+
|
|
338
|
+
Attributes
|
|
339
|
+
----------
|
|
340
|
+
index : int
|
|
341
|
+
Position in sequence where error occurred
|
|
342
|
+
token : object
|
|
343
|
+
Token that caused the error
|
|
344
|
+
message : str
|
|
345
|
+
Error description
|
|
346
|
+
"""
|
|
347
|
+
|
|
348
|
+
def __init__(self, index: int, token: Any, message: str):
|
|
349
|
+
self.index = index
|
|
350
|
+
self.token = token
|
|
351
|
+
self.message = message
|
|
352
|
+
super().__init__(f"At index {index}, token '{token}': {message}")
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
class GrammarConfigurationError(ValueError):
|
|
356
|
+
"""Error in grammar configuration.
|
|
357
|
+
|
|
358
|
+
Attributes
|
|
359
|
+
----------
|
|
360
|
+
section : str
|
|
361
|
+
Configuration section with error
|
|
362
|
+
messages : list[str]
|
|
363
|
+
Error messages
|
|
364
|
+
details : list[tuple[str, str]]
|
|
365
|
+
Additional details
|
|
366
|
+
"""
|
|
367
|
+
|
|
368
|
+
def __init__(
|
|
369
|
+
self,
|
|
370
|
+
section: str,
|
|
371
|
+
messages: list[str],
|
|
372
|
+
*,
|
|
373
|
+
details: list[tuple[str, str]] | None = None,
|
|
374
|
+
):
|
|
375
|
+
self.section = section
|
|
376
|
+
self.messages = messages
|
|
377
|
+
self.details = details or []
|
|
378
|
+
msg = f"Configuration error in {section}: {'; '.join(messages)}"
|
|
379
|
+
super().__init__(msg)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def record_grammar_violation(
|
|
383
|
+
G: "TNFRGraph",
|
|
384
|
+
node: "NodeId",
|
|
385
|
+
error: StructuralGrammarError,
|
|
386
|
+
*,
|
|
387
|
+
stage: str,
|
|
388
|
+
) -> None:
|
|
389
|
+
"""Record grammar violation in node metadata.
|
|
390
|
+
|
|
391
|
+
Parameters
|
|
392
|
+
----------
|
|
393
|
+
G : TNFRGraph
|
|
394
|
+
Graph containing node
|
|
395
|
+
node : NodeId
|
|
396
|
+
Node where violation occurred
|
|
397
|
+
error : StructuralGrammarError
|
|
398
|
+
Grammar error to record
|
|
399
|
+
stage : str
|
|
400
|
+
Processing stage when error occurred
|
|
401
|
+
"""
|
|
402
|
+
if "grammar_violations" not in G.nodes[node]:
|
|
403
|
+
G.nodes[node]["grammar_violations"] = []
|
|
404
|
+
G.nodes[node]["grammar_violations"].append({
|
|
405
|
+
"stage": stage,
|
|
406
|
+
"error": error.to_payload(),
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
# ============================================================================
|
|
411
|
+
# Grammar Context
|
|
412
|
+
# ============================================================================
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
class GrammarContext:
|
|
416
|
+
"""Context object for grammar validation.
|
|
417
|
+
|
|
418
|
+
Minimal implementation for import compatibility.
|
|
419
|
+
|
|
420
|
+
Attributes
|
|
421
|
+
----------
|
|
422
|
+
G : TNFRGraph
|
|
423
|
+
Graph being validated
|
|
424
|
+
cfg_soft : dict
|
|
425
|
+
Soft configuration parameters
|
|
426
|
+
cfg_canon : dict
|
|
427
|
+
Canonical configuration parameters
|
|
428
|
+
norms : dict
|
|
429
|
+
Normalization parameters
|
|
430
|
+
"""
|
|
431
|
+
|
|
432
|
+
def __init__(
|
|
433
|
+
self,
|
|
434
|
+
G: "TNFRGraph",
|
|
435
|
+
cfg_soft: dict[str, Any] | None = None,
|
|
436
|
+
cfg_canon: dict[str, Any] | None = None,
|
|
437
|
+
norms: dict[str, Any] | None = None,
|
|
438
|
+
):
|
|
439
|
+
self.G = G
|
|
440
|
+
self.cfg_soft = cfg_soft or {}
|
|
441
|
+
self.cfg_canon = cfg_canon or {}
|
|
442
|
+
self.norms = norms or {}
|
|
443
|
+
|
|
444
|
+
@classmethod
|
|
445
|
+
def from_graph(cls, G: "TNFRGraph") -> "GrammarContext":
|
|
446
|
+
"""Create context from graph.
|
|
447
|
+
|
|
448
|
+
Parameters
|
|
449
|
+
----------
|
|
450
|
+
G : TNFRGraph
|
|
451
|
+
Graph to create context from
|
|
452
|
+
|
|
453
|
+
Returns
|
|
454
|
+
-------
|
|
455
|
+
GrammarContext
|
|
456
|
+
New context instance
|
|
457
|
+
"""
|
|
458
|
+
return cls(G)
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
class GrammarValidator:
|
|
462
|
+
"""Validates sequences using canonical TNFR grammar constraints.
|
|
463
|
+
|
|
464
|
+
Implements U1-U4 rules that emerge inevitably from TNFR physics.
|
|
465
|
+
This is the single source of truth for grammar validation.
|
|
466
|
+
|
|
467
|
+
All rules derive from:
|
|
468
|
+
- Nodal equation: ∂EPI/∂t = νf · ΔNFR(t)
|
|
469
|
+
- Canonical invariants (AGENTS.md §3)
|
|
470
|
+
- Formal contracts (AGENTS.md §4)
|
|
471
|
+
|
|
472
|
+
No organizational conventions are enforced.
|
|
473
|
+
"""
|
|
474
|
+
|
|
475
|
+
@staticmethod
|
|
476
|
+
def validate_initiation(
|
|
477
|
+
sequence: List[Operator],
|
|
478
|
+
epi_initial: float = 0.0,
|
|
479
|
+
) -> tuple[bool, str]:
|
|
480
|
+
"""Validate U1a: Structural initiation.
|
|
481
|
+
|
|
482
|
+
Physical basis: If EPI=0, then ∂EPI/∂t is undefined or zero.
|
|
483
|
+
Cannot evolve structure that doesn't exist.
|
|
484
|
+
|
|
485
|
+
Generators create structure from:
|
|
486
|
+
- AL (Emission): vacuum via emission
|
|
487
|
+
- NAV (Transition): latent EPI via regime shift
|
|
488
|
+
- REMESH (Recursivity): dormant structure across scales
|
|
489
|
+
|
|
490
|
+
Parameters
|
|
491
|
+
----------
|
|
492
|
+
sequence : List[Operator]
|
|
493
|
+
Sequence of operators to validate
|
|
494
|
+
epi_initial : float, optional
|
|
495
|
+
Initial EPI value (default: 0.0)
|
|
496
|
+
|
|
497
|
+
Returns
|
|
498
|
+
-------
|
|
499
|
+
tuple[bool, str]
|
|
500
|
+
(is_valid, message)
|
|
501
|
+
"""
|
|
502
|
+
if epi_initial > 0.0:
|
|
503
|
+
# Already initialized, no generator required
|
|
504
|
+
return True, "U1a: EPI>0, initiation not required"
|
|
505
|
+
|
|
506
|
+
if not sequence:
|
|
507
|
+
return False, "U1a violated: Empty sequence with EPI=0"
|
|
508
|
+
|
|
509
|
+
first_op = getattr(sequence[0], "canonical_name", sequence[0].name.lower())
|
|
510
|
+
|
|
511
|
+
if first_op not in GENERATORS:
|
|
512
|
+
return (
|
|
513
|
+
False,
|
|
514
|
+
f"U1a violated: EPI=0 requires generator (got '{first_op}'). "
|
|
515
|
+
f"Valid: {sorted(GENERATORS)}",
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
return True, f"U1a satisfied: starts with generator '{first_op}'"
|
|
519
|
+
|
|
520
|
+
@staticmethod
|
|
521
|
+
def validate_closure(sequence: List[Operator]) -> tuple[bool, str]:
|
|
522
|
+
"""Validate U1b: Structural closure.
|
|
523
|
+
|
|
524
|
+
Physical basis: Sequences are bounded action potentials in structural
|
|
525
|
+
space. Like physical waves, they must have termination that leaves
|
|
526
|
+
system in coherent attractor states.
|
|
527
|
+
|
|
528
|
+
Closures stabilize via:
|
|
529
|
+
- SHA (Silence): Terminal closure - freezes evolution (νf → 0)
|
|
530
|
+
- NAV (Transition): Handoff closure - transfers to next regime
|
|
531
|
+
- REMESH (Recursivity): Recursive closure - distributes across scales
|
|
532
|
+
- OZ (Dissonance): Intentional closure - preserves activation/tension
|
|
533
|
+
|
|
534
|
+
Parameters
|
|
535
|
+
----------
|
|
536
|
+
sequence : List[Operator]
|
|
537
|
+
Sequence of operators to validate
|
|
538
|
+
|
|
539
|
+
Returns
|
|
540
|
+
-------
|
|
541
|
+
tuple[bool, str]
|
|
542
|
+
(is_valid, message)
|
|
543
|
+
"""
|
|
544
|
+
if not sequence:
|
|
545
|
+
return False, "U1b violated: Empty sequence has no closure"
|
|
546
|
+
|
|
547
|
+
last_op = getattr(sequence[-1], "canonical_name", sequence[-1].name.lower())
|
|
548
|
+
|
|
549
|
+
if last_op not in CLOSURES:
|
|
550
|
+
return (
|
|
551
|
+
False,
|
|
552
|
+
f"U1b violated: Sequence must end with closure (got '{last_op}'). "
|
|
553
|
+
f"Valid: {sorted(CLOSURES)}",
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
return True, f"U1b satisfied: ends with closure '{last_op}'"
|
|
557
|
+
|
|
558
|
+
@staticmethod
|
|
559
|
+
def validate_convergence(sequence: List[Operator]) -> tuple[bool, str]:
|
|
560
|
+
"""Validate U2: Convergence and boundedness.
|
|
561
|
+
|
|
562
|
+
Physical basis: Without stabilizers, ∫νf·ΔNFR dt → ∞ (diverges).
|
|
563
|
+
Stabilizers provide negative feedback ensuring integral convergence.
|
|
564
|
+
|
|
565
|
+
From integrated nodal equation:
|
|
566
|
+
EPI(t_f) = EPI(t_0) + ∫_{t_0}^{t_f} νf·ΔNFR dτ
|
|
567
|
+
|
|
568
|
+
Without stabilizers:
|
|
569
|
+
d(ΔNFR)/dt > 0 always → ΔNFR ~ e^(λt) → integral diverges
|
|
570
|
+
|
|
571
|
+
With stabilizers (IL or THOL):
|
|
572
|
+
d(ΔNFR)/dt can be < 0 → ΔNFR bounded → integral converges
|
|
573
|
+
|
|
574
|
+
Parameters
|
|
575
|
+
----------
|
|
576
|
+
sequence : List[Operator]
|
|
577
|
+
Sequence of operators to validate
|
|
578
|
+
|
|
579
|
+
Returns
|
|
580
|
+
-------
|
|
581
|
+
tuple[bool, str]
|
|
582
|
+
(is_valid, message)
|
|
583
|
+
"""
|
|
584
|
+
# Check if sequence contains destabilizers
|
|
585
|
+
destabilizers_present = [
|
|
586
|
+
getattr(op, "canonical_name", op.name.lower())
|
|
587
|
+
for op in sequence
|
|
588
|
+
if getattr(op, "canonical_name", op.name.lower()) in DESTABILIZERS
|
|
589
|
+
]
|
|
590
|
+
|
|
591
|
+
if not destabilizers_present:
|
|
592
|
+
# No destabilizers = no divergence risk
|
|
593
|
+
return True, "U2: not applicable (no destabilizers present)"
|
|
594
|
+
|
|
595
|
+
# Check for stabilizers
|
|
596
|
+
stabilizers_present = [
|
|
597
|
+
getattr(op, "canonical_name", op.name.lower())
|
|
598
|
+
for op in sequence
|
|
599
|
+
if getattr(op, "canonical_name", op.name.lower()) in STABILIZERS
|
|
600
|
+
]
|
|
601
|
+
|
|
602
|
+
if not stabilizers_present:
|
|
603
|
+
return (
|
|
604
|
+
False,
|
|
605
|
+
f"U2 violated: destabilizers {destabilizers_present} present "
|
|
606
|
+
f"without stabilizer. Integral ∫νf·ΔNFR dt may diverge. "
|
|
607
|
+
f"Add: {sorted(STABILIZERS)}",
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
return (
|
|
611
|
+
True,
|
|
612
|
+
f"U2 satisfied: stabilizers {stabilizers_present} "
|
|
613
|
+
f"bound destabilizers {destabilizers_present}",
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
@staticmethod
|
|
617
|
+
def validate_resonant_coupling(sequence: List[Operator]) -> tuple[bool, str]:
|
|
618
|
+
"""Validate U3: Resonant coupling.
|
|
619
|
+
|
|
620
|
+
Physical basis: AGENTS.md Invariant #5 states "no coupling is valid
|
|
621
|
+
without explicit phase verification (synchrony)".
|
|
622
|
+
|
|
623
|
+
Resonance physics requires phase compatibility:
|
|
624
|
+
|φᵢ - φⱼ| ≤ Δφ_max
|
|
625
|
+
|
|
626
|
+
Without phase verification:
|
|
627
|
+
Nodes with incompatible phases (antiphase) could attempt coupling
|
|
628
|
+
→ Destructive interference → Violates resonance physics
|
|
629
|
+
|
|
630
|
+
With phase verification:
|
|
631
|
+
Only synchronous nodes couple → Constructive interference
|
|
632
|
+
|
|
633
|
+
Parameters
|
|
634
|
+
----------
|
|
635
|
+
sequence : List[Operator]
|
|
636
|
+
Sequence of operators to validate
|
|
637
|
+
|
|
638
|
+
Returns
|
|
639
|
+
-------
|
|
640
|
+
tuple[bool, str]
|
|
641
|
+
(is_valid, message)
|
|
642
|
+
|
|
643
|
+
Notes
|
|
644
|
+
-----
|
|
645
|
+
U3 is a META-rule: it requires that when UM (Coupling) or RA (Resonance)
|
|
646
|
+
operators are used, the implementation MUST verify phase compatibility.
|
|
647
|
+
The actual phase check happens in operator preconditions.
|
|
648
|
+
|
|
649
|
+
This grammar rule documents the requirement and ensures awareness
|
|
650
|
+
that phase checks are MANDATORY (Invariant #5), not optional.
|
|
651
|
+
"""
|
|
652
|
+
# Check if sequence contains coupling/resonance operators
|
|
653
|
+
coupling_ops = [
|
|
654
|
+
getattr(op, "canonical_name", op.name.lower())
|
|
655
|
+
for op in sequence
|
|
656
|
+
if getattr(op, "canonical_name", op.name.lower()) in COUPLING_RESONANCE
|
|
657
|
+
]
|
|
658
|
+
|
|
659
|
+
if not coupling_ops:
|
|
660
|
+
# No coupling/resonance = U3 not applicable
|
|
661
|
+
return True, "U3: not applicable (no coupling/resonance operators)"
|
|
662
|
+
|
|
663
|
+
# U3 satisfied: Sequence contains coupling/resonance
|
|
664
|
+
# Phase verification is MANDATORY per Invariant #5
|
|
665
|
+
# Actual check happens in operator preconditions
|
|
666
|
+
return (
|
|
667
|
+
True,
|
|
668
|
+
f"U3 awareness: operators {coupling_ops} require phase verification "
|
|
669
|
+
f"(MANDATORY per Invariant #5). Enforced in preconditions.",
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
@staticmethod
|
|
673
|
+
def validate_bifurcation_triggers(sequence: List[Operator]) -> tuple[bool, str]:
|
|
674
|
+
"""Validate U4a: Bifurcation triggers need handlers.
|
|
675
|
+
|
|
676
|
+
Physical basis: AGENTS.md Contract OZ states dissonance may trigger
|
|
677
|
+
bifurcation if ∂²EPI/∂t² > τ. When bifurcation is triggered, handlers
|
|
678
|
+
are required to manage structural reorganization.
|
|
679
|
+
|
|
680
|
+
Bifurcation physics:
|
|
681
|
+
If ∂²EPI/∂t² > τ → multiple reorganization paths viable
|
|
682
|
+
→ System enters bifurcation regime
|
|
683
|
+
→ Requires handlers (THOL or IL) for stable transition
|
|
684
|
+
|
|
685
|
+
Parameters
|
|
686
|
+
----------
|
|
687
|
+
sequence : List[Operator]
|
|
688
|
+
Sequence of operators to validate
|
|
689
|
+
|
|
690
|
+
Returns
|
|
691
|
+
-------
|
|
692
|
+
tuple[bool, str]
|
|
693
|
+
(is_valid, message)
|
|
694
|
+
"""
|
|
695
|
+
# Check if sequence contains bifurcation triggers
|
|
696
|
+
trigger_ops = [
|
|
697
|
+
getattr(op, "canonical_name", op.name.lower())
|
|
698
|
+
for op in sequence
|
|
699
|
+
if getattr(op, "canonical_name", op.name.lower()) in BIFURCATION_TRIGGERS
|
|
700
|
+
]
|
|
701
|
+
|
|
702
|
+
if not trigger_ops:
|
|
703
|
+
# No triggers = U4a not applicable
|
|
704
|
+
return True, "U4a: not applicable (no bifurcation triggers)"
|
|
705
|
+
|
|
706
|
+
# Check for handlers
|
|
707
|
+
handler_ops = [
|
|
708
|
+
getattr(op, "canonical_name", op.name.lower())
|
|
709
|
+
for op in sequence
|
|
710
|
+
if getattr(op, "canonical_name", op.name.lower()) in BIFURCATION_HANDLERS
|
|
711
|
+
]
|
|
712
|
+
|
|
713
|
+
if not handler_ops:
|
|
714
|
+
return (
|
|
715
|
+
False,
|
|
716
|
+
f"U4a violated: bifurcation triggers {trigger_ops} present "
|
|
717
|
+
f"without handler. If ∂²EPI/∂t² > τ, bifurcation may occur unmanaged. "
|
|
718
|
+
f"Add: {sorted(BIFURCATION_HANDLERS)}",
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
return (
|
|
722
|
+
True,
|
|
723
|
+
f"U4a satisfied: bifurcation triggers {trigger_ops} "
|
|
724
|
+
f"have handlers {handler_ops}",
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
@staticmethod
|
|
728
|
+
def validate_transformer_context(sequence: List[Operator]) -> tuple[bool, str]:
|
|
729
|
+
"""Validate U4b: Transformers need context.
|
|
730
|
+
|
|
731
|
+
Physical basis: Bifurcations require threshold energy to cross
|
|
732
|
+
critical points. Transformers (ZHIR, THOL) need recent destabilizers
|
|
733
|
+
to provide sufficient |ΔNFR| for phase transitions.
|
|
734
|
+
|
|
735
|
+
ZHIR (Mutation) requirements:
|
|
736
|
+
1. Prior IL: Stable base prevents transformation from chaos
|
|
737
|
+
2. Recent destabilizer: Threshold energy for bifurcation
|
|
738
|
+
|
|
739
|
+
THOL (Self-organization) requirements:
|
|
740
|
+
1. Recent destabilizer: Disorder to self-organize
|
|
741
|
+
|
|
742
|
+
"Recent" = within ~3 operators (ΔNFR decays via structural relaxation)
|
|
743
|
+
|
|
744
|
+
Parameters
|
|
745
|
+
----------
|
|
746
|
+
sequence : List[Operator]
|
|
747
|
+
Sequence of operators to validate
|
|
748
|
+
|
|
749
|
+
Returns
|
|
750
|
+
-------
|
|
751
|
+
tuple[bool, str]
|
|
752
|
+
(is_valid, message)
|
|
753
|
+
|
|
754
|
+
Notes
|
|
755
|
+
-----
|
|
756
|
+
This implements "graduated destabilization" - transformers need
|
|
757
|
+
sufficient ΔNFR context. The ~3 operator window captures when
|
|
758
|
+
|ΔNFR| remains above bifurcation threshold.
|
|
759
|
+
"""
|
|
760
|
+
# Check if sequence contains transformers
|
|
761
|
+
transformer_ops = []
|
|
762
|
+
for i, op in enumerate(sequence):
|
|
763
|
+
op_name = getattr(op, "canonical_name", op.name.lower())
|
|
764
|
+
if op_name in TRANSFORMERS:
|
|
765
|
+
transformer_ops.append((i, op_name))
|
|
766
|
+
|
|
767
|
+
if not transformer_ops:
|
|
768
|
+
return True, "U4b: not applicable (no transformers)"
|
|
769
|
+
|
|
770
|
+
# For each transformer, check context
|
|
771
|
+
violations = []
|
|
772
|
+
for idx, transformer_name in transformer_ops:
|
|
773
|
+
# Check for recent destabilizer (within 3 operators before)
|
|
774
|
+
window_start = max(0, idx - 3)
|
|
775
|
+
recent_destabilizers = []
|
|
776
|
+
prior_il = False
|
|
777
|
+
|
|
778
|
+
for j in range(window_start, idx):
|
|
779
|
+
op_name = getattr(
|
|
780
|
+
sequence[j], "canonical_name", sequence[j].name.lower()
|
|
781
|
+
)
|
|
782
|
+
if op_name in DESTABILIZERS:
|
|
783
|
+
recent_destabilizers.append((j, op_name))
|
|
784
|
+
if op_name == "coherence":
|
|
785
|
+
prior_il = True
|
|
786
|
+
|
|
787
|
+
# Check requirements
|
|
788
|
+
if not recent_destabilizers:
|
|
789
|
+
violations.append(
|
|
790
|
+
f"{transformer_name} at position {idx} lacks recent destabilizer "
|
|
791
|
+
f"(none in window [{window_start}:{idx}]). "
|
|
792
|
+
f"Need: {sorted(DESTABILIZERS)}"
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
# Additional requirement for ZHIR: prior IL
|
|
796
|
+
if transformer_name == "mutation" and not prior_il:
|
|
797
|
+
violations.append(
|
|
798
|
+
f"mutation at position {idx} lacks prior IL (coherence) "
|
|
799
|
+
f"for stable transformation base"
|
|
800
|
+
)
|
|
801
|
+
|
|
802
|
+
if violations:
|
|
803
|
+
return (False, f"U4b violated: {'; '.join(violations)}")
|
|
804
|
+
|
|
805
|
+
return (True, f"U4b satisfied: transformers have proper context")
|
|
806
|
+
|
|
807
|
+
@staticmethod
|
|
808
|
+
def validate_remesh_amplification(sequence: List[Operator]) -> tuple[bool, str]:
|
|
809
|
+
"""Validate U2-REMESH: Recursive amplification control.
|
|
810
|
+
|
|
811
|
+
Physical basis: REMESH implements temporal coupling EPI(t) ↔ EPI(t-τ)
|
|
812
|
+
which creates feedback that amplifies structural changes. When combined
|
|
813
|
+
with destabilizers, this can cause unbounded growth.
|
|
814
|
+
|
|
815
|
+
From integrated nodal equation:
|
|
816
|
+
EPI(t_f) = EPI(t_0) + ∫_{t_0}^{t_f} νf·ΔNFR dτ
|
|
817
|
+
|
|
818
|
+
REMESH temporal mixing:
|
|
819
|
+
EPI_mixed = (1-α)·EPI_now + α·EPI_past
|
|
820
|
+
|
|
821
|
+
Without stabilizers:
|
|
822
|
+
REMESH + destabilizers → recursive amplification
|
|
823
|
+
→ ∫ νf·ΔNFR dt → ∞ (feedback loop)
|
|
824
|
+
→ System fragments
|
|
825
|
+
|
|
826
|
+
With stabilizers:
|
|
827
|
+
IL or THOL provides negative feedback
|
|
828
|
+
→ Bounded recursive evolution
|
|
829
|
+
→ ∫ νf·ΔNFR dt < ∞
|
|
830
|
+
|
|
831
|
+
Specific combinations requiring stabilizers:
|
|
832
|
+
- REMESH + VAL: Recursive expansion needs coherence stabilization
|
|
833
|
+
- REMESH + OZ: Recursive bifurcation needs self-organization handlers
|
|
834
|
+
- REMESH + ZHIR: Replicative mutation needs coherence consolidation
|
|
835
|
+
|
|
836
|
+
Parameters
|
|
837
|
+
----------
|
|
838
|
+
sequence : List[Operator]
|
|
839
|
+
Sequence of operators to validate
|
|
840
|
+
|
|
841
|
+
Returns
|
|
842
|
+
-------
|
|
843
|
+
tuple[bool, str]
|
|
844
|
+
(is_valid, message)
|
|
845
|
+
|
|
846
|
+
Notes
|
|
847
|
+
-----
|
|
848
|
+
This rule is DISTINCT from general U2 (convergence). While U2 checks
|
|
849
|
+
for destabilizers needing stabilizers, U2-REMESH specifically addresses
|
|
850
|
+
REMESH's amplification property: it multiplies the effect of destabilizers
|
|
851
|
+
through recursive feedback across temporal/spatial scales.
|
|
852
|
+
|
|
853
|
+
Physical derivation: See src/tnfr/operators/remesh.py module docstring,
|
|
854
|
+
section "Grammar Implications from Physical Analysis" → U2: CONVERGENCE.
|
|
855
|
+
"""
|
|
856
|
+
# Check if sequence contains REMESH
|
|
857
|
+
has_remesh = any(
|
|
858
|
+
getattr(op, "canonical_name", op.name.lower()) == "recursivity"
|
|
859
|
+
for op in sequence
|
|
860
|
+
)
|
|
861
|
+
|
|
862
|
+
if not has_remesh:
|
|
863
|
+
return True, "U2-REMESH: not applicable (no recursivity present)"
|
|
864
|
+
|
|
865
|
+
# Check for destabilizers
|
|
866
|
+
destabilizers_present = [
|
|
867
|
+
getattr(op, "canonical_name", op.name.lower())
|
|
868
|
+
for op in sequence
|
|
869
|
+
if getattr(op, "canonical_name", op.name.lower()) in DESTABILIZERS
|
|
870
|
+
]
|
|
871
|
+
|
|
872
|
+
if not destabilizers_present:
|
|
873
|
+
return True, "U2-REMESH: satisfied (no destabilizers to amplify)"
|
|
874
|
+
|
|
875
|
+
# Check for stabilizers
|
|
876
|
+
stabilizers_present = [
|
|
877
|
+
getattr(op, "canonical_name", op.name.lower())
|
|
878
|
+
for op in sequence
|
|
879
|
+
if getattr(op, "canonical_name", op.name.lower()) in STABILIZERS
|
|
880
|
+
]
|
|
881
|
+
|
|
882
|
+
if not stabilizers_present:
|
|
883
|
+
return (
|
|
884
|
+
False,
|
|
885
|
+
f"U2-REMESH violated: recursivity amplifies destabilizers "
|
|
886
|
+
f"{destabilizers_present} via recursive feedback. "
|
|
887
|
+
f"Integral ∫νf·ΔNFR dt may diverge (unbounded growth). "
|
|
888
|
+
f"Required: {sorted(STABILIZERS)} to bound recursive amplification",
|
|
889
|
+
)
|
|
890
|
+
|
|
891
|
+
return (
|
|
892
|
+
True,
|
|
893
|
+
f"U2-REMESH satisfied: stabilizers {stabilizers_present} "
|
|
894
|
+
f"bound recursive amplification of {destabilizers_present}",
|
|
895
|
+
)
|
|
896
|
+
|
|
897
|
+
@classmethod
|
|
898
|
+
def validate(
|
|
899
|
+
cls,
|
|
900
|
+
sequence: List[Operator],
|
|
901
|
+
epi_initial: float = 0.0,
|
|
902
|
+
) -> tuple[bool, List[str]]:
|
|
903
|
+
"""Validate sequence using all unified canonical constraints.
|
|
904
|
+
|
|
905
|
+
This validates pure TNFR physics:
|
|
906
|
+
- U1: Structural initiation & closure
|
|
907
|
+
- U2: Convergence & boundedness
|
|
908
|
+
- U3: Resonant coupling
|
|
909
|
+
- U4: Bifurcation dynamics
|
|
910
|
+
|
|
911
|
+
Parameters
|
|
912
|
+
----------
|
|
913
|
+
sequence : List[Operator]
|
|
914
|
+
Sequence to validate
|
|
915
|
+
epi_initial : float, optional
|
|
916
|
+
Initial EPI value (default: 0.0)
|
|
917
|
+
|
|
918
|
+
Returns
|
|
919
|
+
-------
|
|
920
|
+
tuple[bool, List[str]]
|
|
921
|
+
(is_valid, messages)
|
|
922
|
+
is_valid: True if all constraints satisfied
|
|
923
|
+
messages: List of validation messages
|
|
924
|
+
"""
|
|
925
|
+
messages = []
|
|
926
|
+
all_valid = True
|
|
927
|
+
|
|
928
|
+
# U1a: Initiation
|
|
929
|
+
valid_init, msg_init = cls.validate_initiation(sequence, epi_initial)
|
|
930
|
+
messages.append(f"U1a: {msg_init}")
|
|
931
|
+
all_valid = all_valid and valid_init
|
|
932
|
+
|
|
933
|
+
# U1b: Closure
|
|
934
|
+
valid_closure, msg_closure = cls.validate_closure(sequence)
|
|
935
|
+
messages.append(f"U1b: {msg_closure}")
|
|
936
|
+
all_valid = all_valid and valid_closure
|
|
937
|
+
|
|
938
|
+
# U2: Convergence
|
|
939
|
+
valid_conv, msg_conv = cls.validate_convergence(sequence)
|
|
940
|
+
messages.append(f"U2: {msg_conv}")
|
|
941
|
+
all_valid = all_valid and valid_conv
|
|
942
|
+
|
|
943
|
+
# U3: Resonant coupling
|
|
944
|
+
valid_coupling, msg_coupling = cls.validate_resonant_coupling(sequence)
|
|
945
|
+
messages.append(f"U3: {msg_coupling}")
|
|
946
|
+
all_valid = all_valid and valid_coupling
|
|
947
|
+
|
|
948
|
+
# U4a: Bifurcation triggers
|
|
949
|
+
valid_triggers, msg_triggers = cls.validate_bifurcation_triggers(sequence)
|
|
950
|
+
messages.append(f"U4a: {msg_triggers}")
|
|
951
|
+
all_valid = all_valid and valid_triggers
|
|
952
|
+
|
|
953
|
+
# U4b: Transformer context
|
|
954
|
+
valid_context, msg_context = cls.validate_transformer_context(sequence)
|
|
955
|
+
messages.append(f"U4b: {msg_context}")
|
|
956
|
+
all_valid = all_valid and valid_context
|
|
957
|
+
|
|
958
|
+
# U2-REMESH: Recursive amplification control
|
|
959
|
+
valid_remesh, msg_remesh = cls.validate_remesh_amplification(sequence)
|
|
960
|
+
messages.append(f"U2-REMESH: {msg_remesh}")
|
|
961
|
+
all_valid = all_valid and valid_remesh
|
|
962
|
+
|
|
963
|
+
return all_valid, messages
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
def validate_grammar(
|
|
967
|
+
sequence: List[Operator],
|
|
968
|
+
epi_initial: float = 0.0,
|
|
969
|
+
) -> bool:
|
|
970
|
+
"""Validate sequence using canonical TNFR grammar constraints.
|
|
971
|
+
|
|
972
|
+
Convenience function that returns only boolean result.
|
|
973
|
+
For detailed messages, use GrammarValidator.validate().
|
|
974
|
+
|
|
975
|
+
Parameters
|
|
976
|
+
----------
|
|
977
|
+
sequence : List[Operator]
|
|
978
|
+
Sequence of operators to validate
|
|
979
|
+
epi_initial : float, optional
|
|
980
|
+
Initial EPI value (default: 0.0)
|
|
981
|
+
|
|
982
|
+
Returns
|
|
983
|
+
-------
|
|
984
|
+
bool
|
|
985
|
+
True if sequence satisfies all canonical constraints
|
|
986
|
+
|
|
987
|
+
Examples
|
|
988
|
+
--------
|
|
989
|
+
>>> from tnfr.operators.definitions import Emission, Coherence, Silence
|
|
990
|
+
>>> ops = [Emission(), Coherence(), Silence()]
|
|
991
|
+
>>> validate_grammar(ops, epi_initial=0.0) # doctest: +SKIP
|
|
992
|
+
True
|
|
993
|
+
|
|
994
|
+
Notes
|
|
995
|
+
-----
|
|
996
|
+
This validator is 100% physics-based. All constraints emerge from:
|
|
997
|
+
- Nodal equation: ∂EPI/∂t = νf · ΔNFR(t)
|
|
998
|
+
- TNFR invariants (AGENTS.md §3)
|
|
999
|
+
- Formal operator contracts (AGENTS.md §4)
|
|
1000
|
+
|
|
1001
|
+
See UNIFIED_GRAMMAR_RULES.md for complete derivations.
|
|
1002
|
+
"""
|
|
1003
|
+
is_valid, _ = GrammarValidator.validate(sequence, epi_initial)
|
|
1004
|
+
return is_valid
|
|
1005
|
+
|
|
1006
|
+
|
|
1007
|
+
# ============================================================================
|
|
1008
|
+
# Grammar Application Functions (Minimal Stubs for Import Compatibility)
|
|
1009
|
+
# ============================================================================
|
|
1010
|
+
|
|
1011
|
+
|
|
1012
|
+
def apply_glyph_with_grammar(
|
|
1013
|
+
G: "TNFRGraph",
|
|
1014
|
+
nodes: Any,
|
|
1015
|
+
glyph: Any,
|
|
1016
|
+
window: Any = None,
|
|
1017
|
+
) -> None:
|
|
1018
|
+
"""Apply glyph to nodes with grammar validation.
|
|
1019
|
+
|
|
1020
|
+
Applies the specified glyph to each node in the iterable using the canonical
|
|
1021
|
+
TNFR operator implementation.
|
|
1022
|
+
|
|
1023
|
+
Parameters
|
|
1024
|
+
----------
|
|
1025
|
+
G : TNFRGraph
|
|
1026
|
+
Graph containing nodes
|
|
1027
|
+
nodes : Any
|
|
1028
|
+
Node, list of nodes, or node iterable to apply glyph to
|
|
1029
|
+
glyph : Any
|
|
1030
|
+
Glyph to apply
|
|
1031
|
+
window : Any, optional
|
|
1032
|
+
Grammar window constraint
|
|
1033
|
+
|
|
1034
|
+
Notes
|
|
1035
|
+
-----
|
|
1036
|
+
This function delegates to apply_glyph for each node, which wraps
|
|
1037
|
+
the node in NodeNX and applies the glyph operation.
|
|
1038
|
+
"""
|
|
1039
|
+
from . import apply_glyph
|
|
1040
|
+
|
|
1041
|
+
# Handle single node or iterable of nodes
|
|
1042
|
+
# Check if it's a single hashable node or an iterable
|
|
1043
|
+
try:
|
|
1044
|
+
# Try to treat as single hashable node
|
|
1045
|
+
hash(nodes)
|
|
1046
|
+
# If hashable, it's a single node
|
|
1047
|
+
nodes_iter = [nodes]
|
|
1048
|
+
except (TypeError, AttributeError):
|
|
1049
|
+
# Not hashable, treat as iterable
|
|
1050
|
+
# Convert to list to allow multiple iterations if needed
|
|
1051
|
+
try:
|
|
1052
|
+
nodes_iter = list(nodes)
|
|
1053
|
+
except TypeError:
|
|
1054
|
+
# If not iterable, wrap in list
|
|
1055
|
+
nodes_iter = [nodes]
|
|
1056
|
+
|
|
1057
|
+
for node in nodes_iter:
|
|
1058
|
+
apply_glyph(G, node, glyph, window=window)
|
|
1059
|
+
|
|
1060
|
+
|
|
1061
|
+
def on_applied_glyph(G: "TNFRGraph", n: "NodeId", applied: Any) -> None:
|
|
1062
|
+
"""Record glyph application in node history.
|
|
1063
|
+
|
|
1064
|
+
Minimal stub for tracking operator sequences.
|
|
1065
|
+
|
|
1066
|
+
Parameters
|
|
1067
|
+
----------
|
|
1068
|
+
G : TNFRGraph
|
|
1069
|
+
Graph containing node
|
|
1070
|
+
n : NodeId
|
|
1071
|
+
Node identifier
|
|
1072
|
+
applied : Any
|
|
1073
|
+
Applied glyph or operator name
|
|
1074
|
+
"""
|
|
1075
|
+
# Minimal stub for telemetry
|
|
1076
|
+
if "glyph_history" not in G.nodes[n]:
|
|
1077
|
+
G.nodes[n]["glyph_history"] = []
|
|
1078
|
+
G.nodes[n]["glyph_history"].append(applied)
|
|
1079
|
+
|
|
1080
|
+
|
|
1081
|
+
def enforce_canonical_grammar(
|
|
1082
|
+
G: "TNFRGraph",
|
|
1083
|
+
n: "NodeId",
|
|
1084
|
+
cand: Any,
|
|
1085
|
+
ctx: Any = None,
|
|
1086
|
+
) -> Any:
|
|
1087
|
+
"""Enforce canonical grammar constraints before operator application.
|
|
1088
|
+
|
|
1089
|
+
Minimal stub implementation.
|
|
1090
|
+
|
|
1091
|
+
Parameters
|
|
1092
|
+
----------
|
|
1093
|
+
G : TNFRGraph
|
|
1094
|
+
Graph containing node
|
|
1095
|
+
n : NodeId
|
|
1096
|
+
Node identifier
|
|
1097
|
+
cand : Any
|
|
1098
|
+
Candidate glyph/operator
|
|
1099
|
+
ctx : Any, optional
|
|
1100
|
+
Grammar context
|
|
1101
|
+
|
|
1102
|
+
Returns
|
|
1103
|
+
-------
|
|
1104
|
+
Any
|
|
1105
|
+
Validated glyph/operator
|
|
1106
|
+
"""
|
|
1107
|
+
# Minimal stub - return candidate as-is
|
|
1108
|
+
return cand
|
|
1109
|
+
|
|
1110
|
+
|
|
1111
|
+
def validate_sequence(
|
|
1112
|
+
names: Any = None,
|
|
1113
|
+
**kwargs: Any,
|
|
1114
|
+
) -> Any:
|
|
1115
|
+
"""Validate sequence of operator names.
|
|
1116
|
+
|
|
1117
|
+
Minimal stub implementation for import compatibility.
|
|
1118
|
+
|
|
1119
|
+
Parameters
|
|
1120
|
+
----------
|
|
1121
|
+
names : Iterable[str] | object, optional
|
|
1122
|
+
Sequence of operator names
|
|
1123
|
+
**kwargs : Any
|
|
1124
|
+
Additional validation options
|
|
1125
|
+
|
|
1126
|
+
Returns
|
|
1127
|
+
-------
|
|
1128
|
+
ValidationOutcome
|
|
1129
|
+
Validation result (stub returns success)
|
|
1130
|
+
"""
|
|
1131
|
+
# Minimal stub - return success
|
|
1132
|
+
class ValidationStub:
|
|
1133
|
+
def __init__(self):
|
|
1134
|
+
self.passed = True
|
|
1135
|
+
self.message = "Validation stub"
|
|
1136
|
+
self.metadata = {}
|
|
1137
|
+
return ValidationStub()
|
|
1138
|
+
|
|
1139
|
+
|
|
1140
|
+
def parse_sequence(names: Any) -> Any:
|
|
1141
|
+
"""Parse sequence of operator names.
|
|
1142
|
+
|
|
1143
|
+
Minimal stub implementation.
|
|
1144
|
+
|
|
1145
|
+
Parameters
|
|
1146
|
+
----------
|
|
1147
|
+
names : Iterable[str]
|
|
1148
|
+
Sequence of operator names
|
|
1149
|
+
|
|
1150
|
+
Returns
|
|
1151
|
+
-------
|
|
1152
|
+
SequenceValidationResult
|
|
1153
|
+
Parse result (stub)
|
|
1154
|
+
"""
|
|
1155
|
+
# Minimal stub
|
|
1156
|
+
class ParseStub:
|
|
1157
|
+
def __init__(self):
|
|
1158
|
+
self.tokens = list(names) if names else []
|
|
1159
|
+
self.canonical_tokens = self.tokens
|
|
1160
|
+
self.passed = True
|
|
1161
|
+
self.message = "Parse stub"
|
|
1162
|
+
self.metadata = {}
|
|
1163
|
+
self.error = None
|
|
1164
|
+
return ParseStub()
|