morphata 2.1.0__tar.gz → 2.2.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 (200) hide show
  1. {morphata-2.1.0 → morphata-2.2.0}/.envrc +2 -2
  2. {morphata-2.1.0 → morphata-2.2.0}/.rstcheck.cfg +2 -0
  3. morphata-2.2.0/AGENTS.md +40 -0
  4. {morphata-2.1.0 → morphata-2.2.0}/CHANGELOG.md +122 -44
  5. morphata-2.2.0/PKG-INFO +134 -0
  6. morphata-2.2.0/README.rst +104 -0
  7. morphata-2.2.0/bench/conftest.py +32 -0
  8. {morphata-2.1.0 → morphata-2.2.0}/bench/test_equivalence_perf.py +11 -19
  9. {morphata-2.1.0 → morphata-2.2.0}/bench/test_hoa_exporter_perf.py +48 -37
  10. {morphata-2.1.0 → morphata-2.2.0}/bench/test_hoa_parser_perf.py +7 -5
  11. morphata-2.2.0/bench/test_sere_nlm_perf.py +107 -0
  12. morphata-2.2.0/bench/test_transforms_perf.py +120 -0
  13. morphata-2.2.0/docs/Makefile +20 -0
  14. {morphata-2.1.0 → morphata-2.2.0}/docs/api/modules.rst +6 -11
  15. morphata-2.2.0/docs/api/morphata.acc_expr.rst +6 -0
  16. morphata-2.2.0/docs/api/morphata.acceptance.rst +6 -0
  17. morphata-2.2.0/docs/api/morphata.alphabet.rst +6 -0
  18. morphata-2.2.0/docs/api/morphata.builder.rst +6 -0
  19. morphata-2.2.0/docs/api/morphata.hoa.rst +43 -0
  20. morphata-2.2.0/docs/api/morphata.logic.rst +11 -0
  21. morphata-2.2.0/docs/api/morphata.operators.rst +12 -0
  22. morphata-2.2.0/docs/api/morphata.symbolic.rst +38 -0
  23. morphata-2.2.0/docs/changelog.md +2 -0
  24. {morphata-2.1.0 → morphata-2.2.0}/docs/concepts/acceptance.rst +26 -13
  25. morphata-2.2.0/docs/concepts/hoa.rst +138 -0
  26. {morphata-2.1.0 → morphata-2.2.0}/docs/concepts/index.rst +3 -1
  27. morphata-2.2.0/docs/concepts/operators.rst +229 -0
  28. morphata-2.2.0/docs/concepts/symbolic.rst +99 -0
  29. morphata-2.2.0/docs/concepts/weights.rst +150 -0
  30. morphata-2.2.0/docs/conf.py +186 -0
  31. morphata-2.2.0/docs/index.rst +135 -0
  32. morphata-2.2.0/docs/quick-start.rst +221 -0
  33. {morphata-2.1.0 → morphata-2.2.0}/justfile +1 -0
  34. {morphata-2.1.0 → morphata-2.2.0}/pixi.lock +3098 -989
  35. {morphata-2.1.0 → morphata-2.2.0}/pixi.toml +10 -7
  36. {morphata-2.1.0 → morphata-2.2.0}/pyproject.toml +3 -5
  37. {morphata-2.1.0 → morphata-2.2.0}/pytest.toml +2 -1
  38. morphata-2.2.0/rules/no-derivative-multiarg.yml +11 -0
  39. morphata-2.2.0/rules/no-typing-as-ty.yml +8 -0
  40. morphata-2.2.0/sgconfig.yml +2 -0
  41. morphata-2.2.0/src/morphata/__init__.py +21 -0
  42. {morphata-2.1.0/src/morphata/hoa → morphata-2.2.0/src/morphata}/acc_expr.py +39 -8
  43. morphata-2.2.0/src/morphata/acceptance.py +1174 -0
  44. morphata-2.2.0/src/morphata/alphabet.py +92 -0
  45. morphata-2.2.0/src/morphata/builder.py +190 -0
  46. {morphata-2.1.0 → morphata-2.2.0}/src/morphata/hoa/__init__.py +4 -14
  47. {morphata-2.1.0 → morphata-2.2.0}/src/morphata/hoa/parser.py +102 -2
  48. morphata-2.2.0/src/morphata/logic/__init__.py +14 -0
  49. morphata-2.2.0/src/morphata/logic/__init__.pyi +25 -0
  50. morphata-2.2.0/src/morphata/logic/_common.py +383 -0
  51. morphata-2.2.0/src/morphata/logic/ltl.py +109 -0
  52. morphata-2.2.0/src/morphata/logic/psl.py +75 -0
  53. morphata-2.2.0/src/morphata/logic/sere.py +201 -0
  54. morphata-2.2.0/src/morphata/operators/matrix.py +326 -0
  55. morphata-2.2.0/src/morphata/operators/polynomial.py +438 -0
  56. morphata-2.2.0/src/morphata/symbolic/__init__.py +14 -0
  57. morphata-2.2.0/src/morphata/symbolic/automaton.py +811 -0
  58. morphata-2.2.0/src/morphata/symbolic/bdd.py +1162 -0
  59. morphata-2.2.0/src/morphata/symbolic/utils.py +718 -0
  60. morphata-2.2.0/src/morphata/utils.py +200 -0
  61. {morphata-2.1.0 → morphata-2.2.0}/tests/conftest.py +43 -29
  62. morphata-2.2.0/tests/hoa/test_exporter.py +230 -0
  63. {morphata-2.1.0 → morphata-2.2.0}/tests/logic/test_ltl.py +76 -159
  64. morphata-2.2.0/tests/logic/test_psl.py +176 -0
  65. morphata-2.2.0/tests/logic/test_sere.py +381 -0
  66. morphata-2.2.0/tests/operators/test_matrix.py +328 -0
  67. morphata-2.1.0/tests/operators/test_polynomial_from_ir.py → morphata-2.2.0/tests/operators/test_polynomial.py +51 -34
  68. morphata-2.2.0/tests/symbolic/test_bdd_helpers.py +652 -0
  69. morphata-2.2.0/tests/symbolic/test_compose.py +177 -0
  70. {morphata-2.1.0 → morphata-2.2.0}/tests/symbolic/test_from_hoaf.py +31 -3
  71. morphata-2.2.0/tests/symbolic/test_language_equivalence.py +156 -0
  72. {morphata-2.1.0 → morphata-2.2.0}/tests/symbolic/test_ltlf2dfa_equivalence.py +35 -37
  73. morphata-2.2.0/tests/symbolic/test_symbolic_automaton.py +385 -0
  74. morphata-2.2.0/tests/symbolic/test_to_hoa.py +97 -0
  75. morphata-2.2.0/tests/symbolic/test_transforms.py +530 -0
  76. morphata-2.2.0/tests/test_acceptance_validation.py +79 -0
  77. morphata-2.2.0/tests/test_alphabet.py +50 -0
  78. morphata-2.2.0/tests/test_builder.py +138 -0
  79. morphata-2.2.0/uv.lock +3358 -0
  80. morphata-2.1.0/AGENTS.md +0 -421
  81. morphata-2.1.0/PKG-INFO +0 -138
  82. morphata-2.1.0/README.md +0 -106
  83. morphata-2.1.0/bench/conftest.py +0 -55
  84. morphata-2.1.0/bench/test_sere_nlm_perf.py +0 -99
  85. morphata-2.1.0/bench/test_successors_for_alphabet_perf.py +0 -124
  86. morphata-2.1.0/cliff.toml +0 -59
  87. morphata-2.1.0/docs/api/morphata.acceptance.rst +0 -8
  88. morphata-2.1.0/docs/api/morphata.alphabet.rst +0 -8
  89. morphata-2.1.0/docs/api/morphata.automaton.rst +0 -11
  90. morphata-2.1.0/docs/api/morphata.builder.rst +0 -8
  91. morphata-2.1.0/docs/api/morphata.hoa.rst +0 -29
  92. morphata-2.1.0/docs/api/morphata.logic.ltl.rst +0 -8
  93. morphata-2.1.0/docs/api/morphata.logic.psl.rst +0 -15
  94. morphata-2.1.0/docs/api/morphata.logic.sere.rst +0 -9
  95. morphata-2.1.0/docs/api/morphata.logic.strel.rst +0 -8
  96. morphata-2.1.0/docs/api/morphata.operators.matrix.rst +0 -8
  97. morphata-2.1.0/docs/api/morphata.operators.polynomial.rst +0 -9
  98. morphata-2.1.0/docs/api/morphata.operators.rst +0 -9
  99. morphata-2.1.0/docs/api/morphata.symbolic.rst +0 -48
  100. morphata-2.1.0/docs/concepts/hoa.rst +0 -119
  101. morphata-2.1.0/docs/concepts/operators.rst +0 -184
  102. morphata-2.1.0/docs/concepts/spec.rst +0 -133
  103. morphata-2.1.0/docs/concepts/weights.rst +0 -167
  104. morphata-2.1.0/docs/conf.py +0 -71
  105. morphata-2.1.0/docs/index.rst +0 -139
  106. morphata-2.1.0/docs/quick-start.rst +0 -139
  107. morphata-2.1.0/docs/symbolic-transforms.rst +0 -208
  108. morphata-2.1.0/src/morphata/__init__.py +0 -29
  109. morphata-2.1.0/src/morphata/acceptance.py +0 -565
  110. morphata-2.1.0/src/morphata/alphabet.py +0 -178
  111. morphata-2.1.0/src/morphata/automaton.py +0 -345
  112. morphata-2.1.0/src/morphata/builder.py +0 -159
  113. morphata-2.1.0/src/morphata/hoa/exporter.py +0 -797
  114. morphata-2.1.0/src/morphata/logic/__init__.py +0 -15
  115. morphata-2.1.0/src/morphata/logic/_common.py +0 -325
  116. morphata-2.1.0/src/morphata/logic/ltl.py +0 -268
  117. morphata-2.1.0/src/morphata/logic/psl.py +0 -319
  118. morphata-2.1.0/src/morphata/logic/sere.py +0 -436
  119. morphata-2.1.0/src/morphata/logic/strel.py +0 -488
  120. morphata-2.1.0/src/morphata/operators/matrix.py +0 -308
  121. morphata-2.1.0/src/morphata/operators/polynomial.py +0 -357
  122. morphata-2.1.0/src/morphata/symbolic/__init__.py +0 -57
  123. morphata-2.1.0/src/morphata/symbolic/automaton.py +0 -623
  124. morphata-2.1.0/src/morphata/symbolic/bdd.py +0 -941
  125. morphata-2.1.0/src/morphata/symbolic/compose.py +0 -506
  126. morphata-2.1.0/src/morphata/symbolic/equivalence.py +0 -199
  127. morphata-2.1.0/src/morphata/symbolic/persistence.py +0 -470
  128. morphata-2.1.0/src/morphata/symbolic/transforms.py +0 -861
  129. morphata-2.1.0/src/morphata/utils.py +0 -74
  130. morphata-2.1.0/tests/hoa/test_exporter_basic.py +0 -125
  131. morphata-2.1.0/tests/hoa/test_exporter_roundtrip.py +0 -43
  132. morphata-2.1.0/tests/hoa/test_exporter_streaming.py +0 -310
  133. morphata-2.1.0/tests/logic/test_psl.py +0 -196
  134. morphata-2.1.0/tests/logic/test_sere.py +0 -547
  135. morphata-2.1.0/tests/logic/test_sere_properties.py +0 -152
  136. morphata-2.1.0/tests/logic/test_strel.py +0 -316
  137. morphata-2.1.0/tests/operators/test_matrix_from_ir.py +0 -249
  138. morphata-2.1.0/tests/operators/test_matrix_from_ltl.py +0 -68
  139. morphata-2.1.0/tests/symbolic/test_alphabet_aware_transitions.py +0 -174
  140. morphata-2.1.0/tests/symbolic/test_bdd_helpers.py +0 -318
  141. morphata-2.1.0/tests/symbolic/test_compose.py +0 -449
  142. morphata-2.1.0/tests/symbolic/test_composition_map.py +0 -312
  143. morphata-2.1.0/tests/symbolic/test_equivalence_naive_property.py +0 -138
  144. morphata-2.1.0/tests/symbolic/test_persistence.py +0 -308
  145. morphata-2.1.0/tests/symbolic/test_prune.py +0 -280
  146. morphata-2.1.0/tests/symbolic/test_step.py +0 -77
  147. morphata-2.1.0/tests/symbolic/test_symbolic_automaton.py +0 -459
  148. morphata-2.1.0/tests/symbolic/test_symbolic_from_ir.py +0 -55
  149. morphata-2.1.0/tests/symbolic/test_to_hoa.py +0 -176
  150. morphata-2.1.0/tests/symbolic/test_transforms.py +0 -451
  151. morphata-2.1.0/tests/symbolic/test_transforms_ir.py +0 -41
  152. morphata-2.1.0/tests/symbolic/test_transforms_properties.py +0 -140
  153. morphata-2.1.0/tests/test_acceptance_validation.py +0 -45
  154. morphata-2.1.0/tests/test_alphabet.py +0 -205
  155. morphata-2.1.0/tests/test_automaton.py +0 -242
  156. morphata-2.1.0/tests/test_builder.py +0 -124
  157. morphata-2.1.0/tests/test_from_parsed.py +0 -101
  158. morphata-2.1.0/uv.lock +0 -3331
  159. {morphata-2.1.0 → morphata-2.2.0}/.basedpyright/baseline.json +0 -0
  160. {morphata-2.1.0 → morphata-2.2.0}/.dockerignore +0 -0
  161. {morphata-2.1.0 → morphata-2.2.0}/.editorconfig +0 -0
  162. {morphata-2.1.0 → morphata-2.2.0}/.envrc.recommended +0 -0
  163. {morphata-2.1.0 → morphata-2.2.0}/.fdignore +0 -0
  164. {morphata-2.1.0 → morphata-2.2.0}/.gitattributes +0 -0
  165. {morphata-2.1.0 → morphata-2.2.0}/.gitignore +0 -0
  166. {morphata-2.1.0 → morphata-2.2.0}/.mailmap +0 -0
  167. {morphata-2.1.0 → morphata-2.2.0}/.python-version +0 -0
  168. {morphata-2.1.0 → morphata-2.2.0}/CLAUDE.md +0 -0
  169. {morphata-2.1.0 → morphata-2.2.0}/CONTRIBUTING.md +0 -0
  170. {morphata-2.1.0 → morphata-2.2.0}/LICENSE +0 -0
  171. {morphata-2.1.0 → morphata-2.2.0}/bench/README.md +0 -0
  172. {morphata-2.1.0 → morphata-2.2.0}/bench/__init__.py +0 -0
  173. {morphata-2.1.0 → morphata-2.2.0}/ci/make-release +0 -0
  174. {morphata-2.1.0 → morphata-2.2.0}/docs/_static/navigation.html +0 -0
  175. {morphata-2.1.0 → morphata-2.2.0}/mypy.ini +0 -0
  176. {morphata-2.1.0 → morphata-2.2.0}/project.utf-8.add +0 -0
  177. {morphata-2.1.0 → morphata-2.2.0}/project.utf-8.add.spl +0 -0
  178. {morphata-2.1.0 → morphata-2.2.0}/pyrightconfig.json +0 -0
  179. {morphata-2.1.0 → morphata-2.2.0}/src/morphata/hoa/__main__.py +0 -0
  180. {morphata-2.1.0 → morphata-2.2.0}/src/morphata/hoa/hoa.lark +0 -0
  181. {morphata-2.1.0 → morphata-2.2.0}/src/morphata/operators/__init__.py +0 -0
  182. {morphata-2.1.0 → morphata-2.2.0}/src/morphata/operators/_backend.py +0 -0
  183. {morphata-2.1.0 → morphata-2.2.0}/src/morphata/py.typed +0 -0
  184. {morphata-2.1.0 → morphata-2.2.0}/tests/hoa/aut1.hoa +0 -0
  185. {morphata-2.1.0 → morphata-2.2.0}/tests/hoa/aut11.hoa +0 -0
  186. {morphata-2.1.0 → morphata-2.2.0}/tests/hoa/aut2.hoa +0 -0
  187. {morphata-2.1.0 → morphata-2.2.0}/tests/hoa/aut3.2.hoa +0 -0
  188. {morphata-2.1.0 → morphata-2.2.0}/tests/hoa/aut3.hoa +0 -0
  189. {morphata-2.1.0 → morphata-2.2.0}/tests/hoa/aut4.hoa +0 -0
  190. {morphata-2.1.0 → morphata-2.2.0}/tests/hoa/aut5.hoa +0 -0
  191. {morphata-2.1.0 → morphata-2.2.0}/tests/hoa/aut6.hoa +0 -0
  192. {morphata-2.1.0 → morphata-2.2.0}/tests/hoa/aut7.hoa +0 -0
  193. {morphata-2.1.0 → morphata-2.2.0}/tests/hoa/aut8.hoa +0 -0
  194. {morphata-2.1.0 → morphata-2.2.0}/tests/hoa/finite_aut.hoa +0 -0
  195. {morphata-2.1.0 → morphata-2.2.0}/tests/hoa/gen_buchi_3.hoa +0 -0
  196. {morphata-2.1.0 → morphata-2.2.0}/tests/hoa/gen_cobuchi_2.hoa +0 -0
  197. {morphata-2.1.0 → morphata-2.2.0}/tests/hoa/rabin_2pair.hoa +0 -0
  198. {morphata-2.1.0 → morphata-2.2.0}/tests/hoa/streett_2pair.hoa +0 -0
  199. {morphata-2.1.0 → morphata-2.2.0}/tests/hoa/test_parser.py +0 -0
  200. {morphata-2.1.0 → morphata-2.2.0}/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 "$@"
@@ -23,4 +23,4 @@ export PIXI_ENV
23
23
  dotenv_if_exists
24
24
 
25
25
  watch_file pixi.lock
26
- eval "$(pixi shell-hook -e "$PIXI_ENV")"
26
+ eval "$(pixi shell-hook --as-is -e "$PIXI_ENV")"
@@ -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,124 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.2.0 — 2026-08-19
4
+
5
+ This is a major release as it cleans up a lot of bad code,
6
+ attempts to make the design choices more consistent,
7
+ and increase the surface area of properly typed code.
8
+
9
+ ### Added
10
+
11
+ - Add finite-word composition directly to `SymbolicAutomaton`: `&`, `|`, `^`,
12
+ and `~` implement intersection, union, symmetric difference, and complement.
13
+ The variadic `compose_automata`, `is_language_equivalent`,
14
+ and `forward_bisim_reduction` helpers cover composition, equivalence checking,
15
+ and language-preserving quotienting without leaving the symbolic representation.
16
+ - Add a typed `BDDManager`/`Function` API and symbolic utilities for replacing a
17
+ manager, reindexing or quotienting states, materializing sinks, traversing reachable
18
+ states, and constructing universal acceptors and rejectors.
19
+ - Support arbitrary HOA v1 acceptance expressions through `GenericCondition`,
20
+ in addition to the concrete finite, Büchi, co-Büchi, generalized Büchi/co-Büchi,
21
+ Rabin, and Streett conditions.
22
+ Omega acceptance remains structural only:
23
+ run evaluation and transforms still implement finite-word semantics.
24
+
25
+ ### Changed
26
+
27
+ - **The primary automaton API is now `SymbolicAutomaton`.**
28
+ The intermediate `morphata.automaton.Automaton` representation has been removed.
29
+ Logic conversion, hand construction, transforms, composition, HOA conversion,
30
+ and operators now work directly with the BDD-backed type.
31
+ Migrate logic calls to `formula_to_automaton(...)`
32
+ or `SymbolicAutomaton.from_specification(...)`,
33
+ and use the instance methods `remove_alternation()`, `determinize()`,
34
+ and `to_min_dfa()` instead of the old transform functions.
35
+ - `AutomatonBuilder` now accepts integer state identifiers
36
+ and `build()` returns a `SymbolicAutomaton`; pass a `labeling_fn`
37
+ (and optionally a shared `BDDManager`).
38
+ Set-valued successors are disjunctions;
39
+ use a Boolean state expression for an alternating conjunction.
40
+ The old `variant`/`DeltaKind` construction API is gone.
41
+ - Alphabets now separate symbol labeling from guard weighting.
42
+ `BooleanAlphabet.labeling` returns an `AP -> bool` mapping, while operator weight
43
+ functions receive guards in sum-of-products form. `Alphabet`, `WeightedAlphabet`,
44
+ and `weighted_alphabet` are replaced by `BooleanAlphabet`, `SemiringAlphabet`,
45
+ `powerset_alphabet`, and `finite_alphabet`.
46
+ - `MatrixOperator` and `PolynomialOperator` are constructed with
47
+ `from_symbolic_automaton` or `from_specification`.
48
+ Pass `weight_fn` directly rather than an alphabet;
49
+ matrix construction also requires `backend`.
50
+ Their `accepts()` methods return a scalar `AlgebraicArray`,
51
+ and transition generation is exposed as `op.delta(symbol)`.
52
+ - HOA conversion now goes through `ParsedAutomaton`:
53
+ use `SymbolicAutomaton.from_hoaf(parsed, alphabet=...)` and `str(aut.to_hoaf())`.
54
+ State IDs need not be dense, and states without outgoing edges survive round trips.
55
+
56
+ ### Fixed
57
+
58
+ - Correct symbolic derivatives for negated variables and SERE nodes.
59
+ - Fix breadth-first traversal ordering, symbolic language equivalence, and transition
60
+ partitioning when multiple guards have the same residual.
61
+ - Correct `step()` labeling and the structural checks for deterministic and
62
+ forward-progressing automata.
63
+ - Make builders, HOA conversion, and both operators handle sparse external state IDs
64
+ consistently by using dense indices only internally.
65
+
66
+ ### Removed
67
+
68
+ - Remove the old `morphata.automaton` IR, STREL translator,
69
+ BDD-JSON persistence/cache API, and the former `symbolic.transforms`,
70
+ `symbolic.equivalence`, and `symbolic.compose` modules.
71
+ Their surviving operations now live on `SymbolicAutomaton` or in `morphata.symbolic`;
72
+ there is no replacement for STREL or BDD-JSON persistence in this release.
73
+ - Remove the specialized composition-map/transition-enumeration APIs and structural
74
+ `prune`; use general symbolic composition and `forward_bisim_reduction` where those
75
+ semantics fit.
76
+
3
77
  ## 2.1.0 — 2026-06-11
4
78
 
5
79
  ### Added
6
80
 
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.
81
+ - Add `morphata.symbolic.prune` that does a cheap,
82
+ language-preserving structural pruning of an AFA.
83
+ The idea here is to try and reduce the number of states in the AFA
84
+ (no guarantees)
85
+ by performing a forward reachability pass, dead-state pruning,
86
+ and a parameterized number of passes for one-step acceptance-partition deduplication.
11
87
 
12
88
  ### Changed
13
89
 
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`
90
+ - `MatrixOperator` and `PolynomialOperator` are generic now —
91
+ `MatrixOperator[Symbol, AP]` and `PolynomialOperator[Symbol, AP]`,
92
+ just like `SymbolicAutomaton`.
93
+ When I first banged these out I left them un-parameterized
94
+ (read: `Any` smeared everywhere),
95
+ so now the symbol and AP types actually flow through
96
+ and `MatrixOperator[frozenset[str], str]` means something.
97
+ Nothing changes at runtime;
98
+ the one thing you might notice is your type-checker nudging you to fill in the params
99
+ if you were annotating a bare `MatrixOperator` or `PolynomialOperator`.
100
+ - The BDD-JSON sidecar cache jumped to schema **v2**.
101
+ Acceptance conditions now (de)serialize through `cattrs` instead of the hand-rolled
102
+ `meta.get(...)` dance, and Rabin/Streett pairs are written as objects
103
+ (`{"rejecting": ..., "accepting": ...}`) rather than nested lists.
104
+ Heads up: old `v1` `.bdd.json` caches will politely refuse to load
105
+ (clean error, not a silent miscompile) --- just regenerate them and you're good.
106
+ Remember, this is a *cache*, not a portable format,
107
+ so this shouldn't bite anyone hard.
108
+ - STREL conversion shed ~120 lines.
109
+ All the timed-interval unrolling
110
+ (`G[a,b]`, `F[a,b]`, `U[t1,t2]`, `X[n]`)
111
+ and the spatial `Somewhere`/`Everywhere` desugaring used to be hand-rolled in
112
+ `strel.py`, but `logic-asts` already does all of it in `.expand()` — so I ripped them
113
+ out and just lean on the upstream rules.
114
+ This needs the newer `logic-asts`
31
115
  (the one with the spatial `expand()` rules), so that dep gets bumped along with it.
32
116
 
33
117
  ### Miscellaneous
34
118
 
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.
119
+ - Housekeeping, but worth saying:
120
+ the whole package now type-checks clean under *both* `zuban` and `basedpyright`
121
+ (0 errors, 0 warnings), so contributing should be a bit less whack-a-mole.
38
122
 
39
123
  ## 2.0.10 — 2026-06-08
40
124
 
@@ -46,8 +130,8 @@
46
130
 
47
131
  ### Performance
48
132
 
49
- - The HOA grammar is now LALR compatible! This means we get a huge performance boost
50
- even for pretty large automata.
133
+ - The HOA grammar is now LALR compatible!
134
+ This means we get a huge performance boost even for pretty large automata.
51
135
  - Similar thing was done in `logic-asts` and thus, the version will be bumped.
52
136
 
53
137
  ## 2.0.9 — 2026-06-04
@@ -123,7 +207,7 @@
123
207
  ### Fixed
124
208
 
125
209
  - A major regression where the streaming `to_hoa` stuff changed the label enumeration
126
- semantics to be _minterms_ instead of _cubes_.
210
+ semantics to be *minterms* instead of *cubes*.
127
211
  This is now fixed.
128
212
 
129
213
  ### Added
@@ -344,8 +428,7 @@ which will be summarized below.
344
428
  The reverse direction is deliberately omitted —
345
429
  recovering Boolean semantics from arbitrary `V` needs an ordering and a cut (cf.
346
430
  STL), and pretending otherwise would silently produce garbage.
347
- - **Ergonomic construction.**
348
- `AutomatonBuilder` replaces the old NFA builder.
431
+ - **Ergonomic construction.** `AutomatonBuilder` replaces the old NFA builder.
349
432
  Add states, add transitions with a guard and a successor
350
433
  (single state, set of states, or a raw `BoolExpr` for full alternation),
351
434
  and `build()` cleans up by merging rows that share a successor.
@@ -373,11 +456,9 @@ which will be summarized below.
373
456
  The BDD cofactor path works for any `BooleanAlphabet`,
374
457
  so you can feed a `SymbolicAutomaton` over integers, graph nodes, whatever —
375
458
  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.
459
+ - **The AFA-to-min-DFA pipeline actually exists now.** `nfa_from_afa` (dealternation),
460
+ `determinize` (subset construction with a sink), `minimize` (Hopcroft) —
461
+ all share one BDD manager and successively tighten the invariants on successor BDDs.
381
462
  `SymbolicAutomaton.to_min_dfa` chains the three.
382
463
  Finite-word only for now; omega-regular determinisation is a different beast.
383
464
  - **DFA equivalence checks.**
@@ -388,19 +469,17 @@ which will be summarized below.
388
469
 
389
470
  ### Operators
390
471
 
391
- - **One per-step shape.**
392
- `SymbolicAutomaton`, `MatrixOperator`,
472
+ - **One per-step shape.** `SymbolicAutomaton`, `MatrixOperator`,
393
473
  and `PolynomialOperator` all expose the same `step(current, symbol)` / `run(word)` /
394
474
  `accepts(word)` shape.
395
475
  Each binds its alphabet at construction
396
476
  (matching how the semiring / algebra were already bound),
397
477
  so the per-call API is monomorphic and `V` is pinned end-to-end.
398
478
  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.
479
+ - **`PolynomialOperator` evaluates lazily.** `step` evaluates guards against
480
+ `alphabet.weight` on the fly, with no per-symbol precomputation — important because
481
+ the natural LTL alphabet is `Σ = 2^AP`, and you really don't want to materialise
482
+ `2^|AP|` polynomials up front.
404
483
  The AFA's row structure stays static
405
484
  (good for JAX tracing), and the alphabet decides whether to memoise.
406
485
  - **Polynomial performance.**
@@ -410,9 +489,8 @@ which will be summarized below.
410
489
 
411
490
  ### Tooling and Tests
412
491
 
413
- - **Hand-written changelogs.**
414
- `git cliff` was producing entries that needed enough editing that I might
415
- as well write them by hand.
492
+ - **Hand-written changelogs.** `git cliff` was producing entries
493
+ that needed enough editing that I might as well write them by hand.
416
494
  `ci/make-release` no longer touches `CHANGELOG.md` or tag messages.
417
495
  Run `git cliff` separately if you want a draft to crib from.
418
496
  - **Tests.**
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: morphata
3
+ Version: 2.2.0
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: dd>=0.6.0
18
+ Requires-Dist: jaxtyping>=0.3
19
+ Requires-Dist: lark>=1.3.1
20
+ Requires-Dist: logic-asts>=1.8
21
+ Requires-Dist: numpy
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
+ from collections.abc import Set as AbstractSet
17
+
16
18
  import logic_asts.ltl as ltl
19
+ import pytest
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)