morphata 2.0.9__tar.gz → 2.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- morphata-2.1.0/.basedpyright/baseline.json +3 -0
- {morphata-2.0.9 → morphata-2.1.0}/AGENTS.md +3 -3
- {morphata-2.0.9 → morphata-2.1.0}/CHANGELOG.md +50 -0
- {morphata-2.0.9 → morphata-2.1.0}/CONTRIBUTING.md +10 -48
- {morphata-2.0.9 → morphata-2.1.0}/PKG-INFO +3 -2
- {morphata-2.0.9 → morphata-2.1.0}/bench/README.md +5 -0
- {morphata-2.0.9 → morphata-2.1.0}/bench/conftest.py +4 -2
- {morphata-2.0.9 → morphata-2.1.0}/bench/test_hoa_exporter_perf.py +24 -8
- morphata-2.1.0/bench/test_hoa_parser_perf.py +128 -0
- {morphata-2.0.9 → morphata-2.1.0}/bench/test_sere_nlm_perf.py +21 -6
- {morphata-2.0.9 → morphata-2.1.0}/docs/conf.py +3 -1
- {morphata-2.0.9 → morphata-2.1.0}/docs/symbolic-transforms.rst +21 -0
- {morphata-2.0.9 → morphata-2.1.0}/justfile +7 -5
- {morphata-2.0.9 → morphata-2.1.0}/mypy.ini +10 -0
- {morphata-2.0.9 → morphata-2.1.0}/pixi.lock +546 -492
- {morphata-2.0.9 → morphata-2.1.0}/pixi.toml +17 -10
- {morphata-2.0.9 → morphata-2.1.0}/pyproject.toml +39 -7
- morphata-2.1.0/pyrightconfig.json +26 -0
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/acceptance.py +180 -36
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/alphabet.py +13 -13
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/automaton.py +102 -52
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/builder.py +25 -21
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/hoa/__init__.py +7 -1
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/hoa/__main__.py +3 -1
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/hoa/exporter.py +106 -65
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/hoa/hoa.lark +13 -7
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/hoa/parser.py +21 -11
- morphata-2.1.0/src/morphata/logic/_common.py +325 -0
- morphata-2.1.0/src/morphata/logic/ltl.py +268 -0
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/logic/psl.py +54 -107
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/logic/sere.py +72 -152
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/logic/strel.py +134 -191
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/operators/_backend.py +8 -5
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/operators/matrix.py +78 -46
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/operators/polynomial.py +88 -81
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/symbolic/__init__.py +8 -2
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/symbolic/automaton.py +84 -77
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/symbolic/bdd.py +290 -67
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/symbolic/compose.py +88 -44
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/symbolic/equivalence.py +37 -25
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/symbolic/persistence.py +198 -109
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/symbolic/transforms.py +333 -146
- morphata-2.1.0/src/morphata/utils.py +74 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/conftest.py +33 -17
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/test_exporter_streaming.py +23 -11
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/test_parser.py +5 -3
- {morphata-2.0.9 → morphata-2.1.0}/tests/logic/test_ltl.py +84 -32
- {morphata-2.0.9 → morphata-2.1.0}/tests/logic/test_psl.py +25 -8
- {morphata-2.0.9 → morphata-2.1.0}/tests/logic/test_sere.py +97 -39
- {morphata-2.0.9 → morphata-2.1.0}/tests/logic/test_sere_properties.py +26 -6
- {morphata-2.0.9 → morphata-2.1.0}/tests/logic/test_strel.py +77 -5
- {morphata-2.0.9 → morphata-2.1.0}/tests/operators/test_matrix_from_ir.py +19 -11
- {morphata-2.0.9 → morphata-2.1.0}/tests/operators/test_matrix_from_ltl.py +11 -5
- {morphata-2.0.9 → morphata-2.1.0}/tests/operators/test_polynomial_from_ir.py +20 -11
- {morphata-2.0.9 → morphata-2.1.0}/tests/symbolic/test_alphabet_aware_transitions.py +9 -3
- {morphata-2.0.9 → morphata-2.1.0}/tests/symbolic/test_bdd_helpers.py +53 -12
- {morphata-2.0.9 → morphata-2.1.0}/tests/symbolic/test_compose.py +168 -56
- {morphata-2.0.9 → morphata-2.1.0}/tests/symbolic/test_composition_map.py +76 -28
- {morphata-2.0.9 → morphata-2.1.0}/tests/symbolic/test_equivalence_naive_property.py +12 -4
- {morphata-2.0.9 → morphata-2.1.0}/tests/symbolic/test_ltlf2dfa_equivalence.py +46 -15
- morphata-2.1.0/tests/symbolic/test_persistence.py +308 -0
- morphata-2.1.0/tests/symbolic/test_prune.py +280 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/symbolic/test_step.py +6 -2
- {morphata-2.0.9 → morphata-2.1.0}/tests/symbolic/test_symbolic_automaton.py +135 -20
- {morphata-2.0.9 → morphata-2.1.0}/tests/symbolic/test_symbolic_from_ir.py +3 -3
- {morphata-2.0.9 → morphata-2.1.0}/tests/symbolic/test_to_hoa.py +28 -15
- {morphata-2.0.9 → morphata-2.1.0}/tests/symbolic/test_transforms.py +96 -182
- {morphata-2.0.9 → morphata-2.1.0}/tests/symbolic/test_transforms_ir.py +2 -2
- {morphata-2.0.9 → morphata-2.1.0}/tests/symbolic/test_transforms_properties.py +32 -10
- morphata-2.1.0/tests/test_acceptance_validation.py +45 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/test_alphabet.py +16 -6
- {morphata-2.0.9 → morphata-2.1.0}/tests/test_automaton.py +42 -8
- {morphata-2.0.9 → morphata-2.1.0}/tests/test_from_parsed.py +6 -2
- {morphata-2.0.9 → morphata-2.1.0}/uv.lock +821 -692
- morphata-2.0.9/.basedpyright/baseline.json +0 -854
- morphata-2.0.9/.claude/settings.local.json +0 -32
- morphata-2.0.9/pyrefly.toml +0 -2
- morphata-2.0.9/pyrightconfig.json +0 -14
- morphata-2.0.9/ruff.toml +0 -11
- morphata-2.0.9/src/morphata/logic/_common.py +0 -136
- morphata-2.0.9/src/morphata/logic/ltl.py +0 -499
- morphata-2.0.9/tests/symbolic/test_persistence.py +0 -175
- morphata-2.0.9/ty.toml +0 -7
- {morphata-2.0.9 → morphata-2.1.0}/.dockerignore +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/.editorconfig +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/.envrc +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/.envrc.recommended +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/.fdignore +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/.gitattributes +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/.gitignore +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/.mailmap +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/.python-version +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/.rstcheck.cfg +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/CLAUDE.md +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/LICENSE +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/README.md +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/bench/__init__.py +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/bench/test_equivalence_perf.py +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/bench/test_successors_for_alphabet_perf.py +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/ci/make-release +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/cliff.toml +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/_static/navigation.html +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/api/modules.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/api/morphata.acceptance.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/api/morphata.alphabet.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/api/morphata.automaton.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/api/morphata.builder.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/api/morphata.hoa.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/api/morphata.logic.ltl.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/api/morphata.logic.psl.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/api/morphata.logic.sere.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/api/morphata.logic.strel.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/api/morphata.operators.matrix.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/api/morphata.operators.polynomial.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/api/morphata.operators.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/api/morphata.symbolic.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/concepts/acceptance.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/concepts/hoa.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/concepts/index.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/concepts/operators.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/concepts/spec.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/concepts/weights.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/index.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/docs/quick-start.rst +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/project.utf-8.add +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/project.utf-8.add.spl +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/pytest.toml +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/__init__.py +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/hoa/acc_expr.py +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/logic/__init__.py +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/operators/__init__.py +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/src/morphata/py.typed +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/aut1.hoa +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/aut11.hoa +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/aut2.hoa +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/aut3.2.hoa +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/aut3.hoa +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/aut4.hoa +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/aut5.hoa +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/aut6.hoa +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/aut7.hoa +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/aut8.hoa +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/finite_aut.hoa +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/gen_buchi_3.hoa +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/gen_cobuchi_2.hoa +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/rabin_2pair.hoa +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/streett_2pair.hoa +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/test_exporter_basic.py +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/hoa/test_exporter_roundtrip.py +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/logic/__init__.py +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/symbolic/test_from_hoaf.py +0 -0
- {morphata-2.0.9 → morphata-2.1.0}/tests/test_builder.py +0 -0
|
@@ -7,7 +7,7 @@ It packages structural automaton representations together with semiring-valued
|
|
|
7
7
|
## Development Environment
|
|
8
8
|
|
|
9
9
|
The development shell already has the project virtual environment activated,
|
|
10
|
-
so `pytest`, `
|
|
10
|
+
so `pytest`, `zuban`, `basedpyright`, `ruff`, `sphinx-build`, etc. are on `PATH` directly.
|
|
11
11
|
**Do not** prefix commands with `uv run`, `pixi run`, `poetry run`, or similar.
|
|
12
12
|
Just invoke the tool.
|
|
13
13
|
If a tool is missing, stop and tell the user rather than trying to bootstrap
|
|
@@ -242,7 +242,7 @@ for DFA-shaped symbolic automata -- ``language_equivalent``, ``canonical_form``,
|
|
|
242
242
|
``backend=``; do not bake backend-only assumptions into operator
|
|
243
243
|
code.
|
|
244
244
|
6. **No Deprecation** : delete old code instead of wrapping it.
|
|
245
|
-
7. **Type Safety** : all new code must pass ``
|
|
245
|
+
7. **Type Safety** : all new code must pass ``zuban`` and ``basedpyright``.
|
|
246
246
|
8. **Prefer Refactoring** : update existing patterns rather than
|
|
247
247
|
special-casing.
|
|
248
248
|
|
|
@@ -282,7 +282,7 @@ for DFA-shaped symbolic automata -- ``language_equivalent``, ``canonical_form``,
|
|
|
282
282
|
## Testing
|
|
283
283
|
|
|
284
284
|
- **Framework** : pytest with jaxtyping.
|
|
285
|
-
- **Type Safety** :
|
|
285
|
+
- **Type Safety** : `zuban` and `basedpyright` (both must pass; preferred over mypy for spec conformance and performance).
|
|
286
286
|
- **Layout** : tests live under `tests/`:
|
|
287
287
|
- flat suites at the top: ``test_alphabet.py``, ``test_automaton.py``,
|
|
288
288
|
``test_builder.py``, ``test_from_parsed.py``;
|
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.1.0 — 2026-06-11
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
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.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
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`
|
|
31
|
+
(the one with the spatial `expand()` rules), so that dep gets bumped along with it.
|
|
32
|
+
|
|
33
|
+
### Miscellaneous
|
|
34
|
+
|
|
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.
|
|
38
|
+
|
|
39
|
+
## 2.0.10 — 2026-06-08
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- Using `SymbolicAutomaton.to_bdd_json` and `SymbolicAutomaton.from_bdd_json` did not
|
|
44
|
+
preserve the positive normal form of the BDDs because we were using incorrect Shannon
|
|
45
|
+
cofactor expansion.
|
|
46
|
+
|
|
47
|
+
### Performance
|
|
48
|
+
|
|
49
|
+
- The HOA grammar is now LALR compatible! This means we get a huge performance boost
|
|
50
|
+
even for pretty large automata.
|
|
51
|
+
- Similar thing was done in `logic-asts` and thus, the version will be bumped.
|
|
52
|
+
|
|
3
53
|
## 2.0.9 — 2026-06-04
|
|
4
54
|
|
|
5
55
|
### Fixed
|
|
@@ -39,20 +39,21 @@ just fmt
|
|
|
39
39
|
# OR
|
|
40
40
|
ruff format
|
|
41
41
|
ruff check --output-format concise --fix --exit-non-zero-on-fix
|
|
42
|
-
|
|
43
|
-
# Format + type check
|
|
44
|
-
just lint
|
|
45
42
|
```
|
|
46
43
|
|
|
47
44
|
**Type Checking** :
|
|
48
45
|
```bash
|
|
49
46
|
# Run all type checkers
|
|
50
47
|
just type-check
|
|
51
|
-
|
|
48
|
+
# OR
|
|
52
49
|
# Run specific type checkers
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
zuban check
|
|
51
|
+
basedpyright
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Formatting + Type Checking together**:
|
|
55
|
+
```bash
|
|
56
|
+
just lint
|
|
56
57
|
```
|
|
57
58
|
|
|
58
59
|
**Development Setup** :
|
|
@@ -60,7 +61,7 @@ pyrefly check --output-format min-text
|
|
|
60
61
|
# Set up dev environment (sync dependencies)
|
|
61
62
|
just dev
|
|
62
63
|
# OR
|
|
63
|
-
|
|
64
|
+
pixi install
|
|
64
65
|
|
|
65
66
|
# For CUDA support (set CUDA_VERSION=12 or 13)
|
|
66
67
|
CUDA_VERSION=12 just dev
|
|
@@ -74,7 +75,7 @@ One can also copy the included `.envrc.recommended` file to `.envrc` and use it
|
|
|
74
75
|
|
|
75
76
|
### Type Annotations
|
|
76
77
|
|
|
77
|
-
- All code must pass `
|
|
78
|
+
- All code must pass `zuban` and `basedpyright`. Use explicit type annotations.
|
|
78
79
|
- Use Python 3.12+ syntax: `type` aliases, `|` for unions, generic classes and functions
|
|
79
80
|
with `[T]`
|
|
80
81
|
- Always specify return types, including `-> None`
|
|
@@ -89,42 +90,3 @@ One can also copy the included `.envrc.recommended` file to `.envrc` and use it
|
|
|
89
90
|
- Format: NumPy-style docstrings with sections: Parameters, Returns,
|
|
90
91
|
Raises, Notes, Examples.
|
|
91
92
|
- Inline comments should say _why_ something is done, not _what_.
|
|
92
|
-
|
|
93
|
-
## Source Layout
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
morphata/
|
|
97
|
-
├── src/morphata/
|
|
98
|
-
│ ├── automaton.py # Automaton IR + Outgoing/Step deltas
|
|
99
|
-
│ ├── alphabet.py # Input alphabet + characteristic function
|
|
100
|
-
│ ├── builder.py # AutomatonBuilder (mutable IR builder)
|
|
101
|
-
│ ├── acceptance.py # Finite + omega-regular acceptance
|
|
102
|
-
│ ├── weights.py # Guard-based weight predicates
|
|
103
|
-
│ ├── logic/
|
|
104
|
-
│ │ ├── ltl.py # LTL/LTLf -> AFA
|
|
105
|
-
│ │ └── strel.py # STREL automaton (Step delta)
|
|
106
|
-
│ ├── hoa/ # Extended HOA v1 parser
|
|
107
|
-
│ │ ├── parser.py
|
|
108
|
-
│ │ ├── exporter.py # placeholder; not yet implemented
|
|
109
|
-
│ │ └── acc_expr.py
|
|
110
|
-
│ ├── operators/ # Semiring-valued morphisms
|
|
111
|
-
│ │ ├── matrix.py # MatrixOperator (weighted NFAs)
|
|
112
|
-
│ │ ├── polynomial.py # PolynomialOperator (AFAs)
|
|
113
|
-
│ │ └── _backend.py # backend resolution + JAX pytree aux
|
|
114
|
-
│ └── symbolic/ # BDD-backed structural layer
|
|
115
|
-
│ ├── automaton.py # SymbolicAutomaton
|
|
116
|
-
│ ├── bdd.py # BoolExpr <-> dd.Function bridge
|
|
117
|
-
│ ├── transforms.py # determinize, minimize, nfa_from_afa
|
|
118
|
-
│ └── equivalence.py # language_equivalent, isomorphic, canonical_form
|
|
119
|
-
│
|
|
120
|
-
└── tests/ # Mirrors the package layout
|
|
121
|
-
├── test_alphabet.py
|
|
122
|
-
├── test_automaton.py
|
|
123
|
-
├── test_builder.py
|
|
124
|
-
├── test_from_parsed.py
|
|
125
|
-
├── test_weights.py
|
|
126
|
-
├── hoa/
|
|
127
|
-
├── logic/
|
|
128
|
-
├── operators/
|
|
129
|
-
└── symbolic/
|
|
130
|
-
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: morphata
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.1.0
|
|
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
|
|
@@ -13,10 +13,11 @@ Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
|
13
13
|
Requires-Python: >=3.12
|
|
14
14
|
Requires-Dist: algebraic-arrays>=1.3.0
|
|
15
15
|
Requires-Dist: attrs>=25.4.0
|
|
16
|
+
Requires-Dist: cattrs>=26.1.0
|
|
16
17
|
Requires-Dist: dd>=0.6.0
|
|
17
18
|
Requires-Dist: jaxtyping>=0.3
|
|
18
19
|
Requires-Dist: lark>=1.3.1
|
|
19
|
-
Requires-Dist: logic-asts>=1.
|
|
20
|
+
Requires-Dist: logic-asts>=1.7
|
|
20
21
|
Requires-Dist: networkx
|
|
21
22
|
Requires-Dist: numpy
|
|
22
23
|
Requires-Dist: types-networkx
|
|
@@ -20,6 +20,11 @@ To get pytest-benchmark's tabular wall-time output:
|
|
|
20
20
|
wall time across regimes that stress the per-row guard/successor
|
|
21
21
|
caching (small DFA, sparse-big-alphabet DFA, conjunction min-DFA, raw
|
|
22
22
|
AFA without minimization).
|
|
23
|
+
- `test_hoa_parser_perf.py` -- ``hoa.parser.parse`` (parse + transform)
|
|
24
|
+
across the sample corpus, parametrized over ``earley`` vs ``lalr`` from
|
|
25
|
+
the same grammar to quantify the LALR switch. Run with
|
|
26
|
+
``--benchmark-group-by=param:case`` to put the two algorithms side by
|
|
27
|
+
side per input.
|
|
23
28
|
|
|
24
29
|
## Recording results
|
|
25
30
|
|
|
@@ -12,7 +12,9 @@ from collections.abc import Iterator
|
|
|
12
12
|
|
|
13
13
|
import pytest
|
|
14
14
|
|
|
15
|
-
from morphata.logic.sere import
|
|
15
|
+
from morphata.logic.sere import (
|
|
16
|
+
_expansion_call_counter, # pyright: ignore[reportPrivateUsage]
|
|
17
|
+
)
|
|
16
18
|
from morphata.symbolic.bdd import Manager, make_manager
|
|
17
19
|
|
|
18
20
|
|
|
@@ -30,7 +32,7 @@ def bench_mgr() -> Iterator[Manager]:
|
|
|
30
32
|
try:
|
|
31
33
|
yield manager
|
|
32
34
|
finally:
|
|
33
|
-
gc.collect()
|
|
35
|
+
_ = gc.collect()
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
@pytest.fixture
|
|
@@ -41,7 +41,9 @@ def _small_alphabet_dfa(mgr: Manager) -> SymbolicAutomaton[str]:
|
|
|
41
41
|
aps = frozenset({"a", "b"})
|
|
42
42
|
formula = ltl.Eventually(ltl.And((ltl.Variable("a"), ltl.Variable("b"))))
|
|
43
43
|
alphabet = powerset_alphabet(aps)
|
|
44
|
-
return SymbolicAutomaton.from_ltl(
|
|
44
|
+
return SymbolicAutomaton.from_ltl(
|
|
45
|
+
formula, alphabet=alphabet, manager=mgr, finite=True
|
|
46
|
+
).to_min_dfa()
|
|
45
47
|
|
|
46
48
|
|
|
47
49
|
def _sparse_big_alphabet_dfa(mgr: Manager) -> SymbolicAutomaton[str]:
|
|
@@ -55,7 +57,9 @@ def _sparse_big_alphabet_dfa(mgr: Manager) -> SymbolicAutomaton[str]:
|
|
|
55
57
|
)
|
|
56
58
|
)
|
|
57
59
|
alphabet = powerset_alphabet(aps)
|
|
58
|
-
return SymbolicAutomaton.from_ltl(
|
|
60
|
+
return SymbolicAutomaton.from_ltl(
|
|
61
|
+
formula, alphabet=alphabet, manager=mgr, finite=True
|
|
62
|
+
).to_min_dfa()
|
|
59
63
|
|
|
60
64
|
|
|
61
65
|
def _conjunction_min_dfa(mgr: Manager) -> SymbolicAutomaton[str]:
|
|
@@ -70,7 +74,9 @@ def _conjunction_min_dfa(mgr: Manager) -> SymbolicAutomaton[str]:
|
|
|
70
74
|
)
|
|
71
75
|
)
|
|
72
76
|
alphabet = powerset_alphabet(aps)
|
|
73
|
-
return SymbolicAutomaton.from_ltl(
|
|
77
|
+
return SymbolicAutomaton.from_ltl(
|
|
78
|
+
formula, alphabet=alphabet, manager=mgr, finite=True
|
|
79
|
+
).to_min_dfa()
|
|
74
80
|
|
|
75
81
|
|
|
76
82
|
def _afa_no_minimize(mgr: Manager) -> SymbolicAutomaton[str]:
|
|
@@ -80,15 +86,21 @@ def _afa_no_minimize(mgr: Manager) -> SymbolicAutomaton[str]:
|
|
|
80
86
|
ltl.And(
|
|
81
87
|
(
|
|
82
88
|
ltl.Variable("b"),
|
|
83
|
-
ltl.Eventually(
|
|
89
|
+
ltl.Eventually(
|
|
90
|
+
ltl.And((ltl.Variable("c"), ltl.Eventually(ltl.Variable("d"))))
|
|
91
|
+
),
|
|
84
92
|
)
|
|
85
93
|
),
|
|
86
94
|
)
|
|
87
95
|
alphabet = powerset_alphabet(aps)
|
|
88
|
-
return SymbolicAutomaton.from_ltl(
|
|
96
|
+
return SymbolicAutomaton.from_ltl(
|
|
97
|
+
formula, alphabet=alphabet, manager=mgr, finite=True
|
|
98
|
+
)
|
|
89
99
|
|
|
90
100
|
|
|
91
|
-
def test_hoa_export_small_alphabet_dfa(
|
|
101
|
+
def test_hoa_export_small_alphabet_dfa(
|
|
102
|
+
benchmark: Benchmark, bench_mgr: Manager
|
|
103
|
+
) -> None:
|
|
92
104
|
sym = _small_alphabet_dfa(bench_mgr)
|
|
93
105
|
|
|
94
106
|
def _run() -> int:
|
|
@@ -97,7 +109,9 @@ def test_hoa_export_small_alphabet_dfa(benchmark: Benchmark, bench_mgr: Manager)
|
|
|
97
109
|
assert benchmark(_run) > 0
|
|
98
110
|
|
|
99
111
|
|
|
100
|
-
def test_hoa_export_sparse_big_alphabet_dfa(
|
|
112
|
+
def test_hoa_export_sparse_big_alphabet_dfa(
|
|
113
|
+
benchmark: Benchmark, bench_mgr: Manager
|
|
114
|
+
) -> None:
|
|
101
115
|
sym = _sparse_big_alphabet_dfa(bench_mgr)
|
|
102
116
|
|
|
103
117
|
def _run() -> int:
|
|
@@ -106,7 +120,9 @@ def test_hoa_export_sparse_big_alphabet_dfa(benchmark: Benchmark, bench_mgr: Man
|
|
|
106
120
|
assert benchmark(_run) > 0
|
|
107
121
|
|
|
108
122
|
|
|
109
|
-
def test_hoa_export_conjunction_min_dfa(
|
|
123
|
+
def test_hoa_export_conjunction_min_dfa(
|
|
124
|
+
benchmark: Benchmark, bench_mgr: Manager
|
|
125
|
+
) -> None:
|
|
110
126
|
sym = _conjunction_min_dfa(bench_mgr)
|
|
111
127
|
|
|
112
128
|
def _run() -> int:
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Micro-benchmarks for ``morphata.hoa.parser.parse``.
|
|
2
|
+
|
|
3
|
+
Quantifies the Earley -> LALR switch in the HOA parser. Both variants are
|
|
4
|
+
built from the *same* committed grammar (`hoa.lark`); the only difference is
|
|
5
|
+
``parser="earley"`` (the old default) versus ``parser="lalr"`` (current). The
|
|
6
|
+
grammar's ``acceptance_cond`` is stratified, so it is unambiguous and parses
|
|
7
|
+
under both algorithms -- the comparison is purely the parsing algorithm plus
|
|
8
|
+
the shared ``_AstTransformer`` pass, i.e. the full ``parse()`` pipeline.
|
|
9
|
+
|
|
10
|
+
Cases span the sample corpus from small (``aut1``) to the largest hand-written
|
|
11
|
+
files (``rabin_2pair``, ``streett_2pair``), plus a synthetic ``craftax``
|
|
12
|
+
stress input: a ~36-state min-DFA over 25 APs (the
|
|
13
|
+
``test_min_dfa_of_conjunction_craftax_shaped_stress`` conjunction), exported to
|
|
14
|
+
HOA. At ~18 KB with dense per-edge guards it is ~35x the largest sample file
|
|
15
|
+
and pushes Earley into its superlinear regime, where the LALR win is starkest.
|
|
16
|
+
|
|
17
|
+
Run with::
|
|
18
|
+
|
|
19
|
+
pytest bench/test_hoa_parser_perf.py -v \\
|
|
20
|
+
--benchmark-columns=mean,median,min,max,rounds \\
|
|
21
|
+
--benchmark-group-by=param:case
|
|
22
|
+
|
|
23
|
+
Grouping by ``case`` puts the earley/lalr rows for the same input side by side
|
|
24
|
+
so the speedup is read directly off the table.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import functools
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
|
|
32
|
+
import logic_asts as logic
|
|
33
|
+
import logic_asts.ltl as ltl
|
|
34
|
+
import pytest
|
|
35
|
+
from lark import Lark
|
|
36
|
+
from pytest_benchmark.fixture import BenchmarkFixture as Benchmark
|
|
37
|
+
|
|
38
|
+
from morphata.alphabet import powerset_alphabet
|
|
39
|
+
from morphata.hoa.exporter import symbolic_automaton_to_hoa
|
|
40
|
+
from morphata.hoa.parser import (
|
|
41
|
+
HOA_GRAMMAR_FILE,
|
|
42
|
+
ParsedAutomaton,
|
|
43
|
+
_AstTransformer, # pyright: ignore[reportPrivateUsage]
|
|
44
|
+
)
|
|
45
|
+
from morphata.symbolic.bdd import make_manager
|
|
46
|
+
from morphata.symbolic.compose import min_dfa_of_conjunction
|
|
47
|
+
|
|
48
|
+
_GRAMMAR = HOA_GRAMMAR_FILE.read_text()
|
|
49
|
+
_HOA_DIR = Path(__file__).resolve().parent.parent / "tests" / "hoa"
|
|
50
|
+
|
|
51
|
+
# One Lark instance per algorithm, built once and reused across rounds.
|
|
52
|
+
_PARSERS: dict[str, Lark] = {
|
|
53
|
+
"earley": Lark(
|
|
54
|
+
_GRAMMAR, start="automaton", parser="earley", maybe_placeholders=True
|
|
55
|
+
),
|
|
56
|
+
"lalr": Lark(_GRAMMAR, start="automaton", parser="lalr", maybe_placeholders=True),
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Representative slice of the sample corpus: smallest, a couple of mid-size
|
|
60
|
+
# omega conditions, the two largest acceptance formulas, and a synthetic
|
|
61
|
+
# stress input (built lazily, see ``_craftax_hoa``).
|
|
62
|
+
_CASES = ["aut1", "gen_buchi_3", "aut8", "rabin_2pair", "streett_2pair", "craftax"]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@functools.cache
|
|
66
|
+
def _craftax_hoa() -> str:
|
|
67
|
+
"""Export the Craftax ENCHANT_SWORD-shaped min-DFA (~7 conjuncts, |AP|=25).
|
|
68
|
+
|
|
69
|
+
Mirrors ``test_min_dfa_of_conjunction_craftax_shaped_stress``. Cached so the
|
|
70
|
+
min-DFA is built once regardless of how many benchmark rounds run.
|
|
71
|
+
"""
|
|
72
|
+
aps = {f"p{i}" for i in range(25)}
|
|
73
|
+
alphabet = powerset_alphabet(aps)
|
|
74
|
+
p = sorted(aps)
|
|
75
|
+
|
|
76
|
+
def F(name: str) -> logic.LTLExpr[str]: # noqa: N802
|
|
77
|
+
return ltl.Eventually(logic.Variable(name))
|
|
78
|
+
|
|
79
|
+
def U(lhs: logic.LTLExpr[str], rhs: logic.LTLExpr[str]) -> logic.LTLExpr[str]: # noqa: N802
|
|
80
|
+
return ltl.Until(lhs, rhs)
|
|
81
|
+
|
|
82
|
+
formulas: list[logic.LTLExpr[str]] = [
|
|
83
|
+
F(p[0]),
|
|
84
|
+
U(
|
|
85
|
+
logic.Not(logic.Variable(p[0])),
|
|
86
|
+
logic.Or((logic.Variable(p[1]), logic.Variable(p[2]))),
|
|
87
|
+
),
|
|
88
|
+
U(
|
|
89
|
+
logic.Not(logic.Variable(p[0])),
|
|
90
|
+
logic.Or(
|
|
91
|
+
(logic.Variable(p[3]), logic.Variable(p[4]), logic.Variable(p[5]))
|
|
92
|
+
),
|
|
93
|
+
),
|
|
94
|
+
U(
|
|
95
|
+
logic.Not(logic.Variable(p[0])),
|
|
96
|
+
logic.Or((logic.Variable(p[6]), logic.Variable(p[7]))),
|
|
97
|
+
),
|
|
98
|
+
F(p[1]),
|
|
99
|
+
F(p[4]),
|
|
100
|
+
F(p[7]),
|
|
101
|
+
]
|
|
102
|
+
sym, _ = min_dfa_of_conjunction(formulas, alphabet=alphabet, manager=make_manager())
|
|
103
|
+
return symbolic_automaton_to_hoa(sym)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _hoa_text(case: str) -> str:
|
|
107
|
+
if case == "craftax":
|
|
108
|
+
return _craftax_hoa()
|
|
109
|
+
return (_HOA_DIR / f"{case}.hoa").read_text()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _parse_with(lark: Lark, text: str) -> ParsedAutomaton:
|
|
113
|
+
tree = lark.parse(text)
|
|
114
|
+
ret = _AstTransformer().transform(tree)
|
|
115
|
+
assert isinstance(ret, ParsedAutomaton)
|
|
116
|
+
return ret
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@pytest.mark.parametrize("kind", ["earley", "lalr"])
|
|
120
|
+
@pytest.mark.parametrize("case", _CASES)
|
|
121
|
+
def test_hoa_parse(benchmark: Benchmark, case: str, kind: str) -> None:
|
|
122
|
+
text = _hoa_text(case)
|
|
123
|
+
lark = _PARSERS[kind]
|
|
124
|
+
|
|
125
|
+
def _run() -> int:
|
|
126
|
+
return len(_parse_with(lark, text).body)
|
|
127
|
+
|
|
128
|
+
assert benchmark(_run) >= 0
|
|
@@ -43,7 +43,12 @@ def _lm_of_aps(n: int) -> logic.Expr:
|
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
def _nlm_of_concats(n: int) -> logic.Expr:
|
|
46
|
-
return sere.NLMInter(
|
|
46
|
+
return sere.NLMInter(
|
|
47
|
+
tuple(
|
|
48
|
+
sere.Concat((sere.Variable(f"a{i}"), sere.Variable(f"b{i}")))
|
|
49
|
+
for i in range(n)
|
|
50
|
+
)
|
|
51
|
+
)
|
|
47
52
|
|
|
48
53
|
|
|
49
54
|
def _run_and_measure(formula: logic.Expr, mgr: Manager) -> tuple[int, int]:
|
|
@@ -56,17 +61,23 @@ def _run_and_measure(formula: logic.Expr, mgr: Manager) -> tuple[int, int]:
|
|
|
56
61
|
|
|
57
62
|
@pytest.mark.timeout(10)
|
|
58
63
|
@pytest.mark.parametrize("n", list(range(2, 10)))
|
|
59
|
-
def test_bench_nlm_of_aps(
|
|
64
|
+
def test_bench_nlm_of_aps(
|
|
65
|
+
benchmark: Benchmark, expansion_calls: list[int], bench_mgr: Manager, n: int
|
|
66
|
+
) -> None:
|
|
60
67
|
formula = _nlm_of_aps(n)
|
|
61
68
|
result = benchmark(_run_and_measure, formula, bench_mgr)
|
|
62
69
|
state_count, call_count = result
|
|
63
70
|
expansion_calls[0] = call_count
|
|
64
|
-
print(
|
|
71
|
+
print(
|
|
72
|
+
f"\nNLMInter(a0..a{n - 1}): states={state_count} expansion_calls={call_count}"
|
|
73
|
+
)
|
|
65
74
|
|
|
66
75
|
|
|
67
76
|
@pytest.mark.timeout(10)
|
|
68
77
|
@pytest.mark.parametrize("n", list(range(2, 10)))
|
|
69
|
-
def test_bench_lm_of_aps_baseline(
|
|
78
|
+
def test_bench_lm_of_aps_baseline(
|
|
79
|
+
benchmark: Benchmark, expansion_calls: list[int], bench_mgr: Manager, n: int
|
|
80
|
+
) -> None:
|
|
70
81
|
formula = _lm_of_aps(n)
|
|
71
82
|
result = benchmark(_run_and_measure, formula, bench_mgr)
|
|
72
83
|
state_count, call_count = result
|
|
@@ -76,9 +87,13 @@ def test_bench_lm_of_aps_baseline(benchmark: Benchmark, expansion_calls: list[in
|
|
|
76
87
|
|
|
77
88
|
@pytest.mark.timeout(10)
|
|
78
89
|
@pytest.mark.parametrize("n", list(range(2, 10)))
|
|
79
|
-
def test_bench_nlm_of_concats(
|
|
90
|
+
def test_bench_nlm_of_concats(
|
|
91
|
+
benchmark: Benchmark, expansion_calls: list[int], bench_mgr: Manager, n: int
|
|
92
|
+
) -> None:
|
|
80
93
|
formula = _nlm_of_concats(n)
|
|
81
94
|
result = benchmark(_run_and_measure, formula, bench_mgr)
|
|
82
95
|
state_count, call_count = result
|
|
83
96
|
expansion_calls[0] = call_count
|
|
84
|
-
print(
|
|
97
|
+
print(
|
|
98
|
+
f"\nNLMInter(a_i;b_i) n={n}: states={state_count} expansion_calls={call_count}"
|
|
99
|
+
)
|
|
@@ -36,7 +36,9 @@ autodoc_default_options = {
|
|
|
36
36
|
autodoc_typehints = "description"
|
|
37
37
|
autosummary_generate = True
|
|
38
38
|
|
|
39
|
-
intersphinx_mapping: dict[
|
|
39
|
+
intersphinx_mapping: dict[
|
|
40
|
+
str, tuple[str, None | str | tuple[str, tuple[str | None, ...]]]
|
|
41
|
+
] = {
|
|
40
42
|
"equinox": ("https://docs.kidger.site/equinox", None),
|
|
41
43
|
"algebraic": ("https://docs.anandb.dev/algebraic", None),
|
|
42
44
|
}
|
|
@@ -19,6 +19,27 @@ For the common case of running the whole pipeline, call
|
|
|
19
19
|
<morphata.symbolic.automaton.SymbolicAutomaton.to_min_dfa>`, which
|
|
20
20
|
composes the three transforms in order.
|
|
21
21
|
|
|
22
|
+
Best-effort pruning
|
|
23
|
+
-------------------
|
|
24
|
+
|
|
25
|
+
:func:`morphata.symbolic.transforms.prune` shrinks an alternating
|
|
26
|
+
automaton in place *without* determinising it. Unlike
|
|
27
|
+
:meth:`~morphata.symbolic.automaton.SymbolicAutomaton.to_min_dfa`, it
|
|
28
|
+
preserves the alternating structure (successor BDDs keep their
|
|
29
|
+
conjunctive shape) and is *best-effort*, not minimal. It applies, in
|
|
30
|
+
order: forward reachability, an AFA-aware dead-state fixpoint, and one or
|
|
31
|
+
more rounds (``passes``, default 1) of one-step acceptance-partition
|
|
32
|
+
bisimulation dedup. Requires :class:`~morphata.acceptance.Finite`
|
|
33
|
+
acceptance. ``num_states`` never increases and the accepted language is
|
|
34
|
+
preserved.
|
|
35
|
+
|
|
36
|
+
.. code-block:: python
|
|
37
|
+
|
|
38
|
+
from morphata.symbolic.transforms import prune
|
|
39
|
+
|
|
40
|
+
sym_aut = SymbolicAutomaton.from_ltl(formula, alphabet=ba, finite=True)
|
|
41
|
+
sym_aut = prune(sym_aut)
|
|
42
|
+
|
|
22
43
|
BDD-shape invariants
|
|
23
44
|
--------------------
|
|
24
45
|
|
|
@@ -42,16 +42,18 @@ pyrefly-check:
|
|
|
42
42
|
mypy-check:
|
|
43
43
|
mypy --strict
|
|
44
44
|
|
|
45
|
+
[no-cd]
|
|
46
|
+
[private]
|
|
47
|
+
zuban-check:
|
|
48
|
+
zuban check
|
|
49
|
+
|
|
45
50
|
[no-cd]
|
|
46
51
|
[private]
|
|
47
52
|
pyright-check:
|
|
48
53
|
basedpyright
|
|
49
54
|
|
|
50
|
-
|
|
51
|
-
type-check:
|
|
52
|
-
|
|
53
|
-
# pyright-check
|
|
54
|
-
# ty-check pyrefly-check
|
|
55
|
+
[parallel]
|
|
56
|
+
type-check: zuban-check pyright-check
|
|
55
57
|
|
|
56
58
|
# Run both formatting and type checking
|
|
57
59
|
[no-cd]
|
|
@@ -8,6 +8,8 @@ files = $MYPY_CONFIG_FILE_DIR/src/**/*.py,
|
|
|
8
8
|
|
|
9
9
|
cache_dir = $MYPY_CONFIG_FILE_DIR/.mypy_cache
|
|
10
10
|
|
|
11
|
+
mypy_path = $MYPY_CONFIG_FILE_DIR/typings
|
|
12
|
+
|
|
11
13
|
exclude_gitignore = True
|
|
12
14
|
|
|
13
15
|
|
|
@@ -43,3 +45,11 @@ ignore_missing_imports = True
|
|
|
43
45
|
|
|
44
46
|
[mypy-wadler_lindig]
|
|
45
47
|
ignore_missing_imports = True
|
|
48
|
+
|
|
49
|
+
[mypy-spot]
|
|
50
|
+
ignore_missing_imports = True
|
|
51
|
+
disallow_untyped_defs = False
|
|
52
|
+
|
|
53
|
+
[mypy-spot.*]
|
|
54
|
+
ignore_missing_imports = True
|
|
55
|
+
disallow_untyped_defs = False
|