tnfr 4.5.1__py3-none-any.whl → 4.5.2__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 +91 -90
- tnfr/alias.py +546 -0
- tnfr/cache.py +578 -0
- tnfr/callback_utils.py +388 -0
- tnfr/cli/__init__.py +75 -0
- tnfr/cli/arguments.py +177 -0
- tnfr/cli/execution.py +288 -0
- tnfr/cli/utils.py +36 -0
- tnfr/collections_utils.py +300 -0
- tnfr/config.py +19 -28
- tnfr/constants/__init__.py +174 -0
- tnfr/constants/core.py +159 -0
- tnfr/constants/init.py +31 -0
- tnfr/constants/metric.py +110 -0
- tnfr/constants_glyphs.py +98 -0
- tnfr/dynamics/__init__.py +658 -0
- tnfr/dynamics/dnfr.py +733 -0
- tnfr/dynamics/integrators.py +267 -0
- tnfr/dynamics/sampling.py +31 -0
- tnfr/execution.py +201 -0
- tnfr/flatten.py +283 -0
- tnfr/gamma.py +302 -88
- tnfr/glyph_history.py +290 -0
- tnfr/grammar.py +285 -96
- tnfr/graph_utils.py +84 -0
- tnfr/helpers/__init__.py +71 -0
- tnfr/helpers/numeric.py +87 -0
- tnfr/immutable.py +178 -0
- tnfr/import_utils.py +228 -0
- tnfr/initialization.py +197 -0
- tnfr/io.py +246 -0
- tnfr/json_utils.py +162 -0
- tnfr/locking.py +37 -0
- tnfr/logging_utils.py +116 -0
- tnfr/metrics/__init__.py +41 -0
- tnfr/metrics/coherence.py +829 -0
- tnfr/metrics/common.py +151 -0
- tnfr/metrics/core.py +101 -0
- tnfr/metrics/diagnosis.py +234 -0
- tnfr/metrics/export.py +137 -0
- tnfr/metrics/glyph_timing.py +189 -0
- tnfr/metrics/reporting.py +148 -0
- tnfr/metrics/sense_index.py +120 -0
- tnfr/metrics/trig.py +181 -0
- tnfr/metrics/trig_cache.py +109 -0
- tnfr/node.py +214 -159
- tnfr/observers.py +126 -136
- tnfr/ontosim.py +134 -134
- tnfr/operators/__init__.py +420 -0
- tnfr/operators/jitter.py +203 -0
- tnfr/operators/remesh.py +485 -0
- tnfr/presets.py +46 -14
- tnfr/rng.py +254 -0
- tnfr/selector.py +210 -0
- tnfr/sense.py +284 -131
- tnfr/structural.py +207 -79
- tnfr/tokens.py +60 -0
- tnfr/trace.py +329 -94
- tnfr/types.py +43 -17
- tnfr/validators.py +70 -24
- tnfr/value_utils.py +59 -0
- tnfr-4.5.2.dist-info/METADATA +379 -0
- tnfr-4.5.2.dist-info/RECORD +67 -0
- tnfr/cli.py +0 -322
- tnfr/constants.py +0 -277
- tnfr/dynamics.py +0 -814
- tnfr/helpers.py +0 -264
- tnfr/main.py +0 -47
- tnfr/metrics.py +0 -597
- tnfr/operators.py +0 -525
- tnfr/program.py +0 -176
- tnfr/scenarios.py +0 -34
- tnfr-4.5.1.dist-info/METADATA +0 -221
- tnfr-4.5.1.dist-info/RECORD +0 -28
- {tnfr-4.5.1.dist-info → tnfr-4.5.2.dist-info}/WHEEL +0 -0
- {tnfr-4.5.1.dist-info → tnfr-4.5.2.dist-info}/entry_points.txt +0 -0
- {tnfr-4.5.1.dist-info → tnfr-4.5.2.dist-info}/licenses/LICENSE.md +0 -0
- {tnfr-4.5.1.dist-info → tnfr-4.5.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Trigonometric caches for TNFR metrics.
|
|
2
|
+
|
|
3
|
+
The cosine/sine storage helpers live here to keep :mod:`tnfr.metrics.trig`
|
|
4
|
+
focused on pure mathematical utilities (phase means, compensated sums, etc.).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import math
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import Any, Iterable, Mapping
|
|
12
|
+
|
|
13
|
+
from ..alias import get_attr
|
|
14
|
+
from ..constants import get_aliases
|
|
15
|
+
from ..cache import edge_version_cache
|
|
16
|
+
from ..import_utils import get_numpy
|
|
17
|
+
from ..types import GraphLike
|
|
18
|
+
|
|
19
|
+
ALIAS_THETA = get_aliases("THETA")
|
|
20
|
+
|
|
21
|
+
__all__ = ("TrigCache", "compute_theta_trig", "get_trig_cache", "_compute_trig_python")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(slots=True)
|
|
25
|
+
class TrigCache:
|
|
26
|
+
"""Container for cached trigonometric values per node."""
|
|
27
|
+
|
|
28
|
+
cos: dict[Any, float]
|
|
29
|
+
sin: dict[Any, float]
|
|
30
|
+
theta: dict[Any, float]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _iter_theta_pairs(
|
|
34
|
+
nodes: Iterable[tuple[Any, Mapping[str, Any] | float]],
|
|
35
|
+
) -> Iterable[tuple[Any, float]]:
|
|
36
|
+
"""Yield ``(node, θ)`` pairs from ``nodes``."""
|
|
37
|
+
|
|
38
|
+
for n, data in nodes:
|
|
39
|
+
if isinstance(data, Mapping):
|
|
40
|
+
yield n, get_attr(data, ALIAS_THETA, 0.0)
|
|
41
|
+
else:
|
|
42
|
+
yield n, float(data)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _compute_trig_python(
|
|
46
|
+
nodes: Iterable[tuple[Any, Mapping[str, Any] | float]],
|
|
47
|
+
) -> TrigCache:
|
|
48
|
+
"""Compute trigonometric mappings using pure Python."""
|
|
49
|
+
|
|
50
|
+
cos_th: dict[Any, float] = {}
|
|
51
|
+
sin_th: dict[Any, float] = {}
|
|
52
|
+
thetas: dict[Any, float] = {}
|
|
53
|
+
for n, th in _iter_theta_pairs(nodes):
|
|
54
|
+
thetas[n] = th
|
|
55
|
+
cos_th[n] = math.cos(th)
|
|
56
|
+
sin_th[n] = math.sin(th)
|
|
57
|
+
return TrigCache(cos=cos_th, sin=sin_th, theta=thetas)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def compute_theta_trig(
|
|
61
|
+
nodes: Iterable[tuple[Any, Mapping[str, Any] | float]],
|
|
62
|
+
np: Any | None = None,
|
|
63
|
+
) -> TrigCache:
|
|
64
|
+
"""Return trigonometric mappings of ``θ`` per node."""
|
|
65
|
+
|
|
66
|
+
if np is None:
|
|
67
|
+
np = get_numpy()
|
|
68
|
+
if np is None or not all(hasattr(np, attr) for attr in ("fromiter", "cos", "sin")):
|
|
69
|
+
return _compute_trig_python(nodes)
|
|
70
|
+
|
|
71
|
+
pairs = list(_iter_theta_pairs(nodes))
|
|
72
|
+
if not pairs:
|
|
73
|
+
return TrigCache(cos={}, sin={}, theta={})
|
|
74
|
+
|
|
75
|
+
node_list, theta_vals = zip(*pairs)
|
|
76
|
+
theta_arr = np.fromiter(theta_vals, dtype=float)
|
|
77
|
+
cos_arr = np.cos(theta_arr)
|
|
78
|
+
sin_arr = np.sin(theta_arr)
|
|
79
|
+
|
|
80
|
+
cos_th = dict(zip(node_list, map(float, cos_arr)))
|
|
81
|
+
sin_th = dict(zip(node_list, map(float, sin_arr)))
|
|
82
|
+
thetas = dict(zip(node_list, map(float, theta_arr)))
|
|
83
|
+
return TrigCache(cos=cos_th, sin=sin_th, theta=thetas)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _build_trig_cache(G: GraphLike, np: Any | None = None) -> TrigCache:
|
|
87
|
+
"""Construct trigonometric cache for ``G``."""
|
|
88
|
+
|
|
89
|
+
return compute_theta_trig(G.nodes(data=True), np=np)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def get_trig_cache(
|
|
93
|
+
G: GraphLike,
|
|
94
|
+
*,
|
|
95
|
+
np: Any | None = None,
|
|
96
|
+
cache_size: int | None = 128,
|
|
97
|
+
) -> TrigCache:
|
|
98
|
+
"""Return cached cosines and sines of ``θ`` per node."""
|
|
99
|
+
|
|
100
|
+
if np is None:
|
|
101
|
+
np = get_numpy()
|
|
102
|
+
version = G.graph.setdefault("_trig_version", 0)
|
|
103
|
+
key = ("_trig", version)
|
|
104
|
+
return edge_version_cache(
|
|
105
|
+
G,
|
|
106
|
+
key,
|
|
107
|
+
lambda: _build_trig_cache(G, np=np),
|
|
108
|
+
max_entries=cache_size,
|
|
109
|
+
)
|
tnfr/node.py
CHANGED
|
@@ -1,202 +1,257 @@
|
|
|
1
|
+
"""Node utilities and structures for TNFR graphs."""
|
|
2
|
+
|
|
1
3
|
from __future__ import annotations
|
|
2
|
-
from
|
|
3
|
-
from
|
|
4
|
-
|
|
4
|
+
from typing import Iterable, MutableMapping, Optional, Protocol, TypeVar
|
|
5
|
+
from collections.abc import Hashable
|
|
6
|
+
import math
|
|
7
|
+
|
|
8
|
+
from .constants import get_aliases
|
|
9
|
+
from .alias import (
|
|
10
|
+
get_attr,
|
|
11
|
+
get_attr_str,
|
|
12
|
+
set_attr,
|
|
13
|
+
set_attr_str,
|
|
14
|
+
set_vf,
|
|
15
|
+
set_dnfr,
|
|
16
|
+
set_theta,
|
|
17
|
+
)
|
|
18
|
+
from .cache import (
|
|
19
|
+
cached_node_list,
|
|
20
|
+
ensure_node_offset_map,
|
|
21
|
+
increment_edge_version,
|
|
22
|
+
)
|
|
23
|
+
from .graph_utils import supports_add_edge
|
|
24
|
+
from .locking import get_lock
|
|
25
|
+
|
|
26
|
+
ALIAS_EPI = get_aliases("EPI")
|
|
27
|
+
ALIAS_VF = get_aliases("VF")
|
|
28
|
+
ALIAS_THETA = get_aliases("THETA")
|
|
29
|
+
ALIAS_SI = get_aliases("SI")
|
|
30
|
+
ALIAS_EPI_KIND = get_aliases("EPI_KIND")
|
|
31
|
+
ALIAS_DNFR = get_aliases("DNFR")
|
|
32
|
+
ALIAS_D2EPI = get_aliases("D2EPI")
|
|
33
|
+
|
|
34
|
+
# Mapping of NodoNX attribute specifications used to generate property
|
|
35
|
+
# descriptors. Each entry defines the keyword arguments passed to
|
|
36
|
+
# ``_nx_attr_property`` for a given attribute name.
|
|
37
|
+
ATTR_SPECS: dict[str, dict] = {
|
|
38
|
+
"EPI": {"aliases": ALIAS_EPI},
|
|
39
|
+
"vf": {
|
|
40
|
+
"aliases": ALIAS_VF,
|
|
41
|
+
"setter": set_vf,
|
|
42
|
+
"use_graph_setter": True,
|
|
43
|
+
},
|
|
44
|
+
"theta": {
|
|
45
|
+
"aliases": ALIAS_THETA,
|
|
46
|
+
"setter": set_theta,
|
|
47
|
+
"use_graph_setter": True,
|
|
48
|
+
},
|
|
49
|
+
"Si": {"aliases": ALIAS_SI},
|
|
50
|
+
"epi_kind": {
|
|
51
|
+
"aliases": ALIAS_EPI_KIND,
|
|
52
|
+
"default": "",
|
|
53
|
+
"getter": get_attr_str,
|
|
54
|
+
"setter": set_attr_str,
|
|
55
|
+
"to_python": str,
|
|
56
|
+
"to_storage": str,
|
|
57
|
+
},
|
|
58
|
+
"dnfr": {
|
|
59
|
+
"aliases": ALIAS_DNFR,
|
|
60
|
+
"setter": set_dnfr,
|
|
61
|
+
"use_graph_setter": True,
|
|
62
|
+
},
|
|
63
|
+
"d2EPI": {"aliases": ALIAS_D2EPI},
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
T = TypeVar("T")
|
|
67
|
+
|
|
68
|
+
__all__ = ("NodoNX", "NodoProtocol", "add_edge")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _nx_attr_property(
|
|
72
|
+
aliases: tuple[str, ...],
|
|
73
|
+
*,
|
|
74
|
+
default=0.0,
|
|
75
|
+
getter=get_attr,
|
|
76
|
+
setter=set_attr,
|
|
77
|
+
to_python=float,
|
|
78
|
+
to_storage=float,
|
|
79
|
+
use_graph_setter=False,
|
|
80
|
+
):
|
|
81
|
+
"""Generate ``NodoNX`` property descriptors.
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
aliases:
|
|
86
|
+
Immutable tuple of aliases used to access the attribute in the
|
|
87
|
+
underlying ``networkx`` node.
|
|
88
|
+
default:
|
|
89
|
+
Value returned when the attribute is missing.
|
|
90
|
+
getter, setter:
|
|
91
|
+
Helper functions used to retrieve or store the value. ``setter`` can
|
|
92
|
+
either accept ``(mapping, aliases, value)`` or, when
|
|
93
|
+
``use_graph_setter`` is ``True``, ``(G, n, value)``.
|
|
94
|
+
to_python, to_storage:
|
|
95
|
+
Conversion helpers applied when getting or setting the value,
|
|
96
|
+
respectively.
|
|
97
|
+
use_graph_setter:
|
|
98
|
+
Whether ``setter`` expects ``(G, n, value)`` instead of
|
|
99
|
+
``(mapping, aliases, value)``.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
def fget(self) -> T:
|
|
103
|
+
return to_python(getter(self.G.nodes[self.n], aliases, default))
|
|
104
|
+
|
|
105
|
+
def fset(self, value: T) -> None:
|
|
106
|
+
value = to_storage(value)
|
|
107
|
+
if use_graph_setter:
|
|
108
|
+
setter(self.G, self.n, value)
|
|
109
|
+
else:
|
|
110
|
+
setter(self.G.nodes[self.n], aliases, value)
|
|
5
111
|
|
|
6
|
-
|
|
7
|
-
from .helpers import push_glifo
|
|
112
|
+
return property(fget, fset)
|
|
8
113
|
|
|
9
114
|
|
|
10
|
-
|
|
11
|
-
"""
|
|
115
|
+
def _add_edge_common(n1, n2, weight) -> Optional[float]:
|
|
116
|
+
"""Validate basic edge constraints.
|
|
12
117
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Si: float
|
|
17
|
-
epi_kind: str
|
|
18
|
-
dnfr: float
|
|
19
|
-
d2EPI: float
|
|
20
|
-
graph: Dict[str, object]
|
|
118
|
+
Returns the parsed weight if the edge can be added. ``None`` is returned
|
|
119
|
+
when the edge should be ignored (e.g. self-connections).
|
|
120
|
+
"""
|
|
21
121
|
|
|
22
|
-
|
|
23
|
-
|
|
122
|
+
if n1 == n2:
|
|
123
|
+
return None
|
|
24
124
|
|
|
25
|
-
|
|
26
|
-
|
|
125
|
+
weight = float(weight)
|
|
126
|
+
if not math.isfinite(weight):
|
|
127
|
+
raise ValueError("Edge weight must be a finite number")
|
|
128
|
+
if weight < 0:
|
|
129
|
+
raise ValueError("Edge weight must be non-negative")
|
|
27
130
|
|
|
28
|
-
|
|
29
|
-
...
|
|
131
|
+
return weight
|
|
30
132
|
|
|
31
|
-
def add_edge(self, other: "NodoProtocol", weight: float) -> None:
|
|
32
|
-
...
|
|
33
133
|
|
|
34
|
-
|
|
35
|
-
|
|
134
|
+
def add_edge(
|
|
135
|
+
graph,
|
|
136
|
+
n1,
|
|
137
|
+
n2,
|
|
138
|
+
weight,
|
|
139
|
+
overwrite: bool = False,
|
|
140
|
+
):
|
|
141
|
+
"""Add an edge between ``n1`` and ``n2`` in a ``networkx`` graph."""
|
|
36
142
|
|
|
37
|
-
|
|
38
|
-
|
|
143
|
+
weight = _add_edge_common(n1, n2, weight)
|
|
144
|
+
if weight is None:
|
|
145
|
+
return
|
|
39
146
|
|
|
147
|
+
if not supports_add_edge(graph):
|
|
148
|
+
raise TypeError("add_edge only supports networkx graphs")
|
|
40
149
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"""Representa un nodo TNFR autónomo."""
|
|
150
|
+
if graph.has_edge(n1, n2) and not overwrite:
|
|
151
|
+
return
|
|
44
152
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
theta: float = 0.0
|
|
48
|
-
Si: float = 0.0
|
|
49
|
-
epi_kind: str = ""
|
|
50
|
-
dnfr: float = 0.0
|
|
51
|
-
d2EPI: float = 0.0
|
|
52
|
-
graph: Dict[str, object] = field(default_factory=dict)
|
|
53
|
-
_neighbors: List["NodoTNFR"] = field(default_factory=list)
|
|
54
|
-
_hist_glifos: Deque[str] = field(default_factory=lambda: deque(maxlen=DEFAULTS.get("GLYPH_HYSTERESIS_WINDOW", 7)))
|
|
153
|
+
graph.add_edge(n1, n2, weight=weight)
|
|
154
|
+
increment_edge_version(graph)
|
|
55
155
|
|
|
56
|
-
def neighbors(self) -> Iterable["NodoTNFR"]:
|
|
57
|
-
return list(self._neighbors)
|
|
58
156
|
|
|
59
|
-
|
|
60
|
-
|
|
157
|
+
class NodoProtocol(Protocol):
|
|
158
|
+
"""Minimal protocol for TNFR nodes."""
|
|
61
159
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
160
|
+
EPI: float
|
|
161
|
+
vf: float
|
|
162
|
+
theta: float
|
|
163
|
+
Si: float
|
|
164
|
+
epi_kind: str
|
|
165
|
+
dnfr: float
|
|
166
|
+
d2EPI: float
|
|
167
|
+
graph: dict[str, object]
|
|
66
168
|
|
|
67
|
-
def
|
|
68
|
-
self._hist_glifos.append(glifo)
|
|
69
|
-
while len(self._hist_glifos) > window:
|
|
70
|
-
self._hist_glifos.popleft()
|
|
71
|
-
self.epi_kind = glifo
|
|
169
|
+
def neighbors(self) -> Iterable[NodoProtocol | Hashable]: ...
|
|
72
170
|
|
|
73
|
-
def
|
|
74
|
-
|
|
171
|
+
def _glyph_storage(self) -> MutableMapping[str, object]: ...
|
|
172
|
+
|
|
173
|
+
def has_edge(self, other: "NodoProtocol") -> bool: ...
|
|
75
174
|
|
|
76
|
-
def
|
|
77
|
-
|
|
175
|
+
def add_edge(
|
|
176
|
+
self, other: "NodoProtocol", weight: float, *, overwrite: bool = False
|
|
177
|
+
) -> None: ...
|
|
78
178
|
|
|
79
|
-
def
|
|
80
|
-
from .operators import aplicar_glifo_obj
|
|
81
|
-
aplicar_glifo_obj(self, glifo, window=window)
|
|
179
|
+
def offset(self) -> int: ...
|
|
82
180
|
|
|
83
|
-
def
|
|
84
|
-
self.EPI += self.dnfr * dt
|
|
181
|
+
def all_nodes(self) -> Iterable["NodoProtocol"]: ...
|
|
85
182
|
|
|
86
183
|
|
|
87
184
|
class NodoNX(NodoProtocol):
|
|
88
|
-
"""
|
|
185
|
+
"""Adapter for ``networkx`` nodes."""
|
|
186
|
+
|
|
187
|
+
# Statically defined property descriptors for ``NodoNX`` attributes.
|
|
188
|
+
# Declaring them here makes the attributes discoverable by type checkers
|
|
189
|
+
# and IDEs, avoiding the previous runtime ``setattr`` loop.
|
|
190
|
+
EPI: float = _nx_attr_property(**ATTR_SPECS["EPI"])
|
|
191
|
+
vf: float = _nx_attr_property(**ATTR_SPECS["vf"])
|
|
192
|
+
theta: float = _nx_attr_property(**ATTR_SPECS["theta"])
|
|
193
|
+
Si: float = _nx_attr_property(**ATTR_SPECS["Si"])
|
|
194
|
+
epi_kind: str = _nx_attr_property(**ATTR_SPECS["epi_kind"])
|
|
195
|
+
dnfr: float = _nx_attr_property(**ATTR_SPECS["dnfr"])
|
|
196
|
+
d2EPI: float = _nx_attr_property(**ATTR_SPECS["d2EPI"])
|
|
89
197
|
|
|
90
198
|
def __init__(self, G, n):
|
|
91
199
|
self.G = G
|
|
92
200
|
self.n = n
|
|
93
201
|
self.graph = G.graph
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
_set_attr(self.G.nodes[self.n], ALIAS_VF, float(v))
|
|
118
|
-
|
|
119
|
-
@property
|
|
120
|
-
def theta(self) -> float:
|
|
121
|
-
from .helpers import _get_attr
|
|
122
|
-
from .constants import ALIAS_THETA
|
|
123
|
-
return float(_get_attr(self.G.nodes[self.n], ALIAS_THETA, 0.0))
|
|
124
|
-
|
|
125
|
-
@theta.setter
|
|
126
|
-
def theta(self, v: float) -> None:
|
|
127
|
-
from .helpers import _set_attr
|
|
128
|
-
from .constants import ALIAS_THETA
|
|
129
|
-
_set_attr(self.G.nodes[self.n], ALIAS_THETA, float(v))
|
|
130
|
-
|
|
131
|
-
@property
|
|
132
|
-
def Si(self) -> float:
|
|
133
|
-
from .helpers import _get_attr
|
|
134
|
-
from .constants import ALIAS_SI
|
|
135
|
-
return float(_get_attr(self.G.nodes[self.n], ALIAS_SI, 0.0))
|
|
136
|
-
|
|
137
|
-
@Si.setter
|
|
138
|
-
def Si(self, v: float) -> None:
|
|
139
|
-
from .helpers import _set_attr
|
|
140
|
-
from .constants import ALIAS_SI
|
|
141
|
-
_set_attr(self.G.nodes[self.n], ALIAS_SI, float(v))
|
|
142
|
-
|
|
143
|
-
@property
|
|
144
|
-
def epi_kind(self) -> str:
|
|
145
|
-
from .helpers import _get_attr_str
|
|
146
|
-
from .constants import ALIAS_EPI_KIND
|
|
147
|
-
return _get_attr_str(self.G.nodes[self.n], ALIAS_EPI_KIND, "")
|
|
148
|
-
|
|
149
|
-
@epi_kind.setter
|
|
150
|
-
def epi_kind(self, v: str) -> None:
|
|
151
|
-
from .helpers import _set_attr_str
|
|
152
|
-
from .constants import ALIAS_EPI_KIND
|
|
153
|
-
_set_attr_str(self.G.nodes[self.n], ALIAS_EPI_KIND, str(v))
|
|
154
|
-
|
|
155
|
-
@property
|
|
156
|
-
def dnfr(self) -> float:
|
|
157
|
-
from .helpers import _get_attr
|
|
158
|
-
from .constants import ALIAS_DNFR
|
|
159
|
-
return float(_get_attr(self.G.nodes[self.n], ALIAS_DNFR, 0.0))
|
|
160
|
-
|
|
161
|
-
@dnfr.setter
|
|
162
|
-
def dnfr(self, v: float) -> None:
|
|
163
|
-
from .helpers import _set_attr
|
|
164
|
-
from .constants import ALIAS_DNFR
|
|
165
|
-
_set_attr(self.G.nodes[self.n], ALIAS_DNFR, float(v))
|
|
166
|
-
|
|
167
|
-
@property
|
|
168
|
-
def d2EPI(self) -> float:
|
|
169
|
-
from .helpers import _get_attr
|
|
170
|
-
from .constants import ALIAS_D2EPI
|
|
171
|
-
return float(_get_attr(self.G.nodes[self.n], ALIAS_D2EPI, 0.0))
|
|
172
|
-
|
|
173
|
-
@d2EPI.setter
|
|
174
|
-
def d2EPI(self, v: float) -> None:
|
|
175
|
-
from .helpers import _set_attr
|
|
176
|
-
from .constants import ALIAS_D2EPI
|
|
177
|
-
_set_attr(self.G.nodes[self.n], ALIAS_D2EPI, float(v))
|
|
178
|
-
|
|
179
|
-
def neighbors(self) -> Iterable[NodoProtocol]:
|
|
180
|
-
return [NodoNX(self.G, v) for v in self.G.neighbors(self.n)]
|
|
181
|
-
|
|
182
|
-
def push_glifo(self, glifo: str, window: int) -> None:
|
|
183
|
-
push_glifo(self.G.nodes[self.n], glifo, window)
|
|
184
|
-
self.epi_kind = glifo
|
|
202
|
+
G.graph.setdefault("_node_cache", {})[n] = self
|
|
203
|
+
|
|
204
|
+
def _glyph_storage(self):
|
|
205
|
+
return self.G.nodes[self.n]
|
|
206
|
+
|
|
207
|
+
@classmethod
|
|
208
|
+
def from_graph(cls, G, n):
|
|
209
|
+
"""Return cached ``NodoNX`` for ``(G, n)`` with thread safety."""
|
|
210
|
+
lock = get_lock(f"nodonx_cache_{id(G)}")
|
|
211
|
+
with lock:
|
|
212
|
+
cache = G.graph.setdefault("_node_cache", {})
|
|
213
|
+
node = cache.get(n)
|
|
214
|
+
if node is None:
|
|
215
|
+
node = cls(G, n)
|
|
216
|
+
return node
|
|
217
|
+
|
|
218
|
+
def neighbors(self) -> Iterable[Hashable]:
|
|
219
|
+
"""Iterate neighbour identifiers (IDs).
|
|
220
|
+
|
|
221
|
+
Wrap each resulting ID with :meth:`from_graph` to obtain the cached
|
|
222
|
+
``NodoNX`` instance when actual node objects are required.
|
|
223
|
+
"""
|
|
224
|
+
return self.G.neighbors(self.n)
|
|
185
225
|
|
|
186
226
|
def has_edge(self, other: NodoProtocol) -> bool:
|
|
187
227
|
if isinstance(other, NodoNX):
|
|
188
228
|
return self.G.has_edge(self.n, other.n)
|
|
189
229
|
raise NotImplementedError
|
|
190
230
|
|
|
191
|
-
def add_edge(
|
|
231
|
+
def add_edge(
|
|
232
|
+
self, other: NodoProtocol, weight: float, *, overwrite: bool = False
|
|
233
|
+
) -> None:
|
|
192
234
|
if isinstance(other, NodoNX):
|
|
193
|
-
|
|
235
|
+
add_edge(
|
|
236
|
+
self.G,
|
|
237
|
+
self.n,
|
|
238
|
+
other.n,
|
|
239
|
+
weight,
|
|
240
|
+
overwrite,
|
|
241
|
+
)
|
|
194
242
|
else:
|
|
195
243
|
raise NotImplementedError
|
|
196
244
|
|
|
197
245
|
def offset(self) -> int:
|
|
198
|
-
|
|
199
|
-
return
|
|
246
|
+
mapping = ensure_node_offset_map(self.G)
|
|
247
|
+
return mapping.get(self.n, 0)
|
|
200
248
|
|
|
201
249
|
def all_nodes(self) -> Iterable[NodoProtocol]:
|
|
202
|
-
|
|
250
|
+
override = self.graph.get("_all_nodes")
|
|
251
|
+
if override is not None:
|
|
252
|
+
return override
|
|
253
|
+
|
|
254
|
+
nodes = cached_node_list(self.G)
|
|
255
|
+
return tuple(NodoNX.from_graph(self.G, v) for v in nodes)
|
|
256
|
+
|
|
257
|
+
|