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,212 @@
|
|
|
1
|
+
"""Core constants."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import asdict, field
|
|
6
|
+
from types import MappingProxyType
|
|
7
|
+
from typing import Any, Mapping
|
|
8
|
+
|
|
9
|
+
from ..compat.dataclass import dataclass
|
|
10
|
+
|
|
11
|
+
SELECTOR_THRESHOLD_DEFAULTS: Mapping[str, float] = MappingProxyType(
|
|
12
|
+
{
|
|
13
|
+
"si_hi": 0.66,
|
|
14
|
+
"si_lo": 0.33,
|
|
15
|
+
"dnfr_hi": 0.50,
|
|
16
|
+
"dnfr_lo": 0.10,
|
|
17
|
+
"accel_hi": 0.50,
|
|
18
|
+
"accel_lo": 0.10,
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True, slots=True)
|
|
24
|
+
class CoreDefaults:
|
|
25
|
+
"""Default parameters for the core engine.
|
|
26
|
+
|
|
27
|
+
The fields are exported via :data:`CORE_DEFAULTS` and may therefore appear
|
|
28
|
+
unused to static analysis tools such as Vulture.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
DT: float = 1.0
|
|
32
|
+
INTEGRATOR_METHOD: str = "euler"
|
|
33
|
+
DT_MIN: float = 0.1
|
|
34
|
+
EPI_MIN: float = -1.0
|
|
35
|
+
EPI_MAX: float = 1.0
|
|
36
|
+
VF_MIN: float = 0.0
|
|
37
|
+
VF_MAX: float = 10.0
|
|
38
|
+
THETA_WRAP: bool = True
|
|
39
|
+
CLIP_MODE: str = "hard"
|
|
40
|
+
CLIP_SOFT_K: float = 3.0
|
|
41
|
+
DNFR_WEIGHTS: dict[str, float] = field(
|
|
42
|
+
default_factory=lambda: {
|
|
43
|
+
"phase": 0.34,
|
|
44
|
+
"epi": 0.33,
|
|
45
|
+
"vf": 0.33,
|
|
46
|
+
"topo": 0.0,
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
SI_WEIGHTS: dict[str, float] = field(
|
|
50
|
+
default_factory=lambda: {"alpha": 0.34, "beta": 0.33, "gamma": 0.33}
|
|
51
|
+
)
|
|
52
|
+
PHASE_K_GLOBAL: float = 0.05
|
|
53
|
+
PHASE_K_LOCAL: float = 0.15
|
|
54
|
+
PHASE_ADAPT: dict[str, Any] = field(
|
|
55
|
+
default_factory=lambda: {
|
|
56
|
+
"enabled": True,
|
|
57
|
+
"R_hi": 0.90,
|
|
58
|
+
"R_lo": 0.60,
|
|
59
|
+
"disr_hi": 0.50,
|
|
60
|
+
"disr_lo": 0.25,
|
|
61
|
+
"kG_min": 0.01,
|
|
62
|
+
"kG_max": 0.20,
|
|
63
|
+
"kL_min": 0.05,
|
|
64
|
+
"kL_max": 0.25,
|
|
65
|
+
"up": 0.10,
|
|
66
|
+
"down": 0.07,
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
UM_COMPAT_THRESHOLD: float = 0.75
|
|
70
|
+
UM_CANDIDATE_MODE: str = "sample"
|
|
71
|
+
UM_CANDIDATE_COUNT: int = 0
|
|
72
|
+
GLYPH_HYSTERESIS_WINDOW: int = 7
|
|
73
|
+
AL_MAX_LAG: int = 5
|
|
74
|
+
EN_MAX_LAG: int = 3
|
|
75
|
+
GLYPH_SELECTOR_MARGIN: float = 0.05
|
|
76
|
+
VF_ADAPT_TAU: int = 5
|
|
77
|
+
VF_ADAPT_MU: float = 0.1
|
|
78
|
+
HZ_STR_BRIDGE: float = 1.0
|
|
79
|
+
GLYPH_FACTORS: dict[str, float] = field(
|
|
80
|
+
default_factory=lambda: {
|
|
81
|
+
"AL_boost": 0.05,
|
|
82
|
+
"EN_mix": 0.25,
|
|
83
|
+
"IL_dnfr_factor": 0.7,
|
|
84
|
+
"OZ_dnfr_factor": 1.3,
|
|
85
|
+
"UM_theta_push": 0.25,
|
|
86
|
+
"UM_vf_sync": 0.10,
|
|
87
|
+
"UM_dnfr_reduction": 0.15,
|
|
88
|
+
"RA_epi_diff": 0.15,
|
|
89
|
+
"RA_vf_amplification": 0.05,
|
|
90
|
+
"RA_phase_coupling": 0.10, # Canonical phase alignment strengthening
|
|
91
|
+
"SHA_vf_factor": 0.85,
|
|
92
|
+
# Conservative scaling (1.05) prevents EPI overflow near boundaries
|
|
93
|
+
# while maintaining meaningful expansion capacity. Critical threshold:
|
|
94
|
+
# EPI × 1.05 = 1.0 when EPI ≈ 0.952 (vs previous threshold ≈ 0.870).
|
|
95
|
+
# This preserves structural identity at boundary (EPI_MAX as identity frontier).
|
|
96
|
+
"VAL_scale": 1.05,
|
|
97
|
+
"NUL_scale": 0.85,
|
|
98
|
+
# NUL canonical ΔNFR densification factor: implements structural pressure
|
|
99
|
+
# concentration due to volume reduction. When V' = V × 0.85, density increases
|
|
100
|
+
# by ~1.176× geometrically. Canonical value 1.35 accounts for nonlinear
|
|
101
|
+
# structural effects at smaller scales, per TNFR theory.
|
|
102
|
+
"NUL_densification_factor": 1.35,
|
|
103
|
+
"THOL_accel": 0.10,
|
|
104
|
+
# ZHIR now uses canonical transformation by default (θ → θ' based on ΔNFR)
|
|
105
|
+
# To use fixed shift, explicitly set ZHIR_theta_shift in graph
|
|
106
|
+
"ZHIR_theta_shift_factor": 0.3, # Canonical transformation magnitude
|
|
107
|
+
"NAV_jitter": 0.05,
|
|
108
|
+
"NAV_eta": 0.5,
|
|
109
|
+
"REMESH_alpha": 0.5,
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
GLYPH_THRESHOLDS: dict[str, float] = field(
|
|
113
|
+
default_factory=lambda: {"hi": 0.66, "lo": 0.33, "dnfr": 1e-3}
|
|
114
|
+
)
|
|
115
|
+
NAV_RANDOM: bool = True
|
|
116
|
+
NAV_STRICT: bool = False
|
|
117
|
+
RANDOM_SEED: int = 0
|
|
118
|
+
JITTER_CACHE_SIZE: int = 256
|
|
119
|
+
OZ_NOISE_MODE: bool = False
|
|
120
|
+
OZ_SIGMA: float = 0.1
|
|
121
|
+
GRAMMAR: dict[str, Any] = field(
|
|
122
|
+
default_factory=lambda: {
|
|
123
|
+
"window": 3,
|
|
124
|
+
"avoid_repeats": ["ZHIR", "OZ", "THOL"],
|
|
125
|
+
"force_dnfr": 0.60,
|
|
126
|
+
"force_accel": 0.60,
|
|
127
|
+
"fallbacks": {"ZHIR": "NAV", "OZ": "ZHIR", "THOL": "NAV"},
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
SELECTOR_WEIGHTS: dict[str, float] = field(
|
|
131
|
+
default_factory=lambda: {"w_si": 0.5, "w_dnfr": 0.3, "w_accel": 0.2}
|
|
132
|
+
)
|
|
133
|
+
SELECTOR_THRESHOLDS: dict[str, float] = field(
|
|
134
|
+
default_factory=lambda: dict(SELECTOR_THRESHOLD_DEFAULTS)
|
|
135
|
+
)
|
|
136
|
+
GAMMA: dict[str, Any] = field(
|
|
137
|
+
default_factory=lambda: {"type": "none", "beta": 0.0, "R0": 0.0}
|
|
138
|
+
)
|
|
139
|
+
CALLBACKS_STRICT: bool = False
|
|
140
|
+
VALIDATORS_STRICT: bool = False
|
|
141
|
+
PROGRAM_TRACE_MAXLEN: int = 50
|
|
142
|
+
HISTORY_MAXLEN: int = 0
|
|
143
|
+
NODAL_EQUATION_CLIP_AWARE: bool = True
|
|
144
|
+
NODAL_EQUATION_TOLERANCE: float = 1e-9
|
|
145
|
+
# THOL (Self-organization) vibrational metabolism parameters
|
|
146
|
+
THOL_METABOLIC_ENABLED: bool = True
|
|
147
|
+
THOL_METABOLIC_GRADIENT_WEIGHT: float = 0.15
|
|
148
|
+
THOL_METABOLIC_COMPLEXITY_WEIGHT: float = 0.10
|
|
149
|
+
THOL_BIFURCATION_THRESHOLD: float = 0.1
|
|
150
|
+
|
|
151
|
+
# THOL network propagation and cascade parameters
|
|
152
|
+
THOL_PROPAGATION_ENABLED: bool = True
|
|
153
|
+
THOL_MIN_COUPLING_FOR_PROPAGATION: float = 0.5
|
|
154
|
+
THOL_PROPAGATION_ATTENUATION: float = 0.7
|
|
155
|
+
THOL_CASCADE_MIN_NODES: int = 3
|
|
156
|
+
|
|
157
|
+
# THOL precondition thresholds
|
|
158
|
+
THOL_MIN_EPI: float = 0.2 # Minimum EPI for bifurcation
|
|
159
|
+
THOL_MIN_VF: float = 0.1 # Minimum structural frequency for reorganization
|
|
160
|
+
THOL_MIN_DEGREE: int = 1 # Minimum network connectivity
|
|
161
|
+
THOL_MIN_HISTORY_LENGTH: int = 3 # Minimum EPI history for acceleration computation
|
|
162
|
+
THOL_ALLOW_ISOLATED: bool = False # Require network context by default
|
|
163
|
+
THOL_MIN_COLLECTIVE_COHERENCE: float = 0.3 # Minimum collective coherence for sub-EPI ensemble
|
|
164
|
+
|
|
165
|
+
# VAL (Expansion) precondition thresholds
|
|
166
|
+
VAL_MAX_VF: float = 10.0 # Maximum structural frequency threshold
|
|
167
|
+
VAL_MIN_DNFR: float = 1e-6 # Minimum positive ΔNFR for coherent expansion (very low to minimize breaking changes)
|
|
168
|
+
VAL_MIN_EPI: float = 0.2 # Minimum EPI for coherent expansion base
|
|
169
|
+
VAL_CHECK_NETWORK_CAPACITY: bool = False # Optional network capacity validation
|
|
170
|
+
VAL_MAX_NETWORK_SIZE: int = 1000 # Maximum network size if capacity checking enabled
|
|
171
|
+
|
|
172
|
+
# VAL (Expansion) metric thresholds (Issue #2724)
|
|
173
|
+
VAL_BIFURCATION_THRESHOLD: float = 0.3 # Threshold for |∂²EPI/∂t²| bifurcation detection
|
|
174
|
+
VAL_MIN_COHERENCE: float = 0.5 # Minimum local coherence for healthy expansion
|
|
175
|
+
VAL_FRACTAL_RATIO_MIN: float = 0.5 # Minimum vf_growth/epi_growth ratio for fractality
|
|
176
|
+
VAL_FRACTAL_RATIO_MAX: float = 2.0 # Maximum vf_growth/epi_growth ratio for fractality
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@dataclass(frozen=True, slots=True)
|
|
180
|
+
class RemeshDefaults:
|
|
181
|
+
"""Default parameters for the remeshing subsystem.
|
|
182
|
+
|
|
183
|
+
As with :class:`CoreDefaults`, the fields are exported via
|
|
184
|
+
:data:`REMESH_DEFAULTS` and may look unused to static analysers.
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
EPS_DNFR_STABLE: float = 1e-3
|
|
188
|
+
EPS_DEPI_STABLE: float = 1e-3
|
|
189
|
+
FRACTION_STABLE_REMESH: float = 0.80
|
|
190
|
+
REMESH_COOLDOWN_WINDOW: int = 20
|
|
191
|
+
REMESH_COOLDOWN_TS: float = 0.0
|
|
192
|
+
REMESH_REQUIRE_STABILITY: bool = True
|
|
193
|
+
REMESH_STABILITY_WINDOW: int = 25
|
|
194
|
+
REMESH_MIN_PHASE_SYNC: float = 0.85
|
|
195
|
+
REMESH_MAX_GLYPH_DISR: float = 0.35
|
|
196
|
+
REMESH_MIN_SIGMA_MAG: float = 0.50
|
|
197
|
+
REMESH_MIN_KURAMOTO_R: float = 0.80
|
|
198
|
+
REMESH_MIN_SI_HI_FRAC: float = 0.50
|
|
199
|
+
REMESH_LOG_EVENTS: bool = True
|
|
200
|
+
REMESH_MODE: str = "knn"
|
|
201
|
+
REMESH_COMMUNITY_K: int = 2
|
|
202
|
+
REMESH_TAU_GLOBAL: int = 8
|
|
203
|
+
REMESH_TAU_LOCAL: int = 4
|
|
204
|
+
REMESH_ALPHA: float = 0.5
|
|
205
|
+
REMESH_ALPHA_HARD: bool = False
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
_core_defaults = asdict(CoreDefaults())
|
|
209
|
+
_remesh_defaults = asdict(RemeshDefaults())
|
|
210
|
+
|
|
211
|
+
CORE_DEFAULTS = MappingProxyType(_core_defaults)
|
|
212
|
+
REMESH_DEFAULTS = MappingProxyType(_remesh_defaults)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Initialization constants."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
from dataclasses import asdict
|
|
7
|
+
|
|
8
|
+
from ..compat.dataclass import dataclass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True, slots=True)
|
|
12
|
+
class InitDefaults:
|
|
13
|
+
"""Default parameters for node initialisation.
|
|
14
|
+
|
|
15
|
+
The fields are collected into :data:`INIT_DEFAULTS` and may therefore
|
|
16
|
+
appear unused to tools like Vulture.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
INIT_RANDOM_PHASE: bool = True
|
|
20
|
+
INIT_THETA_MIN: float = -math.pi
|
|
21
|
+
INIT_THETA_MAX: float = math.pi
|
|
22
|
+
INIT_VF_MODE: str = "uniform"
|
|
23
|
+
INIT_VF_MIN: float | None = None
|
|
24
|
+
INIT_VF_MAX: float | None = None
|
|
25
|
+
INIT_VF_MEAN: float = 0.5
|
|
26
|
+
INIT_VF_STD: float = 0.15
|
|
27
|
+
INIT_VF_CLAMP_TO_LIMITS: bool = True
|
|
28
|
+
INIT_SI_MIN: float = 0.4
|
|
29
|
+
INIT_SI_MAX: float = 0.7
|
|
30
|
+
INIT_EPI_VALUE: float = 0.0
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
INIT_DEFAULTS = asdict(InitDefaults())
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Metric constants."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import asdict, field
|
|
6
|
+
from types import MappingProxyType
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from ..compat.dataclass import dataclass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True, slots=True)
|
|
13
|
+
class MetricDefaults:
|
|
14
|
+
"""Default parameters for metric computation.
|
|
15
|
+
|
|
16
|
+
The fields are gathered into :data:`METRIC_DEFAULTS` and exposed through
|
|
17
|
+
read-only views below, so they may appear unused to static analysis tools.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
PHASE_HISTORY_MAXLEN: int = 50
|
|
21
|
+
STOP_EARLY: dict[str, Any] = field(
|
|
22
|
+
default_factory=lambda: {
|
|
23
|
+
"enabled": False,
|
|
24
|
+
"window": 25,
|
|
25
|
+
"fraction": 0.90,
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
SIGMA: dict[str, Any] = field(
|
|
29
|
+
default_factory=lambda: {
|
|
30
|
+
"enabled": True,
|
|
31
|
+
"weight": "Si", # "Si" | "EPI" | "1"
|
|
32
|
+
"smooth": 0.0, # EMA over the global vector (0=off)
|
|
33
|
+
"history_key": "sigma_global",
|
|
34
|
+
"per_node": False,
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
TRACE: dict[str, Any] = field(
|
|
38
|
+
default_factory=lambda: {
|
|
39
|
+
"enabled": True,
|
|
40
|
+
"verbosity": "debug",
|
|
41
|
+
"history_key": "trace_meta",
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
METRICS: dict[str, Any] = field(
|
|
45
|
+
default_factory=lambda: {
|
|
46
|
+
"enabled": True,
|
|
47
|
+
"save_by_node": True,
|
|
48
|
+
"normalize_series": False,
|
|
49
|
+
"n_jobs": 1,
|
|
50
|
+
"verbosity": "debug",
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
GRAMMAR_CANON: dict[str, Any] = field(
|
|
54
|
+
default_factory=lambda: {
|
|
55
|
+
"enabled": True,
|
|
56
|
+
"zhir_requires_oz_window": 3,
|
|
57
|
+
"zhir_dnfr_min": 0.05,
|
|
58
|
+
"thol_min_len": 2,
|
|
59
|
+
"thol_max_len": 6,
|
|
60
|
+
"thol_close_dnfr": 0.15,
|
|
61
|
+
"si_high": 0.66,
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
COHERENCE: dict[str, Any] = field(
|
|
65
|
+
default_factory=lambda: {
|
|
66
|
+
"enabled": True,
|
|
67
|
+
"scope": "neighbors",
|
|
68
|
+
"weights": {"phase": 0.34, "epi": 0.33, "vf": 0.20, "si": 0.13},
|
|
69
|
+
"self_on_diag": True,
|
|
70
|
+
"store_mode": "sparse",
|
|
71
|
+
"threshold": 0.0,
|
|
72
|
+
"n_jobs": 1,
|
|
73
|
+
"history_key": "W_sparse",
|
|
74
|
+
"Wi_history_key": "W_i",
|
|
75
|
+
"stats_history_key": "W_stats",
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
DIAGNOSIS: dict[str, Any] = field(
|
|
79
|
+
default_factory=lambda: {
|
|
80
|
+
"enabled": True,
|
|
81
|
+
"window": 16,
|
|
82
|
+
"history_key": "nodal_diag",
|
|
83
|
+
"stable": {"Rloc_hi": 0.80, "dnfr_lo": 0.20, "persist": 3},
|
|
84
|
+
"dissonance": {"Rloc_lo": 0.40, "dnfr_hi": 0.50, "persist": 3},
|
|
85
|
+
"transition": {"persist": 2},
|
|
86
|
+
"compute_symmetry": True,
|
|
87
|
+
"include_typology": False,
|
|
88
|
+
"advice": {
|
|
89
|
+
"stable": ["Coherence", "Coupling", "Resonance"],
|
|
90
|
+
"transition": ["Transition", "Resonance", "Self-organisation"],
|
|
91
|
+
"dissonant": ["Silence", "Contraction", "Mutation"],
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
METRIC_DEFAULTS = asdict(MetricDefaults())
|
|
98
|
+
|
|
99
|
+
SIGMA = MappingProxyType(METRIC_DEFAULTS["SIGMA"])
|
|
100
|
+
TRACE = MappingProxyType(METRIC_DEFAULTS["TRACE"])
|
|
101
|
+
METRICS = MappingProxyType(METRIC_DEFAULTS["METRICS"])
|
|
102
|
+
GRAMMAR_CANON = MappingProxyType(METRIC_DEFAULTS["GRAMMAR_CANON"])
|
|
103
|
+
COHERENCE = MappingProxyType(METRIC_DEFAULTS["COHERENCE"])
|
|
104
|
+
DIAGNOSIS = MappingProxyType(METRIC_DEFAULTS["DIAGNOSIS"])
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Math feature flag configuration helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from contextlib import contextmanager
|
|
7
|
+
from dataclasses import dataclass, replace
|
|
8
|
+
from typing import Iterator
|
|
9
|
+
|
|
10
|
+
__all__ = ("MathFeatureFlags", "get_flags", "context_flags")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class MathFeatureFlags:
|
|
15
|
+
"""Toggle optional mathematical behaviours in the engine."""
|
|
16
|
+
|
|
17
|
+
enable_math_validation: bool = False
|
|
18
|
+
enable_math_dynamics: bool = False
|
|
19
|
+
log_performance: bool = False
|
|
20
|
+
math_backend: str = "numpy"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
_TRUE_VALUES = {"1", "true", "on", "yes", "y", "t"}
|
|
24
|
+
_FALSE_VALUES = {"0", "false", "off", "no", "n", "f"}
|
|
25
|
+
|
|
26
|
+
_BASE_FLAGS: MathFeatureFlags | None = None
|
|
27
|
+
_FLAGS_STACK: list[MathFeatureFlags] = []
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _parse_env_flag(name: str, default: bool) -> bool:
|
|
31
|
+
value = os.getenv(name)
|
|
32
|
+
if value is None:
|
|
33
|
+
return default
|
|
34
|
+
lowered = value.strip().lower()
|
|
35
|
+
if lowered in _TRUE_VALUES:
|
|
36
|
+
return True
|
|
37
|
+
if lowered in _FALSE_VALUES:
|
|
38
|
+
return False
|
|
39
|
+
return default
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _load_base_flags() -> MathFeatureFlags:
|
|
43
|
+
global _BASE_FLAGS
|
|
44
|
+
if _BASE_FLAGS is None:
|
|
45
|
+
backend = os.getenv("TNFR_MATH_BACKEND")
|
|
46
|
+
backend_choice = backend.strip() if backend else "numpy"
|
|
47
|
+
_BASE_FLAGS = MathFeatureFlags(
|
|
48
|
+
enable_math_validation=_parse_env_flag(
|
|
49
|
+
"TNFR_ENABLE_MATH_VALIDATION", False
|
|
50
|
+
),
|
|
51
|
+
enable_math_dynamics=_parse_env_flag("TNFR_ENABLE_MATH_DYNAMICS", False),
|
|
52
|
+
log_performance=_parse_env_flag("TNFR_LOG_PERF", False),
|
|
53
|
+
math_backend=backend_choice or "numpy",
|
|
54
|
+
)
|
|
55
|
+
return _BASE_FLAGS
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_flags() -> MathFeatureFlags:
|
|
59
|
+
"""Return the currently active feature flags."""
|
|
60
|
+
|
|
61
|
+
if _FLAGS_STACK:
|
|
62
|
+
return _FLAGS_STACK[-1]
|
|
63
|
+
return _load_base_flags()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@contextmanager
|
|
67
|
+
def context_flags(**overrides: bool) -> Iterator[MathFeatureFlags]:
|
|
68
|
+
"""Temporarily override math feature flags."""
|
|
69
|
+
|
|
70
|
+
invalid = set(overrides) - set(MathFeatureFlags.__annotations__)
|
|
71
|
+
if invalid:
|
|
72
|
+
invalid_names = ", ".join(sorted(invalid))
|
|
73
|
+
raise TypeError(f"Unknown flag overrides: {invalid_names}")
|
|
74
|
+
|
|
75
|
+
previous = get_flags()
|
|
76
|
+
next_flags = replace(previous, **overrides)
|
|
77
|
+
_FLAGS_STACK.append(next_flags)
|
|
78
|
+
try:
|
|
79
|
+
yield next_flags
|
|
80
|
+
finally:
|
|
81
|
+
_FLAGS_STACK.pop()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from contextlib import contextmanager
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Iterator
|
|
4
|
+
|
|
5
|
+
__all__ = ["MathFeatureFlags", "get_flags", "context_flags"]
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class MathFeatureFlags:
|
|
9
|
+
enable_math_validation: bool = ...
|
|
10
|
+
enable_math_dynamics: bool = ...
|
|
11
|
+
log_performance: bool = ...
|
|
12
|
+
math_backend: str = ...
|
|
13
|
+
|
|
14
|
+
def get_flags() -> MathFeatureFlags: ...
|
|
15
|
+
@contextmanager
|
|
16
|
+
def context_flags(**overrides: bool) -> Iterator[MathFeatureFlags]: ...
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Shared alias constants for TNFR attributes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from . import get_aliases
|
|
6
|
+
|
|
7
|
+
ALIAS_VF = get_aliases("VF")
|
|
8
|
+
ALIAS_THETA = get_aliases("THETA")
|
|
9
|
+
ALIAS_DNFR = get_aliases("DNFR")
|
|
10
|
+
ALIAS_EPI = get_aliases("EPI")
|
|
11
|
+
ALIAS_EPI_KIND = get_aliases("EPI_KIND")
|
|
12
|
+
ALIAS_SI = get_aliases("SI")
|
|
13
|
+
ALIAS_DEPI = get_aliases("DEPI")
|
|
14
|
+
ALIAS_D2EPI = get_aliases("D2EPI")
|
|
15
|
+
ALIAS_DVF = get_aliases("DVF")
|
|
16
|
+
ALIAS_D2VF = get_aliases("D2VF")
|
|
17
|
+
ALIAS_DSI = get_aliases("DSI")
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"ALIAS_VF",
|
|
21
|
+
"ALIAS_THETA",
|
|
22
|
+
"ALIAS_DNFR",
|
|
23
|
+
"ALIAS_EPI",
|
|
24
|
+
"ALIAS_EPI_KIND",
|
|
25
|
+
"ALIAS_SI",
|
|
26
|
+
"ALIAS_DEPI",
|
|
27
|
+
"ALIAS_D2EPI",
|
|
28
|
+
"ALIAS_DVF",
|
|
29
|
+
"ALIAS_D2VF",
|
|
30
|
+
"ALIAS_DSI",
|
|
31
|
+
]
|
tnfr/config/init.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Core configuration helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
from ..utils import read_structured_file
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING: # pragma: no cover - only for type checkers
|
|
12
|
+
import networkx as nx
|
|
13
|
+
|
|
14
|
+
__all__ = ("load_config", "apply_config")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def load_config(
|
|
18
|
+
path: str | Path,
|
|
19
|
+
*,
|
|
20
|
+
base_dir: str | Path | None = None,
|
|
21
|
+
) -> Mapping[str, Any]:
|
|
22
|
+
"""Read a JSON/YAML file and return a mapping with parameters.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
path : str | Path
|
|
27
|
+
Path to the configuration file.
|
|
28
|
+
base_dir : str | Path | None, optional
|
|
29
|
+
Base directory to restrict config file access. If provided, the
|
|
30
|
+
resolved path must stay within this directory (prevents path traversal).
|
|
31
|
+
|
|
32
|
+
Returns
|
|
33
|
+
-------
|
|
34
|
+
Mapping[str, Any]
|
|
35
|
+
Configuration parameters as a mapping.
|
|
36
|
+
|
|
37
|
+
Raises
|
|
38
|
+
------
|
|
39
|
+
ValueError
|
|
40
|
+
If the configuration file is invalid or contains unsafe patterns.
|
|
41
|
+
PathTraversalError
|
|
42
|
+
If path traversal is detected when base_dir is provided.
|
|
43
|
+
StructuredFileError
|
|
44
|
+
If the file cannot be read or parsed.
|
|
45
|
+
"""
|
|
46
|
+
path_obj = path if isinstance(path, Path) else Path(path)
|
|
47
|
+
data = read_structured_file(path_obj, base_dir=base_dir)
|
|
48
|
+
if not isinstance(data, Mapping):
|
|
49
|
+
raise ValueError("Configuration file must contain an object")
|
|
50
|
+
return data
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def apply_config(
|
|
54
|
+
G: "nx.Graph",
|
|
55
|
+
path: str | Path,
|
|
56
|
+
*,
|
|
57
|
+
base_dir: str | Path | None = None,
|
|
58
|
+
) -> None:
|
|
59
|
+
"""Inject parameters from ``path`` into ``G.graph``.
|
|
60
|
+
|
|
61
|
+
Uses inject_defaults from this module to keep canonical default
|
|
62
|
+
semantics.
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
G : nx.Graph
|
|
67
|
+
The graph to configure.
|
|
68
|
+
path : str | Path
|
|
69
|
+
Path to the configuration file.
|
|
70
|
+
base_dir : str | Path | None, optional
|
|
71
|
+
Base directory to restrict config file access.
|
|
72
|
+
"""
|
|
73
|
+
# Import inject_defaults locally to avoid circular import
|
|
74
|
+
from . import inject_defaults
|
|
75
|
+
|
|
76
|
+
cfg = load_config(path, base_dir=base_dir)
|
|
77
|
+
inject_defaults(G, cfg, override=True)
|