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,712 @@
|
|
|
1
|
+
"""TNFR Invariant Validators.
|
|
2
|
+
|
|
3
|
+
This module implements the 10 canonical TNFR invariants as described in AGENTS.md.
|
|
4
|
+
Each invariant is a structural constraint that must be preserved to maintain
|
|
5
|
+
coherence within the TNFR paradigm.
|
|
6
|
+
|
|
7
|
+
Canonical Invariants:
|
|
8
|
+
1. EPI as coherent form: changes only via structural operators
|
|
9
|
+
2. Structural units: νf expressed in Hz_str (structural hertz)
|
|
10
|
+
3. ΔNFR semantics: sign and magnitude modulate reorganization rate
|
|
11
|
+
4. Operator closure: composition yields valid TNFR states
|
|
12
|
+
5. Phase check: explicit phase verification for coupling
|
|
13
|
+
6. Node birth/collapse: minimal conditions maintained
|
|
14
|
+
7. Operational fractality: EPIs can nest without losing identity
|
|
15
|
+
8. Controlled determinism: reproducible and traceable
|
|
16
|
+
9. Structural metrics: expose C(t), Si, phase, νf
|
|
17
|
+
10. Domain neutrality: trans-scale and trans-domain
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import math
|
|
23
|
+
from abc import ABC, abstractmethod
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from enum import Enum
|
|
26
|
+
from typing import Any, Optional
|
|
27
|
+
|
|
28
|
+
from ..constants import DEFAULTS, DNFR_PRIMARY, EPI_PRIMARY, THETA_PRIMARY, VF_PRIMARY
|
|
29
|
+
from ..types import TNFRGraph
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"InvariantSeverity",
|
|
33
|
+
"InvariantViolation",
|
|
34
|
+
"TNFRInvariant",
|
|
35
|
+
"Invariant1_EPIOnlyThroughOperators",
|
|
36
|
+
"Invariant2_VfInHzStr",
|
|
37
|
+
"Invariant3_DNFRSemantics",
|
|
38
|
+
"Invariant4_OperatorClosure",
|
|
39
|
+
"Invariant5_ExplicitPhaseChecks",
|
|
40
|
+
"Invariant6_NodeBirthCollapse",
|
|
41
|
+
"Invariant7_OperationalFractality",
|
|
42
|
+
"Invariant8_ControlledDeterminism",
|
|
43
|
+
"Invariant9_StructuralMetrics",
|
|
44
|
+
"Invariant10_DomainNeutrality",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class InvariantSeverity(Enum):
|
|
49
|
+
"""Severity levels for invariant violations."""
|
|
50
|
+
|
|
51
|
+
INFO = "info" # Information, not a problem
|
|
52
|
+
WARNING = "warning" # Minor inconsistency
|
|
53
|
+
ERROR = "error" # Violation that prevents execution
|
|
54
|
+
CRITICAL = "critical" # Data corruption
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class InvariantViolation:
|
|
59
|
+
"""Detailed description of invariant violation."""
|
|
60
|
+
|
|
61
|
+
invariant_id: int
|
|
62
|
+
severity: InvariantSeverity
|
|
63
|
+
description: str
|
|
64
|
+
node_id: Optional[str] = None
|
|
65
|
+
expected_value: Optional[Any] = None
|
|
66
|
+
actual_value: Optional[Any] = None
|
|
67
|
+
suggestion: Optional[str] = None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class TNFRInvariant(ABC):
|
|
71
|
+
"""Base class for TNFR invariant validators."""
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
@abstractmethod
|
|
75
|
+
def invariant_id(self) -> int:
|
|
76
|
+
"""TNFR invariant number (1-10)."""
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def description(self) -> str:
|
|
81
|
+
"""Human-readable description of the invariant."""
|
|
82
|
+
|
|
83
|
+
@abstractmethod
|
|
84
|
+
def validate(self, graph: TNFRGraph) -> list[InvariantViolation]:
|
|
85
|
+
"""Validates invariant in the graph, returns found violations."""
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class Invariant1_EPIOnlyThroughOperators(TNFRInvariant):
|
|
89
|
+
"""Invariant 1: EPI changes only through structural operators."""
|
|
90
|
+
|
|
91
|
+
invariant_id = 1
|
|
92
|
+
description = "EPI changes only through structural operators"
|
|
93
|
+
|
|
94
|
+
def __init__(self) -> None:
|
|
95
|
+
self._previous_epi_values: dict[Any, float] = {}
|
|
96
|
+
|
|
97
|
+
def validate(self, graph: TNFRGraph) -> list[InvariantViolation]:
|
|
98
|
+
violations = []
|
|
99
|
+
|
|
100
|
+
# Get configuration bounds
|
|
101
|
+
config = getattr(graph, "graph", {})
|
|
102
|
+
epi_min = config.get("EPI_MIN", DEFAULTS.get("EPI_MIN", 0.0))
|
|
103
|
+
epi_max = config.get("EPI_MAX", DEFAULTS.get("EPI_MAX", 1.0))
|
|
104
|
+
|
|
105
|
+
for node_id in graph.nodes():
|
|
106
|
+
node_data = graph.nodes[node_id]
|
|
107
|
+
current_epi = node_data.get(EPI_PRIMARY, 0.0)
|
|
108
|
+
|
|
109
|
+
# Handle complex EPI structures (dict, complex numbers)
|
|
110
|
+
# Extract scalar value for validation
|
|
111
|
+
if isinstance(current_epi, dict):
|
|
112
|
+
# EPI can be a dict with 'continuous', 'discrete', 'grid' keys
|
|
113
|
+
# Try to extract a scalar value for validation
|
|
114
|
+
if "continuous" in current_epi:
|
|
115
|
+
epi_value = current_epi["continuous"]
|
|
116
|
+
if isinstance(epi_value, (tuple, list)) and len(epi_value) > 0:
|
|
117
|
+
epi_value = epi_value[0]
|
|
118
|
+
if isinstance(epi_value, complex):
|
|
119
|
+
epi_value = abs(epi_value)
|
|
120
|
+
current_epi = (
|
|
121
|
+
float(epi_value)
|
|
122
|
+
if isinstance(epi_value, (int, float, complex))
|
|
123
|
+
else 0.0
|
|
124
|
+
)
|
|
125
|
+
else:
|
|
126
|
+
# Skip validation for complex structures we can't interpret
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
elif isinstance(current_epi, complex):
|
|
130
|
+
# For complex numbers, use magnitude
|
|
131
|
+
current_epi = abs(current_epi)
|
|
132
|
+
|
|
133
|
+
# Verificar rango válido de EPI
|
|
134
|
+
if not (epi_min <= current_epi <= epi_max):
|
|
135
|
+
violations.append(
|
|
136
|
+
InvariantViolation(
|
|
137
|
+
invariant_id=1,
|
|
138
|
+
severity=InvariantSeverity.ERROR,
|
|
139
|
+
description=f"EPI out of valid range [{epi_min},{epi_max}]",
|
|
140
|
+
node_id=str(node_id),
|
|
141
|
+
expected_value=f"{epi_min} <= EPI <= {epi_max}",
|
|
142
|
+
actual_value=current_epi,
|
|
143
|
+
suggestion="Check operator implementation for EPI clamping",
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Verificar que EPI es un número finito
|
|
148
|
+
if not isinstance(current_epi, (int, float)) or not math.isfinite(
|
|
149
|
+
current_epi
|
|
150
|
+
):
|
|
151
|
+
violations.append(
|
|
152
|
+
InvariantViolation(
|
|
153
|
+
invariant_id=1,
|
|
154
|
+
severity=InvariantSeverity.CRITICAL,
|
|
155
|
+
description="EPI is not a finite number",
|
|
156
|
+
node_id=str(node_id),
|
|
157
|
+
expected_value="finite float",
|
|
158
|
+
actual_value=f"{type(current_epi).__name__}: {current_epi}",
|
|
159
|
+
suggestion="Check operator implementation for EPI assignment",
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Detectar cambios no autorizados (requiere tracking)
|
|
164
|
+
# Solo verificar si hay un operador previo registrado
|
|
165
|
+
if hasattr(graph, "_last_operator_applied"):
|
|
166
|
+
if node_id in self._previous_epi_values:
|
|
167
|
+
prev_epi = self._previous_epi_values[node_id]
|
|
168
|
+
if abs(current_epi - prev_epi) > 1e-10: # Cambio detectado
|
|
169
|
+
if not graph._last_operator_applied:
|
|
170
|
+
violations.append(
|
|
171
|
+
InvariantViolation(
|
|
172
|
+
invariant_id=1,
|
|
173
|
+
severity=InvariantSeverity.CRITICAL,
|
|
174
|
+
description="EPI changed without operator application",
|
|
175
|
+
node_id=str(node_id),
|
|
176
|
+
expected_value=prev_epi,
|
|
177
|
+
actual_value=current_epi,
|
|
178
|
+
suggestion="Ensure all EPI modifications go through structural operators",
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Actualizar tracking
|
|
183
|
+
for node_id in graph.nodes():
|
|
184
|
+
epi_value = graph.nodes[node_id].get(EPI_PRIMARY, 0.0)
|
|
185
|
+
# Store scalar value for tracking
|
|
186
|
+
if isinstance(epi_value, dict) and "continuous" in epi_value:
|
|
187
|
+
epi_val = epi_value["continuous"]
|
|
188
|
+
if isinstance(epi_val, (tuple, list)) and len(epi_val) > 0:
|
|
189
|
+
epi_val = epi_val[0]
|
|
190
|
+
if isinstance(epi_val, complex):
|
|
191
|
+
epi_val = abs(epi_val)
|
|
192
|
+
epi_value = (
|
|
193
|
+
float(epi_val)
|
|
194
|
+
if isinstance(epi_val, (int, float, complex))
|
|
195
|
+
else 0.0
|
|
196
|
+
)
|
|
197
|
+
elif isinstance(epi_value, complex):
|
|
198
|
+
epi_value = abs(epi_value)
|
|
199
|
+
|
|
200
|
+
self._previous_epi_values[node_id] = epi_value
|
|
201
|
+
|
|
202
|
+
return violations
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class Invariant2_VfInHzStr(TNFRInvariant):
|
|
206
|
+
"""Invariante 2: νf stays in Hz_str units."""
|
|
207
|
+
|
|
208
|
+
invariant_id = 2
|
|
209
|
+
description = "νf stays in Hz_str units"
|
|
210
|
+
|
|
211
|
+
def validate(self, graph: TNFRGraph) -> list[InvariantViolation]:
|
|
212
|
+
violations = []
|
|
213
|
+
|
|
214
|
+
# Get configuration bounds
|
|
215
|
+
config = getattr(graph, "graph", {})
|
|
216
|
+
vf_min = config.get("VF_MIN", DEFAULTS.get("VF_MIN", 0.001))
|
|
217
|
+
vf_max = config.get("VF_MAX", DEFAULTS.get("VF_MAX", 1000.0))
|
|
218
|
+
|
|
219
|
+
for node_id in graph.nodes():
|
|
220
|
+
node_data = graph.nodes[node_id]
|
|
221
|
+
vf = node_data.get(VF_PRIMARY, 0.0)
|
|
222
|
+
|
|
223
|
+
# Verificar rango estructural válido (Hz_str)
|
|
224
|
+
if not (vf_min <= vf <= vf_max):
|
|
225
|
+
violations.append(
|
|
226
|
+
InvariantViolation(
|
|
227
|
+
invariant_id=2,
|
|
228
|
+
severity=InvariantSeverity.ERROR,
|
|
229
|
+
description=f"νf outside typical Hz_str range [{vf_min}, {vf_max}]",
|
|
230
|
+
node_id=str(node_id),
|
|
231
|
+
expected_value=f"{vf_min} <= νf <= {vf_max} Hz_str",
|
|
232
|
+
actual_value=vf,
|
|
233
|
+
suggestion="Verify νf units and operator calculations",
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
# Verificar que sea un número válido
|
|
238
|
+
if not isinstance(vf, (int, float)) or not math.isfinite(vf):
|
|
239
|
+
violations.append(
|
|
240
|
+
InvariantViolation(
|
|
241
|
+
invariant_id=2,
|
|
242
|
+
severity=InvariantSeverity.CRITICAL,
|
|
243
|
+
description="νf is not a finite number",
|
|
244
|
+
node_id=str(node_id),
|
|
245
|
+
expected_value="finite float",
|
|
246
|
+
actual_value=f"{type(vf).__name__}: {vf}",
|
|
247
|
+
suggestion="Check operator implementation for νf assignment",
|
|
248
|
+
)
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# Verificar que νf sea positivo (requerimiento estructural)
|
|
252
|
+
if isinstance(vf, (int, float)) and vf <= 0:
|
|
253
|
+
violations.append(
|
|
254
|
+
InvariantViolation(
|
|
255
|
+
invariant_id=2,
|
|
256
|
+
severity=InvariantSeverity.ERROR,
|
|
257
|
+
description="νf must be positive (structural frequency)",
|
|
258
|
+
node_id=str(node_id),
|
|
259
|
+
expected_value="νf > 0",
|
|
260
|
+
actual_value=vf,
|
|
261
|
+
suggestion="Structural frequency must be positive for coherent nodes",
|
|
262
|
+
)
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
return violations
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class Invariant5_ExplicitPhaseChecks(TNFRInvariant):
|
|
269
|
+
"""Invariante 5: Explicit phase checks for coupling."""
|
|
270
|
+
|
|
271
|
+
invariant_id = 5
|
|
272
|
+
description = "Explicit phase checks for coupling"
|
|
273
|
+
|
|
274
|
+
def __init__(self, phase_coupling_threshold: float = math.pi / 2) -> None:
|
|
275
|
+
self.phase_coupling_threshold = phase_coupling_threshold
|
|
276
|
+
|
|
277
|
+
def validate(self, graph: TNFRGraph) -> list[InvariantViolation]:
|
|
278
|
+
violations = []
|
|
279
|
+
|
|
280
|
+
for node_id in graph.nodes():
|
|
281
|
+
node_data = graph.nodes[node_id]
|
|
282
|
+
phase = node_data.get(THETA_PRIMARY, 0.0)
|
|
283
|
+
|
|
284
|
+
# Verificar que phase sea un número finito
|
|
285
|
+
if not isinstance(phase, (int, float)) or not math.isfinite(phase):
|
|
286
|
+
violations.append(
|
|
287
|
+
InvariantViolation(
|
|
288
|
+
invariant_id=5,
|
|
289
|
+
severity=InvariantSeverity.CRITICAL,
|
|
290
|
+
description="Phase is not a finite number",
|
|
291
|
+
node_id=str(node_id),
|
|
292
|
+
expected_value="finite float",
|
|
293
|
+
actual_value=f"{type(phase).__name__}: {phase}",
|
|
294
|
+
suggestion="Check operator implementation for phase assignment",
|
|
295
|
+
)
|
|
296
|
+
)
|
|
297
|
+
continue
|
|
298
|
+
|
|
299
|
+
# Verificar rango de fase [0, 2π] o normalizable
|
|
300
|
+
# TNFR permite fases fuera de este rango si se pueden normalizar
|
|
301
|
+
# Emitir warning si la fase no está en el rango canónico
|
|
302
|
+
if not (0.0 <= phase <= 2 * math.pi):
|
|
303
|
+
violations.append(
|
|
304
|
+
InvariantViolation(
|
|
305
|
+
invariant_id=5,
|
|
306
|
+
severity=InvariantSeverity.WARNING,
|
|
307
|
+
description="Phase outside [0, 2π] range (normalization possible)",
|
|
308
|
+
node_id=str(node_id),
|
|
309
|
+
expected_value="0.0 <= phase <= 2π",
|
|
310
|
+
actual_value=phase,
|
|
311
|
+
suggestion="Consider normalizing phase to [0, 2π] range",
|
|
312
|
+
)
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
# Verificar sincronización en nodos acoplados (edges)
|
|
316
|
+
if hasattr(graph, "edges"):
|
|
317
|
+
for edge in graph.edges():
|
|
318
|
+
node1, node2 = edge
|
|
319
|
+
phase1 = graph.nodes[node1].get(THETA_PRIMARY, 0.0)
|
|
320
|
+
phase2 = graph.nodes[node2].get(THETA_PRIMARY, 0.0)
|
|
321
|
+
|
|
322
|
+
# Verificar que ambas fases sean números finitos antes de calcular diferencia
|
|
323
|
+
if not (
|
|
324
|
+
isinstance(phase1, (int, float))
|
|
325
|
+
and math.isfinite(phase1)
|
|
326
|
+
and isinstance(phase2, (int, float))
|
|
327
|
+
and math.isfinite(phase2)
|
|
328
|
+
):
|
|
329
|
+
continue
|
|
330
|
+
|
|
331
|
+
phase_diff = abs(phase1 - phase2)
|
|
332
|
+
# Considerar periodicidad
|
|
333
|
+
phase_diff = min(phase_diff, 2 * math.pi - phase_diff)
|
|
334
|
+
|
|
335
|
+
# Si la diferencia es muy grande, puede indicar desacoplamiento
|
|
336
|
+
if phase_diff > self.phase_coupling_threshold:
|
|
337
|
+
violations.append(
|
|
338
|
+
InvariantViolation(
|
|
339
|
+
invariant_id=5,
|
|
340
|
+
severity=InvariantSeverity.WARNING,
|
|
341
|
+
description="Large phase difference between coupled nodes",
|
|
342
|
+
node_id=f"{node1}-{node2}",
|
|
343
|
+
expected_value=f"< {self.phase_coupling_threshold}",
|
|
344
|
+
actual_value=phase_diff,
|
|
345
|
+
suggestion="Check coupling strength or phase coordination",
|
|
346
|
+
)
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
return violations
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
class Invariant3_DNFRSemantics(TNFRInvariant):
|
|
353
|
+
"""Invariante 3: ΔNFR semantics - sign and magnitude modulate reorganization rate."""
|
|
354
|
+
|
|
355
|
+
invariant_id = 3
|
|
356
|
+
description = "ΔNFR semantics: sign and magnitude modulate reorganization rate"
|
|
357
|
+
|
|
358
|
+
def validate(self, graph: TNFRGraph) -> list[InvariantViolation]:
|
|
359
|
+
violations = []
|
|
360
|
+
|
|
361
|
+
for node_id in graph.nodes():
|
|
362
|
+
node_data = graph.nodes[node_id]
|
|
363
|
+
dnfr = node_data.get(DNFR_PRIMARY, 0.0)
|
|
364
|
+
|
|
365
|
+
# Verificar que ΔNFR es un número finito
|
|
366
|
+
if not isinstance(dnfr, (int, float)) or not math.isfinite(dnfr):
|
|
367
|
+
violations.append(
|
|
368
|
+
InvariantViolation(
|
|
369
|
+
invariant_id=3,
|
|
370
|
+
severity=InvariantSeverity.CRITICAL,
|
|
371
|
+
description="ΔNFR is not a finite number",
|
|
372
|
+
node_id=str(node_id),
|
|
373
|
+
expected_value="finite float",
|
|
374
|
+
actual_value=f"{type(dnfr).__name__}: {dnfr}",
|
|
375
|
+
suggestion="Check operator implementation for ΔNFR calculation",
|
|
376
|
+
)
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
# Verificar que ΔNFR no se trata como error/loss gradient
|
|
380
|
+
# (esto es más conceptual, pero podemos verificar rangos razonables)
|
|
381
|
+
if isinstance(dnfr, (int, float)) and math.isfinite(dnfr):
|
|
382
|
+
# ΔNFR excesivamente grande podría indicar tratamiento erróneo
|
|
383
|
+
if abs(dnfr) > 1000.0:
|
|
384
|
+
violations.append(
|
|
385
|
+
InvariantViolation(
|
|
386
|
+
invariant_id=3,
|
|
387
|
+
severity=InvariantSeverity.WARNING,
|
|
388
|
+
description="ΔNFR magnitude is unusually large",
|
|
389
|
+
node_id=str(node_id),
|
|
390
|
+
expected_value="|ΔNFR| < 1000",
|
|
391
|
+
actual_value=abs(dnfr),
|
|
392
|
+
suggestion="Verify ΔNFR is not being misused as error gradient",
|
|
393
|
+
)
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
return violations
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
class Invariant4_OperatorClosure(TNFRInvariant):
|
|
400
|
+
"""Invariante 4: Operator closure - composition yields valid TNFR states."""
|
|
401
|
+
|
|
402
|
+
invariant_id = 4
|
|
403
|
+
description = "Operator closure: composition yields valid TNFR states"
|
|
404
|
+
|
|
405
|
+
def validate(self, graph: TNFRGraph) -> list[InvariantViolation]:
|
|
406
|
+
violations = []
|
|
407
|
+
|
|
408
|
+
# Verificar que el grafo mantiene estado válido después de operadores
|
|
409
|
+
for node_id in graph.nodes():
|
|
410
|
+
node_data = graph.nodes[node_id]
|
|
411
|
+
|
|
412
|
+
# Verificar que los atributos esenciales existen
|
|
413
|
+
required_attrs = [EPI_PRIMARY, VF_PRIMARY, THETA_PRIMARY]
|
|
414
|
+
missing_attrs = [attr for attr in required_attrs if attr not in node_data]
|
|
415
|
+
|
|
416
|
+
if missing_attrs:
|
|
417
|
+
violations.append(
|
|
418
|
+
InvariantViolation(
|
|
419
|
+
invariant_id=4,
|
|
420
|
+
severity=InvariantSeverity.CRITICAL,
|
|
421
|
+
description=f"Node missing required TNFR attributes: {missing_attrs}",
|
|
422
|
+
node_id=str(node_id),
|
|
423
|
+
expected_value="All TNFR attributes present",
|
|
424
|
+
actual_value=f"Missing: {missing_attrs}",
|
|
425
|
+
suggestion="Operator composition broke TNFR state structure",
|
|
426
|
+
)
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
# Verificar que el grafo tiene hook ΔNFR
|
|
430
|
+
if hasattr(graph, "graph"):
|
|
431
|
+
if "compute_delta_nfr" not in graph.graph:
|
|
432
|
+
violations.append(
|
|
433
|
+
InvariantViolation(
|
|
434
|
+
invariant_id=4,
|
|
435
|
+
severity=InvariantSeverity.WARNING,
|
|
436
|
+
description="Graph missing ΔNFR computation hook",
|
|
437
|
+
expected_value="compute_delta_nfr hook present",
|
|
438
|
+
actual_value="Hook missing",
|
|
439
|
+
suggestion="Ensure ΔNFR hook is installed for proper operator closure",
|
|
440
|
+
)
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
return violations
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
class Invariant6_NodeBirthCollapse(TNFRInvariant):
|
|
447
|
+
"""Invariante 6: Node birth/collapse - minimal conditions maintained."""
|
|
448
|
+
|
|
449
|
+
invariant_id = 6
|
|
450
|
+
description = "Node birth/collapse: minimal conditions maintained"
|
|
451
|
+
|
|
452
|
+
def validate(self, graph: TNFRGraph) -> list[InvariantViolation]:
|
|
453
|
+
violations = []
|
|
454
|
+
|
|
455
|
+
for node_id in graph.nodes():
|
|
456
|
+
node_data = graph.nodes[node_id]
|
|
457
|
+
vf = node_data.get(VF_PRIMARY, 0.0)
|
|
458
|
+
dnfr = node_data.get(DNFR_PRIMARY, 0.0)
|
|
459
|
+
|
|
460
|
+
# Extract scalar values if needed
|
|
461
|
+
if isinstance(vf, dict) and "continuous" in vf:
|
|
462
|
+
continue # Skip complex structures
|
|
463
|
+
|
|
464
|
+
if isinstance(dnfr, dict):
|
|
465
|
+
continue # Skip complex structures
|
|
466
|
+
|
|
467
|
+
# Condiciones mínimas de nacimiento: νf suficiente
|
|
468
|
+
if isinstance(vf, (int, float)) and vf < 0.001:
|
|
469
|
+
violations.append(
|
|
470
|
+
InvariantViolation(
|
|
471
|
+
invariant_id=6,
|
|
472
|
+
severity=InvariantSeverity.WARNING,
|
|
473
|
+
description="Node has insufficient νf for sustained existence",
|
|
474
|
+
node_id=str(node_id),
|
|
475
|
+
expected_value="νf >= 0.001",
|
|
476
|
+
actual_value=vf,
|
|
477
|
+
suggestion="Node may be approaching collapse condition",
|
|
478
|
+
)
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
# Condiciones de colapso: ΔNFR extremo o νf cercano a cero
|
|
482
|
+
if isinstance(dnfr, (int, float)) and math.isfinite(dnfr):
|
|
483
|
+
if abs(dnfr) > 10.0: # Dissonance extrema
|
|
484
|
+
violations.append(
|
|
485
|
+
InvariantViolation(
|
|
486
|
+
invariant_id=6,
|
|
487
|
+
severity=InvariantSeverity.WARNING,
|
|
488
|
+
description="Node experiencing extreme dissonance (collapse risk)",
|
|
489
|
+
node_id=str(node_id),
|
|
490
|
+
expected_value="|ΔNFR| < 10",
|
|
491
|
+
actual_value=abs(dnfr),
|
|
492
|
+
suggestion="High dissonance may trigger node collapse",
|
|
493
|
+
)
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
return violations
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
class Invariant7_OperationalFractality(TNFRInvariant):
|
|
500
|
+
"""Invariante 7: Operational fractality - EPIs can nest without losing identity."""
|
|
501
|
+
|
|
502
|
+
invariant_id = 7
|
|
503
|
+
description = "Operational fractality: EPIs can nest without losing identity"
|
|
504
|
+
|
|
505
|
+
def validate(self, graph: TNFRGraph) -> list[InvariantViolation]:
|
|
506
|
+
violations = []
|
|
507
|
+
|
|
508
|
+
# Verificar que estructuras EPI complejas mantienen identidad
|
|
509
|
+
for node_id in graph.nodes():
|
|
510
|
+
node_data = graph.nodes[node_id]
|
|
511
|
+
epi = node_data.get(EPI_PRIMARY, 0.0)
|
|
512
|
+
|
|
513
|
+
# Si EPI es una estructura anidada, verificar integridad
|
|
514
|
+
if isinstance(epi, dict):
|
|
515
|
+
# Verificar que tiene las claves esperadas para fractality
|
|
516
|
+
expected_keys = {"continuous", "discrete", "grid"}
|
|
517
|
+
actual_keys = set(epi.keys())
|
|
518
|
+
|
|
519
|
+
if not actual_keys.issubset(expected_keys):
|
|
520
|
+
violations.append(
|
|
521
|
+
InvariantViolation(
|
|
522
|
+
invariant_id=7,
|
|
523
|
+
severity=InvariantSeverity.WARNING,
|
|
524
|
+
description="EPI structure has unexpected keys (fractality may be broken)",
|
|
525
|
+
node_id=str(node_id),
|
|
526
|
+
expected_value=f"Keys subset of {expected_keys}",
|
|
527
|
+
actual_value=f"Keys: {actual_keys}",
|
|
528
|
+
suggestion="Verify nested EPI structure maintains identity",
|
|
529
|
+
)
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
# Verificar que los sub-EPIs tienen valores válidos
|
|
533
|
+
for key in ["continuous", "discrete"]:
|
|
534
|
+
if key in epi:
|
|
535
|
+
sub_epi = epi[key]
|
|
536
|
+
if isinstance(sub_epi, (tuple, list)):
|
|
537
|
+
for val in sub_epi:
|
|
538
|
+
if isinstance(val, complex) and not math.isfinite(
|
|
539
|
+
abs(val)
|
|
540
|
+
):
|
|
541
|
+
violations.append(
|
|
542
|
+
InvariantViolation(
|
|
543
|
+
invariant_id=7,
|
|
544
|
+
severity=InvariantSeverity.ERROR,
|
|
545
|
+
description=f"Sub-EPI '{key}' contains non-finite values",
|
|
546
|
+
node_id=str(node_id),
|
|
547
|
+
expected_value="finite values",
|
|
548
|
+
actual_value=f"{val}",
|
|
549
|
+
suggestion="Nested EPI identity compromised by invalid values",
|
|
550
|
+
)
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
return violations
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
class Invariant8_ControlledDeterminism(TNFRInvariant):
|
|
557
|
+
"""Invariante 8: Controlled determinism - reproducible and traceable."""
|
|
558
|
+
|
|
559
|
+
invariant_id = 8
|
|
560
|
+
description = "Controlled determinism: reproducible and traceable"
|
|
561
|
+
|
|
562
|
+
def validate(self, graph: TNFRGraph) -> list[InvariantViolation]:
|
|
563
|
+
violations = []
|
|
564
|
+
|
|
565
|
+
# Verificar que hay trazabilidad (history)
|
|
566
|
+
if hasattr(graph, "graph"):
|
|
567
|
+
config = graph.graph
|
|
568
|
+
|
|
569
|
+
# Verificar que hay sistema de history/trace
|
|
570
|
+
if "history" not in config and "HISTORY_MAXLEN" not in config:
|
|
571
|
+
violations.append(
|
|
572
|
+
InvariantViolation(
|
|
573
|
+
invariant_id=8,
|
|
574
|
+
severity=InvariantSeverity.WARNING,
|
|
575
|
+
description="No history tracking configured (traceability compromised)",
|
|
576
|
+
expected_value="history or HISTORY_MAXLEN in config",
|
|
577
|
+
actual_value="Not found",
|
|
578
|
+
suggestion="Configure history tracking for reproducibility",
|
|
579
|
+
)
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
# Verificar que hay seed configurado para reproducibilidad
|
|
583
|
+
if "RANDOM_SEED" not in config and "seed" not in config:
|
|
584
|
+
violations.append(
|
|
585
|
+
InvariantViolation(
|
|
586
|
+
invariant_id=8,
|
|
587
|
+
severity=InvariantSeverity.WARNING,
|
|
588
|
+
description="No random seed configured (reproducibility at risk)",
|
|
589
|
+
expected_value="RANDOM_SEED or seed in config",
|
|
590
|
+
actual_value="Not found",
|
|
591
|
+
suggestion="Set random seed for deterministic simulations",
|
|
592
|
+
)
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
return violations
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
class Invariant9_StructuralMetrics(TNFRInvariant):
|
|
599
|
+
"""Invariante 9: Structural metrics - expose C(t), Si, phase, νf."""
|
|
600
|
+
|
|
601
|
+
invariant_id = 9
|
|
602
|
+
description = "Structural metrics: expose C(t), Si, phase, νf"
|
|
603
|
+
|
|
604
|
+
def validate(self, graph: TNFRGraph) -> list[InvariantViolation]:
|
|
605
|
+
violations = []
|
|
606
|
+
|
|
607
|
+
# Verificar que los nodos exponen métricas estructurales
|
|
608
|
+
for node_id in graph.nodes():
|
|
609
|
+
node_data = graph.nodes[node_id]
|
|
610
|
+
|
|
611
|
+
# Verificar métricas básicas (νf, phase ya verificados en otros invariantes)
|
|
612
|
+
# Aquí verificamos métricas derivadas si existen
|
|
613
|
+
|
|
614
|
+
# Si hay métrica Si (sense index), verificar que es válida
|
|
615
|
+
if "Si" in node_data or "si" in node_data:
|
|
616
|
+
si = node_data.get("Si", node_data.get("si", 0.0))
|
|
617
|
+
if isinstance(si, (int, float)):
|
|
618
|
+
if not (0.0 <= si <= 1.0):
|
|
619
|
+
violations.append(
|
|
620
|
+
InvariantViolation(
|
|
621
|
+
invariant_id=9,
|
|
622
|
+
severity=InvariantSeverity.WARNING,
|
|
623
|
+
description="Sense index (Si) outside expected range",
|
|
624
|
+
node_id=str(node_id),
|
|
625
|
+
expected_value="0.0 <= Si <= 1.0",
|
|
626
|
+
actual_value=si,
|
|
627
|
+
suggestion="Verify Si calculation maintains TNFR semantics",
|
|
628
|
+
)
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
# Verificar que hay métricas globales de coherencia
|
|
632
|
+
if hasattr(graph, "graph"):
|
|
633
|
+
config = graph.graph
|
|
634
|
+
has_coherence_metric = (
|
|
635
|
+
"coherence" in config or "C_t" in config or "total_coherence" in config
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
if not has_coherence_metric:
|
|
639
|
+
violations.append(
|
|
640
|
+
InvariantViolation(
|
|
641
|
+
invariant_id=9,
|
|
642
|
+
severity=InvariantSeverity.WARNING,
|
|
643
|
+
description="No global coherence metric C(t) exposed",
|
|
644
|
+
expected_value="C(t) or coherence metric in graph",
|
|
645
|
+
actual_value="Not found",
|
|
646
|
+
suggestion="Expose total coherence C(t) for structural metrics",
|
|
647
|
+
)
|
|
648
|
+
)
|
|
649
|
+
|
|
650
|
+
return violations
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
class Invariant10_DomainNeutrality(TNFRInvariant):
|
|
654
|
+
"""Invariante 10: Domain neutrality - trans-scale and trans-domain."""
|
|
655
|
+
|
|
656
|
+
invariant_id = 10
|
|
657
|
+
description = "Domain neutrality: trans-scale and trans-domain"
|
|
658
|
+
|
|
659
|
+
def validate(self, graph: TNFRGraph) -> list[InvariantViolation]:
|
|
660
|
+
violations = []
|
|
661
|
+
|
|
662
|
+
# Verificar que no hay hard-coded domain assumptions
|
|
663
|
+
if hasattr(graph, "graph"):
|
|
664
|
+
config = graph.graph
|
|
665
|
+
|
|
666
|
+
# Buscar claves que sugieran assumptions específicas de dominio
|
|
667
|
+
domain_specific_keys = [
|
|
668
|
+
"physical_units",
|
|
669
|
+
"meters",
|
|
670
|
+
"seconds",
|
|
671
|
+
"temperature",
|
|
672
|
+
"biology",
|
|
673
|
+
"neurons",
|
|
674
|
+
"particles",
|
|
675
|
+
]
|
|
676
|
+
|
|
677
|
+
found_domain_keys = [key for key in domain_specific_keys if key in config]
|
|
678
|
+
|
|
679
|
+
if found_domain_keys:
|
|
680
|
+
violations.append(
|
|
681
|
+
InvariantViolation(
|
|
682
|
+
invariant_id=10,
|
|
683
|
+
severity=InvariantSeverity.WARNING,
|
|
684
|
+
description=f"Domain-specific keys found: {found_domain_keys}",
|
|
685
|
+
expected_value="Domain-neutral configuration",
|
|
686
|
+
actual_value=f"Found: {found_domain_keys}",
|
|
687
|
+
suggestion="Remove domain-specific assumptions from core engine",
|
|
688
|
+
)
|
|
689
|
+
)
|
|
690
|
+
|
|
691
|
+
# Verificar que las unidades son estructurales (Hz_str, no Hz físicos)
|
|
692
|
+
for node_id in graph.nodes():
|
|
693
|
+
node_data = graph.nodes[node_id]
|
|
694
|
+
|
|
695
|
+
# Si hay unidades explícitas, deben ser estructurales
|
|
696
|
+
if "units" in node_data:
|
|
697
|
+
units = node_data["units"]
|
|
698
|
+
if isinstance(units, dict) and "vf" in units:
|
|
699
|
+
if units["vf"] not in ["Hz_str", "structural_hertz", None]:
|
|
700
|
+
violations.append(
|
|
701
|
+
InvariantViolation(
|
|
702
|
+
invariant_id=10,
|
|
703
|
+
severity=InvariantSeverity.ERROR,
|
|
704
|
+
description=f"Non-structural units for νf: {units['vf']}",
|
|
705
|
+
node_id=str(node_id),
|
|
706
|
+
expected_value="Hz_str or structural_hertz",
|
|
707
|
+
actual_value=units["vf"],
|
|
708
|
+
suggestion="Use structural units (Hz_str) not physical units",
|
|
709
|
+
)
|
|
710
|
+
)
|
|
711
|
+
|
|
712
|
+
return violations
|