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/__init__.py
CHANGED
|
@@ -4,88 +4,267 @@ This package only re-exports a handful of high level helpers. Most
|
|
|
4
4
|
functionality lives in submodules that should be imported directly, for
|
|
5
5
|
example :mod:`tnfr.metrics`, :mod:`tnfr.observers` or the DSL utilities
|
|
6
6
|
in :mod:`tnfr.tokens`, :mod:`tnfr.flatten` and :mod:`tnfr.execution`.
|
|
7
|
-
Recommended entry points are:
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
-
|
|
8
|
+
Exported helpers and their dependencies
|
|
9
|
+
---------------------------------------
|
|
10
|
+
The :data:`EXPORT_DEPENDENCIES` mapping enumerates which internal
|
|
11
|
+
submodules and third-party packages are required to load each helper.
|
|
12
|
+
The imports are grouped as follows:
|
|
13
|
+
|
|
14
|
+
``step`` / ``run``
|
|
15
|
+
Provided by :mod:`tnfr.dynamics`. These helpers rely on the
|
|
16
|
+
machinery defined within the :mod:`tnfr.dynamics` package (operator
|
|
17
|
+
orchestration, validation hooks and metrics integration) and require
|
|
18
|
+
the ``networkx`` package for graph handling.
|
|
19
|
+
|
|
20
|
+
``prepare_network``
|
|
21
|
+
Defined in :mod:`tnfr.ontosim`. Besides :mod:`tnfr.ontosim`
|
|
22
|
+
itself, the helper imports :mod:`tnfr.callback_utils`,
|
|
23
|
+
:mod:`tnfr.constants`, :mod:`tnfr.dynamics`, :mod:`tnfr.glyph_history`,
|
|
24
|
+
:mod:`tnfr.initialization` and :mod:`tnfr.utils` to assemble the
|
|
25
|
+
graph preparation pipeline. It also requires ``networkx`` at import
|
|
26
|
+
time.
|
|
27
|
+
|
|
28
|
+
``create_nfr`` / ``run_sequence``
|
|
29
|
+
Re-exported from :mod:`tnfr.structural`. They depend on
|
|
30
|
+
:mod:`tnfr.structural`, :mod:`tnfr.constants`, :mod:`tnfr.dynamics`,
|
|
31
|
+
:mod:`tnfr.operators.definitions`, :mod:`tnfr.operators.registry` and
|
|
32
|
+
:mod:`tnfr.validation`, and additionally require the ``networkx``
|
|
33
|
+
package.
|
|
34
|
+
|
|
35
|
+
``cached_import`` and ``prune_failed_imports`` remain available from
|
|
36
|
+
``tnfr.utils`` for optional dependency management.
|
|
13
37
|
"""
|
|
14
38
|
|
|
15
39
|
from __future__ import annotations
|
|
16
40
|
|
|
17
|
-
|
|
18
|
-
from
|
|
41
|
+
import warnings
|
|
42
|
+
from importlib import import_module, metadata
|
|
43
|
+
from importlib.metadata import PackageNotFoundError
|
|
44
|
+
from typing import Any, Callable, NoReturn
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
EXPORT_DEPENDENCIES: dict[str, dict[str, tuple[str, ...]]] = {
|
|
48
|
+
"step": {
|
|
49
|
+
"submodules": ("tnfr.dynamics",),
|
|
50
|
+
"third_party": ("networkx",),
|
|
51
|
+
},
|
|
52
|
+
"run": {
|
|
53
|
+
"submodules": ("tnfr.dynamics",),
|
|
54
|
+
"third_party": ("networkx",),
|
|
55
|
+
},
|
|
56
|
+
"prepare_network": {
|
|
57
|
+
"submodules": (
|
|
58
|
+
"tnfr.ontosim",
|
|
59
|
+
"tnfr.callback_utils",
|
|
60
|
+
"tnfr.constants",
|
|
61
|
+
"tnfr.dynamics",
|
|
62
|
+
"tnfr.glyph_history",
|
|
63
|
+
"tnfr.initialization",
|
|
64
|
+
"tnfr.utils",
|
|
65
|
+
),
|
|
66
|
+
"third_party": ("networkx",),
|
|
67
|
+
},
|
|
68
|
+
"create_nfr": {
|
|
69
|
+
"submodules": (
|
|
70
|
+
"tnfr.structural",
|
|
71
|
+
"tnfr.constants",
|
|
72
|
+
"tnfr.dynamics",
|
|
73
|
+
"tnfr.operators.definitions",
|
|
74
|
+
"tnfr.operators.registry",
|
|
75
|
+
"tnfr.validation",
|
|
76
|
+
),
|
|
77
|
+
"third_party": ("networkx",),
|
|
78
|
+
},
|
|
79
|
+
"run_sequence": {
|
|
80
|
+
"submodules": (
|
|
81
|
+
"tnfr.structural",
|
|
82
|
+
"tnfr.constants",
|
|
83
|
+
"tnfr.dynamics",
|
|
84
|
+
"tnfr.operators.definitions",
|
|
85
|
+
"tnfr.operators.registry",
|
|
86
|
+
"tnfr.validation",
|
|
87
|
+
),
|
|
88
|
+
"third_party": ("networkx",),
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
try: # pragma: no cover - exercised in version resolution tests
|
|
94
|
+
__version__ = metadata.version("tnfr")
|
|
95
|
+
except PackageNotFoundError: # pragma: no cover - fallback tested explicitly
|
|
96
|
+
from ._version import __version__ as _fallback_version
|
|
97
|
+
|
|
98
|
+
__version__ = _fallback_version
|
|
19
99
|
|
|
20
100
|
|
|
21
|
-
def
|
|
22
|
-
|
|
101
|
+
def _is_internal_import_error(exc: ImportError) -> bool:
|
|
102
|
+
missing_name = getattr(exc, "name", None) or ""
|
|
103
|
+
if missing_name.startswith("tnfr"):
|
|
104
|
+
return True
|
|
105
|
+
|
|
106
|
+
module_name = getattr(exc, "module", None) or ""
|
|
107
|
+
if module_name.startswith("tnfr"):
|
|
108
|
+
return True
|
|
109
|
+
|
|
110
|
+
missing_path = getattr(exc, "path", None) or ""
|
|
111
|
+
if missing_path:
|
|
112
|
+
normalized = missing_path.replace("\\", "/")
|
|
113
|
+
if "/tnfr/" in normalized or normalized.endswith("/tnfr"):
|
|
114
|
+
return True
|
|
115
|
+
|
|
116
|
+
message = str(exc)
|
|
117
|
+
lowered = message.lower()
|
|
118
|
+
mentions_base_package = "module 'tnfr'" in lowered or 'module "tnfr"' in lowered
|
|
119
|
+
if ("tnfr." in message or mentions_base_package) and (
|
|
120
|
+
"circular import" in lowered or "partially initialized module" in lowered
|
|
121
|
+
):
|
|
122
|
+
return True
|
|
123
|
+
|
|
124
|
+
return False
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _missing_dependency(
|
|
128
|
+
name: str, exc: ImportError, *, module: str | None = None
|
|
129
|
+
) -> Callable[..., NoReturn]:
|
|
130
|
+
missing_name = getattr(exc, "name", None)
|
|
131
|
+
|
|
132
|
+
def _stub(*args: Any, **kwargs: Any) -> NoReturn:
|
|
23
133
|
raise ImportError(
|
|
24
134
|
f"{name} is unavailable because required dependencies could not be imported. "
|
|
25
135
|
f"Original error ({exc.__class__.__name__}): {exc}. "
|
|
26
136
|
"Install the missing packages (e.g. 'networkx' or grammar modules)."
|
|
27
137
|
) from exc
|
|
28
138
|
|
|
139
|
+
_stub.__tnfr_missing_dependency__ = {
|
|
140
|
+
"export": name,
|
|
141
|
+
"module": module,
|
|
142
|
+
"missing": missing_name,
|
|
143
|
+
}
|
|
29
144
|
return _stub
|
|
30
145
|
|
|
31
146
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
except ImportError as exc: # pragma: no cover - no missing deps in CI
|
|
35
|
-
step = _missing_dependency("step", exc)
|
|
36
|
-
run = _missing_dependency("run", exc)
|
|
147
|
+
_MISSING_EXPORTS: dict[str, dict[str, Any]] = {}
|
|
148
|
+
|
|
37
149
|
|
|
150
|
+
class ExportDependencyError(RuntimeError):
|
|
151
|
+
"""Raised when the export dependency manifest is inconsistent."""
|
|
38
152
|
|
|
39
|
-
_HAS_RUN_SEQUENCE = False
|
|
40
|
-
try: # pragma: no cover - exercised in import tests
|
|
41
|
-
from .structural import create_nfr, run_sequence
|
|
42
|
-
except ImportError as exc: # pragma: no cover - no missing deps in CI
|
|
43
|
-
create_nfr = _missing_dependency("create_nfr", exc)
|
|
44
|
-
run_sequence = _missing_dependency("run_sequence", exc)
|
|
45
|
-
else:
|
|
46
|
-
_HAS_RUN_SEQUENCE = True
|
|
47
153
|
|
|
154
|
+
def _validate_export_dependencies() -> None:
|
|
155
|
+
"""Ensure exported helpers and their manifest entries stay in sync."""
|
|
48
156
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
157
|
+
if "__all__" not in globals():
|
|
158
|
+
# Defensive guard for unusual import orders (should never trigger).
|
|
159
|
+
return
|
|
52
160
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
161
|
+
issues: list[str] = []
|
|
162
|
+
manifest = EXPORT_DEPENDENCIES
|
|
163
|
+
export_names = [name for name in __all__ if name != "__version__"]
|
|
164
|
+
manifest_names = set(manifest)
|
|
57
165
|
|
|
58
|
-
|
|
59
|
-
|
|
166
|
+
for export_name in export_names:
|
|
167
|
+
if export_name not in manifest:
|
|
168
|
+
issues.append(
|
|
169
|
+
f"helper '{export_name}' is exported via __all__ but missing from EXPORT_DEPENDENCIES"
|
|
170
|
+
)
|
|
171
|
+
continue
|
|
60
172
|
|
|
61
|
-
|
|
62
|
-
|
|
173
|
+
entry = manifest[export_name]
|
|
174
|
+
if not isinstance(entry, dict):
|
|
175
|
+
issues.append(
|
|
176
|
+
f"helper '{export_name}' has a malformed manifest entry (expected mapping, got {type(entry)!r})"
|
|
177
|
+
)
|
|
178
|
+
continue
|
|
63
179
|
|
|
180
|
+
for key in ("submodules", "third_party"):
|
|
181
|
+
value = entry.get(key)
|
|
182
|
+
if not value:
|
|
183
|
+
issues.append(
|
|
184
|
+
f"helper '{export_name}' is missing '{key}' dependencies in EXPORT_DEPENDENCIES"
|
|
185
|
+
)
|
|
64
186
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
187
|
+
missing_exports = manifest_names.difference(export_names).difference(_MISSING_EXPORTS)
|
|
188
|
+
for manifest_only in sorted(missing_exports):
|
|
189
|
+
entry = manifest[manifest_only]
|
|
190
|
+
if not isinstance(entry, dict):
|
|
191
|
+
issues.append(
|
|
192
|
+
f"helper '{manifest_only}' has a malformed manifest entry (expected mapping, got {type(entry)!r})"
|
|
193
|
+
)
|
|
194
|
+
continue
|
|
195
|
+
|
|
196
|
+
for key in ("submodules", "third_party"):
|
|
197
|
+
value = entry.get(key)
|
|
198
|
+
if not value:
|
|
199
|
+
issues.append(
|
|
200
|
+
f"helper '{manifest_only}' is missing '{key}' dependencies in EXPORT_DEPENDENCIES"
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
issues.append(
|
|
204
|
+
f"helper '{manifest_only}' is listed in EXPORT_DEPENDENCIES but not exported via __all__"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
if issues:
|
|
208
|
+
raise ExportDependencyError(
|
|
209
|
+
"Invalid TNFR export dependency manifest:\n- " + "\n- ".join(issues)
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _assign_exports(module: str, names: tuple[str, ...]) -> bool:
|
|
214
|
+
try: # pragma: no cover - exercised in import tests
|
|
215
|
+
mod = import_module(f".{module}", __name__)
|
|
216
|
+
except ImportError as exc: # pragma: no cover - no missing deps in CI
|
|
217
|
+
if _is_internal_import_error(exc):
|
|
218
|
+
raise
|
|
219
|
+
for export_name in names:
|
|
220
|
+
stub = _missing_dependency(export_name, exc, module=module)
|
|
221
|
+
globals()[export_name] = stub
|
|
222
|
+
_MISSING_EXPORTS[export_name] = getattr(
|
|
223
|
+
stub, "__tnfr_missing_dependency__", {}
|
|
224
|
+
)
|
|
225
|
+
return False
|
|
226
|
+
else:
|
|
227
|
+
for export_name in names:
|
|
228
|
+
globals()[export_name] = getattr(mod, export_name)
|
|
229
|
+
return True
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
_assign_exports("dynamics", ("step", "run"))
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
_HAS_PREPARE_NETWORK = _assign_exports("ontosim", ("prepare_network",))
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
_HAS_RUN_SEQUENCE = _assign_exports("structural", ("create_nfr", "run_sequence"))
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _emit_missing_dependency_warning() -> None:
|
|
242
|
+
if not _MISSING_EXPORTS:
|
|
243
|
+
return
|
|
244
|
+
details = ", ".join(
|
|
245
|
+
f"{name} (missing: {info.get('missing') or 'unknown'})"
|
|
246
|
+
for name, info in sorted(_MISSING_EXPORTS.items())
|
|
247
|
+
)
|
|
248
|
+
warnings.warn(
|
|
249
|
+
"TNFR helpers disabled because dependencies are missing: " + details,
|
|
250
|
+
ImportWarning,
|
|
251
|
+
stacklevel=2,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
_emit_missing_dependency_warning()
|
|
71
256
|
|
|
72
|
-
try:
|
|
73
|
-
with (Path(__file__).resolve().parents[2] / "pyproject.toml").open(
|
|
74
|
-
"rb",
|
|
75
|
-
) as f:
|
|
76
|
-
__version__ = tomllib.load(f)["project"]["version"]
|
|
77
|
-
except (OSError, KeyError, ValueError): # pragma: no cover
|
|
78
|
-
__version__ = "0+unknown"
|
|
79
|
-
else: # pragma: no cover
|
|
80
|
-
__version__ = "0+unknown"
|
|
81
257
|
|
|
82
258
|
__all__ = [
|
|
83
259
|
"__version__",
|
|
84
260
|
"step",
|
|
85
261
|
"run",
|
|
86
|
-
"
|
|
262
|
+
"prepare_network",
|
|
87
263
|
"create_nfr",
|
|
88
264
|
]
|
|
89
265
|
|
|
90
266
|
if _HAS_RUN_SEQUENCE:
|
|
91
267
|
__all__.append("run_sequence")
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
_validate_export_dependencies()
|
tnfr/__init__.pyi
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from typing import Any, NoReturn
|
|
5
|
+
|
|
6
|
+
from .dynamics import run, step
|
|
7
|
+
from .ontosim import prepare_network
|
|
8
|
+
from .structural import create_nfr, run_sequence
|
|
9
|
+
|
|
10
|
+
EXPORT_DEPENDENCIES: dict[str, dict[str, tuple[str, ...]]]
|
|
11
|
+
"""Manifest describing required submodules and third-party packages."""
|
|
12
|
+
|
|
13
|
+
_MISSING_EXPORTS: dict[str, dict[str, Any]]
|
|
14
|
+
|
|
15
|
+
__version__: str
|
|
16
|
+
__all__: list[str]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ExportDependencyError(RuntimeError):
|
|
20
|
+
"""Raised when the export dependency manifest is inconsistent."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _is_internal_import_error(exc: ImportError) -> bool: ...
|
|
24
|
+
|
|
25
|
+
def _missing_dependency(
|
|
26
|
+
name: str,
|
|
27
|
+
exc: ImportError,
|
|
28
|
+
*,
|
|
29
|
+
module: str | None = ...,
|
|
30
|
+
) -> Callable[..., NoReturn]: ...
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _validate_export_dependencies() -> None: ...
|
|
34
|
+
|
|
35
|
+
def _assign_exports(module: str, names: tuple[str, ...]) -> bool: ...
|
|
36
|
+
|
|
37
|
+
def _emit_missing_dependency_warning() -> None: ...
|
|
38
|
+
|
|
39
|
+
_HAS_PREPARE_NETWORK: bool
|
|
40
|
+
_HAS_RUN_SEQUENCE: bool
|
tnfr/_compat.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Compatibility helpers for bridging typing features across Python versions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
try: # pragma: no cover - exercised implicitly by importers
|
|
6
|
+
from typing import TypeAlias # type: ignore[attr-defined]
|
|
7
|
+
except (ImportError, AttributeError): # pragma: no cover - Python < 3.10
|
|
8
|
+
from typing_extensions import TypeAlias # type: ignore[assignment]
|
|
9
|
+
|
|
10
|
+
__all__ = ["TypeAlias"]
|
|
11
|
+
|
tnfr/_version.py
ADDED
tnfr/_version.pyi
ADDED
tnfr/alias.py
CHANGED
|
@@ -8,31 +8,32 @@ alias-based attribute access. Legacy wrappers ``alias_get`` and
|
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
from collections import defaultdict
|
|
11
|
-
from collections.abc import Iterable, Sized
|
|
11
|
+
from collections.abc import Iterable, Mapping, MutableMapping, Sized
|
|
12
12
|
from dataclasses import dataclass
|
|
13
|
+
from functools import lru_cache, partial
|
|
14
|
+
from threading import Lock
|
|
15
|
+
from types import ModuleType
|
|
13
16
|
from typing import (
|
|
17
|
+
TYPE_CHECKING,
|
|
14
18
|
Any,
|
|
15
19
|
Callable,
|
|
16
|
-
TypeVar,
|
|
17
|
-
Optional,
|
|
18
20
|
Generic,
|
|
19
21
|
Hashable,
|
|
20
|
-
|
|
22
|
+
Optional,
|
|
23
|
+
TypeVar,
|
|
21
24
|
cast,
|
|
22
25
|
)
|
|
23
26
|
|
|
24
|
-
from functools import lru_cache, partial
|
|
25
|
-
from threading import Lock
|
|
26
|
-
|
|
27
27
|
from .constants import get_aliases
|
|
28
|
-
from .
|
|
28
|
+
from .types import FloatArray, NodeId
|
|
29
|
+
from .utils import convert_value
|
|
29
30
|
|
|
30
31
|
ALIAS_VF = get_aliases("VF")
|
|
31
32
|
ALIAS_DNFR = get_aliases("DNFR")
|
|
32
33
|
ALIAS_THETA = get_aliases("THETA")
|
|
33
34
|
|
|
34
35
|
if TYPE_CHECKING: # pragma: no cover
|
|
35
|
-
import networkx
|
|
36
|
+
import networkx
|
|
36
37
|
|
|
37
38
|
T = TypeVar("T")
|
|
38
39
|
|
|
@@ -184,6 +185,25 @@ class AliasAccessor(Generic[T]):
|
|
|
184
185
|
_generic_accessor: AliasAccessor[Any] = AliasAccessor()
|
|
185
186
|
|
|
186
187
|
|
|
188
|
+
def get_theta_attr(
|
|
189
|
+
d: Mapping[str, Any],
|
|
190
|
+
default: T | None = None,
|
|
191
|
+
*,
|
|
192
|
+
strict: bool = False,
|
|
193
|
+
log_level: int | None = None,
|
|
194
|
+
conv: Callable[[Any], T] = float,
|
|
195
|
+
) -> T | None:
|
|
196
|
+
"""Return ``theta``/``phase`` using the English alias set."""
|
|
197
|
+
return _generic_accessor.get(
|
|
198
|
+
cast(dict[str, Any], d),
|
|
199
|
+
ALIAS_THETA,
|
|
200
|
+
default,
|
|
201
|
+
strict=strict,
|
|
202
|
+
log_level=log_level,
|
|
203
|
+
conv=conv,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
187
207
|
def get_attr(
|
|
188
208
|
d: dict[str, Any],
|
|
189
209
|
aliases: Iterable[str],
|
|
@@ -207,12 +227,12 @@ def get_attr(
|
|
|
207
227
|
|
|
208
228
|
def collect_attr(
|
|
209
229
|
G: "networkx.Graph",
|
|
210
|
-
nodes: Iterable[
|
|
230
|
+
nodes: Iterable[NodeId],
|
|
211
231
|
aliases: Iterable[str],
|
|
212
232
|
default: float = 0.0,
|
|
213
233
|
*,
|
|
214
|
-
np=None,
|
|
215
|
-
):
|
|
234
|
+
np: ModuleType | None = None,
|
|
235
|
+
) -> FloatArray | list[float]:
|
|
216
236
|
"""Collect attribute values for ``nodes`` from ``G`` using ``aliases``.
|
|
217
237
|
|
|
218
238
|
Parameters
|
|
@@ -235,7 +255,7 @@ def collect_attr(
|
|
|
235
255
|
Collected attribute values in the same order as ``nodes``.
|
|
236
256
|
"""
|
|
237
257
|
|
|
238
|
-
def _nodes_iter_and_size(nodes: Iterable[
|
|
258
|
+
def _nodes_iter_and_size(nodes: Iterable[NodeId]) -> tuple[Iterable[NodeId], int]:
|
|
239
259
|
if nodes is G.nodes:
|
|
240
260
|
return G.nodes, G.number_of_nodes()
|
|
241
261
|
if isinstance(nodes, Sized):
|
|
@@ -245,13 +265,42 @@ def collect_attr(
|
|
|
245
265
|
|
|
246
266
|
nodes_iter, size = _nodes_iter_and_size(nodes)
|
|
247
267
|
|
|
268
|
+
def _value(node: NodeId) -> float:
|
|
269
|
+
return float(get_attr(G.nodes[node], aliases, default))
|
|
270
|
+
|
|
248
271
|
if np is not None:
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
272
|
+
values: FloatArray = np.fromiter((_value(n) for n in nodes_iter), float, count=size)
|
|
273
|
+
return values
|
|
274
|
+
return [_value(n) for n in nodes_iter]
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def collect_theta_attr(
|
|
278
|
+
G: "networkx.Graph",
|
|
279
|
+
nodes: Iterable[NodeId],
|
|
280
|
+
default: float = 0.0,
|
|
281
|
+
*,
|
|
282
|
+
np: ModuleType | None = None,
|
|
283
|
+
) -> FloatArray | list[float]:
|
|
284
|
+
"""Collect ``theta`` values honouring the English-only attribute contract."""
|
|
285
|
+
|
|
286
|
+
def _nodes_iter_and_size(nodes: Iterable[NodeId]) -> tuple[Iterable[NodeId], int]:
|
|
287
|
+
if nodes is G.nodes:
|
|
288
|
+
return G.nodes, G.number_of_nodes()
|
|
289
|
+
if isinstance(nodes, Sized):
|
|
290
|
+
return nodes, len(nodes) # type: ignore[arg-type]
|
|
291
|
+
nodes_list = list(nodes)
|
|
292
|
+
return nodes_list, len(nodes_list)
|
|
293
|
+
|
|
294
|
+
nodes_iter, size = _nodes_iter_and_size(nodes)
|
|
295
|
+
|
|
296
|
+
def _value(node: NodeId) -> float:
|
|
297
|
+
return float(get_theta_attr(G.nodes[node], default))
|
|
298
|
+
|
|
299
|
+
if np is not None:
|
|
300
|
+
values: FloatArray = np.fromiter((_value(n) for n in nodes_iter), float, count=size)
|
|
301
|
+
return values
|
|
302
|
+
|
|
303
|
+
return [_value(n) for n in nodes_iter]
|
|
255
304
|
|
|
256
305
|
|
|
257
306
|
def set_attr_generic(
|
|
@@ -273,8 +322,16 @@ get_attr_str = partial(get_attr, conv=str)
|
|
|
273
322
|
set_attr_str = partial(set_attr_generic, conv=str)
|
|
274
323
|
|
|
275
324
|
|
|
325
|
+
def set_theta_attr(d: MutableMapping[str, Any], value: Any) -> float:
|
|
326
|
+
"""Assign ``theta``/``phase`` using the English attribute names."""
|
|
327
|
+
result = float(value)
|
|
328
|
+
d["theta"] = result
|
|
329
|
+
d["phase"] = result
|
|
330
|
+
return result
|
|
331
|
+
|
|
332
|
+
|
|
276
333
|
# -------------------------
|
|
277
|
-
#
|
|
334
|
+
# Cached global maxima
|
|
278
335
|
# -------------------------
|
|
279
336
|
|
|
280
337
|
|
|
@@ -484,7 +541,7 @@ SCALAR_SETTERS: dict[str, dict[str, Any]] = {
|
|
|
484
541
|
"theta": {
|
|
485
542
|
"alias": ALIAS_THETA,
|
|
486
543
|
"extra": _increment_trig_version,
|
|
487
|
-
"doc": "Set
|
|
544
|
+
"doc": "Set ``theta`` for node ``n`` and invalidate trig caches.",
|
|
488
545
|
},
|
|
489
546
|
}
|
|
490
547
|
|
|
@@ -529,14 +586,42 @@ for _name, _spec in SCALAR_SETTERS.items():
|
|
|
529
586
|
del _name, _spec, _make_scalar_setter
|
|
530
587
|
|
|
531
588
|
|
|
589
|
+
_set_theta_impl = cast(
|
|
590
|
+
Callable[["networkx.Graph", Hashable, float], AbsMaxResult | None],
|
|
591
|
+
globals()["set_theta"],
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def _set_theta_with_compat(
|
|
596
|
+
G: "networkx.Graph", n: Hashable, value: float
|
|
597
|
+
) -> AbsMaxResult | None:
|
|
598
|
+
nd = cast(MutableMapping[str, Any], G.nodes[n])
|
|
599
|
+
result = _set_theta_impl(G, n, value)
|
|
600
|
+
theta_val = get_theta_attr(nd, value)
|
|
601
|
+
if theta_val is not None:
|
|
602
|
+
float_theta = float(theta_val)
|
|
603
|
+
nd["theta"] = float_theta
|
|
604
|
+
nd["phase"] = float_theta
|
|
605
|
+
return result
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
_set_theta_with_compat.__name__ = "set_theta"
|
|
609
|
+
_set_theta_with_compat.__qualname__ = "set_theta"
|
|
610
|
+
_set_theta_with_compat.__doc__ = _set_theta_impl.__doc__
|
|
611
|
+
globals()["set_theta"] = _set_theta_with_compat
|
|
612
|
+
|
|
613
|
+
|
|
532
614
|
__all__ = [
|
|
533
615
|
"AbsMaxResult",
|
|
534
616
|
"set_attr_generic",
|
|
535
617
|
"get_attr",
|
|
618
|
+
"get_theta_attr",
|
|
536
619
|
"collect_attr",
|
|
620
|
+
"collect_theta_attr",
|
|
537
621
|
"set_attr",
|
|
538
622
|
"get_attr_str",
|
|
539
623
|
"set_attr_str",
|
|
624
|
+
"set_theta_attr",
|
|
540
625
|
"set_attr_and_cache",
|
|
541
626
|
"set_attr_with_max",
|
|
542
627
|
"set_scalar",
|