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,85 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
from ..selector import _selector_parallel_jobs as _selector_parallel_jobs
|
|
5
|
+
from ..types import (
|
|
6
|
+
GlyphCode as GlyphCode,
|
|
7
|
+
GlyphSelector,
|
|
8
|
+
HistoryState,
|
|
9
|
+
NodeId,
|
|
10
|
+
TNFRGraph,
|
|
11
|
+
)
|
|
12
|
+
from _typeshed import Incomplete
|
|
13
|
+
from abc import ABC, abstractmethod
|
|
14
|
+
from collections.abc import Mapping, MutableMapping, Sequence
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"GlyphCode",
|
|
20
|
+
"AbstractSelector",
|
|
21
|
+
"DefaultGlyphSelector",
|
|
22
|
+
"ParametricGlyphSelector",
|
|
23
|
+
"default_glyph_selector",
|
|
24
|
+
"parametric_glyph_selector",
|
|
25
|
+
"_SelectorPreselection",
|
|
26
|
+
"_configure_selector_weights",
|
|
27
|
+
"_apply_selector",
|
|
28
|
+
"_apply_glyphs",
|
|
29
|
+
"_selector_parallel_jobs",
|
|
30
|
+
"_prepare_selector_preselection",
|
|
31
|
+
"_resolve_preselected_glyph",
|
|
32
|
+
"_choose_glyph",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
class AbstractSelector(ABC, metaclass=abc.ABCMeta):
|
|
36
|
+
def prepare(self, graph: TNFRGraph, nodes: Sequence[NodeId]) -> None: ...
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def select(self, graph: TNFRGraph, node: NodeId) -> GlyphCode: ...
|
|
39
|
+
def __call__(self, graph: TNFRGraph, node: NodeId) -> GlyphCode: ...
|
|
40
|
+
|
|
41
|
+
def _configure_selector_weights(G: TNFRGraph) -> Mapping[str, float]: ...
|
|
42
|
+
@dataclass
|
|
43
|
+
class _SelectorPreselection:
|
|
44
|
+
kind: str
|
|
45
|
+
metrics: Mapping[Any, tuple[float, float, float]]
|
|
46
|
+
base_choices: Mapping[Any, GlyphCode]
|
|
47
|
+
thresholds: Mapping[str, float] | None = ...
|
|
48
|
+
margin: float | None = ...
|
|
49
|
+
|
|
50
|
+
class DefaultGlyphSelector(AbstractSelector):
|
|
51
|
+
def __init__(self) -> None: ...
|
|
52
|
+
def prepare(self, graph: TNFRGraph, nodes: Sequence[NodeId]) -> None: ...
|
|
53
|
+
def select(self, graph: TNFRGraph, node: NodeId) -> GlyphCode: ...
|
|
54
|
+
|
|
55
|
+
class ParametricGlyphSelector(AbstractSelector):
|
|
56
|
+
def __init__(self) -> None: ...
|
|
57
|
+
def prepare(self, graph: TNFRGraph, nodes: Sequence[NodeId]) -> None: ...
|
|
58
|
+
def select(self, graph: TNFRGraph, node: NodeId) -> GlyphCode: ...
|
|
59
|
+
|
|
60
|
+
default_glyph_selector: Incomplete
|
|
61
|
+
parametric_glyph_selector: Incomplete
|
|
62
|
+
|
|
63
|
+
def _choose_glyph(
|
|
64
|
+
G: TNFRGraph,
|
|
65
|
+
n: NodeId,
|
|
66
|
+
selector: GlyphSelector,
|
|
67
|
+
use_canon: bool,
|
|
68
|
+
h_al: MutableMapping[Any, int],
|
|
69
|
+
h_en: MutableMapping[Any, int],
|
|
70
|
+
al_max: int,
|
|
71
|
+
en_max: int,
|
|
72
|
+
) -> GlyphCode: ...
|
|
73
|
+
def _prepare_selector_preselection(
|
|
74
|
+
G: TNFRGraph, selector: GlyphSelector, nodes: Sequence[NodeId]
|
|
75
|
+
) -> _SelectorPreselection | None: ...
|
|
76
|
+
def _resolve_preselected_glyph(
|
|
77
|
+
G: TNFRGraph,
|
|
78
|
+
n: NodeId,
|
|
79
|
+
selector: GlyphSelector,
|
|
80
|
+
preselection: _SelectorPreselection | None,
|
|
81
|
+
) -> GlyphCode: ...
|
|
82
|
+
def _apply_glyphs(
|
|
83
|
+
G: TNFRGraph, selector: GlyphSelector, hist: HistoryState
|
|
84
|
+
) -> None: ...
|
|
85
|
+
def _apply_selector(G: TNFRGraph) -> GlyphSelector: ...
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""Structural boundary preservation for EPI values.
|
|
2
|
+
|
|
3
|
+
This module implements canonical TNFR structural clipping that preserves
|
|
4
|
+
coherence by constraining EPI values to valid structural boundaries while
|
|
5
|
+
maintaining smooth operator behavior.
|
|
6
|
+
|
|
7
|
+
The structural_clip function ensures that EPI remains within [-1.0, 1.0]
|
|
8
|
+
(or configurable bounds) after operator application and integration steps,
|
|
9
|
+
preventing numerical precision issues from violating structural invariants.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import math
|
|
15
|
+
from typing import Literal
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"structural_clip",
|
|
19
|
+
"StructuralClipStats",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class StructuralClipStats:
|
|
24
|
+
"""Telemetry for structural boundary interventions.
|
|
25
|
+
|
|
26
|
+
Tracks how often and by how much the structural_clip function
|
|
27
|
+
adjusts EPI values to preserve structural boundaries.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self) -> None:
|
|
31
|
+
"""Initialize empty statistics."""
|
|
32
|
+
self.hard_clips: int = 0
|
|
33
|
+
self.soft_clips: int = 0
|
|
34
|
+
self.total_adjustments: int = 0
|
|
35
|
+
self.max_delta_hard: float = 0.0
|
|
36
|
+
self.max_delta_soft: float = 0.0
|
|
37
|
+
self.sum_delta_hard: float = 0.0
|
|
38
|
+
self.sum_delta_soft: float = 0.0
|
|
39
|
+
|
|
40
|
+
def record_hard_clip(self, delta: float) -> None:
|
|
41
|
+
"""Record a hard clip intervention."""
|
|
42
|
+
self.hard_clips += 1
|
|
43
|
+
self.total_adjustments += 1
|
|
44
|
+
abs_delta = abs(delta)
|
|
45
|
+
self.max_delta_hard = max(self.max_delta_hard, abs_delta)
|
|
46
|
+
self.sum_delta_hard += abs_delta
|
|
47
|
+
|
|
48
|
+
def record_soft_clip(self, delta: float) -> None:
|
|
49
|
+
"""Record a soft clip intervention."""
|
|
50
|
+
self.soft_clips += 1
|
|
51
|
+
self.total_adjustments += 1
|
|
52
|
+
abs_delta = abs(delta)
|
|
53
|
+
self.max_delta_soft = max(self.max_delta_soft, abs_delta)
|
|
54
|
+
self.sum_delta_soft += abs_delta
|
|
55
|
+
|
|
56
|
+
def reset(self) -> None:
|
|
57
|
+
"""Reset all statistics to zero."""
|
|
58
|
+
self.hard_clips = 0
|
|
59
|
+
self.soft_clips = 0
|
|
60
|
+
self.total_adjustments = 0
|
|
61
|
+
self.max_delta_hard = 0.0
|
|
62
|
+
self.max_delta_soft = 0.0
|
|
63
|
+
self.sum_delta_hard = 0.0
|
|
64
|
+
self.sum_delta_soft = 0.0
|
|
65
|
+
|
|
66
|
+
def summary(self) -> dict[str, float | int]:
|
|
67
|
+
"""Return summary statistics as dictionary."""
|
|
68
|
+
return {
|
|
69
|
+
"hard_clips": self.hard_clips,
|
|
70
|
+
"soft_clips": self.soft_clips,
|
|
71
|
+
"total_adjustments": self.total_adjustments,
|
|
72
|
+
"max_delta_hard": self.max_delta_hard,
|
|
73
|
+
"max_delta_soft": self.max_delta_soft,
|
|
74
|
+
"avg_delta_hard": (
|
|
75
|
+
self.sum_delta_hard / self.hard_clips if self.hard_clips > 0 else 0.0
|
|
76
|
+
),
|
|
77
|
+
"avg_delta_soft": (
|
|
78
|
+
self.sum_delta_soft / self.soft_clips if self.soft_clips > 0 else 0.0
|
|
79
|
+
),
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# Global statistics instance (optional telemetry)
|
|
84
|
+
_global_stats = StructuralClipStats()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def get_clip_stats() -> StructuralClipStats:
|
|
88
|
+
"""Return the global clip statistics instance."""
|
|
89
|
+
return _global_stats
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def reset_clip_stats() -> None:
|
|
93
|
+
"""Reset global clip statistics."""
|
|
94
|
+
_global_stats.reset()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def structural_clip(
|
|
98
|
+
value: float,
|
|
99
|
+
lo: float = -1.0,
|
|
100
|
+
hi: float = 1.0,
|
|
101
|
+
mode: Literal["hard", "soft"] = "hard",
|
|
102
|
+
k: float = 3.0,
|
|
103
|
+
*,
|
|
104
|
+
record_stats: bool = False,
|
|
105
|
+
) -> float:
|
|
106
|
+
"""Apply structural boundary preservation to EPI value.
|
|
107
|
+
|
|
108
|
+
Ensures that values remain within structural boundaries while preserving
|
|
109
|
+
coherence. Two modes are available:
|
|
110
|
+
|
|
111
|
+
- **hard**: Classic clamping for immediate stability (discontinuous derivative)
|
|
112
|
+
- **soft**: Smooth hyperbolic tangent mapping (continuous derivative)
|
|
113
|
+
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
value : float
|
|
117
|
+
The EPI value to clip
|
|
118
|
+
lo : float, default -1.0
|
|
119
|
+
Lower structural boundary (EPI_MIN)
|
|
120
|
+
hi : float, default 1.0
|
|
121
|
+
Upper structural boundary (EPI_MAX)
|
|
122
|
+
mode : {'hard', 'soft'}, default 'hard'
|
|
123
|
+
Clipping mode:
|
|
124
|
+
- 'hard': Clamp to [lo, hi] (fast, discontinuous)
|
|
125
|
+
- 'soft': Smooth tanh-based remapping (slower, smooth)
|
|
126
|
+
k : float, default 3.0
|
|
127
|
+
Steepness parameter for soft mode (higher = sharper transition)
|
|
128
|
+
record_stats : bool, default False
|
|
129
|
+
If True, record intervention statistics in global telemetry
|
|
130
|
+
|
|
131
|
+
Returns
|
|
132
|
+
-------
|
|
133
|
+
float
|
|
134
|
+
Value constrained to [lo, hi] with specified mode
|
|
135
|
+
|
|
136
|
+
Notes
|
|
137
|
+
-----
|
|
138
|
+
The soft mode uses a scaled hyperbolic tangent:
|
|
139
|
+
|
|
140
|
+
y = tanh(k · x) / tanh(k)
|
|
141
|
+
|
|
142
|
+
which maps the input smoothly to [-1, 1], then rescales to [lo, hi].
|
|
143
|
+
This preserves derivative continuity but is computationally more expensive.
|
|
144
|
+
|
|
145
|
+
The hard mode is preferred for most use cases as it directly enforces
|
|
146
|
+
boundaries with minimal overhead.
|
|
147
|
+
|
|
148
|
+
Examples
|
|
149
|
+
--------
|
|
150
|
+
>>> structural_clip(1.1, -1.0, 1.0, mode="hard")
|
|
151
|
+
1.0
|
|
152
|
+
>>> structural_clip(-1.2, -1.0, 1.0, mode="hard")
|
|
153
|
+
-1.0
|
|
154
|
+
>>> abs(structural_clip(0.95, -1.0, 1.0, mode="soft") - 0.95) < 0.01
|
|
155
|
+
True
|
|
156
|
+
"""
|
|
157
|
+
if lo > hi:
|
|
158
|
+
raise ValueError(f"Lower bound {lo} must be <= upper bound {hi}")
|
|
159
|
+
|
|
160
|
+
if mode == "hard":
|
|
161
|
+
# Classic clamping - fast and simple
|
|
162
|
+
clipped = max(lo, min(hi, value))
|
|
163
|
+
if record_stats and clipped != value:
|
|
164
|
+
_global_stats.record_hard_clip(clipped - value)
|
|
165
|
+
return clipped
|
|
166
|
+
|
|
167
|
+
elif mode == "soft":
|
|
168
|
+
# Smooth sigmoid-based mapping that guarantees bounds
|
|
169
|
+
# Uses scaled tanh to create smooth transitions near boundaries
|
|
170
|
+
if lo == hi:
|
|
171
|
+
return lo
|
|
172
|
+
|
|
173
|
+
# First, clamp to slightly extended range to handle the mapping
|
|
174
|
+
# Map [lo, hi] to working range
|
|
175
|
+
margin = (hi - lo) * 0.1 # 10% margin for smooth transition
|
|
176
|
+
working_lo = lo - margin
|
|
177
|
+
working_hi = hi + margin
|
|
178
|
+
|
|
179
|
+
# Normalize to [-1, 1] for tanh
|
|
180
|
+
# Check for zero-width range after extension (shouldn't happen with lo != hi)
|
|
181
|
+
range_width = working_hi - working_lo
|
|
182
|
+
if abs(range_width) < 1e-10:
|
|
183
|
+
# Degenerate case: return midpoint
|
|
184
|
+
return (lo + hi) / 2.0
|
|
185
|
+
|
|
186
|
+
normalized = 2.0 * (value - (working_lo + working_hi) / 2.0) / range_width
|
|
187
|
+
|
|
188
|
+
# Apply tanh with steepness k for smooth S-curve
|
|
189
|
+
# tanh maps R → (-1, 1), scaled by k to control steepness
|
|
190
|
+
smooth_normalized = math.tanh(k * normalized)
|
|
191
|
+
|
|
192
|
+
# Map back from (-1, 1) to [lo, hi]
|
|
193
|
+
# This ensures output is always within [lo, hi]
|
|
194
|
+
mid = (lo + hi) / 2.0
|
|
195
|
+
half_range = (hi - lo) / 2.0
|
|
196
|
+
clipped = mid + smooth_normalized * half_range
|
|
197
|
+
|
|
198
|
+
# Final safety clamp for numerical precision
|
|
199
|
+
clipped = max(lo, min(hi, clipped))
|
|
200
|
+
|
|
201
|
+
if record_stats and abs(clipped - value) > 1e-10:
|
|
202
|
+
_global_stats.record_soft_clip(clipped - value)
|
|
203
|
+
|
|
204
|
+
return clipped
|
|
205
|
+
|
|
206
|
+
else:
|
|
207
|
+
raise ValueError(f"mode must be 'hard' or 'soft', got {mode!r}")
|
tnfr/errors/__init__.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Contextual error messages for TNFR operations.
|
|
2
|
+
|
|
3
|
+
This module provides user-friendly error messages with:
|
|
4
|
+
- Clear explanations of what went wrong
|
|
5
|
+
- Suggestions for how to fix the issue
|
|
6
|
+
- Links to relevant documentation
|
|
7
|
+
- Fuzzy matching for common typos
|
|
8
|
+
|
|
9
|
+
Examples
|
|
10
|
+
--------
|
|
11
|
+
>>> from tnfr.errors import OperatorSequenceError
|
|
12
|
+
>>> raise OperatorSequenceError(
|
|
13
|
+
... "Invalid operator 'emision'",
|
|
14
|
+
... suggestion="Did you mean 'emission'?",
|
|
15
|
+
... docs_url="https://tnfr.readthedocs.io/operators.html"
|
|
16
|
+
... )
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"TNFRUserError",
|
|
23
|
+
"OperatorSequenceError",
|
|
24
|
+
"NetworkConfigError",
|
|
25
|
+
"PhaseError",
|
|
26
|
+
"CoherenceError",
|
|
27
|
+
"FrequencyError",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
from .contextual import (
|
|
31
|
+
TNFRUserError,
|
|
32
|
+
OperatorSequenceError,
|
|
33
|
+
NetworkConfigError,
|
|
34
|
+
PhaseError,
|
|
35
|
+
CoherenceError,
|
|
36
|
+
FrequencyError,
|
|
37
|
+
)
|