unicode-fol-kit 0.5.2__tar.gz → 0.7.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.
- unicode_fol_kit-0.7.0/CHANGELOG.md +472 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/PKG-INFO +354 -7
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/README.md +353 -6
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/pyproject.toml +1 -1
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/__init__.py +39 -1
- unicode_fol_kit-0.7.0/unicode_fol_kit/atp/__init__.py +47 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/atp/fitch.py +1426 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/atp/fitch_search.py +486 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/atp/lj.py +352 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/atp/sequent.py +948 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/atp/tableau.py +255 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/atp/z3_input.py +181 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/fol/__init__.py +12 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/fol/_fol_nodes.py +3 -3
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/fol/_modal_nodes.py +100 -31
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/fol/_msfl_nodes.py +25 -9
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/fol/_so_nodes.py +6 -6
- unicode_fol_kit-0.7.0/unicode_fol_kit/fol/_symbol_names.py +67 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/fol/modal_translation.py +8 -2
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/fol/msflparser.py +16 -174
- unicode_fol_kit-0.7.0/unicode_fol_kit/fol/prover9_input.py +210 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/fol/qml.py +575 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/fol/tptp_input.py +314 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/fol/verbalize.py +106 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/hol/__init__.py +49 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/hol/classical.py +494 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/hol/intuitionistic.py +295 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/hol/isabelle_modal.py +712 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/hol/manyvalued.py +643 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/hol/secondorder.py +529 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/hol/thf_modal.py +449 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/semantics/__init__.py +13 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/semantics/intuitionistic.py +153 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/semantics/kripke.py +73 -6
- unicode_fol_kit-0.7.0/unicode_fol_kit/semantics/modelfinder.py +192 -0
- unicode_fol_kit-0.7.0/unicode_fol_kit/semantics/truthtable.py +158 -0
- unicode_fol_kit-0.5.2/CHANGELOG.md +0 -213
- unicode_fol_kit-0.5.2/unicode_fol_kit/atp/__init__.py +0 -21
- unicode_fol_kit-0.5.2/unicode_fol_kit/fol/grammars/fl.lark +0 -68
- unicode_fol_kit-0.5.2/unicode_fol_kit/fol/grammars/fol.lark +0 -66
- unicode_fol_kit-0.5.2/unicode_fol_kit/fol/grammars/modal.lark +0 -97
- unicode_fol_kit-0.5.2/unicode_fol_kit/fol/grammars/msfl.lark +0 -68
- unicode_fol_kit-0.5.2/unicode_fol_kit/fol/grammars/msfol.lark +0 -64
- unicode_fol_kit-0.5.2/unicode_fol_kit/fol/grammars/so.lark +0 -74
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/.gitignore +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/LICENSE +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/__main__.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/atp/prover9_entailment.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/atp/resolution.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/atp/vampire_entailment.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/atp/z3_arith.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/atp/z3_equivalence.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/atp/z3_fuzzy.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/atp/z3_models.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/eval/__init__.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/eval/canonical.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/eval/predicate_match.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/eval/validate.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/fol/grammars/terminals.lark +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/fol/lambda_tools.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/fol/latex_input.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/fol/naming.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/fol/nodes.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/fol/normalforms.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/fol/unification.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/py.typed +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/semantics/_modal_reject.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/semantics/fuzzy.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/semantics/manyvalued.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/semantics/secondorder.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.7.0}/unicode_fol_kit/semantics/tarski.py +0 -0
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file. The format is
|
|
4
|
+
loosely based on [Keep a Changelog](https://keepachangelog.com/). Versioning is
|
|
5
|
+
semantic, but the project is pre-1.0 (alpha): a **minor** release may contain
|
|
6
|
+
breaking changes.
|
|
7
|
+
|
|
8
|
+
## [0.7.0] - 2026-06-28
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Epistemic / doxastic frame systems in the first-order embedding.**
|
|
13
|
+
`qml_is_valid` / `qml_equivalent` now take `systems={"epistemic": "S5", "doxastic":
|
|
14
|
+
"KD45"}`, emitting per-agent frame axioms for the agent-indexed `Rk` / `Rb` relations
|
|
15
|
+
— so e.g. factivity `∀x (K_x φ → φ)` is valid under a reflexive epistemic system. This
|
|
16
|
+
makes the FO path symmetric to the THF exporter (which already had `systems=`).
|
|
17
|
+
- **Quantified modal logic (QML) via shallow embeddings.** Object quantifiers `∀x` /
|
|
18
|
+
`∃x` under a modality, with the domain-regime semantics that decide the Barcan
|
|
19
|
+
formulas:
|
|
20
|
+
- **Semantics** — `KripkeModel` now takes per-world object domains (`domains={w: …}`
|
|
21
|
+
for varying, `domain=[…]` for constant), and `satisfies_modal` interprets `∀x` /
|
|
22
|
+
`∃x` *actualistically* (at a world `w` they range over `D_w`). Barcan
|
|
23
|
+
(`◇∃x A → ∃x ◇A`) and converse Barcan are valid/invalid exactly as the domains
|
|
24
|
+
vary. Backward compatible (omit the domains for the propositional fragment).
|
|
25
|
+
- **(A) First-order shallow embedding** (`unicode_fol_kit.fol.qml`): `qml_translate`
|
|
26
|
+
(with the existence predicate `E!` relativising actualist quantifiers + world/object
|
|
27
|
+
sort guards), `qml_axioms`, and `qml_is_valid` / `qml_equivalent` decide validity /
|
|
28
|
+
equivalence with Z3 per domain regime (`constant` / `increasing` / `decreasing` /
|
|
29
|
+
`varying`) and frame (K/T/S4/S5/KD/KD45). Sound but bounded-incomplete (first-order
|
|
30
|
+
modal logic is undecidable). The regime↔Barcan correspondence (BF ⇔ decreasing,
|
|
31
|
+
CBF ⇔ increasing, constant ⇔ both) is cross-checked against exhaustive Kripke-model
|
|
32
|
+
enumeration over every regime.
|
|
33
|
+
- **(B) Higher-order shallow embedding** — `to_thf_modal` emits a Benzmüller-style
|
|
34
|
+
TPTP **THF** problem (lifted `mbox`/`mdia`/`mforall`/`mexists`/`mvalid` + frame and
|
|
35
|
+
domain axioms) for an external higher-order prover (Leo-III, Satallax);
|
|
36
|
+
`to_isabelle_modal` emits an Isabelle/HOL skeleton. Alethic □/◇ fragment.
|
|
37
|
+
- All re-exported at the package top level; `BARCAN` / `CONVERSE_BARCAN` are provided
|
|
38
|
+
as the standard litmus formulas.
|
|
39
|
+
- **`⊕L` / `⊕R` (exclusive-or) rules in the sequent calculus** (`A⊕B ≡ ¬(A↔B)`),
|
|
40
|
+
closing the one connective that had no inference rule in either checker.
|
|
41
|
+
- **HOL / Isabelle / THF exporters for all non-fuzzy logics** (new
|
|
42
|
+
`unicode_fol_kit.hol` subpackage) — Benzmüller-style shallow semantical embeddings,
|
|
43
|
+
emitted as complete problem files for an external prover (the toolkit emits; it does
|
|
44
|
+
not run Leo-III / Satallax / Sledgehammer, and FOL / FO-modal / SOL are undecidable, so
|
|
45
|
+
emission means "a sound problem a prover *may* discharge", never "decided"):
|
|
46
|
+
- `hol.isabelle_modal.to_isabelle_modal` — a **real, loadable** Isabelle/HOL theory
|
|
47
|
+
(`theory … imports Main begin … end`, all lifted operators, frame + domain axioms,
|
|
48
|
+
the formula lifted into the embedding, a real `lemma`) for the **full modal family**:
|
|
49
|
+
alethic, epistemic/doxastic over **agent-indexed** relations, deontic, temporal.
|
|
50
|
+
Replaces the old alethic-only skeleton that emitted the lemma inside a comment.
|
|
51
|
+
- `hol.thf_modal.to_thf_modal_full` — the full-modal-family TPTP **THF** export
|
|
52
|
+
(agent-indexed epistemic/doxastic, deontic, temporal), extending the alethic-only
|
|
53
|
+
`qml.to_thf_modal`; self-contained (every relation the macro block references is declared).
|
|
54
|
+
- `hol.classical` (FOL + MSFOL), `hol.manyvalued` (K3 / LP, cross-checked against the
|
|
55
|
+
three-valued evaluator), `hol.secondorder` (native HOL predicate quantification),
|
|
56
|
+
`hol.intuitionistic` (Gödel–McKinsey–Tarski → S4 → HOL, cross-checked against
|
|
57
|
+
`int_valid`) — each → THF and Isabelle.
|
|
58
|
+
- **First-class agent terms for epistemic/doxastic operators.** The `agent` of
|
|
59
|
+
`Knows` / `Believes` is now a **term** (Variable or Constant), a structural child, so
|
|
60
|
+
it is reached by `free_variables` / substitution / β-reduction and can be a quantified
|
|
61
|
+
variable — `∀x (Student(x) → K_x φ)` (a quantified subject, "every student who…")
|
|
62
|
+
finally works: the agent is the bound `x`, not a baked-in relation-name suffix.
|
|
63
|
+
- The Kripke evaluator uses **per-agent** accessibility relations (one agent can know
|
|
64
|
+
what another does not); object quantifiers ground a bound agent before the modality
|
|
65
|
+
is reached.
|
|
66
|
+
- The first-order shallow embedding emits an **agent-indexed ternary** relation
|
|
67
|
+
`Rk(agent, w, v)` / `Rb(agent, w, v)`, so a bound agent genuinely quantifies over
|
|
68
|
+
agents (epistemic/doxastic relations are plain `K` — no frame axioms yet).
|
|
69
|
+
- Parser convention: a free `K_a` is a *named* agent (→ Constant); an agent bound by an
|
|
70
|
+
enclosing quantifier (`K_x` under `∀x`) stays a Variable. A bare string passed to the
|
|
71
|
+
constructor is coerced to a Constant (backward compatible).
|
|
72
|
+
|
|
73
|
+
### Fixed
|
|
74
|
+
|
|
75
|
+
- **HOL/THF/Isabelle exporters: distinct symbols can no longer collapse to one name.**
|
|
76
|
+
A de-colliding resolver (`_ThfNames` / `_IsaNames` / the second-order and classical
|
|
77
|
+
resolvers) guarantees each distinct `(kind, name, arity)` symbol gets a unique emitted
|
|
78
|
+
functor / `consts`, and a predicate used at two arities is two symbols. Fixes a
|
|
79
|
+
**soundness hole** where a non-valid formula could be emitted as valid — e.g. `□Ab →
|
|
80
|
+
□ab` collapsing to the tautology `□ab → □ab` (also in the shipped `qml.to_thf_modal`)
|
|
81
|
+
— and the duplicate / ill-typed declarations that made `to_isabelle_so` / the modal
|
|
82
|
+
Isabelle theories non-loadable.
|
|
83
|
+
- **`to_isabelle_modal` is now the real exporter everywhere.** The top-level
|
|
84
|
+
`to_isabelle_modal` (and `fol.qml.to_isabelle_modal`) delegate to the complete
|
|
85
|
+
`unicode_fol_kit.hol.isabelle_modal` implementation (a loadable theory with a genuine
|
|
86
|
+
`lemma`), instead of the old alethic-only skeleton that emitted the lemma in a comment.
|
|
87
|
+
- **`substitute` is now capture-avoiding for a re-binding quantifier.** Substituting a
|
|
88
|
+
`Variable` (as `satisfies_modal` does when grounding an object quantifier) no longer
|
|
89
|
+
leaks past an inner quantifier that re-binds the same name: `substitute(∃x A(x), x, a)`
|
|
90
|
+
now correctly returns `∃x A(x)` unchanged. This fixes a **soundness bug in the modal
|
|
91
|
+
evaluator**, where a shadowed quantifier (e.g. `∀x ∃x A(x)`, also under modalities)
|
|
92
|
+
evaluated to the wrong truth value. (The `Lambda` branch already stopped at a rebinding
|
|
93
|
+
binder; the `Quantifier` / `SortedQuantifier` branches now do too.)
|
|
94
|
+
- **QML rejects an unknown `mode`.** `qml_translate` / `qml_is_valid` / `qml_equivalent`
|
|
95
|
+
raise `ValueError` on an unrecognised domain regime (e.g. a mis-capitalised
|
|
96
|
+
`'Increasing'`) instead of silently treating it as constant-domain and returning a
|
|
97
|
+
wrong validity verdict — matching the existing `frame` validation.
|
|
98
|
+
- **THF export: `possibilist` now emits the `const_dom` axiom.** Since the FO embedding
|
|
99
|
+
treats `possibilist` as a constant domain, the THF export does too (its actualist
|
|
100
|
+
`mforall`/`mexists` macros would otherwise model a varying domain), keeping the two
|
|
101
|
+
embeddings in agreement.
|
|
102
|
+
- **THF export: `=` / `≠` are now uninterpreted predicates, not primitive HOL identity.**
|
|
103
|
+
`to_thf_modal` previously rendered equality as rigid HOL `=`, diverging from
|
|
104
|
+
`satisfies_modal` and the FO embedding (which key `=` / `≠` as ordinary
|
|
105
|
+
world-relativized predicates) — so e.g. `∀x. x=x` was a THF theorem but
|
|
106
|
+
Kripke-falsifiable. All three embeddings now agree.
|
|
107
|
+
|
|
108
|
+
### Internal
|
|
109
|
+
|
|
110
|
+
- **Retired the parser-equivalence oracle.** The registry-assembled parser was pinned
|
|
111
|
+
during migration by a byte-for-byte equivalence test against the legacy hand-written
|
|
112
|
+
per-mode `.lark` grammars + `*Transformer` classes. With that equivalence long
|
|
113
|
+
established, the reference pipeline was removed: the six per-mode grammar files
|
|
114
|
+
(`fol`/`msfol`/`msfl`/`fl`/`modal`/`so.lark`) and the legacy transformer classes are
|
|
115
|
+
gone; the registry self-assembly + grammar-structure guards remain. Only
|
|
116
|
+
`terminals.lark` survives (imported by the generated grammar at runtime).
|
|
117
|
+
- **Shared symbol-name de-collision** (`fol/_symbol_names.py`): the `dedupe` helper and
|
|
118
|
+
the THF/Isabelle resolver, previously copy-pasted across the exporters, are now one
|
|
119
|
+
`SymbolNames` base + `dedupe` used by the THF, Isabelle, classical, and second-order
|
|
120
|
+
exporters.
|
|
121
|
+
- **Agent token parsing reuses the scope pass.** The epistemic/doxastic agent is parsed
|
|
122
|
+
as a Variable and resolved to bound-Variable / free-Constant by `resolve_agent_variables`
|
|
123
|
+
(mirroring `resolve_lambda_scope`), instead of re-deciding variable-vs-constant with a
|
|
124
|
+
hand-copied lexer regex.
|
|
125
|
+
- **Adversarial audit of the proof checkers.** A multi-agent audit ran independent
|
|
126
|
+
oracles against every accepted proof/derivation of the Fitch and sequent checkers
|
|
127
|
+
across all logics (~75 hand-built adversarial constructions plus >1M fuzzed cases)
|
|
128
|
+
and found **no soundness hole**. Follow-up hardening from the audit's coverage
|
|
129
|
+
findings: added regression tests pinning the `verify_proof` robustness guards (a
|
|
130
|
+
clean `ProofResult(ok=False)` instead of a crash on a non-`Line` premise or subproof
|
|
131
|
+
assumption) and the mixed quantifier-spelling normalisation (`'forall'` vs `∀`); and
|
|
132
|
+
extended the sequent test corpus so the randomised mutation / Z3 audit now also
|
|
133
|
+
exercises `Cut`, weakening, contraction, `∨L`, `↔L`/`↔R`, `∃L`, and `⊕L`/`⊕R`.
|
|
134
|
+
- **Independent differential test harnesses promoted into the committed suite**, so the
|
|
135
|
+
checkers are cross-checked against oracles *other* than the ones they use internally:
|
|
136
|
+
- the alethic modal Fitch checker against brute-force Kripke-frame enumeration
|
|
137
|
+
(`tests/test_modal_differential.py`) — independent of its standard-translation/Z3
|
|
138
|
+
path, covering the K/T/S4/S5 frame-sensitivity facts;
|
|
139
|
+
- the second-order sequent rules against `satisfies_so` (`so_valid_tiny`) under a
|
|
140
|
+
randomised mutation audit (Z3 cannot evaluate second-order nodes);
|
|
141
|
+
- the object-level eigenvariable freshness condition (`∀R`/`∃L`) under randomised
|
|
142
|
+
fresh/non-fresh fuzzing.
|
|
143
|
+
|
|
144
|
+
## [0.6.0] - 2026-06-27
|
|
145
|
+
|
|
146
|
+
A large reasoning-and-interoperability release: a Fitch natural-deduction checker and
|
|
147
|
+
backtracking prover, a Gentzen **LK** sequent calculus (with second-order rules) and an
|
|
148
|
+
intuitionistic **LJ** calculus, analytic tableaux, a finite model finder, truth tables,
|
|
149
|
+
reverse importers for TPTP / Prover9 / Z3-SMT-LIB, intuitionistic Kripke semantics, and
|
|
150
|
+
formula verbalization. All additive.
|
|
151
|
+
|
|
152
|
+
### Added
|
|
153
|
+
|
|
154
|
+
- **Fitch-style natural-deduction proof checker** (`unicode_fol_kit.atp.fitch`) —
|
|
155
|
+
`Proof` / `Subproof` / `Line` / `Justification` proof objects (frozen, hashable,
|
|
156
|
+
JSON-serialisable) plus `check_proof` / `verify_proof`, all re-exported at the
|
|
157
|
+
package top level. The checker is *sound*: it returns `True` only when every
|
|
158
|
+
line genuinely follows by the cited rule and the proof's premises entail its
|
|
159
|
+
conclusion; `verify_proof` reports the certified sequent and the first failing
|
|
160
|
+
line with a reason.
|
|
161
|
+
- **Classical FOL / MSFOL** (`logic="fol"`/`"msfol"`) is checked by a syntactic
|
|
162
|
+
rule table: the connective rules (`∧I`/`∧E`, `∨I`/`∨E`, `→I`/`→E`, `↔I`/`↔E`,
|
|
163
|
+
`¬I`, `⊥I`/`⊥E`, `¬E` double-negation, `RAA`, `Reit`), the quantifier rules
|
|
164
|
+
(`∀I`/`∀E`, `∃I`/`∃E`) with the eigenvariable side-conditions enforced via a
|
|
165
|
+
capture-avoiding substitution, and equality (`=I`/`=E`, certified against Z3).
|
|
166
|
+
Citation accessibility is enforced (no reaching into a closed sibling
|
|
167
|
+
subproof) and discharge rules are checked against the proof's *open
|
|
168
|
+
assumptions*. `⊥` is the reserved logical constant `FALSUM`.
|
|
169
|
+
- **Three-valued K3 / LP** (`logic="K3"`/`"LP"`) certify each step against the
|
|
170
|
+
many-valued decision procedure (`semantics.manyvalued.entails`), so the
|
|
171
|
+
paraconsistency facts hold: LP rejects modus ponens, the disjunctive
|
|
172
|
+
syllogism, and explosion; K3 has no zero-premise theorems. Propositional
|
|
173
|
+
fragment.
|
|
174
|
+
- **Modal family** (`logic="K"`/`"T"`/`"S4"`/`"S5"`) certifies each step by the
|
|
175
|
+
standard translation to FOL plus the frame axioms, decided by Z3. Knowledge
|
|
176
|
+
(`Knows`, S5) is factive; belief (`Believes`, KD45) and obligation
|
|
177
|
+
(`Obligatory`, KD) are not. Propositional fragment; temporal and quantified
|
|
178
|
+
modal input are rejected.
|
|
179
|
+
- **Rendering** — `render_fitch` (Unicode/ASCII scope bars, line-number gutter,
|
|
180
|
+
justification column; also `proof.to_fitch()`) and `render_latex_fitch`
|
|
181
|
+
(self-contained LaTeX `array`; also `proof.to_latex_fitch()`).
|
|
182
|
+
- Tested with hand-derived proofs per rule, soundness guards for the broken
|
|
183
|
+
cases, and a randomised audit that checks every accepted proof line-by-line
|
|
184
|
+
against the Z3 / resolution oracles.
|
|
185
|
+
- **Gentzen sequent-calculus checker** (`unicode_fol_kit.atp.sequent`) — a
|
|
186
|
+
two-sided **LK** derivation checker re-exported at the package top level:
|
|
187
|
+
`Sequent` / `Derivation` / `Comprehension` / `SequentResult`, the helpers
|
|
188
|
+
`sequent` / `derive` / `axiom`, and `check_sequent_proof` / `verify_sequent_proof`
|
|
189
|
+
/ `render_sequent_proof`. A sequent `Γ ⊢ Δ` (multisets, read `⋀Γ → ⋁Δ`) is
|
|
190
|
+
derived by a tree of rules; the checker verifies each step.
|
|
191
|
+
- Rules: `Ax`, structural `WL`/`WR`/`CL`/`CR`/`Cut`, the connective rules
|
|
192
|
+
(`¬`, `∧`, `∨`, `→`, `↔`, each L and R), the first-order quantifier rules
|
|
193
|
+
(`∀L`/`∀R`, `∃L`/`∃R`, with the eigenvariable condition on `∀R`/`∃L`), and the
|
|
194
|
+
**second-order** rules `∀²L`/`∀²R`, `∃²L`/`∃²R`. `∀²L`/`∃²R` instantiate a bound
|
|
195
|
+
predicate variable with a comprehension term `λx̄.ψ` (a `Comprehension`,
|
|
196
|
+
arity-checked, capture-avoiding); `∀²R`/`∃²L` use a fresh predicate
|
|
197
|
+
eigenvariable. This reaches the second-order fragment (`second_order=True`),
|
|
198
|
+
which has no first-order / SMT encoding.
|
|
199
|
+
- Sound but, for full second-order logic, necessarily **not a complete prover**
|
|
200
|
+
(second-order validity is not r.e.). Tested with hand derivations per rule,
|
|
201
|
+
soundness guards, a randomised mutation audit that re-checks every accepted
|
|
202
|
+
derivation node-by-node against Z3 (first-order fragment), and `satisfies_so`
|
|
203
|
+
spot-checks over small finite models (second-order fragment).
|
|
204
|
+
- **Analytic tableaux** (`unicode_fol_kit.atp.tableau`) — `is_valid_tableau`,
|
|
205
|
+
`prove_tableau`, `tableau_closed`, and `tableau_model`, re-exported at the top
|
|
206
|
+
level. A fourth proof method (beside resolution, Fitch, and the sequent calculus):
|
|
207
|
+
the signed-free α/β/γ/δ rules, a branch closing on `φ`/`¬φ`. Decidable and complete
|
|
208
|
+
for the propositional fragment; first-order γ-instantiation is bounded (`max_terms`
|
|
209
|
+
/ `max_steps`). An *open* branch is returned as a countermodel by `tableau_model`.
|
|
210
|
+
- **Finite model finder** (`unicode_fol_kit.semantics.modelfinder`) — `find_model`,
|
|
211
|
+
`find_countermodel`, `is_satisfiable_finite`, and `is_valid_finite`. Brute-force
|
|
212
|
+
enumeration of finite `Structure`s (domain `1..max_size`) checked with the Tarskian
|
|
213
|
+
evaluator — the Mace4-style partner of the provers (a valid entailment has no
|
|
214
|
+
countermodel; an invalid one usually a small finite one). Bounded by
|
|
215
|
+
`max_candidates`.
|
|
216
|
+
- **Truth tables** (`unicode_fol_kit.semantics.truthtable`) — `truth_table` returning
|
|
217
|
+
a `TruthTable` (Markdown `render`, `is_tautology`/`is_contradiction`/`is_satisfiable`),
|
|
218
|
+
plus `is_tautology` / `is_contradiction` / `is_satisfiable_tt`, over **classical**,
|
|
219
|
+
Kleene **K3**, and Priest **LP** value sets (cross-checked against Z3 for classical).
|
|
220
|
+
- **Intuitionistic propositional logic** (`unicode_fol_kit.semantics.intuitionistic`) —
|
|
221
|
+
`IntKripkeModel` with monotone Kripke `forces`, and `int_valid` / `int_countermodel`
|
|
222
|
+
that decide intuitionistic validity by Kripke-model search (the logic has the
|
|
223
|
+
finite-model property). Excluded middle, double-negation elimination, and Peirce's
|
|
224
|
+
law are reported invalid with explicit countermodels; every intuitionistic validity
|
|
225
|
+
is also classically valid (cross-checked).
|
|
226
|
+
- **Intuitionistic sequent calculus LJ** (`unicode_fol_kit.atp.lj`) — `check_lj_proof`
|
|
227
|
+
/ `verify_lj_proof`, re-exported at the top level. Gentzen **LJ** is the LK calculus
|
|
228
|
+
(it reuses the same `Sequent` / `Derivation` data model) restricted to **at most one
|
|
229
|
+
succedent formula** — the change that makes excluded middle / double-negation
|
|
230
|
+
elimination / Peirce's law underivable. Rules: `Ax`, structural `WL`/`WR`/`CL`/`Cut`,
|
|
231
|
+
`¬`/`∧`/`→`/`↔` (L and R), the split disjunction-right `∨R1`/`∨R2` and `∨L`, and the
|
|
232
|
+
quantifier rules `∀L`/`∀R`, `∃L`/`∃R`. Accepted derivations are cross-checked against
|
|
233
|
+
the intuitionistic Kripke decision procedure and classical Z3 validity.
|
|
234
|
+
- **Verbalization** (`unicode_fol_kit.fol.verbalize`) — `to_english`, an English
|
|
235
|
+
paraphrase of a formula (a readability aid, not a parse inverse).
|
|
236
|
+
- **Fitch proof *searcher*** (`unicode_fol_kit.atp.fitch_search`) — `find_fitch_proof`,
|
|
237
|
+
`fitch_prove`, and `is_valid_fitch`, re-exported at the package top level. A
|
|
238
|
+
goal-directed, **iterative-deepening backtracking** search over the classical
|
|
239
|
+
propositional + first-order natural-deduction rules (introduction rules, ∨/∃
|
|
240
|
+
elimination by case split, backward chaining, ex falso, and reductio/RAA — which
|
|
241
|
+
makes it complete for the propositional fragment). It builds an actual `Proof`
|
|
242
|
+
that is re-validated by `check_proof` before being returned, so it is **sound by
|
|
243
|
+
construction**: a search/assembly bug can only make it fail to find a proof, never
|
|
244
|
+
return an unsound one. Like the resolution prover it is sound but, under its depth
|
|
245
|
+
bound, incomplete (`None`/`False` = "not found within `max_depth`"). Tested with
|
|
246
|
+
curated theorems/non-theorems and a randomised cross-check that every found proof
|
|
247
|
+
is Z3-valid.
|
|
248
|
+
- **Reverse importers for TPTP, Prover9, and Z3/SMT-LIB** — the inverses of
|
|
249
|
+
`to_tptp` / `to_prover9` / `to_z3`, all re-exported at the package top level:
|
|
250
|
+
- **TPTP** (`unicode_fol_kit.fol.tptp_input`): `parse_tptp_formula` (one bare
|
|
251
|
+
FOF/CNF formula → `Node`), `parse_tptp` (a whole problem → a list of
|
|
252
|
+
`TptpFormula(name, role, formula)`), and `load_tptp` (a `.p`/`.tptp` file), via
|
|
253
|
+
a dedicated Lark grammar. Round-trips `to_tptp`; `%` and `/* */` comments are
|
|
254
|
+
ignored; predicates are re-capitalised (TPTP lowercases them); typed
|
|
255
|
+
`tff`/`thf` and `include` are out of scope.
|
|
256
|
+
- **Prover9/LADR** (`unicode_fol_kit.fol.prover9_input`): `parse_prover9`,
|
|
257
|
+
following `set(prolog_style_variables)` to match `to_prover9`'s output (a
|
|
258
|
+
trailing `.` is accepted). `Xor` round-trips to its `(a|b) & -(a&b)` desugaring.
|
|
259
|
+
- **Z3** (`unicode_fol_kit.atp.z3_input`): `from_z3` (a `z3.ExprRef` → `Node`)
|
|
260
|
+
and `parse_smtlib` / `load_smtlib` (SMT-LIB2 via Z3's own parser). Conversion is
|
|
261
|
+
meaning-preserving (Z3 collapses variables/constants/numbers onto one
|
|
262
|
+
uninterpreted sort, so a free variable returns as a `Constant`).
|
|
263
|
+
- Tested by round-trip over random formulas (`parse(node.to_X()) == node`) for
|
|
264
|
+
TPTP/Prover9 and by logical equivalence (`is_valid(Iff(node, from_z3(node.to_z3())))`)
|
|
265
|
+
for Z3, plus curated problem-file and SMT-LIB cases.
|
|
266
|
+
|
|
267
|
+
## [0.5.2] - 2026-06-26
|
|
268
|
+
|
|
269
|
+
### Added
|
|
270
|
+
|
|
271
|
+
- **Predicate-aligned string match** (`unicode_fol_kit.eval.predicate_match`) —
|
|
272
|
+
`match_predicates`, `formulas_are_matched_identical`, and
|
|
273
|
+
`formulas_are_identical`, re-exported at the package top level. A lexical
|
|
274
|
+
(string-level) evaluation notion for NL→FOL: `match_predicates` greedily
|
|
275
|
+
renames each predicate/function symbol in a predicted formula to the
|
|
276
|
+
lexically-closest symbol in the reference (by **normalised Levenshtein
|
|
277
|
+
distance**, accepting matches at or below a `max_norm_distance` threshold,
|
|
278
|
+
default `0.6`), so a structurally-correct answer that merely chose different
|
|
279
|
+
predicate names is not penalised. `formulas_are_identical` is the plain
|
|
280
|
+
whitespace- and case-insensitive string equality; `formulas_are_matched_identical`
|
|
281
|
+
combines the two (realign predicates, then compare). This is **complementary**
|
|
282
|
+
to the AST-level `exact_match`: the canonical match quotients out α-renaming /
|
|
283
|
+
commutativity / associativity / double negation but treats different predicate
|
|
284
|
+
names as a mismatch, whereas this matcher quotients out predicate-name (and
|
|
285
|
+
whitespace/case) differences but not the structural rewrites — the two are
|
|
286
|
+
typically reported as separate metrics. The Levenshtein distance is computed in
|
|
287
|
+
pure Python, so **no new dependency** is introduced; the matcher is
|
|
288
|
+
parser-independent and also applies to raw, not-yet-parseable model output.
|
|
289
|
+
|
|
290
|
+
## [0.5.1] - 2026-06-24
|
|
291
|
+
|
|
292
|
+
### Added
|
|
293
|
+
|
|
294
|
+
- **`check_logical_entailment_vampire`** — entailment checking via the
|
|
295
|
+
[Vampire](https://vprover.github.io/) theorem prover, a TPTP-based companion to
|
|
296
|
+
the existing Prover9 backend. Premises are emitted as TPTP `axiom`s and the
|
|
297
|
+
conclusion as a `conjecture`; the path to the Vampire executable is passed as
|
|
298
|
+
the `vampire_path` argument, and a `SZS status Theorem` result means the
|
|
299
|
+
entailment holds. Classical FOL only (the same fragment `to_tptp` supports).
|
|
300
|
+
Pass `use_wsl=True` to drive a Linux Vampire installed in WSL from a Windows
|
|
301
|
+
host (Vampire is launched via `wsl.exe`, with automatic `wslpath` translation of
|
|
302
|
+
the temp-file path).
|
|
303
|
+
|
|
304
|
+
## [0.5.0] - 2026-06-24
|
|
305
|
+
|
|
306
|
+
Adds an NL→FOL **evaluation** toolkit and broad **non-classical logic** coverage —
|
|
307
|
+
modal/temporal/epistemic/deontic logic with Kripke semantics, three-valued
|
|
308
|
+
(Kleene/Priest) logic, and second-order quantification with finite-model
|
|
309
|
+
semantics. All additive; no breaking changes.
|
|
310
|
+
|
|
311
|
+
### Added
|
|
312
|
+
|
|
313
|
+
- **`unicode_fol_kit.eval`** — `canonicalize` / `exact_match` (a fair "canonical
|
|
314
|
+
exact match" that quotients out bound-variable renaming, commutativity/
|
|
315
|
+
associativity, operand duplication, and double negation while staying logically
|
|
316
|
+
equivalent), and `validate` / `is_wellformed` / `validate_text` /
|
|
317
|
+
`ValidationReport` (free variables, inconsistent predicate/function arity,
|
|
318
|
+
leftover lambda nodes, parseability of raw model output).
|
|
319
|
+
- **Modal / temporal / epistemic / deontic logic** (`MSFLParser(modal=True)`):
|
|
320
|
+
node classes `Box`, `Diamond`, `Knows`, `Believes`, `Always`, `Eventually`,
|
|
321
|
+
`Next`, `Until`, `Obligatory`, `Permitted` with surface syntax `□ ◇`, `K_a` /
|
|
322
|
+
`B_a`, `Ⓖ Ⓕ Ⓝ Ⓤ`, `Ⓞ Ⓟ`. Kripke-model semantics (`KripkeModel`,
|
|
323
|
+
`satisfies_modal`, `reflexive_transitive_closure`) and a relational
|
|
324
|
+
`standard_translation()` to classical FOL so Z3/resolution can decide modal
|
|
325
|
+
validity. Propositional/ground (v1).
|
|
326
|
+
- **Many-valued logic** (`unicode_fol_kit.semantics.manyvalued`): three-valued
|
|
327
|
+
strong-Kleene evaluation `kleene_value` over {0, ½, 1}, and `is_valid` /
|
|
328
|
+
`is_satisfiable` / `entails` with selectable designated values for Kleene
|
|
329
|
+
**K3** (`{1}`) and Priest **LP** (`{½, 1}`, paraconsistent). `kleene_value` /
|
|
330
|
+
`DESIGNATED` are also re-exported at the package top level.
|
|
331
|
+
- **Second-order / monadic-second-order quantification** (`MSFLParser(second_order=True)`):
|
|
332
|
+
`SecondOrderQuantifier` (`∀P` / `∃P`, arity inferred from the body) with
|
|
333
|
+
finite-model semantics (`satisfies_so`) that enumerates relations over a finite
|
|
334
|
+
domain. Higher-order *terms* remain available via the existing lambda layer;
|
|
335
|
+
full HOL types are out of scope.
|
|
336
|
+
- **LaTeX import** — `parse_latex()` reads a LaTeX-math formula (the inverse of
|
|
337
|
+
`to_latex()`) and `latex_to_unicode()` does the LaTeX→Unicode translation alone;
|
|
338
|
+
accepts the exact `to_latex()` output (round-trips) and common hand-written synonyms.
|
|
339
|
+
|
|
340
|
+
### Internal
|
|
341
|
+
|
|
342
|
+
- **Operator registry** — operators are now fully self-describing, decoupling
|
|
343
|
+
rendering *and* parsing from the central modules:
|
|
344
|
+
- *Rendering:* each operator registers its glyph, LaTeX markup, precedence, and
|
|
345
|
+
fixity via `register_operator()`; the Unicode and LaTeX renderers are driven
|
|
346
|
+
generically from the registry (no per-operator branches, no hand-maintained
|
|
347
|
+
dispatch tables).
|
|
348
|
+
- *Parsing:* each operator also registers its grammar fragment + transform via
|
|
349
|
+
`register_parser_op()`. `MSFLParser` now assembles BOTH the Lark grammar and
|
|
350
|
+
the transformer for every mode (FOL/MSFOL/MSFL/FL/modal/second-order) from the
|
|
351
|
+
registry — there is no longer a hand-written per-mode transformer or a
|
|
352
|
+
hand-loaded `.lark` grammar on the runtime path.
|
|
353
|
+
- Output and parsed ASTs are byte-identical to before (guarded by a
|
|
354
|
+
legacy-vs-registry equivalence test across a 190-formula × 6-mode corpus).
|
|
355
|
+
Adding an operator — or a whole new logic — is now a self-contained registry
|
|
356
|
+
entry in the operator's own module, with no edit to the renderers, the parser,
|
|
357
|
+
or any shared grammar file.
|
|
358
|
+
- **Hardening of the new evaluators.**
|
|
359
|
+
- The three-valued enumeration (`is_valid` / `is_satisfiable` / `entails`) now
|
|
360
|
+
scores each assignment with a compiled evaluator built once from the formula
|
|
361
|
+
(no per-assignment AST walk or atom re-rendering), and refuses to start an
|
|
362
|
+
enumeration above `manyvalued.MAX_MODELS` rather than hanging.
|
|
363
|
+
- Second-order `satisfies_so` refuses a `∀P` / `∃P` whose `2 ** (n ** k)`
|
|
364
|
+
relation space exceeds `secondorder.MAX_RELATIONS`, with a clear error.
|
|
365
|
+
- Added seeded, randomized cross-checks: the compiled three-valued path against
|
|
366
|
+
the reference `kleene_value` on every assignment; strong-Kleene algebraic
|
|
367
|
+
identities and the K3-vs-LP headline facts; second-order `∀P φ ≡ ¬∃P ¬φ`
|
|
368
|
+
duality and the agreement of `satisfies_so`'s classical core with the
|
|
369
|
+
first-order Tarski evaluator; render→parse round-trips over random FOL, modal,
|
|
370
|
+
Łukasiewicz, and second-order formulas; and a whole-tree `tree_str` / `to_dot`
|
|
371
|
+
coverage check over every node type.
|
|
372
|
+
- Łukasiewicz-algebra cross-checks for the fuzzy evaluator (strong/weak
|
|
373
|
+
De Morgan, double negation, the residuum `a → b ≡ ¬a ⊕ b`, and the defining
|
|
374
|
+
adjunction `a ⊗ b ≤ c ⟺ a ≤ b → c`) over random + boundary-grid valuations.
|
|
375
|
+
- Eval cross-checks against the independent Z3 oracle: `canonicalize` is
|
|
376
|
+
equivalence-preserving, `exact_match` absorbs the rewrites it should and never
|
|
377
|
+
merges Z3-inequivalent formulas, and `validate` flags free variables, arity
|
|
378
|
+
clashes, and leftover lambdas.
|
|
379
|
+
- A README example runner executes every `python` block in the docs (cumulative
|
|
380
|
+
namespace) so the documentation stays in lock-step with the code.
|
|
381
|
+
|
|
382
|
+
## [0.4.0] - 2026-06-23
|
|
383
|
+
|
|
384
|
+
A large feature release adding model-theoretic and many-valued semantics, an
|
|
385
|
+
in-process theorem prover, more solver back-ends, and lambda/normal-form tooling,
|
|
386
|
+
plus a set of correctness fixes. **Includes one breaking change** (see *Changed*).
|
|
387
|
+
|
|
388
|
+
### Added
|
|
389
|
+
|
|
390
|
+
- **Tarskian model theory** (`unicode_fol_kit.semantics.tarski`): define a
|
|
391
|
+
`Structure` (a "world" with a domain of individuals and interpretations of
|
|
392
|
+
constants, functions, predicates, and — for MSFOL — sorts) and compute a
|
|
393
|
+
formula's truth value with `satisfies()` / `models()` / `term_value()`.
|
|
394
|
+
Equality is built in; sorted quantifiers range over their sort universe.
|
|
395
|
+
- **Łukasiewicz fuzzy evaluator** (`fuzzy_evaluate`): the truth degree in [0, 1]
|
|
396
|
+
of an FL/MSFL formula under a valuation (`∀` = inf, `∃` = sup).
|
|
397
|
+
- **Fuzzy satisfiability / validity** via Z3 reals: `fuzzy_is_satisfiable`,
|
|
398
|
+
`fuzzy_is_valid`, `fuzzy_get_model`, `degree_expr`.
|
|
399
|
+
- **Arithmetic-aware Z3 translation**: `to_z3_arith`, `is_satisfiable_arith`,
|
|
400
|
+
`is_valid_arith`, `get_model_arith` interpret `+ - * /` and the comparisons
|
|
401
|
+
over Z3 reals/integers (the default `to_z3` keeps them uninterpreted).
|
|
402
|
+
- **Built-in first-order resolution prover** (`unicode_fol_kit.atp.resolution`):
|
|
403
|
+
`prove`, `is_valid_resolution`, `to_clauses`, `refute` — sound entailment and
|
|
404
|
+
validity checking in-process, without an external prover. Deliberately
|
|
405
|
+
incomplete under a step bound (never reports a non-theorem as proved);
|
|
406
|
+
`=` is treated as an uninterpreted predicate.
|
|
407
|
+
- **Lambda tooling**: `eliminate_lambdas` (beta-eta normalise and verify
|
|
408
|
+
lambda-free), `reduce_trace`, `beta_reduce_step`, `has_lambdas`.
|
|
409
|
+
- **Normal forms**: `to_dnf` (equivalence-preserving) and `to_tseitin_cnf`
|
|
410
|
+
(equisatisfiable, avoids the distributive blow-up).
|
|
411
|
+
- **Robinson unification**: `unify` (most general unifier with occurs-check) and
|
|
412
|
+
`apply_subst`.
|
|
413
|
+
- **Command-line interface**: `python -m unicode_fol_kit "<formula>" --mode … --to …`.
|
|
414
|
+
- **Typing**: a `py.typed` marker (PEP 561).
|
|
415
|
+
- **AST helper**: `Node.map_children`, the single structural-recursion engine.
|
|
416
|
+
|
|
417
|
+
### Changed
|
|
418
|
+
|
|
419
|
+
- **BREAKING — AST nodes are now frozen dataclasses.** Every node is immutable
|
|
420
|
+
and **hashable**, so nodes can be put in sets, used as dict keys, and
|
|
421
|
+
deduplicated.
|
|
422
|
+
- **BREAKING — `Function.args` and `Atom.args` are now `tuple`s, not `list`s.**
|
|
423
|
+
Construction stays lenient: a list passed to the constructor is coerced to a
|
|
424
|
+
tuple, so `Atom("P", [x])` still works and `node == node` comparisons are
|
|
425
|
+
unaffected. Code that relied on `.args` being a *list* (in-place mutation,
|
|
426
|
+
`isinstance(node.args, list)`, or comparing `node.args == [...]`) must switch
|
|
427
|
+
to tuples.
|
|
428
|
+
|
|
429
|
+
### Fixed
|
|
430
|
+
|
|
431
|
+
- `Xor.to_tptp` emitted `~|` (TPTP **NOR**); now emits `<~>` (correct XOR /
|
|
432
|
+
non-equivalence).
|
|
433
|
+
- TPTP arithmetic comparisons (`<`, `>`, `≤`, `≥`) are now emitted as prefix
|
|
434
|
+
dollar-word predicates (`$less(a, b)`), not as invalid infix expressions.
|
|
435
|
+
- Prover9 export: quantified variables are uppercased to match the emitted
|
|
436
|
+
`set(prolog_style_variables)`; nullary predicates render as bare propositional
|
|
437
|
+
atoms instead of the invalid `P()`.
|
|
438
|
+
- `to_latex` escapes the underscore in `c_`-prefixed constants (otherwise read as
|
|
439
|
+
a LaTeX subscript).
|
|
440
|
+
- Prover9 entailment: the temporary input file is no longer leaked when the
|
|
441
|
+
`prover9_path` is invalid (now cleaned up in a `finally`).
|
|
442
|
+
- Several README inaccuracies (clone URL, "three" vs "four" parser modes, the
|
|
443
|
+
exception class raised on mixing same-level connectives, the `Quantifier`
|
|
444
|
+
AST-table annotation), and the `formulas_are_equivalent` / `is_valid`
|
|
445
|
+
docstrings.
|
|
446
|
+
|
|
447
|
+
### Documentation
|
|
448
|
+
|
|
449
|
+
- Clarified that `to_fol` / `to_msfol` is a classical **Boolean projection**
|
|
450
|
+
(the strong and weak Łukasiewicz connectives both collapse to `And`/`Or`), not
|
|
451
|
+
a fuzzy-preserving translation — use `fuzzy_evaluate` / the fuzzy Z3 solver for
|
|
452
|
+
many-valued degrees.
|
|
453
|
+
|
|
454
|
+
### Internal
|
|
455
|
+
|
|
456
|
+
- Refactored the duplicated structural recursions (`free_variables`,
|
|
457
|
+
substitution, beta/eta reduction, scope resolution, `to_msfol`/`_relativize`,
|
|
458
|
+
term substitution) onto the shared `Node.map_children` / `_child_nodes`
|
|
459
|
+
helpers, removing the per-node `isinstance` chains while preserving the
|
|
460
|
+
binder-aware special cases and the public `TypeError` contracts.
|
|
461
|
+
|
|
462
|
+
## [0.3.1] - earlier
|
|
463
|
+
|
|
464
|
+
- LaTeX export, normal forms, Horn check, Z3 models, traversal API, Graphviz export.
|
|
465
|
+
|
|
466
|
+
## [0.3.0] - earlier
|
|
467
|
+
|
|
468
|
+
- `to_unicode_str()` with parser round-trip.
|
|
469
|
+
|
|
470
|
+
## [0.2.1] - earlier
|
|
471
|
+
|
|
472
|
+
- README patch release.
|