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,238 @@
|
|
|
1
|
+
"""Facade that keeps ΔNFR, νf and phase orchestration coherent across TNFR dynamics.
|
|
2
|
+
|
|
3
|
+
Attributes
|
|
4
|
+
----------
|
|
5
|
+
run : callable
|
|
6
|
+
Callable that fully manages the evolution loop, integrating the nodal
|
|
7
|
+
equation while enforcing ΔNFR hooks, νf adaptation and phase coordination
|
|
8
|
+
on every step.
|
|
9
|
+
step : callable
|
|
10
|
+
Callable entry point for a single iteration that reuses the
|
|
11
|
+
ΔNFR/νf/phase pipeline while letting callers interleave bespoke telemetry
|
|
12
|
+
or operator injections.
|
|
13
|
+
set_delta_nfr_hook : callable
|
|
14
|
+
Callable used to install custom ΔNFR supervision under
|
|
15
|
+
``G.graph['compute_delta_nfr']`` so each operator reorganization stays
|
|
16
|
+
coupled to νf drift and phase targets.
|
|
17
|
+
default_glyph_selector, parametric_glyph_selector : AbstractSelector
|
|
18
|
+
Selector implementations that choose glyphs according to ΔNFR trends,
|
|
19
|
+
νf ranges and phase synchrony, ensuring operator firing reinforces
|
|
20
|
+
coherence.
|
|
21
|
+
coordination, dnfr, integrators : module
|
|
22
|
+
Re-exported modules providing explicit control over phase alignment,
|
|
23
|
+
ΔNFR caches and integrator lifecycles to centralize orchestration.
|
|
24
|
+
ProcessPoolExecutor, apply_glyph, compute_Si : callable
|
|
25
|
+
Re-exported utilities for parallel selector evaluation, explicit glyph
|
|
26
|
+
execution and Si telemetry so ΔNFR, νf and phase traces remain observable.
|
|
27
|
+
|
|
28
|
+
Notes
|
|
29
|
+
-----
|
|
30
|
+
The facade aggregates runtime helpers that preserve canonical TNFR dynamics:
|
|
31
|
+
``dnfr`` manages ΔNFR preparation and caching, ``integrators`` drives the
|
|
32
|
+
numerical updates of νf and EPI, and ``coordination`` synchronizes global and
|
|
33
|
+
local phase. Complementary exports such as
|
|
34
|
+
:func:`~tnfr.dynamics.adaptation.adapt_vf_by_coherence` and
|
|
35
|
+
:func:`~tnfr.dynamics.coordination.coordinate_global_local_phase` allow custom
|
|
36
|
+
feedback loops without breaking operator closure.
|
|
37
|
+
|
|
38
|
+
Examples
|
|
39
|
+
--------
|
|
40
|
+
>>> from tnfr.constants import DNFR_PRIMARY, EPI_PRIMARY, VF_PRIMARY
|
|
41
|
+
>>> from tnfr.structural import Coherence, Emission, Resonance, create_nfr, run_sequence
|
|
42
|
+
>>> from tnfr.dynamics import parametric_glyph_selector, run, set_delta_nfr_hook, step
|
|
43
|
+
>>> G, node = create_nfr("seed", epi=0.22, vf=1.0)
|
|
44
|
+
>>> def regulate_delta(graph, *, n_jobs=None):
|
|
45
|
+
... for _, nd in graph.nodes(data=True):
|
|
46
|
+
... delta = nd[VF_PRIMARY] * 0.08
|
|
47
|
+
... nd[DNFR_PRIMARY] = delta
|
|
48
|
+
... nd[EPI_PRIMARY] += delta
|
|
49
|
+
... nd[VF_PRIMARY] += delta * 0.05
|
|
50
|
+
... return None
|
|
51
|
+
>>> set_delta_nfr_hook(G, regulate_delta, note="ΔNFR guided by νf")
|
|
52
|
+
>>> G.graph["glyph_selector"] = parametric_glyph_selector
|
|
53
|
+
>>> run_sequence(G, node, [Emission(), Resonance(), Coherence()])
|
|
54
|
+
>>> run(G, steps=2, dt=0.05)
|
|
55
|
+
>>> # Automatic integration keeps ΔNFR, νf and phase co-modulated.
|
|
56
|
+
>>> step(G, dt=0.05)
|
|
57
|
+
>>> # Manual control reuses the selector state to consolidate coherence traces.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
from __future__ import annotations
|
|
61
|
+
|
|
62
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
63
|
+
|
|
64
|
+
from ..metrics.sense_index import compute_Si
|
|
65
|
+
from ..operators import apply_glyph
|
|
66
|
+
from ..types import GlyphCode
|
|
67
|
+
from ..utils import get_numpy
|
|
68
|
+
from . import canonical, coordination, dnfr, integrators, metabolism
|
|
69
|
+
from .adaptation import adapt_vf_by_coherence
|
|
70
|
+
from .bifurcation import get_bifurcation_paths, compute_bifurcation_score
|
|
71
|
+
from .propagation import (
|
|
72
|
+
propagate_dissonance,
|
|
73
|
+
compute_network_dissonance_field,
|
|
74
|
+
detect_bifurcation_cascade,
|
|
75
|
+
)
|
|
76
|
+
from .aliases import (
|
|
77
|
+
ALIAS_D2EPI,
|
|
78
|
+
ALIAS_DNFR,
|
|
79
|
+
ALIAS_DSI,
|
|
80
|
+
ALIAS_EPI,
|
|
81
|
+
ALIAS_SI,
|
|
82
|
+
ALIAS_VF,
|
|
83
|
+
)
|
|
84
|
+
from .canonical import (
|
|
85
|
+
NodalEquationResult,
|
|
86
|
+
compute_canonical_nodal_derivative,
|
|
87
|
+
validate_nodal_gradient,
|
|
88
|
+
validate_structural_frequency,
|
|
89
|
+
)
|
|
90
|
+
from .coordination import coordinate_global_local_phase
|
|
91
|
+
from .dnfr import (
|
|
92
|
+
_compute_dnfr,
|
|
93
|
+
_compute_neighbor_means,
|
|
94
|
+
_init_dnfr_cache,
|
|
95
|
+
_prepare_dnfr_data,
|
|
96
|
+
_refresh_dnfr_vectors,
|
|
97
|
+
default_compute_delta_nfr,
|
|
98
|
+
dnfr_epi_vf_mixed,
|
|
99
|
+
dnfr_laplacian,
|
|
100
|
+
dnfr_phase_only,
|
|
101
|
+
set_delta_nfr_hook,
|
|
102
|
+
)
|
|
103
|
+
from .dynamic_limits import (
|
|
104
|
+
DynamicLimits,
|
|
105
|
+
DynamicLimitsConfig,
|
|
106
|
+
compute_dynamic_limits,
|
|
107
|
+
)
|
|
108
|
+
from .integrators import (
|
|
109
|
+
AbstractIntegrator,
|
|
110
|
+
DefaultIntegrator,
|
|
111
|
+
prepare_integration_params,
|
|
112
|
+
update_epi_via_nodal_equation,
|
|
113
|
+
)
|
|
114
|
+
from .learning import AdaptiveLearningSystem
|
|
115
|
+
from .feedback import StructuralFeedbackLoop
|
|
116
|
+
from .adaptive_sequences import AdaptiveSequenceSelector
|
|
117
|
+
from .homeostasis import StructuralHomeostasis
|
|
118
|
+
from .structural_clip import (
|
|
119
|
+
structural_clip,
|
|
120
|
+
StructuralClipStats,
|
|
121
|
+
get_clip_stats,
|
|
122
|
+
reset_clip_stats,
|
|
123
|
+
)
|
|
124
|
+
from .runtime import (
|
|
125
|
+
_maybe_remesh,
|
|
126
|
+
_normalize_job_overrides,
|
|
127
|
+
_prepare_dnfr,
|
|
128
|
+
_resolve_jobs_override,
|
|
129
|
+
_run_after_callbacks,
|
|
130
|
+
_run_before_callbacks,
|
|
131
|
+
_run_validators,
|
|
132
|
+
_update_epi_hist,
|
|
133
|
+
_update_nodes,
|
|
134
|
+
run,
|
|
135
|
+
step,
|
|
136
|
+
)
|
|
137
|
+
from .sampling import update_node_sample as _update_node_sample
|
|
138
|
+
from .selectors import (
|
|
139
|
+
AbstractSelector,
|
|
140
|
+
DefaultGlyphSelector,
|
|
141
|
+
ParametricGlyphSelector,
|
|
142
|
+
_apply_glyphs,
|
|
143
|
+
_apply_selector,
|
|
144
|
+
_choose_glyph,
|
|
145
|
+
_collect_selector_metrics,
|
|
146
|
+
_configure_selector_weights,
|
|
147
|
+
_prepare_selector_preselection,
|
|
148
|
+
_resolve_preselected_glyph,
|
|
149
|
+
_selector_parallel_jobs,
|
|
150
|
+
_SelectorPreselection,
|
|
151
|
+
default_glyph_selector,
|
|
152
|
+
parametric_glyph_selector,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
__all__ = (
|
|
156
|
+
"canonical",
|
|
157
|
+
"coordination",
|
|
158
|
+
"dnfr",
|
|
159
|
+
"integrators",
|
|
160
|
+
"metabolism",
|
|
161
|
+
# Bifurcation dynamics
|
|
162
|
+
"get_bifurcation_paths",
|
|
163
|
+
"compute_bifurcation_score",
|
|
164
|
+
# Propagation dynamics
|
|
165
|
+
"propagate_dissonance",
|
|
166
|
+
"compute_network_dissonance_field",
|
|
167
|
+
"detect_bifurcation_cascade",
|
|
168
|
+
"ALIAS_D2EPI",
|
|
169
|
+
"ALIAS_DNFR",
|
|
170
|
+
"ALIAS_DSI",
|
|
171
|
+
"ALIAS_EPI",
|
|
172
|
+
"ALIAS_SI",
|
|
173
|
+
"ALIAS_VF",
|
|
174
|
+
"AbstractSelector",
|
|
175
|
+
"DefaultGlyphSelector",
|
|
176
|
+
"ParametricGlyphSelector",
|
|
177
|
+
"GlyphCode",
|
|
178
|
+
"_SelectorPreselection",
|
|
179
|
+
"_apply_glyphs",
|
|
180
|
+
"_apply_selector",
|
|
181
|
+
"_choose_glyph",
|
|
182
|
+
"_collect_selector_metrics",
|
|
183
|
+
"_configure_selector_weights",
|
|
184
|
+
"ProcessPoolExecutor",
|
|
185
|
+
"_maybe_remesh",
|
|
186
|
+
"_normalize_job_overrides",
|
|
187
|
+
"_prepare_dnfr",
|
|
188
|
+
"_prepare_dnfr_data",
|
|
189
|
+
"_prepare_selector_preselection",
|
|
190
|
+
"_resolve_jobs_override",
|
|
191
|
+
"_resolve_preselected_glyph",
|
|
192
|
+
"_run_after_callbacks",
|
|
193
|
+
"_run_before_callbacks",
|
|
194
|
+
"_run_validators",
|
|
195
|
+
"_selector_parallel_jobs",
|
|
196
|
+
"_update_epi_hist",
|
|
197
|
+
"_update_node_sample",
|
|
198
|
+
"_update_nodes",
|
|
199
|
+
"_compute_dnfr",
|
|
200
|
+
"_compute_neighbor_means",
|
|
201
|
+
"_init_dnfr_cache",
|
|
202
|
+
"_refresh_dnfr_vectors",
|
|
203
|
+
"adapt_vf_by_coherence",
|
|
204
|
+
"coordinate_global_local_phase",
|
|
205
|
+
"compute_Si",
|
|
206
|
+
"compute_canonical_nodal_derivative",
|
|
207
|
+
"NodalEquationResult",
|
|
208
|
+
"validate_nodal_gradient",
|
|
209
|
+
"validate_structural_frequency",
|
|
210
|
+
"default_compute_delta_nfr",
|
|
211
|
+
"default_glyph_selector",
|
|
212
|
+
"dnfr_epi_vf_mixed",
|
|
213
|
+
"dnfr_laplacian",
|
|
214
|
+
"dnfr_phase_only",
|
|
215
|
+
"get_numpy",
|
|
216
|
+
"apply_glyph",
|
|
217
|
+
"parametric_glyph_selector",
|
|
218
|
+
"AbstractIntegrator",
|
|
219
|
+
"DefaultIntegrator",
|
|
220
|
+
"prepare_integration_params",
|
|
221
|
+
"run",
|
|
222
|
+
"set_delta_nfr_hook",
|
|
223
|
+
"step",
|
|
224
|
+
"update_epi_via_nodal_equation",
|
|
225
|
+
"DynamicLimits",
|
|
226
|
+
"DynamicLimitsConfig",
|
|
227
|
+
"compute_dynamic_limits",
|
|
228
|
+
"structural_clip",
|
|
229
|
+
"StructuralClipStats",
|
|
230
|
+
"get_clip_stats",
|
|
231
|
+
"reset_clip_stats",
|
|
232
|
+
"AdaptiveLearningSystem",
|
|
233
|
+
"StructuralFeedbackLoop",
|
|
234
|
+
"AdaptiveSequenceSelector",
|
|
235
|
+
"StructuralHomeostasis",
|
|
236
|
+
"get_bifurcation_paths",
|
|
237
|
+
"compute_bifurcation_score",
|
|
238
|
+
)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal, Sequence
|
|
4
|
+
|
|
5
|
+
from tnfr.types import GlyphCode, TNFRGraph
|
|
6
|
+
|
|
7
|
+
__all__: tuple[str, ...]
|
|
8
|
+
|
|
9
|
+
dnfr: Any
|
|
10
|
+
integrators: Any
|
|
11
|
+
metabolism: Any
|
|
12
|
+
|
|
13
|
+
ALIAS_D2EPI: Sequence[str]
|
|
14
|
+
ALIAS_DNFR: Sequence[str]
|
|
15
|
+
ALIAS_DSI: Sequence[str]
|
|
16
|
+
ALIAS_EPI: Sequence[str]
|
|
17
|
+
ALIAS_SI: Sequence[str]
|
|
18
|
+
ALIAS_VF: Sequence[str]
|
|
19
|
+
|
|
20
|
+
AbstractSelector: Any
|
|
21
|
+
DefaultGlyphSelector: Any
|
|
22
|
+
ParametricGlyphSelector: Any
|
|
23
|
+
StructuralFeedbackLoop: Any
|
|
24
|
+
AdaptiveSequenceSelector: Any
|
|
25
|
+
StructuralHomeostasis: Any
|
|
26
|
+
AdaptiveLearningSystem: Any
|
|
27
|
+
_SelectorPreselection: Any
|
|
28
|
+
_apply_glyphs: Any
|
|
29
|
+
_apply_selector: Any
|
|
30
|
+
_choose_glyph: Any
|
|
31
|
+
_configure_selector_weights: Any
|
|
32
|
+
ProcessPoolExecutor: Any
|
|
33
|
+
_maybe_remesh: Any
|
|
34
|
+
_normalize_job_overrides: Any
|
|
35
|
+
_prepare_dnfr: Any
|
|
36
|
+
_prepare_dnfr_data: Any
|
|
37
|
+
_prepare_selector_preselection: Any
|
|
38
|
+
_resolve_jobs_override: Any
|
|
39
|
+
_resolve_preselected_glyph: Any
|
|
40
|
+
_run_after_callbacks: Any
|
|
41
|
+
_run_before_callbacks: Any
|
|
42
|
+
_run_validators: Any
|
|
43
|
+
_selector_parallel_jobs: Any
|
|
44
|
+
_update_epi_hist: Any
|
|
45
|
+
_update_node_sample: Any
|
|
46
|
+
_update_nodes: Any
|
|
47
|
+
_compute_dnfr: Any
|
|
48
|
+
_compute_neighbor_means: Any
|
|
49
|
+
_init_dnfr_cache: Any
|
|
50
|
+
_refresh_dnfr_vectors: Any
|
|
51
|
+
adapt_vf_by_coherence: Any
|
|
52
|
+
coordinate_global_local_phase: Any
|
|
53
|
+
default_compute_delta_nfr: Any
|
|
54
|
+
default_glyph_selector: Any
|
|
55
|
+
dnfr_epi_vf_mixed: Any
|
|
56
|
+
dnfr_laplacian: Any
|
|
57
|
+
dnfr_phase_only: Any
|
|
58
|
+
get_numpy: Any
|
|
59
|
+
apply_glyph: Any
|
|
60
|
+
parametric_glyph_selector: Any
|
|
61
|
+
|
|
62
|
+
AbstractIntegrator: Any
|
|
63
|
+
DefaultIntegrator: Any
|
|
64
|
+
|
|
65
|
+
def prepare_integration_params(
|
|
66
|
+
G: TNFRGraph,
|
|
67
|
+
dt: float | None = ...,
|
|
68
|
+
t: float | None = ...,
|
|
69
|
+
method: Literal["euler", "rk4"] | None = ...,
|
|
70
|
+
) -> tuple[float, int, float, Literal["euler", "rk4"]]: ...
|
|
71
|
+
|
|
72
|
+
run: Any
|
|
73
|
+
set_delta_nfr_hook: Any
|
|
74
|
+
step: Any
|
|
75
|
+
|
|
76
|
+
def update_epi_via_nodal_equation(
|
|
77
|
+
G: TNFRGraph,
|
|
78
|
+
*,
|
|
79
|
+
dt: float | None = ...,
|
|
80
|
+
t: float | None = ...,
|
|
81
|
+
method: Literal["euler", "rk4"] | None = ...,
|
|
82
|
+
n_jobs: int | None = ...,
|
|
83
|
+
) -> None: ...
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"""νf adaptation routines for TNFR dynamics."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
7
|
+
from typing import Any, cast
|
|
8
|
+
|
|
9
|
+
from ..alias import collect_attr, set_vf
|
|
10
|
+
from ..constants import get_graph_param
|
|
11
|
+
from ..utils import clamp, resolve_chunk_size
|
|
12
|
+
from ..metrics.common import ensure_neighbors_map
|
|
13
|
+
from ..types import CoherenceMetric, DeltaNFR, TNFRGraph
|
|
14
|
+
from ..utils import get_numpy
|
|
15
|
+
from .aliases import ALIAS_DNFR, ALIAS_SI, ALIAS_VF
|
|
16
|
+
|
|
17
|
+
__all__ = ("adapt_vf_by_coherence",)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _vf_adapt_chunk(
|
|
21
|
+
args: tuple[list[tuple[Any, int, tuple[int, ...]]], tuple[float, ...], float],
|
|
22
|
+
) -> list[tuple[Any, float]]:
|
|
23
|
+
"""Return proposed νf updates for ``chunk`` of stable nodes."""
|
|
24
|
+
|
|
25
|
+
chunk, vf_values, mu = args
|
|
26
|
+
updates: list[tuple[Any, float]] = []
|
|
27
|
+
for node, idx, neighbor_idx in chunk:
|
|
28
|
+
vf = vf_values[idx]
|
|
29
|
+
if neighbor_idx:
|
|
30
|
+
mean = math.fsum(vf_values[j] for j in neighbor_idx) / len(neighbor_idx)
|
|
31
|
+
else:
|
|
32
|
+
mean = vf
|
|
33
|
+
updates.append((node, vf + mu * (mean - vf)))
|
|
34
|
+
return updates
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def adapt_vf_by_coherence(G: TNFRGraph, n_jobs: int | None = None) -> None:
|
|
38
|
+
"""Synchronise νf to the neighbour mean once ΔNFR and Si stay coherent.
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
G : TNFRGraph
|
|
43
|
+
Graph that stores the TNFR nodes and configuration required for
|
|
44
|
+
adaptation. The routine reads ``VF_ADAPT_TAU`` (τ) to decide how many
|
|
45
|
+
consecutive stable steps a node must accumulate in ``stable_count``
|
|
46
|
+
before updating. The adaptation weight ``VF_ADAPT_MU`` (μ) controls how
|
|
47
|
+
quickly νf moves toward the neighbour mean. Stability is detected when
|
|
48
|
+
the absolute ΔNFR stays below ``EPS_DNFR_STABLE`` and the sense index Si
|
|
49
|
+
exceeds the selector threshold ``SELECTOR_THRESHOLDS['si_hi']`` (falling
|
|
50
|
+
back to ``GLYPH_THRESHOLDS['hi']``). Only nodes that satisfy both
|
|
51
|
+
thresholds for τ successive evaluations are eligible for μ-weighted
|
|
52
|
+
averaging.
|
|
53
|
+
n_jobs : int or None, optional
|
|
54
|
+
Number of worker processes used for eligible nodes. ``None`` (the
|
|
55
|
+
default) keeps the adaptation serial, ``1`` disables parallelism, and
|
|
56
|
+
any value greater than one dispatches chunks of nodes to a
|
|
57
|
+
:class:`~concurrent.futures.ProcessPoolExecutor` so large graphs can
|
|
58
|
+
adjust νf without blocking the main dynamic loop.
|
|
59
|
+
|
|
60
|
+
Returns
|
|
61
|
+
-------
|
|
62
|
+
None
|
|
63
|
+
The graph is updated in place; no value is returned.
|
|
64
|
+
|
|
65
|
+
Raises
|
|
66
|
+
------
|
|
67
|
+
KeyError
|
|
68
|
+
Raised when ``G.graph`` lacks the canonical adaptation parameters and
|
|
69
|
+
defaults have not been injected.
|
|
70
|
+
|
|
71
|
+
Examples
|
|
72
|
+
--------
|
|
73
|
+
>>> from tnfr.constants import inject_defaults
|
|
74
|
+
>>> from tnfr.dynamics import adapt_vf_by_coherence
|
|
75
|
+
>>> from tnfr.structural import create_nfr
|
|
76
|
+
>>> G, seed = create_nfr("seed", vf=0.2)
|
|
77
|
+
>>> _, anchor = create_nfr("anchor", graph=G, vf=1.0)
|
|
78
|
+
>>> G.add_edge(seed, anchor)
|
|
79
|
+
>>> inject_defaults(G)
|
|
80
|
+
>>> G.graph["VF_ADAPT_TAU"] = 2 # τ: consecutive stable steps
|
|
81
|
+
>>> G.graph["VF_ADAPT_MU"] = 0.5 # μ: neighbour coupling strength
|
|
82
|
+
>>> G.graph["SELECTOR_THRESHOLDS"] = {"si_hi": 0.8}
|
|
83
|
+
>>> for node in G.nodes:
|
|
84
|
+
... G.nodes[node]["Si"] = 0.9 # above ΔSi threshold
|
|
85
|
+
... G.nodes[node]["ΔNFR"] = 0.0 # within |ΔNFR| ≤ eps guard
|
|
86
|
+
... G.nodes[node]["stable_count"] = 1
|
|
87
|
+
>>> adapt_vf_by_coherence(G)
|
|
88
|
+
>>> round(G.nodes[seed]["νf"], 2), round(G.nodes[anchor]["νf"], 2)
|
|
89
|
+
(0.6, 0.6)
|
|
90
|
+
>>> G.nodes[seed]["stable_count"], G.nodes[anchor]["stable_count"] >= 2
|
|
91
|
+
(2, True)
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
required_keys = ("VF_ADAPT_TAU", "VF_ADAPT_MU")
|
|
95
|
+
missing_keys = [key for key in required_keys if key not in G.graph]
|
|
96
|
+
if missing_keys:
|
|
97
|
+
missing_list = ", ".join(sorted(missing_keys))
|
|
98
|
+
raise KeyError(
|
|
99
|
+
"adapt_vf_by_coherence requires graph parameters "
|
|
100
|
+
f"{missing_list}; call tnfr.constants.inject_defaults(G) "
|
|
101
|
+
"before adaptation."
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
tau = get_graph_param(G, "VF_ADAPT_TAU", int)
|
|
105
|
+
mu = float(get_graph_param(G, "VF_ADAPT_MU"))
|
|
106
|
+
eps_dnfr = cast(DeltaNFR, get_graph_param(G, "EPS_DNFR_STABLE"))
|
|
107
|
+
thr_sel = get_graph_param(G, "SELECTOR_THRESHOLDS", dict)
|
|
108
|
+
thr_def = get_graph_param(G, "GLYPH_THRESHOLDS", dict)
|
|
109
|
+
si_hi = cast(
|
|
110
|
+
CoherenceMetric,
|
|
111
|
+
float(thr_sel.get("si_hi", thr_def.get("hi", 0.66))),
|
|
112
|
+
)
|
|
113
|
+
vf_min = float(get_graph_param(G, "VF_MIN"))
|
|
114
|
+
vf_max = float(get_graph_param(G, "VF_MAX"))
|
|
115
|
+
|
|
116
|
+
nodes = list(G.nodes)
|
|
117
|
+
if not nodes:
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
neighbors_map = ensure_neighbors_map(G)
|
|
121
|
+
node_count = len(nodes)
|
|
122
|
+
node_index = {node: idx for idx, node in enumerate(nodes)}
|
|
123
|
+
|
|
124
|
+
jobs: int | None
|
|
125
|
+
if n_jobs is None:
|
|
126
|
+
jobs = None
|
|
127
|
+
else:
|
|
128
|
+
try:
|
|
129
|
+
jobs = int(n_jobs)
|
|
130
|
+
except (TypeError, ValueError):
|
|
131
|
+
jobs = None
|
|
132
|
+
else:
|
|
133
|
+
if jobs <= 1:
|
|
134
|
+
jobs = None
|
|
135
|
+
|
|
136
|
+
np_mod = get_numpy()
|
|
137
|
+
use_np = np_mod is not None
|
|
138
|
+
|
|
139
|
+
si_values = collect_attr(G, nodes, ALIAS_SI, 0.0, np=np_mod if use_np else None)
|
|
140
|
+
dnfr_values = collect_attr(G, nodes, ALIAS_DNFR, 0.0, np=np_mod if use_np else None)
|
|
141
|
+
vf_values = collect_attr(G, nodes, ALIAS_VF, 0.0, np=np_mod if use_np else None)
|
|
142
|
+
|
|
143
|
+
if use_np:
|
|
144
|
+
np = np_mod # type: ignore[assignment]
|
|
145
|
+
assert np is not None
|
|
146
|
+
si_arr = si_values.astype(float, copy=False)
|
|
147
|
+
dnfr_arr = np.abs(dnfr_values.astype(float, copy=False))
|
|
148
|
+
vf_arr = vf_values.astype(float, copy=False)
|
|
149
|
+
|
|
150
|
+
prev_counts = np.fromiter(
|
|
151
|
+
(int(G.nodes[node].get("stable_count", 0)) for node in nodes),
|
|
152
|
+
dtype=int,
|
|
153
|
+
count=node_count,
|
|
154
|
+
)
|
|
155
|
+
stable_mask = (si_arr >= si_hi) & (dnfr_arr <= eps_dnfr)
|
|
156
|
+
new_counts = np.where(stable_mask, prev_counts + 1, 0)
|
|
157
|
+
|
|
158
|
+
for node, count in zip(nodes, new_counts.tolist()):
|
|
159
|
+
G.nodes[node]["stable_count"] = int(count)
|
|
160
|
+
|
|
161
|
+
eligible_mask = new_counts >= tau
|
|
162
|
+
if not bool(eligible_mask.any()):
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
max_degree = 0
|
|
166
|
+
if node_count:
|
|
167
|
+
degree_counts = np.fromiter(
|
|
168
|
+
(len(neighbors_map.get(node, ())) for node in nodes),
|
|
169
|
+
dtype=int,
|
|
170
|
+
count=node_count,
|
|
171
|
+
)
|
|
172
|
+
if degree_counts.size:
|
|
173
|
+
max_degree = int(degree_counts.max())
|
|
174
|
+
|
|
175
|
+
if max_degree > 0:
|
|
176
|
+
neighbor_indices = np.zeros((node_count, max_degree), dtype=int)
|
|
177
|
+
mask = np.zeros((node_count, max_degree), dtype=bool)
|
|
178
|
+
for idx, node in enumerate(nodes):
|
|
179
|
+
neigh = neighbors_map.get(node, ())
|
|
180
|
+
if not neigh:
|
|
181
|
+
continue
|
|
182
|
+
idxs = [node_index[nbr] for nbr in neigh if nbr in node_index]
|
|
183
|
+
if not idxs:
|
|
184
|
+
continue
|
|
185
|
+
length = len(idxs)
|
|
186
|
+
neighbor_indices[idx, :length] = idxs
|
|
187
|
+
mask[idx, :length] = True
|
|
188
|
+
neighbor_values = vf_arr[neighbor_indices]
|
|
189
|
+
sums = (neighbor_values * mask).sum(axis=1)
|
|
190
|
+
counts = mask.sum(axis=1)
|
|
191
|
+
neighbor_means = np.where(counts > 0, sums / counts, vf_arr)
|
|
192
|
+
else:
|
|
193
|
+
neighbor_means = vf_arr
|
|
194
|
+
|
|
195
|
+
vf_updates = vf_arr + mu * (neighbor_means - vf_arr)
|
|
196
|
+
for idx in np.nonzero(eligible_mask)[0]:
|
|
197
|
+
node = nodes[int(idx)]
|
|
198
|
+
vf_new = clamp(float(vf_updates[int(idx)]), vf_min, vf_max)
|
|
199
|
+
set_vf(G, node, vf_new)
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
si_list = [float(val) for val in si_values]
|
|
203
|
+
dnfr_list = [abs(float(val)) for val in dnfr_values]
|
|
204
|
+
vf_list = [float(val) for val in vf_values]
|
|
205
|
+
|
|
206
|
+
prev_counts = [int(G.nodes[node].get("stable_count", 0)) for node in nodes]
|
|
207
|
+
stable_flags = [
|
|
208
|
+
si >= si_hi and dnfr <= eps_dnfr for si, dnfr in zip(si_list, dnfr_list)
|
|
209
|
+
]
|
|
210
|
+
new_counts = [
|
|
211
|
+
prev + 1 if flag else 0 for prev, flag in zip(prev_counts, stable_flags)
|
|
212
|
+
]
|
|
213
|
+
|
|
214
|
+
for node, count in zip(nodes, new_counts):
|
|
215
|
+
G.nodes[node]["stable_count"] = int(count)
|
|
216
|
+
|
|
217
|
+
eligible_nodes = [node for node, count in zip(nodes, new_counts) if count >= tau]
|
|
218
|
+
if not eligible_nodes:
|
|
219
|
+
return
|
|
220
|
+
|
|
221
|
+
if jobs is None:
|
|
222
|
+
for node in eligible_nodes:
|
|
223
|
+
idx = node_index[node]
|
|
224
|
+
neigh_indices = [
|
|
225
|
+
node_index[nbr]
|
|
226
|
+
for nbr in neighbors_map.get(node, ())
|
|
227
|
+
if nbr in node_index
|
|
228
|
+
]
|
|
229
|
+
if neigh_indices:
|
|
230
|
+
total = math.fsum(vf_list[i] for i in neigh_indices)
|
|
231
|
+
mean = total / len(neigh_indices)
|
|
232
|
+
else:
|
|
233
|
+
mean = vf_list[idx]
|
|
234
|
+
vf_new = vf_list[idx] + mu * (mean - vf_list[idx])
|
|
235
|
+
set_vf(G, node, clamp(float(vf_new), vf_min, vf_max))
|
|
236
|
+
return
|
|
237
|
+
|
|
238
|
+
work_items: list[tuple[Any, int, tuple[int, ...]]] = []
|
|
239
|
+
for node in eligible_nodes:
|
|
240
|
+
idx = node_index[node]
|
|
241
|
+
neigh_indices = tuple(
|
|
242
|
+
node_index[nbr] for nbr in neighbors_map.get(node, ()) if nbr in node_index
|
|
243
|
+
)
|
|
244
|
+
work_items.append((node, idx, neigh_indices))
|
|
245
|
+
|
|
246
|
+
approx_chunk = math.ceil(len(work_items) / jobs) if jobs else None
|
|
247
|
+
chunk_size = resolve_chunk_size(
|
|
248
|
+
approx_chunk,
|
|
249
|
+
len(work_items),
|
|
250
|
+
minimum=1,
|
|
251
|
+
)
|
|
252
|
+
chunks = [
|
|
253
|
+
work_items[i : i + chunk_size] for i in range(0, len(work_items), chunk_size)
|
|
254
|
+
]
|
|
255
|
+
vf_tuple = tuple(vf_list)
|
|
256
|
+
updates: dict[Any, float] = {}
|
|
257
|
+
with ProcessPoolExecutor(max_workers=jobs) as executor:
|
|
258
|
+
args = ((chunk, vf_tuple, mu) for chunk in chunks)
|
|
259
|
+
for chunk_updates in executor.map(_vf_adapt_chunk, args):
|
|
260
|
+
for node, value in chunk_updates:
|
|
261
|
+
updates[node] = float(value)
|
|
262
|
+
|
|
263
|
+
for node in eligible_nodes:
|
|
264
|
+
vf_new = updates.get(node)
|
|
265
|
+
if vf_new is None:
|
|
266
|
+
continue
|
|
267
|
+
set_vf(G, node, clamp(float(vf_new), vf_min, vf_max))
|