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,326 @@
|
|
|
1
|
+
"""Network propagation dynamics for OZ-induced dissonance.
|
|
2
|
+
|
|
3
|
+
This module implements propagation of dissonance across network neighbors
|
|
4
|
+
following TNFR resonance principles. When OZ (Dissonance) is applied to a node,
|
|
5
|
+
structural dissonance propagates through the network based on phase compatibility,
|
|
6
|
+
frequency matching, and coupling strength.
|
|
7
|
+
|
|
8
|
+
According to TNFR canonical theory:
|
|
9
|
+
"Nodal interference: Dissonance between nodes that disrupts coherence.
|
|
10
|
+
Can induce reorganization or collapse."
|
|
11
|
+
|
|
12
|
+
OZ introduces topological asymmetry that propagates beyond the local node,
|
|
13
|
+
potentially triggering bifurcation cascades in phase-compatible neighbors.
|
|
14
|
+
|
|
15
|
+
References
|
|
16
|
+
----------
|
|
17
|
+
- TNFR.pdf §2.3.3: OZ introduces topological dissonance
|
|
18
|
+
- Issue: [OZ] Implement dissonance propagation and neighborhood network effects
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import math
|
|
24
|
+
from typing import TYPE_CHECKING, Any
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from ..types import NodeId, TNFRGraph
|
|
28
|
+
|
|
29
|
+
from ..alias import get_attr
|
|
30
|
+
from ..constants.aliases import ALIAS_DNFR, ALIAS_THETA, ALIAS_VF
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"propagate_dissonance",
|
|
34
|
+
"compute_network_dissonance_field",
|
|
35
|
+
"detect_bifurcation_cascade",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def propagate_dissonance(
|
|
40
|
+
G: TNFRGraph,
|
|
41
|
+
source_node: NodeId,
|
|
42
|
+
dissonance_magnitude: float,
|
|
43
|
+
propagation_mode: str = "phase_weighted",
|
|
44
|
+
) -> set[NodeId]:
|
|
45
|
+
"""Propagate OZ-induced dissonance to phase-compatible neighbors.
|
|
46
|
+
|
|
47
|
+
When OZ is applied to a node, structural dissonance propagates through
|
|
48
|
+
the network following TNFR resonance principles:
|
|
49
|
+
|
|
50
|
+
1. **Phase compatibility**: Neighbors with |Δθ| < threshold receive more
|
|
51
|
+
2. **Frequency matching**: Higher νf neighbors respond more strongly
|
|
52
|
+
3. **Coupling strength**: Edge weights modulate propagation
|
|
53
|
+
4. **Distance decay**: Effect diminishes with topological distance
|
|
54
|
+
|
|
55
|
+
Parameters
|
|
56
|
+
----------
|
|
57
|
+
G : TNFRGraph
|
|
58
|
+
Network containing nodes
|
|
59
|
+
source_node : NodeId
|
|
60
|
+
Node where OZ was applied
|
|
61
|
+
dissonance_magnitude : float
|
|
62
|
+
|ΔNFR| increase at source (typically from OZ metrics)
|
|
63
|
+
propagation_mode : str
|
|
64
|
+
'phase_weighted' (default), 'uniform', 'frequency_weighted'
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
set[NodeId]
|
|
69
|
+
Set of affected neighbor nodes
|
|
70
|
+
|
|
71
|
+
Notes
|
|
72
|
+
-----
|
|
73
|
+
Propagation follows coupling physics:
|
|
74
|
+
|
|
75
|
+
ΔNFR_neighbor = ΔNFR_source * w_coupling * w_phase * w_frequency
|
|
76
|
+
|
|
77
|
+
Where:
|
|
78
|
+
- w_coupling: Edge weight (default 1.0)
|
|
79
|
+
- w_phase: Phase compatibility factor
|
|
80
|
+
- w_frequency: Frequency matching factor
|
|
81
|
+
|
|
82
|
+
Examples
|
|
83
|
+
--------
|
|
84
|
+
>>> from tnfr.structural import create_nfr
|
|
85
|
+
>>> from tnfr.operators.definitions import Emission, Dissonance
|
|
86
|
+
>>> from tnfr.dynamics.propagation import propagate_dissonance
|
|
87
|
+
>>>
|
|
88
|
+
>>> G, node0 = create_nfr("source", epi=0.5, vf=1.0)
|
|
89
|
+
>>> # Add neighbors
|
|
90
|
+
>>> for i in range(3):
|
|
91
|
+
... G.add_node(f"n{i}")
|
|
92
|
+
... G.add_edge(node0, f"n{i}")
|
|
93
|
+
... Emission()(G, f"n{i}")
|
|
94
|
+
>>>
|
|
95
|
+
>>> # Apply OZ and propagate
|
|
96
|
+
>>> Dissonance()(G, node0)
|
|
97
|
+
>>> affected = propagate_dissonance(G, node0, 0.15)
|
|
98
|
+
>>> print(f"Affected neighbors: {len(affected)}")
|
|
99
|
+
|
|
100
|
+
See Also
|
|
101
|
+
--------
|
|
102
|
+
compute_network_dissonance_field : Compute field with distance decay
|
|
103
|
+
detect_bifurcation_cascade : Detect cascade-triggered bifurcations
|
|
104
|
+
"""
|
|
105
|
+
neighbors = list(G.neighbors(source_node))
|
|
106
|
+
if not neighbors:
|
|
107
|
+
return set()
|
|
108
|
+
|
|
109
|
+
affected = set()
|
|
110
|
+
source_theta = float(get_attr(G.nodes[source_node], ALIAS_THETA, 0.0))
|
|
111
|
+
source_vf = float(get_attr(G.nodes[source_node], ALIAS_VF, 1.0))
|
|
112
|
+
|
|
113
|
+
# Propagation threshold (configurable)
|
|
114
|
+
phase_threshold = float(G.graph.get("OZ_PHASE_THRESHOLD", math.pi / 2))
|
|
115
|
+
min_propagation = float(G.graph.get("OZ_MIN_PROPAGATION", 0.05))
|
|
116
|
+
|
|
117
|
+
for neighbor in neighbors:
|
|
118
|
+
neighbor_theta = float(get_attr(G.nodes[neighbor], ALIAS_THETA, 0.0))
|
|
119
|
+
neighbor_vf = float(get_attr(G.nodes[neighbor], ALIAS_VF, 1.0))
|
|
120
|
+
neighbor_dnfr = float(get_attr(G.nodes[neighbor], ALIAS_DNFR, 0.0))
|
|
121
|
+
|
|
122
|
+
# Compute phase compatibility
|
|
123
|
+
delta_theta = abs(source_theta - neighbor_theta)
|
|
124
|
+
if delta_theta > phase_threshold:
|
|
125
|
+
continue # Phase incompatible, no propagation
|
|
126
|
+
|
|
127
|
+
phase_weight = 1.0 - (delta_theta / phase_threshold)
|
|
128
|
+
|
|
129
|
+
# Compute frequency matching
|
|
130
|
+
if propagation_mode == "frequency_weighted":
|
|
131
|
+
freq_ratio = min(neighbor_vf, source_vf) / max(
|
|
132
|
+
neighbor_vf, source_vf, 1e-10
|
|
133
|
+
)
|
|
134
|
+
freq_weight = freq_ratio
|
|
135
|
+
else:
|
|
136
|
+
freq_weight = 1.0
|
|
137
|
+
|
|
138
|
+
# Get edge weight (coupling strength)
|
|
139
|
+
edge_data = G.get_edge_data(source_node, neighbor)
|
|
140
|
+
coupling_weight = edge_data.get("weight", 1.0) if edge_data else 1.0
|
|
141
|
+
|
|
142
|
+
# Compute propagated dissonance
|
|
143
|
+
propagated_dnfr = (
|
|
144
|
+
dissonance_magnitude * coupling_weight * phase_weight * freq_weight
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
if abs(propagated_dnfr) >= min_propagation:
|
|
148
|
+
# Apply propagated dissonance to neighbor
|
|
149
|
+
new_dnfr = neighbor_dnfr + propagated_dnfr
|
|
150
|
+
# Use first alias for consistency
|
|
151
|
+
G.nodes[neighbor][ALIAS_DNFR[0]] = new_dnfr
|
|
152
|
+
affected.add(neighbor)
|
|
153
|
+
|
|
154
|
+
# Log propagation for telemetry
|
|
155
|
+
if "_oz_propagation" not in G.nodes[neighbor]:
|
|
156
|
+
G.nodes[neighbor]["_oz_propagation"] = []
|
|
157
|
+
G.nodes[neighbor]["_oz_propagation"].append(
|
|
158
|
+
{
|
|
159
|
+
"from_node": source_node,
|
|
160
|
+
"magnitude": propagated_dnfr,
|
|
161
|
+
"phase_weight": phase_weight,
|
|
162
|
+
"coupling_weight": coupling_weight,
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
return affected
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def compute_network_dissonance_field(
|
|
170
|
+
G: TNFRGraph,
|
|
171
|
+
source_node: NodeId,
|
|
172
|
+
radius: int = 2,
|
|
173
|
+
) -> dict[NodeId, float]:
|
|
174
|
+
"""Compute dissonance field propagation up to radius hops.
|
|
175
|
+
|
|
176
|
+
Returns dict mapping node -> dissonance_level for all nodes
|
|
177
|
+
within radius hops of source.
|
|
178
|
+
|
|
179
|
+
Parameters
|
|
180
|
+
----------
|
|
181
|
+
G : TNFRGraph
|
|
182
|
+
Network
|
|
183
|
+
source_node : NodeId
|
|
184
|
+
OZ application point
|
|
185
|
+
radius : int
|
|
186
|
+
Maximum propagation distance (default 2)
|
|
187
|
+
|
|
188
|
+
Returns
|
|
189
|
+
-------
|
|
190
|
+
dict[NodeId, float]
|
|
191
|
+
Mapping of affected nodes to dissonance level
|
|
192
|
+
|
|
193
|
+
Notes
|
|
194
|
+
-----
|
|
195
|
+
Uses exponential decay: dissonance_level = source_dnfr * (0.5 ** distance)
|
|
196
|
+
|
|
197
|
+
Only nodes reachable via paths (connected) are included in the field.
|
|
198
|
+
|
|
199
|
+
Examples
|
|
200
|
+
--------
|
|
201
|
+
>>> from tnfr.structural import create_nfr
|
|
202
|
+
>>> from tnfr.operators.definitions import Dissonance
|
|
203
|
+
>>> from tnfr.dynamics.propagation import compute_network_dissonance_field
|
|
204
|
+
>>>
|
|
205
|
+
>>> G, node0 = create_nfr("source")
|
|
206
|
+
>>> # Create path topology: 0-1-2-3
|
|
207
|
+
>>> for i in range(1, 4):
|
|
208
|
+
... G.add_node(i)
|
|
209
|
+
... G.add_edge(i-1, i)
|
|
210
|
+
>>>
|
|
211
|
+
>>> Dissonance()(G, node0)
|
|
212
|
+
>>> field = compute_network_dissonance_field(G, node0, radius=2)
|
|
213
|
+
>>> # Returns: {1: high, 2: medium} (node 3 beyond radius)
|
|
214
|
+
"""
|
|
215
|
+
import networkx as nx
|
|
216
|
+
|
|
217
|
+
field = {}
|
|
218
|
+
source_dnfr = abs(float(get_attr(G.nodes[source_node], ALIAS_DNFR, 0.0)))
|
|
219
|
+
|
|
220
|
+
# Get decay factor from graph config
|
|
221
|
+
decay_factor = float(G.graph.get("OZ_DECAY_FACTOR", 0.5))
|
|
222
|
+
|
|
223
|
+
# BFS to propagate with distance decay
|
|
224
|
+
for distance in range(1, radius + 1):
|
|
225
|
+
# Get nodes at this distance
|
|
226
|
+
nodes_at_distance = set()
|
|
227
|
+
for node in G.nodes():
|
|
228
|
+
try:
|
|
229
|
+
path_length = nx.shortest_path_length(G, source_node, node)
|
|
230
|
+
if path_length == distance:
|
|
231
|
+
nodes_at_distance.add(node)
|
|
232
|
+
except nx.NetworkXNoPath:
|
|
233
|
+
continue
|
|
234
|
+
|
|
235
|
+
# Propagate with decay
|
|
236
|
+
decay = decay_factor**distance
|
|
237
|
+
for node in nodes_at_distance:
|
|
238
|
+
field[node] = source_dnfr * decay
|
|
239
|
+
|
|
240
|
+
return field
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def detect_bifurcation_cascade(
|
|
244
|
+
G: TNFRGraph,
|
|
245
|
+
source_node: NodeId,
|
|
246
|
+
threshold: float = 0.5,
|
|
247
|
+
) -> list[NodeId]:
|
|
248
|
+
"""Detect if OZ triggers bifurcation cascade in network.
|
|
249
|
+
|
|
250
|
+
When source node undergoes bifurcation (∂²EPI/∂t² > τ), check if
|
|
251
|
+
propagated dissonance pushes neighbors over their own thresholds.
|
|
252
|
+
|
|
253
|
+
Parameters
|
|
254
|
+
----------
|
|
255
|
+
G : TNFRGraph
|
|
256
|
+
Network containing nodes
|
|
257
|
+
source_node : NodeId
|
|
258
|
+
Node where OZ was applied
|
|
259
|
+
threshold : float
|
|
260
|
+
Bifurcation threshold τ (default 0.5)
|
|
261
|
+
|
|
262
|
+
Returns
|
|
263
|
+
-------
|
|
264
|
+
list[NodeId]
|
|
265
|
+
Nodes that entered bifurcation state due to cascade
|
|
266
|
+
|
|
267
|
+
Notes
|
|
268
|
+
-----
|
|
269
|
+
A node is considered in bifurcation cascade if:
|
|
270
|
+
- It received propagated dissonance from source
|
|
271
|
+
- Its ∂²EPI/∂t² now exceeds threshold τ
|
|
272
|
+
|
|
273
|
+
The function marks cascade nodes with `_bifurcation_cascade` metadata
|
|
274
|
+
for telemetry and further analysis.
|
|
275
|
+
|
|
276
|
+
Examples
|
|
277
|
+
--------
|
|
278
|
+
>>> from tnfr.structural import create_nfr
|
|
279
|
+
>>> from tnfr.operators.definitions import Emission, Dissonance
|
|
280
|
+
>>> from tnfr.dynamics.propagation import detect_bifurcation_cascade
|
|
281
|
+
>>>
|
|
282
|
+
>>> G, node0 = create_nfr("source", epi=0.5, vf=1.2)
|
|
283
|
+
>>> # Add neighbors with EPI history
|
|
284
|
+
>>> for i in range(3):
|
|
285
|
+
... G.add_node(f"n{i}")
|
|
286
|
+
... G.add_edge(node0, f"n{i}")
|
|
287
|
+
... G.nodes[f"n{i}"]["_epi_history"] = [0.3, 0.45, 0.55]
|
|
288
|
+
>>>
|
|
289
|
+
>>> Dissonance()(G, node0, propagate_to_network=True)
|
|
290
|
+
>>> cascade = detect_bifurcation_cascade(G, node0)
|
|
291
|
+
>>> print(f"Cascade size: {len(cascade)}")
|
|
292
|
+
|
|
293
|
+
See Also
|
|
294
|
+
--------
|
|
295
|
+
tnfr.operators.nodal_equation.compute_d2epi_dt2 : Compute structural acceleration
|
|
296
|
+
tnfr.dynamics.bifurcation.get_bifurcation_paths : Identify viable paths
|
|
297
|
+
"""
|
|
298
|
+
from ..operators.nodal_equation import compute_d2epi_dt2
|
|
299
|
+
|
|
300
|
+
cascade_nodes = []
|
|
301
|
+
|
|
302
|
+
# Get neighbors affected by propagation
|
|
303
|
+
neighbors = list(G.neighbors(source_node))
|
|
304
|
+
|
|
305
|
+
for neighbor in neighbors:
|
|
306
|
+
# Check if neighbor has propagation record (was affected)
|
|
307
|
+
if "_oz_propagation" not in G.nodes[neighbor]:
|
|
308
|
+
continue
|
|
309
|
+
|
|
310
|
+
# Check if neighbor now in bifurcation state
|
|
311
|
+
d2epi_neighbor = compute_d2epi_dt2(G, neighbor)
|
|
312
|
+
|
|
313
|
+
if abs(d2epi_neighbor) > threshold:
|
|
314
|
+
cascade_nodes.append(neighbor)
|
|
315
|
+
|
|
316
|
+
# Mark for telemetry
|
|
317
|
+
G.nodes[neighbor]["_bifurcation_cascade"] = {
|
|
318
|
+
"triggered_by": source_node,
|
|
319
|
+
"d2epi": d2epi_neighbor,
|
|
320
|
+
"threshold": threshold,
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
# Set bifurcation_ready flag for path detection
|
|
324
|
+
G.nodes[neighbor]["_bifurcation_ready"] = True
|
|
325
|
+
|
|
326
|
+
return cascade_nodes
|