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,290 @@
|
|
|
1
|
+
"""Input validation utilities for TNFR structural data.
|
|
2
|
+
|
|
3
|
+
This module provides validation functions for TNFR-specific data types
|
|
4
|
+
to ensure structural coherence when persisting or querying data.
|
|
5
|
+
|
|
6
|
+
TNFR Structural Invariants
|
|
7
|
+
---------------------------
|
|
8
|
+
These validators enforce TNFR canonical invariants:
|
|
9
|
+
1. Structural frequency (νf) in Hz_str units
|
|
10
|
+
2. Phase (φ) in valid range [0, 2π]
|
|
11
|
+
3. Coherence C(t) as non-negative value
|
|
12
|
+
4. Sense index Si validation
|
|
13
|
+
|
|
14
|
+
Example
|
|
15
|
+
-------
|
|
16
|
+
>>> validate_structural_frequency(0.5) # Valid
|
|
17
|
+
0.5
|
|
18
|
+
>>> validate_phase_value(3.14) # Valid
|
|
19
|
+
3.14
|
|
20
|
+
>>> validate_structural_frequency(-1.0) # doctest: +SKIP
|
|
21
|
+
Traceback (most recent call last):
|
|
22
|
+
...
|
|
23
|
+
ValueError: Structural frequency must be non-negative
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import math
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def validate_structural_frequency(nu_f: float) -> float:
|
|
33
|
+
"""Validate structural frequency (νf) value.
|
|
34
|
+
|
|
35
|
+
Structural frequency must be non-negative and expressed in Hz_str
|
|
36
|
+
(structural hertz), representing the nodal reorganization rate.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
nu_f : float
|
|
41
|
+
Structural frequency value to validate
|
|
42
|
+
|
|
43
|
+
Returns
|
|
44
|
+
-------
|
|
45
|
+
float
|
|
46
|
+
The validated frequency value
|
|
47
|
+
|
|
48
|
+
Raises
|
|
49
|
+
------
|
|
50
|
+
ValueError
|
|
51
|
+
If the frequency is negative, NaN, or infinite
|
|
52
|
+
|
|
53
|
+
Example
|
|
54
|
+
-------
|
|
55
|
+
>>> validate_structural_frequency(0.5)
|
|
56
|
+
0.5
|
|
57
|
+
>>> validate_structural_frequency(0.0) # Silence operator
|
|
58
|
+
0.0
|
|
59
|
+
>>> validate_structural_frequency(-0.1) # doctest: +SKIP
|
|
60
|
+
Traceback (most recent call last):
|
|
61
|
+
...
|
|
62
|
+
ValueError: Structural frequency must be non-negative, got -0.1
|
|
63
|
+
"""
|
|
64
|
+
if not isinstance(nu_f, (int, float)):
|
|
65
|
+
raise ValueError(
|
|
66
|
+
f"Structural frequency must be numeric, got {type(nu_f).__name__}"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
if math.isnan(nu_f):
|
|
70
|
+
raise ValueError("Structural frequency cannot be NaN")
|
|
71
|
+
|
|
72
|
+
if math.isinf(nu_f):
|
|
73
|
+
raise ValueError("Structural frequency cannot be infinite")
|
|
74
|
+
|
|
75
|
+
if nu_f < 0:
|
|
76
|
+
raise ValueError(f"Structural frequency must be non-negative, got {nu_f}")
|
|
77
|
+
|
|
78
|
+
return float(nu_f)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def validate_phase_value(phase: float, *, allow_wrap: bool = True) -> float:
|
|
82
|
+
"""Validate phase (φ) value.
|
|
83
|
+
|
|
84
|
+
Phase represents synchronization in the network and should be in the
|
|
85
|
+
range [0, 2π]. If allow_wrap is True, values outside this range are
|
|
86
|
+
automatically wrapped.
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
phase : float
|
|
91
|
+
Phase value to validate
|
|
92
|
+
allow_wrap : bool, optional
|
|
93
|
+
If True, wrap phase to [0, 2π] range (default: True)
|
|
94
|
+
|
|
95
|
+
Returns
|
|
96
|
+
-------
|
|
97
|
+
float
|
|
98
|
+
The validated (and possibly wrapped) phase value
|
|
99
|
+
|
|
100
|
+
Raises
|
|
101
|
+
------
|
|
102
|
+
ValueError
|
|
103
|
+
If phase is NaN, infinite, or outside valid range (when allow_wrap=False)
|
|
104
|
+
|
|
105
|
+
Example
|
|
106
|
+
-------
|
|
107
|
+
>>> validate_phase_value(1.57) # π/2
|
|
108
|
+
1.57
|
|
109
|
+
>>> result = validate_phase_value(7.0) # Wrapped to [0, 2π]
|
|
110
|
+
>>> 0.0 <= result <= 6.3
|
|
111
|
+
True
|
|
112
|
+
>>> validate_phase_value(7.0, allow_wrap=False) # doctest: +SKIP
|
|
113
|
+
Traceback (most recent call last):
|
|
114
|
+
...
|
|
115
|
+
ValueError: Phase must be in range [0, 2π], got 7.0
|
|
116
|
+
"""
|
|
117
|
+
if not isinstance(phase, (int, float)):
|
|
118
|
+
raise ValueError(f"Phase must be numeric, got {type(phase).__name__}")
|
|
119
|
+
|
|
120
|
+
if math.isnan(phase):
|
|
121
|
+
raise ValueError("Phase cannot be NaN")
|
|
122
|
+
|
|
123
|
+
if math.isinf(phase):
|
|
124
|
+
raise ValueError("Phase cannot be infinite")
|
|
125
|
+
|
|
126
|
+
two_pi = 2 * math.pi
|
|
127
|
+
|
|
128
|
+
if allow_wrap:
|
|
129
|
+
# Wrap phase to [0, 2π] range
|
|
130
|
+
phase = phase % two_pi
|
|
131
|
+
else:
|
|
132
|
+
if not 0 <= phase <= two_pi:
|
|
133
|
+
raise ValueError(f"Phase must be in range [0, 2π], got {phase}")
|
|
134
|
+
|
|
135
|
+
return float(phase)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def validate_coherence_value(coherence: float) -> float:
|
|
139
|
+
"""Validate coherence C(t) value.
|
|
140
|
+
|
|
141
|
+
Coherence represents the total structural stability and must be
|
|
142
|
+
non-negative.
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
coherence : float
|
|
147
|
+
Coherence value to validate
|
|
148
|
+
|
|
149
|
+
Returns
|
|
150
|
+
-------
|
|
151
|
+
float
|
|
152
|
+
The validated coherence value
|
|
153
|
+
|
|
154
|
+
Raises
|
|
155
|
+
------
|
|
156
|
+
ValueError
|
|
157
|
+
If coherence is negative, NaN, or infinite
|
|
158
|
+
|
|
159
|
+
Example
|
|
160
|
+
-------
|
|
161
|
+
>>> validate_coherence_value(0.8)
|
|
162
|
+
0.8
|
|
163
|
+
>>> validate_coherence_value(0.0) # Minimum coherence
|
|
164
|
+
0.0
|
|
165
|
+
>>> validate_coherence_value(-0.1) # doctest: +SKIP
|
|
166
|
+
Traceback (most recent call last):
|
|
167
|
+
...
|
|
168
|
+
ValueError: Coherence must be non-negative, got -0.1
|
|
169
|
+
"""
|
|
170
|
+
if not isinstance(coherence, (int, float)):
|
|
171
|
+
raise ValueError(f"Coherence must be numeric, got {type(coherence).__name__}")
|
|
172
|
+
|
|
173
|
+
if math.isnan(coherence):
|
|
174
|
+
raise ValueError("Coherence cannot be NaN")
|
|
175
|
+
|
|
176
|
+
if math.isinf(coherence):
|
|
177
|
+
raise ValueError("Coherence cannot be infinite")
|
|
178
|
+
|
|
179
|
+
if coherence < 0:
|
|
180
|
+
raise ValueError(f"Coherence must be non-negative, got {coherence}")
|
|
181
|
+
|
|
182
|
+
return float(coherence)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def validate_sense_index(si: float) -> float:
|
|
186
|
+
"""Validate sense index (Si) value.
|
|
187
|
+
|
|
188
|
+
Sense index represents the capacity to generate stable reorganization.
|
|
189
|
+
Valid range is typically [0, 1] but can exceed 1 in high-coherence networks.
|
|
190
|
+
|
|
191
|
+
Parameters
|
|
192
|
+
----------
|
|
193
|
+
si : float
|
|
194
|
+
Sense index value to validate
|
|
195
|
+
|
|
196
|
+
Returns
|
|
197
|
+
-------
|
|
198
|
+
float
|
|
199
|
+
The validated sense index value
|
|
200
|
+
|
|
201
|
+
Raises
|
|
202
|
+
------
|
|
203
|
+
ValueError
|
|
204
|
+
If Si is negative, NaN, or infinite
|
|
205
|
+
|
|
206
|
+
Example
|
|
207
|
+
-------
|
|
208
|
+
>>> validate_sense_index(0.7)
|
|
209
|
+
0.7
|
|
210
|
+
>>> validate_sense_index(1.2) # High coherence
|
|
211
|
+
1.2
|
|
212
|
+
>>> validate_sense_index(-0.1) # doctest: +SKIP
|
|
213
|
+
Traceback (most recent call last):
|
|
214
|
+
...
|
|
215
|
+
ValueError: Sense index must be non-negative, got -0.1
|
|
216
|
+
"""
|
|
217
|
+
if not isinstance(si, (int, float)):
|
|
218
|
+
raise ValueError(f"Sense index must be numeric, got {type(si).__name__}")
|
|
219
|
+
|
|
220
|
+
if math.isnan(si):
|
|
221
|
+
raise ValueError("Sense index cannot be NaN")
|
|
222
|
+
|
|
223
|
+
if math.isinf(si):
|
|
224
|
+
raise ValueError("Sense index cannot be infinite")
|
|
225
|
+
|
|
226
|
+
if si < 0:
|
|
227
|
+
raise ValueError(f"Sense index must be non-negative, got {si}")
|
|
228
|
+
|
|
229
|
+
return float(si)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def validate_nodal_input(data: dict[str, Any]) -> dict[str, Any]:
|
|
233
|
+
"""Validate a complete nodal data structure.
|
|
234
|
+
|
|
235
|
+
This function validates all common NFR node attributes to ensure
|
|
236
|
+
structural coherence before database persistence.
|
|
237
|
+
|
|
238
|
+
Parameters
|
|
239
|
+
----------
|
|
240
|
+
data : dict
|
|
241
|
+
Dictionary containing nodal attributes
|
|
242
|
+
|
|
243
|
+
Returns
|
|
244
|
+
-------
|
|
245
|
+
dict
|
|
246
|
+
The validated data dictionary
|
|
247
|
+
|
|
248
|
+
Raises
|
|
249
|
+
------
|
|
250
|
+
ValueError
|
|
251
|
+
If any attribute fails validation
|
|
252
|
+
|
|
253
|
+
Example
|
|
254
|
+
-------
|
|
255
|
+
>>> data = {"nu_f": 0.5, "phase": 1.57, "delta_nfr": 0.1}
|
|
256
|
+
>>> validated = validate_nodal_input(data)
|
|
257
|
+
>>> validated["nu_f"]
|
|
258
|
+
0.5
|
|
259
|
+
"""
|
|
260
|
+
validated = {}
|
|
261
|
+
|
|
262
|
+
if "nu_f" in data:
|
|
263
|
+
validated["nu_f"] = validate_structural_frequency(data["nu_f"])
|
|
264
|
+
|
|
265
|
+
if "phase" in data:
|
|
266
|
+
validated["phase"] = validate_phase_value(data["phase"])
|
|
267
|
+
|
|
268
|
+
if "coherence" in data:
|
|
269
|
+
validated["coherence"] = validate_coherence_value(data["coherence"])
|
|
270
|
+
|
|
271
|
+
if "si" in data or "sense_index" in data:
|
|
272
|
+
si_key = "si" if "si" in data else "sense_index"
|
|
273
|
+
validated[si_key] = validate_sense_index(data[si_key])
|
|
274
|
+
|
|
275
|
+
# Pass through other fields without validation
|
|
276
|
+
# (e.g., node_id, epi arrays, etc.)
|
|
277
|
+
for key, value in data.items():
|
|
278
|
+
if key not in validated:
|
|
279
|
+
validated[key] = value
|
|
280
|
+
|
|
281
|
+
return validated
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
__all__ = (
|
|
285
|
+
"validate_coherence_value",
|
|
286
|
+
"validate_nodal_input",
|
|
287
|
+
"validate_phase_value",
|
|
288
|
+
"validate_sense_index",
|
|
289
|
+
"validate_structural_frequency",
|
|
290
|
+
)
|
tnfr/selector.py
CHANGED
|
@@ -1,39 +1,47 @@
|
|
|
1
|
-
"""Utilities to select
|
|
1
|
+
"""Utilities to select structural operator symbols based on structural metrics.
|
|
2
2
|
|
|
3
3
|
This module normalises thresholds, computes selection scores and applies
|
|
4
|
-
hysteresis when assigning glyphs to nodes.
|
|
4
|
+
hysteresis when assigning structural operator symbols (glyphs) to nodes.
|
|
5
|
+
|
|
6
|
+
Each structural operator (Emission, Reception, Coherence, etc.) is represented
|
|
7
|
+
by a glyph symbol (AL, EN, IL, etc.) that this module selects based on the
|
|
8
|
+
node's current structural state.
|
|
5
9
|
"""
|
|
6
10
|
|
|
7
11
|
from __future__ import annotations
|
|
8
12
|
|
|
9
13
|
import threading
|
|
10
14
|
from operator import itemgetter
|
|
11
|
-
from typing import Any, Mapping,
|
|
15
|
+
from typing import TYPE_CHECKING, Any, Mapping, cast
|
|
12
16
|
from weakref import WeakKeyDictionary
|
|
13
17
|
|
|
14
18
|
if TYPE_CHECKING: # pragma: no cover
|
|
15
|
-
import networkx as nx
|
|
19
|
+
import networkx as nx
|
|
16
20
|
|
|
17
21
|
from .constants import DEFAULTS
|
|
18
|
-
from .
|
|
19
|
-
from .
|
|
22
|
+
from .config.defaults_core import SELECTOR_THRESHOLD_DEFAULTS
|
|
23
|
+
from .utils import clamp01
|
|
20
24
|
from .metrics.common import compute_dnfr_accel_max
|
|
21
|
-
from .
|
|
25
|
+
from .types import SelectorNorms, SelectorThresholds, SelectorWeights
|
|
26
|
+
from .utils import is_non_string_sequence
|
|
22
27
|
|
|
28
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
29
|
+
from .types import TNFRGraph
|
|
23
30
|
|
|
24
31
|
HYSTERESIS_GLYPHS: set[str] = {"IL", "OZ", "ZHIR", "THOL", "NAV", "RA"}
|
|
25
32
|
|
|
26
33
|
__all__ = (
|
|
27
34
|
"_selector_thresholds",
|
|
28
|
-
"
|
|
35
|
+
"_selector_norms",
|
|
29
36
|
"_calc_selector_score",
|
|
30
37
|
"_apply_selector_hysteresis",
|
|
38
|
+
"_selector_parallel_jobs",
|
|
31
39
|
)
|
|
32
40
|
|
|
33
|
-
|
|
41
|
+
_SelectorThresholdItems = tuple[tuple[str, float], ...]
|
|
34
42
|
_SelectorThresholdCacheEntry = tuple[
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
_SelectorThresholdItems,
|
|
44
|
+
SelectorThresholds,
|
|
37
45
|
]
|
|
38
46
|
_SELECTOR_THRESHOLD_CACHE: WeakKeyDictionary[
|
|
39
47
|
"nx.Graph",
|
|
@@ -42,7 +50,7 @@ _SELECTOR_THRESHOLD_CACHE: WeakKeyDictionary[
|
|
|
42
50
|
_SELECTOR_THRESHOLD_CACHE_LOCK = threading.Lock()
|
|
43
51
|
|
|
44
52
|
|
|
45
|
-
def _sorted_items(mapping: Mapping[str, float]) ->
|
|
53
|
+
def _sorted_items(mapping: Mapping[str, float]) -> _SelectorThresholdItems:
|
|
46
54
|
"""Return mapping items sorted by key.
|
|
47
55
|
|
|
48
56
|
Parameters
|
|
@@ -59,8 +67,8 @@ def _sorted_items(mapping: Mapping[str, float]) -> tuple[tuple[str, float], ...]
|
|
|
59
67
|
|
|
60
68
|
|
|
61
69
|
def _compute_selector_thresholds(
|
|
62
|
-
thr_sel_items:
|
|
63
|
-
) ->
|
|
70
|
+
thr_sel_items: _SelectorThresholdItems,
|
|
71
|
+
) -> SelectorThresholds:
|
|
64
72
|
"""Construct selector thresholds for a graph.
|
|
65
73
|
|
|
66
74
|
Parameters
|
|
@@ -79,10 +87,10 @@ def _compute_selector_thresholds(
|
|
|
79
87
|
for key, default in SELECTOR_THRESHOLD_DEFAULTS.items():
|
|
80
88
|
val = thr_sel.get(key, default)
|
|
81
89
|
out[key] = clamp01(float(val))
|
|
82
|
-
return out
|
|
90
|
+
return cast(SelectorThresholds, out)
|
|
83
91
|
|
|
84
92
|
|
|
85
|
-
def _selector_thresholds(G: "nx.Graph") ->
|
|
93
|
+
def _selector_thresholds(G: "nx.Graph") -> SelectorThresholds:
|
|
86
94
|
"""Return normalised thresholds for Si, ΔNFR and acceleration.
|
|
87
95
|
|
|
88
96
|
Parameters
|
|
@@ -114,8 +122,8 @@ def _selector_thresholds(G: "nx.Graph") -> dict[str, float]:
|
|
|
114
122
|
return thresholds
|
|
115
123
|
|
|
116
124
|
|
|
117
|
-
def
|
|
118
|
-
"""Compute and cache norms for ΔNFR and acceleration.
|
|
125
|
+
def _selector_norms(G: "nx.Graph") -> SelectorNorms:
|
|
126
|
+
"""Compute and cache selector norms for ΔNFR and acceleration.
|
|
119
127
|
|
|
120
128
|
Parameters
|
|
121
129
|
----------
|
|
@@ -134,7 +142,7 @@ def _norms_para_selector(G: "nx.Graph") -> dict:
|
|
|
134
142
|
|
|
135
143
|
|
|
136
144
|
def _calc_selector_score(
|
|
137
|
-
Si: float, dnfr: float, accel: float, weights:
|
|
145
|
+
Si: float, dnfr: float, accel: float, weights: SelectorWeights
|
|
138
146
|
) -> float:
|
|
139
147
|
"""Compute weighted selector score.
|
|
140
148
|
|
|
@@ -167,7 +175,7 @@ def _apply_selector_hysteresis(
|
|
|
167
175
|
dnfr: float,
|
|
168
176
|
accel: float,
|
|
169
177
|
thr: dict[str, float],
|
|
170
|
-
margin: float,
|
|
178
|
+
margin: float | None,
|
|
171
179
|
) -> str | None:
|
|
172
180
|
"""Apply hysteresis when values are near thresholds.
|
|
173
181
|
|
|
@@ -183,8 +191,10 @@ def _apply_selector_hysteresis(
|
|
|
183
191
|
Normalised acceleration.
|
|
184
192
|
thr : dict[str, float]
|
|
185
193
|
Thresholds returned by :func:`_selector_thresholds`.
|
|
186
|
-
margin : float
|
|
187
|
-
|
|
194
|
+
margin : float or None
|
|
195
|
+
When positive, distance from thresholds below which the previous
|
|
196
|
+
glyph is reused. Falsy margins disable hysteresis entirely, letting
|
|
197
|
+
selectors bypass the reuse logic.
|
|
188
198
|
|
|
189
199
|
Returns
|
|
190
200
|
-------
|
|
@@ -192,6 +202,9 @@ def _apply_selector_hysteresis(
|
|
|
192
202
|
Previous glyph if hysteresis applies, otherwise ``None``.
|
|
193
203
|
"""
|
|
194
204
|
# Batch extraction reduces dictionary lookups inside loops.
|
|
205
|
+
if not margin:
|
|
206
|
+
return None
|
|
207
|
+
|
|
195
208
|
si_hi, si_lo, dnfr_hi, dnfr_lo, accel_hi, accel_lo = itemgetter(
|
|
196
209
|
"si_hi", "si_lo", "dnfr_hi", "dnfr_lo", "accel_hi", "accel_lo"
|
|
197
210
|
)(thr)
|
|
@@ -208,3 +221,27 @@ def _apply_selector_hysteresis(
|
|
|
208
221
|
if isinstance(prev, str) and prev in HYSTERESIS_GLYPHS:
|
|
209
222
|
return prev
|
|
210
223
|
return None
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _selector_parallel_jobs(G: "TNFRGraph") -> int | None:
|
|
227
|
+
"""Return worker count for selector helpers when parallelism is enabled.
|
|
228
|
+
|
|
229
|
+
Parameters
|
|
230
|
+
----------
|
|
231
|
+
G : TNFRGraph
|
|
232
|
+
Graph containing selector configuration.
|
|
233
|
+
|
|
234
|
+
Returns
|
|
235
|
+
-------
|
|
236
|
+
int | None
|
|
237
|
+
Number of parallel jobs to use, or None if parallelism is disabled
|
|
238
|
+
or invalid configuration is provided.
|
|
239
|
+
"""
|
|
240
|
+
raw_jobs = G.graph.get("GLYPH_SELECTOR_N_JOBS")
|
|
241
|
+
try:
|
|
242
|
+
n_jobs = None if raw_jobs is None else int(raw_jobs)
|
|
243
|
+
except (TypeError, ValueError):
|
|
244
|
+
return None
|
|
245
|
+
if n_jobs is None or n_jobs <= 1:
|
|
246
|
+
return None
|
|
247
|
+
return n_jobs
|
tnfr/selector.pyi
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Mapping
|
|
4
|
+
|
|
5
|
+
__all__: Any
|
|
6
|
+
|
|
7
|
+
def __getattr__(name: str) -> Any: ...
|
|
8
|
+
def _apply_selector_hysteresis(
|
|
9
|
+
nd: dict[str, Any],
|
|
10
|
+
Si: float,
|
|
11
|
+
dnfr: float,
|
|
12
|
+
accel: float,
|
|
13
|
+
thr: Mapping[str, float],
|
|
14
|
+
margin: float | None,
|
|
15
|
+
) -> str | None: ...
|
|
16
|
+
|
|
17
|
+
_calc_selector_score: Any
|
|
18
|
+
_selector_norms: Any
|
|
19
|
+
_selector_thresholds: Any
|