unicode-fol-kit 0.5.2__tar.gz → 0.6.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.5.2 → unicode_fol_kit-0.6.0}/CHANGELOG.md +123 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/PKG-INFO +231 -1
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/README.md +230 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/pyproject.toml +1 -1
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/__init__.py +35 -1
- unicode_fol_kit-0.6.0/unicode_fol_kit/atp/__init__.py +47 -0
- unicode_fol_kit-0.6.0/unicode_fol_kit/atp/fitch.py +1422 -0
- unicode_fol_kit-0.6.0/unicode_fol_kit/atp/fitch_search.py +486 -0
- unicode_fol_kit-0.6.0/unicode_fol_kit/atp/lj.py +352 -0
- unicode_fol_kit-0.6.0/unicode_fol_kit/atp/sequent.py +915 -0
- unicode_fol_kit-0.6.0/unicode_fol_kit/atp/tableau.py +255 -0
- unicode_fol_kit-0.6.0/unicode_fol_kit/atp/z3_input.py +179 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/__init__.py +6 -0
- unicode_fol_kit-0.6.0/unicode_fol_kit/fol/prover9_input.py +210 -0
- unicode_fol_kit-0.6.0/unicode_fol_kit/fol/tptp_input.py +314 -0
- unicode_fol_kit-0.6.0/unicode_fol_kit/fol/verbalize.py +106 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/semantics/__init__.py +13 -0
- unicode_fol_kit-0.6.0/unicode_fol_kit/semantics/intuitionistic.py +151 -0
- unicode_fol_kit-0.6.0/unicode_fol_kit/semantics/modelfinder.py +189 -0
- unicode_fol_kit-0.6.0/unicode_fol_kit/semantics/truthtable.py +158 -0
- unicode_fol_kit-0.5.2/unicode_fol_kit/atp/__init__.py +0 -21
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/.gitignore +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/LICENSE +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/__main__.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/atp/prover9_entailment.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/atp/resolution.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/atp/vampire_entailment.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/atp/z3_arith.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/atp/z3_equivalence.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/atp/z3_fuzzy.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/atp/z3_models.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/eval/__init__.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/eval/canonical.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/eval/predicate_match.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/eval/validate.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/_fol_nodes.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/_modal_nodes.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/_msfl_nodes.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/_so_nodes.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/grammars/fl.lark +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/grammars/fol.lark +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/grammars/modal.lark +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/grammars/msfl.lark +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/grammars/msfol.lark +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/grammars/so.lark +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/grammars/terminals.lark +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/lambda_tools.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/latex_input.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/modal_translation.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/msflparser.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/naming.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/nodes.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/normalforms.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/fol/unification.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/py.typed +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/semantics/_modal_reject.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/semantics/fuzzy.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/semantics/kripke.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/semantics/manyvalued.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/semantics/secondorder.py +0 -0
- {unicode_fol_kit-0.5.2 → unicode_fol_kit-0.6.0}/unicode_fol_kit/semantics/tarski.py +0 -0
|
@@ -5,6 +5,129 @@ loosely based on [Keep a Changelog](https://keepachangelog.com/). Versioning is
|
|
|
5
5
|
semantic, but the project is pre-1.0 (alpha): a **minor** release may contain
|
|
6
6
|
breaking changes.
|
|
7
7
|
|
|
8
|
+
## [0.6.0] - 2026-06-27
|
|
9
|
+
|
|
10
|
+
A large reasoning-and-interoperability release: a Fitch natural-deduction checker and
|
|
11
|
+
backtracking prover, a Gentzen **LK** sequent calculus (with second-order rules) and an
|
|
12
|
+
intuitionistic **LJ** calculus, analytic tableaux, a finite model finder, truth tables,
|
|
13
|
+
reverse importers for TPTP / Prover9 / Z3-SMT-LIB, intuitionistic Kripke semantics, and
|
|
14
|
+
formula verbalization. All additive.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **Fitch-style natural-deduction proof checker** (`unicode_fol_kit.atp.fitch`) —
|
|
19
|
+
`Proof` / `Subproof` / `Line` / `Justification` proof objects (frozen, hashable,
|
|
20
|
+
JSON-serialisable) plus `check_proof` / `verify_proof`, all re-exported at the
|
|
21
|
+
package top level. The checker is *sound*: it returns `True` only when every
|
|
22
|
+
line genuinely follows by the cited rule and the proof's premises entail its
|
|
23
|
+
conclusion; `verify_proof` reports the certified sequent and the first failing
|
|
24
|
+
line with a reason.
|
|
25
|
+
- **Classical FOL / MSFOL** (`logic="fol"`/`"msfol"`) is checked by a syntactic
|
|
26
|
+
rule table: the connective rules (`∧I`/`∧E`, `∨I`/`∨E`, `→I`/`→E`, `↔I`/`↔E`,
|
|
27
|
+
`¬I`, `⊥I`/`⊥E`, `¬E` double-negation, `RAA`, `Reit`), the quantifier rules
|
|
28
|
+
(`∀I`/`∀E`, `∃I`/`∃E`) with the eigenvariable side-conditions enforced via a
|
|
29
|
+
capture-avoiding substitution, and equality (`=I`/`=E`, certified against Z3).
|
|
30
|
+
Citation accessibility is enforced (no reaching into a closed sibling
|
|
31
|
+
subproof) and discharge rules are checked against the proof's *open
|
|
32
|
+
assumptions*. `⊥` is the reserved logical constant `FALSUM`.
|
|
33
|
+
- **Three-valued K3 / LP** (`logic="K3"`/`"LP"`) certify each step against the
|
|
34
|
+
many-valued decision procedure (`semantics.manyvalued.entails`), so the
|
|
35
|
+
paraconsistency facts hold: LP rejects modus ponens, the disjunctive
|
|
36
|
+
syllogism, and explosion; K3 has no zero-premise theorems. Propositional
|
|
37
|
+
fragment.
|
|
38
|
+
- **Modal family** (`logic="K"`/`"T"`/`"S4"`/`"S5"`) certifies each step by the
|
|
39
|
+
standard translation to FOL plus the frame axioms, decided by Z3. Knowledge
|
|
40
|
+
(`Knows`, S5) is factive; belief (`Believes`, KD45) and obligation
|
|
41
|
+
(`Obligatory`, KD) are not. Propositional fragment; temporal and quantified
|
|
42
|
+
modal input are rejected.
|
|
43
|
+
- **Rendering** — `render_fitch` (Unicode/ASCII scope bars, line-number gutter,
|
|
44
|
+
justification column; also `proof.to_fitch()`) and `render_latex_fitch`
|
|
45
|
+
(self-contained LaTeX `array`; also `proof.to_latex_fitch()`).
|
|
46
|
+
- Tested with hand-derived proofs per rule, soundness guards for the broken
|
|
47
|
+
cases, and a randomised audit that checks every accepted proof line-by-line
|
|
48
|
+
against the Z3 / resolution oracles.
|
|
49
|
+
- **Gentzen sequent-calculus checker** (`unicode_fol_kit.atp.sequent`) — a
|
|
50
|
+
two-sided **LK** derivation checker re-exported at the package top level:
|
|
51
|
+
`Sequent` / `Derivation` / `Comprehension` / `SequentResult`, the helpers
|
|
52
|
+
`sequent` / `derive` / `axiom`, and `check_sequent_proof` / `verify_sequent_proof`
|
|
53
|
+
/ `render_sequent_proof`. A sequent `Γ ⊢ Δ` (multisets, read `⋀Γ → ⋁Δ`) is
|
|
54
|
+
derived by a tree of rules; the checker verifies each step.
|
|
55
|
+
- Rules: `Ax`, structural `WL`/`WR`/`CL`/`CR`/`Cut`, the connective rules
|
|
56
|
+
(`¬`, `∧`, `∨`, `→`, `↔`, each L and R), the first-order quantifier rules
|
|
57
|
+
(`∀L`/`∀R`, `∃L`/`∃R`, with the eigenvariable condition on `∀R`/`∃L`), and the
|
|
58
|
+
**second-order** rules `∀²L`/`∀²R`, `∃²L`/`∃²R`. `∀²L`/`∃²R` instantiate a bound
|
|
59
|
+
predicate variable with a comprehension term `λx̄.ψ` (a `Comprehension`,
|
|
60
|
+
arity-checked, capture-avoiding); `∀²R`/`∃²L` use a fresh predicate
|
|
61
|
+
eigenvariable. This reaches the second-order fragment (`second_order=True`),
|
|
62
|
+
which has no first-order / SMT encoding.
|
|
63
|
+
- Sound but, for full second-order logic, necessarily **not a complete prover**
|
|
64
|
+
(second-order validity is not r.e.). Tested with hand derivations per rule,
|
|
65
|
+
soundness guards, a randomised mutation audit that re-checks every accepted
|
|
66
|
+
derivation node-by-node against Z3 (first-order fragment), and `satisfies_so`
|
|
67
|
+
spot-checks over small finite models (second-order fragment).
|
|
68
|
+
- **Analytic tableaux** (`unicode_fol_kit.atp.tableau`) — `is_valid_tableau`,
|
|
69
|
+
`prove_tableau`, `tableau_closed`, and `tableau_model`, re-exported at the top
|
|
70
|
+
level. A fourth proof method (beside resolution, Fitch, and the sequent calculus):
|
|
71
|
+
the signed-free α/β/γ/δ rules, a branch closing on `φ`/`¬φ`. Decidable and complete
|
|
72
|
+
for the propositional fragment; first-order γ-instantiation is bounded (`max_terms`
|
|
73
|
+
/ `max_steps`). An *open* branch is returned as a countermodel by `tableau_model`.
|
|
74
|
+
- **Finite model finder** (`unicode_fol_kit.semantics.modelfinder`) — `find_model`,
|
|
75
|
+
`find_countermodel`, `is_satisfiable_finite`, and `is_valid_finite`. Brute-force
|
|
76
|
+
enumeration of finite `Structure`s (domain `1..max_size`) checked with the Tarskian
|
|
77
|
+
evaluator — the Mace4-style partner of the provers (a valid entailment has no
|
|
78
|
+
countermodel; an invalid one usually a small finite one). Bounded by
|
|
79
|
+
`max_candidates`.
|
|
80
|
+
- **Truth tables** (`unicode_fol_kit.semantics.truthtable`) — `truth_table` returning
|
|
81
|
+
a `TruthTable` (Markdown `render`, `is_tautology`/`is_contradiction`/`is_satisfiable`),
|
|
82
|
+
plus `is_tautology` / `is_contradiction` / `is_satisfiable_tt`, over **classical**,
|
|
83
|
+
Kleene **K3**, and Priest **LP** value sets (cross-checked against Z3 for classical).
|
|
84
|
+
- **Intuitionistic propositional logic** (`unicode_fol_kit.semantics.intuitionistic`) —
|
|
85
|
+
`IntKripkeModel` with monotone Kripke `forces`, and `int_valid` / `int_countermodel`
|
|
86
|
+
that decide intuitionistic validity by Kripke-model search (the logic has the
|
|
87
|
+
finite-model property). Excluded middle, double-negation elimination, and Peirce's
|
|
88
|
+
law are reported invalid with explicit countermodels; every intuitionistic validity
|
|
89
|
+
is also classically valid (cross-checked).
|
|
90
|
+
- **Intuitionistic sequent calculus LJ** (`unicode_fol_kit.atp.lj`) — `check_lj_proof`
|
|
91
|
+
/ `verify_lj_proof`, re-exported at the top level. Gentzen **LJ** is the LK calculus
|
|
92
|
+
(it reuses the same `Sequent` / `Derivation` data model) restricted to **at most one
|
|
93
|
+
succedent formula** — the change that makes excluded middle / double-negation
|
|
94
|
+
elimination / Peirce's law underivable. Rules: `Ax`, structural `WL`/`WR`/`CL`/`Cut`,
|
|
95
|
+
`¬`/`∧`/`→`/`↔` (L and R), the split disjunction-right `∨R1`/`∨R2` and `∨L`, and the
|
|
96
|
+
quantifier rules `∀L`/`∀R`, `∃L`/`∃R`. Accepted derivations are cross-checked against
|
|
97
|
+
the intuitionistic Kripke decision procedure and classical Z3 validity.
|
|
98
|
+
- **Verbalization** (`unicode_fol_kit.fol.verbalize`) — `to_english`, an English
|
|
99
|
+
paraphrase of a formula (a readability aid, not a parse inverse).
|
|
100
|
+
- **Fitch proof *searcher*** (`unicode_fol_kit.atp.fitch_search`) — `find_fitch_proof`,
|
|
101
|
+
`fitch_prove`, and `is_valid_fitch`, re-exported at the package top level. A
|
|
102
|
+
goal-directed, **iterative-deepening backtracking** search over the classical
|
|
103
|
+
propositional + first-order natural-deduction rules (introduction rules, ∨/∃
|
|
104
|
+
elimination by case split, backward chaining, ex falso, and reductio/RAA — which
|
|
105
|
+
makes it complete for the propositional fragment). It builds an actual `Proof`
|
|
106
|
+
that is re-validated by `check_proof` before being returned, so it is **sound by
|
|
107
|
+
construction**: a search/assembly bug can only make it fail to find a proof, never
|
|
108
|
+
return an unsound one. Like the resolution prover it is sound but, under its depth
|
|
109
|
+
bound, incomplete (`None`/`False` = "not found within `max_depth`"). Tested with
|
|
110
|
+
curated theorems/non-theorems and a randomised cross-check that every found proof
|
|
111
|
+
is Z3-valid.
|
|
112
|
+
- **Reverse importers for TPTP, Prover9, and Z3/SMT-LIB** — the inverses of
|
|
113
|
+
`to_tptp` / `to_prover9` / `to_z3`, all re-exported at the package top level:
|
|
114
|
+
- **TPTP** (`unicode_fol_kit.fol.tptp_input`): `parse_tptp_formula` (one bare
|
|
115
|
+
FOF/CNF formula → `Node`), `parse_tptp` (a whole problem → a list of
|
|
116
|
+
`TptpFormula(name, role, formula)`), and `load_tptp` (a `.p`/`.tptp` file), via
|
|
117
|
+
a dedicated Lark grammar. Round-trips `to_tptp`; `%` and `/* */` comments are
|
|
118
|
+
ignored; predicates are re-capitalised (TPTP lowercases them); typed
|
|
119
|
+
`tff`/`thf` and `include` are out of scope.
|
|
120
|
+
- **Prover9/LADR** (`unicode_fol_kit.fol.prover9_input`): `parse_prover9`,
|
|
121
|
+
following `set(prolog_style_variables)` to match `to_prover9`'s output (a
|
|
122
|
+
trailing `.` is accepted). `Xor` round-trips to its `(a|b) & -(a&b)` desugaring.
|
|
123
|
+
- **Z3** (`unicode_fol_kit.atp.z3_input`): `from_z3` (a `z3.ExprRef` → `Node`)
|
|
124
|
+
and `parse_smtlib` / `load_smtlib` (SMT-LIB2 via Z3's own parser). Conversion is
|
|
125
|
+
meaning-preserving (Z3 collapses variables/constants/numbers onto one
|
|
126
|
+
uninterpreted sort, so a free variable returns as a `Constant`).
|
|
127
|
+
- Tested by round-trip over random formulas (`parse(node.to_X()) == node`) for
|
|
128
|
+
TPTP/Prover9 and by logical equivalence (`is_valid(Iff(node, from_z3(node.to_z3())))`)
|
|
129
|
+
for Z3, plus curated problem-file and SMT-LIB cases.
|
|
130
|
+
|
|
8
131
|
## [0.5.2] - 2026-06-26
|
|
9
132
|
|
|
10
133
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: unicode-fol-kit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Parser and toolkit for first-order logic formulas using Unicode operators
|
|
5
5
|
Project-URL: Repository, https://github.com/fvossel/unicode-fol-kit
|
|
6
6
|
Project-URL: Issues, https://github.com/fvossel/unicode-fol-kit/issues
|
|
@@ -39,6 +39,7 @@ A Python toolkit for parsing and working with first-order logic (FOL) formulas w
|
|
|
39
39
|
- **Unicode round-trip** — `to_unicode_str()` renders any node back to a parseable Unicode formula; re-parsing in the matching mode yields a structurally equal AST
|
|
40
40
|
- **LaTeX export** — `to_latex()` renders any node as LaTeX math-mode markup, with the same precedence-aware parenthesisation as the Unicode renderer
|
|
41
41
|
- **LaTeX import** — `parse_latex()` reads a LaTeX-math formula (the inverse of `to_latex()`); `latex_to_unicode()` does the LaTeX→Unicode translation on its own
|
|
42
|
+
- **TPTP / Prover9 / SMT-LIB import** — read formulas back from the standard tools: `parse_tptp()` / `parse_tptp_formula()` / `load_tptp()` (FOF & CNF), `parse_prover9()`, and `from_z3()` / `parse_smtlib()` — the inverses of `to_tptp()` / `to_prover9()` / `to_z3()`
|
|
42
43
|
- **Normal forms** — `to_nnf()`, `to_pnf()`, `to_cnf()`, `to_dnf()` (equivalence-preserving), `to_tseitin_cnf()` (equisatisfiable, no blow-up), and `skolemize()` (satisfiability-preserving) for classical FOL
|
|
43
44
|
- **Horn check** — `is_horn()` reports whether a formula's clausal form consists of Horn clauses
|
|
44
45
|
- **Traversal API** — `walk()`, `subformulas()`, `atoms()`, `variables()`, `count()`, `depth()` on every node
|
|
@@ -50,6 +51,14 @@ A Python toolkit for parsing and working with first-order logic (FOL) formulas w
|
|
|
50
51
|
- **Equivalence checking** — check if two formulas are logically equivalent via Z3
|
|
51
52
|
- **Entailment checking** — check if a conclusion follows from premises via Prover9 (`check_logical_entailment`) or Vampire (`check_logical_entailment_vampire`), each taking the prover's executable path as an argument
|
|
52
53
|
- **Built-in resolution prover** — `prove()` and `is_valid_resolution()` decide entailment/validity in-process (sound first-order resolution, no external solver needed)
|
|
54
|
+
- **Natural-deduction proof checker** — `check_proof()` / `verify_proof()` check Fitch-style proofs with nested subproofs and discharge rules (classical FOL/MSFOL, plus K3/LP and the modal family via `logic=`); `render_fitch()` lays them out with scope bars and a justification column
|
|
55
|
+
- **Sequent-calculus proof checker** — `check_sequent_proof()` checks Gentzen **LK** derivations `Γ ⊢ Δ`, including the **second-order** rules (`∀²`/`∃²` with comprehension and predicate eigenvariables) that reach the `second_order=True` fragment; `render_sequent_proof()` prints the derivation tree
|
|
56
|
+
- **Fitch proof search** — `find_fitch_proof()` / `fitch_prove()` / `is_valid_fitch()` automatically *find* a Fitch natural-deduction proof (iterative-deepening backtracking, classical FOL); the result is re-validated by `check_proof`, so it is sound by construction
|
|
57
|
+
- **Analytic tableaux** — `is_valid_tableau()` / `prove_tableau()` / `tableau_closed()` and `tableau_model()` (an open branch is a countermodel); a fourth proof method, decidable propositionally
|
|
58
|
+
- **Finite model finder** — `find_model()` / `find_countermodel()` / `is_satisfiable_finite()` / `is_valid_finite()` search finite `Structure`s up to size N (Mace4-style: a valid entailment has no countermodel, an invalid one usually a small one)
|
|
59
|
+
- **Truth tables** — `truth_table()` (and `is_tautology()` / `is_contradiction()` / `is_satisfiable_tt()`) over classical, Kleene **K3**, and Priest **LP** values, renderable to Markdown
|
|
60
|
+
- **Intuitionistic logic** — `int_valid()` / `int_countermodel()` decide intuitionistic propositional validity by Kripke-model search; the **LJ sequent calculus** checker `check_lj_proof()` / `verify_lj_proof()` verifies intuitionistic derivations; LEM, DNE, and Peirce fail with explicit countermodels (`IntKripkeModel.forces()`)
|
|
61
|
+
- **Verbalization** — `to_english()` paraphrases a formula as an English sentence
|
|
53
62
|
- **Canonical form & exact match** — `canonicalize()` normalises bound-variable renaming, commutativity/associativity, operand duplication, and double negation; `exact_match()` gives a fair NL→FOL comparison stricter than logical equivalence but more forgiving than raw equality
|
|
54
63
|
- **Formula validation** — `validate()` / `is_wellformed()` / `validate_text()` report free variables, inconsistent predicate/function arity, leftover lambda nodes, and parseability of raw model output
|
|
55
64
|
- **Modal, temporal, epistemic & deontic logic** — `MSFLParser(modal=True)` parses `□`/`◇` (alethic), `K_a`/`B_a` (epistemic/doxastic), `Ⓖ`/`Ⓕ`/`Ⓝ`/`Ⓤ` (temporal), `Ⓞ`/`Ⓟ` (obligation/permission); Kripke-model semantics via `satisfies_modal()`; `standard_translation()` to classical FOL so Z3/resolution can decide modal validity
|
|
@@ -233,6 +242,33 @@ parse_latex(ast.to_latex(), many_sorted=True) == ast # True
|
|
|
233
242
|
|
|
234
243
|
`parse_latex` takes the same mode flags as `MSFLParser` (`many_sorted`, `fuzzy`, `modal`, `second_order`); the LaTeX must translate to syntax valid for that mode. Hand-written `c_`-constants need an escaped underscore (`c\_zero` or `c_{zero}`), since a bare `_` is LaTeX subscript.
|
|
235
244
|
|
|
245
|
+
### Reading TPTP, Prover9, and SMT-LIB
|
|
246
|
+
|
|
247
|
+
The exporters `to_tptp()` / `to_prover9()` / `to_z3()` have inverses, so formulas written for the standard automated-reasoning tools can be read back into the AST:
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
from unicode_fol_kit import parse_tptp, parse_tptp_formula, parse_prover9, from_z3, parse_smtlib
|
|
251
|
+
|
|
252
|
+
# TPTP: a single formula, or a whole problem file (fof/cnf statements)
|
|
253
|
+
parse_tptp_formula("![X]: (man(X) => mortal(X))") # AST for: forall x (Man(x) -> Mortal(x))
|
|
254
|
+
problem = parse_tptp("""
|
|
255
|
+
fof(ax, axiom, ![X]: (man(X) => mortal(X))).
|
|
256
|
+
fof(hyp, hypothesis, man(socrates)).
|
|
257
|
+
fof(g, conjecture, mortal(socrates)).
|
|
258
|
+
""")
|
|
259
|
+
roles = [f.role for f in problem] # ['axiom', 'hypothesis', 'conjecture']
|
|
260
|
+
|
|
261
|
+
# Prover9 / LADR
|
|
262
|
+
parse_prover9("(all X (man(X) -> mortal(X)))") # AST for: forall x (man(x) -> mortal(x))
|
|
263
|
+
|
|
264
|
+
# Z3: a z3 expression (from_z3), or SMT-LIB2 text (via Z3's own parser)
|
|
265
|
+
parse_smtlib("(declare-fun x () Int) (assert (< x (+ x 1)))") # [x < x + 1]
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
- **TPTP** — `parse_tptp_formula(s)` reads one bare FOF/CNF formula; `parse_tptp(text)` reads a whole problem into a list of `TptpFormula(name, role, formula)` records; `load_tptp(path)` reads a `.p`/`.tptp` file. `%` and `/* */` comments are ignored. Round-trips `to_tptp()`. TPTP lowercases predicates, so a predicate is capitalised on import (`loves` → `Loves`) to match the toolkit's uppercase-predicate convention; `$true`/`$false` import as opaque atoms; the typed `tff`/`thf` dialects and `include` directives are out of scope.
|
|
269
|
+
- **Prover9** — `parse_prover9(s)` reads a Prover9/LADR formula (a trailing `.` is accepted). It follows `set(prolog_style_variables)` (uppercase/underscore-initial names are variables), matching `to_prover9()`'s output, and is case-preserving. `to_prover9()` desugars exclusive-or, so an `Xor` round-trips to `(a | b) & -(a & b)`.
|
|
270
|
+
- **Z3** — `from_z3(expr)` turns a `z3.ExprRef` back into the AST; `parse_smtlib(text)` / `load_smtlib(path)` parse SMT-LIB2 (via Z3's own parser) and convert every assertion. The conversion is **meaning-preserving, not structure-preserving** — Z3 maps variables/constants/numbers onto one uninterpreted sort, so a *free* variable comes back as a `Constant` (only bound variables survive as `Variable`); `A == B` reads as `Iff` on Booleans and `=` on individuals.
|
|
271
|
+
|
|
236
272
|
### Traversal and inspection
|
|
237
273
|
|
|
238
274
|
Every node exposes a small traversal API:
|
|
@@ -520,6 +556,162 @@ is_valid_resolution(parser.parse("∃x ∀y L(x, y) → ∀y ∃x L(x, y)")) #
|
|
|
520
556
|
- **Equality is uninterpreted.** `=` is treated as an ordinary predicate (no built-in reflexivity/congruence). Entailments that rely on the theory of equality (e.g. `a = b, P(a) ⊨ P(b)`) need those axioms supplied as explicit premises, or use the Z3 backend instead.
|
|
521
557
|
- `to_clauses(formula)` exposes the clausal form, and `refute(clauses)` runs the saturation directly.
|
|
522
558
|
|
|
559
|
+
### Natural deduction (Fitch proofs)
|
|
560
|
+
|
|
561
|
+
The provers above decide *whether* an entailment holds. The package can also **check a Fitch-style natural-deduction proof** — a derivation with nested subproofs (hypothetical reasoning), per-line justifications, and discharge rules. `check_proof` is *sound*: it returns `True` only when every line genuinely follows by the cited rule and the proof's premises really do entail its conclusion. `verify_proof` additionally returns the certified sequent and the first failing line with a reason.
|
|
562
|
+
|
|
563
|
+
```python
|
|
564
|
+
from unicode_fol_kit import (
|
|
565
|
+
MSFLParser, Proof, Subproof, premise, assume, line,
|
|
566
|
+
check_proof, verify_proof, render_fitch,
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
parse = MSFLParser().parse
|
|
570
|
+
|
|
571
|
+
# Hypothetical syllogism: P→Q, Q→R ⊢ P→R
|
|
572
|
+
proof = Proof(
|
|
573
|
+
premises=[premise(1, parse("P → Q")), premise(2, parse("Q → R"))],
|
|
574
|
+
steps=[
|
|
575
|
+
Subproof(
|
|
576
|
+
assumption=assume(3, parse("P")),
|
|
577
|
+
body=[line(4, parse("Q"), "→E", 1, 3),
|
|
578
|
+
line(5, parse("R"), "→E", 2, 4)],
|
|
579
|
+
),
|
|
580
|
+
line(6, parse("P → R"), "→I", (3, 5)),
|
|
581
|
+
],
|
|
582
|
+
)
|
|
583
|
+
|
|
584
|
+
check_proof(proof) # True
|
|
585
|
+
print(render_fitch(proof))
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
`render_fitch` lays the proof out in classic Fitch notation — a line-number gutter, one vertical scope bar per open subproof, a rule under each assumption, and a justification column:
|
|
589
|
+
|
|
590
|
+
```text
|
|
591
|
+
1 │ P → Q Premise
|
|
592
|
+
2 │ Q → R Premise
|
|
593
|
+
├──────
|
|
594
|
+
3 │ │ P Assume
|
|
595
|
+
│ ├──────
|
|
596
|
+
4 │ │ Q →E 1, 3
|
|
597
|
+
5 │ │ R →E 2, 4
|
|
598
|
+
6 │ P → R →I 3–5
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
The classical rule set covers the connectives (`∧I`/`∧E`, `∨I`/`∨E`, `→I`/`→E`, `↔I`/`↔E`, `¬I`, `⊥I`/`⊥E`, `¬E` double-negation, `RAA`, `Reit`), the first-order quantifiers (`∀I`/`∀E`, `∃I`/`∃E`, with the eigenvariable side-conditions enforced via the kit's capture-avoiding substitution), and equality (`=I`/`=E`, certified against Z3 since `=` is otherwise uninterpreted). A subproof is cited by its line span, e.g. `(3, 5)`; the instantiation/witness term of `∀E`/`∃I` is passed as `extra=[term]`. `⊥` is the reserved logical constant `FALSUM`.
|
|
602
|
+
|
|
603
|
+
**Non-classical logics.** Pass `logic=` to check a proof under a different consequence relation. For the three-valued **K3**/**LP** logics each step is certified against the many-valued decision procedure, so the paraconsistency facts come out correctly — in **LP** modus ponens, the disjunctive syllogism, and explosion are *not* valid, and the checker rejects a proof that uses them:
|
|
604
|
+
|
|
605
|
+
```python
|
|
606
|
+
# In LP (paraconsistent) modus ponens is NOT valid — the checker rejects it:
|
|
607
|
+
mp = Proof(premises=[premise(1, parse("P")), premise(2, parse("P → Q"))],
|
|
608
|
+
steps=[line(3, parse("Q"), "→E", 2, 1)], logic="LP")
|
|
609
|
+
check_proof(mp) # False
|
|
610
|
+
|
|
611
|
+
# The very same proof is fine classically:
|
|
612
|
+
check_proof(Proof(premises=mp.premises, steps=mp.steps, logic="fol")) # True
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
For the **modal family** (`logic="K"`/`"T"`/`"S4"`/`"S5"`) each step is certified by the standard translation to FOL plus the frame axioms, decided by Z3. Knowledge (`Knows`) is factive, but belief (`Believes`) and obligation (`Obligatory`) are not:
|
|
616
|
+
|
|
617
|
+
```python
|
|
618
|
+
from unicode_fol_kit import Atom, Knows, Believes
|
|
619
|
+
|
|
620
|
+
p = Atom("P", [])
|
|
621
|
+
|
|
622
|
+
# Knowledge is factive (S5): K_a P ⊢ P
|
|
623
|
+
knows = Proof(premises=[premise(1, Knows("a", p))],
|
|
624
|
+
steps=[line(2, p, "T", 1)], logic="S5")
|
|
625
|
+
check_proof(knows) # True
|
|
626
|
+
|
|
627
|
+
# Belief is NOT factive: B_a P ⊬ P → rejected
|
|
628
|
+
believes = Proof(premises=[premise(1, Believes("a", p))],
|
|
629
|
+
steps=[line(2, p, "T", 1)], logic="S5")
|
|
630
|
+
check_proof(believes) # False
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
- **Sound by construction.** Citation accessibility is enforced (a line may not reach into a closed sibling subproof), discharge rules are checked against the proof's *open assumptions* (not merely the cited lines), and every classical rule application is cross-checked in the test-suite against Z3 and the resolution prover — randomised proofs are audited line-by-line so an accepted step is never one the oracle reports invalid.
|
|
634
|
+
- **Scope.** Classical FOL/MSFOL is checked by the syntactic rule table; K3/LP and the modal family are checked over their propositional fragment. Temporal logic (`Ⓖ`/`Ⓕ`/`Ⓝ`/`Ⓤ`), quantified modal logic, second-order quantification, and the substructural Łukasiewicz connectives are out of scope (no sound finitary Fitch calculus / first-order rendering) and are rejected with a clear message.
|
|
635
|
+
- **LaTeX.** `render_latex_fitch(proof)` (also `proof.to_latex_fitch()`) emits the derivation as a self-contained LaTeX `array`; `proof.to_dict()` / `Proof.from_dict(...)` round-trips a whole proof to JSON.
|
|
636
|
+
|
|
637
|
+
### Finding Fitch proofs (backtracking search)
|
|
638
|
+
|
|
639
|
+
The checker above *verifies* a proof; `find_fitch_proof` *finds* one. It is a goal-directed, iterative-deepening backtracking searcher over the classical propositional and first-order rules — introduction rules, ∨/∃ elimination by case split, backward chaining, and reductio (RAA), which makes it complete for the propositional fragment.
|
|
640
|
+
|
|
641
|
+
```python
|
|
642
|
+
from unicode_fol_kit import find_fitch_proof, fitch_prove, is_valid_fitch
|
|
643
|
+
from unicode_fol_kit.fol.nodes import Atom, Or, Not, Implies
|
|
644
|
+
|
|
645
|
+
P, Q = Atom("P", ()), Atom("Q", ())
|
|
646
|
+
|
|
647
|
+
fitch_prove([], Or(P, Not(P))) # True — a proof of P ∨ ¬P was found
|
|
648
|
+
is_valid_fitch(Implies(Implies(Implies(P, Q), P), P)) # True — Peirce's law
|
|
649
|
+
|
|
650
|
+
proof = find_fitch_proof([P, Implies(P, Q)], Q) # returns a Proof (or None)
|
|
651
|
+
print(proof.to_fitch())
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
```text
|
|
655
|
+
1 │ P Premise
|
|
656
|
+
2 │ P → Q Premise
|
|
657
|
+
├──────
|
|
658
|
+
3 │ Q →E 2, 1
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
- **Sound by construction.** Whatever the search assembles is re-validated by `check_proof` before it is returned, so a bug in the search can only make it *fail to find* a proof — never return an invalid one. Like the resolution prover it is sound and, under its depth bound, *incomplete*: `find_fitch_proof` returning `None` means "no proof found within `max_depth`", never "not a theorem".
|
|
662
|
+
- `fitch_prove(premises, conclusion)` returns a bool; `is_valid_fitch(formula)` proves from no premises; `find_fitch_proof` returns the actual `Proof` (render it, serialise it, or re-check it). Classical FOL only (the non-classical checkers above are verification-only).
|
|
663
|
+
|
|
664
|
+
### Sequent calculus (Gentzen LK, incl. second-order)
|
|
665
|
+
|
|
666
|
+
A second proof system: a **two-sided Gentzen sequent calculus**. A sequent `Γ ⊢ Δ` (multisets of formulas, read as `⋀Γ → ⋁Δ`) is derived by a tree of inference rules, and `check_sequent_proof` verifies the tree. This is the classical system **LK** with the first-order quantifier rules *and* the **second-order** rules (`∀²`/`∃²` over predicate variables), so it reaches the second-order fragment (the `MSFLParser(second_order=True)` syntax) that natural deduction / resolution / Z3 cannot.
|
|
667
|
+
|
|
668
|
+
```python
|
|
669
|
+
from unicode_fol_kit import (
|
|
670
|
+
MSFLParser, sequent, derive, axiom, check_sequent_proof, render_sequent_proof,
|
|
671
|
+
)
|
|
672
|
+
from unicode_fol_kit.fol.nodes import Atom, Implies, Quantifier, Variable, Constant
|
|
673
|
+
|
|
674
|
+
x, c = Variable("x"), Constant("c")
|
|
675
|
+
def Px(t): return Atom("P", [t])
|
|
676
|
+
|
|
677
|
+
# ∀x P(x) ⊢ P(c) via the ∀L rule (instantiating the bound x with the term c)
|
|
678
|
+
d = derive(sequent([Quantifier("∀", x, Px(x))], [Px(c)]), "∀L",
|
|
679
|
+
axiom(sequent([Px(c)], [Px(c)])),
|
|
680
|
+
extra=[c])
|
|
681
|
+
|
|
682
|
+
check_sequent_proof(d) # True
|
|
683
|
+
print(render_sequent_proof(d))
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
`render_sequent_proof` prints the derivation as an indented tree (conclusion first, premises below, each annotated with its rule):
|
|
687
|
+
|
|
688
|
+
```text
|
|
689
|
+
∀x P(x) ⊢ P(c) [∀L c]
|
|
690
|
+
P(c) ⊢ P(c) [Ax]
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
The second-order rules `∀²L` / `∃²R` instantiate a bound predicate variable `X` with a **comprehension term** `λx̄.ψ` (a `Comprehension`), replacing each `X(t̄)` by `ψ[x̄ := t̄]`; `∀²R` / `∃²L` use a fresh **predicate eigenvariable**:
|
|
694
|
+
|
|
695
|
+
```python
|
|
696
|
+
from unicode_fol_kit import Comprehension
|
|
697
|
+
from unicode_fol_kit.fol.nodes import SecondOrderQuantifier
|
|
698
|
+
|
|
699
|
+
z = Variable("z")
|
|
700
|
+
Xc = Atom("X", [c]) # the bound predicate variable X applied to c
|
|
701
|
+
Pc = Px(c)
|
|
702
|
+
|
|
703
|
+
# ∀X X(c) ⊢ P(c) via ∀²L, instantiating X with the comprehension λz. P(z)
|
|
704
|
+
so = derive(sequent([SecondOrderQuantifier("∀", "X", 1, Xc)], [Pc]), "∀²L",
|
|
705
|
+
axiom(sequent([Pc], [Pc])),
|
|
706
|
+
extra=[Comprehension((z,), Px(z))])
|
|
707
|
+
|
|
708
|
+
check_sequent_proof(so) # True
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
- **Rule set.** `Ax`, the structural rules `WL`/`WR` (weakening), `CL`/`CR` (contraction), `Cut`; the connective rules `¬L`/`¬R`, `∧L`/`∧R`, `∨L`/`∨R`, `→L`/`→R`, `↔L`/`↔R`; the quantifier rules `∀L`/`∀R`, `∃L`/`∃R` (with the eigenvariable condition on `∀R`/`∃L`); and the second-order rules `∀²L`/`∀²R`, `∃²L`/`∃²R`. Contexts are shared additively and compared as multisets; the instantiation term / eigenvariable / comprehension goes in `extra=[…]`.
|
|
712
|
+
- **Checker, not prover.** Full second-order validity is not recursively enumerable (Gödel), so no calculus can be a *complete* prover — `check_sequent_proof` verifies a *given* derivation. It is sound by the LK metatheory, cross-checked in the test-suite against Z3 (for the first-order-expressible sequents) and `satisfies_so` over small finite models (for the genuinely second-order ones, where a randomised audit confirms every accepted derivation is valid node-by-node).
|
|
713
|
+
- `derivation.to_dict()` / `Derivation.from_dict(...)` round-trips a whole derivation (including comprehension terms) to JSON.
|
|
714
|
+
|
|
523
715
|
### Tarskian semantics (FOL / MSFOL)
|
|
524
716
|
|
|
525
717
|
Instead of calling an external solver, you can build a concrete **world** (a first-order `Structure`) and compute the truth value of a formula directly, following Tarski's recursive definition of satisfaction. A structure is a non-empty domain of individuals together with interpretations of the constants, functions, and predicates (and, for MSFOL, the named sorts).
|
|
@@ -856,6 +1048,44 @@ holds(parser.parse("∀x ∀y (∀P (P(x) ↔ P(y)) → x = y)"), universe) #
|
|
|
856
1048
|
|
|
857
1049
|
This is second-order **predicate** quantification with full (standard) semantics over finite models. Quantification over functions, third-order and up, and a complete higher-order type system are out of scope; the lambda layer already provides higher-order *terms* (`λP. P(x)`), which you beta-reduce/eliminate before evaluation. Second-order formulas reject `to_z3`/`to_prover9`/`to_tptp` (second-order validity is not first-order / not SMT-expressible) — use `satisfies_so` on finite models instead.
|
|
858
1050
|
|
|
1051
|
+
## Truth tables, model finding, tableaux, and intuitionistic logic
|
|
1052
|
+
|
|
1053
|
+
Five further analysis tools round out the kit. They all reuse the existing AST and evaluators.
|
|
1054
|
+
|
|
1055
|
+
```python
|
|
1056
|
+
from unicode_fol_kit import (
|
|
1057
|
+
MSFLParser, truth_table, is_tautology, find_countermodel, is_valid_finite,
|
|
1058
|
+
is_valid_tableau, tableau_model, int_valid, int_countermodel, to_english,
|
|
1059
|
+
)
|
|
1060
|
+
p = MSFLParser().parse
|
|
1061
|
+
|
|
1062
|
+
# Truth tables (classical / K3 / LP)
|
|
1063
|
+
print(truth_table(p("P → Q")).render())
|
|
1064
|
+
is_tautology(p("P ∨ ¬P")) # True (classical) …
|
|
1065
|
+
is_tautology(p("P ∨ ¬P"), "K3") # … False in K3 (no three-valued tautologies)
|
|
1066
|
+
|
|
1067
|
+
# Finite model finder — a countermodel witnesses a non-entailment
|
|
1068
|
+
find_countermodel([p("P(tom)")], p("∀x P(x)"), max_size=3) # a Structure (or None)
|
|
1069
|
+
is_valid_finite(p("∀x P(x) → P(tom)")) # True (no finite countermodel)
|
|
1070
|
+
|
|
1071
|
+
# Analytic tableaux — and an open branch is a model
|
|
1072
|
+
is_valid_tableau(p("((P → Q) → P) → P")) # True (Peirce, classically)
|
|
1073
|
+
tableau_model([p("P → Q"), p("P")]) # {'P': True, 'Q': True}
|
|
1074
|
+
|
|
1075
|
+
# Intuitionistic logic — LEM and Peirce fail, with a Kripke countermodel
|
|
1076
|
+
int_valid(p("P ∨ ¬P")) # False (int_countermodel returns the model)
|
|
1077
|
+
int_valid(p("¬¬(P ∨ ¬P)")) # True
|
|
1078
|
+
|
|
1079
|
+
# Verbalize a formula in English
|
|
1080
|
+
to_english(p("∀x (Human(x) → Mortal(x))")) # 'for every x, if x is human, then x is mortal'
|
|
1081
|
+
```
|
|
1082
|
+
|
|
1083
|
+
- **Truth tables** — `truth_table(formula, logic)` (`"classical"` / `"K3"` / `"LP"`) returns a `TruthTable` (`.render()` to Markdown, `.is_tautology` / `.is_contradiction` / `.is_satisfiable`); the convenience predicates `is_tautology` / `is_contradiction` / `is_satisfiable_tt` wrap it. Each distinct atom is a propositional variable; quantified formulas are rejected.
|
|
1084
|
+
- **Finite model finder** — `find_model(theory, max_size)` brute-force searches finite `Structure`s (domain `1..N`) satisfying a theory; `find_countermodel(premises, conclusion)` finds one refuting an entailment; `is_satisfiable_finite` / `is_valid_finite` are the booleans. The Mace4-style partner of the provers: a valid entailment has no countermodel, an invalid one usually has a small one. Bounded (a domain whose interpretation space is too large is skipped).
|
|
1085
|
+
- **Analytic tableaux** — `is_valid_tableau`, `prove_tableau`, `tableau_closed`, and `tableau_model` (an open branch is a satisfying assignment / countermodel). A fourth proof method beside resolution, Fitch, and the sequent calculus; decidable and complete propositionally, bounded first-order.
|
|
1086
|
+
- **Intuitionistic logic** — `int_valid` and `int_countermodel` decide intuitionistic *propositional* validity by Kripke-model search (the logic has the finite-model property); `IntKripkeModel.forces(world, φ)` evaluates a model directly. The **LJ sequent calculus** checker `check_lj_proof` / `verify_lj_proof` verifies intuitionistic derivations — Gentzen **LJ** is the LK calculus restricted to *at most one succedent formula*, which is exactly what blocks the classical theorems. Excluded middle, double-negation elimination, and Peirce's law fail, each with an explicit Kripke countermodel; every intuitionistic validity is also classically valid.
|
|
1087
|
+
- **Verbalization** — `to_english(formula)` renders a formula as an English sentence (a readability aid, not a parse inverse).
|
|
1088
|
+
|
|
859
1089
|
## Syntax reference
|
|
860
1090
|
|
|
861
1091
|
This section describes the full surface syntax accepted by the parser. Because the four modes share the same term and atom layer, most of the syntax is identical across modes; differences are called out explicitly.
|