tnfr 4.5.2__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 +334 -50
- 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 +214 -37
- 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 +149 -556
- tnfr/cache.pyi +13 -0
- tnfr/cli/__init__.py +51 -16
- tnfr/cli/__init__.pyi +26 -0
- tnfr/cli/arguments.py +344 -32
- tnfr/cli/arguments.pyi +29 -0
- tnfr/cli/execution.py +676 -50
- tnfr/cli/execution.pyi +70 -0
- tnfr/cli/interactive_validator.py +614 -0
- tnfr/cli/utils.py +18 -3
- 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/{constants_glyphs.py → config/constants.py} +26 -20
- tnfr/config/constants.pyi +12 -0
- tnfr/config/defaults.py +54 -0
- tnfr/{constants/core.py → config/defaults_core.py} +59 -6
- 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 +51 -133
- tnfr/constants/__init__.pyi +92 -0
- tnfr/constants/aliases.py +33 -0
- tnfr/constants/aliases.pyi +27 -0
- tnfr/constants/init.py +3 -1
- tnfr/constants/init.pyi +12 -0
- tnfr/constants/metric.py +9 -15
- 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 +213 -633
- 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 +2699 -398
- 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 +496 -102
- 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 +10 -5
- 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 +77 -55
- 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 +29 -50
- tnfr/flatten.pyi +21 -0
- tnfr/gamma.py +66 -53
- tnfr/gamma.pyi +36 -0
- tnfr/glyph_history.py +144 -57
- tnfr/glyph_history.pyi +35 -0
- tnfr/glyph_runtime.py +19 -0
- tnfr/glyph_runtime.pyi +8 -0
- tnfr/immutable.py +70 -30
- tnfr/immutable.pyi +36 -0
- tnfr/initialization.py +22 -16
- tnfr/initialization.pyi +65 -0
- tnfr/io.py +5 -241
- tnfr/io.pyi +13 -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 +47 -9
- 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 +1510 -330
- tnfr/metrics/coherence.pyi +129 -0
- tnfr/metrics/common.py +23 -16
- tnfr/metrics/common.pyi +35 -0
- tnfr/metrics/core.py +251 -36
- tnfr/metrics/core.pyi +13 -0
- tnfr/metrics/diagnosis.py +709 -110
- tnfr/metrics/diagnosis.pyi +86 -0
- tnfr/metrics/emergence.py +245 -0
- tnfr/metrics/export.py +60 -18
- tnfr/metrics/export.pyi +7 -0
- tnfr/metrics/glyph_timing.py +233 -43
- 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 +63 -28
- tnfr/metrics/reporting.pyi +25 -0
- tnfr/metrics/sense_index.py +1126 -43
- tnfr/metrics/sense_index.pyi +9 -0
- tnfr/metrics/trig.py +215 -23
- tnfr/metrics/trig.pyi +13 -0
- tnfr/metrics/trig_cache.py +148 -24
- tnfr/metrics/trig_cache.pyi +10 -0
- tnfr/multiscale/__init__.py +32 -0
- tnfr/multiscale/hierarchical.py +517 -0
- tnfr/node.py +646 -140
- tnfr/node.pyi +139 -0
- tnfr/observers.py +160 -45
- tnfr/observers.pyi +31 -0
- tnfr/ontosim.py +23 -19
- tnfr/ontosim.pyi +28 -0
- tnfr/operators/__init__.py +1358 -106
- 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 +107 -38
- 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 +1415 -91
- 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 +75 -151
- 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 +59 -22
- tnfr/selector.pyi +19 -0
- tnfr/sense.py +92 -67
- 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 +639 -263
- 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 +2 -4
- 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 +300 -126
- 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 +743 -12
- 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/{callback_utils.py → utils/callbacks.py} +26 -39
- 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/{helpers → utils}/numeric.py +51 -24
- 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-4.5.2.dist-info → tnfr-8.5.0.dist-info}/entry_points.txt +1 -0
- {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/licenses/LICENSE.md +1 -1
- tnfr/collections_utils.py +0 -300
- tnfr/config.py +0 -32
- tnfr/grammar.py +0 -344
- tnfr/graph_utils.py +0 -84
- tnfr/helpers/__init__.py +0 -71
- tnfr/import_utils.py +0 -228
- tnfr/json_utils.py +0 -162
- tnfr/logging_utils.py +0 -116
- tnfr/presets.py +0 -60
- tnfr/validators.py +0 -84
- tnfr/value_utils.py +0 -59
- tnfr-4.5.2.dist-info/METADATA +0 -379
- tnfr-4.5.2.dist-info/RECORD +0 -67
- {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/WHEEL +0 -0
- {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
"""Fused ΔNFR computation kernels for optimized backend.
|
|
2
|
+
|
|
3
|
+
This module provides optimized implementations of ΔNFR computation that fuse
|
|
4
|
+
multiple operations to reduce memory traffic and improve cache locality.
|
|
5
|
+
|
|
6
|
+
The fused kernels maintain exact TNFR semantics while achieving better
|
|
7
|
+
performance through:
|
|
8
|
+
|
|
9
|
+
1. Combined gradient computation (phase + EPI + topology in single pass)
|
|
10
|
+
2. Reduced intermediate array allocations
|
|
11
|
+
3. Better cache locality through sequential memory access
|
|
12
|
+
4. Optional Numba JIT compilation for critical loops
|
|
13
|
+
|
|
14
|
+
All implementations preserve TNFR structural invariants:
|
|
15
|
+
- ΔNFR = w_phase·g_phase + w_epi·g_epi + w_vf·g_vf + w_topo·g_topo
|
|
16
|
+
- Isolated nodes receive ΔNFR = 0
|
|
17
|
+
- Deterministic results with fixed topology
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import math
|
|
23
|
+
from typing import Any, Mapping
|
|
24
|
+
|
|
25
|
+
from ..utils import get_numpy, get_logger
|
|
26
|
+
|
|
27
|
+
logger = get_logger(__name__)
|
|
28
|
+
|
|
29
|
+
# Try to import Numba for JIT acceleration
|
|
30
|
+
_NUMBA_AVAILABLE = False
|
|
31
|
+
_numba = None
|
|
32
|
+
try:
|
|
33
|
+
import numba
|
|
34
|
+
|
|
35
|
+
_numba = numba
|
|
36
|
+
_NUMBA_AVAILABLE = True
|
|
37
|
+
logger.debug("Numba JIT available for fused gradient acceleration")
|
|
38
|
+
except ImportError:
|
|
39
|
+
logger.debug("Numba not available, using pure NumPy implementation")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _compute_fused_gradients_jit_kernel(
|
|
43
|
+
edge_src,
|
|
44
|
+
edge_dst,
|
|
45
|
+
phase,
|
|
46
|
+
epi,
|
|
47
|
+
vf,
|
|
48
|
+
w_phase: float,
|
|
49
|
+
w_epi: float,
|
|
50
|
+
w_vf: float,
|
|
51
|
+
w_topo: float,
|
|
52
|
+
delta_nfr,
|
|
53
|
+
):
|
|
54
|
+
"""Numba JIT kernel for fused gradient computation.
|
|
55
|
+
|
|
56
|
+
This function is designed to be JIT-compiled by Numba for maximum
|
|
57
|
+
performance. It operates directly on arrays with explicit loops.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
edge_src : ndarray
|
|
62
|
+
Source node indices (int)
|
|
63
|
+
edge_dst : ndarray
|
|
64
|
+
Destination node indices (int)
|
|
65
|
+
phase : ndarray
|
|
66
|
+
Phase values (float)
|
|
67
|
+
epi : ndarray
|
|
68
|
+
EPI values (float)
|
|
69
|
+
vf : ndarray
|
|
70
|
+
νf values (float)
|
|
71
|
+
w_phase : float
|
|
72
|
+
Phase gradient weight
|
|
73
|
+
w_epi : float
|
|
74
|
+
EPI gradient weight
|
|
75
|
+
w_vf : float
|
|
76
|
+
νf gradient weight
|
|
77
|
+
w_topo : float
|
|
78
|
+
Topology gradient weight
|
|
79
|
+
delta_nfr : ndarray
|
|
80
|
+
Output array for ΔNFR (modified in-place)
|
|
81
|
+
"""
|
|
82
|
+
n_edges = edge_src.shape[0]
|
|
83
|
+
|
|
84
|
+
for i in range(n_edges):
|
|
85
|
+
src = edge_src[i]
|
|
86
|
+
dst = edge_dst[i]
|
|
87
|
+
|
|
88
|
+
# Compute gradients for this edge
|
|
89
|
+
phase_diff = math.sin(phase[dst] - phase[src])
|
|
90
|
+
epi_diff = epi[dst] - epi[src]
|
|
91
|
+
vf_diff = vf[dst] - vf[src]
|
|
92
|
+
|
|
93
|
+
# Fused contribution
|
|
94
|
+
contrib = (
|
|
95
|
+
w_phase * phase_diff + w_epi * epi_diff + w_vf * vf_diff + w_topo * 1.0
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Accumulate to destination node
|
|
99
|
+
delta_nfr[dst] += contrib
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# Create JIT-compiled version if Numba is available
|
|
103
|
+
if _NUMBA_AVAILABLE:
|
|
104
|
+
try:
|
|
105
|
+
_compute_fused_gradients_jit = _numba.njit(
|
|
106
|
+
_compute_fused_gradients_jit_kernel,
|
|
107
|
+
parallel=False,
|
|
108
|
+
fastmath=True,
|
|
109
|
+
cache=True,
|
|
110
|
+
)
|
|
111
|
+
logger.debug("Numba JIT compilation successful for fused gradients")
|
|
112
|
+
except Exception as e:
|
|
113
|
+
logger.warning(f"Numba JIT compilation failed: {e}")
|
|
114
|
+
_compute_fused_gradients_jit = _compute_fused_gradients_jit_kernel
|
|
115
|
+
else:
|
|
116
|
+
_compute_fused_gradients_jit = _compute_fused_gradients_jit_kernel
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def compute_fused_gradients(
|
|
120
|
+
*,
|
|
121
|
+
edge_src: Any,
|
|
122
|
+
edge_dst: Any,
|
|
123
|
+
phase: Any,
|
|
124
|
+
epi: Any,
|
|
125
|
+
vf: Any,
|
|
126
|
+
weights: Mapping[str, float],
|
|
127
|
+
np: Any,
|
|
128
|
+
use_jit: bool = True,
|
|
129
|
+
) -> Any:
|
|
130
|
+
"""Compute all ΔNFR gradients in a fused kernel.
|
|
131
|
+
|
|
132
|
+
This function combines phase, EPI, and νf gradient computations into
|
|
133
|
+
a single pass over the edge list, reducing memory traffic by ~50%
|
|
134
|
+
compared to separate accumulations.
|
|
135
|
+
|
|
136
|
+
When Numba is available and `use_jit=True`, uses JIT-compiled inner
|
|
137
|
+
loop for additional 2-3x speedup on large graphs.
|
|
138
|
+
|
|
139
|
+
Parameters
|
|
140
|
+
----------
|
|
141
|
+
edge_src : array-like
|
|
142
|
+
Source node indices for each edge (shape: [E])
|
|
143
|
+
edge_dst : array-like
|
|
144
|
+
Destination node indices for each edge (shape: [E])
|
|
145
|
+
phase : array-like
|
|
146
|
+
Phase values for each node (shape: [N])
|
|
147
|
+
epi : array-like
|
|
148
|
+
EPI values for each node (shape: [N])
|
|
149
|
+
vf : array-like
|
|
150
|
+
Structural frequency νf for each node (shape: [N])
|
|
151
|
+
weights : Mapping[str, float]
|
|
152
|
+
ΔNFR component weights (w_phase, w_epi, w_vf, w_topo)
|
|
153
|
+
np : module
|
|
154
|
+
NumPy module
|
|
155
|
+
use_jit : bool, default=True
|
|
156
|
+
Whether to use JIT compilation if available
|
|
157
|
+
|
|
158
|
+
Returns
|
|
159
|
+
-------
|
|
160
|
+
ndarray
|
|
161
|
+
Fused gradient vector (shape: [N])
|
|
162
|
+
|
|
163
|
+
Notes
|
|
164
|
+
-----
|
|
165
|
+
The fused kernel computes:
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
for each edge (i → j):
|
|
169
|
+
g_phase = sin(phase[j] - phase[i])
|
|
170
|
+
g_epi = epi[j] - epi[i]
|
|
171
|
+
g_vf = vf[j] - vf[i]
|
|
172
|
+
|
|
173
|
+
delta_nfr[dst] += w_phase * g_phase + w_epi * g_epi + w_vf * g_vf + w_topo
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Where dst is the destination node index (j) for each edge.
|
|
177
|
+
|
|
178
|
+
By processing all components simultaneously, we:
|
|
179
|
+
- Access edge arrays once instead of 3+ times
|
|
180
|
+
- Reduce cache misses on node attribute arrays
|
|
181
|
+
- Minimize temporary array allocations
|
|
182
|
+
- Enable JIT compilation for 2-3x additional speedup
|
|
183
|
+
|
|
184
|
+
Examples
|
|
185
|
+
--------
|
|
186
|
+
>>> import numpy as np
|
|
187
|
+
>>> edge_src = np.array([0, 0, 1])
|
|
188
|
+
>>> edge_dst = np.array([1, 2, 2])
|
|
189
|
+
>>> phase = np.array([0.0, 0.1, 0.2])
|
|
190
|
+
>>> epi = np.array([0.5, 0.6, 0.7])
|
|
191
|
+
>>> vf = np.array([1.0, 1.0, 1.0])
|
|
192
|
+
>>> weights = {'w_phase': 0.3, 'w_epi': 0.3, 'w_vf': 0.2, 'w_topo': 0.2}
|
|
193
|
+
>>> result = compute_fused_gradients(
|
|
194
|
+
... edge_src=edge_src, edge_dst=edge_dst,
|
|
195
|
+
... phase=phase, epi=epi, vf=vf,
|
|
196
|
+
... weights=weights, np=np
|
|
197
|
+
... )
|
|
198
|
+
>>> result.shape
|
|
199
|
+
(3,)
|
|
200
|
+
"""
|
|
201
|
+
n_nodes = phase.shape[0]
|
|
202
|
+
n_edges = edge_src.shape[0]
|
|
203
|
+
|
|
204
|
+
# Extract weights
|
|
205
|
+
w_phase = float(weights.get("w_phase", 0.0))
|
|
206
|
+
w_epi = float(weights.get("w_epi", 0.0))
|
|
207
|
+
w_vf = float(weights.get("w_vf", 0.0))
|
|
208
|
+
w_topo = float(weights.get("w_topo", 0.0))
|
|
209
|
+
|
|
210
|
+
# Allocate result
|
|
211
|
+
delta_nfr = np.zeros(n_nodes, dtype=float)
|
|
212
|
+
|
|
213
|
+
if n_edges == 0:
|
|
214
|
+
return delta_nfr
|
|
215
|
+
|
|
216
|
+
# Choose implementation based on JIT availability and preference
|
|
217
|
+
if use_jit and _NUMBA_AVAILABLE and n_edges > 100:
|
|
218
|
+
# Use JIT-compiled kernel for large graphs
|
|
219
|
+
_compute_fused_gradients_jit(
|
|
220
|
+
edge_src, edge_dst, phase, epi, vf, w_phase, w_epi, w_vf, w_topo, delta_nfr
|
|
221
|
+
)
|
|
222
|
+
else:
|
|
223
|
+
# Use vectorized NumPy implementation
|
|
224
|
+
# Compute edge contributions (fused)
|
|
225
|
+
phase_src = phase[edge_src]
|
|
226
|
+
phase_dst = phase[edge_dst]
|
|
227
|
+
phase_diff = np.sin(phase_dst - phase_src)
|
|
228
|
+
|
|
229
|
+
epi_src = epi[edge_src]
|
|
230
|
+
epi_dst = epi[edge_dst]
|
|
231
|
+
epi_diff = epi_dst - epi_src
|
|
232
|
+
|
|
233
|
+
vf_src = vf[edge_src]
|
|
234
|
+
vf_dst = vf[edge_dst]
|
|
235
|
+
vf_diff = vf_dst - vf_src
|
|
236
|
+
|
|
237
|
+
# Fused contribution per edge
|
|
238
|
+
edge_contrib = (
|
|
239
|
+
w_phase * phase_diff + w_epi * epi_diff + w_vf * vf_diff + w_topo * 1.0
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
# Accumulate contributions to destination nodes
|
|
243
|
+
np.add.at(delta_nfr, edge_dst, edge_contrib)
|
|
244
|
+
|
|
245
|
+
return delta_nfr
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def compute_fused_gradients_symmetric(
|
|
249
|
+
*,
|
|
250
|
+
edge_src: Any,
|
|
251
|
+
edge_dst: Any,
|
|
252
|
+
phase: Any,
|
|
253
|
+
epi: Any,
|
|
254
|
+
vf: Any,
|
|
255
|
+
weights: Mapping[str, float],
|
|
256
|
+
np: Any,
|
|
257
|
+
) -> Any:
|
|
258
|
+
"""Compute ΔNFR gradients using TNFR canonical formula with neighbor means.
|
|
259
|
+
|
|
260
|
+
For undirected graphs, this function implements the canonical TNFR gradient
|
|
261
|
+
computation where each node's gradient is computed from the circular mean
|
|
262
|
+
of its neighbors' phases (divided by π), and arithmetic means for EPI/νf.
|
|
263
|
+
|
|
264
|
+
The canonical TNFR formulas are:
|
|
265
|
+
- Phase gradient: g_phase = sin(θ_node - θ̄_neighbors) / π
|
|
266
|
+
- EPI/νf gradient: g = x̄_neighbors - x_node
|
|
267
|
+
- Topology gradient: g_topo = degree · w_topo
|
|
268
|
+
|
|
269
|
+
This implementation performs the computation in two passes:
|
|
270
|
+
1. Accumulate neighbor cos/sin sums and value sums for means
|
|
271
|
+
2. Compute gradients from means for each node
|
|
272
|
+
|
|
273
|
+
Parameters
|
|
274
|
+
----------
|
|
275
|
+
edge_src : array-like
|
|
276
|
+
Source node indices (shape: [E])
|
|
277
|
+
edge_dst : array-like
|
|
278
|
+
Destination node indices (shape: [E])
|
|
279
|
+
phase : array-like
|
|
280
|
+
Phase values (shape: [N])
|
|
281
|
+
epi : array-like
|
|
282
|
+
EPI values (shape: [N])
|
|
283
|
+
vf : array-like
|
|
284
|
+
νf values (shape: [N])
|
|
285
|
+
weights : Mapping[str, float]
|
|
286
|
+
Component weights (w_phase, w_epi, w_vf, w_topo)
|
|
287
|
+
np : module
|
|
288
|
+
NumPy module
|
|
289
|
+
|
|
290
|
+
Returns
|
|
291
|
+
-------
|
|
292
|
+
ndarray
|
|
293
|
+
ΔNFR gradient vector (shape: [N])
|
|
294
|
+
|
|
295
|
+
Notes
|
|
296
|
+
-----
|
|
297
|
+
The division by π in the phase gradient is part of the canonical TNFR
|
|
298
|
+
formulation and ensures proper scaling of phase-based reorganization.
|
|
299
|
+
|
|
300
|
+
For isolated nodes (no neighbors), all gradients are zero.
|
|
301
|
+
|
|
302
|
+
Examples
|
|
303
|
+
--------
|
|
304
|
+
>>> import numpy as np
|
|
305
|
+
>>> edge_src = np.array([0, 0, 1])
|
|
306
|
+
>>> edge_dst = np.array([1, 2, 2])
|
|
307
|
+
>>> phase = np.array([0.0, 0.1, 0.2])
|
|
308
|
+
>>> epi = np.array([0.5, 0.6, 0.7])
|
|
309
|
+
>>> vf = np.array([1.0, 1.0, 1.0])
|
|
310
|
+
>>> weights = {'w_phase': 0.3, 'w_epi': 0.3, 'w_vf': 0.2, 'w_topo': 0.2}
|
|
311
|
+
>>> result = compute_fused_gradients_symmetric(
|
|
312
|
+
... edge_src=edge_src, edge_dst=edge_dst,
|
|
313
|
+
... phase=phase, epi=epi, vf=vf,
|
|
314
|
+
... weights=weights, np=np
|
|
315
|
+
... )
|
|
316
|
+
>>> result.shape
|
|
317
|
+
(3,)
|
|
318
|
+
"""
|
|
319
|
+
n_nodes = phase.shape[0]
|
|
320
|
+
n_edges = edge_src.shape[0]
|
|
321
|
+
|
|
322
|
+
w_phase = float(weights.get("w_phase", 0.0))
|
|
323
|
+
w_epi = float(weights.get("w_epi", 0.0))
|
|
324
|
+
w_vf = float(weights.get("w_vf", 0.0))
|
|
325
|
+
w_topo = float(weights.get("w_topo", 0.0))
|
|
326
|
+
|
|
327
|
+
delta_nfr = np.zeros(n_nodes, dtype=float)
|
|
328
|
+
|
|
329
|
+
if n_edges == 0:
|
|
330
|
+
return delta_nfr
|
|
331
|
+
|
|
332
|
+
# Pass 1: Accumulate neighbor statistics for computing means
|
|
333
|
+
# For phase: accumulate cos/sin sums for circular mean
|
|
334
|
+
# For EPI/vf: accumulate value sums for arithmetic mean
|
|
335
|
+
neighbor_cos_sum = np.zeros(n_nodes, dtype=float)
|
|
336
|
+
neighbor_sin_sum = np.zeros(n_nodes, dtype=float)
|
|
337
|
+
neighbor_epi_sum = np.zeros(n_nodes, dtype=float)
|
|
338
|
+
neighbor_vf_sum = np.zeros(n_nodes, dtype=float)
|
|
339
|
+
neighbor_count = np.zeros(n_nodes, dtype=float)
|
|
340
|
+
|
|
341
|
+
# For undirected graphs, each edge contributes to both endpoints
|
|
342
|
+
# Extract neighbor values
|
|
343
|
+
phase_src_vals = phase[edge_src]
|
|
344
|
+
phase_dst_vals = phase[edge_dst]
|
|
345
|
+
epi_src_vals = epi[edge_src]
|
|
346
|
+
epi_dst_vals = epi[edge_dst]
|
|
347
|
+
vf_src_vals = vf[edge_src]
|
|
348
|
+
vf_dst_vals = vf[edge_dst]
|
|
349
|
+
|
|
350
|
+
# Accumulate from src to dst (dst's neighbors include src)
|
|
351
|
+
np.add.at(neighbor_cos_sum, edge_dst, np.cos(phase_src_vals))
|
|
352
|
+
np.add.at(neighbor_sin_sum, edge_dst, np.sin(phase_src_vals))
|
|
353
|
+
np.add.at(neighbor_epi_sum, edge_dst, epi_src_vals)
|
|
354
|
+
np.add.at(neighbor_vf_sum, edge_dst, vf_src_vals)
|
|
355
|
+
np.add.at(neighbor_count, edge_dst, 1.0)
|
|
356
|
+
|
|
357
|
+
# Accumulate from dst to src (src's neighbors include dst)
|
|
358
|
+
np.add.at(neighbor_cos_sum, edge_src, np.cos(phase_dst_vals))
|
|
359
|
+
np.add.at(neighbor_sin_sum, edge_src, np.sin(phase_dst_vals))
|
|
360
|
+
np.add.at(neighbor_epi_sum, edge_src, epi_dst_vals)
|
|
361
|
+
np.add.at(neighbor_vf_sum, edge_src, vf_dst_vals)
|
|
362
|
+
np.add.at(neighbor_count, edge_src, 1.0)
|
|
363
|
+
|
|
364
|
+
# Pass 2: Compute gradients from means
|
|
365
|
+
# Avoid division by zero for isolated nodes
|
|
366
|
+
has_neighbors = neighbor_count > 0
|
|
367
|
+
|
|
368
|
+
# Compute circular mean phase for nodes with neighbors
|
|
369
|
+
phase_mean = np.zeros(n_nodes, dtype=float)
|
|
370
|
+
phase_mean[has_neighbors] = np.arctan2(
|
|
371
|
+
neighbor_sin_sum[has_neighbors], neighbor_cos_sum[has_neighbors]
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
# Compute arithmetic means for EPI and νf
|
|
375
|
+
epi_mean = np.zeros(n_nodes, dtype=float)
|
|
376
|
+
vf_mean = np.zeros(n_nodes, dtype=float)
|
|
377
|
+
epi_mean[has_neighbors] = (
|
|
378
|
+
neighbor_epi_sum[has_neighbors] / neighbor_count[has_neighbors]
|
|
379
|
+
)
|
|
380
|
+
vf_mean[has_neighbors] = (
|
|
381
|
+
neighbor_vf_sum[has_neighbors] / neighbor_count[has_neighbors]
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
# Compute gradients using TNFR canonical formula
|
|
385
|
+
# Phase: g_phase = -angle_diff(θ_node, θ_mean) / π
|
|
386
|
+
# angle_diff(a, b) = (a - b + π) % 2π - π (minimal angular difference)
|
|
387
|
+
phase_diff = (phase_mean - phase + np.pi) % (2 * np.pi) - np.pi
|
|
388
|
+
g_phase = phase_diff / np.pi
|
|
389
|
+
g_phase[~has_neighbors] = 0.0 # Isolated nodes have no gradient
|
|
390
|
+
|
|
391
|
+
# EPI/νf: g = mean - node_value
|
|
392
|
+
g_epi = epi_mean - epi
|
|
393
|
+
g_epi[~has_neighbors] = 0.0
|
|
394
|
+
|
|
395
|
+
g_vf = vf_mean - vf
|
|
396
|
+
g_vf[~has_neighbors] = 0.0
|
|
397
|
+
|
|
398
|
+
# Topology: each neighbor contributes w_topo
|
|
399
|
+
g_topo = neighbor_count * w_topo
|
|
400
|
+
|
|
401
|
+
# Combine gradients
|
|
402
|
+
delta_nfr = (
|
|
403
|
+
w_phase * g_phase
|
|
404
|
+
+ w_epi * g_epi
|
|
405
|
+
+ w_vf * g_vf
|
|
406
|
+
+ g_topo # Already scaled by w_topo
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
return delta_nfr
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def apply_vf_scaling(
|
|
413
|
+
*,
|
|
414
|
+
delta_nfr: Any,
|
|
415
|
+
vf: Any,
|
|
416
|
+
np: Any,
|
|
417
|
+
) -> None:
|
|
418
|
+
"""Apply structural frequency scaling to ΔNFR in-place.
|
|
419
|
+
|
|
420
|
+
Applies the fundamental TNFR transformation:
|
|
421
|
+
ΔNFR_final = νf · ΔNFR_gradient
|
|
422
|
+
|
|
423
|
+
This completes the nodal equation: ∂EPI/∂t = νf · ΔNFR(t)
|
|
424
|
+
|
|
425
|
+
Parameters
|
|
426
|
+
----------
|
|
427
|
+
delta_nfr : array-like
|
|
428
|
+
Gradient vector to scale in-place (shape: [N])
|
|
429
|
+
vf : array-like
|
|
430
|
+
Structural frequency for each node (shape: [N])
|
|
431
|
+
np : module
|
|
432
|
+
NumPy module
|
|
433
|
+
|
|
434
|
+
Notes
|
|
435
|
+
-----
|
|
436
|
+
Modified in-place to avoid allocating result array.
|
|
437
|
+
|
|
438
|
+
Examples
|
|
439
|
+
--------
|
|
440
|
+
>>> import numpy as np
|
|
441
|
+
>>> delta_nfr = np.array([1.0, 2.0, 3.0])
|
|
442
|
+
>>> vf = np.array([0.5, 1.0, 1.5])
|
|
443
|
+
>>> apply_vf_scaling(delta_nfr=delta_nfr, vf=vf, np=np)
|
|
444
|
+
>>> delta_nfr
|
|
445
|
+
array([0.5, 2. , 4.5])
|
|
446
|
+
"""
|
|
447
|
+
np.multiply(delta_nfr, vf, out=delta_nfr)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
__all__ = [
|
|
451
|
+
"compute_fused_gradients",
|
|
452
|
+
"compute_fused_gradients_symmetric",
|
|
453
|
+
"apply_vf_scaling",
|
|
454
|
+
]
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""Structural homeostasis for TNFR nodes.
|
|
2
|
+
|
|
3
|
+
This module implements homeostatic regulation that maintains nodal parameters
|
|
4
|
+
within target ranges. When parameters drift outside acceptable bounds, corrective
|
|
5
|
+
operators are automatically applied to restore equilibrium.
|
|
6
|
+
|
|
7
|
+
Homeostasis ensures long-term stability while allowing dynamic adaptation
|
|
8
|
+
within safe operating ranges.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import TYPE_CHECKING
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from ..types import TNFRGraph, NodeId
|
|
17
|
+
|
|
18
|
+
from ..alias import get_attr
|
|
19
|
+
from ..constants.aliases import ALIAS_DNFR, ALIAS_EPI, ALIAS_VF
|
|
20
|
+
from ..operators.registry import get_operator_class
|
|
21
|
+
from ..config.operator_names import (
|
|
22
|
+
COHERENCE,
|
|
23
|
+
CONTRACTION,
|
|
24
|
+
EMISSION,
|
|
25
|
+
EXPANSION,
|
|
26
|
+
RECURSIVITY,
|
|
27
|
+
SILENCE,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
__all__ = ["StructuralHomeostasis"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class StructuralHomeostasis:
|
|
34
|
+
"""Maintains dynamic equilibrium in nodal parameters.
|
|
35
|
+
|
|
36
|
+
This class monitors EPI, νf, and ΔNFR values and applies corrective operators
|
|
37
|
+
when they drift outside target ranges. The goal is to maintain healthy
|
|
38
|
+
structural dynamics without constraining natural evolution.
|
|
39
|
+
|
|
40
|
+
**Homeostatic Principles:**
|
|
41
|
+
|
|
42
|
+
- **EPI range**: Maintain adequate activation without saturation
|
|
43
|
+
- **νf range**: Keep reorganization rate within functional bounds
|
|
44
|
+
- **ΔNFR range**: Prevent excessive reorganization pressure
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
graph : TNFRGraph
|
|
49
|
+
Graph containing the regulated node
|
|
50
|
+
node : NodeId
|
|
51
|
+
Identifier of the node to regulate
|
|
52
|
+
epi_range : tuple[float, float], optional
|
|
53
|
+
Target EPI range (min, max). Default: (0.4, 0.8)
|
|
54
|
+
vf_range : tuple[float, float], optional
|
|
55
|
+
Target νf range (min, max). Default: (0.8, 1.2)
|
|
56
|
+
dnfr_range : tuple[float, float], optional
|
|
57
|
+
Target ΔNFR range (min, max). Default: (0.0, 0.15)
|
|
58
|
+
|
|
59
|
+
Attributes
|
|
60
|
+
----------
|
|
61
|
+
G : TNFRGraph
|
|
62
|
+
Graph reference
|
|
63
|
+
node : NodeId
|
|
64
|
+
Node identifier
|
|
65
|
+
epi_range : tuple[float, float]
|
|
66
|
+
Target EPI range (min, max)
|
|
67
|
+
vf_range : tuple[float, float]
|
|
68
|
+
Target νf range (min, max)
|
|
69
|
+
dnfr_range : tuple[float, float]
|
|
70
|
+
Target ΔNFR range (min, max)
|
|
71
|
+
|
|
72
|
+
Examples
|
|
73
|
+
--------
|
|
74
|
+
>>> from tnfr.structural import create_nfr
|
|
75
|
+
>>> from tnfr.dynamics.homeostasis import StructuralHomeostasis
|
|
76
|
+
>>> G, node = create_nfr("test_node")
|
|
77
|
+
>>> homeostasis = StructuralHomeostasis(G, node)
|
|
78
|
+
>>> homeostasis.maintain_equilibrium()
|
|
79
|
+
|
|
80
|
+
Notes
|
|
81
|
+
-----
|
|
82
|
+
Corrective operators follow TNFR canonical principles:
|
|
83
|
+
|
|
84
|
+
- **Low EPI**: Apply AL (Emission) to activate
|
|
85
|
+
- **High EPI**: Apply NUL (Contraction) to reduce
|
|
86
|
+
- **Low νf**: Apply VAL (Expansion) to increase frequency
|
|
87
|
+
- **High νf**: Apply SHA (Silence) to slow down
|
|
88
|
+
- **High ΔNFR**: Apply IL (Coherence) to stabilize
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
def __init__(
|
|
92
|
+
self,
|
|
93
|
+
graph: TNFRGraph,
|
|
94
|
+
node: NodeId,
|
|
95
|
+
epi_range: tuple[float, float] = (0.4, 0.8),
|
|
96
|
+
vf_range: tuple[float, float] = (0.8, 1.2),
|
|
97
|
+
dnfr_range: tuple[float, float] = (0.0, 0.15),
|
|
98
|
+
) -> None:
|
|
99
|
+
self.G = graph
|
|
100
|
+
self.node = node
|
|
101
|
+
|
|
102
|
+
# Target ranges for homeostatic regulation
|
|
103
|
+
self.epi_range = epi_range
|
|
104
|
+
self.vf_range = vf_range
|
|
105
|
+
self.dnfr_range = dnfr_range
|
|
106
|
+
|
|
107
|
+
def maintain_equilibrium(self) -> None:
|
|
108
|
+
"""Apply corrective operators if parameters exceed target ranges.
|
|
109
|
+
|
|
110
|
+
Checks each parameter (EPI, νf, ΔNFR) and applies appropriate
|
|
111
|
+
operators when out of bounds. Multiple corrections can occur
|
|
112
|
+
in a single call if multiple parameters are out of range.
|
|
113
|
+
|
|
114
|
+
Notes
|
|
115
|
+
-----
|
|
116
|
+
Corrections are applied sequentially:
|
|
117
|
+
|
|
118
|
+
1. Check and correct EPI
|
|
119
|
+
2. Check and correct νf
|
|
120
|
+
3. Check and correct ΔNFR
|
|
121
|
+
|
|
122
|
+
Each correction is minimal: one operator application per parameter.
|
|
123
|
+
"""
|
|
124
|
+
epi = get_attr(self.G.nodes[self.node], ALIAS_EPI, 0.0)
|
|
125
|
+
vf = get_attr(self.G.nodes[self.node], ALIAS_VF, 1.0)
|
|
126
|
+
dnfr = get_attr(self.G.nodes[self.node], ALIAS_DNFR, 0.0)
|
|
127
|
+
|
|
128
|
+
# Correct EPI if out of range
|
|
129
|
+
if epi < self.epi_range[0]:
|
|
130
|
+
# EPI too low → activate with Emission
|
|
131
|
+
operator_class = get_operator_class(EMISSION)
|
|
132
|
+
operator = operator_class()
|
|
133
|
+
operator(self.G, self.node)
|
|
134
|
+
elif epi > self.epi_range[1]:
|
|
135
|
+
# EPI too high → contract with Contraction
|
|
136
|
+
operator_class = get_operator_class(CONTRACTION)
|
|
137
|
+
operator = operator_class()
|
|
138
|
+
operator(self.G, self.node)
|
|
139
|
+
|
|
140
|
+
# Correct νf if out of range
|
|
141
|
+
if vf < self.vf_range[0]:
|
|
142
|
+
# Frequency too low → expand with Expansion
|
|
143
|
+
operator_class = get_operator_class(EXPANSION)
|
|
144
|
+
operator = operator_class()
|
|
145
|
+
operator(self.G, self.node)
|
|
146
|
+
elif vf > self.vf_range[1]:
|
|
147
|
+
# Frequency too high → silence with Silence
|
|
148
|
+
operator_class = get_operator_class(SILENCE)
|
|
149
|
+
operator = operator_class()
|
|
150
|
+
operator(self.G, self.node)
|
|
151
|
+
|
|
152
|
+
# Correct ΔNFR if too high
|
|
153
|
+
if dnfr > self.dnfr_range[1]:
|
|
154
|
+
# ΔNFR too high → stabilize with Coherence
|
|
155
|
+
operator_class = get_operator_class(COHERENCE)
|
|
156
|
+
operator = operator_class()
|
|
157
|
+
operator(self.G, self.node)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Type stubs for tnfr.dynamics.homeostasis module."""
|
|
2
|
+
|
|
3
|
+
from typing import Tuple
|
|
4
|
+
from ..types import TNFRGraph, NodeId
|
|
5
|
+
|
|
6
|
+
class StructuralHomeostasis:
|
|
7
|
+
G: TNFRGraph
|
|
8
|
+
node: NodeId
|
|
9
|
+
epi_range: Tuple[float, float]
|
|
10
|
+
vf_range: Tuple[float, float]
|
|
11
|
+
dnfr_range: Tuple[float, float]
|
|
12
|
+
|
|
13
|
+
def __init__(self, graph: TNFRGraph, node: NodeId) -> None: ...
|
|
14
|
+
def maintain_equilibrium(self) -> None: ...
|