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,755 @@
|
|
|
1
|
+
"""Input validation for TNFR structural operators.
|
|
2
|
+
|
|
3
|
+
This module provides security-focused input validation for structural operator
|
|
4
|
+
parameters to prevent injection attacks and ensure canonical TNFR invariants
|
|
5
|
+
are preserved. All validation functions respect TNFR structural semantics:
|
|
6
|
+
|
|
7
|
+
- EPI (Primary Information Structure) bounds
|
|
8
|
+
- νf (structural frequency) Hz_str units and ranges
|
|
9
|
+
- θ (phase) normalization to [-π, π]
|
|
10
|
+
- ΔNFR (reorganization operator) magnitude constraints
|
|
11
|
+
- Type safety for TNFRGraph, NodeId, and Glyph enumerations
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import math
|
|
17
|
+
import re
|
|
18
|
+
import warnings
|
|
19
|
+
from typing import Any, Mapping
|
|
20
|
+
|
|
21
|
+
import networkx as nx
|
|
22
|
+
|
|
23
|
+
from ..constants import DEFAULTS
|
|
24
|
+
from ..types import Glyph, TNFRGraph
|
|
25
|
+
|
|
26
|
+
# Check HzStr availability once at module level
|
|
27
|
+
_HZ_STR_AVAILABLE = False
|
|
28
|
+
try:
|
|
29
|
+
from ..operators.structural_units import HzStr
|
|
30
|
+
|
|
31
|
+
_HZ_STR_AVAILABLE = True
|
|
32
|
+
except ImportError:
|
|
33
|
+
HzStr = None # type: ignore[assignment, misc]
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"ValidationError",
|
|
37
|
+
"validate_epi_value",
|
|
38
|
+
"validate_vf_value",
|
|
39
|
+
"validate_theta_value",
|
|
40
|
+
"validate_dnfr_value",
|
|
41
|
+
"validate_node_id",
|
|
42
|
+
"validate_glyph",
|
|
43
|
+
"validate_tnfr_graph",
|
|
44
|
+
"validate_glyph_factors",
|
|
45
|
+
"validate_operator_parameters",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ValidationError(ValueError):
|
|
50
|
+
"""Raised when input validation fails for structural operators."""
|
|
51
|
+
|
|
52
|
+
__slots__ = ("parameter", "value", "constraint")
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
message: str,
|
|
57
|
+
*,
|
|
58
|
+
parameter: str | None = None,
|
|
59
|
+
value: Any = None,
|
|
60
|
+
constraint: str | None = None,
|
|
61
|
+
) -> None:
|
|
62
|
+
super().__init__(message)
|
|
63
|
+
self.parameter = parameter
|
|
64
|
+
self.value = value
|
|
65
|
+
self.constraint = constraint
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _get_bound(config: Mapping[str, Any] | None, key: str, default: float) -> float:
|
|
69
|
+
"""Retrieve a numeric bound from configuration or DEFAULTS.
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
config : Mapping[str, Any] | None
|
|
74
|
+
Graph configuration mapping (typically from G.graph).
|
|
75
|
+
key : str
|
|
76
|
+
Configuration key (e.g., "EPI_MIN", "VF_MAX").
|
|
77
|
+
default : float
|
|
78
|
+
Fallback value when key is not in config or DEFAULTS.
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
float
|
|
83
|
+
The configured bound or default value.
|
|
84
|
+
|
|
85
|
+
Notes
|
|
86
|
+
-----
|
|
87
|
+
DEFAULTS is a mapping defined in tnfr.constants containing canonical
|
|
88
|
+
TNFR parameter bounds and configuration values.
|
|
89
|
+
"""
|
|
90
|
+
if config is not None and key in config:
|
|
91
|
+
try:
|
|
92
|
+
return float(config[key])
|
|
93
|
+
except (TypeError, ValueError):
|
|
94
|
+
pass
|
|
95
|
+
return float(DEFAULTS.get(key, default))
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def validate_epi_value(
|
|
99
|
+
value: Any,
|
|
100
|
+
*,
|
|
101
|
+
config: Mapping[str, Any] | None = None,
|
|
102
|
+
allow_complex: bool = True,
|
|
103
|
+
) -> float | complex:
|
|
104
|
+
"""Validate EPI (Primary Information Structure) value.
|
|
105
|
+
|
|
106
|
+
EPI represents the coherent form of a node and must remain within
|
|
107
|
+
canonical bounds to preserve structural stability.
|
|
108
|
+
|
|
109
|
+
Parameters
|
|
110
|
+
----------
|
|
111
|
+
value : Any
|
|
112
|
+
EPI value to validate. Must be numeric (float or complex if allowed).
|
|
113
|
+
config : Mapping[str, Any] | None
|
|
114
|
+
Graph configuration containing EPI_MIN and EPI_MAX bounds.
|
|
115
|
+
allow_complex : bool
|
|
116
|
+
Whether to allow complex EPI values (default: True).
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
float or complex
|
|
121
|
+
Validated EPI value.
|
|
122
|
+
|
|
123
|
+
Raises
|
|
124
|
+
------
|
|
125
|
+
ValidationError
|
|
126
|
+
If value is not numeric, contains special floats (nan, inf),
|
|
127
|
+
or violates bounds.
|
|
128
|
+
|
|
129
|
+
Examples
|
|
130
|
+
--------
|
|
131
|
+
>>> validate_epi_value(0.5)
|
|
132
|
+
0.5
|
|
133
|
+
>>> validate_epi_value(1.5, config={"EPI_MAX": 1.0})
|
|
134
|
+
Traceback (most recent call last):
|
|
135
|
+
...
|
|
136
|
+
ValidationError: EPI value 1.5 exceeds maximum bound 1.0
|
|
137
|
+
"""
|
|
138
|
+
if not isinstance(value, (int, float, complex)):
|
|
139
|
+
raise ValidationError(
|
|
140
|
+
f"EPI must be numeric, got {type(value).__name__}",
|
|
141
|
+
parameter="EPI",
|
|
142
|
+
value=value,
|
|
143
|
+
constraint="numeric type",
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
if isinstance(value, complex):
|
|
147
|
+
if not allow_complex:
|
|
148
|
+
raise ValidationError(
|
|
149
|
+
"EPI must be real-valued in this context",
|
|
150
|
+
parameter="EPI",
|
|
151
|
+
value=value,
|
|
152
|
+
constraint="real-valued",
|
|
153
|
+
)
|
|
154
|
+
# Check both real and imaginary parts for special values
|
|
155
|
+
if not math.isfinite(value.real) or not math.isfinite(value.imag):
|
|
156
|
+
raise ValidationError(
|
|
157
|
+
f"EPI cannot contain nan or inf: {value}",
|
|
158
|
+
parameter="EPI",
|
|
159
|
+
value=value,
|
|
160
|
+
constraint="finite",
|
|
161
|
+
)
|
|
162
|
+
magnitude = abs(value)
|
|
163
|
+
else:
|
|
164
|
+
if not math.isfinite(float(value)):
|
|
165
|
+
raise ValidationError(
|
|
166
|
+
f"EPI cannot be nan or inf: {value}",
|
|
167
|
+
parameter="EPI",
|
|
168
|
+
value=value,
|
|
169
|
+
constraint="finite",
|
|
170
|
+
)
|
|
171
|
+
magnitude = abs(float(value))
|
|
172
|
+
|
|
173
|
+
epi_min = _get_bound(config, "EPI_MIN", 0.0)
|
|
174
|
+
epi_max = _get_bound(config, "EPI_MAX", 1.0)
|
|
175
|
+
|
|
176
|
+
if magnitude < epi_min:
|
|
177
|
+
raise ValidationError(
|
|
178
|
+
f"EPI magnitude {magnitude} below minimum bound {epi_min}",
|
|
179
|
+
parameter="EPI",
|
|
180
|
+
value=value,
|
|
181
|
+
constraint=f"magnitude >= {epi_min}",
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
if magnitude > epi_max:
|
|
185
|
+
raise ValidationError(
|
|
186
|
+
f"EPI magnitude {magnitude} exceeds maximum bound {epi_max}",
|
|
187
|
+
parameter="EPI",
|
|
188
|
+
value=value,
|
|
189
|
+
constraint=f"magnitude <= {epi_max}",
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
return value
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def validate_vf_value(
|
|
196
|
+
value: Any,
|
|
197
|
+
*,
|
|
198
|
+
config: Mapping[str, Any] | None = None,
|
|
199
|
+
enforce_hz_str: bool = False,
|
|
200
|
+
) -> float:
|
|
201
|
+
"""Validate νf (structural frequency) value in Hz_str units.
|
|
202
|
+
|
|
203
|
+
Structural frequency represents the reorganization rate and must be
|
|
204
|
+
positive and within canonical bounds. Optionally enforces Hz_str type.
|
|
205
|
+
|
|
206
|
+
Parameters
|
|
207
|
+
----------
|
|
208
|
+
value : Any
|
|
209
|
+
νf value to validate. Can be numeric or HzStr instance.
|
|
210
|
+
config : Mapping[str, Any] | None
|
|
211
|
+
Graph configuration containing VF_MIN and VF_MAX bounds.
|
|
212
|
+
enforce_hz_str : bool, default False
|
|
213
|
+
If True, warns when value is not a HzStr instance (encourages
|
|
214
|
+
canonical unit usage without breaking existing code).
|
|
215
|
+
|
|
216
|
+
Returns
|
|
217
|
+
-------
|
|
218
|
+
float
|
|
219
|
+
Validated νf value.
|
|
220
|
+
|
|
221
|
+
Raises
|
|
222
|
+
------
|
|
223
|
+
ValidationError
|
|
224
|
+
If value is not numeric, not finite, negative, or violates bounds.
|
|
225
|
+
|
|
226
|
+
Examples
|
|
227
|
+
--------
|
|
228
|
+
>>> validate_vf_value(1.0)
|
|
229
|
+
1.0
|
|
230
|
+
>>> validate_vf_value(-0.5)
|
|
231
|
+
Traceback (most recent call last):
|
|
232
|
+
...
|
|
233
|
+
ValidationError: νf must be non-negative, got -0.5
|
|
234
|
+
|
|
235
|
+
Notes
|
|
236
|
+
-----
|
|
237
|
+
When enforce_hz_str is True and value is not a HzStr instance, logs a
|
|
238
|
+
warning to encourage canonical unit usage but still accepts the value.
|
|
239
|
+
"""
|
|
240
|
+
# Accept HzStr instances (canonical form)
|
|
241
|
+
if _HZ_STR_AVAILABLE and HzStr is not None and isinstance(value, HzStr):
|
|
242
|
+
value = float(value) # Extract numeric value
|
|
243
|
+
elif enforce_hz_str and _HZ_STR_AVAILABLE:
|
|
244
|
+
# Log warning but don't raise (soft enforcement for migration)
|
|
245
|
+
warnings.warn(
|
|
246
|
+
f"νf should use Hz_str units for TNFR canonical compliance. "
|
|
247
|
+
f"Got {type(value).__name__} instead.",
|
|
248
|
+
UserWarning,
|
|
249
|
+
stacklevel=2,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
if not isinstance(value, (int, float)):
|
|
253
|
+
raise ValidationError(
|
|
254
|
+
f"νf must be numeric, got {type(value).__name__}",
|
|
255
|
+
parameter="vf",
|
|
256
|
+
value=value,
|
|
257
|
+
constraint="numeric type",
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
value = float(value)
|
|
261
|
+
|
|
262
|
+
if not math.isfinite(value):
|
|
263
|
+
raise ValidationError(
|
|
264
|
+
f"νf cannot be nan or inf: {value}",
|
|
265
|
+
parameter="vf",
|
|
266
|
+
value=value,
|
|
267
|
+
constraint="finite",
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
if value < 0:
|
|
271
|
+
raise ValidationError(
|
|
272
|
+
f"νf must be non-negative, got {value}",
|
|
273
|
+
parameter="vf",
|
|
274
|
+
value=value,
|
|
275
|
+
constraint=">= 0",
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
vf_min = _get_bound(config, "VF_MIN", 0.0)
|
|
279
|
+
vf_max = _get_bound(config, "VF_MAX", 1.0) # Match DEFAULTS["VF_MAX"]
|
|
280
|
+
|
|
281
|
+
if value < vf_min:
|
|
282
|
+
raise ValidationError(
|
|
283
|
+
f"νf value {value} below minimum bound {vf_min}",
|
|
284
|
+
parameter="vf",
|
|
285
|
+
value=value,
|
|
286
|
+
constraint=f">= {vf_min}",
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
if value > vf_max:
|
|
290
|
+
raise ValidationError(
|
|
291
|
+
f"νf value {value} exceeds maximum bound {vf_max}",
|
|
292
|
+
parameter="vf",
|
|
293
|
+
value=value,
|
|
294
|
+
constraint=f"<= {vf_max}",
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
return value
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def validate_theta_value(
|
|
301
|
+
value: Any,
|
|
302
|
+
*,
|
|
303
|
+
normalize: bool = True,
|
|
304
|
+
) -> float:
|
|
305
|
+
"""Validate θ (phase) value.
|
|
306
|
+
|
|
307
|
+
Phase represents synchrony with the network and is normalized to [-π, π].
|
|
308
|
+
|
|
309
|
+
Parameters
|
|
310
|
+
----------
|
|
311
|
+
value : Any
|
|
312
|
+
θ value to validate. Must be a real number.
|
|
313
|
+
normalize : bool
|
|
314
|
+
Whether to normalize phase to [-π, π] (default: True).
|
|
315
|
+
|
|
316
|
+
Returns
|
|
317
|
+
-------
|
|
318
|
+
float
|
|
319
|
+
Validated (and possibly normalized) θ value.
|
|
320
|
+
|
|
321
|
+
Raises
|
|
322
|
+
------
|
|
323
|
+
ValidationError
|
|
324
|
+
If value is not numeric or not finite.
|
|
325
|
+
|
|
326
|
+
Examples
|
|
327
|
+
--------
|
|
328
|
+
>>> validate_theta_value(math.pi / 2)
|
|
329
|
+
1.5707963267948966
|
|
330
|
+
>>> validate_theta_value(4 * math.pi, normalize=True)
|
|
331
|
+
0.0
|
|
332
|
+
"""
|
|
333
|
+
if not isinstance(value, (int, float)):
|
|
334
|
+
raise ValidationError(
|
|
335
|
+
f"θ must be numeric, got {type(value).__name__}",
|
|
336
|
+
parameter="theta",
|
|
337
|
+
value=value,
|
|
338
|
+
constraint="numeric type",
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
value = float(value)
|
|
342
|
+
|
|
343
|
+
if not math.isfinite(value):
|
|
344
|
+
raise ValidationError(
|
|
345
|
+
f"θ cannot be nan or inf: {value}",
|
|
346
|
+
parameter="theta",
|
|
347
|
+
value=value,
|
|
348
|
+
constraint="finite",
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
if normalize:
|
|
352
|
+
# Normalize to [-π, π]
|
|
353
|
+
value = (value + math.pi) % (2 * math.pi) - math.pi
|
|
354
|
+
|
|
355
|
+
return value
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def validate_dnfr_value(
|
|
359
|
+
value: Any,
|
|
360
|
+
*,
|
|
361
|
+
config: Mapping[str, Any] | None = None,
|
|
362
|
+
) -> float:
|
|
363
|
+
"""Validate ΔNFR (reorganization operator) magnitude.
|
|
364
|
+
|
|
365
|
+
ΔNFR represents the internal reorganization differential and should be
|
|
366
|
+
bounded to prevent excessive structural changes.
|
|
367
|
+
|
|
368
|
+
Parameters
|
|
369
|
+
----------
|
|
370
|
+
value : Any
|
|
371
|
+
ΔNFR value to validate. Must be a real number.
|
|
372
|
+
config : Mapping[str, Any] | None
|
|
373
|
+
Graph configuration containing DNFR_MAX bound.
|
|
374
|
+
|
|
375
|
+
Returns
|
|
376
|
+
-------
|
|
377
|
+
float
|
|
378
|
+
Validated ΔNFR value.
|
|
379
|
+
|
|
380
|
+
Raises
|
|
381
|
+
------
|
|
382
|
+
ValidationError
|
|
383
|
+
If value is not numeric, not finite, or exceeds bounds.
|
|
384
|
+
|
|
385
|
+
Examples
|
|
386
|
+
--------
|
|
387
|
+
>>> validate_dnfr_value(0.1)
|
|
388
|
+
0.1
|
|
389
|
+
>>> validate_dnfr_value(2.0, config={"DNFR_MAX": 1.0})
|
|
390
|
+
Traceback (most recent call last):
|
|
391
|
+
...
|
|
392
|
+
ValidationError: |ΔNFR| value 2.0 exceeds maximum bound 1.0
|
|
393
|
+
"""
|
|
394
|
+
if not isinstance(value, (int, float)):
|
|
395
|
+
raise ValidationError(
|
|
396
|
+
f"ΔNFR must be numeric, got {type(value).__name__}",
|
|
397
|
+
parameter="dnfr",
|
|
398
|
+
value=value,
|
|
399
|
+
constraint="numeric type",
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
value = float(value)
|
|
403
|
+
|
|
404
|
+
if not math.isfinite(value):
|
|
405
|
+
raise ValidationError(
|
|
406
|
+
f"ΔNFR cannot be nan or inf: {value}",
|
|
407
|
+
parameter="dnfr",
|
|
408
|
+
value=value,
|
|
409
|
+
constraint="finite",
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
dnfr_max = _get_bound(config, "DNFR_MAX", 1.0)
|
|
413
|
+
|
|
414
|
+
if abs(value) > dnfr_max:
|
|
415
|
+
raise ValidationError(
|
|
416
|
+
f"|ΔNFR| value {abs(value)} exceeds maximum bound {dnfr_max}",
|
|
417
|
+
parameter="dnfr",
|
|
418
|
+
value=value,
|
|
419
|
+
constraint=f"|ΔNFR| <= {dnfr_max}",
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
return value
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def validate_node_id(value: Any) -> Any:
|
|
426
|
+
"""Validate NodeId value.
|
|
427
|
+
|
|
428
|
+
NodeId must be hashable and not contain special characters that could
|
|
429
|
+
enable injection attacks.
|
|
430
|
+
|
|
431
|
+
Parameters
|
|
432
|
+
----------
|
|
433
|
+
value : Any
|
|
434
|
+
NodeId to validate.
|
|
435
|
+
|
|
436
|
+
Returns
|
|
437
|
+
-------
|
|
438
|
+
Any
|
|
439
|
+
Validated NodeId (unchanged if valid).
|
|
440
|
+
|
|
441
|
+
Raises
|
|
442
|
+
------
|
|
443
|
+
ValidationError
|
|
444
|
+
If value is not hashable or contains suspicious patterns.
|
|
445
|
+
|
|
446
|
+
Examples
|
|
447
|
+
--------
|
|
448
|
+
>>> validate_node_id("node_1")
|
|
449
|
+
'node_1'
|
|
450
|
+
>>> validate_node_id(42)
|
|
451
|
+
42
|
|
452
|
+
>>> validate_node_id(['list'])
|
|
453
|
+
Traceback (most recent call last):
|
|
454
|
+
...
|
|
455
|
+
ValidationError: NodeId must be hashable
|
|
456
|
+
"""
|
|
457
|
+
try:
|
|
458
|
+
hash(value)
|
|
459
|
+
except TypeError:
|
|
460
|
+
raise ValidationError(
|
|
461
|
+
"NodeId must be hashable",
|
|
462
|
+
parameter="node",
|
|
463
|
+
value=value,
|
|
464
|
+
constraint="hashable",
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
# For string node IDs, check for suspicious patterns
|
|
468
|
+
if isinstance(value, str):
|
|
469
|
+
# Disallow control characters and common injection patterns
|
|
470
|
+
if any(ord(c) < 32 or ord(c) == 127 for c in value):
|
|
471
|
+
raise ValidationError(
|
|
472
|
+
"NodeId cannot contain control characters",
|
|
473
|
+
parameter="node",
|
|
474
|
+
value=value,
|
|
475
|
+
constraint="printable",
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
# Check for common injection patterns (basic protection)
|
|
479
|
+
suspicious_patterns = [
|
|
480
|
+
r"<script", # XSS attempts
|
|
481
|
+
r"javascript:", # JavaScript protocol
|
|
482
|
+
r"on\w+\s*=", # Event handlers
|
|
483
|
+
r"\$\{", # Template injection
|
|
484
|
+
r"`", # Backticks for command injection
|
|
485
|
+
]
|
|
486
|
+
|
|
487
|
+
for pattern in suspicious_patterns:
|
|
488
|
+
if re.search(pattern, value, re.IGNORECASE):
|
|
489
|
+
raise ValidationError(
|
|
490
|
+
f"NodeId contains suspicious pattern: {pattern}",
|
|
491
|
+
parameter="node",
|
|
492
|
+
value=value,
|
|
493
|
+
constraint="safe string",
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
return value
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def validate_glyph(value: Any) -> Glyph:
|
|
500
|
+
"""Validate Glyph enumeration value or structural operator name.
|
|
501
|
+
|
|
502
|
+
Accepts both Glyph codes (e.g., "AL", "IL") and structural operator names
|
|
503
|
+
(e.g., "emission", "coherence") following TNFR canonical grammar.
|
|
504
|
+
|
|
505
|
+
Parameters
|
|
506
|
+
----------
|
|
507
|
+
value : Any
|
|
508
|
+
Value to validate as a Glyph. Can be:
|
|
509
|
+
- A Glyph enum instance (e.g., Glyph.AL)
|
|
510
|
+
- A glyph code string (e.g., "AL", "IL")
|
|
511
|
+
- A structural operator name (e.g., "emission", "coherence")
|
|
512
|
+
|
|
513
|
+
Returns
|
|
514
|
+
-------
|
|
515
|
+
Glyph
|
|
516
|
+
Validated Glyph enumeration.
|
|
517
|
+
|
|
518
|
+
Raises
|
|
519
|
+
------
|
|
520
|
+
ValidationError
|
|
521
|
+
If value is not a valid Glyph or structural operator name.
|
|
522
|
+
|
|
523
|
+
Examples
|
|
524
|
+
--------
|
|
525
|
+
>>> validate_glyph(Glyph.AL)
|
|
526
|
+
<Glyph.AL: 'AL'>
|
|
527
|
+
>>> validate_glyph("AL")
|
|
528
|
+
<Glyph.AL: 'AL'>
|
|
529
|
+
>>> validate_glyph("emission")
|
|
530
|
+
<Glyph.AL: 'AL'>
|
|
531
|
+
>>> validate_glyph("INVALID")
|
|
532
|
+
Traceback (most recent call last):
|
|
533
|
+
...
|
|
534
|
+
ValidationError: Invalid glyph value: 'INVALID'
|
|
535
|
+
"""
|
|
536
|
+
if isinstance(value, Glyph):
|
|
537
|
+
return value
|
|
538
|
+
|
|
539
|
+
if isinstance(value, str):
|
|
540
|
+
# Try direct Glyph code first
|
|
541
|
+
try:
|
|
542
|
+
return Glyph(value)
|
|
543
|
+
except ValueError:
|
|
544
|
+
pass
|
|
545
|
+
|
|
546
|
+
# Try structural operator name mapping
|
|
547
|
+
try:
|
|
548
|
+
from ..operators.grammar import function_name_to_glyph
|
|
549
|
+
|
|
550
|
+
glyph = function_name_to_glyph(value)
|
|
551
|
+
if glyph is not None:
|
|
552
|
+
return glyph
|
|
553
|
+
except Exception:
|
|
554
|
+
# If grammar module import fails, continue to error
|
|
555
|
+
pass
|
|
556
|
+
|
|
557
|
+
raise ValidationError(
|
|
558
|
+
f"Invalid glyph value: {value!r}",
|
|
559
|
+
parameter="glyph",
|
|
560
|
+
value=value,
|
|
561
|
+
constraint="valid Glyph enumeration or structural operator name",
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def validate_tnfr_graph(value: Any) -> TNFRGraph:
|
|
566
|
+
"""Validate TNFRGraph instance.
|
|
567
|
+
|
|
568
|
+
Parameters
|
|
569
|
+
----------
|
|
570
|
+
value : Any
|
|
571
|
+
Value to validate as a TNFRGraph.
|
|
572
|
+
|
|
573
|
+
Returns
|
|
574
|
+
-------
|
|
575
|
+
TNFRGraph
|
|
576
|
+
Validated TNFRGraph instance.
|
|
577
|
+
|
|
578
|
+
Raises
|
|
579
|
+
------
|
|
580
|
+
ValidationError
|
|
581
|
+
If value is not a valid TNFRGraph.
|
|
582
|
+
|
|
583
|
+
Examples
|
|
584
|
+
--------
|
|
585
|
+
>>> import networkx as nx
|
|
586
|
+
>>> G = nx.DiGraph()
|
|
587
|
+
>>> validate_tnfr_graph(G)
|
|
588
|
+
<networkx.classes.digraph.DiGraph object at ...>
|
|
589
|
+
>>> validate_tnfr_graph("not a graph")
|
|
590
|
+
Traceback (most recent call last):
|
|
591
|
+
...
|
|
592
|
+
ValidationError: Expected TNFRGraph, got str
|
|
593
|
+
"""
|
|
594
|
+
if not isinstance(value, (nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph)):
|
|
595
|
+
raise ValidationError(
|
|
596
|
+
f"Expected TNFRGraph, got {type(value).__name__}",
|
|
597
|
+
parameter="G",
|
|
598
|
+
value=value,
|
|
599
|
+
constraint="networkx.Graph instance",
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
# Ensure required graph attributes exist
|
|
603
|
+
if not hasattr(value, "graph"):
|
|
604
|
+
raise ValidationError(
|
|
605
|
+
"TNFRGraph must have 'graph' attribute",
|
|
606
|
+
parameter="G",
|
|
607
|
+
value=value,
|
|
608
|
+
constraint="graph attribute",
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
return value
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def validate_glyph_factors(
|
|
615
|
+
factors: Any,
|
|
616
|
+
*,
|
|
617
|
+
required_keys: set[str] | None = None,
|
|
618
|
+
) -> dict[str, float]:
|
|
619
|
+
"""Validate glyph factors dictionary.
|
|
620
|
+
|
|
621
|
+
Glyph factors contain operator-specific coefficients that modulate
|
|
622
|
+
structural transformations.
|
|
623
|
+
|
|
624
|
+
Parameters
|
|
625
|
+
----------
|
|
626
|
+
factors : Any
|
|
627
|
+
Glyph factors to validate.
|
|
628
|
+
required_keys : set[str] | None
|
|
629
|
+
Required factor keys. If None, no specific keys are required.
|
|
630
|
+
|
|
631
|
+
Returns
|
|
632
|
+
-------
|
|
633
|
+
dict[str, float]
|
|
634
|
+
Validated glyph factors.
|
|
635
|
+
|
|
636
|
+
Raises
|
|
637
|
+
------
|
|
638
|
+
ValidationError
|
|
639
|
+
If factors is not a mapping or contains invalid values.
|
|
640
|
+
|
|
641
|
+
Examples
|
|
642
|
+
--------
|
|
643
|
+
>>> validate_glyph_factors({"AL_boost": 0.1, "EN_mix": 0.25})
|
|
644
|
+
{'AL_boost': 0.1, 'EN_mix': 0.25}
|
|
645
|
+
>>> validate_glyph_factors("not a dict")
|
|
646
|
+
Traceback (most recent call last):
|
|
647
|
+
...
|
|
648
|
+
ValidationError: Glyph factors must be a mapping
|
|
649
|
+
"""
|
|
650
|
+
if not isinstance(factors, Mapping):
|
|
651
|
+
raise ValidationError(
|
|
652
|
+
"Glyph factors must be a mapping",
|
|
653
|
+
parameter="glyph_factors",
|
|
654
|
+
value=factors,
|
|
655
|
+
constraint="mapping type",
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
validated: dict[str, float] = {}
|
|
659
|
+
|
|
660
|
+
for key, value in factors.items():
|
|
661
|
+
if not isinstance(key, str):
|
|
662
|
+
raise ValidationError(
|
|
663
|
+
f"Glyph factor key must be string, got {type(key).__name__}",
|
|
664
|
+
parameter=f"glyph_factors[{key!r}]",
|
|
665
|
+
value=value,
|
|
666
|
+
constraint="string key",
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
if not isinstance(value, (int, float)):
|
|
670
|
+
raise ValidationError(
|
|
671
|
+
f"Glyph factor value must be numeric, got {type(value).__name__}",
|
|
672
|
+
parameter=f"glyph_factors[{key!r}]",
|
|
673
|
+
value=value,
|
|
674
|
+
constraint="numeric value",
|
|
675
|
+
)
|
|
676
|
+
|
|
677
|
+
if not math.isfinite(float(value)):
|
|
678
|
+
raise ValidationError(
|
|
679
|
+
f"Glyph factor value cannot be nan or inf: {value}",
|
|
680
|
+
parameter=f"glyph_factors[{key!r}]",
|
|
681
|
+
value=value,
|
|
682
|
+
constraint="finite",
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
validated[key] = float(value)
|
|
686
|
+
|
|
687
|
+
if required_keys is not None:
|
|
688
|
+
missing = required_keys - set(validated.keys())
|
|
689
|
+
if missing:
|
|
690
|
+
raise ValidationError(
|
|
691
|
+
f"Missing required glyph factor keys: {sorted(missing)}",
|
|
692
|
+
parameter="glyph_factors",
|
|
693
|
+
value=factors,
|
|
694
|
+
constraint=f"required keys: {sorted(required_keys)}",
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
return validated
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
def validate_operator_parameters(
|
|
701
|
+
parameters: Mapping[str, Any],
|
|
702
|
+
*,
|
|
703
|
+
config: Mapping[str, Any] | None = None,
|
|
704
|
+
) -> dict[str, Any]:
|
|
705
|
+
"""Validate structural operator parameters.
|
|
706
|
+
|
|
707
|
+
This function validates common operator parameters including EPI, νf, θ,
|
|
708
|
+
and ΔNFR values to ensure they respect canonical bounds.
|
|
709
|
+
|
|
710
|
+
Parameters
|
|
711
|
+
----------
|
|
712
|
+
parameters : Mapping[str, Any]
|
|
713
|
+
Operator parameters to validate.
|
|
714
|
+
config : Mapping[str, Any] | None
|
|
715
|
+
Graph configuration for bound checking.
|
|
716
|
+
|
|
717
|
+
Returns
|
|
718
|
+
-------
|
|
719
|
+
dict[str, Any]
|
|
720
|
+
Validated parameters.
|
|
721
|
+
|
|
722
|
+
Raises
|
|
723
|
+
------
|
|
724
|
+
ValidationError
|
|
725
|
+
If any parameter fails validation.
|
|
726
|
+
|
|
727
|
+
Examples
|
|
728
|
+
--------
|
|
729
|
+
>>> validate_operator_parameters({"epi": 0.5, "vf": 1.0, "theta": 0.0})
|
|
730
|
+
{'epi': 0.5, 'vf': 1.0, 'theta': 0.0}
|
|
731
|
+
"""
|
|
732
|
+
validated: dict[str, Any] = {}
|
|
733
|
+
|
|
734
|
+
for key, value in parameters.items():
|
|
735
|
+
if key in ("epi", "EPI"):
|
|
736
|
+
validated[key] = validate_epi_value(value, config=config)
|
|
737
|
+
elif key in ("vf", "VF", "nu_f"):
|
|
738
|
+
validated[key] = validate_vf_value(value, config=config)
|
|
739
|
+
elif key in ("theta", "THETA", "phase"):
|
|
740
|
+
validated[key] = validate_theta_value(value)
|
|
741
|
+
elif key in ("dnfr", "DNFR", "delta_nfr"):
|
|
742
|
+
validated[key] = validate_dnfr_value(value, config=config)
|
|
743
|
+
elif key == "node":
|
|
744
|
+
validated[key] = validate_node_id(value)
|
|
745
|
+
elif key == "glyph":
|
|
746
|
+
validated[key] = validate_glyph(value)
|
|
747
|
+
elif key == "G" or key == "graph":
|
|
748
|
+
validated[key] = validate_tnfr_graph(value)
|
|
749
|
+
elif key == "glyph_factors":
|
|
750
|
+
validated[key] = validate_glyph_factors(value)
|
|
751
|
+
else:
|
|
752
|
+
# Pass through other parameters unchanged
|
|
753
|
+
validated[key] = value
|
|
754
|
+
|
|
755
|
+
return validated
|