tnfr 6.0.0__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 (176) hide show
  1. tnfr/__init__.py +50 -5
  2. tnfr/__init__.pyi +0 -7
  3. tnfr/_compat.py +0 -1
  4. tnfr/_generated_version.py +34 -0
  5. tnfr/_version.py +44 -2
  6. tnfr/alias.py +14 -13
  7. tnfr/alias.pyi +5 -37
  8. tnfr/cache.py +9 -729
  9. tnfr/cache.pyi +8 -224
  10. tnfr/callback_utils.py +16 -31
  11. tnfr/callback_utils.pyi +3 -29
  12. tnfr/cli/__init__.py +17 -11
  13. tnfr/cli/__init__.pyi +0 -21
  14. tnfr/cli/arguments.py +175 -14
  15. tnfr/cli/arguments.pyi +5 -11
  16. tnfr/cli/execution.py +434 -48
  17. tnfr/cli/execution.pyi +14 -24
  18. tnfr/cli/utils.py +20 -3
  19. tnfr/cli/utils.pyi +5 -5
  20. tnfr/config/__init__.py +2 -1
  21. tnfr/config/__init__.pyi +2 -0
  22. tnfr/config/feature_flags.py +83 -0
  23. tnfr/config/init.py +1 -1
  24. tnfr/config/operator_names.py +1 -14
  25. tnfr/config/presets.py +6 -26
  26. tnfr/constants/__init__.py +10 -13
  27. tnfr/constants/__init__.pyi +10 -22
  28. tnfr/constants/aliases.py +31 -0
  29. tnfr/constants/core.py +4 -3
  30. tnfr/constants/init.py +1 -1
  31. tnfr/constants/metric.py +3 -3
  32. tnfr/dynamics/__init__.py +64 -10
  33. tnfr/dynamics/__init__.pyi +3 -4
  34. tnfr/dynamics/adaptation.py +79 -13
  35. tnfr/dynamics/aliases.py +10 -9
  36. tnfr/dynamics/coordination.py +77 -35
  37. tnfr/dynamics/dnfr.py +575 -274
  38. tnfr/dynamics/dnfr.pyi +1 -10
  39. tnfr/dynamics/integrators.py +47 -33
  40. tnfr/dynamics/integrators.pyi +0 -1
  41. tnfr/dynamics/runtime.py +489 -129
  42. tnfr/dynamics/sampling.py +2 -0
  43. tnfr/dynamics/selectors.py +101 -62
  44. tnfr/execution.py +15 -8
  45. tnfr/execution.pyi +5 -25
  46. tnfr/flatten.py +7 -3
  47. tnfr/flatten.pyi +1 -8
  48. tnfr/gamma.py +22 -26
  49. tnfr/gamma.pyi +0 -6
  50. tnfr/glyph_history.py +37 -26
  51. tnfr/glyph_history.pyi +1 -19
  52. tnfr/glyph_runtime.py +16 -0
  53. tnfr/glyph_runtime.pyi +9 -0
  54. tnfr/immutable.py +20 -15
  55. tnfr/immutable.pyi +4 -7
  56. tnfr/initialization.py +5 -7
  57. tnfr/initialization.pyi +1 -9
  58. tnfr/io.py +6 -305
  59. tnfr/io.pyi +13 -8
  60. tnfr/mathematics/__init__.py +81 -0
  61. tnfr/mathematics/backend.py +426 -0
  62. tnfr/mathematics/dynamics.py +398 -0
  63. tnfr/mathematics/epi.py +254 -0
  64. tnfr/mathematics/generators.py +222 -0
  65. tnfr/mathematics/metrics.py +119 -0
  66. tnfr/mathematics/operators.py +233 -0
  67. tnfr/mathematics/operators_factory.py +71 -0
  68. tnfr/mathematics/projection.py +78 -0
  69. tnfr/mathematics/runtime.py +173 -0
  70. tnfr/mathematics/spaces.py +247 -0
  71. tnfr/mathematics/transforms.py +292 -0
  72. tnfr/metrics/__init__.py +10 -10
  73. tnfr/metrics/coherence.py +123 -94
  74. tnfr/metrics/common.py +22 -13
  75. tnfr/metrics/common.pyi +42 -11
  76. tnfr/metrics/core.py +72 -14
  77. tnfr/metrics/diagnosis.py +48 -57
  78. tnfr/metrics/diagnosis.pyi +3 -7
  79. tnfr/metrics/export.py +3 -5
  80. tnfr/metrics/glyph_timing.py +41 -31
  81. tnfr/metrics/reporting.py +13 -6
  82. tnfr/metrics/sense_index.py +884 -114
  83. tnfr/metrics/trig.py +167 -11
  84. tnfr/metrics/trig.pyi +1 -0
  85. tnfr/metrics/trig_cache.py +112 -15
  86. tnfr/node.py +400 -17
  87. tnfr/node.pyi +55 -38
  88. tnfr/observers.py +111 -8
  89. tnfr/observers.pyi +0 -15
  90. tnfr/ontosim.py +9 -6
  91. tnfr/ontosim.pyi +0 -5
  92. tnfr/operators/__init__.py +529 -42
  93. tnfr/operators/__init__.pyi +14 -0
  94. tnfr/operators/definitions.py +350 -18
  95. tnfr/operators/definitions.pyi +0 -14
  96. tnfr/operators/grammar.py +760 -0
  97. tnfr/operators/jitter.py +28 -22
  98. tnfr/operators/registry.py +7 -12
  99. tnfr/operators/registry.pyi +0 -2
  100. tnfr/operators/remesh.py +38 -61
  101. tnfr/rng.py +17 -300
  102. tnfr/schemas/__init__.py +8 -0
  103. tnfr/schemas/grammar.json +94 -0
  104. tnfr/selector.py +3 -4
  105. tnfr/selector.pyi +1 -1
  106. tnfr/sense.py +22 -24
  107. tnfr/sense.pyi +0 -7
  108. tnfr/structural.py +504 -21
  109. tnfr/structural.pyi +41 -18
  110. tnfr/telemetry/__init__.py +23 -1
  111. tnfr/telemetry/cache_metrics.py +226 -0
  112. tnfr/telemetry/nu_f.py +423 -0
  113. tnfr/telemetry/nu_f.pyi +123 -0
  114. tnfr/tokens.py +1 -4
  115. tnfr/tokens.pyi +1 -6
  116. tnfr/trace.py +20 -53
  117. tnfr/trace.pyi +9 -37
  118. tnfr/types.py +244 -15
  119. tnfr/types.pyi +200 -14
  120. tnfr/units.py +69 -0
  121. tnfr/units.pyi +16 -0
  122. tnfr/utils/__init__.py +107 -48
  123. tnfr/utils/__init__.pyi +80 -11
  124. tnfr/utils/cache.py +1705 -65
  125. tnfr/utils/cache.pyi +370 -58
  126. tnfr/utils/chunks.py +104 -0
  127. tnfr/utils/chunks.pyi +21 -0
  128. tnfr/utils/data.py +95 -5
  129. tnfr/utils/data.pyi +8 -17
  130. tnfr/utils/graph.py +2 -4
  131. tnfr/utils/init.py +31 -7
  132. tnfr/utils/init.pyi +4 -11
  133. tnfr/utils/io.py +313 -14
  134. tnfr/{helpers → utils}/numeric.py +50 -24
  135. tnfr/utils/numeric.pyi +21 -0
  136. tnfr/validation/__init__.py +92 -4
  137. tnfr/validation/__init__.pyi +77 -17
  138. tnfr/validation/compatibility.py +79 -43
  139. tnfr/validation/compatibility.pyi +4 -6
  140. tnfr/validation/grammar.py +55 -133
  141. tnfr/validation/grammar.pyi +37 -8
  142. tnfr/validation/graph.py +138 -0
  143. tnfr/validation/graph.pyi +17 -0
  144. tnfr/validation/rules.py +161 -74
  145. tnfr/validation/rules.pyi +55 -18
  146. tnfr/validation/runtime.py +263 -0
  147. tnfr/validation/runtime.pyi +31 -0
  148. tnfr/validation/soft_filters.py +170 -0
  149. tnfr/validation/soft_filters.pyi +37 -0
  150. tnfr/validation/spectral.py +159 -0
  151. tnfr/validation/spectral.pyi +46 -0
  152. tnfr/validation/syntax.py +28 -139
  153. tnfr/validation/syntax.pyi +7 -4
  154. tnfr/validation/window.py +39 -0
  155. tnfr/validation/window.pyi +1 -0
  156. tnfr/viz/__init__.py +9 -0
  157. tnfr/viz/matplotlib.py +246 -0
  158. {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/METADATA +63 -19
  159. tnfr-7.0.0.dist-info/RECORD +185 -0
  160. {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/licenses/LICENSE.md +1 -1
  161. tnfr/constants_glyphs.py +0 -16
  162. tnfr/constants_glyphs.pyi +0 -12
  163. tnfr/grammar.py +0 -25
  164. tnfr/grammar.pyi +0 -13
  165. tnfr/helpers/__init__.py +0 -151
  166. tnfr/helpers/__init__.pyi +0 -66
  167. tnfr/helpers/numeric.pyi +0 -12
  168. tnfr/presets.py +0 -15
  169. tnfr/presets.pyi +0 -7
  170. tnfr/utils/io.pyi +0 -10
  171. tnfr/utils/validators.py +0 -130
  172. tnfr/utils/validators.pyi +0 -19
  173. tnfr-6.0.0.dist-info/RECORD +0 -157
  174. {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/WHEEL +0 -0
  175. {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/entry_points.txt +0 -0
  176. {tnfr-6.0.0.dist-info → tnfr-7.0.0.dist-info}/top_level.txt +0 -0
tnfr/dynamics/dnfr.pyi CHANGED
@@ -1,6 +1,7 @@
1
1
  from typing import Any
2
2
 
3
3
  from tnfr.types import DeltaNFRHook, TNFRGraph
4
+ from tnfr.utils.cache import DnfrCache as DnfrCache
4
5
 
5
6
  __all__: tuple[str, ...]
6
7
 
@@ -10,17 +11,9 @@ def default_compute_delta_nfr(
10
11
  cache_size: int | None = ...,
11
12
  n_jobs: int | None = ...,
12
13
  ) -> None: ...
13
-
14
-
15
14
  def dnfr_epi_vf_mixed(G: TNFRGraph, *, n_jobs: int | None = ...) -> None: ...
16
-
17
-
18
15
  def dnfr_laplacian(G: TNFRGraph, *, n_jobs: int | None = ...) -> None: ...
19
-
20
-
21
16
  def dnfr_phase_only(G: TNFRGraph, *, n_jobs: int | None = ...) -> None: ...
22
-
23
-
24
17
  def set_delta_nfr_hook(
25
18
  G: TNFRGraph,
26
19
  func: DeltaNFRHook,
@@ -28,6 +21,4 @@ def set_delta_nfr_hook(
28
21
  name: str | None = ...,
29
22
  note: str | None = ...,
30
23
  ) -> None: ...
31
-
32
-
33
24
  def __getattr__(name: str) -> Any: ...
@@ -1,3 +1,5 @@
1
+ """Canonical ΔNFR integrators driving TNFR runtime evolution."""
2
+
1
3
  from __future__ import annotations
2
4
 
3
5
  import math
@@ -9,22 +11,20 @@ from typing import Any, Literal, cast
9
11
 
10
12
  import networkx as nx
11
13
 
12
- from ..constants import (
13
- DEFAULTS,
14
- get_aliases,
14
+ from .._compat import TypeAlias
15
+ from ..alias import collect_attr, get_attr, get_attr_str, set_attr, set_attr_str
16
+ from ..constants import DEFAULTS
17
+ from ..constants.aliases import (
18
+ ALIAS_D2EPI,
19
+ ALIAS_DEPI,
20
+ ALIAS_DNFR,
21
+ ALIAS_EPI,
22
+ ALIAS_EPI_KIND,
23
+ ALIAS_VF,
15
24
  )
16
25
  from ..gamma import _get_gamma_spec, eval_gamma
17
- from ..alias import collect_attr, get_attr, get_attr_str, set_attr, set_attr_str
18
- from ..utils import get_numpy
19
26
  from ..types import NodeId, TNFRGraph
20
- from .._compat import TypeAlias
21
-
22
- ALIAS_VF = get_aliases("VF")
23
- ALIAS_DNFR = get_aliases("DNFR")
24
- ALIAS_DEPI = get_aliases("DEPI")
25
- ALIAS_EPI = get_aliases("EPI")
26
- ALIAS_EPI_KIND = get_aliases("EPI_KIND")
27
- ALIAS_D2EPI = get_aliases("D2EPI")
27
+ from ..utils import get_numpy, resolve_chunk_size
28
28
 
29
29
  __all__ = (
30
30
  "AbstractIntegrator",
@@ -63,9 +63,7 @@ def _gamma_worker(task: tuple[list[NodeId], float]) -> list[tuple[NodeId, float]
63
63
  chunk, t = task
64
64
  if _PARALLEL_GRAPH is None:
65
65
  raise RuntimeError("Parallel Γ worker initialised without graph reference")
66
- return [
67
- (node, float(eval_gamma(_PARALLEL_GRAPH, node, t))) for node in chunk
68
- ]
66
+ return [(node, float(eval_gamma(_PARALLEL_GRAPH, node, t))) for node in chunk]
69
67
 
70
68
 
71
69
  def _normalise_jobs(n_jobs: int | None, total: int) -> int | None:
@@ -86,7 +84,7 @@ def _chunk_nodes(nodes: list[NodeId], chunk_size: int) -> Iterable[list[NodeId]]
86
84
  """Yield deterministic chunks from ``nodes`` respecting insertion order."""
87
85
 
88
86
  for idx in range(0, len(nodes), chunk_size):
89
- yield nodes[idx:idx + chunk_size]
87
+ yield nodes[idx : idx + chunk_size]
90
88
 
91
89
 
92
90
  def _apply_increment_chunk(
@@ -127,7 +125,12 @@ def _evaluate_gamma_map(
127
125
  if workers is None:
128
126
  return {n: float(eval_gamma(G, n, t)) for n in nodes}
129
127
 
130
- chunk_size = max(1, math.ceil(len(nodes) / (workers * 4)))
128
+ approx_chunk = math.ceil(len(nodes) / (workers * 4)) if workers > 0 else None
129
+ chunk_size = resolve_chunk_size(
130
+ approx_chunk,
131
+ len(nodes),
132
+ minimum=1,
133
+ )
131
134
  mp_ctx = get_context("spawn")
132
135
  tasks = ((chunk, t) for chunk in _chunk_nodes(nodes, chunk_size))
133
136
 
@@ -153,9 +156,16 @@ def prepare_integration_params(
153
156
  ) -> tuple[float, int, float, Literal["euler", "rk4"]]:
154
157
  """Validate and normalise ``dt``, ``t`` and ``method`` for integration.
155
158
 
156
- Returns ``(dt_step, steps, t0, method)`` where ``dt_step`` is the
157
- effective step, ``steps`` the number of substeps and ``t0`` the prepared
158
- initial time.
159
+ The function raises :class:`TypeError` when ``dt`` cannot be coerced to a
160
+ number, :class:`ValueError` if ``dt`` is negative, and another
161
+ :class:`ValueError` when an unsupported method is requested. When ``dt``
162
+ exceeds a positive ``DT_MIN`` stored on ``G`` the span is deterministically
163
+ subdivided into integer steps so that the resulting ``dt_step`` never falls
164
+ below that minimum threshold.
165
+
166
+ Returns ``(dt_step, steps, t0, method)`` where ``dt_step`` is the effective
167
+ step, ``steps`` the number of substeps and ``t0`` the prepared initial
168
+ time.
159
169
  """
160
170
  if dt is None:
161
171
  dt = float(G.graph.get("DT", DEFAULTS["DT"]))
@@ -173,20 +183,19 @@ def prepare_integration_params(
173
183
 
174
184
  method_value = (
175
185
  method
176
- or G.graph.get(
177
- "INTEGRATOR_METHOD", DEFAULTS.get("INTEGRATOR_METHOD", "euler")
178
- )
186
+ or G.graph.get("INTEGRATOR_METHOD", DEFAULTS.get("INTEGRATOR_METHOD", "euler"))
179
187
  ).lower()
180
188
  if method_value not in ("euler", "rk4"):
181
189
  raise ValueError("method must be 'euler' or 'rk4'")
182
190
 
183
191
  dt_min = float(G.graph.get("DT_MIN", DEFAULTS.get("DT_MIN", 0.0)))
192
+ steps = 1
184
193
  if dt_min > 0 and dt > dt_min:
185
- steps = int(math.ceil(dt / dt_min))
186
- else:
187
- steps = 1
188
- # ``steps`` is guaranteed to be ≥1 at this point
189
- dt_step = dt / steps
194
+ ratio = dt / dt_min
195
+ steps = max(1, int(math.floor(ratio + 1e-12)))
196
+ if dt / steps < dt_min:
197
+ steps = int(math.ceil(ratio))
198
+ dt_step = dt / steps if steps else 0.0
190
199
 
191
200
  return dt_step, steps, t, cast(Literal["euler", "rk4"], method_value)
192
201
 
@@ -263,7 +272,12 @@ def _apply_increments(
263
272
  if workers is None:
264
273
  return dict(_apply_increment_chunk(payload, dt_step, method))
265
274
 
266
- chunk_size = max(1, math.ceil(len(nodes) / (workers * 4)))
275
+ approx_chunk = math.ceil(len(nodes) / (workers * 4)) if workers > 0 else None
276
+ chunk_size = resolve_chunk_size(
277
+ approx_chunk,
278
+ len(nodes),
279
+ minimum=1,
280
+ )
267
281
  mp_ctx = get_context("spawn")
268
282
 
269
283
  results: NodalUpdate = {}
@@ -398,9 +412,7 @@ def _build_gamma_increments(
398
412
  g4_map = _evaluate_gamma_map(G, nodes, t_end, n_jobs=n_jobs)
399
413
  gamma_maps = (g1_map, g_mid_map, g_mid_map, g4_map)
400
414
  else: # method == "euler"
401
- gamma_maps = (
402
- _evaluate_gamma_map(G, nodes, t_local, n_jobs=n_jobs),
403
- )
415
+ gamma_maps = (_evaluate_gamma_map(G, nodes, t_local, n_jobs=n_jobs),)
404
416
 
405
417
  return _collect_nodal_increments(G, gamma_maps, method=method)
406
418
 
@@ -481,6 +493,8 @@ class DefaultIntegrator(AbstractIntegrator):
481
493
  method: str | None,
482
494
  n_jobs: int | None,
483
495
  ) -> None:
496
+ """Integrate the nodal equation updating EPI, ΔEPI and Δ²EPI."""
497
+
484
498
  if not isinstance(
485
499
  graph, (nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph)
486
500
  ):
@@ -24,7 +24,6 @@ def prepare_integration_params(
24
24
  t: float | None = ...,
25
25
  method: Literal["euler", "rk4"] | None = ...,
26
26
  ) -> tuple[float, int, float, Literal["euler", "rk4"]]: ...
27
-
28
27
  def update_epi_via_nodal_equation(
29
28
  G: TNFRGraph,
30
29
  *,