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,492 @@
|
|
|
1
|
+
"""Contextual error handling for TNFR operations.
|
|
2
|
+
|
|
3
|
+
This module provides enhanced error messages that guide users to solutions
|
|
4
|
+
while maintaining TNFR theoretical compliance. All errors include:
|
|
5
|
+
|
|
6
|
+
1. Clear explanation of the violation
|
|
7
|
+
2. Actionable suggestions for resolution
|
|
8
|
+
3. Links to relevant documentation
|
|
9
|
+
4. Context about the structural operation that failed
|
|
10
|
+
|
|
11
|
+
Canonical Invariants Preserved
|
|
12
|
+
------------------------------
|
|
13
|
+
These errors enforce TNFR invariants from AGENTS.md:
|
|
14
|
+
- Operator closure and sequence validity
|
|
15
|
+
- Phase synchrony requirements for coupling
|
|
16
|
+
- Frequency (νf) bounds in Hz_str units
|
|
17
|
+
- ΔNFR semantic correctness
|
|
18
|
+
- EPI coherence preservation
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from difflib import get_close_matches
|
|
24
|
+
from typing import Optional, List, Dict, Any
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"TNFRUserError",
|
|
28
|
+
"OperatorSequenceError",
|
|
29
|
+
"NetworkConfigError",
|
|
30
|
+
"PhaseError",
|
|
31
|
+
"CoherenceError",
|
|
32
|
+
"FrequencyError",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class TNFRUserError(Exception):
|
|
37
|
+
"""Base class for user-facing TNFR errors with helpful context.
|
|
38
|
+
|
|
39
|
+
All TNFR errors inherit from this class and provide:
|
|
40
|
+
- Human-readable error messages
|
|
41
|
+
- Actionable suggestions
|
|
42
|
+
- Documentation links
|
|
43
|
+
- Structural context
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
message : str
|
|
48
|
+
Primary error message describing what went wrong.
|
|
49
|
+
suggestion : str, optional
|
|
50
|
+
Specific suggestion for how to fix the issue.
|
|
51
|
+
docs_url : str, optional
|
|
52
|
+
URL to relevant documentation section.
|
|
53
|
+
context : dict, optional
|
|
54
|
+
Additional context about the failed operation (node IDs, values, etc).
|
|
55
|
+
|
|
56
|
+
Examples
|
|
57
|
+
--------
|
|
58
|
+
>>> raise TNFRUserError(
|
|
59
|
+
... "Invalid structural frequency",
|
|
60
|
+
... suggestion="νf must be positive in Hz_str units",
|
|
61
|
+
... docs_url="https://tnfr.readthedocs.io/api/core.html#frequency"
|
|
62
|
+
... )
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def __init__(
|
|
66
|
+
self,
|
|
67
|
+
message: str,
|
|
68
|
+
suggestion: Optional[str] = None,
|
|
69
|
+
docs_url: Optional[str] = None,
|
|
70
|
+
context: Optional[Dict[str, Any]] = None,
|
|
71
|
+
):
|
|
72
|
+
self.message = message
|
|
73
|
+
self.suggestion = suggestion
|
|
74
|
+
self.docs_url = docs_url
|
|
75
|
+
self.context = context or {}
|
|
76
|
+
|
|
77
|
+
# Build comprehensive error message
|
|
78
|
+
full_message = f"\n{'='*70}\n"
|
|
79
|
+
full_message += f"TNFR Error: {message}\n"
|
|
80
|
+
full_message += f"{'='*70}\n"
|
|
81
|
+
|
|
82
|
+
if suggestion:
|
|
83
|
+
full_message += f"\n💡 Suggestion: {suggestion}\n"
|
|
84
|
+
|
|
85
|
+
if context:
|
|
86
|
+
full_message += f"\n📊 Context:\n"
|
|
87
|
+
for key, value in context.items():
|
|
88
|
+
full_message += f" • {key}: {value}\n"
|
|
89
|
+
|
|
90
|
+
if docs_url:
|
|
91
|
+
full_message += f"\n📚 Documentation: {docs_url}\n"
|
|
92
|
+
|
|
93
|
+
full_message += f"{'='*70}\n"
|
|
94
|
+
|
|
95
|
+
super().__init__(full_message)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class OperatorSequenceError(TNFRUserError):
|
|
99
|
+
"""Error raised when operator sequence violates TNFR grammar.
|
|
100
|
+
|
|
101
|
+
TNFR operators must be applied in valid sequences that respect
|
|
102
|
+
structural coherence. This error provides:
|
|
103
|
+
- The invalid sequence attempted
|
|
104
|
+
- Which operator violated the grammar
|
|
105
|
+
- Valid next operators
|
|
106
|
+
- Fuzzy matching for typos
|
|
107
|
+
|
|
108
|
+
Enforces Invariant #4: Operator closure from AGENTS.md
|
|
109
|
+
|
|
110
|
+
Parameters
|
|
111
|
+
----------
|
|
112
|
+
invalid_operator : str
|
|
113
|
+
The operator that violated the grammar.
|
|
114
|
+
sequence_so_far : list of str
|
|
115
|
+
Operators successfully applied before the error.
|
|
116
|
+
valid_next : list of str, optional
|
|
117
|
+
Valid operators that can follow the current sequence.
|
|
118
|
+
|
|
119
|
+
Examples
|
|
120
|
+
--------
|
|
121
|
+
>>> raise OperatorSequenceError(
|
|
122
|
+
... "emision",
|
|
123
|
+
... ["reception", "coherence"],
|
|
124
|
+
... ["emission", "recursivity"]
|
|
125
|
+
... )
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
# Valid TNFR operators (13 canonical operators)
|
|
129
|
+
VALID_OPERATORS = {
|
|
130
|
+
"emission",
|
|
131
|
+
"reception",
|
|
132
|
+
"coherence",
|
|
133
|
+
"dissonance",
|
|
134
|
+
"coupling",
|
|
135
|
+
"resonance",
|
|
136
|
+
"silence",
|
|
137
|
+
"expansion",
|
|
138
|
+
"contraction",
|
|
139
|
+
"self_organization",
|
|
140
|
+
"mutation",
|
|
141
|
+
"transition",
|
|
142
|
+
"recursivity",
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
# Operator aliases for user convenience
|
|
146
|
+
OPERATOR_ALIASES = {
|
|
147
|
+
"emit": "emission",
|
|
148
|
+
"receive": "reception",
|
|
149
|
+
"cohere": "coherence",
|
|
150
|
+
"couple": "coupling",
|
|
151
|
+
"resonate": "resonance",
|
|
152
|
+
"silent": "silence",
|
|
153
|
+
"expand": "expansion",
|
|
154
|
+
"contract": "contraction",
|
|
155
|
+
"self_organize": "self_organization",
|
|
156
|
+
"mutate": "mutation",
|
|
157
|
+
"recurse": "recursivity",
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
def __init__(
|
|
161
|
+
self,
|
|
162
|
+
invalid_operator: str,
|
|
163
|
+
sequence_so_far: Optional[List[str]] = None,
|
|
164
|
+
valid_next: Optional[List[str]] = None,
|
|
165
|
+
):
|
|
166
|
+
sequence_so_far = sequence_so_far or []
|
|
167
|
+
|
|
168
|
+
# Try fuzzy matching for typos
|
|
169
|
+
all_valid = list(self.VALID_OPERATORS) + list(self.OPERATOR_ALIASES.keys())
|
|
170
|
+
matches = get_close_matches(invalid_operator, all_valid, n=3, cutoff=0.6)
|
|
171
|
+
|
|
172
|
+
suggestion_parts = []
|
|
173
|
+
if matches:
|
|
174
|
+
suggestion_parts.append(f"Did you mean one of: {', '.join(matches)}?")
|
|
175
|
+
|
|
176
|
+
if valid_next:
|
|
177
|
+
suggestion_parts.append(f"Valid next operators: {', '.join(valid_next)}")
|
|
178
|
+
else:
|
|
179
|
+
suggestion_parts.append(
|
|
180
|
+
f"Use one of the 13 canonical operators: "
|
|
181
|
+
f"{', '.join(sorted(self.VALID_OPERATORS))}"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
suggestion = " ".join(suggestion_parts) if suggestion_parts else None
|
|
185
|
+
|
|
186
|
+
context = {
|
|
187
|
+
"invalid_operator": invalid_operator,
|
|
188
|
+
"sequence_so_far": (
|
|
189
|
+
" → ".join(sequence_so_far) if sequence_so_far else "empty"
|
|
190
|
+
),
|
|
191
|
+
"operator_count": len(sequence_so_far),
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
super().__init__(
|
|
195
|
+
message=f"Invalid operator sequence: '{invalid_operator}' cannot be applied",
|
|
196
|
+
suggestion=suggestion,
|
|
197
|
+
docs_url="https://github.com/fermga/Teoria-de-la-naturaleza-fractal-resonante-TNFR-/blob/main/docs/source/api/operators.md",
|
|
198
|
+
context=context,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class NetworkConfigError(TNFRUserError):
|
|
203
|
+
"""Error raised when network configuration violates TNFR constraints.
|
|
204
|
+
|
|
205
|
+
This error validates configuration parameters and provides valid ranges
|
|
206
|
+
with physical/structural meaning.
|
|
207
|
+
|
|
208
|
+
Enforces multiple invariants:
|
|
209
|
+
- Invariant #2: Structural units (νf in Hz_str)
|
|
210
|
+
- Invariant #5: Phase check requirements
|
|
211
|
+
- Invariant #6: Node birth/collapse conditions
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
parameter : str
|
|
216
|
+
The configuration parameter that is invalid.
|
|
217
|
+
value : any
|
|
218
|
+
The invalid value provided.
|
|
219
|
+
valid_range : tuple, optional
|
|
220
|
+
Valid range for the parameter (min, max).
|
|
221
|
+
reason : str, optional
|
|
222
|
+
Structural reason for the constraint.
|
|
223
|
+
|
|
224
|
+
Examples
|
|
225
|
+
--------
|
|
226
|
+
>>> raise NetworkConfigError(
|
|
227
|
+
... "vf",
|
|
228
|
+
... -0.5,
|
|
229
|
+
... (0.01, 100.0),
|
|
230
|
+
... "Structural frequency must be positive (Hz_str units)"
|
|
231
|
+
... )
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
# Valid parameter ranges with structural meaning
|
|
235
|
+
PARAMETER_CONSTRAINTS = {
|
|
236
|
+
"vf": {
|
|
237
|
+
"range": (0.01, 100.0),
|
|
238
|
+
"unit": "Hz_str",
|
|
239
|
+
"description": "Structural frequency (reorganization rate)",
|
|
240
|
+
},
|
|
241
|
+
"phase": {
|
|
242
|
+
"range": (0.0, 2 * 3.14159),
|
|
243
|
+
"unit": "radians",
|
|
244
|
+
"description": "Phase angle for network synchrony",
|
|
245
|
+
},
|
|
246
|
+
"coherence": {
|
|
247
|
+
"range": (0.0, 1.0),
|
|
248
|
+
"unit": "dimensionless",
|
|
249
|
+
"description": "Structural stability measure C(t)",
|
|
250
|
+
},
|
|
251
|
+
"delta_nfr": {
|
|
252
|
+
"range": (-10.0, 10.0),
|
|
253
|
+
"unit": "dimensionless",
|
|
254
|
+
"description": "Internal reorganization gradient ΔNFR",
|
|
255
|
+
},
|
|
256
|
+
"epi": {
|
|
257
|
+
"range": (0.0, 1.0),
|
|
258
|
+
"unit": "dimensionless",
|
|
259
|
+
"description": "Primary Information Structure magnitude",
|
|
260
|
+
},
|
|
261
|
+
"edge_probability": {
|
|
262
|
+
"range": (0.0, 1.0),
|
|
263
|
+
"unit": "probability",
|
|
264
|
+
"description": "Network edge connection probability",
|
|
265
|
+
},
|
|
266
|
+
"num_nodes": {
|
|
267
|
+
"range": (1, 100000),
|
|
268
|
+
"unit": "count",
|
|
269
|
+
"description": "Number of nodes in network",
|
|
270
|
+
},
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
def __init__(
|
|
274
|
+
self,
|
|
275
|
+
parameter: str,
|
|
276
|
+
value: Any,
|
|
277
|
+
valid_range: Optional[tuple] = None,
|
|
278
|
+
reason: Optional[str] = None,
|
|
279
|
+
):
|
|
280
|
+
# Get constraint info if available
|
|
281
|
+
constraint_info = self.PARAMETER_CONSTRAINTS.get(parameter)
|
|
282
|
+
|
|
283
|
+
if constraint_info and not valid_range:
|
|
284
|
+
valid_range = constraint_info["range"]
|
|
285
|
+
reason = reason or constraint_info["description"]
|
|
286
|
+
|
|
287
|
+
suggestion_parts = []
|
|
288
|
+
if valid_range:
|
|
289
|
+
min_val, max_val = valid_range
|
|
290
|
+
suggestion_parts.append(
|
|
291
|
+
f"'{parameter}' must be in range [{min_val}, {max_val}]"
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
if constraint_info:
|
|
295
|
+
suggestion_parts.append(f"Unit: {constraint_info['unit']}")
|
|
296
|
+
|
|
297
|
+
if reason:
|
|
298
|
+
suggestion_parts.append(f"Structural meaning: {reason}")
|
|
299
|
+
|
|
300
|
+
context = {
|
|
301
|
+
"parameter": parameter,
|
|
302
|
+
"provided_value": value,
|
|
303
|
+
"valid_range": (
|
|
304
|
+
f"[{valid_range[0]}, {valid_range[1]}]" if valid_range else "see docs"
|
|
305
|
+
),
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
super().__init__(
|
|
309
|
+
message=f"Invalid network configuration for '{parameter}'",
|
|
310
|
+
suggestion=" | ".join(suggestion_parts) if suggestion_parts else None,
|
|
311
|
+
docs_url="https://github.com/fermga/Teoria-de-la-naturaleza-fractal-resonante-TNFR-/blob/main/docs/source/api/overview.md",
|
|
312
|
+
context=context,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
class PhaseError(TNFRUserError):
|
|
317
|
+
"""Error raised when phase synchrony is violated.
|
|
318
|
+
|
|
319
|
+
TNFR requires explicit phase checking before coupling operations.
|
|
320
|
+
This error indicates phase incompatibility between nodes.
|
|
321
|
+
|
|
322
|
+
Enforces Invariant #5: Phase check from AGENTS.md
|
|
323
|
+
|
|
324
|
+
Parameters
|
|
325
|
+
----------
|
|
326
|
+
node1 : str
|
|
327
|
+
First node ID.
|
|
328
|
+
node2 : str
|
|
329
|
+
Second node ID.
|
|
330
|
+
phase1 : float
|
|
331
|
+
Phase of first node (radians).
|
|
332
|
+
phase2 : float
|
|
333
|
+
Phase of second node (radians).
|
|
334
|
+
threshold : float
|
|
335
|
+
Phase difference threshold for coupling.
|
|
336
|
+
|
|
337
|
+
Examples
|
|
338
|
+
--------
|
|
339
|
+
>>> raise PhaseError("n1", "n2", 0.5, 2.8, 0.5)
|
|
340
|
+
"""
|
|
341
|
+
|
|
342
|
+
def __init__(
|
|
343
|
+
self,
|
|
344
|
+
node1: str,
|
|
345
|
+
node2: str,
|
|
346
|
+
phase1: float,
|
|
347
|
+
phase2: float,
|
|
348
|
+
threshold: float = 0.5,
|
|
349
|
+
):
|
|
350
|
+
phase_diff = abs(phase1 - phase2)
|
|
351
|
+
|
|
352
|
+
suggestion = (
|
|
353
|
+
f"Nodes cannot couple: phase difference ({phase_diff:.3f} rad) "
|
|
354
|
+
f"exceeds threshold ({threshold:.3f} rad). "
|
|
355
|
+
f"Apply phase synchronization or adjust threshold."
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
context = {
|
|
359
|
+
"node1": node1,
|
|
360
|
+
"node2": node2,
|
|
361
|
+
"phase1": f"{phase1:.3f} rad",
|
|
362
|
+
"phase2": f"{phase2:.3f} rad",
|
|
363
|
+
"phase_difference": f"{phase_diff:.3f} rad",
|
|
364
|
+
"threshold": f"{threshold:.3f} rad",
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
super().__init__(
|
|
368
|
+
message=f"Phase synchrony violation between nodes '{node1}' and '{node2}'",
|
|
369
|
+
suggestion=suggestion,
|
|
370
|
+
docs_url="https://github.com/fermga/Teoria-de-la-naturaleza-fractal-resonante-TNFR-/blob/main/GLOSSARY.md#phase",
|
|
371
|
+
context=context,
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class CoherenceError(TNFRUserError):
|
|
376
|
+
"""Error raised when coherence operations violate monotonicity.
|
|
377
|
+
|
|
378
|
+
Coherence operator must not decrease C(t) except in controlled
|
|
379
|
+
dissonance tests. This error indicates unexpected coherence loss.
|
|
380
|
+
|
|
381
|
+
Enforces Invariant #1: EPI coherent form from AGENTS.md
|
|
382
|
+
|
|
383
|
+
Parameters
|
|
384
|
+
----------
|
|
385
|
+
operation : str
|
|
386
|
+
The operation that caused coherence decrease.
|
|
387
|
+
before : float
|
|
388
|
+
Coherence C(t) before operation.
|
|
389
|
+
after : float
|
|
390
|
+
Coherence C(t) after operation.
|
|
391
|
+
node_id : str, optional
|
|
392
|
+
Node ID if the error is node-specific.
|
|
393
|
+
|
|
394
|
+
Examples
|
|
395
|
+
--------
|
|
396
|
+
>>> raise CoherenceError("coherence", 0.85, 0.42)
|
|
397
|
+
"""
|
|
398
|
+
|
|
399
|
+
def __init__(
|
|
400
|
+
self,
|
|
401
|
+
operation: str,
|
|
402
|
+
before: float,
|
|
403
|
+
after: float,
|
|
404
|
+
node_id: Optional[str] = None,
|
|
405
|
+
):
|
|
406
|
+
decrease = before - after
|
|
407
|
+
percent_loss = (decrease / before * 100) if before > 0 else 0
|
|
408
|
+
|
|
409
|
+
suggestion = (
|
|
410
|
+
f"Coherence decreased by {decrease:.3f} ({percent_loss:.1f}%). "
|
|
411
|
+
f"This violates the coherence monotonicity invariant. "
|
|
412
|
+
f"Check if this is a controlled dissonance test or if "
|
|
413
|
+
f"there's an unexpected structural instability."
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
context = {
|
|
417
|
+
"operation": operation,
|
|
418
|
+
"coherence_before": f"{before:.3f}",
|
|
419
|
+
"coherence_after": f"{after:.3f}",
|
|
420
|
+
"decrease": f"{decrease:.3f}",
|
|
421
|
+
"percent_loss": f"{percent_loss:.1f}%",
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if node_id:
|
|
425
|
+
context["node_id"] = node_id
|
|
426
|
+
|
|
427
|
+
super().__init__(
|
|
428
|
+
message=f"Unexpected coherence decrease during '{operation}'",
|
|
429
|
+
suggestion=suggestion,
|
|
430
|
+
docs_url="https://github.com/fermga/Teoria-de-la-naturaleza-fractal-resonante-TNFR-/blob/main/AGENTS.md#canonical-invariants",
|
|
431
|
+
context=context,
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
class FrequencyError(TNFRUserError):
|
|
436
|
+
"""Error raised when structural frequency νf is invalid.
|
|
437
|
+
|
|
438
|
+
Structural frequency must be positive and expressed in Hz_str
|
|
439
|
+
(structural hertz) units. This error indicates frequency violations.
|
|
440
|
+
|
|
441
|
+
Enforces Invariant #2: Structural units from AGENTS.md
|
|
442
|
+
|
|
443
|
+
Parameters
|
|
444
|
+
----------
|
|
445
|
+
node_id : str
|
|
446
|
+
Node ID with invalid frequency.
|
|
447
|
+
vf : float
|
|
448
|
+
The invalid frequency value.
|
|
449
|
+
operation : str, optional
|
|
450
|
+
Operation that triggered the check.
|
|
451
|
+
|
|
452
|
+
Examples
|
|
453
|
+
--------
|
|
454
|
+
>>> raise FrequencyError("n1", -0.5, "emission")
|
|
455
|
+
"""
|
|
456
|
+
|
|
457
|
+
def __init__(
|
|
458
|
+
self,
|
|
459
|
+
node_id: str,
|
|
460
|
+
vf: float,
|
|
461
|
+
operation: Optional[str] = None,
|
|
462
|
+
):
|
|
463
|
+
if vf <= 0:
|
|
464
|
+
suggestion = (
|
|
465
|
+
f"Structural frequency νf must be positive (Hz_str units). "
|
|
466
|
+
f"Set νf > 0 for node '{node_id}'. "
|
|
467
|
+
f"Typical range: 0.1 to 10.0 Hz_str."
|
|
468
|
+
)
|
|
469
|
+
elif vf > 100:
|
|
470
|
+
suggestion = (
|
|
471
|
+
f"Structural frequency νf = {vf:.3f} Hz_str is very high. "
|
|
472
|
+
f"Typical range: 0.1 to 10.0 Hz_str. "
|
|
473
|
+
f"Verify this is intentional."
|
|
474
|
+
)
|
|
475
|
+
else:
|
|
476
|
+
suggestion = f"Verify structural frequency for node '{node_id}'."
|
|
477
|
+
|
|
478
|
+
context = {
|
|
479
|
+
"node_id": node_id,
|
|
480
|
+
"vf": f"{vf:.3f} Hz_str",
|
|
481
|
+
"valid_range": "[0.01, 100.0] Hz_str",
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if operation:
|
|
485
|
+
context["operation"] = operation
|
|
486
|
+
|
|
487
|
+
super().__init__(
|
|
488
|
+
message=f"Invalid structural frequency for node '{node_id}'",
|
|
489
|
+
suggestion=suggestion,
|
|
490
|
+
docs_url="https://github.com/fermga/Teoria-de-la-naturaleza-fractal-resonante-TNFR-/blob/main/GLOSSARY.md#structural-frequency",
|
|
491
|
+
context=context,
|
|
492
|
+
)
|
tnfr/execution.py
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""Execution helpers for canonical TNFR programs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections import deque
|
|
6
|
+
from collections.abc import Callable, Iterable, Sequence
|
|
7
|
+
from typing import Any, Optional, cast
|
|
8
|
+
|
|
9
|
+
from ._compat import TypeAlias
|
|
10
|
+
from .constants import get_param
|
|
11
|
+
from .dynamics import step
|
|
12
|
+
from .flatten import _flatten
|
|
13
|
+
from .glyph_history import ensure_history
|
|
14
|
+
from .tokens import TARGET, THOL, WAIT, OpTag, Token
|
|
15
|
+
from .types import Glyph, NodeId, TNFRGraph
|
|
16
|
+
from .utils import MAX_MATERIALIZE_DEFAULT, ensure_collection, is_non_string_sequence
|
|
17
|
+
from .validation import apply_glyph_with_grammar
|
|
18
|
+
|
|
19
|
+
AdvanceFn = Callable[[TNFRGraph], None]
|
|
20
|
+
TraceEntry = dict[str, Any]
|
|
21
|
+
ProgramTrace: TypeAlias = deque[TraceEntry]
|
|
22
|
+
HandlerFn = Callable[
|
|
23
|
+
[TNFRGraph, Any, Optional[Sequence[NodeId]], ProgramTrace, AdvanceFn],
|
|
24
|
+
Optional[Sequence[NodeId]],
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"AdvanceFn",
|
|
29
|
+
"CANONICAL_PRESET_NAME",
|
|
30
|
+
"CANONICAL_PROGRAM_TOKENS",
|
|
31
|
+
"HANDLERS",
|
|
32
|
+
"_apply_glyph_to_targets",
|
|
33
|
+
"_record_trace",
|
|
34
|
+
"compile_sequence",
|
|
35
|
+
"basic_canonical_example",
|
|
36
|
+
"block",
|
|
37
|
+
"play",
|
|
38
|
+
"seq",
|
|
39
|
+
"target",
|
|
40
|
+
"wait",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
CANONICAL_PRESET_NAME = "canonical_example"
|
|
44
|
+
CANONICAL_PROGRAM_TOKENS: tuple[Token, ...] = (
|
|
45
|
+
Glyph.SHA, # silence - initial stabilization
|
|
46
|
+
Glyph.AL, # emission - initiate pattern
|
|
47
|
+
Glyph.RA, # reception - capture information
|
|
48
|
+
Glyph.OZ, # dissonance - required before mutation (grammar rule)
|
|
49
|
+
Glyph.ZHIR, # mutation - phase change
|
|
50
|
+
Glyph.NUL, # contraction - compress structure
|
|
51
|
+
Glyph.THOL, # self_organization - recursive reorganization
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _window(G: TNFRGraph) -> int:
|
|
56
|
+
return int(get_param(G, "GLYPH_HYSTERESIS_WINDOW"))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _apply_glyph_to_targets(
|
|
60
|
+
G: TNFRGraph, g: Glyph | str, nodes: Optional[Iterable[NodeId]] = None
|
|
61
|
+
) -> None:
|
|
62
|
+
"""Apply ``g`` to ``nodes`` (or all nodes) respecting the grammar."""
|
|
63
|
+
|
|
64
|
+
nodes_iter = G.nodes() if nodes is None else nodes
|
|
65
|
+
w = _window(G)
|
|
66
|
+
apply_glyph_with_grammar(G, nodes_iter, g, w)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _advance(G: TNFRGraph, step_fn: AdvanceFn) -> None:
|
|
70
|
+
step_fn(G)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _record_trace(trace: ProgramTrace, G: TNFRGraph, op: OpTag, **data: Any) -> None:
|
|
74
|
+
"""Append an operation snapshot to ``trace`` using graph time metadata."""
|
|
75
|
+
|
|
76
|
+
trace.append({"t": float(G.graph.get("_t", 0.0)), "op": op.name, **data})
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _advance_and_record(
|
|
80
|
+
G: TNFRGraph,
|
|
81
|
+
trace: ProgramTrace,
|
|
82
|
+
label: OpTag,
|
|
83
|
+
step_fn: AdvanceFn,
|
|
84
|
+
*,
|
|
85
|
+
times: int = 1,
|
|
86
|
+
**data: Any,
|
|
87
|
+
) -> None:
|
|
88
|
+
for _ in range(times):
|
|
89
|
+
_advance(G, step_fn)
|
|
90
|
+
_record_trace(trace, G, label, **data)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _handle_target(
|
|
94
|
+
G: TNFRGraph,
|
|
95
|
+
payload: TARGET,
|
|
96
|
+
_curr_target: Optional[Sequence[NodeId]],
|
|
97
|
+
trace: ProgramTrace,
|
|
98
|
+
_step_fn: AdvanceFn,
|
|
99
|
+
) -> Sequence[NodeId]:
|
|
100
|
+
"""Handle a ``TARGET`` token and return the active node set."""
|
|
101
|
+
|
|
102
|
+
nodes_src = G.nodes() if payload.nodes is None else payload.nodes
|
|
103
|
+
nodes = ensure_collection(nodes_src, max_materialize=None)
|
|
104
|
+
if is_non_string_sequence(nodes):
|
|
105
|
+
curr_target = cast(Sequence[NodeId], nodes)
|
|
106
|
+
else:
|
|
107
|
+
curr_target = tuple(nodes)
|
|
108
|
+
_record_trace(trace, G, OpTag.TARGET, n=len(curr_target))
|
|
109
|
+
return curr_target
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _handle_wait(
|
|
113
|
+
G: TNFRGraph,
|
|
114
|
+
steps: int,
|
|
115
|
+
curr_target: Optional[Sequence[NodeId]],
|
|
116
|
+
trace: ProgramTrace,
|
|
117
|
+
step_fn: AdvanceFn,
|
|
118
|
+
) -> Optional[Sequence[NodeId]]:
|
|
119
|
+
_advance_and_record(G, trace, OpTag.WAIT, step_fn, times=steps, k=steps)
|
|
120
|
+
return curr_target
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _handle_glyph(
|
|
124
|
+
G: TNFRGraph,
|
|
125
|
+
g: Glyph | str,
|
|
126
|
+
curr_target: Optional[Sequence[NodeId]],
|
|
127
|
+
trace: ProgramTrace,
|
|
128
|
+
step_fn: AdvanceFn,
|
|
129
|
+
label: OpTag = OpTag.GLYPH,
|
|
130
|
+
) -> Optional[Sequence[NodeId]]:
|
|
131
|
+
_apply_glyph_to_targets(G, g, curr_target)
|
|
132
|
+
_advance_and_record(G, trace, label, step_fn, g=g)
|
|
133
|
+
return curr_target
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _handle_thol(
|
|
137
|
+
G: TNFRGraph,
|
|
138
|
+
g: Glyph | str | None,
|
|
139
|
+
curr_target: Optional[Sequence[NodeId]],
|
|
140
|
+
trace: ProgramTrace,
|
|
141
|
+
step_fn: AdvanceFn,
|
|
142
|
+
) -> Optional[Sequence[NodeId]]:
|
|
143
|
+
return _handle_glyph(
|
|
144
|
+
G, g or Glyph.THOL.value, curr_target, trace, step_fn, label=OpTag.THOL
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
HANDLERS: dict[OpTag, HandlerFn] = {
|
|
149
|
+
OpTag.TARGET: _handle_target,
|
|
150
|
+
OpTag.WAIT: _handle_wait,
|
|
151
|
+
OpTag.GLYPH: _handle_glyph,
|
|
152
|
+
OpTag.THOL: _handle_thol,
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def play(
|
|
157
|
+
G: TNFRGraph, sequence: Sequence[Token], step_fn: Optional[AdvanceFn] = None
|
|
158
|
+
) -> None:
|
|
159
|
+
"""Execute a canonical sequence on graph ``G``."""
|
|
160
|
+
|
|
161
|
+
step_fn = step_fn or step
|
|
162
|
+
|
|
163
|
+
curr_target: Optional[Sequence[NodeId]] = None
|
|
164
|
+
|
|
165
|
+
history = ensure_history(G)
|
|
166
|
+
maxlen = int(get_param(G, "PROGRAM_TRACE_MAXLEN"))
|
|
167
|
+
trace_obj = history.get("program_trace")
|
|
168
|
+
trace: ProgramTrace
|
|
169
|
+
if not isinstance(trace_obj, deque) or trace_obj.maxlen != maxlen:
|
|
170
|
+
trace = cast(ProgramTrace, deque(trace_obj or [], maxlen=maxlen))
|
|
171
|
+
history["program_trace"] = trace
|
|
172
|
+
else:
|
|
173
|
+
trace = cast(ProgramTrace, trace_obj)
|
|
174
|
+
|
|
175
|
+
for op, payload in _flatten(sequence):
|
|
176
|
+
handler: HandlerFn | None = HANDLERS.get(op)
|
|
177
|
+
if handler is None:
|
|
178
|
+
raise ValueError(f"Unknown operation: {op}")
|
|
179
|
+
curr_target = handler(G, payload, curr_target, trace, step_fn)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def compile_sequence(
|
|
183
|
+
sequence: Iterable[Token] | Sequence[Token] | Any,
|
|
184
|
+
*,
|
|
185
|
+
max_materialize: int | None = MAX_MATERIALIZE_DEFAULT,
|
|
186
|
+
) -> list[tuple[OpTag, Any]]:
|
|
187
|
+
"""Return the operations executed by :func:`play` for ``sequence``."""
|
|
188
|
+
|
|
189
|
+
return _flatten(sequence, max_materialize=max_materialize)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def seq(*tokens: Token) -> list[Token]:
|
|
193
|
+
"""Return a mutable list of ``tokens`` for explicit sequence editing."""
|
|
194
|
+
|
|
195
|
+
return list(tokens)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def block(*tokens: Token, repeat: int = 1, close: Optional[Glyph] = None) -> THOL:
|
|
199
|
+
"""Build a THOL block with optional repetition and forced closure."""
|
|
200
|
+
|
|
201
|
+
return THOL(body=list(tokens), repeat=repeat, force_close=close)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def target(nodes: Optional[Iterable[NodeId]] = None) -> TARGET:
|
|
205
|
+
"""Return a TARGET token selecting ``nodes`` (defaults to all nodes)."""
|
|
206
|
+
|
|
207
|
+
return TARGET(nodes=nodes)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def wait(steps: int = 1) -> WAIT:
|
|
211
|
+
"""Return a WAIT token forcing ``steps`` structural updates before resuming."""
|
|
212
|
+
|
|
213
|
+
return WAIT(steps=max(1, int(steps)))
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def basic_canonical_example() -> list[Token]:
|
|
217
|
+
"""Return the canonical preset sequence.
|
|
218
|
+
|
|
219
|
+
Returns a copy of the canonical preset tokens to keep CLI defaults aligned
|
|
220
|
+
with :func:`tnfr.config.presets.get_preset`.
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
return list(CANONICAL_PROGRAM_TOKENS)
|