tnfr 4.5.2__py3-none-any.whl → 6.0.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.
- tnfr/__init__.py +228 -49
- tnfr/__init__.pyi +40 -0
- tnfr/_compat.py +11 -0
- tnfr/_version.py +7 -0
- tnfr/_version.pyi +7 -0
- tnfr/alias.py +106 -21
- tnfr/alias.pyi +140 -0
- tnfr/cache.py +666 -512
- tnfr/cache.pyi +232 -0
- tnfr/callback_utils.py +2 -9
- tnfr/callback_utils.pyi +105 -0
- tnfr/cli/__init__.py +21 -7
- tnfr/cli/__init__.pyi +47 -0
- tnfr/cli/arguments.py +42 -20
- tnfr/cli/arguments.pyi +33 -0
- tnfr/cli/execution.py +54 -20
- tnfr/cli/execution.pyi +80 -0
- tnfr/cli/utils.py +0 -2
- tnfr/cli/utils.pyi +8 -0
- tnfr/config/__init__.py +12 -0
- tnfr/config/__init__.pyi +8 -0
- tnfr/config/constants.py +104 -0
- tnfr/config/constants.pyi +12 -0
- tnfr/{config.py → config/init.py} +11 -7
- tnfr/config/init.pyi +8 -0
- tnfr/config/operator_names.py +106 -0
- tnfr/config/operator_names.pyi +28 -0
- tnfr/config/presets.py +104 -0
- tnfr/config/presets.pyi +7 -0
- tnfr/constants/__init__.py +78 -24
- tnfr/constants/__init__.pyi +104 -0
- tnfr/constants/core.py +1 -2
- tnfr/constants/core.pyi +17 -0
- tnfr/constants/init.pyi +12 -0
- tnfr/constants/metric.py +4 -12
- tnfr/constants/metric.pyi +19 -0
- tnfr/constants_glyphs.py +9 -91
- tnfr/constants_glyphs.pyi +12 -0
- tnfr/dynamics/__init__.py +112 -634
- tnfr/dynamics/__init__.pyi +83 -0
- tnfr/dynamics/adaptation.py +201 -0
- tnfr/dynamics/aliases.py +22 -0
- tnfr/dynamics/coordination.py +343 -0
- tnfr/dynamics/dnfr.py +1936 -354
- tnfr/dynamics/dnfr.pyi +33 -0
- tnfr/dynamics/integrators.py +369 -75
- tnfr/dynamics/integrators.pyi +35 -0
- tnfr/dynamics/runtime.py +521 -0
- tnfr/dynamics/sampling.py +8 -5
- tnfr/dynamics/sampling.pyi +7 -0
- tnfr/dynamics/selectors.py +680 -0
- tnfr/execution.py +56 -41
- tnfr/execution.pyi +65 -0
- tnfr/flatten.py +7 -7
- tnfr/flatten.pyi +28 -0
- tnfr/gamma.py +54 -37
- tnfr/gamma.pyi +40 -0
- tnfr/glyph_history.py +85 -38
- tnfr/glyph_history.pyi +53 -0
- tnfr/grammar.py +19 -338
- tnfr/grammar.pyi +13 -0
- tnfr/helpers/__init__.py +110 -30
- tnfr/helpers/__init__.pyi +66 -0
- tnfr/helpers/numeric.py +1 -0
- tnfr/helpers/numeric.pyi +12 -0
- tnfr/immutable.py +55 -19
- tnfr/immutable.pyi +37 -0
- tnfr/initialization.py +12 -10
- tnfr/initialization.pyi +73 -0
- tnfr/io.py +99 -34
- tnfr/io.pyi +11 -0
- tnfr/locking.pyi +7 -0
- tnfr/metrics/__init__.pyi +20 -0
- tnfr/metrics/coherence.py +934 -294
- tnfr/metrics/common.py +1 -3
- tnfr/metrics/common.pyi +15 -0
- tnfr/metrics/core.py +192 -34
- tnfr/metrics/core.pyi +13 -0
- tnfr/metrics/diagnosis.py +707 -101
- tnfr/metrics/diagnosis.pyi +89 -0
- tnfr/metrics/export.py +27 -13
- tnfr/metrics/glyph_timing.py +218 -38
- tnfr/metrics/reporting.py +22 -18
- tnfr/metrics/reporting.pyi +12 -0
- tnfr/metrics/sense_index.py +199 -25
- tnfr/metrics/sense_index.pyi +9 -0
- tnfr/metrics/trig.py +53 -18
- tnfr/metrics/trig.pyi +12 -0
- tnfr/metrics/trig_cache.py +3 -7
- tnfr/metrics/trig_cache.pyi +10 -0
- tnfr/node.py +148 -125
- tnfr/node.pyi +161 -0
- tnfr/observers.py +44 -30
- tnfr/observers.pyi +46 -0
- tnfr/ontosim.py +14 -13
- tnfr/ontosim.pyi +33 -0
- tnfr/operators/__init__.py +84 -52
- tnfr/operators/__init__.pyi +31 -0
- tnfr/operators/definitions.py +181 -0
- tnfr/operators/definitions.pyi +92 -0
- tnfr/operators/jitter.py +86 -23
- tnfr/operators/jitter.pyi +11 -0
- tnfr/operators/registry.py +80 -0
- tnfr/operators/registry.pyi +15 -0
- tnfr/operators/remesh.py +141 -57
- tnfr/presets.py +9 -54
- tnfr/presets.pyi +7 -0
- tnfr/py.typed +0 -0
- tnfr/rng.py +259 -73
- tnfr/rng.pyi +14 -0
- tnfr/selector.py +24 -17
- tnfr/selector.pyi +19 -0
- tnfr/sense.py +55 -43
- tnfr/sense.pyi +30 -0
- tnfr/structural.py +44 -267
- tnfr/structural.pyi +46 -0
- tnfr/telemetry/__init__.py +13 -0
- tnfr/telemetry/verbosity.py +37 -0
- tnfr/tokens.py +3 -2
- tnfr/tokens.pyi +41 -0
- tnfr/trace.py +272 -82
- tnfr/trace.pyi +68 -0
- tnfr/types.py +345 -6
- tnfr/types.pyi +145 -0
- tnfr/utils/__init__.py +158 -0
- tnfr/utils/__init__.pyi +133 -0
- tnfr/utils/cache.py +755 -0
- tnfr/utils/cache.pyi +156 -0
- tnfr/{collections_utils.py → utils/data.py} +57 -90
- tnfr/utils/data.pyi +73 -0
- tnfr/utils/graph.py +87 -0
- tnfr/utils/graph.pyi +10 -0
- tnfr/utils/init.py +746 -0
- tnfr/utils/init.pyi +85 -0
- tnfr/{json_utils.py → utils/io.py} +13 -18
- tnfr/utils/io.pyi +10 -0
- tnfr/utils/validators.py +130 -0
- tnfr/utils/validators.pyi +19 -0
- tnfr/validation/__init__.py +25 -0
- tnfr/validation/__init__.pyi +17 -0
- tnfr/validation/compatibility.py +59 -0
- tnfr/validation/compatibility.pyi +8 -0
- tnfr/validation/grammar.py +149 -0
- tnfr/validation/grammar.pyi +11 -0
- tnfr/validation/rules.py +194 -0
- tnfr/validation/rules.pyi +18 -0
- tnfr/validation/syntax.py +151 -0
- tnfr/validation/syntax.pyi +7 -0
- tnfr-6.0.0.dist-info/METADATA +135 -0
- tnfr-6.0.0.dist-info/RECORD +157 -0
- tnfr/graph_utils.py +0 -84
- tnfr/import_utils.py +0 -228
- tnfr/logging_utils.py +0 -116
- 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-6.0.0.dist-info}/WHEEL +0 -0
- {tnfr-4.5.2.dist-info → tnfr-6.0.0.dist-info}/entry_points.txt +0 -0
- {tnfr-4.5.2.dist-info → tnfr-6.0.0.dist-info}/licenses/LICENSE.md +0 -0
- {tnfr-4.5.2.dist-info → tnfr-6.0.0.dist-info}/top_level.txt +0 -0
tnfr/helpers/__init__.py
CHANGED
|
@@ -7,19 +7,29 @@ cache invalidation.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
from
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
10
|
+
from collections import Counter
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Callable, Mapping, MutableMapping, Protocol, cast
|
|
12
|
+
|
|
13
|
+
from ..types import TNFRGraph
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING: # pragma: no cover - import-time only for typing
|
|
16
|
+
from ..utils import (
|
|
17
|
+
CacheManager,
|
|
18
|
+
EdgeCacheManager,
|
|
19
|
+
cached_node_list,
|
|
20
|
+
cached_nodes_and_A,
|
|
21
|
+
edge_version_cache,
|
|
22
|
+
edge_version_update,
|
|
23
|
+
ensure_node_index_map,
|
|
24
|
+
ensure_node_offset_map,
|
|
25
|
+
get_graph,
|
|
26
|
+
get_graph_mapping,
|
|
27
|
+
increment_edge_version,
|
|
28
|
+
mark_dnfr_prep_dirty,
|
|
29
|
+
node_set_checksum,
|
|
30
|
+
stable_json,
|
|
31
|
+
)
|
|
32
|
+
from ..glyph_history import HistoryDict
|
|
23
33
|
from .numeric import (
|
|
24
34
|
angle_diff,
|
|
25
35
|
clamp,
|
|
@@ -27,17 +37,8 @@ from .numeric import (
|
|
|
27
37
|
kahan_sum_nd,
|
|
28
38
|
)
|
|
29
39
|
|
|
30
|
-
|
|
31
|
-
def __getattr__(name: str):
|
|
32
|
-
if name in _GLYPH_HISTORY_EXPORTS:
|
|
33
|
-
from .. import glyph_history as _glyph_history
|
|
34
|
-
|
|
35
|
-
value = getattr(_glyph_history, name)
|
|
36
|
-
globals()[name] = value
|
|
37
|
-
return value
|
|
38
|
-
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
39
|
-
|
|
40
40
|
__all__ = (
|
|
41
|
+
"CacheManager",
|
|
41
42
|
"EdgeCacheManager",
|
|
42
43
|
"angle_diff",
|
|
43
44
|
"cached_node_list",
|
|
@@ -60,12 +61,91 @@ __all__ = (
|
|
|
60
61
|
"last_glyph",
|
|
61
62
|
"push_glyph",
|
|
62
63
|
"recent_glyph",
|
|
64
|
+
"__getattr__",
|
|
63
65
|
)
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
|
|
67
|
+
|
|
68
|
+
_UTIL_EXPORTS = {
|
|
69
|
+
"CacheManager",
|
|
70
|
+
"EdgeCacheManager",
|
|
71
|
+
"cached_node_list",
|
|
72
|
+
"cached_nodes_and_A",
|
|
73
|
+
"edge_version_cache",
|
|
74
|
+
"edge_version_update",
|
|
75
|
+
"ensure_node_index_map",
|
|
76
|
+
"ensure_node_offset_map",
|
|
77
|
+
"get_graph",
|
|
78
|
+
"get_graph_mapping",
|
|
79
|
+
"increment_edge_version",
|
|
80
|
+
"mark_dnfr_prep_dirty",
|
|
81
|
+
"node_set_checksum",
|
|
82
|
+
"stable_json",
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def __getattr__(name: str) -> Any: # pragma: no cover - simple delegation
|
|
87
|
+
if name in _UTIL_EXPORTS:
|
|
88
|
+
from .. import utils as _utils
|
|
89
|
+
|
|
90
|
+
value = getattr(_utils, name)
|
|
91
|
+
globals()[name] = value
|
|
92
|
+
return value
|
|
93
|
+
raise AttributeError(name)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def __dir__() -> list[str]: # pragma: no cover - simple reflection
|
|
97
|
+
return sorted(set(__all__))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class _PushGlyphCallable(Protocol):
|
|
101
|
+
def __call__(self, nd: MutableMapping[str, Any], glyph: str, window: int) -> None:
|
|
102
|
+
...
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class _RecentGlyphCallable(Protocol):
|
|
106
|
+
def __call__(self, nd: MutableMapping[str, Any], glyph: str, window: int) -> bool:
|
|
107
|
+
...
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class _EnsureHistoryCallable(Protocol):
|
|
111
|
+
def __call__(self, G: TNFRGraph) -> "HistoryDict | dict[str, Any]":
|
|
112
|
+
...
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class _LastGlyphCallable(Protocol):
|
|
116
|
+
def __call__(self, nd: Mapping[str, Any]) -> str | None:
|
|
117
|
+
...
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class _CountGlyphsCallable(Protocol):
|
|
121
|
+
def __call__(
|
|
122
|
+
self, G: TNFRGraph, window: int | None = ..., *, last_only: bool = ...
|
|
123
|
+
) -> Counter[str]:
|
|
124
|
+
...
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _glyph_history_proxy(name: str) -> Callable[..., Any]:
|
|
128
|
+
"""Return a wrapper that delegates to :mod:`tnfr.glyph_history` lazily."""
|
|
129
|
+
|
|
130
|
+
target: dict[str, Callable[..., Any] | None] = {"func": None}
|
|
131
|
+
|
|
132
|
+
def _call(*args: Any, **kwargs: Any) -> Any:
|
|
133
|
+
func = target["func"]
|
|
134
|
+
if func is None:
|
|
135
|
+
from .. import glyph_history as _glyph_history
|
|
136
|
+
|
|
137
|
+
func = getattr(_glyph_history, name)
|
|
138
|
+
target["func"] = func
|
|
139
|
+
return func(*args, **kwargs)
|
|
140
|
+
|
|
141
|
+
_call.__name__ = name
|
|
142
|
+
_call.__qualname__ = name
|
|
143
|
+
_call.__doc__ = f"Proxy for :func:`tnfr.glyph_history.{name}`."
|
|
144
|
+
return _call
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
count_glyphs = cast(_CountGlyphsCallable, _glyph_history_proxy("count_glyphs"))
|
|
148
|
+
ensure_history = cast(_EnsureHistoryCallable, _glyph_history_proxy("ensure_history"))
|
|
149
|
+
last_glyph = cast(_LastGlyphCallable, _glyph_history_proxy("last_glyph"))
|
|
150
|
+
push_glyph = cast(_PushGlyphCallable, _glyph_history_proxy("push_glyph"))
|
|
151
|
+
recent_glyph = cast(_RecentGlyphCallable, _glyph_history_proxy("recent_glyph"))
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from ..cache import CacheManager as CacheManager
|
|
6
|
+
from ..glyph_history import (
|
|
7
|
+
HistoryDict,
|
|
8
|
+
count_glyphs as count_glyphs,
|
|
9
|
+
ensure_history as ensure_history,
|
|
10
|
+
last_glyph as last_glyph,
|
|
11
|
+
push_glyph as push_glyph,
|
|
12
|
+
recent_glyph as recent_glyph,
|
|
13
|
+
)
|
|
14
|
+
from ..utils.cache import (
|
|
15
|
+
EdgeCacheManager as EdgeCacheManager,
|
|
16
|
+
cached_node_list as cached_node_list,
|
|
17
|
+
cached_nodes_and_A as cached_nodes_and_A,
|
|
18
|
+
edge_version_cache as edge_version_cache,
|
|
19
|
+
edge_version_update as edge_version_update,
|
|
20
|
+
ensure_node_index_map as ensure_node_index_map,
|
|
21
|
+
ensure_node_offset_map as ensure_node_offset_map,
|
|
22
|
+
node_set_checksum as node_set_checksum,
|
|
23
|
+
stable_json as stable_json,
|
|
24
|
+
)
|
|
25
|
+
from ..utils.graph import (
|
|
26
|
+
get_graph as get_graph,
|
|
27
|
+
get_graph_mapping as get_graph_mapping,
|
|
28
|
+
increment_edge_version as increment_edge_version,
|
|
29
|
+
mark_dnfr_prep_dirty as mark_dnfr_prep_dirty,
|
|
30
|
+
)
|
|
31
|
+
from .numeric import (
|
|
32
|
+
angle_diff as angle_diff,
|
|
33
|
+
clamp as clamp,
|
|
34
|
+
clamp01 as clamp01,
|
|
35
|
+
kahan_sum_nd as kahan_sum_nd,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
__all__ = (
|
|
39
|
+
"CacheManager",
|
|
40
|
+
"EdgeCacheManager",
|
|
41
|
+
"angle_diff",
|
|
42
|
+
"cached_node_list",
|
|
43
|
+
"cached_nodes_and_A",
|
|
44
|
+
"clamp",
|
|
45
|
+
"clamp01",
|
|
46
|
+
"edge_version_cache",
|
|
47
|
+
"edge_version_update",
|
|
48
|
+
"ensure_node_index_map",
|
|
49
|
+
"ensure_node_offset_map",
|
|
50
|
+
"get_graph",
|
|
51
|
+
"get_graph_mapping",
|
|
52
|
+
"increment_edge_version",
|
|
53
|
+
"kahan_sum_nd",
|
|
54
|
+
"mark_dnfr_prep_dirty",
|
|
55
|
+
"node_set_checksum",
|
|
56
|
+
"stable_json",
|
|
57
|
+
"count_glyphs",
|
|
58
|
+
"ensure_history",
|
|
59
|
+
"last_glyph",
|
|
60
|
+
"push_glyph",
|
|
61
|
+
"recent_glyph",
|
|
62
|
+
"__getattr__",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def __getattr__(name: str) -> Any: ...
|
tnfr/helpers/numeric.py
CHANGED
tnfr/helpers/numeric.pyi
ADDED
tnfr/immutable.py
CHANGED
|
@@ -9,21 +9,44 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
from contextlib import contextmanager
|
|
11
11
|
from dataclasses import asdict, is_dataclass
|
|
12
|
-
from functools import lru_cache, singledispatch, wraps
|
|
13
|
-
from typing import Any, Callable
|
|
12
|
+
from functools import lru_cache, partial, singledispatch, wraps
|
|
13
|
+
from typing import Any, Callable, Iterable, Iterator, cast
|
|
14
14
|
from collections.abc import Mapping
|
|
15
15
|
from types import MappingProxyType
|
|
16
16
|
import threading
|
|
17
17
|
import weakref
|
|
18
18
|
|
|
19
|
+
from ._compat import TypeAlias
|
|
20
|
+
|
|
19
21
|
# Types considered immutable without further inspection
|
|
20
22
|
IMMUTABLE_SIMPLE = frozenset(
|
|
21
23
|
{int, float, complex, str, bool, bytes, type(None)}
|
|
22
24
|
)
|
|
23
25
|
|
|
24
26
|
|
|
27
|
+
FrozenPrimitive: TypeAlias = int | float | complex | str | bool | bytes | None
|
|
28
|
+
"""Primitive immutable values handled directly by :func:`_freeze`."""
|
|
29
|
+
|
|
30
|
+
FrozenCollectionItems: TypeAlias = tuple["FrozenSnapshot", ...]
|
|
31
|
+
"""Frozen representation for generic iterables."""
|
|
32
|
+
|
|
33
|
+
FrozenMappingItems: TypeAlias = tuple[tuple[Any, "FrozenSnapshot"], ...]
|
|
34
|
+
"""Frozen representation for mapping ``items()`` snapshots."""
|
|
35
|
+
|
|
36
|
+
FrozenTaggedCollection: TypeAlias = tuple[str, FrozenCollectionItems]
|
|
37
|
+
"""Tagged iterable snapshot identifying the original container type."""
|
|
38
|
+
|
|
39
|
+
FrozenTaggedMapping: TypeAlias = tuple[str, FrozenMappingItems]
|
|
40
|
+
"""Tagged mapping snapshot identifying the original mapping flavour."""
|
|
41
|
+
|
|
42
|
+
FrozenSnapshot: TypeAlias = (
|
|
43
|
+
FrozenPrimitive | FrozenCollectionItems | FrozenTaggedCollection | FrozenTaggedMapping
|
|
44
|
+
)
|
|
45
|
+
"""Union describing the immutable snapshot returned by :func:`_freeze`."""
|
|
46
|
+
|
|
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(
|
|
63
|
+
def _check_cycle(
|
|
64
|
+
func: Callable[[Any, set[int] | None], FrozenSnapshot]
|
|
65
|
+
) -> Callable[[Any, set[int] | None], FrozenSnapshot]:
|
|
41
66
|
"""Decorator applying :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,27 @@ def _freeze(value: Any, seen: set[int] | None = None):
|
|
|
69
95
|
|
|
70
96
|
@_freeze.register(tuple)
|
|
71
97
|
@_check_cycle
|
|
72
|
-
def _freeze_tuple(value: tuple, seen: set[int] | None = None): # noqa: F401
|
|
98
|
+
def _freeze_tuple(value: tuple[Any, ...], seen: set[int] | None = None) -> FrozenCollectionItems: # noqa: F401
|
|
99
|
+
assert seen is not None
|
|
73
100
|
return tuple(_freeze(v, seen) for v in value)
|
|
74
101
|
|
|
75
102
|
|
|
76
|
-
def _freeze_iterable(
|
|
103
|
+
def _freeze_iterable(
|
|
104
|
+
container: Iterable[Any], tag: str, seen: set[int]
|
|
105
|
+
) -> FrozenTaggedCollection:
|
|
77
106
|
return (tag, tuple(_freeze(v, seen) for v in container))
|
|
78
107
|
|
|
79
108
|
|
|
80
109
|
def _freeze_iterable_with_tag(
|
|
81
|
-
value: Any, seen: set[int] | None = None, *, tag: str
|
|
82
|
-
) ->
|
|
110
|
+
value: Iterable[Any], seen: set[int] | None = None, *, tag: str
|
|
111
|
+
) -> FrozenTaggedCollection:
|
|
112
|
+
assert seen is not None
|
|
83
113
|
return _freeze_iterable(value, tag, seen)
|
|
84
114
|
|
|
85
115
|
|
|
86
116
|
def _register_iterable(cls: type, tag: str) -> None:
|
|
87
|
-
|
|
117
|
+
handler = _check_cycle(partial(_freeze_iterable_with_tag, tag=tag))
|
|
118
|
+
_freeze.register(cls)(cast(Callable[[Any, set[int] | None], FrozenSnapshot], handler))
|
|
88
119
|
|
|
89
120
|
|
|
90
121
|
for _cls, _tag in (
|
|
@@ -98,17 +129,22 @@ for _cls, _tag in (
|
|
|
98
129
|
|
|
99
130
|
@_freeze.register(Mapping)
|
|
100
131
|
@_check_cycle
|
|
101
|
-
def _freeze_mapping(
|
|
132
|
+
def _freeze_mapping(
|
|
133
|
+
value: Mapping[Any, Any], seen: set[int] | None = None
|
|
134
|
+
) -> FrozenTaggedMapping: # noqa: F401
|
|
135
|
+
assert seen is not None
|
|
102
136
|
tag = "dict" if hasattr(value, "__setitem__") else "mapping"
|
|
103
137
|
return (tag, tuple((k, _freeze(v, seen)) for k, v in value.items()))
|
|
104
138
|
|
|
105
139
|
|
|
106
|
-
def _all_immutable(iterable) -> bool:
|
|
140
|
+
def _all_immutable(iterable: Iterable[Any]) -> bool:
|
|
107
141
|
return all(_is_immutable_inner(v) for v in iterable)
|
|
108
142
|
|
|
109
143
|
|
|
110
144
|
# Dispatch table kept immutable to avoid accidental mutation.
|
|
111
|
-
|
|
145
|
+
ImmutableTagHandler: TypeAlias = Callable[[tuple[Any, ...]], bool]
|
|
146
|
+
|
|
147
|
+
_IMMUTABLE_TAG_DISPATCH: Mapping[str, ImmutableTagHandler] = MappingProxyType(
|
|
112
148
|
{
|
|
113
149
|
"mapping": lambda v: _all_immutable(v[1]),
|
|
114
150
|
"frozenset": lambda v: _all_immutable(v[1]),
|
|
@@ -127,7 +163,7 @@ def _is_immutable_inner(value: Any) -> bool:
|
|
|
127
163
|
|
|
128
164
|
|
|
129
165
|
@_is_immutable_inner.register(tuple)
|
|
130
|
-
def _is_immutable_inner_tuple(value: tuple) -> bool: # noqa: F401
|
|
166
|
+
def _is_immutable_inner_tuple(value: tuple[Any, ...]) -> bool: # noqa: F401
|
|
131
167
|
if value and isinstance(value[0], str):
|
|
132
168
|
handler = _IMMUTABLE_TAG_DISPATCH.get(value[0])
|
|
133
169
|
if handler is not None:
|
|
@@ -136,7 +172,7 @@ def _is_immutable_inner_tuple(value: tuple) -> bool: # noqa: F401
|
|
|
136
172
|
|
|
137
173
|
|
|
138
174
|
@_is_immutable_inner.register(frozenset)
|
|
139
|
-
def _is_immutable_inner_frozenset(value: frozenset) -> bool: # noqa: F401
|
|
175
|
+
def _is_immutable_inner_frozenset(value: frozenset[Any]) -> bool: # noqa: F401
|
|
140
176
|
return _all_immutable(value)
|
|
141
177
|
|
|
142
178
|
|
tnfr/immutable.pyi
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from typing import Any, Callable, Iterator, Mapping
|
|
2
|
+
|
|
3
|
+
from ._compat import TypeAlias
|
|
4
|
+
|
|
5
|
+
FrozenPrimitive: TypeAlias = int | float | complex | str | bool | bytes | None
|
|
6
|
+
FrozenCollectionItems: TypeAlias = tuple["FrozenSnapshot", ...]
|
|
7
|
+
FrozenMappingItems: TypeAlias = tuple[tuple[Any, "FrozenSnapshot"], ...]
|
|
8
|
+
FrozenTaggedCollection: TypeAlias = tuple[str, FrozenCollectionItems]
|
|
9
|
+
FrozenTaggedMapping: TypeAlias = tuple[str, FrozenMappingItems]
|
|
10
|
+
FrozenSnapshot: TypeAlias = (
|
|
11
|
+
FrozenPrimitive | FrozenCollectionItems | FrozenTaggedCollection | FrozenTaggedMapping
|
|
12
|
+
)
|
|
13
|
+
ImmutableTagHandler: TypeAlias = Callable[[tuple[Any, ...]], bool]
|
|
14
|
+
|
|
15
|
+
__all__: tuple[str, ...]
|
|
16
|
+
|
|
17
|
+
def __getattr__(name: str) -> Any: ...
|
|
18
|
+
|
|
19
|
+
def _cycle_guard(value: Any, seen: set[int] | None = ...) -> Iterator[set[int]]: ...
|
|
20
|
+
|
|
21
|
+
def _check_cycle(
|
|
22
|
+
func: Callable[[Any, set[int] | None], FrozenSnapshot],
|
|
23
|
+
) -> Callable[[Any, set[int] | None], FrozenSnapshot]: ...
|
|
24
|
+
|
|
25
|
+
def _freeze(value: Any, seen: set[int] | None = ...) -> FrozenSnapshot: ...
|
|
26
|
+
|
|
27
|
+
def _freeze_mapping(
|
|
28
|
+
value: Mapping[Any, Any],
|
|
29
|
+
seen: set[int] | None = ...,
|
|
30
|
+
) -> FrozenTaggedMapping: ...
|
|
31
|
+
|
|
32
|
+
def _is_immutable(value: Any) -> bool: ...
|
|
33
|
+
|
|
34
|
+
def _is_immutable_inner(value: Any) -> bool: ...
|
|
35
|
+
|
|
36
|
+
_IMMUTABLE_CACHE: Any
|
|
37
|
+
_IMMUTABLE_TAG_DISPATCH: Mapping[str, ImmutableTagHandler]
|
tnfr/initialization.py
CHANGED
|
@@ -2,23 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
import random
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
5
|
+
from typing import TYPE_CHECKING, cast
|
|
6
6
|
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
|
|
9
9
|
from .constants import VF_KEY, THETA_KEY, get_graph_param
|
|
10
10
|
from .helpers.numeric 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),
|
|
@@ -62,7 +63,7 @@ class InitParams:
|
|
|
62
63
|
|
|
63
64
|
|
|
64
65
|
def _init_phase(
|
|
65
|
-
nd:
|
|
66
|
+
nd: NodeInitAttrMap,
|
|
66
67
|
rng: random.Random,
|
|
67
68
|
*,
|
|
68
69
|
override: bool,
|
|
@@ -82,7 +83,7 @@ def _init_phase(
|
|
|
82
83
|
|
|
83
84
|
|
|
84
85
|
def _init_vf(
|
|
85
|
-
nd:
|
|
86
|
+
nd: NodeInitAttrMap,
|
|
86
87
|
rng: random.Random,
|
|
87
88
|
*,
|
|
88
89
|
override: bool,
|
|
@@ -118,7 +119,7 @@ def _init_vf(
|
|
|
118
119
|
|
|
119
120
|
|
|
120
121
|
def _init_si_epi(
|
|
121
|
-
nd:
|
|
122
|
+
nd: NodeInitAttrMap,
|
|
122
123
|
rng: random.Random,
|
|
123
124
|
*,
|
|
124
125
|
override: bool,
|
|
@@ -163,9 +164,10 @@ def init_node_attrs(G: "nx.Graph", *, override: bool = True) -> "nx.Graph":
|
|
|
163
164
|
|
|
164
165
|
rng = make_rng(params.seed, -1, G)
|
|
165
166
|
for _, nd in G.nodes(data=True):
|
|
167
|
+
node_attrs = cast(NodeInitAttrMap, nd)
|
|
166
168
|
|
|
167
169
|
_init_phase(
|
|
168
|
-
|
|
170
|
+
node_attrs,
|
|
169
171
|
rng,
|
|
170
172
|
override=override,
|
|
171
173
|
random_phase=params.init_rand_phase,
|
|
@@ -173,7 +175,7 @@ def init_node_attrs(G: "nx.Graph", *, override: bool = True) -> "nx.Graph":
|
|
|
173
175
|
th_max=params.th_max,
|
|
174
176
|
)
|
|
175
177
|
_init_vf(
|
|
176
|
-
|
|
178
|
+
node_attrs,
|
|
177
179
|
rng,
|
|
178
180
|
override=override,
|
|
179
181
|
mode=params.vf_mode,
|
|
@@ -186,7 +188,7 @@ def init_node_attrs(G: "nx.Graph", *, override: bool = True) -> "nx.Graph":
|
|
|
186
188
|
clamp_to_limits=params.clamp_to_limits,
|
|
187
189
|
)
|
|
188
190
|
_init_si_epi(
|
|
189
|
-
|
|
191
|
+
node_attrs,
|
|
190
192
|
rng,
|
|
191
193
|
override=override,
|
|
192
194
|
si_min=params.si_min,
|
tnfr/initialization.pyi
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
import random
|
|
5
|
+
|
|
6
|
+
import networkx as nx
|
|
7
|
+
|
|
8
|
+
from .types import NodeInitAttrMap
|
|
9
|
+
|
|
10
|
+
__all__: tuple[str, str] = ("InitParams", "init_node_attrs")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class InitParams:
|
|
15
|
+
seed: int | None
|
|
16
|
+
init_rand_phase: bool
|
|
17
|
+
th_min: float
|
|
18
|
+
th_max: float
|
|
19
|
+
vf_mode: str
|
|
20
|
+
vf_min_lim: float
|
|
21
|
+
vf_max_lim: float
|
|
22
|
+
vf_uniform_min: float | None
|
|
23
|
+
vf_uniform_max: float | None
|
|
24
|
+
vf_mean: float
|
|
25
|
+
vf_std: float
|
|
26
|
+
clamp_to_limits: bool
|
|
27
|
+
si_min: float
|
|
28
|
+
si_max: float
|
|
29
|
+
epi_val: float
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def from_graph(cls, G: nx.Graph) -> InitParams: ...
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _init_phase(
|
|
36
|
+
nd: NodeInitAttrMap,
|
|
37
|
+
rng: random.Random,
|
|
38
|
+
*,
|
|
39
|
+
override: bool,
|
|
40
|
+
random_phase: bool,
|
|
41
|
+
th_min: float,
|
|
42
|
+
th_max: float,
|
|
43
|
+
) -> None: ...
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _init_vf(
|
|
47
|
+
nd: NodeInitAttrMap,
|
|
48
|
+
rng: random.Random,
|
|
49
|
+
*,
|
|
50
|
+
override: bool,
|
|
51
|
+
mode: str,
|
|
52
|
+
vf_uniform_min: float,
|
|
53
|
+
vf_uniform_max: float,
|
|
54
|
+
vf_mean: float,
|
|
55
|
+
vf_std: float,
|
|
56
|
+
vf_min_lim: float,
|
|
57
|
+
vf_max_lim: float,
|
|
58
|
+
clamp_to_limits: bool,
|
|
59
|
+
) -> None: ...
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _init_si_epi(
|
|
63
|
+
nd: NodeInitAttrMap,
|
|
64
|
+
rng: random.Random,
|
|
65
|
+
*,
|
|
66
|
+
override: bool,
|
|
67
|
+
si_min: float,
|
|
68
|
+
si_max: float,
|
|
69
|
+
epi_val: float,
|
|
70
|
+
) -> None: ...
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def init_node_attrs(G: nx.Graph, *, override: bool = True) -> nx.Graph: ...
|