Choosing a tool¶
The kit spans several proof methods, a model finder, and a wide non-classical periphery across many logics. The tables below map a question (and a logic) to the entry point that answers it; each entry point is documented in detail in its own section. After the tables, the Worked recipes section gives one short, runnable example for every row — copy a recipe, change the formula, and you have a working program.
Three things hold across the whole kit:
Parse first. Every reasoning entry point takes an AST
Node, not a string. You get one from a parser —MSFLParser().parsefor classical FOL, with flagsmany_sorted/fuzzy/modal/second_orderfor the other modes — or from an importer (parse_tptp,parse_prover9,parse_smtlib,parse_latex).A single lowercase letter is a variable; a constant needs a multi-character name (
socrates,tom). This trips up almost every first formula.Decidable vs. semidecidable vs. bounded. Truth tables, propositional tableaux, matrices, ALC, and (propositional) modal/intuitionistic validity terminate with a verdict. Resolution and Fitch are semidecidable (they may not halt on a non-theorem). The finite model finder and the second-order / quantified non-classical searches are bounded — a negative result means “not found within the bound”, not a proof.
Which tool for which question¶
Your question |
Use |
Returns |
Sound / complete / decidable |
|---|---|---|---|
Is this valid / does Γ entail φ? (general FOL, no external solver) |
|
bool |
sound; refutation-complete; semidecidable |
Same, with an SMT solver |
|
bool / model |
sound & complete on Z3’s decidable fragment |
Same, via an external FO prover |
|
bool |
sound; complete for FOL; needs the binary |
Propositional / modal tautology? (decidably) |
|
bool |
sound & complete; decidable propositionally; routes modal inputs to the native modal tableau |
Modal validity in-process (no external solver) |
|
bool / verdict / Kripke counter-model |
sound & complete for propositional K, T, D, B, K4, K45, S4, S5, KD45 |
Check a Fitch proof I wrote |
|
bool / |
sound; FOL/MSFOL by rule table, K3/LP + modal propositionally |
Find a Fitch proof |
|
|
sound; complete propositionally, depth-bounded FO |
Check a sequent (LK) derivation |
|
bool / |
sound; reaches the second-order fragment |
Check an intuitionistic (LJ) derivation |
|
bool |
sound for intuitionistic consequence |
Find a model / countermodel |
|
|
finite search up to size N; enumerates sort universes for MSFOL |
Find a second-order model / decide finite SO validity |
|
|
bounded finite-model search |
Truth table (classical / K3 / LP) |
|
|
decidable; propositional only |
Finite-valued matrix / Belnap–Dunn FDE consequence |
|
matrix verdicts |
decidable; propositional, any finite matrix |
Intuitionistic validity (prop. or first-order) |
|
bool / |
decidable propositionally; bounded Kripke search for quantifiers |
Evaluate truth in a structure |
|
bool |
direct Tarskian / Kripke / finite SO semantics |
Decide modal validity with a real prover (Isabelle installed) |
|
|
sound (kernel-checked proof or genuine nitpick countermodel); incomplete |
Fuzzy degree or decision (Łukasiewicz / Gödel / product) |
|
degree / bool |
real-arithmetic decision via Z3; quantifiers grounded over a finite domain |
Description-logic concept reasoning (ALC) |
|
bool |
sound & complete; tableau with TBox internalisation and blocking |
Read a formula back as English |
|
str |
readability aid, not a parse inverse |
Check / canonicalize a formula before reasoning |
|
report / bool / Node |
static checks; equivalence via Z3 |
Import a problem from another format |
|
Node(s) |
round-trips into the same AST |
Worked recipes¶
One runnable example per question above, in reading order. They share these imports — later recipes assume parse and the helpers are already defined:
from unicode_fol_kit import *
from unicode_fol_kit import is_modal_valid, modal_decide, modal_countermodel
import unicode_fol_kit.dl as dl
parse = MSFLParser().parse # classical FOL
I want to … decide validity / entailment (general FOL, no external solver)¶
is_valid_resolution decides a single formula by refutation; prove decides premises ⊨ conclusion. Both are sound and refutation-complete, but only semidecidable — bound the work with max_steps if you suspect a non-theorem.
is_valid_resolution(parse("(∀x P(x)) → P(a)")) # → True
prove(
[parse("∀x (Human(x) → Mortal(x))"), parse("Human(socrates)")],
parse("Mortal(socrates)"),
) # → True
prove([parse("P → Q")], parse("Q → P")) # → False (converse does not follow)
I want to … decide validity / get a model with Z3¶
is_valid / is_satisfiable call Z3; get_model returns a satisfying assignment (or None). Z3 is sound and complete on its decidable fragment and is the fastest default for ground / propositional / EPR-shaped problems.
is_valid(parse("(P → Q) ↔ (¬Q → ¬P)")) # → True (contraposition)
is_satisfiable(parse("P ∧ ¬Q")) # → True
get_model(parse("P ∧ ¬Q")) is not None # → True (a concrete model exists)
I want to … decide FOL with an external prover¶
check_logical_entailment (Prover9) and check_logical_entailment_vampire (Vampire) are complete for FOL but need the binary installed, so they are not run here.
# needs an installed Prover9 binary
check_logical_entailment(
[parse("∀x (P(x) → Q(x))"), parse("P(a)")],
parse("Q(a)"),
prover9_path="/usr/bin/prover9",
) # → True # doctest: +SKIP
I want to … decide a propositional / modal tautology¶
is_valid_tableau is a decidable analytic-tableau check for the propositional fragment; prove_tableau takes premises; tableau_closed reports whether a set of formulas is jointly unsatisfiable.
is_valid_tableau(parse("P ∨ ¬P")) # → True
prove_tableau([parse("P → Q"), parse("P")], parse("Q")) # → True (modus ponens)
tableau_closed([parse("P"), parse("¬P")]) # → True (P, ¬P clash)
I want to … decide modal validity in-process¶
is_modal_valid returns a bool over a named frame; modal_decide returns the verdict string "valid" / "invalid"; modal_countermodel returns a refuting KripkeModel. Frames: K, T, D, B, K4, K45, S4, S5, KD45.
mp = MSFLParser(modal=True).parse
is_modal_valid(mp("□(P → Q) → (□P → □Q)"), frame="K") # → True (K axiom holds everywhere)
is_modal_valid(mp("□P → P"), frame="K") # → False (T axiom needs reflexivity)
modal_decide(mp("□P → P"), frame="T") # → 'valid'
cm = modal_countermodel(mp("□P → P"), frame="K")
cm is not None # → True (a Kripke refutation)
I want to … check a Fitch proof I wrote / find one¶
check_proof verifies a Proof; find_fitch_proof searches for one (depth-bounded), fitch_prove / is_valid_fitch are its boolean wrappers.
proof = find_fitch_proof([parse("P → Q"), parse("P")], parse("Q"))
proof is not None # → True
check_proof(proof) # → True (re-verify the found proof)
is_valid_fitch(parse("P → P")) # → True
I want to … check a sequent (LK / LJ) derivation¶
sequent / derive / axiom build a derivation tree; check_sequent_proof verifies it (classical LK, reaching the second-order fragment), check_lj_proof verifies the single-succedent LJ restriction (intuitionistic).
from unicode_fol_kit import sequent, derive, axiom
x, c = Variable("x"), Constant("c")
Px = lambda t: Atom("P", [t])
lk = derive(sequent([Quantifier("∀", x, Px(x))], [Px(c)]), "∀L",
axiom(sequent([Px(c)], [Px(c)])), extra=[c])
check_sequent_proof(lk) # → True (∀L instantiates ∀x P(x) to P(c))
P = Atom("P", ())
lj = derive(sequent([], [Implies(P, Not(Not(P)))]), "→R",
derive(sequent([P], [Not(Not(P))]), "¬R",
derive(sequent([P, Not(P)], []), "¬L",
axiom(sequent([P], [P])))))
check_lj_proof(lj) # → True (P → ¬¬P is intuitionistically valid)
I want to … find a (counter)model by finite search¶
find_model returns a Structure satisfying a theory; find_countermodel satisfies the premises but refutes the conclusion; is_valid_finite / is_satisfiable_finite are the booleans. The search is bounded by max_size.
find_model([parse("∃x (P(x) ∧ ¬Q(x))")]) is not None # → True
cm = find_countermodel([parse("P(tom)")], parse("∀x P(x)"), max_size=3)
cm is not None # → True
is_valid_finite(parse("∀x P(x) → P(tom)")) # → True (no finite countermodel)
I want to … find a second-order model / decide finite SO validity¶
Parse with second_order=True. so_is_valid_finite decides over finite models up to max_size; so_find_model / so_find_countermodel return witnesses.
so = MSFLParser(second_order=True).parse
so_is_valid_finite(so("∀P (P(a) → P(a))")) # → True
so_find_model(so("∃P ∃x P(x)")) is not None # → True
I want to … build a truth table (classical / K3 / LP)¶
truth_table(φ, logic=…) builds a TruthTable; is_tautology / is_contradiction / is_satisfiable_tt are classical boolean shortcuts. Each distinct atom is a propositional variable.
is_tautology(parse("P → P")) # → True
is_contradiction(parse("P ∧ ¬P")) # → True
truth_table(parse("P ∨ ¬P"), logic="classical").is_tautology # → True
truth_table(parse("P ∨ ¬P"), logic="K3").is_tautology # → False (½ ∨ ½ = ½)
truth_table(parse("(P → Q) ∨ (Q → P)"), logic="LP").is_tautology # → True
I want to … reason in an arbitrary finite-valued matrix (incl. FDE)¶
matrix_is_valid / matrix_entails / matrix_is_satisfiable evaluate over a TruthMatrix. The kit ships K3_MATRIX, LP_MATRIX, FDE_MATRIX. FDE is paraconsistent — it blocks explosion.
matrix_is_valid(parse("P ∨ ¬P"), K3_MATRIX) # → False (Kleene: no designated ½)
matrix_is_valid(parse("P ∨ ¬P"), LP_MATRIX) # → True (Priest designates ½)
matrix_entails([parse("P")], parse("P ∨ Q"), FDE_MATRIX) # → True
matrix_entails([parse("P"), parse("¬P")], parse("Q"), FDE_MATRIX) # → False (no explosion)
I want to … decide intuitionistic validity / get a Kripke countermodel¶
int_valid is decidable propositionally and does a bounded Kripke search for quantifiers; int_countermodel returns the refuting (IntKripkeModel, world) pair.
int_valid(parse("P ∨ ¬P")) # → False (excluded middle is not intuitionistic)
int_valid(parse("¬¬(P ∨ ¬P)")) # → True (its double negation is)
int_countermodel(parse("¬¬P → P")) is not None # → True (DNE fails — countermodel found)
I want to … evaluate truth in a structure I built¶
satisfies evaluates a closed FOL/MSFOL formula in a hand-built Structure; satisfies_so / holds do the second-order case; satisfies_modal evaluates against a KripkeModel.
S = Structure(domain=(0, 1), constants={"tom": 0},
predicates={("P", 1): {(0,)}})
satisfies(parse("P(tom)"), S) # → True
satisfies(parse("∀x P(x)"), S) # → False (P fails at 1)
I want to … decide a fuzzy degree or fuzzy validity¶
Parse with fuzzy=True. fuzzy_evaluate returns the degree of a formula under a [0,1] valuation; fuzzy_is_valid / fuzzy_is_satisfiable decide via Z3 reals. Pick the t-norm with tnorm="lukasiewicz" / "godel" / "product".
fp = MSFLParser(fuzzy=True).parse
round(fuzzy_evaluate(fp("P ⊗ Q"), {"P": 0.6, "Q": 0.7},
tnorm="lukasiewicz"), 2) # → 0.3 (max(0, .6+.7−1))
fuzzy_is_valid(fp("P → P"), tnorm="lukasiewicz") # → True
I want to … reason about ALC concepts / an ABox¶
Use unicode_fol_kit.dl. Build concepts with dl.Atomic / dl.And / dl.Exists / …, axioms with dl.TBox().add(...), facts with dl.ABox(). Then dl.subsumes / dl.concept_satisfiable / dl.equivalent / dl.abox_consistent.
t = dl.TBox()
t.add(dl.Atomic("Dog"), dl.Atomic("Mammal"))
t.add(dl.Atomic("Mammal"), dl.Atomic("Animal"))
dl.subsumes(dl.Atomic("Dog"), dl.Atomic("Animal"), t) # → True (transitivity)
dl.concept_satisfiable(dl.And(dl.Atomic("A"), dl.Not(dl.Atomic("A")))) # → False
I want to … read a formula back as English / check it statically¶
to_english verbalizes a formula; validate_text returns a ValidationReport; is_wellformed and formulas_are_equivalent are quick booleans.
to_english(parse("∀x (Human(x) → Mortal(x))"))
# → 'for every x, if x is human, then x is mortal'
is_wellformed(parse("P ∧ Q")) # → True
formulas_are_equivalent(parse("P → Q"), parse("¬Q → ¬P")) # → True
I want to … import a problem from another format¶
The importers return the same AST every reasoning function consumes, so you can pipe an external problem straight into a prover.
phi = parse_tptp_formula("![X] : (p(X) => q(X))")
phi.to_unicode_str() # → '∀x (P(x) → Q(x))'
psi = parse_latex(r"\forall x\, (P(x) \rightarrow Q(x))")
is_valid_resolution(Implies(psi, parse("P(a) → Q(a)"))) # → True
Logics supported¶
Logic |
Enable |
Operators added |
Semantics |
What can decide / reason about it |
|---|---|---|---|---|
Classical FOL |
|
∀ ∃ ∧ ∨ ¬ → ↔ ⊕ = ≠ |
|
resolution, Z3, Prover9/Vampire, tableaux (prop.), Fitch, LK, finite model finder |
Many-sorted FOL (MSFOL) |
|
sorted |
|
resolution / Z3 (via |
Fuzzy (FL) |
|
weak ∧ ∨, strong ⊗ ⊕, Łuk ¬ → ↔ |
|
|
Many-sorted fuzzy (MSFL) |
|
sorts + Łukasiewicz |
|
|
Modal / temporal / epistemic / deontic |
|
□ ◇, K_a B_a, Ⓖ Ⓕ Ⓝ Ⓤ, Ⓞ Ⓟ (+ past-tense ⒣ ⒫ ⒴ ⒮) |
|
native modal tableau ( |
Many-valued K3 / LP / FDE |
|
classical syntax over {0, ½, 1} / four-valued |
|
|
Second-order |
|
∀P ∃P over predicate vars |
|
|
Intuitionistic |
|
classical syntax |
|
|
Description logic ALC |
|
⊤ ⊥, ¬ ⊓ ⊔, ∃r.C ∀r.C |
concept/ABox interpretations |
|
Free / dynamic-epistemic / counterfactual / circumscriptive |
|
logic-specific |
per-module model classes |
free-logic evaluation, public-announcement (PAL) updates, Lewis-sphere counterfactuals, circumscriptive non-monotonic entailment |
Every non-fuzzy logic above also has a higher-order exporter in unicode_fol_kit.hol — a Benzmüller-style shallow embedding emitted as an Isabelle/HOL theory or a TPTP THF problem for an external prover (Leo-III / Satallax / Sledgehammer) — and, with a local Isabelle installed, isabelle_decide_modal actually runs it to decide modal validity.
Recipes for the peripheral logics¶
The four small evaluators each build an AST from unicode_fol_kit.fol.nodes and reason over an explicit, hand-built model. See Further Non-Classical Logics for the full treatment; these are the one-line “is this the tool I want?” probes.
Free logic — universal instantiation fails when a constant is non-denoting:
from unicode_fol_kit.fol.nodes import Atom, Implies, Quantifier, Variable, Constant
from unicode_fol_kit.semantics.free_logic import FreeModel, free_holds
xv, cc = Variable("x"), Constant("c")
all_P = Quantifier("∀", xv, Atom("P", [xv]))
m = FreeModel(outer=(0, 1), existing=frozenset({0}), constants={"c": 1},
predicates={("P", 1): frozenset({(0,)})})
free_holds(all_P, m) # → True (∀x P(x) over {0})
free_holds(Implies(all_P, Atom("P", [cc])), m) # → False (UI invalid: c is non-existing)
Public-announcement (dynamic epistemic) logic — knowledge changes after a truthful announcement:
from unicode_fol_kit.fol.nodes import Knows
from unicode_fol_kit.semantics.dynamic_epistemic import announce, box_announce
pp = Atom("p", ())
Kap = Knows("a", pp)
M = KripkeModel([0, 1], {"K:a": {(0, 0), (0, 1), (1, 0), (1, 1)}}, {0: {"p"}})
satisfies_modal(Kap, M, 0) # → False (a does not yet know p)
box_announce(M, 0, pp, Kap) # → True ([p!] K_a p — announcing p makes a know it)
Counterfactual conditionals — Lewis-sphere “would” / “might”, non-monotone in the antecedent:
from unicode_fol_kit.fol.nodes import And as FAnd, Not as FNot
from unicode_fol_kit.semantics.conditional import CounterfactualModel, would
A, B, C = Atom("A", ()), Atom("B", ()), Atom("C", ())
CF = CounterfactualModel(
(0, 1, 2),
{0: frozenset(), 1: frozenset({"A", "B"}), 2: frozenset({"A", "C"})},
{0: [frozenset({0}), frozenset({0, 1}), frozenset({0, 1, 2})]},
)
would(CF, 0, A, B) # → True (if A were, B would be)
would(CF, 0, FAnd(A, C), B) # → False (strengthening the antecedent breaks it)
Circumscription (non-monotonic entailment) — minimal-model reasoning that strengthening can retract:
from unicode_fol_kit.fol.nodes import Implies as FImplies
from unicode_fol_kit.semantics.nonmonotonic import minimal_entails
a = Constant("a")
Pa, Qa = Atom("P", [a]), Atom("Q", [a])
minimal_entails([FImplies(Pa, Qa)], FNot(Qa), circumscribed={"P", "Q"}, max_size=2) # → True
minimal_entails([FImplies(Pa, Qa), Pa], FNot(Qa), circumscribed={"P", "Q"}, max_size=2) # → False
Recipes for the exporters and bridges¶
Standard translation lowers a modal formula to first-order logic so the classical provers can take it:
mp = MSFLParser(modal=True).parse
standard_translation(mp("□P → P")).to_unicode_str()
# → '∀w0 (R(w, w0) → P(w0)) → P(w)'
Quantified modal validity in-process, choosing the domain mode (constant / varying / increasing / decreasing):
qml_is_valid(mp("◇∃x P(x) → ∃x ◇P(x)"), mode="constant", frame="S5") # → True
qml_equivalent(mp("□P"), mp("¬◇¬P"), frame="K") # → True
Higher-order THF / Isabelle export for an external HOL prover — to_thf_modal returns a THF problem string:
thf = to_thf_modal(mp("□P → P"), mode="constant", frame="K")
isinstance(thf, str) and "thf" in thf # → True (a TPTP THF problem ready for Leo-III / Satallax)
Composing parser modes¶
The four core parser modes form the many_sorted × fuzzy 2×2; the modal and second-order modes are each “classical unsorted FOL + one extension” and do not combine with sorts, fuzziness, or each other. The dependence, linear, and lambek modes are standalone logics (their connectives and semantics replace the classical ones), so they combine with nothing. The constructor rejects an unsupported combination with a clear ValueError. (The matrix, ALC, intuitionistic, relevant, and peripheral logics are separate subsystems, not parser flags.)
Combine… |
with sorts |
with fuzzy |
with modal |
with second-order |
|---|---|---|---|---|
base FOL |
✅ MSFOL |
✅ FL |
✅ modal |
✅ second-order |
sorts |
— |
✅ MSFL |
❌ |
❌ |
fuzzy |
✅ MSFL |
— |
❌ |
❌ |
modal |
❌ |
❌ |
— |
❌ |
second-order |
❌ |
❌ |
❌ |
— |
The frontier families¶
The families this page used to list as out of scope now ship first-class:
Hybrid logic H(@) — nominals and the satisfaction operator, inside the modal mode (
MSFLParser(modal=True)parses@i (P ∧ ◇j)); Kripke evaluation viaKripkeModel(nominals=…)and validity per frame viahybrid_is_valid. See Hybrid logic H(@) — naming worlds.Relevant logic B — Routley–Meyer semantics over the classical syntax;
rel_valid/rel_countermodelrefute the paradoxes of material implication. See Relevant logic.Dependence / IF logic — team semantics over finite structures:
MSFLParser(dependence=True)parses=(x, y)and∃y/{x} φ; evaluate withteam_satisfies/team_models. See Dependence logic and IF logic (team semantics).Substructural logics —
MSFLParser(linear=True)(⊗ & ⊕ ⊸ ! 𝟙, proverill_prove) andMSFLParser(lambek=True)(• \ /, decision procedurelambek_derivable). See Substructural logics: linear and Lambek.