morphata 2.2.3__tar.gz → 2.3.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 (153) hide show
  1. {morphata-2.2.3 → morphata-2.3.1}/.envrc +2 -0
  2. morphata-2.3.1/.rstcheck.cfg +43 -0
  3. {morphata-2.2.3 → morphata-2.3.1}/AGENTS.md +19 -0
  4. {morphata-2.2.3 → morphata-2.3.1}/CHANGELOG.md +248 -86
  5. {morphata-2.2.3 → morphata-2.3.1}/PKG-INFO +4 -3
  6. {morphata-2.2.3 → morphata-2.3.1}/bench/README.md +6 -0
  7. {morphata-2.2.3 → morphata-2.3.1}/bench/test_distance_perf.py +19 -20
  8. {morphata-2.2.3 → morphata-2.3.1}/bench/test_transforms_perf.py +129 -10
  9. {morphata-2.2.3 → morphata-2.3.1}/docs/api/modules.rst +1 -0
  10. {morphata-2.2.3 → morphata-2.3.1}/docs/api/morphata.operators.rst +9 -0
  11. {morphata-2.2.3 → morphata-2.3.1}/docs/api/morphata.storage.rst +19 -0
  12. {morphata-2.2.3 → morphata-2.3.1}/docs/api/morphata.symbolic.rst +11 -1
  13. morphata-2.3.1/docs/api/morphata.utils.rst +61 -0
  14. morphata-2.3.1/docs/bibliography.rst +4 -0
  15. {morphata-2.2.3 → morphata-2.3.1}/docs/concepts/acceptance.rst +2 -2
  16. {morphata-2.2.3 → morphata-2.3.1}/docs/concepts/hoa.rst +3 -3
  17. {morphata-2.2.3 → morphata-2.3.1}/docs/concepts/operators.rst +25 -1
  18. {morphata-2.2.3 → morphata-2.3.1}/docs/concepts/symbolic.rst +55 -0
  19. {morphata-2.2.3 → morphata-2.3.1}/docs/conf.py +14 -4
  20. {morphata-2.2.3 → morphata-2.3.1}/docs/index.rst +4 -2
  21. {morphata-2.2.3 → morphata-2.3.1}/docs/quick-start.rst +2 -2
  22. morphata-2.3.1/docs/refs.bib +516 -0
  23. {morphata-2.2.3 → morphata-2.3.1}/pixi.lock +663 -692
  24. {morphata-2.2.3 → morphata-2.3.1}/pyproject.toml +25 -4
  25. {morphata-2.2.3 → morphata-2.3.1}/pytest.toml +2 -0
  26. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/_version.py +2 -2
  27. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/acceptance.py +1 -1
  28. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/alphabet.py +128 -17
  29. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/hoa/__init__.py +2 -0
  30. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/logic/__init__.py +8 -1
  31. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/logic/_common.py +5 -2
  32. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/operators/_backend.py +1 -1
  33. morphata-2.3.1/src/morphata/operators/jax.py +811 -0
  34. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/operators/matrix.py +12 -16
  35. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/operators/polynomial.py +3 -0
  36. morphata-2.3.1/src/morphata/storage/__init__.py +14 -0
  37. morphata-2.3.1/src/morphata/storage/run.py +204 -0
  38. morphata-2.3.1/src/morphata/storage/serialize.py +554 -0
  39. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/storage/signature.py +99 -26
  40. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/symbolic/__init__.py +13 -1
  41. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/symbolic/automaton.py +378 -12
  42. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/symbolic/bdd.py +144 -74
  43. morphata-2.3.1/src/morphata/symbolic/distance.py +1029 -0
  44. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/symbolic/transforms.py +457 -107
  45. morphata-2.3.1/src/morphata/utils/__init__.py +22 -0
  46. morphata-2.3.1/src/morphata/utils/edit_distance.py +624 -0
  47. morphata-2.3.1/src/morphata/utils/sampling.py +728 -0
  48. morphata-2.3.1/src/morphata/utils/witness.py +139 -0
  49. {morphata-2.2.3 → morphata-2.3.1}/tests/hoa/test_exporter.py +100 -52
  50. morphata-2.3.1/tests/hoa/test_parser.py +118 -0
  51. {morphata-2.2.3 → morphata-2.3.1}/tests/logic/test_ltl.py +22 -20
  52. {morphata-2.2.3 → morphata-2.3.1}/tests/logic/test_sere.py +0 -30
  53. morphata-2.3.1/tests/operators/test_jax.py +81 -0
  54. {morphata-2.2.3 → morphata-2.3.1}/tests/operators/test_matrix.py +78 -20
  55. {morphata-2.2.3 → morphata-2.3.1}/tests/operators/test_polynomial.py +9 -10
  56. morphata-2.3.1/tests/operators/test_reductions.py +116 -0
  57. {morphata-2.2.3 → morphata-2.3.1}/tests/storage/test_serialize.py +153 -7
  58. {morphata-2.2.3 → morphata-2.3.1}/tests/storage/test_signature.py +84 -2
  59. {morphata-2.2.3 → morphata-2.3.1}/tests/symbolic/test_bdd_helpers.py +16 -19
  60. morphata-2.3.1/tests/symbolic/test_distance.py +652 -0
  61. {morphata-2.2.3 → morphata-2.3.1}/tests/symbolic/test_language_equivalence.py +146 -0
  62. {morphata-2.2.3 → morphata-2.3.1}/tests/symbolic/test_ltlf2dfa_equivalence.py +19 -30
  63. morphata-2.3.1/tests/symbolic/test_rfsa.py +529 -0
  64. {morphata-2.2.3 → morphata-2.3.1}/tests/symbolic/test_symbolic_automaton.py +54 -10
  65. {morphata-2.2.3 → morphata-2.3.1}/tests/symbolic/test_transforms.py +156 -1
  66. morphata-2.3.1/tests/test_alphabet.py +98 -0
  67. morphata-2.3.1/tests/utils/test_edit_distance.py +499 -0
  68. {morphata-2.2.3 → morphata-2.3.1}/uv.lock +1031 -863
  69. morphata-2.2.3/.rstcheck.cfg +0 -13
  70. morphata-2.2.3/src/morphata/storage/__init__.py +0 -6
  71. morphata-2.2.3/src/morphata/storage/serialize.py +0 -260
  72. morphata-2.2.3/src/morphata/symbolic/distance.py +0 -361
  73. morphata-2.2.3/src/morphata/utils/__init__.py +0 -9
  74. morphata-2.2.3/tests/hoa/test_parser.py +0 -62
  75. morphata-2.2.3/tests/symbolic/test_distance.py +0 -150
  76. morphata-2.2.3/tests/test_alphabet.py +0 -58
  77. {morphata-2.2.3 → morphata-2.3.1}/.basedpyright/baseline.json +0 -0
  78. {morphata-2.2.3 → morphata-2.3.1}/.dockerignore +0 -0
  79. {morphata-2.2.3 → morphata-2.3.1}/.editorconfig +0 -0
  80. {morphata-2.2.3 → morphata-2.3.1}/.envrc.recommended +0 -0
  81. {morphata-2.2.3 → morphata-2.3.1}/.fdignore +0 -0
  82. {morphata-2.2.3 → morphata-2.3.1}/.gitattributes +0 -0
  83. {morphata-2.2.3 → morphata-2.3.1}/.gitignore +0 -0
  84. {morphata-2.2.3 → morphata-2.3.1}/.mailmap +0 -0
  85. {morphata-2.2.3 → morphata-2.3.1}/.python-version +0 -0
  86. {morphata-2.2.3 → morphata-2.3.1}/CLAUDE.md +0 -0
  87. {morphata-2.2.3 → morphata-2.3.1}/CONTRIBUTING.md +0 -0
  88. {morphata-2.2.3 → morphata-2.3.1}/LICENSE +0 -0
  89. {morphata-2.2.3 → morphata-2.3.1}/README.rst +0 -0
  90. {morphata-2.2.3 → morphata-2.3.1}/bench/__init__.py +0 -0
  91. {morphata-2.2.3 → morphata-2.3.1}/bench/conftest.py +0 -0
  92. {morphata-2.2.3 → morphata-2.3.1}/bench/test_equivalence_perf.py +0 -0
  93. {morphata-2.2.3 → morphata-2.3.1}/bench/test_hoa_exporter_perf.py +0 -0
  94. {morphata-2.2.3 → morphata-2.3.1}/bench/test_hoa_parser_perf.py +0 -0
  95. {morphata-2.2.3 → morphata-2.3.1}/bench/test_matrix_operator_perf.py +0 -0
  96. {morphata-2.2.3 → morphata-2.3.1}/bench/test_sere_nlm_perf.py +0 -0
  97. {morphata-2.2.3 → morphata-2.3.1}/ci/make-release +0 -0
  98. {morphata-2.2.3 → morphata-2.3.1}/docs/Makefile +0 -0
  99. {morphata-2.2.3 → morphata-2.3.1}/docs/_static/navigation.html +0 -0
  100. {morphata-2.2.3 → morphata-2.3.1}/docs/api/morphata.acc_expr.rst +0 -0
  101. {morphata-2.2.3 → morphata-2.3.1}/docs/api/morphata.acceptance.rst +0 -0
  102. {morphata-2.2.3 → morphata-2.3.1}/docs/api/morphata.alphabet.rst +0 -0
  103. {morphata-2.2.3 → morphata-2.3.1}/docs/api/morphata.builder.rst +0 -0
  104. {morphata-2.2.3 → morphata-2.3.1}/docs/api/morphata.hoa.rst +0 -0
  105. {morphata-2.2.3 → morphata-2.3.1}/docs/api/morphata.logic.rst +0 -0
  106. {morphata-2.2.3 → morphata-2.3.1}/docs/changelog.md +0 -0
  107. {morphata-2.2.3 → morphata-2.3.1}/docs/concepts/index.rst +0 -0
  108. {morphata-2.2.3 → morphata-2.3.1}/docs/concepts/weights.rst +0 -0
  109. {morphata-2.2.3 → morphata-2.3.1}/justfile +0 -0
  110. {morphata-2.2.3 → morphata-2.3.1}/mypy.ini +0 -0
  111. {morphata-2.2.3 → morphata-2.3.1}/project.utf-8.add +0 -0
  112. {morphata-2.2.3 → morphata-2.3.1}/project.utf-8.add.spl +0 -0
  113. {morphata-2.2.3 → morphata-2.3.1}/pyrightconfig.json +0 -0
  114. {morphata-2.2.3 → morphata-2.3.1}/rules/no-derivative-multiarg.yml +0 -0
  115. {morphata-2.2.3 → morphata-2.3.1}/rules/no-typing-as-ty.yml +0 -0
  116. {morphata-2.2.3 → morphata-2.3.1}/sgconfig.yml +0 -0
  117. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/__init__.py +0 -0
  118. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/acc_expr.py +0 -0
  119. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/builder.py +0 -0
  120. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/hoa/__main__.py +0 -0
  121. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/hoa/hoa.lark +0 -0
  122. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/hoa/parser.py +0 -0
  123. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/logic/__init__.pyi +0 -0
  124. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/logic/ltl.py +0 -0
  125. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/logic/psl.py +0 -0
  126. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/logic/sere.py +0 -0
  127. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/operators/__init__.py +0 -0
  128. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/py.typed +0 -0
  129. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/utils/expressions.py +0 -0
  130. {morphata-2.2.3 → morphata-2.3.1}/src/morphata/utils/memory.py +0 -0
  131. {morphata-2.2.3 → morphata-2.3.1}/tests/conftest.py +0 -0
  132. {morphata-2.2.3 → morphata-2.3.1}/tests/hoa/aut1.hoa +0 -0
  133. {morphata-2.2.3 → morphata-2.3.1}/tests/hoa/aut11.hoa +0 -0
  134. {morphata-2.2.3 → morphata-2.3.1}/tests/hoa/aut2.hoa +0 -0
  135. {morphata-2.2.3 → morphata-2.3.1}/tests/hoa/aut3.2.hoa +0 -0
  136. {morphata-2.2.3 → morphata-2.3.1}/tests/hoa/aut3.hoa +0 -0
  137. {morphata-2.2.3 → morphata-2.3.1}/tests/hoa/aut4.hoa +0 -0
  138. {morphata-2.2.3 → morphata-2.3.1}/tests/hoa/aut5.hoa +0 -0
  139. {morphata-2.2.3 → morphata-2.3.1}/tests/hoa/aut6.hoa +0 -0
  140. {morphata-2.2.3 → morphata-2.3.1}/tests/hoa/aut7.hoa +0 -0
  141. {morphata-2.2.3 → morphata-2.3.1}/tests/hoa/aut8.hoa +0 -0
  142. {morphata-2.2.3 → morphata-2.3.1}/tests/hoa/finite_aut.hoa +0 -0
  143. {morphata-2.2.3 → morphata-2.3.1}/tests/hoa/gen_buchi_3.hoa +0 -0
  144. {morphata-2.2.3 → morphata-2.3.1}/tests/hoa/gen_cobuchi_2.hoa +0 -0
  145. {morphata-2.2.3 → morphata-2.3.1}/tests/hoa/rabin_2pair.hoa +0 -0
  146. {morphata-2.2.3 → morphata-2.3.1}/tests/hoa/streett_2pair.hoa +0 -0
  147. {morphata-2.2.3 → morphata-2.3.1}/tests/logic/__init__.py +0 -0
  148. {morphata-2.2.3 → morphata-2.3.1}/tests/logic/test_psl.py +0 -0
  149. {morphata-2.2.3 → morphata-2.3.1}/tests/symbolic/test_compose.py +0 -0
  150. {morphata-2.2.3 → morphata-2.3.1}/tests/symbolic/test_from_hoaf.py +0 -0
  151. {morphata-2.2.3 → morphata-2.3.1}/tests/symbolic/test_to_hoa.py +0 -0
  152. {morphata-2.2.3 → morphata-2.3.1}/tests/test_acceptance_validation.py +0 -0
  153. {morphata-2.2.3 → morphata-2.3.1}/tests/test_builder.py +0 -0
@@ -21,4 +21,6 @@ fi
21
21
 
22
22
  dotenv_if_exists
23
23
 
24
+ export PIXI_FROZEN=true
25
+
24
26
  eval "$(pixi shell-hook)"
@@ -0,0 +1,43 @@
1
+ [rstcheck]
2
+ ignore_directives =
3
+ automodule,
4
+ autoclass,
5
+ autofunction,
6
+ autotype,
7
+ autodata,
8
+ autoexception,
9
+ bibliography,
10
+ footbibliography
11
+ ignore_roles =
12
+ external:py:mod,
13
+ cite,
14
+ cite:p,
15
+ cite:ps,
16
+ cite:t,
17
+ cite:ts,
18
+ cite:ct,
19
+ cite:cts,
20
+ cite:empty,
21
+ cite:alp,
22
+ cite:alps,
23
+ cite:label,
24
+ cite:labelpar,
25
+ cite:year,
26
+ cite:yearpar,
27
+ cite:author,
28
+ cite:authors,
29
+ cite:authorpar,
30
+ cite:authorpars,
31
+ cite:cauthor,
32
+ cite:cauthors,
33
+ footcite,
34
+ footcite:p,
35
+ footcite:ps,
36
+ footcite:t,
37
+ footcite:ts,
38
+ footcite:ct,
39
+ footcite:cts
40
+ report_level = WARNING
41
+
42
+
43
+ # vim: set ft=dosini
@@ -29,6 +29,13 @@ The narrow exception: a **surgical, already-decided edit** the author has spelle
29
29
  Do exactly that and nothing more.
30
30
  When unsure whether a change qualifies, treat it as out of scope and ask.
31
31
 
32
+ ## Project Boundaries
33
+
34
+ Morphata owns automata representations, algebraic operations, semiring operators,
35
+ and execution. Automata Encoders owns symbolic input features, trainable
36
+ encoders, training/evaluation tools, and comparison baselines. Morphata must
37
+ not depend on Automata Encoders.
38
+
32
39
  ## Development Environment
33
40
 
34
41
  The development shell already has the project virtual environment activated,
@@ -38,3 +45,15 @@ etc. are on `PATH` directly.
38
45
  Just invoke the tool.
39
46
  If a tool is missing, stop and tell the user rather than trying to bootstrap
40
47
  or reinstall the environment.
48
+
49
+ ## Tests
50
+
51
+ Write tests to verify the common case and edge cases with distinct failure modes.
52
+ Do not add tests solely to increase coverage. Check expected behavior against
53
+ the public contract or an independent reference; avoid tests that merely repeat
54
+ the implementation. Each test should identify a behavior or failure it checks.
55
+
56
+ ## Documentation
57
+
58
+ Use NumPy/numpydoc-style sections for Python docstrings. Use RST math markup
59
+ inside those sections and in the main docstring body.
@@ -1,5 +1,159 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.3.1 — 2026-11-11
4
+
5
+ This isn't a hefty release,
6
+ but does have some nice QoL things as well as some performance updates and bug fixes.
7
+ A large chunk of these was discovered/needed
8
+ while dogfooding my learned automata embeddings project,
9
+ so I guess they are informed by "real-world" scenarios.
10
+
11
+ ### Added
12
+
13
+ - `SymbolicAutomaton.distinguishing_words` finds the shortest words accepted by exactly
14
+ one of two automata.
15
+ You can ask for several witnesses, set a minimum length, and randomize ties.
16
+ Works with alternating automata too, without first determinizing them,
17
+ and the two automata can use different BDD managers.
18
+
19
+ - Persistent automata can now be run directly through `morphata.storage`,
20
+ without reconstructing a symbolic automaton or needing a BDD manager.
21
+ There is a `distinguishing_words` helper for these too.
22
+
23
+ - Edit distances for DFAs given as explicit transition tables,
24
+ through `DFATable` and the `dfa_*` distance functions in `morphata.utils`.
25
+ These cover unsigned and signed directed Levenshtein distances,
26
+ and minimum language separation, without BDD operations.
27
+ Helpers convert deterministic symbolic and persistent automata to tables.
28
+ The unsigned distance also accepts a `cap`,
29
+ useful when you only care how far apart the languages are up to some budget.
30
+
31
+ - `CanonicalDfaHasher.residual_digests_from_table` computes residual digests directly
32
+ from a complete DFA table.
33
+ Gives the same digests as the symbolic path with `minimize=False`,
34
+ without going through BDDs.
35
+
36
+ ### Performance
37
+
38
+ - Symbolic language repair distance now starts with small edit budgets
39
+ and increases them until it finds one that works.
40
+ Small distances no longer require building the automaton
41
+ for the full finite-repair bound before the search even starts.
42
+
43
+ - Transition partitioning does less repeated BDD work.
44
+ Transformations also retain known determinism and alternation flags,
45
+ saving checks whose answers the construction already knows.
46
+
47
+ ### Fixed
48
+
49
+ - Matrix operators built from specifications now materialize constant accepting
50
+ and rejecting sinks.
51
+ Those successors need actual states in the matrix representation too.
52
+
53
+ - BDD caches used during partitioning, determinization,
54
+ and dealternation are now cleared when their scope ends,
55
+ fixing memory leaks from retained BDD references.
56
+
57
+ - Alphabet domains are now applied when compiling specifications,
58
+ exposing transition partitions, and reversing DFA transitions.
59
+ Impossible predicate valuations no longer contribute edges in these paths.
60
+
61
+ - Automata with constant initial formulas and no states now compose correctly.
62
+ Previously, composition assumed there was at least one state to remap.
63
+
64
+ ### Documentation
65
+
66
+ - The automaton sampling helpers now have API documentation, covering random automata,
67
+ structured language families, and mutations.
68
+
69
+ ## 2.3.0 — 2026-10-21
70
+
71
+ This is *another* hefty release,
72
+ and has been the culmination of `morphata` being used to dogfood multiple other
73
+ projects --- I will not reveal the level of success of those other projects though ---
74
+ which has allowed me to harden `morphata` and also add a bunch of relevant features.
75
+
76
+ ### Added
77
+
78
+ - `BooleanAlphabet.as_guard` turns a concrete symbol into its BDD guard.
79
+
80
+ - `to_min_rfsa`, also available as `SymbolicAutomaton.to_min_rfsa`,
81
+ constructs canonical residual finite-state automata using cover-aware subset
82
+ exploration and language reversal, without building the full DFA.
83
+ Follows the same API as the other transformers.
84
+
85
+ - `SymbolicAutomaton` now has helpers for determining if its language is empty,
86
+ whether it contains the language of another automaton,
87
+ and a helper to return relational edges (for nondeterministic automata only).
88
+
89
+ - `JaxBatchedMatrixOperator` runs batches of integer-encoded words over dense matrix
90
+ operators using JAX's `lax.scan`.
91
+ You can run every word on every operator, or pair each word with one operator.
92
+ Padded words with different lengths are supported too.
93
+
94
+ - A JAX min-max Boolean algebra with a choice of native or smooth surrogate gradients.
95
+ The forward pass still computes exact min and max;
96
+ the surrogate gives the backward pass a smoother signal.
97
+
98
+ - Automaton sampling helpers in `morphata.utils.sampling`,
99
+ covering random DFAs and AFAs, structured language families, temporal specifications,
100
+ and local mutations.
101
+ Useful when you want some variety in the automata you are working with.
102
+
103
+ - `factor_initial=True` on `remove_alternation`, `determinize`,
104
+ and `to_min_dfa` transforms the components of the initial formula separately,
105
+ then combines them smallest-first.
106
+ Another option for constructions where exploring everything together gets large.
107
+
108
+ - `EditDistanceAutomaton` converts a given symbolic automaton to a min-plus weighted
109
+ automaton that computes the edit distance between a word and the given automaton.
110
+ Additionally, we have lifted this word-language edit distance to a quasimetric
111
+ that computes a directed Hausdorff distance to the `EditDistanceAutomaton` from some
112
+ other automaton.
113
+
114
+ - `unsigned_levenshtein_distance` computes the worst-case number of edits needed to
115
+ repair a source word into the target language.
116
+ It returns zero when the source language is contained in the target.
117
+ `signed_levenshtein_distance` reports a containment margin
118
+ when the source language is contained in the target,
119
+ and the same repair cost otherwise.
120
+
121
+ - `language_separation_distance` computes the minimum edit distance between any pair of
122
+ words from two languages.
123
+ It returns infinity if either is empty.
124
+
125
+ ### Changed
126
+
127
+ - Persistent automata now retain the original predicate objects.
128
+ `morphata.storage.configure_converter` lets you use your own cattrs converter
129
+ and predicate serialization hooks, with consistent ordering across backends.
130
+ The JSON helpers remain for string predicates.
131
+
132
+ - Custom alphabet domain callbacks now need to accept `care_vars`.
133
+ `BooleanAlphabet.intersect_with_domain` uses this to constrain guards using only the
134
+ predicates those guards actually mention.
135
+
136
+ ### Fixed
137
+
138
+ - DFA minimization now compares guards on the full alphabet domain,
139
+ so guards that differ only on impossible predicate valuations do not split equivalent
140
+ states.
141
+
142
+ - Symbolic transformations reuse alphabet domains within each construction stage.
143
+ Dealternation, determinization,
144
+ and RFSA subset exploration cache projected domains by the combined relation's
145
+ predicate support and apply them before partitioning.
146
+
147
+ - Canonical finite-alphabet traversal now restricts transitions before partitioning,
148
+ so impossible predicate valuations do not affect BFS state numbering.
149
+
150
+ - `compose_automata` now removes pruned states from the finite accepting set too.
151
+ Previously, accepting states could survive in the bookkeeping
152
+ after their transition rows had been removed.
153
+
154
+ - `has_conjunctive_state_terms` previously did not correctly handle successor states
155
+ that were just Boolean constants, i.e., unmaterialized accepting/rejecting sinks.
156
+
3
157
  ## 2.2.3 — 2026-09-22
4
158
 
5
159
  This is a hefty release, but all really cool things that I am super proud of
@@ -57,8 +211,8 @@ so I will just be making this a patch release
57
211
  which keeps finite-alphabet guards compact without changing their meaning.
58
212
 
59
213
  - Symbolic automaton transformations accept an optional initial state formula,
60
- allowing a transformation to start from a formula other than the automaton's
61
- stored initial condition without modifying the source automaton.
214
+ allowing a transformation to start from a formula other than the automaton's stored
215
+ initial condition without modifying the source automaton.
62
216
 
63
217
  - `prune_unreachable`, which removes the states unreachable from an automaton's initial
64
218
  states and, with `remove_sink_states=True`, the materialized sink states too (an
@@ -83,9 +237,9 @@ so I will just be making this a patch release
83
237
  since the manager's variable levels are not stable under reordering.
84
238
  The partition is recomputed on each call.
85
239
 
86
- - A `make_sink_states` keyword on `remove_alternation`, `determinize`, and
87
- `minimize_dfa`, controlling whether constant `True` and `False` successors are
88
- materialized as indexed sink states.
240
+ - A `make_sink_states` keyword on `remove_alternation`, `determinize`,
241
+ and `minimize_dfa`, controlling whether constant `True`
242
+ and `False` successors are materialized as indexed sink states.
89
243
 
90
244
  - A `reverse_language` utility that produces an automaton
91
245
  that accepts the reversed language.
@@ -111,12 +265,12 @@ so I will just be making this a patch release
111
265
  - Add a signed version of the Levenshtein distance between two nondeterministic finite
112
266
  automata.
113
267
 
114
- - `SemiringAlphabet` now has two optional fields that allows one to enumerate all
115
- symbols in the alphabet (not just predicates), and test if some symbol is contained in
116
- the alphabet.
268
+ - `SemiringAlphabet` now has two optional fields
269
+ that allows one to enumerate all symbols in the alphabet
270
+ (not just predicates), and test if some symbol is contained in the alphabet.
117
271
 
118
- - In addition, `BooleanAlphabet` now have a mandatory `domain` keyword argument which
119
- constrains the universe of allowed predicate valuations as a BDD.
272
+ - In addition, `BooleanAlphabet` now have a mandatory `domain` keyword argument
273
+ which constrains the universe of allowed predicate valuations as a BDD.
120
274
 
121
275
  - `morphata.storage` has means to store canonical representations (morphata-specific) of
122
276
  `SymbolicAutomaton` to disk.
@@ -139,9 +293,11 @@ so I will just be making this a patch release
139
293
 
140
294
  ### Developer Experience
141
295
 
142
- - The Pixi environments now describe CPU, CUDA 12, and CUDA 13 targets with virtual
143
- packages, so dependency resolution does not have to guess which machine it is
144
- eventually going to meet. `sphinx-autobuild` also joins the development toolbox
296
+ - The Pixi environments now describe CPU, CUDA 12,
297
+ and CUDA 13 targets with virtual packages,
298
+ so dependency resolution does not have to guess
299
+ which machine it is eventually going to meet.
300
+ `sphinx-autobuild` also joins the development toolbox
145
301
  for faster documentation tinkering.
146
302
 
147
303
  ### Fixed
@@ -211,9 +367,9 @@ and increase the surface area of properly typed code.
211
367
  The variadic `compose_automata`, `is_language_equivalent`,
212
368
  and `forward_bisim_reduction` helpers cover composition, equivalence checking,
213
369
  and language-preserving quotienting without leaving the symbolic representation.
214
- - Add a typed `BDDManager`/`Function` API and symbolic utilities for replacing a
215
- manager, reindexing or quotienting states, materializing sinks, traversing reachable
216
- states, and constructing universal acceptors and rejectors.
370
+ - Add a typed `BDDManager`/`Function` API and symbolic utilities
371
+ for replacing a manager, reindexing or quotienting states, materializing sinks,
372
+ traversing reachable states, and constructing universal acceptors and rejectors.
217
373
  - Support arbitrary HOA v1 acceptance expressions through `GenericCondition`,
218
374
  in addition to the concrete finite, Büchi, co-Büchi, generalized Büchi/co-Büchi,
219
375
  Rabin, and Streett conditions.
@@ -237,8 +393,9 @@ and increase the surface area of properly typed code.
237
393
  use a Boolean state expression for an alternating conjunction.
238
394
  The old `variant`/`DeltaKind` construction API is gone.
239
395
  - Alphabets now separate symbol labeling from guard weighting.
240
- `BooleanAlphabet.labeling` returns an `AP -> bool` mapping, while operator weight
241
- functions receive guards in sum-of-products form. `Alphabet`, `WeightedAlphabet`,
396
+ `BooleanAlphabet.labeling` returns an `AP -> bool` mapping,
397
+ while operator weight functions receive guards in sum-of-products form.
398
+ `Alphabet`, `WeightedAlphabet`,
242
399
  and `weighted_alphabet` are replaced by `BooleanAlphabet`, `SemiringAlphabet`,
243
400
  `powerset_alphabet`, and `finite_alphabet`.
244
401
  - `MatrixOperator` and `PolynomialOperator` are constructed with
@@ -254,12 +411,13 @@ and increase the surface area of properly typed code.
254
411
  ### Fixed
255
412
 
256
413
  - Correct symbolic derivatives for negated variables and SERE nodes.
257
- - Fix breadth-first traversal ordering, symbolic language equivalence, and transition
258
- partitioning when multiple guards have the same residual.
259
- - Correct `step()` labeling and the structural checks for deterministic and
260
- forward-progressing automata.
261
- - Make builders, HOA conversion, and both operators handle sparse external state IDs
262
- consistently by using dense indices only internally.
414
+ - Fix breadth-first traversal ordering, symbolic language equivalence,
415
+ and transition partitioning when multiple guards have the same residual.
416
+ - Correct `step()` labeling and the structural checks for deterministic
417
+ and forward-progressing automata.
418
+ - Make builders, HOA conversion,
419
+ and both operators handle sparse external state IDs consistently by using dense
420
+ indices only internally.
263
421
 
264
422
  ### Removed
265
423
 
@@ -268,9 +426,9 @@ and increase the surface area of properly typed code.
268
426
  `symbolic.equivalence`, and `symbolic.compose` modules.
269
427
  Their surviving operations now live on `SymbolicAutomaton` or in `morphata.symbolic`;
270
428
  there is no replacement for STREL or BDD-JSON persistence in this release.
271
- - Remove the specialized composition-map/transition-enumeration APIs and structural
272
- `prune`; use general symbolic composition and `forward_bisim_reduction` where those
273
- semantics fit.
429
+ - Remove the specialized composition-map/transition-enumeration APIs
430
+ and structural `prune`; use general symbolic composition and `forward_bisim_reduction`
431
+ where those semantics fit.
274
432
 
275
433
  ## 2.1.0 — 2026-06-11
276
434
 
@@ -322,9 +480,9 @@ and increase the surface area of properly typed code.
322
480
 
323
481
  ### Fixed
324
482
 
325
- - Using `SymbolicAutomaton.to_bdd_json` and `SymbolicAutomaton.from_bdd_json` did not
326
- preserve the positive normal form of the BDDs because we were using incorrect Shannon
327
- cofactor expansion.
483
+ - Using `SymbolicAutomaton.to_bdd_json`
484
+ and `SymbolicAutomaton.from_bdd_json` did not preserve the positive normal form of the
485
+ BDDs because we were using incorrect Shannon cofactor expansion.
328
486
 
329
487
  ### Performance
330
488
 
@@ -366,10 +524,9 @@ and increase the surface area of properly typed code.
366
524
  but minimization can merge classes such
367
525
  that a representative's per-factor successor lands on a non-representative tuple,
368
526
  so the full surjection has to be there.
369
- - `iter_composed_transitions` enumerates `(state, symbol, successor)`
370
- of a composed DFA without building the product BDD, walking factor
371
- deltas directly and hitting `tuple_to_class` once per
372
- `(state, symbol)`.
527
+ - `iter_composed_transitions` enumerates `(state, symbol, successor)` of a composed DFA
528
+ without building the product BDD, walking factor deltas directly and hitting
529
+ `tuple_to_class` once per `(state, symbol)`.
373
530
 
374
531
  ### Performance
375
532
 
@@ -382,23 +539,22 @@ and increase the surface area of properly typed code.
382
539
  and per-class point dicts across same-support rows.
383
540
  Conjunction-fold DFAs get 1.3-1.6x; `F a & F b & F c & F d` q1 went ~343us -> ~175us.
384
541
  - `isomorphic` routes through a compact support-reduced fingerprint
385
- and skips the broadcast expansion. `|AP|=19` sparse case:
386
- ~333ms -> ~83ms.
542
+ and skips the broadcast expansion.
543
+ `|AP|=19` sparse case: ~333ms -> ~83ms.
387
544
 
388
545
  ### Changed
389
546
 
390
- - `SymbolicAutomaton` carries a derived `ap_inverse: Mapping[str, AP]`
391
- populated in `__post_init__`; `to_automaton` and
392
- `compose._merge_managers` route through it.
393
- - New symbolic benches are pytest-benchmark cases, so timings get
394
- recorded rather than gated on a hard-coded threshold.
547
+ - `SymbolicAutomaton` carries a derived `ap_inverse: Mapping[str, AP]` populated in
548
+ `__post_init__`; `to_automaton` and `compose._merge_managers` route through it.
549
+ - New symbolic benches are pytest-benchmark cases,
550
+ so timings get recorded rather than gated on a hard-coded threshold.
395
551
 
396
552
  ## 2.0.7 — 2026-06-02
397
553
 
398
554
  ### Fixed
399
555
 
400
- - The BDD JSON exporter in `dd.cudd` needed to be worked around because of a bug where
401
- `mgr.true` and `mrg.false` do not survive a round trip serialization.
556
+ - The BDD JSON exporter in `dd.cudd` needed to be worked around because of a bug
557
+ where `mgr.true` and `mrg.false` do not survive a round trip serialization.
402
558
 
403
559
  ## 2.0.6 — 2026-06-02
404
560
 
@@ -410,8 +566,9 @@ and increase the surface area of properly typed code.
410
566
 
411
567
  ### Added
412
568
 
413
- - Added a free-floating function that helps enumerate the successor BDDs from a symbolic
414
- automaton state, given a specific alphabet and set of symbols.
569
+ - Added a free-floating function
570
+ that helps enumerate the successor BDDs from a symbolic automaton state,
571
+ given a specific alphabet and set of symbols.
415
572
 
416
573
  ## 2.0.5 — 2026-06-01
417
574
 
@@ -465,8 +622,8 @@ smaller minimal DFAs, having a better composition operation benefits us.
465
622
  and bounded `FusionRepeat` desugar through `logic_asts.spec.Expr.expand`.
466
623
  Unbounded `FusionRepeat[:*i..]` deliberately raises `NotImplementedError` —
467
624
  I don't have a clean expansion for it yet.
468
- - Benchmarking script for SERE NLM intersection, so I can see when I
469
- break the perf curve.
625
+ - Benchmarking script for SERE NLM intersection,
626
+ so I can see when I break the perf curve.
470
627
 
471
628
  ### Changed
472
629
 
@@ -477,8 +634,7 @@ smaller minimal DFAs, having a better composition operation benefits us.
477
634
 
478
635
  ### Miscellaneous
479
636
 
480
- - The README is linked from `pyproject.toml`, so it actually shows up
481
- on PyPI.
637
+ - The README is linked from `pyproject.toml`, so it actually shows up on PyPI.
482
638
 
483
639
  ## 2.0.1 — 2026-05-24
484
640
 
@@ -512,8 +668,9 @@ interest.
512
668
  Two LTL helpers (`_aut_symbolic_expansion`, `_empty_trace_value`) were renamed to
513
669
  `ltl_symbolic_expansion` / `ltl_empty_trace_value` so PSL can delegate to them for
514
670
  pure-LTL subformulas.
515
- - Moved off `uv` and onto Pixi. `uv`'s handling of CUDA packages and
516
- the way it churns the lockfile weren't working for me; Pixi does.
671
+ - Moved off `uv` and onto Pixi.
672
+ `uv`'s handling of CUDA packages and the way it churns the lockfile weren't working
673
+ for me; Pixi does.
517
674
 
518
675
  ### Fixed
519
676
 
@@ -559,9 +716,8 @@ short-lived manager for these things.
559
716
  - `psl_to_automaton` does not yet decompose LTL temporal operators
560
717
  that wrap PSL closures at the top level (e.g. `Always(StrongClosure(r))`).
561
718
  Lift the PSL fragment out of the surrounding LTL temporal operator before calling.
562
- - The continuation of a `SuffixImpliesExist` / `SuffixImpliesUniv`
563
- must be a pure-LTL formula; nested PSL closures inside a
564
- continuation raise `NotImplementedError`.
719
+ - The continuation of a `SuffixImpliesExist` / `SuffixImpliesUniv` must be a pure-LTL
720
+ formula; nested PSL closures inside a continuation raise `NotImplementedError`.
565
721
 
566
722
  ## 2.0.0 — 2026-05-23
567
723
 
@@ -624,9 +780,10 @@ which will be summarized below.
624
780
  and `WeightedAlphabet.from_predicates(...)` folds per-AP predicates through the
625
781
  semiring.
626
782
  The reverse direction is deliberately omitted —
627
- recovering Boolean semantics from arbitrary `V` needs an ordering and a cut (cf.
628
- STL), and pretending otherwise would silently produce garbage.
629
- - **Ergonomic construction.** `AutomatonBuilder` replaces the old NFA builder.
783
+ recovering Boolean semantics from arbitrary `V` needs an ordering and a cut
784
+ (cf. STL), and pretending otherwise would silently produce garbage.
785
+ - **Ergonomic construction.**
786
+ `AutomatonBuilder` replaces the old NFA builder.
630
787
  Add states, add transitions with a guard and a successor
631
788
  (single state, set of states, or a raw `BoolExpr` for full alternation),
632
789
  and `build()` cleans up by merging rows that share a successor.
@@ -654,9 +811,11 @@ which will be summarized below.
654
811
  The BDD cofactor path works for any `BooleanAlphabet`,
655
812
  so you can feed a `SymbolicAutomaton` over integers, graph nodes, whatever —
656
813
  as long as you can write a labeling.
657
- - **The AFA-to-min-DFA pipeline actually exists now.** `nfa_from_afa` (dealternation),
658
- `determinize` (subset construction with a sink), `minimize` (Hopcroft) —
659
- all share one BDD manager and successively tighten the invariants on successor BDDs.
814
+ - **The AFA-to-min-DFA pipeline actually exists now.**
815
+ `nfa_from_afa` (dealternation), `determinize`
816
+ (subset construction with a sink),
817
+ `minimize` (Hopcroft) — all share one BDD manager
818
+ and successively tighten the invariants on successor BDDs.
660
819
  `SymbolicAutomaton.to_min_dfa` chains the three.
661
820
  Finite-word only for now; omega-regular determinisation is a different beast.
662
821
  - **DFA equivalence checks.**
@@ -667,17 +826,19 @@ which will be summarized below.
667
826
 
668
827
  ### Operators
669
828
 
670
- - **One per-step shape.** `SymbolicAutomaton`, `MatrixOperator`,
829
+ - **One per-step shape.**
830
+ `SymbolicAutomaton`, `MatrixOperator`,
671
831
  and `PolynomialOperator` all expose the same `step(current, symbol)` / `run(word)` /
672
832
  `accepts(word)` shape.
673
833
  Each binds its alphabet at construction
674
834
  (matching how the semiring / algebra were already bound),
675
835
  so the per-call API is monomorphic and `V` is pinned end-to-end.
676
836
  Wanted symmetry between the layers; got it.
677
- - **`PolynomialOperator` evaluates lazily.** `step` evaluates guards against
678
- `alphabet.weight` on the fly, with no per-symbol precomputation — important because
679
- the natural LTL alphabet is `Σ = 2^AP`, and you really don't want to materialise
680
- `2^|AP|` polynomials up front.
837
+ - **`PolynomialOperator` evaluates lazily.**
838
+ `step` evaluates guards against `alphabet.weight` on the fly,
839
+ with no per-symbol precomputation —
840
+ important because the natural LTL alphabet is `Σ = 2^AP`,
841
+ and you really don't want to materialise `2^|AP|` polynomials up front.
681
842
  The AFA's row structure stays static
682
843
  (good for JAX tracing), and the alphabet decides whether to memoise.
683
844
  - **Polynomial performance.**
@@ -687,8 +848,9 @@ which will be summarized below.
687
848
 
688
849
  ### Tooling and Tests
689
850
 
690
- - **Hand-written changelogs.** `git cliff` was producing entries
691
- that needed enough editing that I might as well write them by hand.
851
+ - **Hand-written changelogs.**
852
+ `git cliff` was producing entries that needed enough editing that I might
853
+ as well write them by hand.
692
854
  `ci/make-release` no longer touches `CHANGELOG.md` or tag messages.
693
855
  Run `git cliff` separately if you want a draft to crib from.
694
856
  - **Tests.**
@@ -701,23 +863,23 @@ which will be summarized below.
701
863
 
702
864
  Documentation and tooling cleanup.
703
865
 
704
- - Replaced stray non-ASCII characters in docs (those that survived from
705
- copy-paste rather than authored content).
866
+ - Replaced stray non-ASCII characters in docs
867
+ (those that survived from copy-paste rather than authored content).
706
868
  - Started publishing CHANGELOGs and wired them into the Sphinx docs.
707
869
  - Added a `pytest-timeout` dev dependency so hanging tests fail loudly.
708
- - Added a mypy override for the `dd` BDD library so type-checking is
709
- clean for symbolic code.
870
+ - Added a mypy override for the `dd` BDD library so type-checking is clean
871
+ for symbolic code.
710
872
 
711
873
  ## 1.0.2 — 2026-04-17
712
874
 
713
- - Added `dd` as a dependency along with a small in-tree wrapper, in
714
- preparation for the BDD-backed symbolic layer.
715
- - Fixed a queue ordering bug in `ltl_to_automaton` that produced
716
- incorrect successor expansions for some operator combinations.
717
- - First pass at user-facing documentation (README, LICENSE, package
718
- URLs) and at RST linting for the docs.
719
- - Trimmed CI to a single type checker (`mypy`) and stopped silently
720
- hiding missing-import errors.
875
+ - Added `dd` as a dependency along with a small in-tree wrapper,
876
+ in preparation for the BDD-backed symbolic layer.
877
+ - Fixed a queue ordering bug in `ltl_to_automaton`
878
+ that produced incorrect successor expansions for some operator combinations.
879
+ - First pass at user-facing documentation
880
+ (README, LICENSE, package URLs) and at RST linting for the docs.
881
+ - Trimmed CI to a single type checker (`mypy`)
882
+ and stopped silently hiding missing-import errors.
721
883
  - Ported all operators to the current `algebraic` API.
722
884
  This is source-incompatible with the previous internal `automatix` algebra module.
723
885
 
@@ -726,15 +888,15 @@ Documentation and tooling cleanup.
726
888
  Initial wave of feature work that established the current shape of the library.
727
889
 
728
890
  - Boolean polynomials implemented on top of BDDs.
729
- - STREL: Lark-based parser for STREL expressions and a translation to
730
- alternating finite automata, including the spatial Reach/Escape
731
- operators.
891
+ - STREL: Lark-based parser for STREL expressions
892
+ and a translation to alternating finite automata,
893
+ including the spatial Reach/Escape operators.
732
894
  - LTL/LTLf to alternating Büchi / finite automata.
733
895
  - Algebraic structures moved to the separate `algebraic` package.
734
- - HOA parsing moved out into a dedicated `hoaparser` package (later
735
- re-integrated as `morphata.hoa`).
736
- - JAX and PyTorch made optional dependencies; both backends are picked
737
- up automatically by `algebraic` when installed.
896
+ - HOA parsing moved out into a dedicated `hoaparser` package
897
+ (later re-integrated as `morphata.hoa`).
898
+ - JAX and PyTorch made optional dependencies;
899
+ both backends are picked up automatically by `algebraic` when installed.
738
900
  - Test suite rebuilt against the new APIs.
739
901
 
740
902
  ## Pre-1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: morphata
3
- Version: 2.2.3
3
+ Version: 2.3.1
4
4
  Summary: Representation theory for automata, made practical.
5
5
  Project-URL: Documentation, https://docs.anandb.dev/morphata
6
6
  Project-URL: Repository, https://git.anandb.dev/morphata.git
@@ -11,7 +11,7 @@ License-File: LICENSE
11
11
  Classifier: Intended Audience :: Science/Research
12
12
  Classifier: Topic :: Scientific/Engineering :: Mathematics
13
13
  Requires-Python: >=3.12
14
- Requires-Dist: algebraic-arrays>=1.3.0
14
+ Requires-Dist: algebraic-arrays>=1.3.10
15
15
  Requires-Dist: attrs>=25.4.0
16
16
  Requires-Dist: bitarray>=3.10.0
17
17
  Requires-Dist: cattrs>=26.1.0
@@ -24,7 +24,8 @@ Requires-Dist: typing-extensions>=4.14
24
24
  Provides-Extra: dot
25
25
  Requires-Dist: pydot>=4.0.1; extra == 'dot'
26
26
  Provides-Extra: jax
27
- Requires-Dist: jax>=0.8; extra == 'jax'
27
+ Requires-Dist: flax; extra == 'jax'
28
+ Requires-Dist: jax<0.11.2,>=0.8; extra == 'jax'
28
29
  Provides-Extra: torch
29
30
  Requires-Dist: torch>=2.0; extra == 'torch'
30
31
  Description-Content-Type: text/x-rst
@@ -20,6 +20,12 @@ To get pytest-benchmark's tabular wall-time output:
20
20
  JAX-only scale cases are opt-in via
21
21
  `MORPHATA_RUN_SCALE_BENCHMARKS=1`; run each scale case in a fresh process under
22
22
  a hard memory limit.
23
+ - `test_transforms_perf.py` -- alternation removal and determinization over
24
+ single-root conjoined-eventuality and until-chain AFA families, plus composed
25
+ eventuality AFAs whose initial formulas have multiple state variables. The
26
+ composed cases compare the monolithic and opt-in factored-initial
27
+ constructions. Results are grouped by transform, construction, and formula
28
+ size. Regular cases stop at size 10.
23
29
  - `test_sere_nlm_perf.py` -- NLM-intersection state-space blowup.
24
30
  Cases: `NLMInter(a0..a_{n-1})` for n in 2..6, `NLMInter(a_i;b_i)`
25
31
  for n in 2..4, plus length-matching `Inter` baselines.