morphata 2.1.0__tar.gz → 2.2.1__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 (201) hide show
  1. {morphata-2.1.0 → morphata-2.2.1}/.envrc +1 -1
  2. {morphata-2.1.0 → morphata-2.2.1}/.gitignore +5 -1
  3. {morphata-2.1.0 → morphata-2.2.1}/.rstcheck.cfg +2 -0
  4. morphata-2.2.1/AGENTS.md +40 -0
  5. {morphata-2.1.0 → morphata-2.2.1}/CHANGELOG.md +143 -44
  6. morphata-2.2.1/PKG-INFO +134 -0
  7. morphata-2.2.1/README.rst +104 -0
  8. morphata-2.2.1/bench/conftest.py +32 -0
  9. {morphata-2.1.0 → morphata-2.2.1}/bench/test_equivalence_perf.py +12 -20
  10. {morphata-2.1.0 → morphata-2.2.1}/bench/test_hoa_exporter_perf.py +49 -38
  11. {morphata-2.1.0 → morphata-2.2.1}/bench/test_hoa_parser_perf.py +8 -6
  12. morphata-2.2.1/bench/test_sere_nlm_perf.py +107 -0
  13. morphata-2.2.1/bench/test_transforms_perf.py +120 -0
  14. {morphata-2.1.0 → morphata-2.2.1}/ci/make-release +16 -18
  15. morphata-2.2.1/docs/Makefile +20 -0
  16. {morphata-2.1.0 → morphata-2.2.1}/docs/api/modules.rst +6 -11
  17. morphata-2.2.1/docs/api/morphata.acc_expr.rst +6 -0
  18. morphata-2.2.1/docs/api/morphata.acceptance.rst +6 -0
  19. morphata-2.2.1/docs/api/morphata.alphabet.rst +6 -0
  20. morphata-2.2.1/docs/api/morphata.builder.rst +6 -0
  21. morphata-2.2.1/docs/api/morphata.hoa.rst +43 -0
  22. morphata-2.2.1/docs/api/morphata.logic.rst +11 -0
  23. morphata-2.2.1/docs/api/morphata.operators.rst +12 -0
  24. morphata-2.2.1/docs/api/morphata.symbolic.rst +38 -0
  25. morphata-2.2.1/docs/changelog.md +2 -0
  26. {morphata-2.1.0 → morphata-2.2.1}/docs/concepts/acceptance.rst +26 -13
  27. morphata-2.2.1/docs/concepts/hoa.rst +138 -0
  28. {morphata-2.1.0 → morphata-2.2.1}/docs/concepts/index.rst +3 -1
  29. morphata-2.2.1/docs/concepts/operators.rst +229 -0
  30. morphata-2.2.1/docs/concepts/symbolic.rst +99 -0
  31. morphata-2.2.1/docs/concepts/weights.rst +150 -0
  32. morphata-2.2.1/docs/conf.py +186 -0
  33. morphata-2.2.1/docs/index.rst +135 -0
  34. morphata-2.2.1/docs/quick-start.rst +221 -0
  35. {morphata-2.1.0 → morphata-2.2.1}/justfile +1 -0
  36. {morphata-2.1.0 → morphata-2.2.1}/pixi.lock +3465 -1291
  37. {morphata-2.1.0 → morphata-2.2.1}/pixi.toml +12 -8
  38. {morphata-2.1.0 → morphata-2.2.1}/pyproject.toml +13 -7
  39. {morphata-2.1.0 → morphata-2.2.1}/pytest.toml +2 -1
  40. morphata-2.2.1/rules/no-derivative-multiarg.yml +11 -0
  41. morphata-2.2.1/rules/no-typing-as-ty.yml +8 -0
  42. morphata-2.2.1/sgconfig.yml +2 -0
  43. morphata-2.2.1/src/morphata/__init__.py +23 -0
  44. morphata-2.2.1/src/morphata/_version.py +24 -0
  45. {morphata-2.1.0/src/morphata/hoa → morphata-2.2.1/src/morphata}/acc_expr.py +40 -10
  46. morphata-2.2.1/src/morphata/acceptance.py +1172 -0
  47. morphata-2.2.1/src/morphata/alphabet.py +92 -0
  48. morphata-2.2.1/src/morphata/builder.py +193 -0
  49. {morphata-2.1.0 → morphata-2.2.1}/src/morphata/hoa/__init__.py +4 -14
  50. {morphata-2.1.0 → morphata-2.2.1}/src/morphata/hoa/__main__.py +1 -1
  51. {morphata-2.1.0 → morphata-2.2.1}/src/morphata/hoa/parser.py +105 -6
  52. morphata-2.2.1/src/morphata/logic/__init__.py +14 -0
  53. morphata-2.2.1/src/morphata/logic/__init__.pyi +25 -0
  54. morphata-2.2.1/src/morphata/logic/_common.py +390 -0
  55. morphata-2.2.1/src/morphata/logic/ltl.py +109 -0
  56. morphata-2.2.1/src/morphata/logic/psl.py +74 -0
  57. morphata-2.2.1/src/morphata/logic/sere.py +205 -0
  58. {morphata-2.1.0 → morphata-2.2.1}/src/morphata/operators/_backend.py +3 -2
  59. morphata-2.2.1/src/morphata/operators/matrix.py +329 -0
  60. morphata-2.2.1/src/morphata/operators/polynomial.py +450 -0
  61. morphata-2.2.1/src/morphata/symbolic/__init__.py +14 -0
  62. morphata-2.2.1/src/morphata/symbolic/automaton.py +820 -0
  63. morphata-2.2.1/src/morphata/symbolic/bdd.py +1117 -0
  64. morphata-2.2.1/src/morphata/symbolic/utils.py +708 -0
  65. morphata-2.2.1/src/morphata/utils.py +200 -0
  66. {morphata-2.1.0 → morphata-2.2.1}/tests/conftest.py +45 -32
  67. morphata-2.2.1/tests/hoa/test_exporter.py +230 -0
  68. {morphata-2.1.0 → morphata-2.2.1}/tests/hoa/test_parser.py +1 -1
  69. {morphata-2.1.0 → morphata-2.2.1}/tests/logic/test_ltl.py +76 -160
  70. morphata-2.2.1/tests/logic/test_psl.py +178 -0
  71. morphata-2.2.1/tests/logic/test_sere.py +385 -0
  72. morphata-2.2.1/tests/operators/test_matrix.py +328 -0
  73. morphata-2.1.0/tests/operators/test_polynomial_from_ir.py → morphata-2.2.1/tests/operators/test_polynomial.py +52 -35
  74. morphata-2.2.1/tests/symbolic/test_bdd_helpers.py +652 -0
  75. morphata-2.2.1/tests/symbolic/test_compose.py +177 -0
  76. {morphata-2.1.0 → morphata-2.2.1}/tests/symbolic/test_from_hoaf.py +31 -3
  77. morphata-2.2.1/tests/symbolic/test_language_equivalence.py +156 -0
  78. {morphata-2.1.0 → morphata-2.2.1}/tests/symbolic/test_ltlf2dfa_equivalence.py +36 -38
  79. morphata-2.2.1/tests/symbolic/test_symbolic_automaton.py +462 -0
  80. morphata-2.2.1/tests/symbolic/test_to_hoa.py +97 -0
  81. morphata-2.2.1/tests/symbolic/test_transforms.py +530 -0
  82. morphata-2.2.1/tests/test_acceptance_validation.py +79 -0
  83. morphata-2.2.1/tests/test_alphabet.py +50 -0
  84. morphata-2.2.1/tests/test_builder.py +138 -0
  85. morphata-2.2.1/uv.lock +3335 -0
  86. morphata-2.1.0/AGENTS.md +0 -421
  87. morphata-2.1.0/PKG-INFO +0 -138
  88. morphata-2.1.0/README.md +0 -106
  89. morphata-2.1.0/bench/conftest.py +0 -55
  90. morphata-2.1.0/bench/test_sere_nlm_perf.py +0 -99
  91. morphata-2.1.0/bench/test_successors_for_alphabet_perf.py +0 -124
  92. morphata-2.1.0/cliff.toml +0 -59
  93. morphata-2.1.0/docs/api/morphata.acceptance.rst +0 -8
  94. morphata-2.1.0/docs/api/morphata.alphabet.rst +0 -8
  95. morphata-2.1.0/docs/api/morphata.automaton.rst +0 -11
  96. morphata-2.1.0/docs/api/morphata.builder.rst +0 -8
  97. morphata-2.1.0/docs/api/morphata.hoa.rst +0 -29
  98. morphata-2.1.0/docs/api/morphata.logic.ltl.rst +0 -8
  99. morphata-2.1.0/docs/api/morphata.logic.psl.rst +0 -15
  100. morphata-2.1.0/docs/api/morphata.logic.sere.rst +0 -9
  101. morphata-2.1.0/docs/api/morphata.logic.strel.rst +0 -8
  102. morphata-2.1.0/docs/api/morphata.operators.matrix.rst +0 -8
  103. morphata-2.1.0/docs/api/morphata.operators.polynomial.rst +0 -9
  104. morphata-2.1.0/docs/api/morphata.operators.rst +0 -9
  105. morphata-2.1.0/docs/api/morphata.symbolic.rst +0 -48
  106. morphata-2.1.0/docs/concepts/hoa.rst +0 -119
  107. morphata-2.1.0/docs/concepts/operators.rst +0 -184
  108. morphata-2.1.0/docs/concepts/spec.rst +0 -133
  109. morphata-2.1.0/docs/concepts/weights.rst +0 -167
  110. morphata-2.1.0/docs/conf.py +0 -71
  111. morphata-2.1.0/docs/index.rst +0 -139
  112. morphata-2.1.0/docs/quick-start.rst +0 -139
  113. morphata-2.1.0/docs/symbolic-transforms.rst +0 -208
  114. morphata-2.1.0/src/morphata/__init__.py +0 -29
  115. morphata-2.1.0/src/morphata/acceptance.py +0 -565
  116. morphata-2.1.0/src/morphata/alphabet.py +0 -178
  117. morphata-2.1.0/src/morphata/automaton.py +0 -345
  118. morphata-2.1.0/src/morphata/builder.py +0 -159
  119. morphata-2.1.0/src/morphata/hoa/exporter.py +0 -797
  120. morphata-2.1.0/src/morphata/logic/__init__.py +0 -15
  121. morphata-2.1.0/src/morphata/logic/_common.py +0 -325
  122. morphata-2.1.0/src/morphata/logic/ltl.py +0 -268
  123. morphata-2.1.0/src/morphata/logic/psl.py +0 -319
  124. morphata-2.1.0/src/morphata/logic/sere.py +0 -436
  125. morphata-2.1.0/src/morphata/logic/strel.py +0 -488
  126. morphata-2.1.0/src/morphata/operators/matrix.py +0 -308
  127. morphata-2.1.0/src/morphata/operators/polynomial.py +0 -357
  128. morphata-2.1.0/src/morphata/symbolic/__init__.py +0 -57
  129. morphata-2.1.0/src/morphata/symbolic/automaton.py +0 -623
  130. morphata-2.1.0/src/morphata/symbolic/bdd.py +0 -941
  131. morphata-2.1.0/src/morphata/symbolic/compose.py +0 -506
  132. morphata-2.1.0/src/morphata/symbolic/equivalence.py +0 -199
  133. morphata-2.1.0/src/morphata/symbolic/persistence.py +0 -470
  134. morphata-2.1.0/src/morphata/symbolic/transforms.py +0 -861
  135. morphata-2.1.0/src/morphata/utils.py +0 -74
  136. morphata-2.1.0/tests/hoa/test_exporter_basic.py +0 -125
  137. morphata-2.1.0/tests/hoa/test_exporter_roundtrip.py +0 -43
  138. morphata-2.1.0/tests/hoa/test_exporter_streaming.py +0 -310
  139. morphata-2.1.0/tests/logic/test_psl.py +0 -196
  140. morphata-2.1.0/tests/logic/test_sere.py +0 -547
  141. morphata-2.1.0/tests/logic/test_sere_properties.py +0 -152
  142. morphata-2.1.0/tests/logic/test_strel.py +0 -316
  143. morphata-2.1.0/tests/operators/test_matrix_from_ir.py +0 -249
  144. morphata-2.1.0/tests/operators/test_matrix_from_ltl.py +0 -68
  145. morphata-2.1.0/tests/symbolic/test_alphabet_aware_transitions.py +0 -174
  146. morphata-2.1.0/tests/symbolic/test_bdd_helpers.py +0 -318
  147. morphata-2.1.0/tests/symbolic/test_compose.py +0 -449
  148. morphata-2.1.0/tests/symbolic/test_composition_map.py +0 -312
  149. morphata-2.1.0/tests/symbolic/test_equivalence_naive_property.py +0 -138
  150. morphata-2.1.0/tests/symbolic/test_persistence.py +0 -308
  151. morphata-2.1.0/tests/symbolic/test_prune.py +0 -280
  152. morphata-2.1.0/tests/symbolic/test_step.py +0 -77
  153. morphata-2.1.0/tests/symbolic/test_symbolic_automaton.py +0 -459
  154. morphata-2.1.0/tests/symbolic/test_symbolic_from_ir.py +0 -55
  155. morphata-2.1.0/tests/symbolic/test_to_hoa.py +0 -176
  156. morphata-2.1.0/tests/symbolic/test_transforms.py +0 -451
  157. morphata-2.1.0/tests/symbolic/test_transforms_ir.py +0 -41
  158. morphata-2.1.0/tests/symbolic/test_transforms_properties.py +0 -140
  159. morphata-2.1.0/tests/test_acceptance_validation.py +0 -45
  160. morphata-2.1.0/tests/test_alphabet.py +0 -205
  161. morphata-2.1.0/tests/test_automaton.py +0 -242
  162. morphata-2.1.0/tests/test_builder.py +0 -124
  163. morphata-2.1.0/tests/test_from_parsed.py +0 -101
  164. morphata-2.1.0/uv.lock +0 -3331
  165. {morphata-2.1.0 → morphata-2.2.1}/.basedpyright/baseline.json +0 -0
  166. {morphata-2.1.0 → morphata-2.2.1}/.dockerignore +0 -0
  167. {morphata-2.1.0 → morphata-2.2.1}/.editorconfig +0 -0
  168. {morphata-2.1.0 → morphata-2.2.1}/.envrc.recommended +0 -0
  169. {morphata-2.1.0 → morphata-2.2.1}/.fdignore +0 -0
  170. {morphata-2.1.0 → morphata-2.2.1}/.gitattributes +0 -0
  171. {morphata-2.1.0 → morphata-2.2.1}/.mailmap +0 -0
  172. {morphata-2.1.0 → morphata-2.2.1}/.python-version +0 -0
  173. {morphata-2.1.0 → morphata-2.2.1}/CLAUDE.md +0 -0
  174. {morphata-2.1.0 → morphata-2.2.1}/CONTRIBUTING.md +0 -0
  175. {morphata-2.1.0 → morphata-2.2.1}/LICENSE +0 -0
  176. {morphata-2.1.0 → morphata-2.2.1}/bench/README.md +0 -0
  177. {morphata-2.1.0 → morphata-2.2.1}/bench/__init__.py +0 -0
  178. {morphata-2.1.0 → morphata-2.2.1}/docs/_static/navigation.html +0 -0
  179. {morphata-2.1.0 → morphata-2.2.1}/mypy.ini +0 -0
  180. {morphata-2.1.0 → morphata-2.2.1}/project.utf-8.add +0 -0
  181. {morphata-2.1.0 → morphata-2.2.1}/project.utf-8.add.spl +0 -0
  182. {morphata-2.1.0 → morphata-2.2.1}/pyrightconfig.json +0 -0
  183. {morphata-2.1.0 → morphata-2.2.1}/src/morphata/hoa/hoa.lark +0 -0
  184. {morphata-2.1.0 → morphata-2.2.1}/src/morphata/operators/__init__.py +0 -0
  185. {morphata-2.1.0 → morphata-2.2.1}/src/morphata/py.typed +0 -0
  186. {morphata-2.1.0 → morphata-2.2.1}/tests/hoa/aut1.hoa +0 -0
  187. {morphata-2.1.0 → morphata-2.2.1}/tests/hoa/aut11.hoa +0 -0
  188. {morphata-2.1.0 → morphata-2.2.1}/tests/hoa/aut2.hoa +0 -0
  189. {morphata-2.1.0 → morphata-2.2.1}/tests/hoa/aut3.2.hoa +0 -0
  190. {morphata-2.1.0 → morphata-2.2.1}/tests/hoa/aut3.hoa +0 -0
  191. {morphata-2.1.0 → morphata-2.2.1}/tests/hoa/aut4.hoa +0 -0
  192. {morphata-2.1.0 → morphata-2.2.1}/tests/hoa/aut5.hoa +0 -0
  193. {morphata-2.1.0 → morphata-2.2.1}/tests/hoa/aut6.hoa +0 -0
  194. {morphata-2.1.0 → morphata-2.2.1}/tests/hoa/aut7.hoa +0 -0
  195. {morphata-2.1.0 → morphata-2.2.1}/tests/hoa/aut8.hoa +0 -0
  196. {morphata-2.1.0 → morphata-2.2.1}/tests/hoa/finite_aut.hoa +0 -0
  197. {morphata-2.1.0 → morphata-2.2.1}/tests/hoa/gen_buchi_3.hoa +0 -0
  198. {morphata-2.1.0 → morphata-2.2.1}/tests/hoa/gen_cobuchi_2.hoa +0 -0
  199. {morphata-2.1.0 → morphata-2.2.1}/tests/hoa/rabin_2pair.hoa +0 -0
  200. {morphata-2.1.0 → morphata-2.2.1}/tests/hoa/streett_2pair.hoa +0 -0
  201. {morphata-2.1.0 → morphata-2.2.1}/tests/logic/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
- set -euo pipefail
3
+ # set -euo pipefail
4
4
 
5
5
  info() {
6
6
  DIRENV_LOG_FORMAT="envrc: %s" log_status "$@"
@@ -1,7 +1,11 @@
1
+ # generated version file
2
+ src/morphata/_version.py
3
+
1
4
  # generated type stubs
2
5
  /typings/
3
6
  # pixi environments
4
- .pixi
7
+ .pixi/*
8
+ !.pixi/config.toml
5
9
 
6
10
  # Created by https://www.toptal.com/developers/gitignore/api/python
7
11
  # Edit at https://www.toptal.com/developers/gitignore?templates=python
@@ -3,6 +3,8 @@ ignore_directives =
3
3
  automodule,
4
4
  autoclass,
5
5
  autofunction,
6
+ autotype,
7
+ autoexception,
6
8
  ignore_roles =
7
9
  external:py:mod,
8
10
  report_level = WARNING
@@ -0,0 +1,40 @@
1
+ # Morphata: Agent Context
2
+
3
+ `morphata` does representation theory for automata, and makes it practical.
4
+ It packages structural automaton representations together with semiring-valued
5
+ *operators* that turn an automaton into a computable interpretation.
6
+
7
+ ## What Agents Are for Here
8
+
9
+ This codebase is written and maintained by a single, highly opinionated author.
10
+ The default mode for an LLM here is **read-only**: understand the code, explain it,
11
+ answer questions, and locate things.
12
+ That is the job.
13
+
14
+ **Do not write or restructure code.**
15
+ Assume every design decision was made deliberately.
16
+ If you think something should change, say so and stop — do not implement it.
17
+ In particular, never:
18
+
19
+ - introduce new modules, abstractions, or layers of indirection;
20
+ - refactor, rename, or "clean up" working code;
21
+ - add compatibility shims, fallbacks, or defensive scaffolding;
22
+ - broaden an API or generalize past what was asked;
23
+ - follow a tangent into a rabbit hole.
24
+ When the task grows past a line or two of obvious change,
25
+ stop and report instead of chasing it.
26
+
27
+ The narrow exception: a **surgical, already-decided edit** the author has spelled out
28
+ (a typo, a one-line fix, a mechanical sweep).
29
+ Do exactly that and nothing more.
30
+ When unsure whether a change qualifies, treat it as out of scope and ask.
31
+
32
+ ## Development Environment
33
+
34
+ The development shell already has the project virtual environment activated,
35
+ so `pytest`, `zuban`, `basedpyright`, `ruff`, `sphinx-build`,
36
+ etc. are on `PATH` directly.
37
+ **Do not** prefix commands with `uv run`, `pixi run`, `poetry run`, or similar.
38
+ Just invoke the tool.
39
+ If a tool is missing, stop and tell the user rather than trying to bootstrap
40
+ or reinstall the environment.
@@ -1,40 +1,145 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.2.1 — 2026-08-20
4
+
5
+ This version bump is mostly a cleanup after the 2.2.0 rewrite,
6
+ but also swaps `dd` for `pollywog` as the wrapper around CUDD
7
+ for the symbolic automaton.
8
+ The main reason for the latter is that `dd` wasn't well-typed,
9
+ and because `dd` doesn't expose Algebraic Decision Diagrams,
10
+ which may be useful for a future symbolic weighted automaton.
11
+
12
+ ### Changed
13
+
14
+ - The BDD layer now uses `pollywog` instead of `dd`,
15
+ and the symbolic automaton code was adjusted to match.
16
+ Most of the visible work here is internal:
17
+ manager transfers and substitutions were tightened up,
18
+ and the tests moved along with the backend swap.
19
+ - Project versioning now comes from the VCS tag instead of being hard-coded in
20
+ `pyproject.toml`.
21
+ The release script now takes an explicit target version,
22
+ and `morphata.__version__` is exported from the generated version file.
23
+
24
+ ## 2.2.0 — 2026-08-19
25
+
26
+ This is a major release as it cleans up a lot of bad code,
27
+ attempts to make the design choices more consistent,
28
+ and increase the surface area of properly typed code.
29
+
30
+ ### Added
31
+
32
+ - Add finite-word composition directly to `SymbolicAutomaton`: `&`, `|`, `^`,
33
+ and `~` implement intersection, union, symmetric difference, and complement.
34
+ The variadic `compose_automata`, `is_language_equivalent`,
35
+ and `forward_bisim_reduction` helpers cover composition, equivalence checking,
36
+ and language-preserving quotienting without leaving the symbolic representation.
37
+ - Add a typed `BDDManager`/`Function` API and symbolic utilities for replacing a
38
+ manager, reindexing or quotienting states, materializing sinks, traversing reachable
39
+ states, and constructing universal acceptors and rejectors.
40
+ - Support arbitrary HOA v1 acceptance expressions through `GenericCondition`,
41
+ in addition to the concrete finite, Büchi, co-Büchi, generalized Büchi/co-Büchi,
42
+ Rabin, and Streett conditions.
43
+ Omega acceptance remains structural only:
44
+ run evaluation and transforms still implement finite-word semantics.
45
+
46
+ ### Changed
47
+
48
+ - **The primary automaton API is now `SymbolicAutomaton`.**
49
+ The intermediate `morphata.automaton.Automaton` representation has been removed.
50
+ Logic conversion, hand construction, transforms, composition, HOA conversion,
51
+ and operators now work directly with the BDD-backed type.
52
+ Migrate logic calls to `formula_to_automaton(...)`
53
+ or `SymbolicAutomaton.from_specification(...)`,
54
+ and use the instance methods `remove_alternation()`, `determinize()`,
55
+ and `to_min_dfa()` instead of the old transform functions.
56
+ - `AutomatonBuilder` now accepts integer state identifiers
57
+ and `build()` returns a `SymbolicAutomaton`; pass a `labeling_fn`
58
+ (and optionally a shared `BDDManager`).
59
+ Set-valued successors are disjunctions;
60
+ use a Boolean state expression for an alternating conjunction.
61
+ The old `variant`/`DeltaKind` construction API is gone.
62
+ - Alphabets now separate symbol labeling from guard weighting.
63
+ `BooleanAlphabet.labeling` returns an `AP -> bool` mapping, while operator weight
64
+ functions receive guards in sum-of-products form. `Alphabet`, `WeightedAlphabet`,
65
+ and `weighted_alphabet` are replaced by `BooleanAlphabet`, `SemiringAlphabet`,
66
+ `powerset_alphabet`, and `finite_alphabet`.
67
+ - `MatrixOperator` and `PolynomialOperator` are constructed with
68
+ `from_symbolic_automaton` or `from_specification`.
69
+ Pass `weight_fn` directly rather than an alphabet;
70
+ matrix construction also requires `backend`.
71
+ Their `accepts()` methods return a scalar `AlgebraicArray`,
72
+ and transition generation is exposed as `op.delta(symbol)`.
73
+ - HOA conversion now goes through `ParsedAutomaton`:
74
+ use `SymbolicAutomaton.from_hoaf(parsed, alphabet=...)` and `str(aut.to_hoaf())`.
75
+ State IDs need not be dense, and states without outgoing edges survive round trips.
76
+
77
+ ### Fixed
78
+
79
+ - Correct symbolic derivatives for negated variables and SERE nodes.
80
+ - Fix breadth-first traversal ordering, symbolic language equivalence, and transition
81
+ partitioning when multiple guards have the same residual.
82
+ - Correct `step()` labeling and the structural checks for deterministic and
83
+ forward-progressing automata.
84
+ - Make builders, HOA conversion, and both operators handle sparse external state IDs
85
+ consistently by using dense indices only internally.
86
+
87
+ ### Removed
88
+
89
+ - Remove the old `morphata.automaton` IR, STREL translator,
90
+ BDD-JSON persistence/cache API, and the former `symbolic.transforms`,
91
+ `symbolic.equivalence`, and `symbolic.compose` modules.
92
+ Their surviving operations now live on `SymbolicAutomaton` or in `morphata.symbolic`;
93
+ there is no replacement for STREL or BDD-JSON persistence in this release.
94
+ - Remove the specialized composition-map/transition-enumeration APIs and structural
95
+ `prune`; use general symbolic composition and `forward_bisim_reduction` where those
96
+ semantics fit.
97
+
3
98
  ## 2.1.0 — 2026-06-11
4
99
 
5
100
  ### Added
6
101
 
7
- - Add `morphata.symbolic.prune` that does a cheap, language-preserving structural
8
- pruning of an AFA. The idea here is to try and reduce the number of states in the AFA
9
- (no guarantees) by performing a forward reachability pass, dead-state pruning, and
10
- a parameterized number of passes for one-step acceptance-partition deduplication.
102
+ - Add `morphata.symbolic.prune` that does a cheap,
103
+ language-preserving structural pruning of an AFA.
104
+ The idea here is to try and reduce the number of states in the AFA
105
+ (no guarantees)
106
+ by performing a forward reachability pass, dead-state pruning,
107
+ and a parameterized number of passes for one-step acceptance-partition deduplication.
11
108
 
12
109
  ### Changed
13
110
 
14
- - `MatrixOperator` and `PolynomialOperator` are generic now — `MatrixOperator[Symbol, AP]`
15
- and `PolynomialOperator[Symbol, AP]`, just like `SymbolicAutomaton`. When I first banged
16
- these out I left them un-parameterized (read: `Any` smeared everywhere), so now the
17
- symbol and AP types actually flow through and `MatrixOperator[frozenset[str], str]`
18
- means something. Nothing changes at runtime; the one thing you might notice is your
19
- type-checker nudging you to fill in the params if you were annotating a bare
20
- `MatrixOperator` or `PolynomialOperator`.
21
- - The BDD-JSON sidecar cache jumped to schema **v2**. Acceptance conditions now
22
- (de)serialize through `cattrs` instead of the hand-rolled `meta.get(...)` dance, and
23
- Rabin/Streett pairs are written as objects (`{"rejecting": ..., "accepting": ...}`)
24
- rather than nested lists. Heads up: old `v1` `.bdd.json` caches will politely refuse
25
- to load (clean error, not a silent miscompile) --- just regenerate them and you're good.
26
- Remember, this is a *cache*, not a portable format, so this shouldn't bite anyone hard.
27
- - STREL conversion shed ~120 lines. All the timed-interval unrolling (`G[a,b]`, `F[a,b]`,
28
- `U[t1,t2]`, `X[n]`) and the spatial `Somewhere`/`Everywhere` desugaring used to be
29
- hand-rolled in `strel.py`, but `logic-asts` already does all of it in `.expand()` — so
30
- I ripped them out and just lean on the upstream rules. This needs the newer `logic-asts`
111
+ - `MatrixOperator` and `PolynomialOperator` are generic now —
112
+ `MatrixOperator[Symbol, AP]` and `PolynomialOperator[Symbol, AP]`,
113
+ just like `SymbolicAutomaton`.
114
+ When I first banged these out I left them un-parameterized
115
+ (read: `Any` smeared everywhere),
116
+ so now the symbol and AP types actually flow through
117
+ and `MatrixOperator[frozenset[str], str]` means something.
118
+ Nothing changes at runtime;
119
+ the one thing you might notice is your type-checker nudging you to fill in the params
120
+ if you were annotating a bare `MatrixOperator` or `PolynomialOperator`.
121
+ - The BDD-JSON sidecar cache jumped to schema **v2**.
122
+ Acceptance conditions now (de)serialize through `cattrs` instead of the hand-rolled
123
+ `meta.get(...)` dance, and Rabin/Streett pairs are written as objects
124
+ (`{"rejecting": ..., "accepting": ...}`) rather than nested lists.
125
+ Heads up: old `v1` `.bdd.json` caches will politely refuse to load
126
+ (clean error, not a silent miscompile) --- just regenerate them and you're good.
127
+ Remember, this is a *cache*, not a portable format,
128
+ so this shouldn't bite anyone hard.
129
+ - STREL conversion shed ~120 lines.
130
+ All the timed-interval unrolling
131
+ (`G[a,b]`, `F[a,b]`, `U[t1,t2]`, `X[n]`)
132
+ and the spatial `Somewhere`/`Everywhere` desugaring used to be hand-rolled in
133
+ `strel.py`, but `logic-asts` already does all of it in `.expand()` — so I ripped them
134
+ out and just lean on the upstream rules.
135
+ This needs the newer `logic-asts`
31
136
  (the one with the spatial `expand()` rules), so that dep gets bumped along with it.
32
137
 
33
138
  ### Miscellaneous
34
139
 
35
- - Housekeeping, but worth saying: the whole package now type-checks clean under *both*
36
- `zuban` and `basedpyright` (0 errors, 0 warnings), so contributing should be a bit less
37
- whack-a-mole.
140
+ - Housekeeping, but worth saying:
141
+ the whole package now type-checks clean under *both* `zuban` and `basedpyright`
142
+ (0 errors, 0 warnings), so contributing should be a bit less whack-a-mole.
38
143
 
39
144
  ## 2.0.10 — 2026-06-08
40
145
 
@@ -46,8 +151,8 @@
46
151
 
47
152
  ### Performance
48
153
 
49
- - The HOA grammar is now LALR compatible! This means we get a huge performance boost
50
- even for pretty large automata.
154
+ - The HOA grammar is now LALR compatible!
155
+ This means we get a huge performance boost even for pretty large automata.
51
156
  - Similar thing was done in `logic-asts` and thus, the version will be bumped.
52
157
 
53
158
  ## 2.0.9 — 2026-06-04
@@ -123,7 +228,7 @@
123
228
  ### Fixed
124
229
 
125
230
  - A major regression where the streaming `to_hoa` stuff changed the label enumeration
126
- semantics to be _minterms_ instead of _cubes_.
231
+ semantics to be *minterms* instead of *cubes*.
127
232
  This is now fixed.
128
233
 
129
234
  ### Added
@@ -344,8 +449,7 @@ which will be summarized below.
344
449
  The reverse direction is deliberately omitted —
345
450
  recovering Boolean semantics from arbitrary `V` needs an ordering and a cut (cf.
346
451
  STL), and pretending otherwise would silently produce garbage.
347
- - **Ergonomic construction.**
348
- `AutomatonBuilder` replaces the old NFA builder.
452
+ - **Ergonomic construction.** `AutomatonBuilder` replaces the old NFA builder.
349
453
  Add states, add transitions with a guard and a successor
350
454
  (single state, set of states, or a raw `BoolExpr` for full alternation),
351
455
  and `build()` cleans up by merging rows that share a successor.
@@ -373,11 +477,9 @@ which will be summarized below.
373
477
  The BDD cofactor path works for any `BooleanAlphabet`,
374
478
  so you can feed a `SymbolicAutomaton` over integers, graph nodes, whatever —
375
479
  as long as you can write a labeling.
376
- - **The AFA-to-min-DFA pipeline actually exists now.**
377
- `nfa_from_afa` (dealternation), `determinize`
378
- (subset construction with a sink),
379
- `minimize` (Hopcroft) — all share one BDD manager
380
- and successively tighten the invariants on successor BDDs.
480
+ - **The AFA-to-min-DFA pipeline actually exists now.** `nfa_from_afa` (dealternation),
481
+ `determinize` (subset construction with a sink), `minimize` (Hopcroft) —
482
+ all share one BDD manager and successively tighten the invariants on successor BDDs.
381
483
  `SymbolicAutomaton.to_min_dfa` chains the three.
382
484
  Finite-word only for now; omega-regular determinisation is a different beast.
383
485
  - **DFA equivalence checks.**
@@ -388,19 +490,17 @@ which will be summarized below.
388
490
 
389
491
  ### Operators
390
492
 
391
- - **One per-step shape.**
392
- `SymbolicAutomaton`, `MatrixOperator`,
493
+ - **One per-step shape.** `SymbolicAutomaton`, `MatrixOperator`,
393
494
  and `PolynomialOperator` all expose the same `step(current, symbol)` / `run(word)` /
394
495
  `accepts(word)` shape.
395
496
  Each binds its alphabet at construction
396
497
  (matching how the semiring / algebra were already bound),
397
498
  so the per-call API is monomorphic and `V` is pinned end-to-end.
398
499
  Wanted symmetry between the layers; got it.
399
- - **`PolynomialOperator` evaluates lazily.**
400
- `step` evaluates guards against `alphabet.weight` on the fly,
401
- with no per-symbol precomputation —
402
- important because the natural LTL alphabet is `Σ = 2^AP`,
403
- and you really don't want to materialise `2^|AP|` polynomials up front.
500
+ - **`PolynomialOperator` evaluates lazily.** `step` evaluates guards against
501
+ `alphabet.weight` on the fly, with no per-symbol precomputation — important because
502
+ the natural LTL alphabet is `Σ = 2^AP`, and you really don't want to materialise
503
+ `2^|AP|` polynomials up front.
404
504
  The AFA's row structure stays static
405
505
  (good for JAX tracing), and the alphabet decides whether to memoise.
406
506
  - **Polynomial performance.**
@@ -410,9 +510,8 @@ which will be summarized below.
410
510
 
411
511
  ### Tooling and Tests
412
512
 
413
- - **Hand-written changelogs.**
414
- `git cliff` was producing entries that needed enough editing that I might
415
- as well write them by hand.
513
+ - **Hand-written changelogs.** `git cliff` was producing entries
514
+ that needed enough editing that I might as well write them by hand.
416
515
  `ci/make-release` no longer touches `CHANGELOG.md` or tag messages.
417
516
  Run `git cliff` separately if you want a draft to crib from.
418
517
  - **Tests.**
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: morphata
3
+ Version: 2.2.1
4
+ Summary: Representation theory for automata, made practical.
5
+ Project-URL: Documentation, https://docs.anandb.dev/morphata
6
+ Project-URL: Repository, https://git.anandb.dev/morphata.git
7
+ Project-URL: Changelog, https://docs.anandb.dev/morphata/changelog.html
8
+ Author-email: Anand Balakrishnan <anandbala1597@gmail.com>
9
+ License-Expression: BSD-3-Clause
10
+ License-File: LICENSE
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
13
+ Requires-Python: >=3.12
14
+ Requires-Dist: algebraic-arrays>=1.3.0
15
+ Requires-Dist: attrs>=25.4.0
16
+ Requires-Dist: cattrs>=26.1.0
17
+ Requires-Dist: jaxtyping>=0.3
18
+ Requires-Dist: lark>=1.3.1
19
+ Requires-Dist: logic-asts>=1.8
20
+ Requires-Dist: numpy
21
+ Requires-Dist: pollywog>=0.1.1
22
+ Requires-Dist: typing-extensions>=4.14
23
+ Provides-Extra: dot
24
+ Requires-Dist: pydot>=4.0.1; extra == 'dot'
25
+ Provides-Extra: jax
26
+ Requires-Dist: jax>=0.8; extra == 'jax'
27
+ Provides-Extra: torch
28
+ Requires-Dist: torch>=2.0; extra == 'torch'
29
+ Description-Content-Type: text/x-rst
30
+
31
+ Morphata
32
+ ========
33
+
34
+ **morphata** does representation theory for automata.
35
+ An automaton -- its states, transitions,
36
+ and acceptance condition -- is a *structural* object.
37
+ ``SymbolicAutomaton`` is the BDD-backed structural representation.
38
+ ``MatrixOperator`` and ``PolynomialOperator`` interpret that structure over
39
+ semiring and bounded-distributive-lattice domains.
40
+
41
+ It builds on `algebraic <https://docs.anandb.dev/algebraic>`_ for
42
+ backend-agnostic semiring algebra (NumPy, JAX, PyTorch).
43
+
44
+ To install the latest release of ``morphata``, install from
45
+ `PyPI <https://pypi.org/project/morphata/>`_:
46
+
47
+ .. code-block:: bash
48
+
49
+ pip install morphata
50
+
51
+ Or from the latest Git head:
52
+
53
+ .. code-block:: bash
54
+
55
+ pip install 'morphata @ git+https://git.anandb.dev/morphata.git@main'
56
+
57
+ For more, see the `documentation <https://docs.anandb.dev/morphata>`_.
58
+
59
+ .. code-block:: python
60
+
61
+ >>> import logic_asts as logic
62
+ >>> import logic_asts.ltl as ltl
63
+
64
+ >>> from morphata.alphabet import powerset_alphabet
65
+ >>> from morphata.logic import formula_to_automaton
66
+
67
+ >>> a = logic.Variable("a")
68
+ >>> aut = formula_to_automaton(
69
+ ... ltl.Eventually(a), alphabet=powerset_alphabet({"a"}), finite=True
70
+ ... )
71
+ >>> aut.accepts([set(), {"a"}])
72
+ True
73
+
74
+ Omega-regular conditions are supported structurally, but infinite-run acceptance
75
+ evaluation is not implemented. See the full documentation for details.
76
+
77
+ Citation
78
+ --------
79
+
80
+ To cite the use of this package or the papers
81
+ that are derived from it you can use the below BibTeX entries.
82
+
83
+ - For differentiable weighted automata in general:
84
+
85
+ .. code-block:: bibtex
86
+
87
+ @inproceedings{balakrishnan2024differentiable,
88
+ title = {Differentiable {{Weighted Automata}}},
89
+ booktitle = {{{ICML}} 2024 {{Workshop}} on {{Differentiable Almost Everything}}: {{Differentiable Relaxations}}, {{Algorithms}}, {{Operators}}, and {{Simulators}}},
90
+ author = {Balakrishnan, Anand and Deshmukh, Jyotirmoy V.},
91
+ year = 2024,
92
+ month = jun,
93
+ url = {https://openreview.net/forum?id=k2hIQYqHTh},
94
+ copyright = {All rights reserved},
95
+ langid = {english}
96
+ }
97
+
98
+ - For weighted automata in motion planning:
99
+
100
+ .. code-block:: bibtex
101
+
102
+ @inproceedings{balakrishnan2024motion,
103
+ title = {Motion {{Planning}} for {{Automata-based Objectives}} Using {{Efficient Gradient-based Methods}}},
104
+ booktitle = {2024 {{IEEE}}/{{RSJ International Conference}} on {{Intelligent Robots}} and {{Systems}} ({{IROS}})},
105
+ author = {Balakrishnan, Anand and Atasever, Merve and Deshmukh, Jyotirmoy V.},
106
+ year = 2024,
107
+ month = oct,
108
+ pages = {13734--13740},
109
+ issn = {2153-0866},
110
+ doi = {10.1109/IROS58592.2024.10802177}
111
+ }
112
+
113
+ - For alternating weighted automata in multi-agent systems:
114
+
115
+ .. code-block:: bibtex
116
+
117
+ @inproceedings{balakrishnan2025monitoring,
118
+ title = {Monitoring {{Spatially Distributed Cyber-Physical Systems}} with {{Alternating Finite Automata}}},
119
+ booktitle = {Proceedings of the 28th {{ACM International Conference}} on {{Hybrid Systems}}: {{Computation}} and {{Control}}},
120
+ author = {Balakrishnan, Anand and Paul, Sheryl and Silvetti, Simone and Nenzi, Laura and Deshmukh, Jyotirmoy V.},
121
+ year = 2025,
122
+ month = may,
123
+ pages = {1--11},
124
+ publisher = {ACM},
125
+ address = {Irvine CA USA},
126
+ doi = {10.1145/3716863.3718033},
127
+ isbn = {979-8-4007-1504-4},
128
+ langid = {english}
129
+ }
130
+
131
+ License
132
+ -------
133
+
134
+ See the ``LICENSE`` file for details.
@@ -0,0 +1,104 @@
1
+ Morphata
2
+ ========
3
+
4
+ **morphata** does representation theory for automata.
5
+ An automaton -- its states, transitions,
6
+ and acceptance condition -- is a *structural* object.
7
+ ``SymbolicAutomaton`` is the BDD-backed structural representation.
8
+ ``MatrixOperator`` and ``PolynomialOperator`` interpret that structure over
9
+ semiring and bounded-distributive-lattice domains.
10
+
11
+ It builds on `algebraic <https://docs.anandb.dev/algebraic>`_ for
12
+ backend-agnostic semiring algebra (NumPy, JAX, PyTorch).
13
+
14
+ To install the latest release of ``morphata``, install from
15
+ `PyPI <https://pypi.org/project/morphata/>`_:
16
+
17
+ .. code-block:: bash
18
+
19
+ pip install morphata
20
+
21
+ Or from the latest Git head:
22
+
23
+ .. code-block:: bash
24
+
25
+ pip install 'morphata @ git+https://git.anandb.dev/morphata.git@main'
26
+
27
+ For more, see the `documentation <https://docs.anandb.dev/morphata>`_.
28
+
29
+ .. code-block:: python
30
+
31
+ >>> import logic_asts as logic
32
+ >>> import logic_asts.ltl as ltl
33
+
34
+ >>> from morphata.alphabet import powerset_alphabet
35
+ >>> from morphata.logic import formula_to_automaton
36
+
37
+ >>> a = logic.Variable("a")
38
+ >>> aut = formula_to_automaton(
39
+ ... ltl.Eventually(a), alphabet=powerset_alphabet({"a"}), finite=True
40
+ ... )
41
+ >>> aut.accepts([set(), {"a"}])
42
+ True
43
+
44
+ Omega-regular conditions are supported structurally, but infinite-run acceptance
45
+ evaluation is not implemented. See the full documentation for details.
46
+
47
+ Citation
48
+ --------
49
+
50
+ To cite the use of this package or the papers
51
+ that are derived from it you can use the below BibTeX entries.
52
+
53
+ - For differentiable weighted automata in general:
54
+
55
+ .. code-block:: bibtex
56
+
57
+ @inproceedings{balakrishnan2024differentiable,
58
+ title = {Differentiable {{Weighted Automata}}},
59
+ booktitle = {{{ICML}} 2024 {{Workshop}} on {{Differentiable Almost Everything}}: {{Differentiable Relaxations}}, {{Algorithms}}, {{Operators}}, and {{Simulators}}},
60
+ author = {Balakrishnan, Anand and Deshmukh, Jyotirmoy V.},
61
+ year = 2024,
62
+ month = jun,
63
+ url = {https://openreview.net/forum?id=k2hIQYqHTh},
64
+ copyright = {All rights reserved},
65
+ langid = {english}
66
+ }
67
+
68
+ - For weighted automata in motion planning:
69
+
70
+ .. code-block:: bibtex
71
+
72
+ @inproceedings{balakrishnan2024motion,
73
+ title = {Motion {{Planning}} for {{Automata-based Objectives}} Using {{Efficient Gradient-based Methods}}},
74
+ booktitle = {2024 {{IEEE}}/{{RSJ International Conference}} on {{Intelligent Robots}} and {{Systems}} ({{IROS}})},
75
+ author = {Balakrishnan, Anand and Atasever, Merve and Deshmukh, Jyotirmoy V.},
76
+ year = 2024,
77
+ month = oct,
78
+ pages = {13734--13740},
79
+ issn = {2153-0866},
80
+ doi = {10.1109/IROS58592.2024.10802177}
81
+ }
82
+
83
+ - For alternating weighted automata in multi-agent systems:
84
+
85
+ .. code-block:: bibtex
86
+
87
+ @inproceedings{balakrishnan2025monitoring,
88
+ title = {Monitoring {{Spatially Distributed Cyber-Physical Systems}} with {{Alternating Finite Automata}}},
89
+ booktitle = {Proceedings of the 28th {{ACM International Conference}} on {{Hybrid Systems}}: {{Computation}} and {{Control}}},
90
+ author = {Balakrishnan, Anand and Paul, Sheryl and Silvetti, Simone and Nenzi, Laura and Deshmukh, Jyotirmoy V.},
91
+ year = 2025,
92
+ month = may,
93
+ pages = {1--11},
94
+ publisher = {ACM},
95
+ address = {Irvine CA USA},
96
+ doi = {10.1145/3716863.3718033},
97
+ isbn = {979-8-4007-1504-4},
98
+ langid = {english}
99
+ }
100
+
101
+ License
102
+ -------
103
+
104
+ See the ``LICENSE`` file for details.
@@ -0,0 +1,32 @@
1
+ """pytest fixtures for the morphata bench suite.
2
+
3
+ The bench/ tree is opt-in: invoke with `pytest bench/` (or
4
+ `pytest bench/test_sere_nlm_perf.py`). It is not collected by the
5
+ default `pytest` invocation.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import gc
11
+ from collections.abc import Iterator
12
+
13
+ import pytest
14
+
15
+ from morphata.symbolic.bdd import BDDManager
16
+
17
+
18
+ @pytest.fixture
19
+ def bench_mgr() -> Iterator[BDDManager[str, int]]:
20
+ """Shared BDD manager scoped to one bench test (= many benchmark rounds).
21
+
22
+ Bench tests pass this manager to ``sere_to_automaton(..., manager=mgr)``
23
+ so the per-call ``BDDManager()`` allocation is skipped. That path leaks
24
+ ~12 MB of CUDD-internal state per call, which compounds across the
25
+ ~100-1000 rounds pytest-benchmark runs per test and exhausts host
26
+ RAM. Sharing the manager keeps RSS flat.
27
+ """
28
+ manager = BDDManager[str, int]()
29
+ try:
30
+ yield manager
31
+ finally:
32
+ _ = gc.collect()
@@ -2,8 +2,8 @@
2
2
 
3
3
  The case: an LTLf min-DFA whose AP universe is large (|aps| ~ 19) but
4
4
  whose guards mention only 4 APs. Pre-rewrite this regime explodes;
5
- post-rewrite the per-pair (language_equivalent) and per-automaton
6
- (isomorphic) support reductions keep work bounded.
5
+ post-rewrite the per-pair (``is_language_equivalent``) support reduction
6
+ keeps work bounded.
7
7
 
8
8
  pytest-benchmark records the timing distribution; no hard-coded
9
9
  thresholds, the dataset is the regression signal.
@@ -13,18 +13,18 @@ Run with: ``pytest bench/test_equivalence_perf.py -v``
13
13
 
14
14
  from __future__ import annotations
15
15
 
16
- import logic_asts.ltl as ltl
16
+ from collections.abc import Set as AbstractSet
17
+
18
+ import pytest
19
+ from logic_asts import ltl
17
20
  from pytest_benchmark.fixture import BenchmarkFixture as Benchmark
18
21
 
19
22
  from morphata.alphabet import powerset_alphabet
20
- from morphata.automaton import Automaton
21
- from morphata.logic.ltl import Input, ltl_to_automaton
22
- from morphata.symbolic import isomorphic
23
23
  from morphata.symbolic.automaton import SymbolicAutomaton
24
- from morphata.symbolic.equivalence import language_equivalent
24
+ from morphata.symbolic.utils import is_language_equivalent
25
25
 
26
26
 
27
- def _sparse_big_alphabet_dfa() -> SymbolicAutomaton[str]:
27
+ def _sparse_big_alphabet_dfa() -> SymbolicAutomaton[str, AbstractSet[str]]:
28
28
  aps = frozenset({f"p{i}" for i in range(15)} | {"a", "b", "c", "d"})
29
29
  formula = ltl.And(
30
30
  (
@@ -35,23 +35,15 @@ def _sparse_big_alphabet_dfa() -> SymbolicAutomaton[str]:
35
35
  )
36
36
  )
37
37
  alphabet = powerset_alphabet(aps)
38
- aut: Automaton[int, Input[str], str] = ltl_to_automaton(formula, finite=True)
39
- return SymbolicAutomaton.from_automaton(aut, alphabet=alphabet).to_min_dfa()
38
+ aut = SymbolicAutomaton.from_specification(formula, alphabet=alphabet, finite=True)
39
+ return aut.to_min_dfa()
40
40
 
41
41
 
42
+ @pytest.mark.limit_memory("2 GB")
42
43
  def test_language_equivalent_sparse_big_alphabet(benchmark: Benchmark) -> None:
43
44
  dfa = _sparse_big_alphabet_dfa()
44
45
 
45
46
  def _run() -> bool:
46
- return language_equivalent(dfa, dfa)
47
-
48
- assert benchmark(_run)
49
-
50
-
51
- def test_isomorphic_sparse_big_alphabet(benchmark: Benchmark) -> None:
52
- dfa = _sparse_big_alphabet_dfa()
53
-
54
- def _run() -> bool:
55
- return isomorphic(dfa, dfa)
47
+ return is_language_equivalent(dfa, dfa)
56
48
 
57
49
  assert benchmark(_run)