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
tnfr/glyph_history.pyi
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections import Counter
|
|
4
|
+
from collections.abc import Mapping, MutableMapping
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from .types import TNFRGraph
|
|
8
|
+
|
|
9
|
+
__all__: tuple[str, ...]
|
|
10
|
+
|
|
11
|
+
class HistoryDict(dict[str, Any]):
|
|
12
|
+
_maxlen: int
|
|
13
|
+
_counts: Counter[str]
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self, data: Mapping[str, Any] | None = ..., *, maxlen: int = ...
|
|
17
|
+
) -> None: ...
|
|
18
|
+
def get_increment(self, key: str, default: Any = ...) -> Any: ...
|
|
19
|
+
def __getitem__(self, key: str) -> Any: ...
|
|
20
|
+
def get(self, key: str, default: Any | None = ...) -> Any: ...
|
|
21
|
+
def __setitem__(self, key: str, value: Any) -> None: ...
|
|
22
|
+
def setdefault(self, key: str, default: Any | None = ...) -> Any: ...
|
|
23
|
+
def pop_least_used(self) -> Any: ...
|
|
24
|
+
def pop_least_used_batch(self, k: int) -> None: ...
|
|
25
|
+
|
|
26
|
+
def push_glyph(nd: MutableMapping[str, Any], glyph: str, window: int) -> None: ...
|
|
27
|
+
def recent_glyph(nd: MutableMapping[str, Any], glyph: str, window: int) -> bool: ...
|
|
28
|
+
def ensure_history(G: TNFRGraph) -> HistoryDict | dict[str, Any]: ...
|
|
29
|
+
def current_step_idx(G: TNFRGraph | Mapping[str, Any]) -> int: ...
|
|
30
|
+
def append_metric(
|
|
31
|
+
hist: MutableMapping[str, list[Any]], key: str, value: Any
|
|
32
|
+
) -> None: ...
|
|
33
|
+
def count_glyphs(
|
|
34
|
+
G: TNFRGraph, window: int | None = ..., *, last_only: bool = ...
|
|
35
|
+
) -> Counter[str]: ...
|
tnfr/glyph_runtime.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Runtime helpers for structural operator glyphs decoupled from validation internals.
|
|
2
|
+
|
|
3
|
+
This module provides utilities for working with glyphs (structural symbols like
|
|
4
|
+
AL, EN, IL, etc.) that represent the application of structural operators to nodes.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import Mapping
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
__all__ = ("last_glyph",)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def last_glyph(nd: Mapping[str, Any]) -> str | None:
|
|
16
|
+
"""Return the most recent glyph for node or ``None``."""
|
|
17
|
+
|
|
18
|
+
hist = nd.get("glyph_history")
|
|
19
|
+
return hist[-1] if hist else None
|
tnfr/glyph_runtime.pyi
ADDED
tnfr/immutable.py
CHANGED
|
@@ -7,23 +7,46 @@ encountered.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import threading
|
|
11
|
+
import weakref
|
|
12
|
+
from collections.abc import Mapping
|
|
10
13
|
from contextlib import contextmanager
|
|
11
14
|
from dataclasses import asdict, is_dataclass
|
|
12
|
-
from functools import lru_cache, singledispatch, wraps
|
|
13
|
-
from typing import Any, Callable
|
|
14
|
-
from collections.abc import Mapping
|
|
15
|
+
from functools import lru_cache, partial, singledispatch, wraps
|
|
15
16
|
from types import MappingProxyType
|
|
16
|
-
import
|
|
17
|
-
|
|
17
|
+
from typing import Any, Callable, Iterable, Iterator, cast
|
|
18
|
+
|
|
19
|
+
from ._compat import TypeAlias
|
|
18
20
|
|
|
19
21
|
# Types considered immutable without further inspection
|
|
20
|
-
IMMUTABLE_SIMPLE = frozenset(
|
|
21
|
-
|
|
22
|
+
IMMUTABLE_SIMPLE = frozenset({int, float, complex, str, bool, bytes, type(None)})
|
|
23
|
+
|
|
24
|
+
FrozenPrimitive: TypeAlias = int | float | complex | str | bool | bytes | None
|
|
25
|
+
"""Primitive immutable values handled directly by :func:`_freeze`."""
|
|
26
|
+
|
|
27
|
+
FrozenCollectionItems: TypeAlias = tuple["FrozenSnapshot", ...]
|
|
28
|
+
"""Frozen representation for generic iterables."""
|
|
29
|
+
|
|
30
|
+
FrozenMappingItems: TypeAlias = tuple[tuple[Any, "FrozenSnapshot"], ...]
|
|
31
|
+
"""Frozen representation for mapping ``items()`` snapshots."""
|
|
32
|
+
|
|
33
|
+
FrozenTaggedCollection: TypeAlias = tuple[str, FrozenCollectionItems]
|
|
34
|
+
"""Tagged iterable snapshot identifying the original container type."""
|
|
35
|
+
|
|
36
|
+
FrozenTaggedMapping: TypeAlias = tuple[str, FrozenMappingItems]
|
|
37
|
+
"""Tagged mapping snapshot identifying the original mapping flavour."""
|
|
38
|
+
|
|
39
|
+
FrozenSnapshot: TypeAlias = (
|
|
40
|
+
FrozenPrimitive
|
|
41
|
+
| FrozenCollectionItems
|
|
42
|
+
| FrozenTaggedCollection
|
|
43
|
+
| FrozenTaggedMapping
|
|
22
44
|
)
|
|
45
|
+
"""Union describing the immutable snapshot returned by :func:`_freeze`."""
|
|
23
46
|
|
|
24
47
|
|
|
25
48
|
@contextmanager
|
|
26
|
-
def _cycle_guard(value: Any, seen: set[int] | None = None):
|
|
49
|
+
def _cycle_guard(value: Any, seen: set[int] | None = None) -> Iterator[set[int]]:
|
|
27
50
|
"""Context manager that detects reference cycles during freezing."""
|
|
28
51
|
if seen is None:
|
|
29
52
|
seen = set()
|
|
@@ -37,18 +60,20 @@ def _cycle_guard(value: Any, seen: set[int] | None = None):
|
|
|
37
60
|
seen.remove(obj_id)
|
|
38
61
|
|
|
39
62
|
|
|
40
|
-
def _check_cycle(
|
|
41
|
-
|
|
63
|
+
def _check_cycle(
|
|
64
|
+
func: Callable[[Any, set[int] | None], FrozenSnapshot],
|
|
65
|
+
) -> Callable[[Any, set[int] | None], FrozenSnapshot]:
|
|
66
|
+
"""Apply :func:`_cycle_guard` to ``func``."""
|
|
42
67
|
|
|
43
68
|
@wraps(func)
|
|
44
|
-
def wrapper(value: Any, seen: set[int] | None = None):
|
|
45
|
-
with _cycle_guard(value, seen) as
|
|
46
|
-
return func(value,
|
|
69
|
+
def wrapper(value: Any, seen: set[int] | None = None) -> FrozenSnapshot:
|
|
70
|
+
with _cycle_guard(value, seen) as guard_seen:
|
|
71
|
+
return func(value, guard_seen)
|
|
47
72
|
|
|
48
73
|
return wrapper
|
|
49
74
|
|
|
50
75
|
|
|
51
|
-
def _freeze_dataclass(value: Any, seen: set[int]):
|
|
76
|
+
def _freeze_dataclass(value: Any, seen: set[int]) -> FrozenTaggedMapping:
|
|
52
77
|
params = getattr(type(value), "__dataclass_params__", None)
|
|
53
78
|
frozen = bool(params and params.frozen)
|
|
54
79
|
data = asdict(value)
|
|
@@ -58,9 +83,10 @@ def _freeze_dataclass(value: Any, seen: set[int]):
|
|
|
58
83
|
|
|
59
84
|
@singledispatch
|
|
60
85
|
@_check_cycle
|
|
61
|
-
def _freeze(value: Any, seen: set[int] | None = None):
|
|
86
|
+
def _freeze(value: Any, seen: set[int] | None = None) -> FrozenSnapshot:
|
|
62
87
|
"""Recursively convert ``value`` into an immutable representation."""
|
|
63
88
|
if is_dataclass(value) and not isinstance(value, type):
|
|
89
|
+
assert seen is not None
|
|
64
90
|
return _freeze_dataclass(value, seen)
|
|
65
91
|
if type(value) in IMMUTABLE_SIMPLE:
|
|
66
92
|
return value
|
|
@@ -69,22 +95,31 @@ def _freeze(value: Any, seen: set[int] | None = None):
|
|
|
69
95
|
|
|
70
96
|
@_freeze.register(tuple)
|
|
71
97
|
@_check_cycle
|
|
72
|
-
def _freeze_tuple(
|
|
98
|
+
def _freeze_tuple(
|
|
99
|
+
value: tuple[Any, ...], seen: set[int] | None = None
|
|
100
|
+
) -> FrozenCollectionItems: # noqa: F401
|
|
101
|
+
assert seen is not None
|
|
73
102
|
return tuple(_freeze(v, seen) for v in value)
|
|
74
103
|
|
|
75
104
|
|
|
76
|
-
def _freeze_iterable(
|
|
105
|
+
def _freeze_iterable(
|
|
106
|
+
container: Iterable[Any], tag: str, seen: set[int]
|
|
107
|
+
) -> FrozenTaggedCollection:
|
|
77
108
|
return (tag, tuple(_freeze(v, seen) for v in container))
|
|
78
109
|
|
|
79
110
|
|
|
80
111
|
def _freeze_iterable_with_tag(
|
|
81
|
-
value: Any, seen: set[int] | None = None, *, tag: str
|
|
82
|
-
) ->
|
|
112
|
+
value: Iterable[Any], seen: set[int] | None = None, *, tag: str
|
|
113
|
+
) -> FrozenTaggedCollection:
|
|
114
|
+
assert seen is not None
|
|
83
115
|
return _freeze_iterable(value, tag, seen)
|
|
84
116
|
|
|
85
117
|
|
|
86
118
|
def _register_iterable(cls: type, tag: str) -> None:
|
|
87
|
-
|
|
119
|
+
handler = _check_cycle(partial(_freeze_iterable_with_tag, tag=tag))
|
|
120
|
+
_freeze.register(cls)(
|
|
121
|
+
cast(Callable[[Any, set[int] | None], FrozenSnapshot], handler)
|
|
122
|
+
)
|
|
88
123
|
|
|
89
124
|
|
|
90
125
|
for _cls, _tag in (
|
|
@@ -98,17 +133,22 @@ for _cls, _tag in (
|
|
|
98
133
|
|
|
99
134
|
@_freeze.register(Mapping)
|
|
100
135
|
@_check_cycle
|
|
101
|
-
def _freeze_mapping(
|
|
136
|
+
def _freeze_mapping(
|
|
137
|
+
value: Mapping[Any, Any], seen: set[int] | None = None
|
|
138
|
+
) -> FrozenTaggedMapping: # noqa: F401
|
|
139
|
+
assert seen is not None
|
|
102
140
|
tag = "dict" if hasattr(value, "__setitem__") else "mapping"
|
|
103
141
|
return (tag, tuple((k, _freeze(v, seen)) for k, v in value.items()))
|
|
104
142
|
|
|
105
143
|
|
|
106
|
-
def _all_immutable(iterable) -> bool:
|
|
144
|
+
def _all_immutable(iterable: Iterable[Any]) -> bool:
|
|
107
145
|
return all(_is_immutable_inner(v) for v in iterable)
|
|
108
146
|
|
|
109
147
|
|
|
110
148
|
# Dispatch table kept immutable to avoid accidental mutation.
|
|
111
|
-
|
|
149
|
+
ImmutableTagHandler: TypeAlias = Callable[[tuple[Any, ...]], bool]
|
|
150
|
+
|
|
151
|
+
_IMMUTABLE_TAG_DISPATCH: Mapping[str, ImmutableTagHandler] = MappingProxyType(
|
|
112
152
|
{
|
|
113
153
|
"mapping": lambda v: _all_immutable(v[1]),
|
|
114
154
|
"frozenset": lambda v: _all_immutable(v[1]),
|
|
@@ -123,11 +163,13 @@ _IMMUTABLE_TAG_DISPATCH: Mapping[str, Callable[[tuple], bool]] = MappingProxyTyp
|
|
|
123
163
|
@lru_cache(maxsize=1024)
|
|
124
164
|
@singledispatch
|
|
125
165
|
def _is_immutable_inner(value: Any) -> bool:
|
|
166
|
+
"""Return ``True`` when ``value`` belongs to the canonical immutable set."""
|
|
167
|
+
|
|
126
168
|
return type(value) in IMMUTABLE_SIMPLE
|
|
127
169
|
|
|
128
170
|
|
|
129
171
|
@_is_immutable_inner.register(tuple)
|
|
130
|
-
def _is_immutable_inner_tuple(value: tuple) -> bool: # noqa: F401
|
|
172
|
+
def _is_immutable_inner_tuple(value: tuple[Any, ...]) -> bool: # noqa: F401
|
|
131
173
|
if value and isinstance(value[0], str):
|
|
132
174
|
handler = _IMMUTABLE_TAG_DISPATCH.get(value[0])
|
|
133
175
|
if handler is not None:
|
|
@@ -136,13 +178,11 @@ def _is_immutable_inner_tuple(value: tuple) -> bool: # noqa: F401
|
|
|
136
178
|
|
|
137
179
|
|
|
138
180
|
@_is_immutable_inner.register(frozenset)
|
|
139
|
-
def _is_immutable_inner_frozenset(value: frozenset) -> bool: # noqa: F401
|
|
181
|
+
def _is_immutable_inner_frozenset(value: frozenset[Any]) -> bool: # noqa: F401
|
|
140
182
|
return _all_immutable(value)
|
|
141
183
|
|
|
142
184
|
|
|
143
|
-
_IMMUTABLE_CACHE: weakref.WeakKeyDictionary[Any, bool] = (
|
|
144
|
-
weakref.WeakKeyDictionary()
|
|
145
|
-
)
|
|
185
|
+
_IMMUTABLE_CACHE: weakref.WeakKeyDictionary[Any, bool] = weakref.WeakKeyDictionary()
|
|
146
186
|
_IMMUTABLE_CACHE_LOCK = threading.Lock()
|
|
147
187
|
|
|
148
188
|
|
|
@@ -152,7 +192,7 @@ def _is_immutable(value: Any) -> bool:
|
|
|
152
192
|
try:
|
|
153
193
|
return _IMMUTABLE_CACHE[value]
|
|
154
194
|
except (KeyError, TypeError):
|
|
155
|
-
pass
|
|
195
|
+
pass # Not in cache or value is unhashable
|
|
156
196
|
|
|
157
197
|
try:
|
|
158
198
|
frozen = _freeze(value)
|
|
@@ -165,7 +205,7 @@ def _is_immutable(value: Any) -> bool:
|
|
|
165
205
|
try:
|
|
166
206
|
_IMMUTABLE_CACHE[value] = result
|
|
167
207
|
except TypeError:
|
|
168
|
-
pass
|
|
208
|
+
pass # Value is unhashable, cannot cache
|
|
169
209
|
|
|
170
210
|
return result
|
|
171
211
|
|
tnfr/immutable.pyi
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable, Iterator, Mapping, Union
|
|
4
|
+
|
|
5
|
+
from ._compat import TypeAlias
|
|
6
|
+
|
|
7
|
+
FrozenPrimitive: TypeAlias = Union[int, float, complex, str, bool, bytes, None]
|
|
8
|
+
FrozenCollectionItems: TypeAlias = tuple["FrozenSnapshot", ...]
|
|
9
|
+
FrozenMappingItems: TypeAlias = tuple[tuple[Any, "FrozenSnapshot"], ...]
|
|
10
|
+
FrozenTaggedCollection: TypeAlias = tuple[str, FrozenCollectionItems]
|
|
11
|
+
FrozenTaggedMapping: TypeAlias = tuple[str, FrozenMappingItems]
|
|
12
|
+
FrozenSnapshot: TypeAlias = Union[
|
|
13
|
+
FrozenPrimitive,
|
|
14
|
+
FrozenCollectionItems,
|
|
15
|
+
FrozenTaggedCollection,
|
|
16
|
+
FrozenTaggedMapping,
|
|
17
|
+
]
|
|
18
|
+
ImmutableTagHandler: TypeAlias = Callable[[tuple[Any, ...]], bool]
|
|
19
|
+
|
|
20
|
+
__all__: tuple[str, ...]
|
|
21
|
+
|
|
22
|
+
def __getattr__(name: str) -> Any: ...
|
|
23
|
+
def _cycle_guard(value: Any, seen: set[int] | None = ...) -> Iterator[set[int]]: ...
|
|
24
|
+
def _check_cycle(
|
|
25
|
+
func: Callable[[Any, set[int] | None], FrozenSnapshot],
|
|
26
|
+
) -> Callable[[Any, set[int] | None], FrozenSnapshot]: ...
|
|
27
|
+
def _freeze(value: Any, seen: set[int] | None = ...) -> FrozenSnapshot: ...
|
|
28
|
+
def _freeze_mapping(
|
|
29
|
+
value: Mapping[Any, Any],
|
|
30
|
+
seen: set[int] | None = ...,
|
|
31
|
+
) -> FrozenTaggedMapping: ...
|
|
32
|
+
def _is_immutable(value: Any) -> bool: ...
|
|
33
|
+
def _is_immutable_inner(value: Any) -> bool: ...
|
|
34
|
+
|
|
35
|
+
_IMMUTABLE_CACHE: Any
|
|
36
|
+
_IMMUTABLE_TAG_DISPATCH: Mapping[str, ImmutableTagHandler]
|
tnfr/initialization.py
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
"""Node initialization."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
|
-
import random
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
6
4
|
|
|
5
|
+
import random
|
|
7
6
|
from dataclasses import dataclass
|
|
7
|
+
from typing import TYPE_CHECKING, cast
|
|
8
8
|
|
|
9
|
-
from .constants import
|
|
10
|
-
from .
|
|
9
|
+
from .constants import THETA_KEY, VF_KEY, get_graph_param
|
|
10
|
+
from .utils import clamp
|
|
11
11
|
from .rng import make_rng
|
|
12
|
+
from .types import NodeInitAttrMap
|
|
12
13
|
|
|
13
14
|
if TYPE_CHECKING: # pragma: no cover
|
|
14
|
-
import networkx as nx
|
|
15
|
+
import networkx as nx
|
|
15
16
|
|
|
16
17
|
__all__ = ("InitParams", "init_node_attrs")
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
@dataclass
|
|
20
21
|
class InitParams:
|
|
21
|
-
"""
|
|
22
|
+
"""Parameters governing node initialisation."""
|
|
22
23
|
|
|
23
24
|
seed: int | None
|
|
24
25
|
init_rand_phase: bool
|
|
@@ -38,7 +39,7 @@ class InitParams:
|
|
|
38
39
|
|
|
39
40
|
@classmethod
|
|
40
41
|
def from_graph(cls, G: "nx.Graph") -> "InitParams":
|
|
41
|
-
"""
|
|
42
|
+
"""Construct ``InitParams`` from ``G.graph`` configuration."""
|
|
42
43
|
|
|
43
44
|
return cls(
|
|
44
45
|
seed=get_graph_param(G, "RANDOM_SEED", int),
|
|
@@ -52,9 +53,7 @@ class InitParams:
|
|
|
52
53
|
vf_uniform_max=get_graph_param(G, "INIT_VF_MAX"),
|
|
53
54
|
vf_mean=get_graph_param(G, "INIT_VF_MEAN"),
|
|
54
55
|
vf_std=get_graph_param(G, "INIT_VF_STD"),
|
|
55
|
-
clamp_to_limits=get_graph_param(
|
|
56
|
-
G, "INIT_VF_CLAMP_TO_LIMITS", bool
|
|
57
|
-
),
|
|
56
|
+
clamp_to_limits=get_graph_param(G, "INIT_VF_CLAMP_TO_LIMITS", bool),
|
|
58
57
|
si_min=get_graph_param(G, "INIT_SI_MIN"),
|
|
59
58
|
si_max=get_graph_param(G, "INIT_SI_MAX"),
|
|
60
59
|
epi_val=get_graph_param(G, "INIT_EPI_VALUE"),
|
|
@@ -62,7 +61,7 @@ class InitParams:
|
|
|
62
61
|
|
|
63
62
|
|
|
64
63
|
def _init_phase(
|
|
65
|
-
nd:
|
|
64
|
+
nd: NodeInitAttrMap,
|
|
66
65
|
rng: random.Random,
|
|
67
66
|
*,
|
|
68
67
|
override: bool,
|
|
@@ -82,7 +81,7 @@ def _init_phase(
|
|
|
82
81
|
|
|
83
82
|
|
|
84
83
|
def _init_vf(
|
|
85
|
-
nd:
|
|
84
|
+
nd: NodeInitAttrMap,
|
|
86
85
|
rng: random.Random,
|
|
87
86
|
*,
|
|
88
87
|
override: bool,
|
|
@@ -118,7 +117,7 @@ def _init_vf(
|
|
|
118
117
|
|
|
119
118
|
|
|
120
119
|
def _init_si_epi(
|
|
121
|
-
nd:
|
|
120
|
+
nd: NodeInitAttrMap,
|
|
122
121
|
rng: random.Random,
|
|
123
122
|
*,
|
|
124
123
|
override: bool,
|
|
@@ -145,6 +144,8 @@ def init_node_attrs(G: "nx.Graph", *, override: bool = True) -> "nx.Graph":
|
|
|
145
144
|
Ranges for ``Si`` are added via ``INIT_SI_MIN`` and ``INIT_SI_MAX``, and
|
|
146
145
|
for ``EPI`` via ``INIT_EPI_VALUE``. If ``INIT_VF_MIN`` is greater than
|
|
147
146
|
``INIT_VF_MAX``, values are swapped and clamped to ``VF_MIN``/``VF_MAX``.
|
|
147
|
+
When clamping results in an invalid range (min > max), both bounds
|
|
148
|
+
collapse to ``VF_MIN``, ensuring ``VF_MIN``/``VF_MAX`` are hard limits.
|
|
148
149
|
"""
|
|
149
150
|
params = InitParams.from_graph(G)
|
|
150
151
|
|
|
@@ -160,12 +161,17 @@ def init_node_attrs(G: "nx.Graph", *, override: bool = True) -> "nx.Graph":
|
|
|
160
161
|
vf_uniform_min, vf_uniform_max = vf_uniform_max, vf_uniform_min
|
|
161
162
|
params.vf_uniform_min = max(vf_uniform_min, vf_min_lim)
|
|
162
163
|
params.vf_uniform_max = min(vf_uniform_max, vf_max_lim)
|
|
164
|
+
# After clamping to VF_MIN/VF_MAX, ensure min <= max
|
|
165
|
+
if params.vf_uniform_min > params.vf_uniform_max:
|
|
166
|
+
# Collapse to VF_MIN when the requested range is entirely below the limit
|
|
167
|
+
params.vf_uniform_min = params.vf_uniform_max = vf_min_lim
|
|
163
168
|
|
|
164
169
|
rng = make_rng(params.seed, -1, G)
|
|
165
170
|
for _, nd in G.nodes(data=True):
|
|
171
|
+
node_attrs = cast(NodeInitAttrMap, nd)
|
|
166
172
|
|
|
167
173
|
_init_phase(
|
|
168
|
-
|
|
174
|
+
node_attrs,
|
|
169
175
|
rng,
|
|
170
176
|
override=override,
|
|
171
177
|
random_phase=params.init_rand_phase,
|
|
@@ -173,7 +179,7 @@ def init_node_attrs(G: "nx.Graph", *, override: bool = True) -> "nx.Graph":
|
|
|
173
179
|
th_max=params.th_max,
|
|
174
180
|
)
|
|
175
181
|
_init_vf(
|
|
176
|
-
|
|
182
|
+
node_attrs,
|
|
177
183
|
rng,
|
|
178
184
|
override=override,
|
|
179
185
|
mode=params.vf_mode,
|
|
@@ -186,7 +192,7 @@ def init_node_attrs(G: "nx.Graph", *, override: bool = True) -> "nx.Graph":
|
|
|
186
192
|
clamp_to_limits=params.clamp_to_limits,
|
|
187
193
|
)
|
|
188
194
|
_init_si_epi(
|
|
189
|
-
|
|
195
|
+
node_attrs,
|
|
190
196
|
rng,
|
|
191
197
|
override=override,
|
|
192
198
|
si_min=params.si_min,
|
tnfr/initialization.pyi
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
import networkx as nx
|
|
7
|
+
|
|
8
|
+
from .types import NodeInitAttrMap
|
|
9
|
+
|
|
10
|
+
__all__: tuple[str, str] = ("InitParams", "init_node_attrs")
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class InitParams:
|
|
14
|
+
seed: int | None
|
|
15
|
+
init_rand_phase: bool
|
|
16
|
+
th_min: float
|
|
17
|
+
th_max: float
|
|
18
|
+
vf_mode: str
|
|
19
|
+
vf_min_lim: float
|
|
20
|
+
vf_max_lim: float
|
|
21
|
+
vf_uniform_min: float | None
|
|
22
|
+
vf_uniform_max: float | None
|
|
23
|
+
vf_mean: float
|
|
24
|
+
vf_std: float
|
|
25
|
+
clamp_to_limits: bool
|
|
26
|
+
si_min: float
|
|
27
|
+
si_max: float
|
|
28
|
+
epi_val: float
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def from_graph(cls, G: nx.Graph) -> InitParams: ...
|
|
32
|
+
|
|
33
|
+
def _init_phase(
|
|
34
|
+
nd: NodeInitAttrMap,
|
|
35
|
+
rng: random.Random,
|
|
36
|
+
*,
|
|
37
|
+
override: bool,
|
|
38
|
+
random_phase: bool,
|
|
39
|
+
th_min: float,
|
|
40
|
+
th_max: float,
|
|
41
|
+
) -> None: ...
|
|
42
|
+
def _init_vf(
|
|
43
|
+
nd: NodeInitAttrMap,
|
|
44
|
+
rng: random.Random,
|
|
45
|
+
*,
|
|
46
|
+
override: bool,
|
|
47
|
+
mode: str,
|
|
48
|
+
vf_uniform_min: float,
|
|
49
|
+
vf_uniform_max: float,
|
|
50
|
+
vf_mean: float,
|
|
51
|
+
vf_std: float,
|
|
52
|
+
vf_min_lim: float,
|
|
53
|
+
vf_max_lim: float,
|
|
54
|
+
clamp_to_limits: bool,
|
|
55
|
+
) -> None: ...
|
|
56
|
+
def _init_si_epi(
|
|
57
|
+
nd: NodeInitAttrMap,
|
|
58
|
+
rng: random.Random,
|
|
59
|
+
*,
|
|
60
|
+
override: bool,
|
|
61
|
+
si_min: float,
|
|
62
|
+
si_max: float,
|
|
63
|
+
epi_val: float,
|
|
64
|
+
) -> None: ...
|
|
65
|
+
def init_node_attrs(G: nx.Graph, *, override: bool = True) -> nx.Graph: ...
|