tnfr 4.5.2__py3-none-any.whl → 8.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of tnfr might be problematic. Click here for more details.
- tnfr/__init__.py +334 -50
- tnfr/__init__.pyi +33 -0
- tnfr/_compat.py +10 -0
- tnfr/_generated_version.py +34 -0
- tnfr/_version.py +49 -0
- tnfr/_version.pyi +7 -0
- tnfr/alias.py +214 -37
- tnfr/alias.pyi +108 -0
- tnfr/backends/__init__.py +354 -0
- tnfr/backends/jax_backend.py +173 -0
- tnfr/backends/numpy_backend.py +238 -0
- tnfr/backends/optimized_numpy.py +420 -0
- tnfr/backends/torch_backend.py +408 -0
- tnfr/cache.py +149 -556
- tnfr/cache.pyi +13 -0
- tnfr/cli/__init__.py +51 -16
- tnfr/cli/__init__.pyi +26 -0
- tnfr/cli/arguments.py +344 -32
- tnfr/cli/arguments.pyi +29 -0
- tnfr/cli/execution.py +676 -50
- tnfr/cli/execution.pyi +70 -0
- tnfr/cli/interactive_validator.py +614 -0
- tnfr/cli/utils.py +18 -3
- tnfr/cli/utils.pyi +7 -0
- tnfr/cli/validate.py +236 -0
- tnfr/compat/__init__.py +85 -0
- tnfr/compat/dataclass.py +136 -0
- tnfr/compat/jsonschema_stub.py +61 -0
- tnfr/compat/matplotlib_stub.py +73 -0
- tnfr/compat/numpy_stub.py +155 -0
- tnfr/config/__init__.py +224 -0
- tnfr/config/__init__.pyi +10 -0
- tnfr/{constants_glyphs.py → config/constants.py} +26 -20
- tnfr/config/constants.pyi +12 -0
- tnfr/config/defaults.py +54 -0
- tnfr/{constants/core.py → config/defaults_core.py} +59 -6
- tnfr/config/defaults_init.py +33 -0
- tnfr/config/defaults_metric.py +104 -0
- tnfr/config/feature_flags.py +81 -0
- tnfr/config/feature_flags.pyi +16 -0
- tnfr/config/glyph_constants.py +31 -0
- tnfr/config/init.py +77 -0
- tnfr/config/init.pyi +8 -0
- tnfr/config/operator_names.py +254 -0
- tnfr/config/operator_names.pyi +36 -0
- tnfr/config/physics_derivation.py +354 -0
- tnfr/config/presets.py +83 -0
- tnfr/config/presets.pyi +7 -0
- tnfr/config/security.py +927 -0
- tnfr/config/thresholds.py +114 -0
- tnfr/config/tnfr_config.py +498 -0
- tnfr/constants/__init__.py +51 -133
- tnfr/constants/__init__.pyi +92 -0
- tnfr/constants/aliases.py +33 -0
- tnfr/constants/aliases.pyi +27 -0
- tnfr/constants/init.py +3 -1
- tnfr/constants/init.pyi +12 -0
- tnfr/constants/metric.py +9 -15
- tnfr/constants/metric.pyi +19 -0
- tnfr/core/__init__.py +33 -0
- tnfr/core/container.py +226 -0
- tnfr/core/default_implementations.py +329 -0
- tnfr/core/interfaces.py +279 -0
- tnfr/dynamics/__init__.py +213 -633
- tnfr/dynamics/__init__.pyi +83 -0
- tnfr/dynamics/adaptation.py +267 -0
- tnfr/dynamics/adaptation.pyi +7 -0
- tnfr/dynamics/adaptive_sequences.py +189 -0
- tnfr/dynamics/adaptive_sequences.pyi +14 -0
- tnfr/dynamics/aliases.py +23 -0
- tnfr/dynamics/aliases.pyi +19 -0
- tnfr/dynamics/bifurcation.py +232 -0
- tnfr/dynamics/canonical.py +229 -0
- tnfr/dynamics/canonical.pyi +48 -0
- tnfr/dynamics/coordination.py +385 -0
- tnfr/dynamics/coordination.pyi +25 -0
- tnfr/dynamics/dnfr.py +2699 -398
- tnfr/dynamics/dnfr.pyi +26 -0
- tnfr/dynamics/dynamic_limits.py +225 -0
- tnfr/dynamics/feedback.py +252 -0
- tnfr/dynamics/feedback.pyi +24 -0
- tnfr/dynamics/fused_dnfr.py +454 -0
- tnfr/dynamics/homeostasis.py +157 -0
- tnfr/dynamics/homeostasis.pyi +14 -0
- tnfr/dynamics/integrators.py +496 -102
- tnfr/dynamics/integrators.pyi +36 -0
- tnfr/dynamics/learning.py +310 -0
- tnfr/dynamics/learning.pyi +33 -0
- tnfr/dynamics/metabolism.py +254 -0
- tnfr/dynamics/nbody.py +796 -0
- tnfr/dynamics/nbody_tnfr.py +783 -0
- tnfr/dynamics/propagation.py +326 -0
- tnfr/dynamics/runtime.py +908 -0
- tnfr/dynamics/runtime.pyi +77 -0
- tnfr/dynamics/sampling.py +10 -5
- tnfr/dynamics/sampling.pyi +7 -0
- tnfr/dynamics/selectors.py +711 -0
- tnfr/dynamics/selectors.pyi +85 -0
- tnfr/dynamics/structural_clip.py +207 -0
- tnfr/errors/__init__.py +37 -0
- tnfr/errors/contextual.py +492 -0
- tnfr/execution.py +77 -55
- tnfr/execution.pyi +45 -0
- tnfr/extensions/__init__.py +205 -0
- tnfr/extensions/__init__.pyi +18 -0
- tnfr/extensions/base.py +173 -0
- tnfr/extensions/base.pyi +35 -0
- tnfr/extensions/business/__init__.py +71 -0
- tnfr/extensions/business/__init__.pyi +11 -0
- tnfr/extensions/business/cookbook.py +88 -0
- tnfr/extensions/business/cookbook.pyi +8 -0
- tnfr/extensions/business/health_analyzers.py +202 -0
- tnfr/extensions/business/health_analyzers.pyi +9 -0
- tnfr/extensions/business/patterns.py +183 -0
- tnfr/extensions/business/patterns.pyi +8 -0
- tnfr/extensions/medical/__init__.py +73 -0
- tnfr/extensions/medical/__init__.pyi +11 -0
- tnfr/extensions/medical/cookbook.py +88 -0
- tnfr/extensions/medical/cookbook.pyi +8 -0
- tnfr/extensions/medical/health_analyzers.py +181 -0
- tnfr/extensions/medical/health_analyzers.pyi +9 -0
- tnfr/extensions/medical/patterns.py +163 -0
- tnfr/extensions/medical/patterns.pyi +8 -0
- tnfr/flatten.py +29 -50
- tnfr/flatten.pyi +21 -0
- tnfr/gamma.py +66 -53
- tnfr/gamma.pyi +36 -0
- tnfr/glyph_history.py +144 -57
- tnfr/glyph_history.pyi +35 -0
- tnfr/glyph_runtime.py +19 -0
- tnfr/glyph_runtime.pyi +8 -0
- tnfr/immutable.py +70 -30
- tnfr/immutable.pyi +36 -0
- tnfr/initialization.py +22 -16
- tnfr/initialization.pyi +65 -0
- tnfr/io.py +5 -241
- tnfr/io.pyi +13 -0
- tnfr/locking.pyi +7 -0
- tnfr/mathematics/__init__.py +79 -0
- tnfr/mathematics/backend.py +453 -0
- tnfr/mathematics/backend.pyi +99 -0
- tnfr/mathematics/dynamics.py +408 -0
- tnfr/mathematics/dynamics.pyi +90 -0
- tnfr/mathematics/epi.py +391 -0
- tnfr/mathematics/epi.pyi +65 -0
- tnfr/mathematics/generators.py +242 -0
- tnfr/mathematics/generators.pyi +29 -0
- tnfr/mathematics/metrics.py +119 -0
- tnfr/mathematics/metrics.pyi +16 -0
- tnfr/mathematics/operators.py +239 -0
- tnfr/mathematics/operators.pyi +59 -0
- tnfr/mathematics/operators_factory.py +124 -0
- tnfr/mathematics/operators_factory.pyi +11 -0
- tnfr/mathematics/projection.py +87 -0
- tnfr/mathematics/projection.pyi +33 -0
- tnfr/mathematics/runtime.py +182 -0
- tnfr/mathematics/runtime.pyi +64 -0
- tnfr/mathematics/spaces.py +256 -0
- tnfr/mathematics/spaces.pyi +83 -0
- tnfr/mathematics/transforms.py +305 -0
- tnfr/mathematics/transforms.pyi +62 -0
- tnfr/metrics/__init__.py +47 -9
- tnfr/metrics/__init__.pyi +20 -0
- tnfr/metrics/buffer_cache.py +163 -0
- tnfr/metrics/buffer_cache.pyi +24 -0
- tnfr/metrics/cache_utils.py +214 -0
- tnfr/metrics/coherence.py +1510 -330
- tnfr/metrics/coherence.pyi +129 -0
- tnfr/metrics/common.py +23 -16
- tnfr/metrics/common.pyi +35 -0
- tnfr/metrics/core.py +251 -36
- tnfr/metrics/core.pyi +13 -0
- tnfr/metrics/diagnosis.py +709 -110
- tnfr/metrics/diagnosis.pyi +86 -0
- tnfr/metrics/emergence.py +245 -0
- tnfr/metrics/export.py +60 -18
- tnfr/metrics/export.pyi +7 -0
- tnfr/metrics/glyph_timing.py +233 -43
- tnfr/metrics/glyph_timing.pyi +81 -0
- tnfr/metrics/learning_metrics.py +280 -0
- tnfr/metrics/learning_metrics.pyi +21 -0
- tnfr/metrics/phase_coherence.py +351 -0
- tnfr/metrics/phase_compatibility.py +349 -0
- tnfr/metrics/reporting.py +63 -28
- tnfr/metrics/reporting.pyi +25 -0
- tnfr/metrics/sense_index.py +1126 -43
- tnfr/metrics/sense_index.pyi +9 -0
- tnfr/metrics/trig.py +215 -23
- tnfr/metrics/trig.pyi +13 -0
- tnfr/metrics/trig_cache.py +148 -24
- tnfr/metrics/trig_cache.pyi +10 -0
- tnfr/multiscale/__init__.py +32 -0
- tnfr/multiscale/hierarchical.py +517 -0
- tnfr/node.py +646 -140
- tnfr/node.pyi +139 -0
- tnfr/observers.py +160 -45
- tnfr/observers.pyi +31 -0
- tnfr/ontosim.py +23 -19
- tnfr/ontosim.pyi +28 -0
- tnfr/operators/__init__.py +1358 -106
- tnfr/operators/__init__.pyi +31 -0
- tnfr/operators/algebra.py +277 -0
- tnfr/operators/canonical_patterns.py +420 -0
- tnfr/operators/cascade.py +267 -0
- tnfr/operators/cycle_detection.py +358 -0
- tnfr/operators/definitions.py +4108 -0
- tnfr/operators/definitions.pyi +78 -0
- tnfr/operators/grammar.py +1164 -0
- tnfr/operators/grammar.pyi +140 -0
- tnfr/operators/hamiltonian.py +710 -0
- tnfr/operators/health_analyzer.py +809 -0
- tnfr/operators/jitter.py +107 -38
- tnfr/operators/jitter.pyi +11 -0
- tnfr/operators/lifecycle.py +314 -0
- tnfr/operators/metabolism.py +618 -0
- tnfr/operators/metrics.py +2138 -0
- tnfr/operators/network_analysis/__init__.py +27 -0
- tnfr/operators/network_analysis/source_detection.py +186 -0
- tnfr/operators/nodal_equation.py +395 -0
- tnfr/operators/pattern_detection.py +660 -0
- tnfr/operators/patterns.py +669 -0
- tnfr/operators/postconditions/__init__.py +38 -0
- tnfr/operators/postconditions/mutation.py +236 -0
- tnfr/operators/preconditions/__init__.py +1226 -0
- tnfr/operators/preconditions/coherence.py +305 -0
- tnfr/operators/preconditions/dissonance.py +236 -0
- tnfr/operators/preconditions/emission.py +128 -0
- tnfr/operators/preconditions/mutation.py +580 -0
- tnfr/operators/preconditions/reception.py +125 -0
- tnfr/operators/preconditions/resonance.py +364 -0
- tnfr/operators/registry.py +74 -0
- tnfr/operators/registry.pyi +9 -0
- tnfr/operators/remesh.py +1415 -91
- tnfr/operators/remesh.pyi +26 -0
- tnfr/operators/structural_units.py +268 -0
- tnfr/operators/unified_grammar.py +105 -0
- tnfr/parallel/__init__.py +54 -0
- tnfr/parallel/auto_scaler.py +234 -0
- tnfr/parallel/distributed.py +384 -0
- tnfr/parallel/engine.py +238 -0
- tnfr/parallel/gpu_engine.py +420 -0
- tnfr/parallel/monitoring.py +248 -0
- tnfr/parallel/partitioner.py +459 -0
- tnfr/py.typed +0 -0
- tnfr/recipes/__init__.py +22 -0
- tnfr/recipes/cookbook.py +743 -0
- tnfr/rng.py +75 -151
- tnfr/rng.pyi +26 -0
- tnfr/schemas/__init__.py +8 -0
- tnfr/schemas/grammar.json +94 -0
- tnfr/sdk/__init__.py +107 -0
- tnfr/sdk/__init__.pyi +19 -0
- tnfr/sdk/adaptive_system.py +173 -0
- tnfr/sdk/adaptive_system.pyi +21 -0
- tnfr/sdk/builders.py +370 -0
- tnfr/sdk/builders.pyi +51 -0
- tnfr/sdk/fluent.py +1121 -0
- tnfr/sdk/fluent.pyi +74 -0
- tnfr/sdk/templates.py +342 -0
- tnfr/sdk/templates.pyi +41 -0
- tnfr/sdk/utils.py +341 -0
- tnfr/secure_config.py +46 -0
- tnfr/security/__init__.py +70 -0
- tnfr/security/database.py +514 -0
- tnfr/security/subprocess.py +503 -0
- tnfr/security/validation.py +290 -0
- tnfr/selector.py +59 -22
- tnfr/selector.pyi +19 -0
- tnfr/sense.py +92 -67
- tnfr/sense.pyi +23 -0
- tnfr/services/__init__.py +17 -0
- tnfr/services/orchestrator.py +325 -0
- tnfr/sparse/__init__.py +39 -0
- tnfr/sparse/representations.py +492 -0
- tnfr/structural.py +639 -263
- tnfr/structural.pyi +83 -0
- tnfr/telemetry/__init__.py +35 -0
- tnfr/telemetry/cache_metrics.py +226 -0
- tnfr/telemetry/cache_metrics.pyi +64 -0
- tnfr/telemetry/nu_f.py +422 -0
- tnfr/telemetry/nu_f.pyi +108 -0
- tnfr/telemetry/verbosity.py +36 -0
- tnfr/telemetry/verbosity.pyi +15 -0
- tnfr/tokens.py +2 -4
- tnfr/tokens.pyi +36 -0
- tnfr/tools/__init__.py +20 -0
- tnfr/tools/domain_templates.py +478 -0
- tnfr/tools/sequence_generator.py +846 -0
- tnfr/topology/__init__.py +13 -0
- tnfr/topology/asymmetry.py +151 -0
- tnfr/trace.py +300 -126
- tnfr/trace.pyi +42 -0
- tnfr/tutorials/__init__.py +38 -0
- tnfr/tutorials/autonomous_evolution.py +285 -0
- tnfr/tutorials/interactive.py +1576 -0
- tnfr/tutorials/structural_metabolism.py +238 -0
- tnfr/types.py +743 -12
- tnfr/types.pyi +357 -0
- tnfr/units.py +68 -0
- tnfr/units.pyi +13 -0
- tnfr/utils/__init__.py +282 -0
- tnfr/utils/__init__.pyi +215 -0
- tnfr/utils/cache.py +4223 -0
- tnfr/utils/cache.pyi +470 -0
- tnfr/{callback_utils.py → utils/callbacks.py} +26 -39
- tnfr/utils/callbacks.pyi +49 -0
- tnfr/utils/chunks.py +108 -0
- tnfr/utils/chunks.pyi +22 -0
- tnfr/utils/data.py +428 -0
- tnfr/utils/data.pyi +74 -0
- tnfr/utils/graph.py +85 -0
- tnfr/utils/graph.pyi +10 -0
- tnfr/utils/init.py +821 -0
- tnfr/utils/init.pyi +80 -0
- tnfr/utils/io.py +559 -0
- tnfr/utils/io.pyi +66 -0
- tnfr/{helpers → utils}/numeric.py +51 -24
- tnfr/utils/numeric.pyi +21 -0
- tnfr/validation/__init__.py +257 -0
- tnfr/validation/__init__.pyi +85 -0
- tnfr/validation/compatibility.py +460 -0
- tnfr/validation/compatibility.pyi +6 -0
- tnfr/validation/config.py +73 -0
- tnfr/validation/graph.py +139 -0
- tnfr/validation/graph.pyi +18 -0
- tnfr/validation/input_validation.py +755 -0
- tnfr/validation/invariants.py +712 -0
- tnfr/validation/rules.py +253 -0
- tnfr/validation/rules.pyi +44 -0
- tnfr/validation/runtime.py +279 -0
- tnfr/validation/runtime.pyi +28 -0
- tnfr/validation/sequence_validator.py +162 -0
- tnfr/validation/soft_filters.py +170 -0
- tnfr/validation/soft_filters.pyi +32 -0
- tnfr/validation/spectral.py +164 -0
- tnfr/validation/spectral.pyi +42 -0
- tnfr/validation/validator.py +1266 -0
- tnfr/validation/window.py +39 -0
- tnfr/validation/window.pyi +1 -0
- tnfr/visualization/__init__.py +98 -0
- tnfr/visualization/cascade_viz.py +256 -0
- tnfr/visualization/hierarchy.py +284 -0
- tnfr/visualization/sequence_plotter.py +784 -0
- tnfr/viz/__init__.py +60 -0
- tnfr/viz/matplotlib.py +278 -0
- tnfr/viz/matplotlib.pyi +35 -0
- tnfr-8.5.0.dist-info/METADATA +573 -0
- tnfr-8.5.0.dist-info/RECORD +353 -0
- {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/entry_points.txt +1 -0
- {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/licenses/LICENSE.md +1 -1
- tnfr/collections_utils.py +0 -300
- tnfr/config.py +0 -32
- tnfr/grammar.py +0 -344
- tnfr/graph_utils.py +0 -84
- tnfr/helpers/__init__.py +0 -71
- tnfr/import_utils.py +0 -228
- tnfr/json_utils.py +0 -162
- tnfr/logging_utils.py +0 -116
- tnfr/presets.py +0 -60
- tnfr/validators.py +0 -84
- tnfr/value_utils.py +0 -59
- tnfr-4.5.2.dist-info/METADATA +0 -379
- tnfr-4.5.2.dist-info/RECORD +0 -67
- {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/WHEEL +0 -0
- {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
"""TNFR-aware network partitioning for parallel computation.
|
|
2
|
+
|
|
3
|
+
Partitions networks respecting structural coherence rather than classical graph
|
|
4
|
+
metrics. Communities are grown based on phase synchrony and frequency alignment
|
|
5
|
+
to preserve the fractal organization inherent in TNFR.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import math
|
|
11
|
+
from typing import TYPE_CHECKING, Any, List, Optional, Set, Tuple
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
14
|
+
from ..types import TNFRGraph
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
import numpy as np
|
|
18
|
+
|
|
19
|
+
HAS_NUMPY = True
|
|
20
|
+
except ImportError:
|
|
21
|
+
np = None # type: ignore
|
|
22
|
+
HAS_NUMPY = False
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
from scipy.spatial import KDTree
|
|
26
|
+
|
|
27
|
+
HAS_SCIPY = True
|
|
28
|
+
except ImportError:
|
|
29
|
+
HAS_SCIPY = False
|
|
30
|
+
KDTree = None # type: ignore
|
|
31
|
+
|
|
32
|
+
from ..alias import get_attr
|
|
33
|
+
from ..constants.aliases import ALIAS_THETA, ALIAS_VF
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class FractalPartitioner:
|
|
37
|
+
"""Partitions TNFR networks respecting structural coherence.
|
|
38
|
+
|
|
39
|
+
This partitioner detects communities based on TNFR metrics (frequency and
|
|
40
|
+
phase) rather than classical graph metrics. It ensures that nodes with
|
|
41
|
+
similar structural frequencies and synchronized phases are grouped together,
|
|
42
|
+
preserving operational fractality during parallel processing.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
max_partition_size : int, default=100
|
|
47
|
+
Maximum number of nodes per partition. Larger partitions reduce
|
|
48
|
+
communication overhead but may limit parallelism. If None, uses
|
|
49
|
+
adaptive partitioning based on network density.
|
|
50
|
+
coherence_threshold : float, default=0.3
|
|
51
|
+
Minimum coherence score for adding a node to a community. Higher values
|
|
52
|
+
create tighter communities but may result in more partitions.
|
|
53
|
+
use_spatial_index : bool, default=True
|
|
54
|
+
Whether to use spatial indexing (KDTree) for O(n log n) neighbor
|
|
55
|
+
finding. Requires scipy. Falls back to O(n²) if unavailable.
|
|
56
|
+
adaptive : bool, default=True
|
|
57
|
+
Whether to use adaptive partitioning that adjusts partition size
|
|
58
|
+
based on network density and clustering coefficient.
|
|
59
|
+
|
|
60
|
+
Examples
|
|
61
|
+
--------
|
|
62
|
+
>>> import networkx as nx
|
|
63
|
+
>>> from tnfr.parallel import FractalPartitioner
|
|
64
|
+
>>> G = nx.Graph()
|
|
65
|
+
>>> G.add_edges_from([("a", "b"), ("b", "c")])
|
|
66
|
+
>>> for node in G.nodes():
|
|
67
|
+
... G.nodes[node]["vf"] = 1.0
|
|
68
|
+
... G.nodes[node]["phase"] = 0.0
|
|
69
|
+
>>> partitioner = FractalPartitioner(max_partition_size=50)
|
|
70
|
+
>>> partitions = partitioner.partition_network(G)
|
|
71
|
+
>>> len(partitions) >= 1
|
|
72
|
+
True
|
|
73
|
+
|
|
74
|
+
Notes
|
|
75
|
+
-----
|
|
76
|
+
Spatial indexing provides O(n log n) complexity for large networks
|
|
77
|
+
compared to O(n²) without it. Adaptive partitioning automatically
|
|
78
|
+
adjusts partition size based on network characteristics.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
max_partition_size: Optional[int] = 100,
|
|
84
|
+
coherence_threshold: float = 0.3,
|
|
85
|
+
use_spatial_index: bool = True,
|
|
86
|
+
adaptive: bool = True,
|
|
87
|
+
):
|
|
88
|
+
self.max_partition_size = max_partition_size
|
|
89
|
+
self.coherence_threshold = coherence_threshold
|
|
90
|
+
self.use_spatial_index = use_spatial_index and HAS_SCIPY and HAS_NUMPY
|
|
91
|
+
self.adaptive = adaptive
|
|
92
|
+
self._kdtree = None
|
|
93
|
+
self._node_index_map = None
|
|
94
|
+
|
|
95
|
+
def partition_network(self, graph: TNFRGraph) -> List[Tuple[Set[Any], TNFRGraph]]:
|
|
96
|
+
"""Partition network into coherent subgraphs.
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
graph : TNFRGraph
|
|
101
|
+
TNFR network to partition. Nodes must have 'vf' and 'phase' attrs.
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
List[Tuple[Set[Any], TNFRGraph]]
|
|
106
|
+
List of (node_set, subgraph) tuples for parallel processing.
|
|
107
|
+
|
|
108
|
+
Notes
|
|
109
|
+
-----
|
|
110
|
+
Maintains TNFR structural invariants:
|
|
111
|
+
- Communities formed by resonance (not just topology)
|
|
112
|
+
- Phase coherence preserved within partitions
|
|
113
|
+
- Frequency alignment respected
|
|
114
|
+
|
|
115
|
+
Uses spatial indexing for O(n log n) complexity when available.
|
|
116
|
+
Adapts partition size based on network density when adaptive=True.
|
|
117
|
+
"""
|
|
118
|
+
import networkx as nx
|
|
119
|
+
|
|
120
|
+
if len(graph) == 0:
|
|
121
|
+
return []
|
|
122
|
+
|
|
123
|
+
# Determine optimal partition size adaptively
|
|
124
|
+
if self.adaptive:
|
|
125
|
+
partition_size = self._compute_adaptive_partition_size(graph)
|
|
126
|
+
else:
|
|
127
|
+
partition_size = self.max_partition_size or 100
|
|
128
|
+
|
|
129
|
+
# Build spatial index if requested and available
|
|
130
|
+
if self.use_spatial_index:
|
|
131
|
+
self._build_spatial_index(graph)
|
|
132
|
+
|
|
133
|
+
# Detect TNFR communities
|
|
134
|
+
communities = self._detect_tnfr_communities(graph)
|
|
135
|
+
|
|
136
|
+
# Create balanced partitions
|
|
137
|
+
partitions = []
|
|
138
|
+
current_partition = set()
|
|
139
|
+
|
|
140
|
+
for community in communities:
|
|
141
|
+
if len(current_partition) + len(community) <= partition_size:
|
|
142
|
+
current_partition.update(community)
|
|
143
|
+
else:
|
|
144
|
+
if current_partition:
|
|
145
|
+
subgraph = graph.subgraph(current_partition).copy()
|
|
146
|
+
partitions.append((current_partition.copy(), subgraph))
|
|
147
|
+
current_partition = community.copy()
|
|
148
|
+
|
|
149
|
+
# Add final partition
|
|
150
|
+
if current_partition:
|
|
151
|
+
subgraph = graph.subgraph(current_partition).copy()
|
|
152
|
+
partitions.append((current_partition, subgraph))
|
|
153
|
+
|
|
154
|
+
# Clean up spatial index
|
|
155
|
+
self._kdtree = None
|
|
156
|
+
self._node_index_map = None
|
|
157
|
+
|
|
158
|
+
return partitions
|
|
159
|
+
|
|
160
|
+
def _compute_adaptive_partition_size(self, graph: TNFRGraph) -> int:
|
|
161
|
+
"""Compute optimal partition size based on network characteristics.
|
|
162
|
+
|
|
163
|
+
Adapts partition size based on:
|
|
164
|
+
- Network density (sparse vs dense)
|
|
165
|
+
- Clustering coefficient (community structure)
|
|
166
|
+
- Total network size
|
|
167
|
+
|
|
168
|
+
Returns
|
|
169
|
+
-------
|
|
170
|
+
int
|
|
171
|
+
Recommended partition size for this network
|
|
172
|
+
"""
|
|
173
|
+
import networkx as nx
|
|
174
|
+
|
|
175
|
+
n_nodes = len(graph)
|
|
176
|
+
|
|
177
|
+
# Base size from configuration or defaults
|
|
178
|
+
if self.max_partition_size:
|
|
179
|
+
base_size = self.max_partition_size
|
|
180
|
+
else:
|
|
181
|
+
# Default adaptive sizing
|
|
182
|
+
if n_nodes < 100:
|
|
183
|
+
base_size = n_nodes # Don't partition small networks
|
|
184
|
+
elif n_nodes < 1000:
|
|
185
|
+
base_size = 100
|
|
186
|
+
else:
|
|
187
|
+
base_size = 200
|
|
188
|
+
|
|
189
|
+
# Adjust based on density
|
|
190
|
+
density = nx.density(graph)
|
|
191
|
+
|
|
192
|
+
if density > 0.5:
|
|
193
|
+
# Dense networks: smaller partitions reduce communication overhead
|
|
194
|
+
size_multiplier = 0.5
|
|
195
|
+
elif density > 0.1:
|
|
196
|
+
# Medium density: balanced partitioning
|
|
197
|
+
size_multiplier = 1.0
|
|
198
|
+
else:
|
|
199
|
+
# Sparse networks: larger partitions okay
|
|
200
|
+
size_multiplier = 1.5
|
|
201
|
+
|
|
202
|
+
# Adjust based on clustering
|
|
203
|
+
try:
|
|
204
|
+
avg_clustering = nx.average_clustering(graph)
|
|
205
|
+
if avg_clustering > 0.6:
|
|
206
|
+
# High clustering: communities are well-defined, can use smaller partitions
|
|
207
|
+
size_multiplier *= 0.8
|
|
208
|
+
elif avg_clustering < 0.2:
|
|
209
|
+
# Low clustering: use larger partitions
|
|
210
|
+
size_multiplier *= 1.2
|
|
211
|
+
except:
|
|
212
|
+
# If clustering calculation fails, skip adjustment
|
|
213
|
+
pass
|
|
214
|
+
|
|
215
|
+
adapted_size = int(base_size * size_multiplier)
|
|
216
|
+
# Ensure reasonable bounds
|
|
217
|
+
return max(10, min(adapted_size, 500))
|
|
218
|
+
|
|
219
|
+
def _build_spatial_index(self, graph: TNFRGraph) -> None:
|
|
220
|
+
"""Build KDTree spatial index for O(n log n) neighbor finding.
|
|
221
|
+
|
|
222
|
+
Constructs a 2D spatial index using (νf, phase) coordinates
|
|
223
|
+
to enable fast nearest-neighbor queries.
|
|
224
|
+
"""
|
|
225
|
+
if not HAS_SCIPY or not HAS_NUMPY:
|
|
226
|
+
return
|
|
227
|
+
|
|
228
|
+
nodes = list(graph.nodes())
|
|
229
|
+
if len(nodes) == 0:
|
|
230
|
+
return
|
|
231
|
+
|
|
232
|
+
# Extract νf and phase coordinates
|
|
233
|
+
def _get_node_attr(
|
|
234
|
+
node_id: Any, alias: tuple, fallback_key: str, default: float
|
|
235
|
+
) -> float:
|
|
236
|
+
"""Get node attribute via TNFR alias or direct access."""
|
|
237
|
+
return float(
|
|
238
|
+
get_attr(graph.nodes[node_id], alias, None)
|
|
239
|
+
or graph.nodes[node_id].get(fallback_key, default)
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
coords = np.array(
|
|
243
|
+
[
|
|
244
|
+
[
|
|
245
|
+
_get_node_attr(node, ALIAS_VF, "vf", 1.0),
|
|
246
|
+
_get_node_attr(node, ALIAS_THETA, "phase", 0.0),
|
|
247
|
+
]
|
|
248
|
+
for node in nodes
|
|
249
|
+
]
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
# Normalize coordinates for better distance metrics
|
|
253
|
+
# νf: normalize by mean
|
|
254
|
+
if coords[:, 0].std() > 0:
|
|
255
|
+
coords[:, 0] = (coords[:, 0] - coords[:, 0].mean()) / coords[:, 0].std()
|
|
256
|
+
|
|
257
|
+
# phase: wrap to [-π, π] for periodicity
|
|
258
|
+
coords[:, 1] = np.arctan2(np.sin(coords[:, 1]), np.cos(coords[:, 1]))
|
|
259
|
+
|
|
260
|
+
# Build KDTree
|
|
261
|
+
self._kdtree = KDTree(coords)
|
|
262
|
+
self._node_index_map = {i: node for i, node in enumerate(nodes)}
|
|
263
|
+
|
|
264
|
+
def _find_coherent_neighbors_spatial(
|
|
265
|
+
self, graph: TNFRGraph, seed: Any, available: Set[Any], k: int = 20
|
|
266
|
+
) -> List[Any]:
|
|
267
|
+
"""Find k nearest coherent neighbors using spatial index.
|
|
268
|
+
|
|
269
|
+
Uses KDTree for O(log n) nearest neighbor finding instead of O(n).
|
|
270
|
+
|
|
271
|
+
Parameters
|
|
272
|
+
----------
|
|
273
|
+
graph : TNFRGraph
|
|
274
|
+
Network graph
|
|
275
|
+
seed : Any
|
|
276
|
+
Seed node
|
|
277
|
+
available : Set[Any]
|
|
278
|
+
Available nodes to consider
|
|
279
|
+
k : int
|
|
280
|
+
Number of nearest neighbors to find
|
|
281
|
+
|
|
282
|
+
Returns
|
|
283
|
+
-------
|
|
284
|
+
List[Any]
|
|
285
|
+
List of up to k nearest coherent neighbors
|
|
286
|
+
"""
|
|
287
|
+
if self._kdtree is None or self._node_index_map is None:
|
|
288
|
+
# Fallback to graph neighbors
|
|
289
|
+
return list(set(graph.neighbors(seed)) & available)
|
|
290
|
+
|
|
291
|
+
# Find seed index
|
|
292
|
+
seed_idx = None
|
|
293
|
+
for idx, node in self._node_index_map.items():
|
|
294
|
+
if node == seed:
|
|
295
|
+
seed_idx = idx
|
|
296
|
+
break
|
|
297
|
+
|
|
298
|
+
if seed_idx is None:
|
|
299
|
+
return []
|
|
300
|
+
|
|
301
|
+
# Query k nearest neighbors (k+1 to exclude seed itself)
|
|
302
|
+
distances, indices = self._kdtree.query(
|
|
303
|
+
self._kdtree.data[seed_idx], k=min(k + 1, len(self._node_index_map))
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
# Filter to available nodes and exclude seed
|
|
307
|
+
neighbors = []
|
|
308
|
+
for idx in indices:
|
|
309
|
+
if idx == seed_idx:
|
|
310
|
+
continue
|
|
311
|
+
node = self._node_index_map[idx]
|
|
312
|
+
if node in available:
|
|
313
|
+
neighbors.append(node)
|
|
314
|
+
|
|
315
|
+
return neighbors
|
|
316
|
+
|
|
317
|
+
def _detect_tnfr_communities(self, graph: TNFRGraph) -> List[Set[Any]]:
|
|
318
|
+
"""Detect communities using TNFR coherence metrics.
|
|
319
|
+
|
|
320
|
+
Uses structural frequency and phase to grow coherent communities rather
|
|
321
|
+
than classical modularity or betweenness metrics.
|
|
322
|
+
"""
|
|
323
|
+
communities = []
|
|
324
|
+
unprocessed = set(graph.nodes())
|
|
325
|
+
|
|
326
|
+
while unprocessed:
|
|
327
|
+
# Select seed node
|
|
328
|
+
seed = next(iter(unprocessed))
|
|
329
|
+
community = self._grow_coherent_community(graph, seed, unprocessed)
|
|
330
|
+
communities.append(community)
|
|
331
|
+
unprocessed -= community
|
|
332
|
+
|
|
333
|
+
return communities
|
|
334
|
+
|
|
335
|
+
def _grow_coherent_community(
|
|
336
|
+
self, graph: TNFRGraph, seed: Any, available: Set[Any]
|
|
337
|
+
) -> Set[Any]:
|
|
338
|
+
"""Grow community from seed based on structural coherence.
|
|
339
|
+
|
|
340
|
+
Parameters
|
|
341
|
+
----------
|
|
342
|
+
graph : TNFRGraph
|
|
343
|
+
Full network graph
|
|
344
|
+
seed : Any
|
|
345
|
+
Starting node for community growth
|
|
346
|
+
available : Set[Any]
|
|
347
|
+
Nodes that haven't been assigned to communities yet
|
|
348
|
+
|
|
349
|
+
Returns
|
|
350
|
+
-------
|
|
351
|
+
Set[Any]
|
|
352
|
+
Set of nodes forming a coherent community
|
|
353
|
+
|
|
354
|
+
Notes
|
|
355
|
+
-----
|
|
356
|
+
Uses spatial indexing for O(log n) neighbor finding when available,
|
|
357
|
+
falling back to O(n) graph neighbors otherwise.
|
|
358
|
+
"""
|
|
359
|
+
community = {seed}
|
|
360
|
+
|
|
361
|
+
# Use spatial index if available for faster neighbor finding
|
|
362
|
+
if self.use_spatial_index and self._kdtree is not None:
|
|
363
|
+
candidates = set(
|
|
364
|
+
self._find_coherent_neighbors_spatial(graph, seed, available, k=50)
|
|
365
|
+
)
|
|
366
|
+
else:
|
|
367
|
+
neighbors = graph.neighbors(seed)
|
|
368
|
+
candidates = set(neighbors) & available
|
|
369
|
+
|
|
370
|
+
while candidates:
|
|
371
|
+
# Find most coherent candidate
|
|
372
|
+
best_candidate = None
|
|
373
|
+
best_coherence = -1.0
|
|
374
|
+
|
|
375
|
+
for candidate in candidates:
|
|
376
|
+
coherence = self._compute_community_coherence(
|
|
377
|
+
graph, community, candidate
|
|
378
|
+
)
|
|
379
|
+
if coherence > best_coherence:
|
|
380
|
+
best_coherence = coherence
|
|
381
|
+
best_candidate = candidate
|
|
382
|
+
|
|
383
|
+
# Add if above threshold
|
|
384
|
+
if best_coherence > self.coherence_threshold:
|
|
385
|
+
community.add(best_candidate)
|
|
386
|
+
candidates.remove(best_candidate)
|
|
387
|
+
|
|
388
|
+
# Add new neighbors as candidates
|
|
389
|
+
if self.use_spatial_index and self._kdtree is not None:
|
|
390
|
+
new_neighbors = set(
|
|
391
|
+
self._find_coherent_neighbors_spatial(
|
|
392
|
+
graph, best_candidate, available, k=50
|
|
393
|
+
)
|
|
394
|
+
)
|
|
395
|
+
else:
|
|
396
|
+
new_neighbors = set(graph.neighbors(best_candidate)) & available
|
|
397
|
+
|
|
398
|
+
candidates.update(new_neighbors - community)
|
|
399
|
+
else:
|
|
400
|
+
break # No more coherent candidates
|
|
401
|
+
|
|
402
|
+
return community
|
|
403
|
+
|
|
404
|
+
def _compute_community_coherence(
|
|
405
|
+
self, graph: TNFRGraph, community: Set[Any], candidate: Any
|
|
406
|
+
) -> float:
|
|
407
|
+
"""Compute coherence between candidate and existing community.
|
|
408
|
+
|
|
409
|
+
Uses TNFR metrics: frequency alignment (νf) and phase synchrony.
|
|
410
|
+
|
|
411
|
+
Parameters
|
|
412
|
+
----------
|
|
413
|
+
graph : TNFRGraph
|
|
414
|
+
Network graph
|
|
415
|
+
community : Set[Any]
|
|
416
|
+
Existing community nodes
|
|
417
|
+
candidate : Any
|
|
418
|
+
Candidate node to evaluate
|
|
419
|
+
|
|
420
|
+
Returns
|
|
421
|
+
-------
|
|
422
|
+
float
|
|
423
|
+
Coherence score in [0, 1], where higher means better alignment
|
|
424
|
+
"""
|
|
425
|
+
if not community:
|
|
426
|
+
return 0.0
|
|
427
|
+
|
|
428
|
+
def _get_node_attr(
|
|
429
|
+
node_id: Any, alias: tuple, fallback_key: str, default: float
|
|
430
|
+
) -> float:
|
|
431
|
+
"""Get node attribute via TNFR alias or direct access."""
|
|
432
|
+
return float(
|
|
433
|
+
get_attr(graph.nodes[node_id], alias, None)
|
|
434
|
+
or graph.nodes[node_id].get(fallback_key, default)
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
candidate_vf = _get_node_attr(candidate, ALIAS_VF, "vf", 1.0)
|
|
438
|
+
candidate_phase = _get_node_attr(candidate, ALIAS_THETA, "phase", 0.0)
|
|
439
|
+
|
|
440
|
+
coherences = []
|
|
441
|
+
for member in community:
|
|
442
|
+
member_vf = _get_node_attr(member, ALIAS_VF, "vf", 1.0)
|
|
443
|
+
member_phase = _get_node_attr(member, ALIAS_THETA, "phase", 0.0)
|
|
444
|
+
|
|
445
|
+
# Frequency coherence: inversely proportional to difference
|
|
446
|
+
vf_diff = abs(candidate_vf - member_vf)
|
|
447
|
+
vf_coherence = 1.0 / (1.0 + vf_diff)
|
|
448
|
+
|
|
449
|
+
# Phase coherence: cosine of phase difference
|
|
450
|
+
phase_diff = candidate_phase - member_phase
|
|
451
|
+
if HAS_NUMPY:
|
|
452
|
+
phase_coherence = float(np.cos(phase_diff))
|
|
453
|
+
else:
|
|
454
|
+
phase_coherence = math.cos(phase_diff)
|
|
455
|
+
|
|
456
|
+
# Weighted combination: prioritize frequency alignment
|
|
457
|
+
coherences.append(0.6 * vf_coherence + 0.4 * phase_coherence)
|
|
458
|
+
|
|
459
|
+
return sum(coherences) / len(coherences) if coherences else 0.0
|
tnfr/py.typed
ADDED
|
File without changes
|
tnfr/recipes/__init__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""TNFR Pattern Cookbook - Validated recipes organized by domain.
|
|
2
|
+
|
|
3
|
+
This module provides programmatic access to the TNFR Pattern Cookbook,
|
|
4
|
+
a comprehensive library of pre-validated operator sequences with health
|
|
5
|
+
metrics, use cases, and domain-specific context.
|
|
6
|
+
|
|
7
|
+
Examples
|
|
8
|
+
--------
|
|
9
|
+
>>> from tnfr.recipes import TNFRCookbook
|
|
10
|
+
>>> cookbook = TNFRCookbook()
|
|
11
|
+
>>> recipe = cookbook.get_recipe("therapeutic", "crisis_intervention")
|
|
12
|
+
>>> print(f"{recipe.name}: Health {recipe.health_metrics.overall_health:.3f}")
|
|
13
|
+
Crisis Intervention: Health 0.786
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from .cookbook import TNFRCookbook, CookbookRecipe, RecipeVariation
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"TNFRCookbook",
|
|
20
|
+
"CookbookRecipe",
|
|
21
|
+
"RecipeVariation",
|
|
22
|
+
]
|