sofic 0.1.0__tar.gz

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.
Files changed (232) hide show
  1. sofic-0.1.0/.gitignore +42 -0
  2. sofic-0.1.0/LICENSE.txt +29 -0
  3. sofic-0.1.0/PKG-INFO +444 -0
  4. sofic-0.1.0/README.rst +370 -0
  5. sofic-0.1.0/pyproject.toml +236 -0
  6. sofic-0.1.0/sofic/__init__.py +185 -0
  7. sofic-0.1.0/sofic/automata/__init__.py +207 -0
  8. sofic-0.1.0/sofic/automata/_config_simulation.py +40 -0
  9. sofic-0.1.0/sofic/automata/active.py +611 -0
  10. sofic-0.1.0/sofic/automata/alergia.py +222 -0
  11. sofic-0.1.0/sofic/automata/algorithms.py +376 -0
  12. sofic-0.1.0/sofic/automata/atomaton.py +58 -0
  13. sofic-0.1.0/sofic/automata/base.py +161 -0
  14. sofic-0.1.0/sofic/automata/buchi.py +23 -0
  15. sofic-0.1.0/sofic/automata/buchi_simulation.py +67 -0
  16. sofic-0.1.0/sofic/automata/canonical_dual.py +18 -0
  17. sofic-0.1.0/sofic/automata/canonical_extraction.py +122 -0
  18. sofic-0.1.0/sofic/automata/dfa.py +85 -0
  19. sofic-0.1.0/sofic/automata/dfasat.py +195 -0
  20. sofic-0.1.0/sofic/automata/edsm.py +219 -0
  21. sofic-0.1.0/sofic/automata/enumeration.py +44 -0
  22. sofic-0.1.0/sofic/automata/icdfa.py +421 -0
  23. sofic-0.1.0/sofic/automata/idfa.py +363 -0
  24. sofic-0.1.0/sofic/automata/languages/__init__.py +39 -0
  25. sofic-0.1.0/sofic/automata/languages/_quotient_utils.py +64 -0
  26. sofic-0.1.0/sofic/automata/languages/atoms.py +31 -0
  27. sofic-0.1.0/sofic/automata/languages/automaton_ops.py +243 -0
  28. sofic-0.1.0/sofic/automata/languages/base.py +67 -0
  29. sofic-0.1.0/sofic/automata/languages/operations.py +78 -0
  30. sofic-0.1.0/sofic/automata/languages/quotients.py +66 -0
  31. sofic-0.1.0/sofic/automata/languages/residuals.py +25 -0
  32. sofic-0.1.0/sofic/automata/learning.py +79 -0
  33. sofic-0.1.0/sofic/automata/nfa.py +39 -0
  34. sofic-0.1.0/sofic/automata/nwa.py +343 -0
  35. sofic-0.1.0/sofic/automata/nwa_simulation.py +56 -0
  36. sofic-0.1.0/sofic/automata/observation.py +40 -0
  37. sofic-0.1.0/sofic/automata/papni.py +301 -0
  38. sofic-0.1.0/sofic/automata/regex.py +128 -0
  39. sofic-0.1.0/sofic/automata/rfsa.py +35 -0
  40. sofic-0.1.0/sofic/automata/rpni.py +193 -0
  41. sofic-0.1.0/sofic/automata/subsequential.py +201 -0
  42. sofic-0.1.0/sofic/automata/transducer_operations.py +350 -0
  43. sofic-0.1.0/sofic/automata/transducer_simulation.py +150 -0
  44. sofic-0.1.0/sofic/automata/transducers.py +365 -0
  45. sofic-0.1.0/sofic/automata/unifilar.py +107 -0
  46. sofic-0.1.0/sofic/automata/vpa.py +1373 -0
  47. sofic-0.1.0/sofic/automata/vpa_simulation.py +53 -0
  48. sofic-0.1.0/sofic/base.py +153 -0
  49. sofic-0.1.0/sofic/core.py +47 -0
  50. sofic-0.1.0/sofic/examples/__init__.py +86 -0
  51. sofic-0.1.0/sofic/examples/epsilon_machines.py +1089 -0
  52. sofic-0.1.0/sofic/examples/processes.py +1491 -0
  53. sofic-0.1.0/sofic/examples/shifts.py +144 -0
  54. sofic-0.1.0/sofic/exceptions.py +33 -0
  55. sofic-0.1.0/sofic/generators/__init__.py +115 -0
  56. sofic-0.1.0/sofic/generators/_word_measures.py +94 -0
  57. sofic-0.1.0/sofic/generators/alternative_complexity.py +104 -0
  58. sofic-0.1.0/sofic/generators/base.py +327 -0
  59. sofic-0.1.0/sofic/generators/bidirectional_construction.py +717 -0
  60. sofic-0.1.0/sofic/generators/bidirectional_epsilon_machine.py +689 -0
  61. sofic-0.1.0/sofic/generators/block_convergence.py +668 -0
  62. sofic-0.1.0/sofic/generators/block_entropy.py +578 -0
  63. sofic-0.1.0/sofic/generators/channel_measures.py +75 -0
  64. sofic-0.1.0/sofic/generators/conversions.py +182 -0
  65. sofic-0.1.0/sofic/generators/directional_flow.py +245 -0
  66. sofic-0.1.0/sofic/generators/edge_emissions.py +36 -0
  67. sofic-0.1.0/sofic/generators/edge_machine.py +178 -0
  68. sofic-0.1.0/sofic/generators/epsilon_construction.py +193 -0
  69. sofic-0.1.0/sofic/generators/epsilon_inference.py +703 -0
  70. sofic-0.1.0/sofic/generators/epsilon_machine.py +557 -0
  71. sofic-0.1.0/sofic/generators/epsilon_transducer.py +168 -0
  72. sofic-0.1.0/sofic/generators/epsilon_transducer_construction.py +185 -0
  73. sofic-0.1.0/sofic/generators/epsilon_transducer_inference.py +499 -0
  74. sofic-0.1.0/sofic/generators/hmm_inference.py +719 -0
  75. sofic-0.1.0/sofic/generators/information_diagram.py +428 -0
  76. sofic-0.1.0/sofic/generators/lumping.py +447 -0
  77. sofic-0.1.0/sofic/generators/markov.py +100 -0
  78. sofic-0.1.0/sofic/generators/mealy.py +156 -0
  79. sofic-0.1.0/sofic/generators/measures.py +257 -0
  80. sofic-0.1.0/sofic/generators/minimal_generative_model.py +821 -0
  81. sofic-0.1.0/sofic/generators/mixed_state.py +250 -0
  82. sofic-0.1.0/sofic/generators/mixed_state_construction.py +163 -0
  83. sofic-0.1.0/sofic/generators/moore.py +75 -0
  84. sofic-0.1.0/sofic/generators/nmachine.py +78 -0
  85. sofic-0.1.0/sofic/generators/nmachine_construction.py +70 -0
  86. sofic-0.1.0/sofic/generators/pfa.py +100 -0
  87. sofic-0.1.0/sofic/generators/prob.py +291 -0
  88. sofic-0.1.0/sofic/generators/process_equivalence.py +207 -0
  89. sofic-0.1.0/sofic/generators/quasi_inference.py +74 -0
  90. sofic-0.1.0/sofic/generators/quasi_realization.py +97 -0
  91. sofic-0.1.0/sofic/generators/reversal.py +66 -0
  92. sofic-0.1.0/sofic/generators/stack_hmm.py +426 -0
  93. sofic-0.1.0/sofic/generators/stack_inference.py +509 -0
  94. sofic-0.1.0/sofic/generators/stationary.py +134 -0
  95. sofic-0.1.0/sofic/generators/stochastic.py +65 -0
  96. sofic-0.1.0/sofic/generators/synchronization.py +407 -0
  97. sofic-0.1.0/sofic/generators/topological_epsilon_enumeration.py +349 -0
  98. sofic-0.1.0/sofic/generators/words.py +226 -0
  99. sofic-0.1.0/sofic/graph.py +135 -0
  100. sofic-0.1.0/sofic/indexing.py +31 -0
  101. sofic-0.1.0/sofic/inference/__init__.py +45 -0
  102. sofic-0.1.0/sofic/inference/bayesian/__init__.py +68 -0
  103. sofic-0.1.0/sofic/inference/bayesian/comparison.py +199 -0
  104. sofic-0.1.0/sofic/inference/bayesian/counts.py +219 -0
  105. sofic-0.1.0/sofic/inference/bayesian/diversity.py +254 -0
  106. sofic-0.1.0/sofic/inference/bayesian/epsilon.py +270 -0
  107. sofic-0.1.0/sofic/inference/bayesian/hdp_hmm.py +340 -0
  108. sofic-0.1.0/sofic/inference/bayesian/markov.py +294 -0
  109. sofic-0.1.0/sofic/inference/bayesian/pymc_backend.py +71 -0
  110. sofic-0.1.0/sofic/inference/bayesian/stack_hmm.py +215 -0
  111. sofic-0.1.0/sofic/inference/model_selection.py +365 -0
  112. sofic-0.1.0/sofic/inference/spectral.py +564 -0
  113. sofic-0.1.0/sofic/operations.py +16 -0
  114. sofic-0.1.0/sofic/properties.py +339 -0
  115. sofic-0.1.0/sofic/serialization.py +450 -0
  116. sofic-0.1.0/sofic/shifts/__init__.py +48 -0
  117. sofic-0.1.0/sofic/shifts/algorithms.py +84 -0
  118. sofic-0.1.0/sofic/shifts/base.py +49 -0
  119. sofic-0.1.0/sofic/shifts/cover_construction.py +76 -0
  120. sofic-0.1.0/sofic/shifts/covers.py +47 -0
  121. sofic-0.1.0/sofic/shifts/dyck_algorithms.py +100 -0
  122. sofic-0.1.0/sofic/shifts/dyck_enumeration.py +275 -0
  123. sofic-0.1.0/sofic/shifts/markov_dyck.py +172 -0
  124. sofic-0.1.0/sofic/shifts/parry_construction.py +82 -0
  125. sofic-0.1.0/sofic/shifts/sft.py +104 -0
  126. sofic-0.1.0/sofic/shifts/sft_construction.py +52 -0
  127. sofic-0.1.0/sofic/shifts/sliding_block_code.py +156 -0
  128. sofic-0.1.0/sofic/shifts/sofic.py +111 -0
  129. sofic-0.1.0/sofic/shifts/sofic_dyck.py +110 -0
  130. sofic-0.1.0/sofic/shifts/sofic_relation.py +64 -0
  131. sofic-0.1.0/sofic/shifts/textile.py +104 -0
  132. sofic-0.1.0/sofic/shifts/tmc.py +46 -0
  133. sofic-0.1.0/sofic/shifts/tmc_construction.py +58 -0
  134. sofic-0.1.0/sofic/shifts/topological_anatomy.py +150 -0
  135. sofic-0.1.0/sofic/states.py +27 -0
  136. sofic-0.1.0/sofic/testing/__init__.py +8 -0
  137. sofic-0.1.0/sofic/testing/strategies.py +154 -0
  138. sofic-0.1.0/sofic/viz/__init__.py +16 -0
  139. sofic-0.1.0/sofic/viz/_context.py +345 -0
  140. sofic-0.1.0/sofic/viz/_edge.py +216 -0
  141. sofic-0.1.0/sofic/viz/_format.py +89 -0
  142. sofic-0.1.0/sofic/viz/_labels.py +34 -0
  143. sofic-0.1.0/sofic/viz/_names.py +17 -0
  144. sofic-0.1.0/sofic/viz/_rational.py +20 -0
  145. sofic-0.1.0/sofic/viz/_tikz_compile.py +177 -0
  146. sofic-0.1.0/sofic/viz/_tikz_format.py +122 -0
  147. sofic-0.1.0/sofic/viz/_tikz_layout.py +218 -0
  148. sofic-0.1.0/sofic/viz/assets/vaucanson.tikz +71 -0
  149. sofic-0.1.0/sofic/viz/graphviz.py +158 -0
  150. sofic-0.1.0/sofic/viz/idiagram.py +350 -0
  151. sofic-0.1.0/sofic/viz/tikz.py +381 -0
  152. sofic-0.1.0/tests/__init__.py +0 -0
  153. sofic-0.1.0/tests/conftest.py +8 -0
  154. sofic-0.1.0/tests/test_active_learning.py +170 -0
  155. sofic-0.1.0/tests/test_algorithms.py +232 -0
  156. sofic-0.1.0/tests/test_atomaton.py +33 -0
  157. sofic-0.1.0/tests/test_bayesian_inference.py +88 -0
  158. sofic-0.1.0/tests/test_bidirectional_epsilon.py +507 -0
  159. sofic-0.1.0/tests/test_bidirectional_hypothesis.py +24 -0
  160. sofic-0.1.0/tests/test_block_convergence.py +201 -0
  161. sofic-0.1.0/tests/test_block_entropy.py +185 -0
  162. sofic-0.1.0/tests/test_buchi.py +55 -0
  163. sofic-0.1.0/tests/test_channel_measures.py +89 -0
  164. sofic-0.1.0/tests/test_cm_gap_measures.py +109 -0
  165. sofic-0.1.0/tests/test_conversions.py +137 -0
  166. sofic-0.1.0/tests/test_covers.py +30 -0
  167. sofic-0.1.0/tests/test_dfa.py +122 -0
  168. sofic-0.1.0/tests/test_dfasat.py +85 -0
  169. sofic-0.1.0/tests/test_edge_machine.py +163 -0
  170. sofic-0.1.0/tests/test_epsilon_inference.py +206 -0
  171. sofic-0.1.0/tests/test_epsilon_machine.py +75 -0
  172. sofic-0.1.0/tests/test_epsilon_transducer.py +131 -0
  173. sofic-0.1.0/tests/test_epsilon_transducer_inference.py +86 -0
  174. sofic-0.1.0/tests/test_examples.py +118 -0
  175. sofic-0.1.0/tests/test_examples_nrps.py +34 -0
  176. sofic-0.1.0/tests/test_graph.py +42 -0
  177. sofic-0.1.0/tests/test_hdp_hmm.py +121 -0
  178. sofic-0.1.0/tests/test_hmm_inference.py +324 -0
  179. sofic-0.1.0/tests/test_icdfa.py +148 -0
  180. sofic-0.1.0/tests/test_information_anatomy.py +858 -0
  181. sofic-0.1.0/tests/test_information_anatomy_hypothesis.py +67 -0
  182. sofic-0.1.0/tests/test_information_diagram.py +314 -0
  183. sofic-0.1.0/tests/test_languages.py +73 -0
  184. sofic-0.1.0/tests/test_learning.py +27 -0
  185. sofic-0.1.0/tests/test_lumping.py +231 -0
  186. sofic-0.1.0/tests/test_markov.py +44 -0
  187. sofic-0.1.0/tests/test_markov_dyck.py +93 -0
  188. sofic-0.1.0/tests/test_mealy_hmm.py +85 -0
  189. sofic-0.1.0/tests/test_measures.py +156 -0
  190. sofic-0.1.0/tests/test_minimal_generative_model.py +551 -0
  191. sofic-0.1.0/tests/test_mixed_state.py +96 -0
  192. sofic-0.1.0/tests/test_mixed_state_recurrent.py +66 -0
  193. sofic-0.1.0/tests/test_model_selection.py +189 -0
  194. sofic-0.1.0/tests/test_moore_hmm.py +40 -0
  195. sofic-0.1.0/tests/test_nfa.py +68 -0
  196. sofic-0.1.0/tests/test_nmachine.py +64 -0
  197. sofic-0.1.0/tests/test_nwa.py +186 -0
  198. sofic-0.1.0/tests/test_observation.py +25 -0
  199. sofic-0.1.0/tests/test_pfa.py +34 -0
  200. sofic-0.1.0/tests/test_posterior_diversity.py +138 -0
  201. sofic-0.1.0/tests/test_processes_port.py +122 -0
  202. sofic-0.1.0/tests/test_properties.py +236 -0
  203. sofic-0.1.0/tests/test_quasi_realization.py +53 -0
  204. sofic-0.1.0/tests/test_reverse.py +108 -0
  205. sofic-0.1.0/tests/test_rfsa.py +31 -0
  206. sofic-0.1.0/tests/test_sft.py +39 -0
  207. sofic-0.1.0/tests/test_shift_examples.py +110 -0
  208. sofic-0.1.0/tests/test_sliding_block_code.py +68 -0
  209. sofic-0.1.0/tests/test_smoke.py +7 -0
  210. sofic-0.1.0/tests/test_sofic.py +27 -0
  211. sofic-0.1.0/tests/test_sofic_dyck.py +108 -0
  212. sofic-0.1.0/tests/test_sofic_relation.py +41 -0
  213. sofic-0.1.0/tests/test_spectral_inference.py +170 -0
  214. sofic-0.1.0/tests/test_stack_hmm.py +146 -0
  215. sofic-0.1.0/tests/test_stack_inference.py +221 -0
  216. sofic-0.1.0/tests/test_state_merging.py +139 -0
  217. sofic-0.1.0/tests/test_stochastic.py +26 -0
  218. sofic-0.1.0/tests/test_subsequential.py +77 -0
  219. sofic-0.1.0/tests/test_symbolic_hmm.py +213 -0
  220. sofic-0.1.0/tests/test_synchronization.py +275 -0
  221. sofic-0.1.0/tests/test_testing_strategies.py +56 -0
  222. sofic-0.1.0/tests/test_textile.py +39 -0
  223. sofic-0.1.0/tests/test_tikz.py +222 -0
  224. sofic-0.1.0/tests/test_tmc.py +34 -0
  225. sofic-0.1.0/tests/test_topological_anatomy.py +156 -0
  226. sofic-0.1.0/tests/test_topological_epsilon.py +130 -0
  227. sofic-0.1.0/tests/test_transducer_composition.py +52 -0
  228. sofic-0.1.0/tests/test_transducers.py +131 -0
  229. sofic-0.1.0/tests/test_viz.py +307 -0
  230. sofic-0.1.0/tests/test_vpa.py +361 -0
  231. sofic-0.1.0/tests/test_words.py +115 -0
  232. sofic-0.1.0/tests/test_yaml.py +344 -0
sofic-0.1.0/.gitignore ADDED
@@ -0,0 +1,42 @@
1
+ # Python
2
+ *.pyc
3
+ *.pyo
4
+ *.so
5
+ __pycache__/
6
+ *.egg-info/
7
+ .eggs/
8
+ build/
9
+ dist/
10
+ MANIFEST
11
+
12
+ # Virtual environments
13
+ .venv/
14
+
15
+ # Packaging
16
+ sofic.egg-info/
17
+
18
+ # Test and coverage
19
+ .coverage*
20
+ coverage.xml
21
+ coverage_html_report/
22
+ .pytest_cache/
23
+ .hypothesis/
24
+
25
+ # Type checkers
26
+ .mypy_cache/
27
+ .ruff_cache/
28
+
29
+ # IDEs and editors
30
+ .idea/
31
+ .vscode/
32
+ .cursor/
33
+ *.swp
34
+ *.swo
35
+ *.*~
36
+
37
+ # Documentation
38
+ docs/_build/
39
+
40
+ # Misc
41
+ .cache/
42
+ .pypirc
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, sofic contributors.
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ * Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
sofic-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,444 @@
1
+ Metadata-Version: 2.5
2
+ Name: sofic
3
+ Version: 0.1.0
4
+ Summary: Python package for hidden Markov models, symbolic dynamics, finite state machines, and stochastic symbol generators.
5
+ Project-URL: Homepage, https://github.com/dit/sofic
6
+ Project-URL: Repository, https://github.com/dit/sofic
7
+ Project-URL: Documentation, https://sofic.readthedocs.io
8
+ Author-email: chebee7i <admin@dit.io>, "Ryan G. James" <dit@autoplectic.com>
9
+ License-Expression: BSD-3-Clause
10
+ License-File: LICENSE.txt
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: License :: OSI Approved :: BSD License
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Topic :: Scientific/Engineering
18
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
19
+ Classifier: Topic :: Scientific/Engineering :: Physics
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: dit>=2.2
22
+ Requires-Dist: networkx>=2.6
23
+ Requires-Dist: numpy>=1.22
24
+ Requires-Dist: pyyaml>=6.0
25
+ Requires-Dist: scipy>=1.7
26
+ Provides-Extra: bayes
27
+ Requires-Dist: arviz; extra == 'bayes'
28
+ Requires-Dist: pymc>=5; extra == 'bayes'
29
+ Provides-Extra: dev
30
+ Requires-Dist: dit[symbolic]>=2.2; extra == 'dev'
31
+ Requires-Dist: graphviz>=0.20; extra == 'dev'
32
+ Requires-Dist: hypothesis>=6.0; extra == 'dev'
33
+ Requires-Dist: hypothesis[numpy]; extra == 'dev'
34
+ Requires-Dist: ipython; extra == 'dev'
35
+ Requires-Dist: matplotlib; extra == 'dev'
36
+ Requires-Dist: matplotlib>=3.5; extra == 'dev'
37
+ Requires-Dist: pytest-cov; extra == 'dev'
38
+ Requires-Dist: pytest-rerunfailures; extra == 'dev'
39
+ Requires-Dist: pytest-sugar; extra == 'dev'
40
+ Requires-Dist: pytest-xdist; extra == 'dev'
41
+ Requires-Dist: pytest>=9.0.3; extra == 'dev'
42
+ Requires-Dist: ruff; extra == 'dev'
43
+ Requires-Dist: sphinx; extra == 'dev'
44
+ Requires-Dist: sphinx-rtd-theme; extra == 'dev'
45
+ Requires-Dist: sphinxcontrib-bibtex; extra == 'dev'
46
+ Requires-Dist: sympy>=1.12; extra == 'dev'
47
+ Requires-Dist: ty; extra == 'dev'
48
+ Provides-Extra: docs
49
+ Requires-Dist: dit[symbolic]>=2.2; extra == 'docs'
50
+ Requires-Dist: ipython; extra == 'docs'
51
+ Requires-Dist: matplotlib; extra == 'docs'
52
+ Requires-Dist: sphinx; extra == 'docs'
53
+ Requires-Dist: sphinx-rtd-theme; extra == 'docs'
54
+ Requires-Dist: sphinxcontrib-bibtex; extra == 'docs'
55
+ Requires-Dist: sympy>=1.12; extra == 'docs'
56
+ Provides-Extra: sat
57
+ Requires-Dist: python-sat>=1.8; extra == 'sat'
58
+ Provides-Extra: symbolic
59
+ Requires-Dist: dit[symbolic]>=2.2; extra == 'symbolic'
60
+ Requires-Dist: sympy>=1.12; extra == 'symbolic'
61
+ Provides-Extra: test
62
+ Requires-Dist: graphviz>=0.20; extra == 'test'
63
+ Requires-Dist: hypothesis>=6.0; extra == 'test'
64
+ Requires-Dist: hypothesis[numpy]; extra == 'test'
65
+ Requires-Dist: pytest-cov; extra == 'test'
66
+ Requires-Dist: pytest-rerunfailures; extra == 'test'
67
+ Requires-Dist: pytest-sugar; extra == 'test'
68
+ Requires-Dist: pytest-xdist; extra == 'test'
69
+ Requires-Dist: pytest>=9.0.3; extra == 'test'
70
+ Provides-Extra: viz
71
+ Requires-Dist: graphviz>=0.20; extra == 'viz'
72
+ Requires-Dist: matplotlib>=3.5; extra == 'viz'
73
+ Description-Content-Type: text/x-rst
74
+
75
+ =======
76
+ sofic
77
+ =======
78
+
79
+ ``sofic`` is a Python package for hidden Markov models, symbolic dynamics,
80
+ finite state machines, and other stochastic symbol generators.
81
+
82
+ Basic Information
83
+ -----------------
84
+
85
+ Documentation
86
+ ~~~~~~~~~~~~~~
87
+
88
+ https://sofic.readthedocs.io
89
+
90
+ Repository
91
+ ~~~~~~~~~~
92
+
93
+ https://github.com/dit/sofic
94
+
95
+ Dependencies
96
+ ~~~~~~~~~~~~
97
+
98
+ * Python 3.11+
99
+ * `networkx <https://networkx.github.io/>`_
100
+ * `numpy <http://www.numpy.org/>`_
101
+ * `scipy <https://www.scipy.org/>`_
102
+ * `pyyaml <https://pyyaml.org/>`_
103
+ * `dit <https://github.com/dit/dit>`_ (information-theoretic measures)
104
+
105
+ Development
106
+ ~~~~~~~~~~~
107
+
108
+ Clone the repository and install development dependencies with ``uv``:
109
+
110
+ .. code-block:: bash
111
+
112
+ git clone https://github.com/dit/sofic.git
113
+ cd sofic
114
+ uv sync --extra dev
115
+
116
+ Run tests with ``uv run pytest``. See the ``generalinfo`` page in the Sphinx
117
+ docs for linting, type checking, and documentation builds.
118
+
119
+ Introduction
120
+ ------------
121
+
122
+ Many natural and engineered processes produce sequences of symbols whose
123
+ statistics are governed by latent structure: hidden states, transition rules,
124
+ or algebraic constraints on allowed paths. ``sofic`` collects algorithms and
125
+ data structures for representing, simulating, and analyzing such generators
126
+ behind a single, composable Python API.
127
+
128
+ Every model is a graph-backed state machine, so the same objects support
129
+ construction, validation, simulation, visualization, (de)serialization, and a
130
+ large library of structural and information-theoretic measures. The three
131
+ main families are:
132
+
133
+ * **Stochastic generators** (``sofic.generators``) — Markov chains, hidden
134
+ Markov models (Moore and Mealy presentations), ε-machines, probabilistic
135
+ finite automata, mixed-state presentations, and quasiprobabilistic
136
+ generators. These assign probabilities to sequences.
137
+ * **Finite automata** (``sofic.automata``) — DFAs, NFAs, transducers
138
+ (Mealy/Moore machines), regular languages, Büchi automata, visibly pushdown
139
+ and nested-word automata, and residual finite-state automata. These recognize
140
+ or transform languages.
141
+ * **Symbolic shifts** (``sofic.shifts``) — shifts of finite type, sofic
142
+ shifts, topological Markov chains, and Dyck/sofic-Dyck shifts. These describe
143
+ the *support* (set of allowed sequences) of a process.
144
+
145
+ The package builds on NumPy, SciPy, and NetworkX and is designed to sit
146
+ alongside the `dit <https://github.com/dit/dit>`_ ecosystem for
147
+ information-theoretic analysis of the processes these models describe.
148
+
149
+ Installation
150
+ ------------
151
+
152
+ .. code-block:: bash
153
+
154
+ pip install sofic
155
+
156
+ Optional extras:
157
+
158
+ * ``sofic[viz]`` — Graphviz diagrams in terminals and Jupyter
159
+ * ``sofic[bayes]`` — PyMC/ArviZ backends for Bayesian inference
160
+ * ``sofic[test]`` — pytest, hypothesis, and graphviz for the test suite
161
+ * ``sofic[docs]`` — Sphinx, IPython, and matplotlib for the docs
162
+ * ``sofic[dev]`` — linting, type checking, docs, and all of the above
163
+
164
+ Quickstart
165
+ ----------
166
+
167
+ The basic workflow is the same for every model: build (or load) a generator,
168
+ call ``validate()``, then compute properties or convert to another
169
+ presentation. States can be any hashable object and every model exposes
170
+ ``states()``, ``transitions()``, ``draw()``, and ``to_yaml()``.
171
+
172
+ Stochastic generators
173
+ ~~~~~~~~~~~~~~~~~~~~~~~
174
+
175
+ **Markov chain** — a visible-state process. Edges carry ``P(target | source)``:
176
+
177
+ .. code-block:: python
178
+
179
+ from sofic import MarkovChain
180
+
181
+ mc = MarkovChain(initial_distribution={"sunny": 0.5, "rainy": 0.5})
182
+ mc.add_transition("sunny", "sunny", 0.9)
183
+ mc.add_transition("sunny", "rainy", 0.1)
184
+ mc.add_transition("rainy", "sunny", 0.5)
185
+ mc.add_transition("rainy", "rainy", 0.5)
186
+ mc.validate()
187
+
188
+ mc.stationary_distribution() # array([0.8333, 0.1667])
189
+ mc.entropy_rate() # 0.5575 bits/symbol
190
+ mc.is_deterministic() # False
191
+
192
+ **Moore HMM** — hidden states with an emission law ``P(observation | state)`` on
193
+ states and ``P(target | source)`` on edges:
194
+
195
+ .. code-block:: python
196
+
197
+ from sofic import MooreHMM
198
+
199
+ moore = MooreHMM(
200
+ observation_alphabet=frozenset({"H", "T"}),
201
+ initial_distribution={0: 0.5, 1: 0.5},
202
+ )
203
+ moore.add_transition(0, 0, 0.5)
204
+ moore.add_transition(0, 1, 0.5)
205
+ moore.add_transition(1, 0, 0.5)
206
+ moore.add_transition(1, 1, 0.5)
207
+ moore.set_emission_distribution(0, {"H": 0.9, "T": 0.1})
208
+ moore.set_emission_distribution(1, {"H": 0.1, "T": 0.9})
209
+ moore.validate()
210
+
211
+ moore.stationary_distribution() # array([0.5, 0.5])
212
+ moore.log_likelihood(["H", "H", "T", "T"]) # -2.7726 (log2)
213
+ observations, hidden = moore.sample(6) # simulate the process
214
+
215
+ **Mealy HMM** — hidden states with a joint transition/emission law
216
+ ``P(target, symbol | source)`` on edges. Here is the golden-mean process
217
+ (consecutive ``1``\ s are forbidden):
218
+
219
+ .. code-block:: python
220
+
221
+ from sofic import MealyHMM
222
+
223
+ gm = MealyHMM(
224
+ observation_alphabet=frozenset({0, 1}),
225
+ initial_distribution={"A": 2 / 3, "B": 1 / 3},
226
+ )
227
+ gm.add_transition("A", "A", 0, 0.5) # source, target, symbol, probability
228
+ gm.add_transition("A", "B", 1, 0.5)
229
+ gm.add_transition("B", "A", 0, 1.0)
230
+ gm.validate()
231
+
232
+ gm.entropy_rate() # 0.6667 bits/symbol
233
+ gm.is_unifilar() # True
234
+ gm.word_probability([1, 0, 1]) # 0.1667
235
+
236
+ Many canonical models ship in ``sofic.examples``, so the golden mean is
237
+ also just ``from sofic.examples import golden_mean; gm = golden_mean(0.5)``.
238
+
239
+ **ε-machine** — the minimal unifilar (causal-state) presentation of a stationary
240
+ process. Build one from any HMM, from an observed sequence, or directly, and
241
+ read off computational-mechanics quantities:
242
+
243
+ .. code-block:: python
244
+
245
+ from sofic import EpsilonMachine
246
+
247
+ eps = EpsilonMachine.from_hmm(gm) # minimize an HMM presentation
248
+ # eps = EpsilonMachine.from_sequence(data, method="cssr", Lmax=4) # infer
249
+ # eps = EpsilonMachine.from_sequence(data, method="spectral", prefix_length=3, rank=2)
250
+
251
+ eps.statistical_complexity() # 0.9183 bits (C_mu)
252
+ eps.entropy_rate() # 0.6667 bits/symbol (h_mu)
253
+ eps.markov_order() # 1
254
+ eps.cryptic_order() # 1
255
+
256
+ Finite automata
257
+ ~~~~~~~~~~~~~~~
258
+
259
+ **DFA** — a deterministic recognizer. This one accepts strings with an even
260
+ number of ``b``\ s (states are added before their transitions so determinism can
261
+ be checked as you go):
262
+
263
+ .. code-block:: python
264
+
265
+ from sofic import DFA
266
+
267
+ dfa = DFA(
268
+ input_alphabet=frozenset({"a", "b"}),
269
+ initial_states=frozenset({"even"}),
270
+ accepting_states=frozenset({"even"}),
271
+ )
272
+ dfa.graph.add_state("even")
273
+ dfa.graph.add_state("odd")
274
+ dfa.add_transition("even", "even", "a")
275
+ dfa.add_transition("even", "odd", "b")
276
+ dfa.add_transition("odd", "odd", "a")
277
+ dfa.add_transition("odd", "even", "b")
278
+ dfa.validate()
279
+
280
+ dfa.recognizes("abba") # True (two b's)
281
+ dfa.recognizes("abbb") # False (three b's)
282
+ dfa.minimize() # DFA(2 states, 4 transitions)
283
+ dfa.to_regex() # 'a*|a*b(?:...)*ba*'
284
+
285
+ **NFA** — nondeterministic, with first-class ε-transitions. Determinize to a DFA
286
+ when you need one:
287
+
288
+ .. code-block:: python
289
+
290
+ from sofic import NFA
291
+
292
+ nfa = NFA(
293
+ input_alphabet=frozenset({"a", "b"}),
294
+ initial_states=frozenset({"q0"}),
295
+ accepting_states=frozenset({"q2"}),
296
+ )
297
+ nfa.add_transition("q0", "q0", "a")
298
+ nfa.add_transition("q0", "q0", "b")
299
+ nfa.add_transition("q0", "q1", "a")
300
+ nfa.add_transition("q1", "q2", "b")
301
+ nfa.validate()
302
+
303
+ nfa.recognizes("ab") # True
304
+ dfa = nfa.determinize()
305
+
306
+ **Transducer (Mealy machine)** — maps input words to output words. This one
307
+ inverts bits:
308
+
309
+ .. code-block:: python
310
+
311
+ from sofic import MealyMachine
312
+
313
+ inv = MealyMachine(
314
+ input_alphabet=frozenset({"0", "1"}),
315
+ output_alphabet=frozenset({"0", "1"}),
316
+ initial_states=frozenset({"q"}),
317
+ )
318
+ inv.add_transition("q", "q", "0", "1") # source, target, input, output
319
+ inv.add_transition("q", "q", "1", "0")
320
+ inv.validate()
321
+
322
+ inv.transduce("0110") # {('1', '0', '0', '1')}
323
+
324
+ All automata also support ``union``, ``intersection``, ``complement``,
325
+ ``difference``, ``concat``, and ``kleene_star`` (see the
326
+ ``LabeledAutomaton`` base class).
327
+
328
+ Symbolic shifts
329
+ ~~~~~~~~~~~~~~~
330
+
331
+ **Shift of finite type** — the set of sequences avoiding a finite list of
332
+ forbidden words. The golden-mean shift forbids ``11``:
333
+
334
+ .. code-block:: python
335
+
336
+ from sofic import ShiftOfFiniteType
337
+
338
+ sft = ShiftOfFiniteType.from_forbidden_words(
339
+ {("1", "1")},
340
+ symbol_alphabet=frozenset({"0", "1"}),
341
+ )
342
+ sft.validate()
343
+
344
+ sft.forbidden_words() # frozenset({('1', '1')})
345
+ sorted(sft.factor_language(3)) # allowed length-3 words
346
+ sft.is_unifilar() # True (right-resolving presentation)
347
+
348
+ **Topological Markov chain** — an adjacency-matrix presentation; compute the
349
+ topological entropy or extract the measure of maximal entropy:
350
+
351
+ .. code-block:: python
352
+
353
+ import numpy as np
354
+ from sofic import TopologicalMarkovChain
355
+
356
+ tmc = TopologicalMarkovChain.from_adjacency(
357
+ np.array([[1, 1], [1, 0]], dtype=float), # golden-mean adjacency
358
+ symbol_alphabet=frozenset({0, 1}),
359
+ )
360
+ tmc.validate()
361
+
362
+ tmc.topological_entropy() # 0.4812 (ln of the golden ratio)
363
+ tmc.parry_measure() # MealyHMM at maximal entropy
364
+
365
+ **Sofic shift** — a labeled-graph presentation (the shift-space analog of an
366
+ NFA). This is the even shift (even-length runs of ``0`` between ``1``\ s):
367
+
368
+ .. code-block:: python
369
+
370
+ from sofic import SoficShift
371
+
372
+ even = SoficShift(symbol_alphabet=frozenset({0, 1}))
373
+ even.add_transition("even", "even", 0)
374
+ even.add_transition("even", "odd", 1)
375
+ even.add_transition("odd", "even", 1)
376
+ even.validate()
377
+
378
+ even.topological_entropy() # 0.4812
379
+
380
+ Converting between model types
381
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
382
+
383
+ The presentations are related by a web of conversions. Probabilistic models can
384
+ drop their weights to become automata or shifts (keeping only their support);
385
+ HMMs can be minimized to ε-machines; automata can be determinized, minimized,
386
+ and turned into regular expressions:
387
+
388
+ .. code-block:: python
389
+
390
+ # Between stochastic presentations
391
+ gm.to_mealy() # any HMM -> Mealy HMM
392
+ moore.to_mealy() # Moore -> Mealy (joint edge law)
393
+ EpsilonMachine.from_hmm(gm) # HMM -> minimal causal ε-machine
394
+ eps.to_bidirectional() # ε-machine -> bidirectional presentation
395
+ eps.mixed_state_presentation() # -> observer belief-state dynamics
396
+ EpsilonMachine.from_time_reversed(eps) # forward -> reverse ε-machine
397
+
398
+ # Stochastic -> topological (drop probabilities, keep the support)
399
+ gm.to_sofic_shift() # HMM support as a sofic shift
400
+ gm.to_support_nfa() # HMM support as an NFA
401
+ gm.to_support_dfa() # HMM support as a (determinized) DFA
402
+
403
+ # Between automata
404
+ nfa.determinize() # NFA -> DFA
405
+ DFA.from_nfa(nfa) # NFA -> DFA (same result)
406
+ dfa.minimize() # DFA -> minimal DFA
407
+ dfa.to_regex() # automaton -> regular expression
408
+
409
+ # Between / out of shifts
410
+ tmc.to_sofic_shift() # topological Markov chain -> sofic shift
411
+ tmc.parry_measure() # shift -> max-entropy MealyHMM
412
+ sofic.parry_measure() # sofic shift -> max-entropy MealyHMM
413
+
414
+ Every model can also be round-tripped through YAML:
415
+
416
+ .. code-block:: python
417
+
418
+ text = eps.to_yaml()
419
+ restored = EpsilonMachine.from_yaml(text)
420
+ # or: sofic.model_to_yaml(eps) / sofic.model_from_yaml(text)
421
+
422
+ Information anatomy (advanced)
423
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
424
+
425
+ With ``dit`` installed, a bidirectional ε-machine exposes the full stored- and
426
+ transient-information anatomy of a process (James, Burke & Crutchfield, 2013):
427
+
428
+ .. code-block:: python
429
+
430
+ from sofic.examples import golden_mean_bidirectional, tent_map_misiurewicz_bidirectional
431
+
432
+ bidir = golden_mean_bidirectional(0.5)
433
+ bidir.statistical_complexity() # 1.5850 bits (C±)
434
+ bidir.excess_entropy() # 0.2516 bits (E)
435
+ bidir.crypticity() # 1.3333 bits (chi = C± - E)
436
+
437
+ tent = tent_map_misiurewicz_bidirectional()
438
+ tent.information_anatomy() # {'rho_mu', 'bound_mu', 'ephemeral_mu',
439
+ # 'entropy_rate', 'excess_entropy', 'crypticity'}
440
+
441
+ License
442
+ -------
443
+
444
+ ``sofic`` is distributed under the BSD 3-Clause License; see ``LICENSE.txt``.