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
tnfr/gamma.py
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"""Gamma registry."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import logging
|
|
7
|
+
import math
|
|
8
|
+
from collections.abc import Mapping
|
|
9
|
+
from functools import lru_cache
|
|
10
|
+
from types import MappingProxyType
|
|
11
|
+
from typing import Any, Callable, NamedTuple
|
|
12
|
+
|
|
13
|
+
from .alias import get_theta_attr
|
|
14
|
+
from .constants import DEFAULTS
|
|
15
|
+
from .utils import json_dumps
|
|
16
|
+
from .metrics.trig_cache import get_trig_cache
|
|
17
|
+
from .types import GammaSpec, NodeId, TNFRGraph
|
|
18
|
+
from .utils import (
|
|
19
|
+
edge_version_cache,
|
|
20
|
+
get_graph_mapping,
|
|
21
|
+
get_logger,
|
|
22
|
+
node_set_checksum,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
logger = get_logger(__name__)
|
|
26
|
+
|
|
27
|
+
DEFAULT_GAMMA: Mapping[str, Any] = MappingProxyType(dict(DEFAULTS["GAMMA"]))
|
|
28
|
+
|
|
29
|
+
__all__ = (
|
|
30
|
+
"kuramoto_R_psi",
|
|
31
|
+
"gamma_none",
|
|
32
|
+
"gamma_kuramoto_linear",
|
|
33
|
+
"gamma_kuramoto_bandpass",
|
|
34
|
+
"gamma_kuramoto_tanh",
|
|
35
|
+
"gamma_harmonic",
|
|
36
|
+
"GammaEntry",
|
|
37
|
+
"GAMMA_REGISTRY",
|
|
38
|
+
"eval_gamma",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@lru_cache(maxsize=1)
|
|
43
|
+
def _default_gamma_spec() -> tuple[bytes, str]:
|
|
44
|
+
dumped = json_dumps(dict(DEFAULT_GAMMA), sort_keys=True, to_bytes=True)
|
|
45
|
+
hash_ = hashlib.blake2b(dumped, digest_size=16).hexdigest()
|
|
46
|
+
return dumped, hash_
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _ensure_kuramoto_cache(G: TNFRGraph, t: float | int) -> None:
|
|
50
|
+
"""Cache ``(R, ψ)`` for the current step ``t`` using ``edge_version_cache``."""
|
|
51
|
+
checksum = G.graph.get("_dnfr_nodes_checksum")
|
|
52
|
+
if checksum is None:
|
|
53
|
+
# reuse checksum from cached_nodes_and_A when available
|
|
54
|
+
checksum = node_set_checksum(G)
|
|
55
|
+
nodes_sig = (len(G), checksum)
|
|
56
|
+
max_steps = int(G.graph.get("KURAMOTO_CACHE_STEPS", 1))
|
|
57
|
+
|
|
58
|
+
def builder() -> dict[str, float]:
|
|
59
|
+
R, psi = kuramoto_R_psi(G)
|
|
60
|
+
return {"R": R, "psi": psi}
|
|
61
|
+
|
|
62
|
+
key = (t, nodes_sig)
|
|
63
|
+
entry = edge_version_cache(G, key, builder, max_entries=max_steps)
|
|
64
|
+
G.graph["_kuramoto_cache"] = entry
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def kuramoto_R_psi(G: TNFRGraph) -> tuple[float, float]:
|
|
68
|
+
"""Return ``(R, ψ)`` for Kuramoto order using θ from all nodes."""
|
|
69
|
+
max_steps = int(G.graph.get("KURAMOTO_CACHE_STEPS", 1))
|
|
70
|
+
trig = get_trig_cache(G, cache_size=max_steps)
|
|
71
|
+
n = len(trig.theta)
|
|
72
|
+
if n == 0:
|
|
73
|
+
return 0.0, 0.0
|
|
74
|
+
|
|
75
|
+
cos_sum = sum(trig.cos.values())
|
|
76
|
+
sin_sum = sum(trig.sin.values())
|
|
77
|
+
R = math.hypot(cos_sum, sin_sum) / n
|
|
78
|
+
psi = math.atan2(sin_sum, cos_sum)
|
|
79
|
+
return R, psi
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _kuramoto_common(
|
|
83
|
+
G: TNFRGraph, node: NodeId, _cfg: GammaSpec
|
|
84
|
+
) -> tuple[float, float, float]:
|
|
85
|
+
"""Return ``(θ_i, R, ψ)`` for Kuramoto-based Γ functions.
|
|
86
|
+
|
|
87
|
+
Reads cached global order ``R`` and mean phase ``ψ`` and obtains node
|
|
88
|
+
phase ``θ_i``. ``_cfg`` is accepted only to keep a homogeneous signature
|
|
89
|
+
with Γ evaluators.
|
|
90
|
+
"""
|
|
91
|
+
cache = G.graph.get("_kuramoto_cache", {})
|
|
92
|
+
R = float(cache.get("R", 0.0))
|
|
93
|
+
psi = float(cache.get("psi", 0.0))
|
|
94
|
+
th_val = get_theta_attr(G.nodes[node], 0.0)
|
|
95
|
+
th_i = float(th_val if th_val is not None else 0.0)
|
|
96
|
+
return th_i, R, psi
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _read_gamma_raw(G: TNFRGraph) -> GammaSpec | None:
|
|
100
|
+
"""Return raw Γ specification from ``G.graph['GAMMA']``.
|
|
101
|
+
|
|
102
|
+
The returned value is the direct contents of ``G.graph['GAMMA']`` when
|
|
103
|
+
it is a mapping or the result of :func:`get_graph_mapping` if a path is
|
|
104
|
+
provided. Final validation and caching are handled elsewhere.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
raw = G.graph.get("GAMMA")
|
|
108
|
+
if raw is None or isinstance(raw, Mapping):
|
|
109
|
+
return raw
|
|
110
|
+
return get_graph_mapping(
|
|
111
|
+
G,
|
|
112
|
+
"GAMMA",
|
|
113
|
+
"G.graph['GAMMA'] is not a mapping; using {'type': 'none'}",
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _get_gamma_spec(G: TNFRGraph) -> GammaSpec:
|
|
118
|
+
"""Return validated Γ specification caching results.
|
|
119
|
+
|
|
120
|
+
The raw value from ``G.graph['GAMMA']`` is cached together with the
|
|
121
|
+
normalized specification and its hash. When the raw value is unchanged,
|
|
122
|
+
the cached spec is returned without re-reading or re-validating,
|
|
123
|
+
preventing repeated warnings or costly hashing.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
raw = G.graph.get("GAMMA")
|
|
127
|
+
cached_raw = G.graph.get("_gamma_raw")
|
|
128
|
+
cached_spec = G.graph.get("_gamma_spec")
|
|
129
|
+
cached_hash = G.graph.get("_gamma_spec_hash")
|
|
130
|
+
|
|
131
|
+
def _hash_mapping(mapping: GammaSpec) -> str:
|
|
132
|
+
dumped = json_dumps(mapping, sort_keys=True, to_bytes=True)
|
|
133
|
+
return hashlib.blake2b(dumped, digest_size=16).hexdigest()
|
|
134
|
+
|
|
135
|
+
mapping_hash: str | None = None
|
|
136
|
+
if isinstance(raw, Mapping):
|
|
137
|
+
mapping_hash = _hash_mapping(raw)
|
|
138
|
+
if (
|
|
139
|
+
raw is cached_raw
|
|
140
|
+
and cached_spec is not None
|
|
141
|
+
and cached_hash == mapping_hash
|
|
142
|
+
):
|
|
143
|
+
return cached_spec
|
|
144
|
+
elif raw is cached_raw and cached_spec is not None and cached_hash is not None:
|
|
145
|
+
return cached_spec
|
|
146
|
+
|
|
147
|
+
if raw is None:
|
|
148
|
+
spec = DEFAULT_GAMMA
|
|
149
|
+
_, cur_hash = _default_gamma_spec()
|
|
150
|
+
elif isinstance(raw, Mapping):
|
|
151
|
+
spec = raw
|
|
152
|
+
cur_hash = mapping_hash if mapping_hash is not None else _hash_mapping(spec)
|
|
153
|
+
else:
|
|
154
|
+
spec_raw = _read_gamma_raw(G)
|
|
155
|
+
if isinstance(spec_raw, Mapping) and spec_raw is not None:
|
|
156
|
+
spec = spec_raw
|
|
157
|
+
cur_hash = _hash_mapping(spec)
|
|
158
|
+
else:
|
|
159
|
+
spec = DEFAULT_GAMMA
|
|
160
|
+
_, cur_hash = _default_gamma_spec()
|
|
161
|
+
|
|
162
|
+
# Store raw input, validated spec and its hash for future calls
|
|
163
|
+
G.graph["_gamma_raw"] = raw
|
|
164
|
+
G.graph["_gamma_spec"] = spec
|
|
165
|
+
G.graph["_gamma_spec_hash"] = cur_hash
|
|
166
|
+
return spec
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# -----------------
|
|
170
|
+
# Helpers
|
|
171
|
+
# -----------------
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _gamma_params(cfg: GammaSpec, **defaults: float) -> tuple[float, ...]:
|
|
175
|
+
"""Return normalized Γ parameters from ``cfg``.
|
|
176
|
+
|
|
177
|
+
Parameters are retrieved from ``cfg`` using the keys in ``defaults`` and
|
|
178
|
+
converted to ``float``. If a key is missing, its value from ``defaults`` is
|
|
179
|
+
used. Values convertible to ``float`` (e.g. strings) are accepted.
|
|
180
|
+
|
|
181
|
+
Example
|
|
182
|
+
-------
|
|
183
|
+
>>> beta, R0 = _gamma_params(cfg, beta=0.0, R0=0.0)
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
return tuple(float(cfg.get(name, default)) for name, default in defaults.items())
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# -----------------
|
|
190
|
+
# Canonical Γi(R)
|
|
191
|
+
# -----------------
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def gamma_none(G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec) -> float:
|
|
195
|
+
"""Return ``0.0`` to disable Γ forcing for the given node."""
|
|
196
|
+
|
|
197
|
+
return 0.0
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _gamma_kuramoto(
|
|
201
|
+
G: TNFRGraph,
|
|
202
|
+
node: NodeId,
|
|
203
|
+
cfg: GammaSpec,
|
|
204
|
+
builder: Callable[..., float],
|
|
205
|
+
**defaults: float,
|
|
206
|
+
) -> float:
|
|
207
|
+
"""Construct a Kuramoto-based Γ function.
|
|
208
|
+
|
|
209
|
+
``builder`` receives ``(θ_i, R, ψ, *params)`` where ``params`` are
|
|
210
|
+
extracted from ``cfg`` according to ``defaults``.
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
params = _gamma_params(cfg, **defaults)
|
|
214
|
+
th_i, R, psi = _kuramoto_common(G, node, cfg)
|
|
215
|
+
return builder(th_i, R, psi, *params)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _builder_linear(th_i: float, R: float, psi: float, beta: float, R0: float) -> float:
|
|
219
|
+
return beta * (R - R0) * math.cos(th_i - psi)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _builder_bandpass(th_i: float, R: float, psi: float, beta: float) -> float:
|
|
223
|
+
sgn = 1.0 if math.cos(th_i - psi) >= 0.0 else -1.0
|
|
224
|
+
return beta * R * (1.0 - R) * sgn
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _builder_tanh(
|
|
228
|
+
th_i: float, R: float, psi: float, beta: float, k: float, R0: float
|
|
229
|
+
) -> float:
|
|
230
|
+
return beta * math.tanh(k * (R - R0)) * math.cos(th_i - psi)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def gamma_kuramoto_linear(
|
|
234
|
+
G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec
|
|
235
|
+
) -> float:
|
|
236
|
+
"""Linear Kuramoto coupling for Γi(R).
|
|
237
|
+
|
|
238
|
+
Formula: Γ = β · (R - R0) · cos(θ_i - ψ)
|
|
239
|
+
- R ∈ [0,1] is the global phase order.
|
|
240
|
+
- ψ is the mean phase (coordination direction).
|
|
241
|
+
- β, R0 are parameters (gain/threshold).
|
|
242
|
+
|
|
243
|
+
Use: reinforces integration when the network already shows phase
|
|
244
|
+
coherence (R>R0).
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
return _gamma_kuramoto(G, node, cfg, _builder_linear, beta=0.0, R0=0.0)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def gamma_kuramoto_bandpass(
|
|
251
|
+
G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec
|
|
252
|
+
) -> float:
|
|
253
|
+
"""Compute Γ = β · R(1-R) · sign(cos(θ_i - ψ))."""
|
|
254
|
+
|
|
255
|
+
return _gamma_kuramoto(G, node, cfg, _builder_bandpass, beta=0.0)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def gamma_kuramoto_tanh(
|
|
259
|
+
G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec
|
|
260
|
+
) -> float:
|
|
261
|
+
"""Saturating tanh coupling for Γi(R).
|
|
262
|
+
|
|
263
|
+
Formula: Γ = β · tanh(k·(R - R0)) · cos(θ_i - ψ)
|
|
264
|
+
- β: coupling gain
|
|
265
|
+
- k: tanh slope (how fast it saturates)
|
|
266
|
+
- R0: activation threshold
|
|
267
|
+
"""
|
|
268
|
+
|
|
269
|
+
return _gamma_kuramoto(G, node, cfg, _builder_tanh, beta=0.0, k=1.0, R0=0.0)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def gamma_harmonic(G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec) -> float:
|
|
273
|
+
"""Harmonic forcing aligned with the global phase field.
|
|
274
|
+
|
|
275
|
+
Formula: Γ = β · sin(ω·t + φ) · cos(θ_i - ψ)
|
|
276
|
+
- β: coupling gain
|
|
277
|
+
- ω: angular frequency of the forcing
|
|
278
|
+
- φ: initial phase of the forcing
|
|
279
|
+
"""
|
|
280
|
+
beta, omega, phi = _gamma_params(cfg, beta=0.0, omega=1.0, phi=0.0)
|
|
281
|
+
th_i, _, psi = _kuramoto_common(G, node, cfg)
|
|
282
|
+
return beta * math.sin(omega * t + phi) * math.cos(th_i - psi)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class GammaEntry(NamedTuple):
|
|
286
|
+
"""Lookup entry linking Γ evaluators with their preconditions."""
|
|
287
|
+
|
|
288
|
+
fn: Callable[[TNFRGraph, NodeId, float | int, GammaSpec], float]
|
|
289
|
+
needs_kuramoto: bool
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
# ``GAMMA_REGISTRY`` associates each coupling name with a ``GammaEntry`` where
|
|
293
|
+
# ``fn`` is the evaluation function and ``needs_kuramoto`` indicates whether
|
|
294
|
+
# the global phase order must be precomputed.
|
|
295
|
+
GAMMA_REGISTRY: dict[str, GammaEntry] = {
|
|
296
|
+
"none": GammaEntry(gamma_none, False),
|
|
297
|
+
"kuramoto_linear": GammaEntry(gamma_kuramoto_linear, True),
|
|
298
|
+
"kuramoto_bandpass": GammaEntry(gamma_kuramoto_bandpass, True),
|
|
299
|
+
"kuramoto_tanh": GammaEntry(gamma_kuramoto_tanh, True),
|
|
300
|
+
"harmonic": GammaEntry(gamma_harmonic, True),
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def eval_gamma(
|
|
305
|
+
G: TNFRGraph,
|
|
306
|
+
node: NodeId,
|
|
307
|
+
t: float | int,
|
|
308
|
+
*,
|
|
309
|
+
strict: bool = False,
|
|
310
|
+
log_level: int | None = None,
|
|
311
|
+
) -> float:
|
|
312
|
+
"""Evaluate Γi for ``node`` using ``G.graph['GAMMA']`` specification.
|
|
313
|
+
|
|
314
|
+
If ``strict`` is ``True`` exceptions raised during evaluation are
|
|
315
|
+
propagated instead of returning ``0.0``. Likewise, if the specified
|
|
316
|
+
Γ type is not registered a warning is emitted (or ``ValueError`` in
|
|
317
|
+
strict mode) and ``gamma_none`` is used.
|
|
318
|
+
|
|
319
|
+
``log_level`` controls the logging level for captured errors when
|
|
320
|
+
``strict`` is ``False``. If omitted, ``logging.ERROR`` is used in
|
|
321
|
+
strict mode and ``logging.DEBUG`` otherwise.
|
|
322
|
+
"""
|
|
323
|
+
spec = _get_gamma_spec(G)
|
|
324
|
+
spec_type = spec.get("type", "none")
|
|
325
|
+
reg_entry = GAMMA_REGISTRY.get(spec_type)
|
|
326
|
+
if reg_entry is None:
|
|
327
|
+
msg = f"Unknown GAMMA type: {spec_type}"
|
|
328
|
+
if strict:
|
|
329
|
+
raise ValueError(msg)
|
|
330
|
+
logger.warning(msg)
|
|
331
|
+
entry = GammaEntry(gamma_none, False)
|
|
332
|
+
else:
|
|
333
|
+
entry = reg_entry
|
|
334
|
+
if entry.needs_kuramoto:
|
|
335
|
+
_ensure_kuramoto_cache(G, t)
|
|
336
|
+
try:
|
|
337
|
+
return float(entry.fn(G, node, t, spec))
|
|
338
|
+
except (ValueError, TypeError, ArithmeticError) as exc:
|
|
339
|
+
level = (
|
|
340
|
+
log_level
|
|
341
|
+
if log_level is not None
|
|
342
|
+
else (logging.ERROR if strict else logging.DEBUG)
|
|
343
|
+
)
|
|
344
|
+
logger.log(
|
|
345
|
+
level,
|
|
346
|
+
"Failed to evaluate Γi for node %s at t=%s: %s: %s",
|
|
347
|
+
node,
|
|
348
|
+
t,
|
|
349
|
+
exc.__class__.__name__,
|
|
350
|
+
exc,
|
|
351
|
+
)
|
|
352
|
+
if strict:
|
|
353
|
+
raise
|
|
354
|
+
return 0.0
|
tnfr/gamma.pyi
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Callable, NamedTuple
|
|
4
|
+
|
|
5
|
+
from .types import GammaSpec, NodeId, TNFRGraph
|
|
6
|
+
|
|
7
|
+
__all__: tuple[str, ...]
|
|
8
|
+
|
|
9
|
+
class GammaEntry(NamedTuple):
|
|
10
|
+
fn: Callable[[TNFRGraph, NodeId, float | int, GammaSpec], float]
|
|
11
|
+
needs_kuramoto: bool
|
|
12
|
+
|
|
13
|
+
GAMMA_REGISTRY: dict[str, GammaEntry]
|
|
14
|
+
|
|
15
|
+
def kuramoto_R_psi(G: TNFRGraph) -> tuple[float, float]: ...
|
|
16
|
+
def gamma_none(G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec) -> float: ...
|
|
17
|
+
def gamma_kuramoto_linear(
|
|
18
|
+
G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec
|
|
19
|
+
) -> float: ...
|
|
20
|
+
def gamma_kuramoto_bandpass(
|
|
21
|
+
G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec
|
|
22
|
+
) -> float: ...
|
|
23
|
+
def gamma_kuramoto_tanh(
|
|
24
|
+
G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec
|
|
25
|
+
) -> float: ...
|
|
26
|
+
def gamma_harmonic(
|
|
27
|
+
G: TNFRGraph, node: NodeId, t: float | int, cfg: GammaSpec
|
|
28
|
+
) -> float: ...
|
|
29
|
+
def eval_gamma(
|
|
30
|
+
G: TNFRGraph,
|
|
31
|
+
node: NodeId,
|
|
32
|
+
t: float | int,
|
|
33
|
+
*,
|
|
34
|
+
strict: bool = ...,
|
|
35
|
+
log_level: int | None = ...,
|
|
36
|
+
) -> float: ...
|