tnfr 4.5.2__py3-none-any.whl → 7.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.

Potentially problematic release.


This version of tnfr might be problematic. Click here for more details.

Files changed (195) hide show
  1. tnfr/__init__.py +275 -51
  2. tnfr/__init__.pyi +33 -0
  3. tnfr/_compat.py +10 -0
  4. tnfr/_generated_version.py +34 -0
  5. tnfr/_version.py +49 -0
  6. tnfr/_version.pyi +7 -0
  7. tnfr/alias.py +117 -31
  8. tnfr/alias.pyi +108 -0
  9. tnfr/cache.py +6 -572
  10. tnfr/cache.pyi +16 -0
  11. tnfr/callback_utils.py +16 -38
  12. tnfr/callback_utils.pyi +79 -0
  13. tnfr/cli/__init__.py +34 -14
  14. tnfr/cli/__init__.pyi +26 -0
  15. tnfr/cli/arguments.py +211 -28
  16. tnfr/cli/arguments.pyi +27 -0
  17. tnfr/cli/execution.py +470 -50
  18. tnfr/cli/execution.pyi +70 -0
  19. tnfr/cli/utils.py +18 -3
  20. tnfr/cli/utils.pyi +8 -0
  21. tnfr/config/__init__.py +13 -0
  22. tnfr/config/__init__.pyi +10 -0
  23. tnfr/{constants_glyphs.py → config/constants.py} +26 -20
  24. tnfr/config/constants.pyi +12 -0
  25. tnfr/config/feature_flags.py +83 -0
  26. tnfr/{config.py → config/init.py} +11 -7
  27. tnfr/config/init.pyi +8 -0
  28. tnfr/config/operator_names.py +93 -0
  29. tnfr/config/operator_names.pyi +28 -0
  30. tnfr/config/presets.py +84 -0
  31. tnfr/config/presets.pyi +7 -0
  32. tnfr/constants/__init__.py +80 -29
  33. tnfr/constants/__init__.pyi +92 -0
  34. tnfr/constants/aliases.py +31 -0
  35. tnfr/constants/core.py +4 -4
  36. tnfr/constants/core.pyi +17 -0
  37. tnfr/constants/init.py +1 -1
  38. tnfr/constants/init.pyi +12 -0
  39. tnfr/constants/metric.py +7 -15
  40. tnfr/constants/metric.pyi +19 -0
  41. tnfr/dynamics/__init__.py +165 -633
  42. tnfr/dynamics/__init__.pyi +82 -0
  43. tnfr/dynamics/adaptation.py +267 -0
  44. tnfr/dynamics/aliases.py +23 -0
  45. tnfr/dynamics/coordination.py +385 -0
  46. tnfr/dynamics/dnfr.py +2283 -400
  47. tnfr/dynamics/dnfr.pyi +24 -0
  48. tnfr/dynamics/integrators.py +406 -98
  49. tnfr/dynamics/integrators.pyi +34 -0
  50. tnfr/dynamics/runtime.py +881 -0
  51. tnfr/dynamics/sampling.py +10 -5
  52. tnfr/dynamics/sampling.pyi +7 -0
  53. tnfr/dynamics/selectors.py +719 -0
  54. tnfr/execution.py +70 -48
  55. tnfr/execution.pyi +45 -0
  56. tnfr/flatten.py +13 -9
  57. tnfr/flatten.pyi +21 -0
  58. tnfr/gamma.py +66 -53
  59. tnfr/gamma.pyi +34 -0
  60. tnfr/glyph_history.py +110 -52
  61. tnfr/glyph_history.pyi +35 -0
  62. tnfr/glyph_runtime.py +16 -0
  63. tnfr/glyph_runtime.pyi +9 -0
  64. tnfr/immutable.py +69 -28
  65. tnfr/immutable.pyi +34 -0
  66. tnfr/initialization.py +16 -16
  67. tnfr/initialization.pyi +65 -0
  68. tnfr/io.py +6 -240
  69. tnfr/io.pyi +16 -0
  70. tnfr/locking.pyi +7 -0
  71. tnfr/mathematics/__init__.py +81 -0
  72. tnfr/mathematics/backend.py +426 -0
  73. tnfr/mathematics/dynamics.py +398 -0
  74. tnfr/mathematics/epi.py +254 -0
  75. tnfr/mathematics/generators.py +222 -0
  76. tnfr/mathematics/metrics.py +119 -0
  77. tnfr/mathematics/operators.py +233 -0
  78. tnfr/mathematics/operators_factory.py +71 -0
  79. tnfr/mathematics/projection.py +78 -0
  80. tnfr/mathematics/runtime.py +173 -0
  81. tnfr/mathematics/spaces.py +247 -0
  82. tnfr/mathematics/transforms.py +292 -0
  83. tnfr/metrics/__init__.py +10 -10
  84. tnfr/metrics/__init__.pyi +20 -0
  85. tnfr/metrics/coherence.py +993 -324
  86. tnfr/metrics/common.py +23 -16
  87. tnfr/metrics/common.pyi +46 -0
  88. tnfr/metrics/core.py +251 -35
  89. tnfr/metrics/core.pyi +13 -0
  90. tnfr/metrics/diagnosis.py +708 -111
  91. tnfr/metrics/diagnosis.pyi +85 -0
  92. tnfr/metrics/export.py +27 -15
  93. tnfr/metrics/glyph_timing.py +232 -42
  94. tnfr/metrics/reporting.py +33 -22
  95. tnfr/metrics/reporting.pyi +12 -0
  96. tnfr/metrics/sense_index.py +987 -43
  97. tnfr/metrics/sense_index.pyi +9 -0
  98. tnfr/metrics/trig.py +214 -23
  99. tnfr/metrics/trig.pyi +13 -0
  100. tnfr/metrics/trig_cache.py +115 -22
  101. tnfr/metrics/trig_cache.pyi +10 -0
  102. tnfr/node.py +542 -136
  103. tnfr/node.pyi +178 -0
  104. tnfr/observers.py +152 -35
  105. tnfr/observers.pyi +31 -0
  106. tnfr/ontosim.py +23 -19
  107. tnfr/ontosim.pyi +28 -0
  108. tnfr/operators/__init__.py +601 -82
  109. tnfr/operators/__init__.pyi +45 -0
  110. tnfr/operators/definitions.py +513 -0
  111. tnfr/operators/definitions.pyi +78 -0
  112. tnfr/operators/grammar.py +760 -0
  113. tnfr/operators/jitter.py +107 -38
  114. tnfr/operators/jitter.pyi +11 -0
  115. tnfr/operators/registry.py +75 -0
  116. tnfr/operators/registry.pyi +13 -0
  117. tnfr/operators/remesh.py +149 -88
  118. tnfr/py.typed +0 -0
  119. tnfr/rng.py +46 -143
  120. tnfr/rng.pyi +14 -0
  121. tnfr/schemas/__init__.py +8 -0
  122. tnfr/schemas/grammar.json +94 -0
  123. tnfr/selector.py +25 -19
  124. tnfr/selector.pyi +19 -0
  125. tnfr/sense.py +72 -62
  126. tnfr/sense.pyi +23 -0
  127. tnfr/structural.py +522 -262
  128. tnfr/structural.pyi +69 -0
  129. tnfr/telemetry/__init__.py +35 -0
  130. tnfr/telemetry/cache_metrics.py +226 -0
  131. tnfr/telemetry/nu_f.py +423 -0
  132. tnfr/telemetry/nu_f.pyi +123 -0
  133. tnfr/telemetry/verbosity.py +37 -0
  134. tnfr/tokens.py +1 -3
  135. tnfr/tokens.pyi +36 -0
  136. tnfr/trace.py +270 -113
  137. tnfr/trace.pyi +40 -0
  138. tnfr/types.py +574 -6
  139. tnfr/types.pyi +331 -0
  140. tnfr/units.py +69 -0
  141. tnfr/units.pyi +16 -0
  142. tnfr/utils/__init__.py +217 -0
  143. tnfr/utils/__init__.pyi +202 -0
  144. tnfr/utils/cache.py +2395 -0
  145. tnfr/utils/cache.pyi +468 -0
  146. tnfr/utils/chunks.py +104 -0
  147. tnfr/utils/chunks.pyi +21 -0
  148. tnfr/{collections_utils.py → utils/data.py} +147 -90
  149. tnfr/utils/data.pyi +64 -0
  150. tnfr/utils/graph.py +85 -0
  151. tnfr/utils/graph.pyi +10 -0
  152. tnfr/utils/init.py +770 -0
  153. tnfr/utils/init.pyi +78 -0
  154. tnfr/utils/io.py +456 -0
  155. tnfr/{helpers → utils}/numeric.py +51 -24
  156. tnfr/utils/numeric.pyi +21 -0
  157. tnfr/validation/__init__.py +113 -0
  158. tnfr/validation/__init__.pyi +77 -0
  159. tnfr/validation/compatibility.py +95 -0
  160. tnfr/validation/compatibility.pyi +6 -0
  161. tnfr/validation/grammar.py +71 -0
  162. tnfr/validation/grammar.pyi +40 -0
  163. tnfr/validation/graph.py +138 -0
  164. tnfr/validation/graph.pyi +17 -0
  165. tnfr/validation/rules.py +281 -0
  166. tnfr/validation/rules.pyi +55 -0
  167. tnfr/validation/runtime.py +263 -0
  168. tnfr/validation/runtime.pyi +31 -0
  169. tnfr/validation/soft_filters.py +170 -0
  170. tnfr/validation/soft_filters.pyi +37 -0
  171. tnfr/validation/spectral.py +159 -0
  172. tnfr/validation/spectral.pyi +46 -0
  173. tnfr/validation/syntax.py +40 -0
  174. tnfr/validation/syntax.pyi +10 -0
  175. tnfr/validation/window.py +39 -0
  176. tnfr/validation/window.pyi +1 -0
  177. tnfr/viz/__init__.py +9 -0
  178. tnfr/viz/matplotlib.py +246 -0
  179. tnfr-7.0.0.dist-info/METADATA +179 -0
  180. tnfr-7.0.0.dist-info/RECORD +185 -0
  181. {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/licenses/LICENSE.md +1 -1
  182. tnfr/grammar.py +0 -344
  183. tnfr/graph_utils.py +0 -84
  184. tnfr/helpers/__init__.py +0 -71
  185. tnfr/import_utils.py +0 -228
  186. tnfr/json_utils.py +0 -162
  187. tnfr/logging_utils.py +0 -116
  188. tnfr/presets.py +0 -60
  189. tnfr/validators.py +0 -84
  190. tnfr/value_utils.py +0 -59
  191. tnfr-4.5.2.dist-info/METADATA +0 -379
  192. tnfr-4.5.2.dist-info/RECORD +0 -67
  193. {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/WHEEL +0 -0
  194. {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/entry_points.txt +0 -0
  195. {tnfr-4.5.2.dist-info → tnfr-7.0.0.dist-info}/top_level.txt +0 -0
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 VF_KEY, THETA_KEY, get_graph_param
10
- from .helpers.numeric import clamp
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 # type: ignore[import-untyped]
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
- """Parametros de inicialización nodal."""
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
- """Construir ``InitParams`` desde ``G.graph``."""
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: dict,
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: dict,
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: dict,
120
+ nd: NodeInitAttrMap,
122
121
  rng: random.Random,
123
122
  *,
124
123
  override: bool,
@@ -163,9 +162,10 @@ def init_node_attrs(G: "nx.Graph", *, override: bool = True) -> "nx.Graph":
163
162
 
164
163
  rng = make_rng(params.seed, -1, G)
165
164
  for _, nd in G.nodes(data=True):
165
+ node_attrs = cast(NodeInitAttrMap, nd)
166
166
 
167
167
  _init_phase(
168
- nd,
168
+ node_attrs,
169
169
  rng,
170
170
  override=override,
171
171
  random_phase=params.init_rand_phase,
@@ -173,7 +173,7 @@ def init_node_attrs(G: "nx.Graph", *, override: bool = True) -> "nx.Graph":
173
173
  th_max=params.th_max,
174
174
  )
175
175
  _init_vf(
176
- nd,
176
+ node_attrs,
177
177
  rng,
178
178
  override=override,
179
179
  mode=params.vf_mode,
@@ -186,7 +186,7 @@ def init_node_attrs(G: "nx.Graph", *, override: bool = True) -> "nx.Graph":
186
186
  clamp_to_limits=params.clamp_to_limits,
187
187
  )
188
188
  _init_si_epi(
189
- nd,
189
+ node_attrs,
190
190
  rng,
191
191
  override=override,
192
192
  si_min=params.si_min,
@@ -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: ...
tnfr/io.py CHANGED
@@ -1,246 +1,12 @@
1
- """Structured file I/O utilities.
1
+ """Legacy JSON utilities module.
2
2
 
3
- Optional parsers such as ``tomllib``/``tomli`` and ``pyyaml`` are loaded via
4
- the :func:`tnfr.import_utils.cached_import` helper. Their import results and
5
- failure states are cached and can be cleared with
6
- ``cached_import.cache_clear()`` and :func:`tnfr.import_utils.prune_failed_imports`
7
- when needed.
3
+ The backwards-compatible re-export was removed; use :mod:`tnfr.utils.io`
4
+ directly. Importing :mod:`tnfr.io` now raises an :class:`ImportError` with a
5
+ clear migration hint.
8
6
  """
9
7
 
10
8
  from __future__ import annotations
11
9
 
12
- import json
13
- import os
14
- import tempfile
15
- from pathlib import Path
16
- from typing import Any, Callable
17
- from functools import partial
18
-
19
- from .import_utils import cached_import
20
- from .logging_utils import get_logger
21
-
22
-
23
- def _raise_import_error(name: str, *_: Any, **__: Any) -> Any:
24
- raise ImportError(f"{name} is not installed")
25
-
26
-
27
- _MISSING_TOML_ERROR = type(
28
- "MissingTOMLDependencyError",
29
- (Exception,),
30
- {"__doc__": "Fallback error used when tomllib/tomli is missing."},
31
- )
32
-
33
- _MISSING_YAML_ERROR = type(
34
- "MissingPyYAMLDependencyError",
35
- (Exception,),
36
- {"__doc__": "Fallback error used when pyyaml is missing."},
37
- )
38
-
39
-
40
- tomllib = cached_import(
41
- "tomllib",
42
- emit="log",
43
- fallback=cached_import("tomli", emit="log"),
44
- )
45
- has_toml = tomllib is not None
46
-
47
-
48
- TOMLDecodeError = cached_import(
49
- "tomllib",
50
- "TOMLDecodeError",
51
- emit="log",
52
- fallback=cached_import(
53
- "tomli",
54
- "TOMLDecodeError",
55
- emit="log",
56
- fallback=_MISSING_TOML_ERROR,
57
- ),
58
- )
59
-
60
-
61
- _TOML_LOADS: Callable[[str], Any] = cached_import(
62
- "tomllib",
63
- "loads",
64
- emit="log",
65
- fallback=cached_import(
66
- "tomli",
67
- "loads",
68
- emit="log",
69
- fallback=partial(_raise_import_error, "tomllib/tomli"),
70
- ),
71
- )
72
-
73
-
74
- yaml = cached_import("yaml", emit="log")
75
-
76
-
77
- YAMLError = cached_import(
78
- "yaml",
79
- "YAMLError",
80
- emit="log",
81
- fallback=_MISSING_YAML_ERROR,
82
- )
83
-
84
-
85
- _YAML_SAFE_LOAD: Callable[[str], Any] = cached_import(
86
- "yaml",
87
- "safe_load",
88
- emit="log",
89
- fallback=partial(_raise_import_error, "pyyaml"),
90
- )
91
-
92
-
93
- def _parse_yaml(text: str) -> Any:
94
- """Parse YAML ``text`` using ``safe_load`` if available."""
95
- return _YAML_SAFE_LOAD(text)
96
-
97
-
98
- def _parse_toml(text: str) -> Any:
99
- """Parse TOML ``text`` using ``tomllib`` or ``tomli``."""
100
- return _TOML_LOADS(text)
101
-
102
-
103
- PARSERS = {
104
- ".json": json.loads,
105
- ".yaml": _parse_yaml,
106
- ".yml": _parse_yaml,
107
- ".toml": _parse_toml,
108
- }
109
-
110
-
111
- def _get_parser(suffix: str) -> Callable[[str], Any]:
112
- try:
113
- return PARSERS[suffix]
114
- except KeyError as exc:
115
- raise ValueError(f"Unsupported suffix: {suffix}") from exc
116
-
117
-
118
- ERROR_MESSAGES = {
119
- OSError: "Could not read {path}: {e}",
120
- UnicodeDecodeError: "Encoding error while reading {path}: {e}",
121
- json.JSONDecodeError: "Error parsing JSON file at {path}: {e}",
122
- YAMLError: "Error parsing YAML file at {path}: {e}",
123
- ImportError: "Missing dependency parsing {path}: {e}",
124
- }
125
- if has_toml:
126
- ERROR_MESSAGES[TOMLDecodeError] = "Error parsing TOML file at {path}: {e}"
127
-
128
-
129
- def _format_structured_file_error(path: Path, e: Exception) -> str:
130
- for exc, msg in ERROR_MESSAGES.items():
131
- if isinstance(e, exc):
132
- return msg.format(path=path, e=e)
133
- return f"Error parsing {path}: {e}"
134
-
135
-
136
- class StructuredFileError(Exception):
137
- """Error while reading or parsing a structured file."""
138
-
139
- def __init__(self, path: Path, original: Exception):
140
- super().__init__(_format_structured_file_error(path, original))
141
- self.path = path
142
-
143
-
144
- def read_structured_file(path: Path) -> Any:
145
- """Read a JSON, YAML or TOML file and return parsed data."""
146
- suffix = path.suffix.lower()
147
- try:
148
- parser = _get_parser(suffix)
149
- except ValueError as e:
150
- raise StructuredFileError(path, e) from e
151
- try:
152
- text = path.read_text(encoding="utf-8")
153
- return parser(text)
154
- except (
155
- OSError,
156
- UnicodeDecodeError,
157
- json.JSONDecodeError,
158
- YAMLError,
159
- TOMLDecodeError,
160
- ImportError,
161
- ) as e:
162
- raise StructuredFileError(path, e) from e
163
-
164
-
165
- logger = get_logger(__name__)
166
-
167
-
168
- def safe_write(
169
- path: str | Path,
170
- write: Callable[[Any], Any],
171
- *,
172
- mode: str = "w",
173
- encoding: str | None = "utf-8",
174
- atomic: bool = True,
175
- sync: bool | None = None,
176
- **open_kwargs: Any,
177
- ) -> None:
178
- """Write to ``path`` ensuring parent directory exists and handle errors.
179
-
180
- Parameters
181
- ----------
182
- path:
183
- Destination file path.
184
- write:
185
- Callback receiving the opened file object and performing the actual
186
- write.
187
- mode:
188
- File mode passed to :func:`open`. Text modes (default) use UTF-8
189
- encoding unless ``encoding`` is ``None``. When a binary mode is used
190
- (``'b'`` in ``mode``) no encoding parameter is supplied so
191
- ``write`` may write bytes.
192
- encoding:
193
- Encoding for text modes. Ignored for binary modes.
194
- atomic:
195
- When ``True`` (default) writes to a temporary file and atomically
196
- replaces the destination after flushing to disk. When ``False``
197
- writes directly to ``path`` without any atomicity guarantee.
198
- sync:
199
- When ``True`` flushes and fsyncs the file descriptor after writing.
200
- ``None`` uses ``atomic`` to determine syncing behaviour.
201
- """
202
- path = Path(path)
203
- path.parent.mkdir(parents=True, exist_ok=True)
204
- open_params = dict(mode=mode, **open_kwargs)
205
- if "b" not in mode and encoding is not None:
206
- open_params["encoding"] = encoding
207
- if sync is None:
208
- sync = atomic
209
- tmp_path: Path | None = None
210
- try:
211
- if atomic:
212
- tmp_fd = tempfile.NamedTemporaryFile(dir=path.parent, delete=False)
213
- tmp_path = Path(tmp_fd.name)
214
- tmp_fd.close()
215
- with open(tmp_path, **open_params) as fd:
216
- write(fd)
217
- if sync:
218
- fd.flush()
219
- os.fsync(fd.fileno())
220
- try:
221
- os.replace(tmp_path, path)
222
- except OSError as e:
223
- logger.error(
224
- "Atomic replace failed for %s -> %s: %s", tmp_path, path, e
225
- )
226
- raise
227
- else:
228
- with open(path, **open_params) as fd:
229
- write(fd)
230
- if sync:
231
- fd.flush()
232
- os.fsync(fd.fileno())
233
- except (OSError, ValueError, TypeError) as e:
234
- raise type(e)(f"Failed to write file {path}: {e}") from e
235
- finally:
236
- if tmp_path is not None:
237
- tmp_path.unlink(missing_ok=True)
238
-
239
-
240
- __all__ = (
241
- "read_structured_file",
242
- "safe_write",
243
- "StructuredFileError",
244
- "TOMLDecodeError",
245
- "YAMLError",
10
+ raise ImportError(
11
+ "`tnfr.io` was removed. Import helpers from `tnfr.utils.io` instead."
246
12
  )
tnfr/io.pyi ADDED
@@ -0,0 +1,16 @@
1
+ """Compatibility stub removed; import from :mod:`tnfr.utils.io` instead."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import NoReturn
6
+
7
+ __all__ = ()
8
+
9
+
10
+ def __getattr__(name: str) -> NoReturn:
11
+ """Indicate that :mod:`tnfr.io` no longer exports IO helpers."""
12
+
13
+
14
+ def __dir__() -> tuple[str, ...]:
15
+ """Return an empty set of exports to mirror the removed shim."""
16
+
tnfr/locking.pyi ADDED
@@ -0,0 +1,7 @@
1
+ from typing import Any
2
+
3
+ __all__: Any
4
+
5
+ def __getattr__(name: str) -> Any: ...
6
+
7
+ get_lock: Any
@@ -0,0 +1,81 @@
1
+ """Mathematics primitives aligned with TNFR coherence modeling.
2
+
3
+ Backend selection
4
+ -----------------
5
+ Use :func:`get_backend` to retrieve a numerical backend compatible with TNFR's
6
+ structural operators. The selection order is ``name`` → ``TNFR_MATH_BACKEND``
7
+ → :func:`tnfr.config.get_flags`. NumPy remains the canonical default so
8
+ existing code continues to operate even when optional dependencies are absent.
9
+ """
10
+
11
+ from .backend import (
12
+ MathematicsBackend,
13
+ available_backends,
14
+ ensure_array,
15
+ ensure_numpy,
16
+ get_backend,
17
+ register_backend,
18
+ )
19
+ from .dynamics import ContractiveDynamicsEngine, MathematicalDynamicsEngine
20
+ from .epi import BEPIElement, CoherenceEvaluation, evaluate_coherence_transform
21
+ from .generators import build_delta_nfr, build_lindblad_delta_nfr
22
+ from .metrics import dcoh
23
+ from .operators import CoherenceOperator, FrequencyOperator
24
+ from .operators_factory import make_coherence_operator, make_frequency_operator
25
+ from .projection import BasicStateProjector, StateProjector
26
+ from .runtime import (
27
+ coherence,
28
+ coherence_expectation,
29
+ frequency_expectation,
30
+ frequency_positive,
31
+ normalized,
32
+ stable_unitary,
33
+ )
34
+ from .spaces import BanachSpaceEPI, HilbertSpace
35
+ from .transforms import (
36
+ CoherenceMonotonicityReport,
37
+ CoherenceViolation,
38
+ IsometryFactory,
39
+ build_isometry_factory,
40
+ ensure_coherence_monotonicity,
41
+ validate_norm_preservation,
42
+ )
43
+ from ..validation import NFRValidator
44
+
45
+ __all__ = [
46
+ "MathematicsBackend",
47
+ "ensure_array",
48
+ "ensure_numpy",
49
+ "HilbertSpace",
50
+ "BanachSpaceEPI",
51
+ "BEPIElement",
52
+ "CoherenceEvaluation",
53
+ "CoherenceOperator",
54
+ "ContractiveDynamicsEngine",
55
+ "CoherenceMonotonicityReport",
56
+ "CoherenceViolation",
57
+ "FrequencyOperator",
58
+ "MathematicalDynamicsEngine",
59
+ "build_delta_nfr",
60
+ "build_lindblad_delta_nfr",
61
+ "make_coherence_operator",
62
+ "make_frequency_operator",
63
+ "NFRValidator",
64
+ "IsometryFactory",
65
+ "build_isometry_factory",
66
+ "validate_norm_preservation",
67
+ "ensure_coherence_monotonicity",
68
+ "evaluate_coherence_transform",
69
+ "StateProjector",
70
+ "BasicStateProjector",
71
+ "normalized",
72
+ "coherence",
73
+ "frequency_positive",
74
+ "stable_unitary",
75
+ "dcoh",
76
+ "coherence_expectation",
77
+ "frequency_expectation",
78
+ "available_backends",
79
+ "get_backend",
80
+ "register_backend",
81
+ ]