tnfr 3.0.3__py3-none-any.whl → 8.5.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 (360) hide show
  1. tnfr/__init__.py +375 -56
  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 +723 -0
  8. tnfr/alias.pyi +108 -0
  9. tnfr/backends/__init__.py +354 -0
  10. tnfr/backends/jax_backend.py +173 -0
  11. tnfr/backends/numpy_backend.py +238 -0
  12. tnfr/backends/optimized_numpy.py +420 -0
  13. tnfr/backends/torch_backend.py +408 -0
  14. tnfr/cache.py +171 -0
  15. tnfr/cache.pyi +13 -0
  16. tnfr/cli/__init__.py +110 -0
  17. tnfr/cli/__init__.pyi +26 -0
  18. tnfr/cli/arguments.py +489 -0
  19. tnfr/cli/arguments.pyi +29 -0
  20. tnfr/cli/execution.py +914 -0
  21. tnfr/cli/execution.pyi +70 -0
  22. tnfr/cli/interactive_validator.py +614 -0
  23. tnfr/cli/utils.py +51 -0
  24. tnfr/cli/utils.pyi +7 -0
  25. tnfr/cli/validate.py +236 -0
  26. tnfr/compat/__init__.py +85 -0
  27. tnfr/compat/dataclass.py +136 -0
  28. tnfr/compat/jsonschema_stub.py +61 -0
  29. tnfr/compat/matplotlib_stub.py +73 -0
  30. tnfr/compat/numpy_stub.py +155 -0
  31. tnfr/config/__init__.py +224 -0
  32. tnfr/config/__init__.pyi +10 -0
  33. tnfr/config/constants.py +104 -0
  34. tnfr/config/constants.pyi +12 -0
  35. tnfr/config/defaults.py +54 -0
  36. tnfr/config/defaults_core.py +212 -0
  37. tnfr/config/defaults_init.py +33 -0
  38. tnfr/config/defaults_metric.py +104 -0
  39. tnfr/config/feature_flags.py +81 -0
  40. tnfr/config/feature_flags.pyi +16 -0
  41. tnfr/config/glyph_constants.py +31 -0
  42. tnfr/config/init.py +77 -0
  43. tnfr/config/init.pyi +8 -0
  44. tnfr/config/operator_names.py +254 -0
  45. tnfr/config/operator_names.pyi +36 -0
  46. tnfr/config/physics_derivation.py +354 -0
  47. tnfr/config/presets.py +83 -0
  48. tnfr/config/presets.pyi +7 -0
  49. tnfr/config/security.py +927 -0
  50. tnfr/config/thresholds.py +114 -0
  51. tnfr/config/tnfr_config.py +498 -0
  52. tnfr/constants/__init__.py +92 -0
  53. tnfr/constants/__init__.pyi +92 -0
  54. tnfr/constants/aliases.py +33 -0
  55. tnfr/constants/aliases.pyi +27 -0
  56. tnfr/constants/init.py +33 -0
  57. tnfr/constants/init.pyi +12 -0
  58. tnfr/constants/metric.py +104 -0
  59. tnfr/constants/metric.pyi +19 -0
  60. tnfr/core/__init__.py +33 -0
  61. tnfr/core/container.py +226 -0
  62. tnfr/core/default_implementations.py +329 -0
  63. tnfr/core/interfaces.py +279 -0
  64. tnfr/dynamics/__init__.py +238 -0
  65. tnfr/dynamics/__init__.pyi +83 -0
  66. tnfr/dynamics/adaptation.py +267 -0
  67. tnfr/dynamics/adaptation.pyi +7 -0
  68. tnfr/dynamics/adaptive_sequences.py +189 -0
  69. tnfr/dynamics/adaptive_sequences.pyi +14 -0
  70. tnfr/dynamics/aliases.py +23 -0
  71. tnfr/dynamics/aliases.pyi +19 -0
  72. tnfr/dynamics/bifurcation.py +232 -0
  73. tnfr/dynamics/canonical.py +229 -0
  74. tnfr/dynamics/canonical.pyi +48 -0
  75. tnfr/dynamics/coordination.py +385 -0
  76. tnfr/dynamics/coordination.pyi +25 -0
  77. tnfr/dynamics/dnfr.py +3034 -0
  78. tnfr/dynamics/dnfr.pyi +26 -0
  79. tnfr/dynamics/dynamic_limits.py +225 -0
  80. tnfr/dynamics/feedback.py +252 -0
  81. tnfr/dynamics/feedback.pyi +24 -0
  82. tnfr/dynamics/fused_dnfr.py +454 -0
  83. tnfr/dynamics/homeostasis.py +157 -0
  84. tnfr/dynamics/homeostasis.pyi +14 -0
  85. tnfr/dynamics/integrators.py +661 -0
  86. tnfr/dynamics/integrators.pyi +36 -0
  87. tnfr/dynamics/learning.py +310 -0
  88. tnfr/dynamics/learning.pyi +33 -0
  89. tnfr/dynamics/metabolism.py +254 -0
  90. tnfr/dynamics/nbody.py +796 -0
  91. tnfr/dynamics/nbody_tnfr.py +783 -0
  92. tnfr/dynamics/propagation.py +326 -0
  93. tnfr/dynamics/runtime.py +908 -0
  94. tnfr/dynamics/runtime.pyi +77 -0
  95. tnfr/dynamics/sampling.py +36 -0
  96. tnfr/dynamics/sampling.pyi +7 -0
  97. tnfr/dynamics/selectors.py +711 -0
  98. tnfr/dynamics/selectors.pyi +85 -0
  99. tnfr/dynamics/structural_clip.py +207 -0
  100. tnfr/errors/__init__.py +37 -0
  101. tnfr/errors/contextual.py +492 -0
  102. tnfr/execution.py +223 -0
  103. tnfr/execution.pyi +45 -0
  104. tnfr/extensions/__init__.py +205 -0
  105. tnfr/extensions/__init__.pyi +18 -0
  106. tnfr/extensions/base.py +173 -0
  107. tnfr/extensions/base.pyi +35 -0
  108. tnfr/extensions/business/__init__.py +71 -0
  109. tnfr/extensions/business/__init__.pyi +11 -0
  110. tnfr/extensions/business/cookbook.py +88 -0
  111. tnfr/extensions/business/cookbook.pyi +8 -0
  112. tnfr/extensions/business/health_analyzers.py +202 -0
  113. tnfr/extensions/business/health_analyzers.pyi +9 -0
  114. tnfr/extensions/business/patterns.py +183 -0
  115. tnfr/extensions/business/patterns.pyi +8 -0
  116. tnfr/extensions/medical/__init__.py +73 -0
  117. tnfr/extensions/medical/__init__.pyi +11 -0
  118. tnfr/extensions/medical/cookbook.py +88 -0
  119. tnfr/extensions/medical/cookbook.pyi +8 -0
  120. tnfr/extensions/medical/health_analyzers.py +181 -0
  121. tnfr/extensions/medical/health_analyzers.pyi +9 -0
  122. tnfr/extensions/medical/patterns.py +163 -0
  123. tnfr/extensions/medical/patterns.pyi +8 -0
  124. tnfr/flatten.py +262 -0
  125. tnfr/flatten.pyi +21 -0
  126. tnfr/gamma.py +354 -0
  127. tnfr/gamma.pyi +36 -0
  128. tnfr/glyph_history.py +377 -0
  129. tnfr/glyph_history.pyi +35 -0
  130. tnfr/glyph_runtime.py +19 -0
  131. tnfr/glyph_runtime.pyi +8 -0
  132. tnfr/immutable.py +218 -0
  133. tnfr/immutable.pyi +36 -0
  134. tnfr/initialization.py +203 -0
  135. tnfr/initialization.pyi +65 -0
  136. tnfr/io.py +10 -0
  137. tnfr/io.pyi +13 -0
  138. tnfr/locking.py +37 -0
  139. tnfr/locking.pyi +7 -0
  140. tnfr/mathematics/__init__.py +79 -0
  141. tnfr/mathematics/backend.py +453 -0
  142. tnfr/mathematics/backend.pyi +99 -0
  143. tnfr/mathematics/dynamics.py +408 -0
  144. tnfr/mathematics/dynamics.pyi +90 -0
  145. tnfr/mathematics/epi.py +391 -0
  146. tnfr/mathematics/epi.pyi +65 -0
  147. tnfr/mathematics/generators.py +242 -0
  148. tnfr/mathematics/generators.pyi +29 -0
  149. tnfr/mathematics/metrics.py +119 -0
  150. tnfr/mathematics/metrics.pyi +16 -0
  151. tnfr/mathematics/operators.py +239 -0
  152. tnfr/mathematics/operators.pyi +59 -0
  153. tnfr/mathematics/operators_factory.py +124 -0
  154. tnfr/mathematics/operators_factory.pyi +11 -0
  155. tnfr/mathematics/projection.py +87 -0
  156. tnfr/mathematics/projection.pyi +33 -0
  157. tnfr/mathematics/runtime.py +182 -0
  158. tnfr/mathematics/runtime.pyi +64 -0
  159. tnfr/mathematics/spaces.py +256 -0
  160. tnfr/mathematics/spaces.pyi +83 -0
  161. tnfr/mathematics/transforms.py +305 -0
  162. tnfr/mathematics/transforms.pyi +62 -0
  163. tnfr/metrics/__init__.py +79 -0
  164. tnfr/metrics/__init__.pyi +20 -0
  165. tnfr/metrics/buffer_cache.py +163 -0
  166. tnfr/metrics/buffer_cache.pyi +24 -0
  167. tnfr/metrics/cache_utils.py +214 -0
  168. tnfr/metrics/coherence.py +2009 -0
  169. tnfr/metrics/coherence.pyi +129 -0
  170. tnfr/metrics/common.py +158 -0
  171. tnfr/metrics/common.pyi +35 -0
  172. tnfr/metrics/core.py +316 -0
  173. tnfr/metrics/core.pyi +13 -0
  174. tnfr/metrics/diagnosis.py +833 -0
  175. tnfr/metrics/diagnosis.pyi +86 -0
  176. tnfr/metrics/emergence.py +245 -0
  177. tnfr/metrics/export.py +179 -0
  178. tnfr/metrics/export.pyi +7 -0
  179. tnfr/metrics/glyph_timing.py +379 -0
  180. tnfr/metrics/glyph_timing.pyi +81 -0
  181. tnfr/metrics/learning_metrics.py +280 -0
  182. tnfr/metrics/learning_metrics.pyi +21 -0
  183. tnfr/metrics/phase_coherence.py +351 -0
  184. tnfr/metrics/phase_compatibility.py +349 -0
  185. tnfr/metrics/reporting.py +183 -0
  186. tnfr/metrics/reporting.pyi +25 -0
  187. tnfr/metrics/sense_index.py +1203 -0
  188. tnfr/metrics/sense_index.pyi +9 -0
  189. tnfr/metrics/trig.py +373 -0
  190. tnfr/metrics/trig.pyi +13 -0
  191. tnfr/metrics/trig_cache.py +233 -0
  192. tnfr/metrics/trig_cache.pyi +10 -0
  193. tnfr/multiscale/__init__.py +32 -0
  194. tnfr/multiscale/hierarchical.py +517 -0
  195. tnfr/node.py +763 -0
  196. tnfr/node.pyi +139 -0
  197. tnfr/observers.py +255 -130
  198. tnfr/observers.pyi +31 -0
  199. tnfr/ontosim.py +144 -137
  200. tnfr/ontosim.pyi +28 -0
  201. tnfr/operators/__init__.py +1672 -0
  202. tnfr/operators/__init__.pyi +31 -0
  203. tnfr/operators/algebra.py +277 -0
  204. tnfr/operators/canonical_patterns.py +420 -0
  205. tnfr/operators/cascade.py +267 -0
  206. tnfr/operators/cycle_detection.py +358 -0
  207. tnfr/operators/definitions.py +4108 -0
  208. tnfr/operators/definitions.pyi +78 -0
  209. tnfr/operators/grammar.py +1164 -0
  210. tnfr/operators/grammar.pyi +140 -0
  211. tnfr/operators/hamiltonian.py +710 -0
  212. tnfr/operators/health_analyzer.py +809 -0
  213. tnfr/operators/jitter.py +272 -0
  214. tnfr/operators/jitter.pyi +11 -0
  215. tnfr/operators/lifecycle.py +314 -0
  216. tnfr/operators/metabolism.py +618 -0
  217. tnfr/operators/metrics.py +2138 -0
  218. tnfr/operators/network_analysis/__init__.py +27 -0
  219. tnfr/operators/network_analysis/source_detection.py +186 -0
  220. tnfr/operators/nodal_equation.py +395 -0
  221. tnfr/operators/pattern_detection.py +660 -0
  222. tnfr/operators/patterns.py +669 -0
  223. tnfr/operators/postconditions/__init__.py +38 -0
  224. tnfr/operators/postconditions/mutation.py +236 -0
  225. tnfr/operators/preconditions/__init__.py +1226 -0
  226. tnfr/operators/preconditions/coherence.py +305 -0
  227. tnfr/operators/preconditions/dissonance.py +236 -0
  228. tnfr/operators/preconditions/emission.py +128 -0
  229. tnfr/operators/preconditions/mutation.py +580 -0
  230. tnfr/operators/preconditions/reception.py +125 -0
  231. tnfr/operators/preconditions/resonance.py +364 -0
  232. tnfr/operators/registry.py +74 -0
  233. tnfr/operators/registry.pyi +9 -0
  234. tnfr/operators/remesh.py +1809 -0
  235. tnfr/operators/remesh.pyi +26 -0
  236. tnfr/operators/structural_units.py +268 -0
  237. tnfr/operators/unified_grammar.py +105 -0
  238. tnfr/parallel/__init__.py +54 -0
  239. tnfr/parallel/auto_scaler.py +234 -0
  240. tnfr/parallel/distributed.py +384 -0
  241. tnfr/parallel/engine.py +238 -0
  242. tnfr/parallel/gpu_engine.py +420 -0
  243. tnfr/parallel/monitoring.py +248 -0
  244. tnfr/parallel/partitioner.py +459 -0
  245. tnfr/py.typed +0 -0
  246. tnfr/recipes/__init__.py +22 -0
  247. tnfr/recipes/cookbook.py +743 -0
  248. tnfr/rng.py +178 -0
  249. tnfr/rng.pyi +26 -0
  250. tnfr/schemas/__init__.py +8 -0
  251. tnfr/schemas/grammar.json +94 -0
  252. tnfr/sdk/__init__.py +107 -0
  253. tnfr/sdk/__init__.pyi +19 -0
  254. tnfr/sdk/adaptive_system.py +173 -0
  255. tnfr/sdk/adaptive_system.pyi +21 -0
  256. tnfr/sdk/builders.py +370 -0
  257. tnfr/sdk/builders.pyi +51 -0
  258. tnfr/sdk/fluent.py +1121 -0
  259. tnfr/sdk/fluent.pyi +74 -0
  260. tnfr/sdk/templates.py +342 -0
  261. tnfr/sdk/templates.pyi +41 -0
  262. tnfr/sdk/utils.py +341 -0
  263. tnfr/secure_config.py +46 -0
  264. tnfr/security/__init__.py +70 -0
  265. tnfr/security/database.py +514 -0
  266. tnfr/security/subprocess.py +503 -0
  267. tnfr/security/validation.py +290 -0
  268. tnfr/selector.py +247 -0
  269. tnfr/selector.pyi +19 -0
  270. tnfr/sense.py +378 -0
  271. tnfr/sense.pyi +23 -0
  272. tnfr/services/__init__.py +17 -0
  273. tnfr/services/orchestrator.py +325 -0
  274. tnfr/sparse/__init__.py +39 -0
  275. tnfr/sparse/representations.py +492 -0
  276. tnfr/structural.py +705 -0
  277. tnfr/structural.pyi +83 -0
  278. tnfr/telemetry/__init__.py +35 -0
  279. tnfr/telemetry/cache_metrics.py +226 -0
  280. tnfr/telemetry/cache_metrics.pyi +64 -0
  281. tnfr/telemetry/nu_f.py +422 -0
  282. tnfr/telemetry/nu_f.pyi +108 -0
  283. tnfr/telemetry/verbosity.py +36 -0
  284. tnfr/telemetry/verbosity.pyi +15 -0
  285. tnfr/tokens.py +58 -0
  286. tnfr/tokens.pyi +36 -0
  287. tnfr/tools/__init__.py +20 -0
  288. tnfr/tools/domain_templates.py +478 -0
  289. tnfr/tools/sequence_generator.py +846 -0
  290. tnfr/topology/__init__.py +13 -0
  291. tnfr/topology/asymmetry.py +151 -0
  292. tnfr/trace.py +543 -0
  293. tnfr/trace.pyi +42 -0
  294. tnfr/tutorials/__init__.py +38 -0
  295. tnfr/tutorials/autonomous_evolution.py +285 -0
  296. tnfr/tutorials/interactive.py +1576 -0
  297. tnfr/tutorials/structural_metabolism.py +238 -0
  298. tnfr/types.py +775 -0
  299. tnfr/types.pyi +357 -0
  300. tnfr/units.py +68 -0
  301. tnfr/units.pyi +13 -0
  302. tnfr/utils/__init__.py +282 -0
  303. tnfr/utils/__init__.pyi +215 -0
  304. tnfr/utils/cache.py +4223 -0
  305. tnfr/utils/cache.pyi +470 -0
  306. tnfr/utils/callbacks.py +375 -0
  307. tnfr/utils/callbacks.pyi +49 -0
  308. tnfr/utils/chunks.py +108 -0
  309. tnfr/utils/chunks.pyi +22 -0
  310. tnfr/utils/data.py +428 -0
  311. tnfr/utils/data.pyi +74 -0
  312. tnfr/utils/graph.py +85 -0
  313. tnfr/utils/graph.pyi +10 -0
  314. tnfr/utils/init.py +821 -0
  315. tnfr/utils/init.pyi +80 -0
  316. tnfr/utils/io.py +559 -0
  317. tnfr/utils/io.pyi +66 -0
  318. tnfr/utils/numeric.py +114 -0
  319. tnfr/utils/numeric.pyi +21 -0
  320. tnfr/validation/__init__.py +257 -0
  321. tnfr/validation/__init__.pyi +85 -0
  322. tnfr/validation/compatibility.py +460 -0
  323. tnfr/validation/compatibility.pyi +6 -0
  324. tnfr/validation/config.py +73 -0
  325. tnfr/validation/graph.py +139 -0
  326. tnfr/validation/graph.pyi +18 -0
  327. tnfr/validation/input_validation.py +755 -0
  328. tnfr/validation/invariants.py +712 -0
  329. tnfr/validation/rules.py +253 -0
  330. tnfr/validation/rules.pyi +44 -0
  331. tnfr/validation/runtime.py +279 -0
  332. tnfr/validation/runtime.pyi +28 -0
  333. tnfr/validation/sequence_validator.py +162 -0
  334. tnfr/validation/soft_filters.py +170 -0
  335. tnfr/validation/soft_filters.pyi +32 -0
  336. tnfr/validation/spectral.py +164 -0
  337. tnfr/validation/spectral.pyi +42 -0
  338. tnfr/validation/validator.py +1266 -0
  339. tnfr/validation/window.py +39 -0
  340. tnfr/validation/window.pyi +1 -0
  341. tnfr/visualization/__init__.py +98 -0
  342. tnfr/visualization/cascade_viz.py +256 -0
  343. tnfr/visualization/hierarchy.py +284 -0
  344. tnfr/visualization/sequence_plotter.py +784 -0
  345. tnfr/viz/__init__.py +60 -0
  346. tnfr/viz/matplotlib.py +278 -0
  347. tnfr/viz/matplotlib.pyi +35 -0
  348. tnfr-8.5.0.dist-info/METADATA +573 -0
  349. tnfr-8.5.0.dist-info/RECORD +353 -0
  350. tnfr-8.5.0.dist-info/entry_points.txt +3 -0
  351. tnfr-3.0.3.dist-info/licenses/LICENSE.txt → tnfr-8.5.0.dist-info/licenses/LICENSE.md +1 -1
  352. tnfr/constants.py +0 -183
  353. tnfr/dynamics.py +0 -543
  354. tnfr/helpers.py +0 -198
  355. tnfr/main.py +0 -37
  356. tnfr/operators.py +0 -296
  357. tnfr-3.0.3.dist-info/METADATA +0 -35
  358. tnfr-3.0.3.dist-info/RECORD +0 -13
  359. {tnfr-3.0.3.dist-info → tnfr-8.5.0.dist-info}/WHEEL +0 -0
  360. {tnfr-3.0.3.dist-info → tnfr-8.5.0.dist-info}/top_level.txt +0 -0
tnfr/sdk/fluent.py ADDED
@@ -0,0 +1,1121 @@
1
+ """Fluent API for simplified TNFR network creation and simulation.
2
+
3
+ This module implements the core :class:`TNFRNetwork` class that provides
4
+ a user-friendly, chainable interface for working with TNFR networks. The
5
+ API hides low-level complexity while maintaining full theoretical fidelity
6
+ to TNFR invariants and structural operators.
7
+
8
+ Examples
9
+ --------
10
+ Create and simulate a simple TNFR network:
11
+
12
+ >>> network = TNFRNetwork("my_experiment")
13
+ >>> network.add_nodes(10).connect_nodes(0.3, "random")
14
+ >>> network.apply_sequence("basic_activation", repeat=5)
15
+ >>> results = network.measure()
16
+ >>> print(results.summary())
17
+
18
+ Chain operations for rapid prototyping:
19
+
20
+ >>> results = (TNFRNetwork("quick_test")
21
+ ... .add_nodes(20)
22
+ ... .connect_nodes(0.4, "ring")
23
+ ... .apply_sequence("network_sync", repeat=10)
24
+ ... .measure())
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ from dataclasses import dataclass, field
30
+ from pathlib import Path
31
+ from typing import Any, Dict, List, Optional, Union
32
+
33
+ try:
34
+ import numpy as np
35
+
36
+ _HAS_NUMPY = True
37
+ except ImportError:
38
+ np = None # type: ignore[assignment]
39
+ _HAS_NUMPY = False
40
+
41
+ import networkx as nx
42
+
43
+ from ..structural import create_nfr, run_sequence
44
+ from ..metrics.coherence import compute_coherence
45
+ from ..metrics.sense_index import compute_Si, compute_Si_node
46
+ from ..constants.aliases import ALIAS_DNFR
47
+ from ..validation import validate_sequence
48
+
49
+ __all__ = ["TNFRNetwork", "NetworkConfig", "NetworkResults"]
50
+
51
+
52
+ # Predefined operator sequences for common patterns (optimized with Grammar 2.0)
53
+ # All sequences must respect TNFR grammar rules:
54
+ # - Start with emission or recursivity
55
+ # - Include reception→coherence segment
56
+ # - Include coupling/dissonance/resonance segment
57
+ # - End with recursivity, silence, or transition
58
+ #
59
+ # Sequences optimized for structural health ≥ 0.7 using Grammar 2.0:
60
+ # - Balanced stabilizers/destabilizers
61
+ # - Harmonic frequency transitions
62
+ # - Proper closure operators
63
+ # - Pattern completeness
64
+ NAMED_SEQUENCES = {
65
+ # Basic activation pattern - optimized with expansion for balance
66
+ # Health: 0.79 (good) - Pattern: activation
67
+ # Includes controlled expansion for structural balance
68
+ "basic_activation": [
69
+ "emission", # AL: Initiate coherent structure
70
+ "reception", # EN: Stabilize incoming energy
71
+ "coherence", # IL: Primary stabilization (required)
72
+ "expansion", # VAL: Controlled growth (balance +0.33)
73
+ "resonance", # RA: Amplify coherent structure
74
+ "silence", # SHA: Sustainable pause state
75
+ ],
76
+ # Stabilization with expansion - optimized for regenerative cycles
77
+ # Health: 0.76 (good) - Pattern: regenerative
78
+ # Enables recursive consolidation with controlled expansion
79
+ "stabilization": [
80
+ "emission", # AL: Initiate structure
81
+ "reception", # EN: Gather information
82
+ "coherence", # IL: Stabilize
83
+ "expansion", # VAL: Controlled growth (balance +0.50)
84
+ "resonance", # RA: Amplify through network
85
+ "recursivity", # REMESH: Enable fractal recursion
86
+ ],
87
+ # Creative mutation - already optimal (health: 0.81)
88
+ # Pattern: activation with controlled transformation
89
+ "creative_mutation": [
90
+ "emission", # AL: Initiate exploration
91
+ "dissonance", # OZ: Introduce creative tension
92
+ "reception", # EN: Gather alternatives
93
+ "coherence", # IL: Stabilize insights
94
+ "mutation", # ZHIR: Phase transformation
95
+ "resonance", # RA: Amplify new patterns
96
+ "silence", # SHA: Integration pause
97
+ ],
98
+ # Network synchronization - optimized with transition for regenerative capability
99
+ # Health: 0.77 (good) - Pattern: regenerative
100
+ # Enables phase synchronization across multi-node networks with dissonance for balance
101
+ "network_sync": [
102
+ "emission", # AL: Initiate network activity
103
+ "reception", # EN: Gather network state
104
+ "coherence", # IL: Stabilize local structure
105
+ "coupling", # UM: Establish phase synchronization
106
+ "resonance", # RA: Propagate through network
107
+ "transition", # NAV: Enable regenerative cycles (changed from silence)
108
+ ],
109
+ # Exploration - already excellent (health: 0.87)
110
+ # Pattern: regenerative with transformative potential
111
+ "exploration": [
112
+ "emission", # AL: Begin exploration
113
+ "dissonance", # OZ: Introduce instability
114
+ "reception", # EN: Sense environment
115
+ "coherence", # IL: Find stable attractor
116
+ "resonance", # RA: Reinforce discovery
117
+ "transition", # NAV: Navigate to new state
118
+ ],
119
+ # Consolidation - optimized with expansion for structural balance
120
+ # Health: 0.80 (good) - Pattern: stabilization
121
+ # Recursive consolidation with controlled expansion
122
+ "consolidation": [
123
+ "recursivity", # REMESH: Start from fractal structure
124
+ "reception", # EN: Gather current state
125
+ "coherence", # IL: Consolidate structure
126
+ "expansion", # VAL: Controlled growth (balance +0.25)
127
+ "resonance", # RA: Amplify consolidated state
128
+ "coherence", # IL: Re-stabilize after expansion
129
+ "silence", # SHA: Sustained stable state
130
+ ],
131
+ }
132
+
133
+
134
+ @dataclass
135
+ class NetworkConfig:
136
+ """Configuration for TNFR network creation.
137
+
138
+ Parameters
139
+ ----------
140
+ random_seed : int, optional
141
+ Seed for random number generation to ensure reproducibility.
142
+ validate_invariants : bool, default=True
143
+ Whether to validate TNFR invariants after operator application.
144
+ auto_stabilization : bool, default=True
145
+ Whether to automatically apply stabilization after mutations.
146
+ default_vf_range : tuple[float, float], default=(0.1, 1.0)
147
+ Default range for structural frequency (νf) generation in Hz_str.
148
+ default_epi_range : tuple[float, float], default=(0.1, 0.9)
149
+ Default range for EPI (Primary Information Structure) generation.
150
+ """
151
+
152
+ random_seed: Optional[int] = None
153
+ validate_invariants: bool = True
154
+ auto_stabilization: bool = True
155
+ default_vf_range: tuple[float, float] = (0.1, 1.0)
156
+ default_epi_range: tuple[float, float] = (0.1, 0.9)
157
+
158
+
159
+ @dataclass
160
+ class NetworkResults:
161
+ """Results from TNFR network simulation.
162
+
163
+ Attributes
164
+ ----------
165
+ coherence : float
166
+ Global coherence C(t) of the network.
167
+ sense_indices : Dict[str, float]
168
+ Sense index Si for each node, measuring stable reorganization capacity.
169
+ delta_nfr : Dict[str, float]
170
+ Internal reorganization gradient ΔNFR for each node.
171
+ graph : TNFRGraph
172
+ The underlying NetworkX graph with full TNFR state.
173
+ avg_vf : float, optional
174
+ Average structural frequency across all nodes.
175
+ avg_phase : float, optional
176
+ Average phase angle across all nodes.
177
+ """
178
+
179
+ coherence: float
180
+ sense_indices: Dict[str, float]
181
+ delta_nfr: Dict[str, float]
182
+ graph: Any # TNFRGraph
183
+ avg_vf: Optional[float] = None
184
+ avg_phase: Optional[float] = None
185
+
186
+ def summary(self) -> str:
187
+ """Generate human-readable summary of network results.
188
+
189
+ Returns
190
+ -------
191
+ str
192
+ Formatted summary string with key metrics.
193
+ """
194
+ si_values = list(self.sense_indices.values())
195
+ dnfr_values = list(self.delta_nfr.values())
196
+
197
+ avg_si = sum(si_values) / len(si_values) if si_values else 0.0
198
+ avg_dnfr = sum(dnfr_values) / len(dnfr_values) if dnfr_values else 0.0
199
+
200
+ return f"""
201
+ TNFR Network Results:
202
+ • Coherence C(t): {self.coherence:.3f}
203
+ • Nodes: {len(self.sense_indices)}
204
+ • Avg Sense Index Si: {avg_si:.3f}
205
+ • Avg ΔNFR: {avg_dnfr:.3f}
206
+ • Avg νf: {self.avg_vf:.3f} Hz_str{' (computed)' if self.avg_vf else ''}
207
+ • Avg Phase: {self.avg_phase:.3f} rad{' (computed)' if self.avg_phase else ''}
208
+ """.strip()
209
+
210
+ def to_dict(self) -> Dict[str, Any]:
211
+ """Convert results to serializable dictionary.
212
+
213
+ Returns
214
+ -------
215
+ Dict[str, Any]
216
+ Dictionary containing all metrics and summary statistics.
217
+ """
218
+ si_values = list(self.sense_indices.values())
219
+ dnfr_values = list(self.delta_nfr.values())
220
+
221
+ return {
222
+ "coherence": self.coherence,
223
+ "sense_indices": self.sense_indices,
224
+ "delta_nfr": self.delta_nfr,
225
+ "summary_stats": {
226
+ "node_count": len(self.sense_indices),
227
+ "avg_si": sum(si_values) / len(si_values) if si_values else 0.0,
228
+ "avg_delta_nfr": (
229
+ sum(dnfr_values) / len(dnfr_values) if dnfr_values else 0.0
230
+ ),
231
+ "avg_vf": self.avg_vf,
232
+ "avg_phase": self.avg_phase,
233
+ },
234
+ }
235
+
236
+
237
+ class TNFRNetwork:
238
+ """Fluent API for creating and simulating TNFR networks.
239
+
240
+ This class provides a simplified, chainable interface for working with
241
+ TNFR networks. All methods return ``self`` to enable method chaining.
242
+ The API automatically handles node creation, operator validation, and
243
+ metric computation while preserving TNFR structural invariants.
244
+
245
+ Parameters
246
+ ----------
247
+ name : str, default="tnfr_network"
248
+ Name identifier for the network.
249
+ config : NetworkConfig, optional
250
+ Configuration settings. If None, uses default configuration.
251
+
252
+ Examples
253
+ --------
254
+ Create a network with fluent interface:
255
+
256
+ >>> network = TNFRNetwork("experiment_1")
257
+ >>> network.add_nodes(15).connect_nodes(0.25, "random")
258
+ >>> network.apply_sequence("basic_activation", repeat=3)
259
+ >>> results = network.measure()
260
+
261
+ One-line network creation and simulation:
262
+
263
+ >>> results = (TNFRNetwork("test")
264
+ ... .add_nodes(10)
265
+ ... .connect_nodes(0.3)
266
+ ... .apply_sequence("network_sync", repeat=5)
267
+ ... .measure())
268
+ """
269
+
270
+ def __init__(
271
+ self, name: str = "tnfr_network", config: Optional[NetworkConfig] = None
272
+ ):
273
+ """Initialize TNFR network with given name and configuration."""
274
+ self.name = name
275
+ self._config = config if config is not None else NetworkConfig()
276
+ self._graph: Optional[nx.Graph] = None
277
+ self._results: Optional[NetworkResults] = None
278
+ self._node_counter = 0
279
+
280
+ # Initialize RNG if seed provided
281
+ if _HAS_NUMPY and self._config.random_seed is not None:
282
+ self._rng = np.random.RandomState(self._config.random_seed)
283
+ else:
284
+ import random
285
+
286
+ if self._config.random_seed is not None:
287
+ random.seed(self._config.random_seed)
288
+ self._rng = random # type: ignore[assignment]
289
+
290
+ def add_nodes(
291
+ self,
292
+ count: int,
293
+ vf_range: Optional[tuple[float, float]] = None,
294
+ epi_range: Optional[tuple[float, float]] = None,
295
+ phase_range: tuple[float, float] = (0.0, 6.283185307179586), # (0, 2π)
296
+ random_seed: Optional[int] = None,
297
+ ) -> TNFRNetwork:
298
+ """Add nodes with random TNFR properties within valid ranges.
299
+
300
+ Creates ``count`` new nodes with structural properties (EPI, νf, phase)
301
+ randomly sampled within specified ranges. All values respect TNFR
302
+ invariants and canonical units (νf in Hz_str, phase in radians).
303
+
304
+ Parameters
305
+ ----------
306
+ count : int
307
+ Number of nodes to create.
308
+ vf_range : tuple[float, float], optional
309
+ Range for structural frequency νf in Hz_str. If None, uses
310
+ config default. Values should be positive.
311
+ epi_range : tuple[float, float], optional
312
+ Range for Primary Information Structure (EPI). If None, uses
313
+ config default. Typical range is (0.1, 0.9) to avoid extremes.
314
+ phase_range : tuple[float, float], default=(0, 2π)
315
+ Range for phase in radians. Default covers full circle.
316
+ random_seed : int, optional
317
+ Override seed for this operation only. If None, uses network seed.
318
+
319
+ Returns
320
+ -------
321
+ TNFRNetwork
322
+ Self for method chaining.
323
+
324
+ Examples
325
+ --------
326
+ Add nodes with default properties:
327
+
328
+ >>> network = TNFRNetwork().add_nodes(5)
329
+
330
+ Add nodes with custom frequency range:
331
+
332
+ >>> network = TNFRNetwork().add_nodes(10, vf_range=(0.5, 2.0))
333
+ """
334
+ if self._graph is None:
335
+ self._graph = nx.Graph()
336
+
337
+ if vf_range is None:
338
+ vf_range = self._config.default_vf_range
339
+ if epi_range is None:
340
+ epi_range = self._config.default_epi_range
341
+
342
+ # Setup RNG for this operation
343
+ if _HAS_NUMPY:
344
+ rng = (
345
+ np.random.RandomState(random_seed)
346
+ if random_seed is not None
347
+ else self._rng
348
+ )
349
+
350
+ for _ in range(count):
351
+ node_id = f"node_{self._node_counter}"
352
+ self._node_counter += 1
353
+
354
+ # Generate valid TNFR properties
355
+ vf = rng.uniform(*vf_range)
356
+ phase = rng.uniform(*phase_range)
357
+ epi = rng.uniform(*epi_range)
358
+
359
+ # Create NFR node with structural properties
360
+ self._graph, _ = create_nfr(
361
+ node_id,
362
+ graph=self._graph,
363
+ vf=vf,
364
+ theta=phase,
365
+ epi=epi,
366
+ )
367
+ else:
368
+ # Fallback to standard random
369
+ import random
370
+
371
+ if random_seed is not None:
372
+ random.seed(random_seed)
373
+
374
+ for _ in range(count):
375
+ node_id = f"node_{self._node_counter}"
376
+ self._node_counter += 1
377
+
378
+ vf = random.uniform(*vf_range)
379
+ phase = random.uniform(*phase_range)
380
+ epi = random.uniform(*epi_range)
381
+
382
+ self._graph, _ = create_nfr(
383
+ node_id,
384
+ graph=self._graph,
385
+ vf=vf,
386
+ theta=phase,
387
+ epi=epi,
388
+ )
389
+
390
+ return self
391
+
392
+ def connect_nodes(
393
+ self,
394
+ connection_probability: float = 0.3,
395
+ connection_pattern: str = "random",
396
+ ) -> TNFRNetwork:
397
+ """Connect nodes according to specified topology pattern.
398
+
399
+ Establishes coupling between nodes using common network patterns.
400
+ Connections enable resonance and phase synchronization between nodes.
401
+
402
+ Parameters
403
+ ----------
404
+ connection_probability : float, default=0.3
405
+ For random pattern: probability of edge between any two nodes.
406
+ For small_world: rewiring probability (Watts-Strogatz model).
407
+ connection_pattern : str, default="random"
408
+ Topology pattern to use. Options:
409
+ - "random": Erdős-Rényi random graph
410
+ - "ring": Each node connects to next in circle
411
+ - "small_world": Watts-Strogatz small-world network
412
+
413
+ Returns
414
+ -------
415
+ TNFRNetwork
416
+ Self for method chaining.
417
+
418
+ Raises
419
+ ------
420
+ ValueError
421
+ If graph has no nodes or invalid pattern specified.
422
+
423
+ Examples
424
+ --------
425
+ Create random network:
426
+
427
+ >>> network = TNFRNetwork().add_nodes(10).connect_nodes(0.3, "random")
428
+
429
+ Create ring lattice:
430
+
431
+ >>> network = TNFRNetwork().add_nodes(15).connect_nodes(pattern="ring")
432
+
433
+ Create small-world network:
434
+
435
+ >>> network = TNFRNetwork().add_nodes(20).connect_nodes(0.1, "small_world")
436
+ """
437
+ if self._graph is None or self._graph.number_of_nodes() == 0:
438
+ raise ValueError("No nodes in graph. Call add_nodes() first.")
439
+
440
+ nodes = list(self._graph.nodes())
441
+
442
+ if connection_pattern == "random":
443
+ # Erdős-Rényi random graph
444
+ if _HAS_NUMPY:
445
+ for i, node1 in enumerate(nodes):
446
+ for node2 in nodes[i + 1 :]:
447
+ if self._rng.random() < connection_probability:
448
+ self._graph.add_edge(node1, node2)
449
+ else:
450
+ import random
451
+
452
+ for i, node1 in enumerate(nodes):
453
+ for node2 in nodes[i + 1 :]:
454
+ if random.random() < connection_probability:
455
+ self._graph.add_edge(node1, node2)
456
+
457
+ elif connection_pattern == "ring":
458
+ # Ring lattice
459
+ for i in range(len(nodes)):
460
+ next_node = nodes[(i + 1) % len(nodes)]
461
+ self._graph.add_edge(nodes[i], next_node)
462
+
463
+ elif connection_pattern == "small_world":
464
+ # Watts-Strogatz small-world network
465
+ # Start with ring lattice, then rewire
466
+ k = max(4, int(len(nodes) * 0.1)) # ~10% degree, minimum 4
467
+
468
+ # Create initial ring with k nearest neighbors
469
+ for i in range(len(nodes)):
470
+ for j in range(1, k // 2 + 1):
471
+ target = (i + j) % len(nodes)
472
+ if nodes[i] != nodes[target]: # Avoid self-loops
473
+ self._graph.add_edge(nodes[i], nodes[target])
474
+
475
+ # Rewire edges with given probability
476
+ edges = list(self._graph.edges())
477
+ if _HAS_NUMPY:
478
+ for u, v in edges:
479
+ if self._rng.random() < connection_probability:
480
+ # Remove edge and create new random edge
481
+ self._graph.remove_edge(u, v)
482
+ # Find node not already connected
483
+ candidates = [
484
+ n
485
+ for n in nodes
486
+ if n != u and not self._graph.has_edge(u, n)
487
+ ]
488
+ if candidates:
489
+ idx = int(self._rng.randint(0, len(candidates)))
490
+ if idx >= len(candidates):
491
+ idx = len(candidates) - 1
492
+ w = candidates[idx]
493
+ self._graph.add_edge(u, w)
494
+ else:
495
+ import random
496
+
497
+ for u, v in edges:
498
+ if random.random() < connection_probability:
499
+ self._graph.remove_edge(u, v)
500
+ candidates = [
501
+ n
502
+ for n in nodes
503
+ if n != u and not self._graph.has_edge(u, n)
504
+ ]
505
+ if candidates:
506
+ w = random.choice(candidates)
507
+ self._graph.add_edge(u, w)
508
+
509
+ else:
510
+ available = ", ".join(["random", "ring", "small_world"])
511
+ raise ValueError(
512
+ f"Unknown connection pattern '{connection_pattern}'. "
513
+ f"Available: {available}"
514
+ )
515
+
516
+ return self
517
+
518
+ def apply_sequence(
519
+ self,
520
+ sequence: Union[str, List[str]],
521
+ repeat: int = 1,
522
+ ) -> TNFRNetwork:
523
+ """Apply structural operator sequence to evolve the network.
524
+
525
+ Executes a validated sequence of TNFR operators that reorganize
526
+ network structure according to the nodal equation ∂EPI/∂t = νf·ΔNFR(t).
527
+ Sequences can be predefined names or custom operator lists. The
528
+ sequence is applied to all nodes in the network.
529
+
530
+ Parameters
531
+ ----------
532
+ sequence : str or List[str]
533
+ Either a predefined sequence name or list of operator names.
534
+ Predefined sequences:
535
+ - "basic_activation": [emission, reception, coherence, resonance, silence]
536
+ - "stabilization": [emission, reception, coherence, resonance, recursivity]
537
+ - "creative_mutation": [emission, dissonance, reception, coherence, mutation, resonance, silence]
538
+ - "network_sync": [emission, reception, coherence, coupling, resonance, silence]
539
+ - "exploration": [emission, dissonance, reception, coherence, resonance, transition]
540
+ - "consolidation": [recursivity, reception, coherence, resonance, silence]
541
+ repeat : int, default=1
542
+ Number of times to apply the sequence.
543
+
544
+ Returns
545
+ -------
546
+ TNFRNetwork
547
+ Self for method chaining.
548
+
549
+ Raises
550
+ ------
551
+ ValueError
552
+ If graph has no nodes or sequence name is invalid.
553
+
554
+ Examples
555
+ --------
556
+ Apply predefined sequence:
557
+
558
+ >>> network = (TNFRNetwork()
559
+ ... .add_nodes(10)
560
+ ... .connect_nodes(0.3)
561
+ ... .apply_sequence("basic_activation", repeat=5))
562
+
563
+ Apply custom operator sequence:
564
+
565
+ >>> network.apply_sequence(["emission", "reception", "coherence", "resonance", "silence"])
566
+ """
567
+ if self._graph is None or self._graph.number_of_nodes() == 0:
568
+ raise ValueError("No nodes in graph. Call add_nodes() first.")
569
+
570
+ # Expand named sequences
571
+ if isinstance(sequence, str):
572
+ if sequence not in NAMED_SEQUENCES:
573
+ available = ", ".join(sorted(NAMED_SEQUENCES.keys()))
574
+ raise ValueError(
575
+ f"Unknown sequence '{sequence}'. Available: {available}"
576
+ )
577
+ operator_list = NAMED_SEQUENCES[sequence]
578
+ else:
579
+ operator_list = sequence
580
+
581
+ # Validate sequence if configured
582
+ if self._config.validate_invariants:
583
+ validate_sequence(operator_list)
584
+
585
+ # Convert operator names to operator instances
586
+ from ..operators.registry import get_operator_class
587
+
588
+ operator_instances = [get_operator_class(name)() for name in operator_list]
589
+
590
+ # Apply sequence repeatedly to all nodes
591
+ for _ in range(repeat):
592
+ for node in list(self._graph.nodes()):
593
+ run_sequence(self._graph, node, operator_instances)
594
+
595
+ return self
596
+
597
+ def measure(self) -> NetworkResults:
598
+ """Calculate TNFR metrics and return structured results.
599
+
600
+ Computes coherence C(t), sense indices Si, and ΔNFR values for
601
+ all nodes, plus aggregate statistics. Results are cached internally
602
+ and returned as a :class:`NetworkResults` instance.
603
+
604
+ Returns
605
+ -------
606
+ NetworkResults
607
+ Structured container with all computed metrics.
608
+
609
+ Raises
610
+ ------
611
+ ValueError
612
+ If no network has been created.
613
+
614
+ Examples
615
+ --------
616
+ Measure and display results:
617
+
618
+ >>> results = network.measure()
619
+ >>> print(results.summary())
620
+
621
+ Access specific metrics:
622
+
623
+ >>> coherence = results.coherence
624
+ >>> si_values = results.sense_indices
625
+ """
626
+ if self._graph is None or self._graph.number_of_nodes() == 0:
627
+ raise ValueError("No network created. Use add_nodes() first.")
628
+
629
+ # Compute coherence C(t)
630
+ coherence = compute_coherence(self._graph)
631
+
632
+ # Compute sense indices Si for all nodes
633
+ si_dict = compute_Si(self._graph, inplace=False)
634
+
635
+ # Extract ΔNFR values
636
+ delta_nfr_dict = {}
637
+ for node_id in self._graph.nodes():
638
+ delta_nfr_dict[node_id] = self._graph.nodes[node_id].get(ALIAS_DNFR, 0.0)
639
+
640
+ # Compute aggregate statistics
641
+ vf_sum = 0.0
642
+ phase_sum = 0.0
643
+ node_count = self._graph.number_of_nodes()
644
+
645
+ for node_id in self._graph.nodes():
646
+ node_data = self._graph.nodes[node_id]
647
+ vf_sum += node_data.get("nu_f", 0.0)
648
+ phase_sum += node_data.get("phase", 0.0)
649
+
650
+ avg_vf = vf_sum / node_count if node_count > 0 else 0.0
651
+ avg_phase = phase_sum / node_count if node_count > 0 else 0.0
652
+
653
+ # Create and cache results
654
+ self._results = NetworkResults(
655
+ coherence=coherence,
656
+ sense_indices=si_dict,
657
+ delta_nfr=delta_nfr_dict,
658
+ graph=self._graph,
659
+ avg_vf=avg_vf,
660
+ avg_phase=avg_phase,
661
+ )
662
+
663
+ return self._results
664
+
665
+ def apply_canonical_sequence(
666
+ self,
667
+ sequence_name: str,
668
+ node: Optional[int] = None,
669
+ collect_metrics: bool = True,
670
+ ) -> TNFRNetwork:
671
+ """Apply a canonical predefined operator sequence from TNFR theory.
672
+
673
+ Executes one of the 6 archetypal sequences involving OZ (Dissonance)
674
+ from "El pulso que nos atraviesa" (Table 2.5). These sequences represent
675
+ validated structural patterns with documented use cases and domain contexts.
676
+
677
+ Parameters
678
+ ----------
679
+ sequence_name : str
680
+ Name of canonical sequence. Available sequences:
681
+ - 'bifurcated_base': OZ → ZHIR (mutation path)
682
+ - 'bifurcated_collapse': OZ → NUL (collapse path)
683
+ - 'therapeutic_protocol': Complete healing cycle
684
+ - 'theory_system': Epistemological construction
685
+ - 'full_deployment': Complete reorganization trajectory
686
+ - 'mod_stabilizer': OZ → ZHIR → IL (reusable macro)
687
+ node : int, optional
688
+ Target node ID. If None, applies to the most recently added node.
689
+ collect_metrics : bool, default=True
690
+ Whether to collect detailed operator metrics during execution.
691
+
692
+ Returns
693
+ -------
694
+ TNFRNetwork
695
+ Self for method chaining.
696
+
697
+ Raises
698
+ ------
699
+ ValueError
700
+ If sequence_name is not recognized or network has no nodes.
701
+
702
+ Examples
703
+ --------
704
+ Apply therapeutic protocol:
705
+
706
+ >>> net = TNFRNetwork("therapy_session")
707
+ >>> net.add_nodes(1).apply_canonical_sequence("therapeutic_protocol")
708
+ >>> results = net.measure()
709
+ >>> print(f"Coherence: {results.coherence:.3f}")
710
+
711
+ Apply MOD_STABILIZER as reusable transformation module:
712
+
713
+ >>> net = TNFRNetwork("modular")
714
+ >>> net.add_nodes(1)
715
+ >>> net.apply_canonical_sequence("mod_stabilizer").measure()
716
+
717
+ See Also
718
+ --------
719
+ list_canonical_sequences : List available sequences with filters
720
+ apply_sequence : Apply predefined or custom operator sequences
721
+
722
+ Notes
723
+ -----
724
+ Canonical sequences are archetypal patterns from TNFR theory documented
725
+ in "El pulso que nos atraviesa", Tabla 2.5. Each sequence has been
726
+ validated for structural coherence and grammar compliance.
727
+ """
728
+ if self._graph is None or self._graph.number_of_nodes() == 0:
729
+ raise ValueError("No nodes in graph. Call add_nodes() first.")
730
+
731
+ # Import canonical sequences registry
732
+ from ..operators.canonical_patterns import CANONICAL_SEQUENCES
733
+
734
+ if sequence_name not in CANONICAL_SEQUENCES:
735
+ available = ", ".join(sorted(CANONICAL_SEQUENCES.keys()))
736
+ raise ValueError(
737
+ f"Unknown canonical sequence '{sequence_name}'. "
738
+ f"Available: {available}"
739
+ )
740
+
741
+ sequence = CANONICAL_SEQUENCES[sequence_name]
742
+
743
+ # Determine target node
744
+ if node is None:
745
+ # Use last added node
746
+ nodes_list = list(self._graph.nodes())
747
+ target_node = nodes_list[-1] if nodes_list else 0
748
+ else:
749
+ target_node = node
750
+ if target_node not in self._graph.nodes():
751
+ raise ValueError(f"Node {target_node} not found in network")
752
+
753
+ # Configure metrics collection
754
+ self._graph.graph["COLLECT_OPERATOR_METRICS"] = collect_metrics
755
+
756
+ # Map glyphs to operator instances
757
+ from ..operators.definitions import (
758
+ Emission,
759
+ Reception,
760
+ Coherence,
761
+ Dissonance,
762
+ Coupling,
763
+ Resonance,
764
+ Silence,
765
+ Expansion,
766
+ Contraction,
767
+ SelfOrganization,
768
+ Mutation,
769
+ Transition,
770
+ Recursivity,
771
+ )
772
+ from ..types import Glyph
773
+
774
+ glyph_to_operator = {
775
+ Glyph.AL: Emission(),
776
+ Glyph.EN: Reception(),
777
+ Glyph.IL: Coherence(),
778
+ Glyph.OZ: Dissonance(),
779
+ Glyph.UM: Coupling(),
780
+ Glyph.RA: Resonance(),
781
+ Glyph.SHA: Silence(),
782
+ Glyph.VAL: Expansion(),
783
+ Glyph.NUL: Contraction(),
784
+ Glyph.THOL: SelfOrganization(),
785
+ Glyph.ZHIR: Mutation(),
786
+ Glyph.NAV: Transition(),
787
+ Glyph.REMESH: Recursivity(),
788
+ }
789
+
790
+ operators = [glyph_to_operator[g] for g in sequence.glyphs]
791
+ run_sequence(self._graph, target_node, operators)
792
+
793
+ return self
794
+
795
+ def list_canonical_sequences(
796
+ self,
797
+ domain: Optional[str] = None,
798
+ with_oz: bool = False,
799
+ ) -> Dict[str, Any]:
800
+ """List available canonical sequences with optional filters.
801
+
802
+ Returns a dictionary of canonical operator sequences from TNFR theory.
803
+ Sequences can be filtered by domain or by presence of OZ (Dissonance).
804
+
805
+ Parameters
806
+ ----------
807
+ domain : str, optional
808
+ Filter by domain. Options:
809
+ - 'general': Cross-domain patterns
810
+ - 'biomedical': Therapeutic and healing sequences
811
+ - 'cognitive': Epistemological and learning patterns
812
+ - 'social': Organizational and collective sequences
813
+ with_oz : bool, default=False
814
+ If True, only return sequences containing OZ (Dissonance) operator.
815
+
816
+ Returns
817
+ -------
818
+ dict
819
+ Dictionary mapping sequence names to CanonicalSequence objects.
820
+ Each entry contains: name, glyphs, pattern_type, description,
821
+ use_cases, domain, and references.
822
+
823
+ Examples
824
+ --------
825
+ List all canonical sequences:
826
+
827
+ >>> net = TNFRNetwork("explorer")
828
+ >>> sequences = net.list_canonical_sequences()
829
+ >>> for name in sequences:
830
+ ... print(name)
831
+ bifurcated_base
832
+ bifurcated_collapse
833
+ therapeutic_protocol
834
+ theory_system
835
+ full_deployment
836
+ mod_stabilizer
837
+
838
+ List only sequences with OZ:
839
+
840
+ >>> oz_sequences = net.list_canonical_sequences(with_oz=True)
841
+ >>> print(f"Found {len(oz_sequences)} sequences with OZ")
842
+ Found 6 sequences with OZ
843
+
844
+ List biomedical domain sequences:
845
+
846
+ >>> bio_sequences = net.list_canonical_sequences(domain="biomedical")
847
+ >>> for name, seq in bio_sequences.items():
848
+ ... print(f"{name}: {seq.description[:50]}...")
849
+
850
+ See Also
851
+ --------
852
+ apply_canonical_sequence : Apply a canonical sequence to the network
853
+ """
854
+ from ..operators.canonical_patterns import CANONICAL_SEQUENCES
855
+ from ..types import Glyph
856
+
857
+ sequences = CANONICAL_SEQUENCES.copy()
858
+
859
+ # Filter by domain if specified
860
+ if domain is not None:
861
+ sequences = {
862
+ name: seq for name, seq in sequences.items() if seq.domain == domain
863
+ }
864
+
865
+ # Filter by OZ presence if requested
866
+ if with_oz:
867
+ sequences = {
868
+ name: seq for name, seq in sequences.items() if Glyph.OZ in seq.glyphs
869
+ }
870
+
871
+ return sequences
872
+
873
+ def visualize(self, **kwargs: Any) -> TNFRNetwork:
874
+ """Visualize the network with TNFR metrics.
875
+
876
+ Creates a visual representation of the network showing node states
877
+ and connections. Requires matplotlib to be installed.
878
+
879
+ Parameters
880
+ ----------
881
+ **kwargs
882
+ Additional arguments passed to visualization function.
883
+
884
+ Returns
885
+ -------
886
+ TNFRNetwork
887
+ Self for method chaining.
888
+
889
+ Raises
890
+ ------
891
+ ImportError
892
+ If matplotlib is not installed.
893
+ ValueError
894
+ If no network has been created.
895
+
896
+ Notes
897
+ -----
898
+ This is a placeholder for future visualization functionality.
899
+ Current implementation will raise NotImplementedError.
900
+ """
901
+ if self._graph is None or self._graph.number_of_nodes() == 0:
902
+ raise ValueError("No network created. Use add_nodes() first.")
903
+
904
+ # Compute metrics if not done yet
905
+ if self._results is None:
906
+ self.measure()
907
+
908
+ # Visualization will be implemented in future PR
909
+ raise NotImplementedError(
910
+ "Visualization functionality will be added in a future update. "
911
+ "Use NetworkX's drawing functions directly on network._graph for now."
912
+ )
913
+
914
+ def save(self, filepath: Union[str, Path]) -> TNFRNetwork:
915
+ """Save network state and results to file.
916
+
917
+ Serializes the network graph and computed metrics to a file for
918
+ later analysis or reproduction.
919
+
920
+ Parameters
921
+ ----------
922
+ filepath : str or Path
923
+ Path where network data should be saved.
924
+
925
+ Returns
926
+ -------
927
+ TNFRNetwork
928
+ Self for method chaining.
929
+
930
+ Raises
931
+ ------
932
+ ValueError
933
+ If no network has been created.
934
+
935
+ Notes
936
+ -----
937
+ This is a placeholder for future I/O functionality.
938
+ Current implementation will raise NotImplementedError.
939
+ """
940
+ if self._graph is None or self._graph.number_of_nodes() == 0:
941
+ raise ValueError("No network created. Use add_nodes() first.")
942
+
943
+ # Compute metrics if not done yet
944
+ if self._results is None:
945
+ self.measure()
946
+
947
+ # I/O functionality will be implemented in future PR
948
+ raise NotImplementedError(
949
+ "Save functionality will be added in a future update. "
950
+ "Use networkx.write_gpickle or similar for now."
951
+ )
952
+
953
+ @property
954
+ def graph(self) -> nx.Graph:
955
+ """Access the underlying NetworkX graph.
956
+
957
+ Returns
958
+ -------
959
+ nx.Graph
960
+ The NetworkX graph with TNFR node attributes.
961
+
962
+ Raises
963
+ ------
964
+ ValueError
965
+ If no network has been created yet.
966
+ """
967
+ if self._graph is None:
968
+ raise ValueError("No network created. Use add_nodes() first.")
969
+ return self._graph
970
+
971
+ def get_node_count(self) -> int:
972
+ """Get the number of nodes in the network.
973
+
974
+ Returns
975
+ -------
976
+ int
977
+ Number of nodes.
978
+
979
+ Raises
980
+ ------
981
+ ValueError
982
+ If no network has been created.
983
+ """
984
+ if self._graph is None:
985
+ raise ValueError("No network created. Use add_nodes() first.")
986
+ return self._graph.number_of_nodes()
987
+
988
+ def get_edge_count(self) -> int:
989
+ """Get the number of edges in the network.
990
+
991
+ Returns
992
+ -------
993
+ int
994
+ Number of edges.
995
+
996
+ Raises
997
+ ------
998
+ ValueError
999
+ If no network has been created.
1000
+ """
1001
+ if self._graph is None:
1002
+ raise ValueError("No network created. Use add_nodes() first.")
1003
+ return self._graph.number_of_edges()
1004
+
1005
+ def get_average_degree(self) -> float:
1006
+ """Get the average degree of nodes in the network.
1007
+
1008
+ Returns
1009
+ -------
1010
+ float
1011
+ Average node degree.
1012
+
1013
+ Raises
1014
+ ------
1015
+ ValueError
1016
+ If no network has been created.
1017
+ """
1018
+ if self._graph is None:
1019
+ raise ValueError("No network created. Use add_nodes() first.")
1020
+ if self._graph.number_of_nodes() == 0:
1021
+ return 0.0
1022
+ return 2.0 * self._graph.number_of_edges() / self._graph.number_of_nodes()
1023
+
1024
+ def get_density(self) -> float:
1025
+ """Get the density of the network.
1026
+
1027
+ Network density is the ratio of actual edges to possible edges.
1028
+
1029
+ Returns
1030
+ -------
1031
+ float
1032
+ Network density between 0 and 1.
1033
+
1034
+ Raises
1035
+ ------
1036
+ ValueError
1037
+ If no network has been created.
1038
+ """
1039
+ if self._graph is None:
1040
+ raise ValueError("No network created. Use add_nodes() first.")
1041
+ n = self._graph.number_of_nodes()
1042
+ if n <= 1:
1043
+ return 0.0
1044
+ m = self._graph.number_of_edges()
1045
+ max_edges = n * (n - 1) / 2
1046
+ return m / max_edges if max_edges > 0 else 0.0
1047
+
1048
+ def clone(self) -> TNFRNetwork:
1049
+ """Create a copy of the network structure.
1050
+
1051
+ Returns
1052
+ -------
1053
+ TNFRNetwork
1054
+ A new network with copied structure. Note that this copies
1055
+ the graph structure but not all internal state (like locks).
1056
+
1057
+ Raises
1058
+ ------
1059
+ ValueError
1060
+ If no network has been created.
1061
+ """
1062
+ if self._graph is None:
1063
+ raise ValueError("No network created. Use add_nodes() first.")
1064
+
1065
+ import networkx as nx
1066
+
1067
+ new_network = TNFRNetwork(f"{self.name}_copy", config=self._config)
1068
+ # Use NetworkX's copy method which handles TNFR graphs properly
1069
+ new_network._graph = nx.Graph(self._graph)
1070
+ new_network._node_counter = self._node_counter
1071
+ return new_network
1072
+
1073
+ def reset(self) -> TNFRNetwork:
1074
+ """Reset the network to empty state.
1075
+
1076
+ Returns
1077
+ -------
1078
+ TNFRNetwork
1079
+ Self for method chaining.
1080
+ """
1081
+ self._graph = None
1082
+ self._results = None
1083
+ self._node_counter = 0
1084
+ return self
1085
+
1086
+ def export_to_dict(self) -> dict:
1087
+ """Export network structure to dictionary format.
1088
+
1089
+ Returns
1090
+ -------
1091
+ dict
1092
+ Dictionary with network metadata and structure.
1093
+
1094
+ Raises
1095
+ ------
1096
+ ValueError
1097
+ If no network has been created.
1098
+ """
1099
+ if self._graph is None:
1100
+ raise ValueError("No network created. Use add_nodes() first.")
1101
+
1102
+ # Measure if not done yet
1103
+ if self._results is None:
1104
+ self.measure()
1105
+
1106
+ return {
1107
+ "name": self.name,
1108
+ "metadata": {
1109
+ "nodes": self.get_node_count(),
1110
+ "edges": self.get_edge_count(),
1111
+ "density": self.get_density(),
1112
+ "average_degree": self.get_average_degree(),
1113
+ },
1114
+ "metrics": self._results.to_dict() if self._results else None,
1115
+ "config": {
1116
+ "random_seed": self._config.random_seed,
1117
+ "validate_invariants": self._config.validate_invariants,
1118
+ "vf_range": self._config.default_vf_range,
1119
+ "epi_range": self._config.default_epi_range,
1120
+ },
1121
+ }