miyamok / epsilonproofchecker

Proof assistant for Hilbert style proof systems including predicate calculus and epsilon calculus

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Proof assistant for Hilbert's epsilon calculus and predicate calculus

Epsilon is a proof assistant system for Hilbert's epsilon calculus and predicate calculus. It supports Hilbert style proofs in epsilon calculus as well as in first order predicate calculus. The proof scripting language is simple, and there are useful features such as proof transformation due to deduction theorem, which makes proof scripting in Hilbert style system easier, the lemma feature to use a previously proven lemma, and proof automation. Automated theorem proving is due to an external tool Microsoft Z3 (https://github.com/Z3Prover/z3), and formulas in predicate logic and it's subsystems are supported.

Table of contents
% git clone https://github.com/miyamok/epsilonproofchecker
% cd epsilonproofchecker
## ghc-9.2.8 and cabal used
% cabal install
% epsilonproofchecker examples/ex03_identity.proof
-- Correct proof of ⊢ A -> A
% cat examples/ex03_identity.proof 
A -> (A -> A) -> A by K
A -> A -> A by K
(A -> (A -> A) -> A) -> (A -> A -> A) -> A -> A by S
(A -> A -> A) -> A -> A by MP
A -> A by MP
% epsilonproofchecker examples/ex24_drinkers_paradox.proof
-- Correct proof of ⊢ ex x(P(x) -> all x P(x))
% epsilonproofchecker examples/ex25_drinkers_paradox_eps.proof
-- Correct proof of ⊢ P(eps x(P(x) -> P(eps x ~P(x)))) -> P(eps x ~P(x))
% epsilonproofchecker examples/ex05_wrong.proof
Error at line 1: A -> B by K
% cat examples/ex05_wrong.proof
A -> B by K
% epsilonproofchecker examples/ex07_assumption.proof 
-- Correct proof of ⊢ A -> A
% cat examples/ex07_assumption.proof 
A by Asm
deduction-transformation
% z3 -version ## assume Microsoft's Z3 is installed
Z3 version 4.12.3 - 64 bit
% epsilonproofchecker examples/ex18_inverse_AllShift_auto.proof
-- Correct proof of ⊢ (B -> all x P(x)) -> all y (B -> P(y))
% cat examples/ex18_inverse_AllShift_auto.proof
(B -> all x P(x)) -> all y (B -> P(y)) by Auto

Logic

This section describes propositional calculus first, and then extends this calculus step-by-step to arrive at more powerful calculi such as predicate calculus and epsilon calculus.

Propositional calculus is a simple logic whose language consists of atomic propositions and logical connectives such as implication, negation, conjunction and disjunction which all appear even in our daily life. Elementary calculus is an extension of propositional calculus. It is obtained by extending propositional calculus by predicates and term constants. Elementary calculus is a common basis of richer calculi such as predicate calculus and epsilon calculus. Predicate calculus, first-order logic in other words, is elementary calculus with quantifiers and additional logical reasoning methods concerning the quantifiers. An example statement involving quantification is "there exists a white pigeon." where there is a quantification over pigeons. Epsilon calculus is another extension of elementary calculus. The language is enriched by the epsilon operator, and an additional reasoning method is available. Epsilon operator forms a term from a formula. Consider a formula with a free variable, say x, then the formula is supposed to state a property of x. Feeding this formula to epsilon operator, a variable in the given formula is bound, let's choose x for this case, and forms a term. Informally speaking, a term formed by epsilon operator is meant to be an object which satisfies the property the fed formula describes. For example, assume we have a formula claiming that "x is a white pegion," then epsilon operator allows to make a term meaning "an object which is a white pegion," namely, a white pegion. The epsilon operator is often explained as a choice operator. See the difference between "there exists a white pigeon" and "an object which is a white pegion," where the former one is a claim of the existence of such a pigeon, that is definitely different from such a pigeon denoted by epsilon operator.

Propositional calculus

For this moment, we restrict our base logic to the fragment of negation and implication. Propositional formula F is defined as follows, where P0 is ranging over propositional variables and A is an element of P0.

F ::= A | bot | F -> F

The arrow denotes logical implication, and bot is a special constant denoting falsum, which means the condtradiction. The following formula means that if A then it contradicts.

A -> bot

In order words, it's not the case that A holds. This formula, A -> bot, denotes the negation of A (namely, not A), and is abbreviated as

~A

Let A, B, and C be propositional variables in P0. The following three expressions are examples of propositional formulas.

A
~A -> B
~A -> ~B -> B -> A
A -> B -> A

Here we assume negation has higher priority than implication, namely, the second formula above claims that if not A holds then B holds, but doesn't mean that it is not the case that A implies B. Using parentheses, one can write a formula meaning that it is not the case that A implies B.

~(A -> B)

The third formula above claims that if not A holds, and also if not B holds, and also if B holds, then A holds. If we supply (redundant) parentheses, it should look as

~A -> (~B -> (B -> A))

Implication in the right hand side has higher priority than the left, and we say that implication associates to the right. In order to mean that if not A implies not B, then B implies A, the use of parentheses is inevitable.

(~A -> ~B) -> B -> A

In order to give an objective explanation that a claim is true, one gives a proof to the claim. A proof is a list of expressions, where an expression consists of a formula to claim and a reason of claiming it. If there is a proof of A, we write

⊢A

If a proof concludes A from assumptions A1, ..., Ak, we write A1, ..., Ak ⊢ A. We often abbreviate the sequence of assumptions A1, ..., Ak as Γ. A reason is either an axiom, an inference rule, or a reference to an assumption. We have the following axioms

A -> B -> A
(A -> B -> C) -> (A -> B) -> A -> C
bot -> A
~~A -> A

and one inference rule.

If Γ⊢A -> B and Γ⊢A then Γ⊢B

Each of the above has the names K, S, EFQ, DNE, and MP, respectively. K and S are traditional names, and the rest stands for ex falso quodlibet, double negation elimitaion, and modus ponens. Note that the axioms are actually axiom schemas, namely, those propositional variables in the axiom formulas may be replaced by arbitrary formulas. In order words, those occurrences of A, B, C are metavariables and diffrent from propositional variables, and those metavariables will be instantiated by concrete formulas in actual use. Here we give a proof of A -> A.

(A -> (A -> A) -> A) -> (A -> A -> A) -> A -> A by S
A -> (A -> A) -> A by K
(A -> A -> A) -> A -> A by MP
A -> A -> A by K
A -> A by MP

For example in the second line, the axiom scheme K got its metavariable A replaced by a formula A, and another metavariable B replaced by a formula A -> A.

Now we get rid of the limitation of our language, and see not only implication and negation but also conjunction and disjunction. The grammar of the language of propositional calculus is defined as follows.

F ::= A | bot | F -> F | F & F | (F | F)

The vertical line is used for both the BNF syntax notation and our logical language, hence parentheses are inserted to make the matter a bit clear. A conjunction formula A & B claims that A and B hold. A disjunction formula A | B claims that A or B hold.

The way of reasoning with conjunction and disjunction is described in the next section, Syntax for proof scripts.

Elementary calculus

Elementary calculus extends propositional calculus by terms and predicates for its language. Let C0 be a set of nullary constants, C1 a set of unary (function) constants, and so, and let c and f be nullary and unary constants, respectively. Also, let Q be an element of P1, a set of unary atomic predicates. Let V be a set of variables, and assume x is a variable in V Then the terms t and formulas F of elementary calculus is given as follows.

t ::= x | c | f(t)
F ::= A | bot | F -> F | F & F | (F | F) | Q(t)

Generally a formula E may contain a variable x. In such a case, it is convenient to allow writing E(x) instead of E, and also allow writing E(t) for the formula obtained by replacing all occurrences of x in E by t. Its axioms and inference rule are same as propositional calculus.

Predicate calculus

Predicate caluclus is an extension of elementary calculus by quantifications. The language is enriched by the existential quantifier and the universal quantifier. The syntax is given as follows.

t ::= x | c | f(t)
F ::= A | bot | F -> F | F & F | (F | F) | Q(t) | ex x F | all x F

Assume E(x) is a formula containing a free variable x. One interpretation of this formula is that it states some property of x. By means of the quantifiers, it is possible to form the following quantified formulas

ex x E(x)
all x E(x)

which are commonly written as ∃x E(x) and ∀x E(x), respectively. They denote that there is some x such that E(x) holds, and that for any x, E(x) holds.

We have two kinds of variable occurrences due to the presence of the quantifiers. Assume a formula E(x) is free from a quantifier and x has at least one occurrences in E(x). In the formula all x E(x), all the occurrences of x is bounded, while all the occurrences of x in E(x) is free. This variable binding mechanism is important to formulate the logic of predicate calculus, and the definition of free varialbles FV is given as follows.

FV(x) = {x}
FV(f(t)) = FV(t)
FV(bot) = {}
FV(P(t)) = FV(t)
FV(A -> B) = FV(A) ∪ FV(B)
FV(A & B) = FV(A) ∪ FV(B)
FV(A | B) = FV(A) ∪ FV(B)
FV(all x E(x)) = FV(E(x)) - {x}
FV(ex x E(x)) = FV(E(x)) - {x}

We allow to write FV(A1, ..., Ak) to mean FV(A1)∪...∪FV(Ak). From now on, if we write a formula in the form A(x), it means that x may occur freely in A(x), however, it is not the case that a bound variable x is indicated in this notation. Moreover, a change of bound variable names doesn't affect the meaning of formulas and terms. Consider a formula A(x) which does not have a free occurrence of variables other than x. Then, ex x A(x) is equivalent as ex y A(y) for any variable y. This is same as the fact that formal argument names of a function definition are changeable without modifying the meaning of the function. It also requires a delicate treatment of the substitution, that is, by replacing x in A(x) by t, we should avoid to get any free variable in t newly captured. We assume bound variables in A(x) are properly renamed before the operation of substitution, so that there is no free variable in t which is bound in A(t). For example, let a formula A(x) be ex y (P(x) & P(y)). Apparently, the occurrence of x is free and the ones of y are bound. In case we consider a substitution A(y), we cannot simply replace x in ex y (P(x) & P(y)) by y to get ex y (P(y) & P(y)). The right way to do the substitution is that we rename the bound variable y in A(y) before the replacement, for example by using a fresh variable z, we form a logically equivalent formula ex z (P(x) & P(z)), and perform the replacement to get ex z (P(y) & P(z)).

In order to reason about formulas involving the quantifiers, predicate calculus employs additional 4 axioms and 1 inference rule.

all x A(x) -> A(t)
A(t) -> ex x A(x)
all x(B -> A(x)) -> (B -> all y A(y))
all x(A(x) -> B) -> (ex y A(y) -> B)

Here we assumed x does not have a free occurrence in B, and also if x is distinct variable from y, then y doesn't have a free occurrence in A(x). The new inference rule is called the rule of generalization, which allows to derive A1, ..., Ak ⊢ all x E(x) from A1, ..., Ak ⊢ E(x) under the condition that x does not have a free occurrence in A1, ..., Ak and also that if x is distinct variable from y, then y doesn't have a free occurrence in A(x).

Epsilon calculus

Epsilon calculus extends elementary calculus by epsilon operator and so-called critical axiom. Epsilon operator is denoted by eps and forming a term taking a variable and a formula. The language definition of epsilon calculus is as follows.

t ::= x | c | f(t) | eps x F
F ::= A | bot | F -> F | F & F | (F | F) | Q(t)

A term of the form eps x E(x) is called epsilon term. Intuitive meaning of an epsilon term eps x E(x) is the term which satisfies the property of x denoted by E(x). Therefore, epsilon operator is often explained as a choice operator. The definition of free variables is extended by the following clause to support epsilon terms.

FV(eps x E(x)) = FV(E(x)) - {x}

This intuition is formulated by the following critical axiom.

E(t) -> E(eps x E(x))

where t is an arbitrary term in epsilon calculus. Epsilon operator is expressive enough to define the existential and universal quantifiers of predicate calculus, hence epsilon calculus is more expressive than predicate calculus, although the existential and universal quantifiers are missing in epsilon calculus. Let E(x) be a formula, then the corresponding quantified formulas are defined as follows.

ex x E(x) := E(eps x E(x))
all x E(x) := E(eps x ~E(x))

We are going to look at examples. Assume the formula A does not contain a free variable x. The following formula is known as independence of premise, whose proof is given as examples/ex22_independence_of_premise.proof .

(A -> ex x P(x)) -> ex x (A -> P(x))

Applying the definition of the existential quantifier by epsilon operator, the above formula goes to the following one.

(A -> P(eps x P(x))) -> A -> B(eps x(A -> P(x)))

A proof to this formula is given in examples/ex23_independence_of_premise_eps.proof .

(A -> P(eps x P(x))) -> A -> P(eps x (A -> P(x))) by C

Notice that this formula is an instance of the critical axiom. Another example is a so-called Drinker's formula, which is often referred to as Drinker's paradox, and a proof is given as examples/ex24_drinkers_paradox.proof .

ex x(P(x) -> all x P(x))

The meaning of this formula is often explained through a story of a pub, that is, in a pub there is such a guy that if the guy is drinking then everybody in the pub is drinking. This claim may sound a bit confusing, and this is the reason why this provable formula is called a paradox. If there is a guy in the pub who is not drinking, you pick this guy, then the premise of the implication goes false, hence the whole formula is true. Otherwise everybody is drinking, hence you can pick an arbitrary guy. In case of a real pub, it is decidable whether there is a guy who is not drinking. This formula is true even in case the matter is undecidable. The epsilon version of the above formula is

P(eps x(P(x) -> P(eps x ~P(x)))) -> P(eps x ~P(x))

A proof is given in examples/ex25_drinkers_paradox_eps.proof After proving the identity formula P(eps x ~P(x)) -> P(eps x ~P(x)), the rest of the proof goes as follows.

(P(eps x ~P(x)) -> P(eps x ~P(x))) -> P(eps x(P(x) -> P(eps x ~P(x)))) -> P(eps x ~P(x)) by C
P(eps x(P(x) -> P(eps x ~P(x)))) -> P(eps x ~P(x)) by MP

Deduction theorem

Deduction theorem claims that if Γ, A ⊢ B then Γ ⊢ A -> B. An interesting aspect of the proof of this theorem is that it actually tells us how to get a proof of Γ ⊢ A -> B from a proof of Γ, A ⊢ B. The epsilon proof assitant has a feature of proof transformation, which implements the algorithm in the proof of deduction theorem.

Deduction theorem holds for all the calculi supported by the proof assistant epsilon.

Usage of the epsilon proof assistant

The Glasgow Haskell Compiler is prerequisite. Get the source code and compile the code in the following way.

% cd epsilonproofcheker
% ghc Main

Then you can try examples in the examples directory.

% epsilonproofchecker examples/ex22_independence_of_premise.proof
-- Correct proof of ⊢ (A -> ex x P(x)) -> ex x(A -> P(x))
% cat examples/ex22_independence_of_premise.proof
A -> ex x P(x) by Asm
~ex x(A -> P(x)) by Asm
A by Asm

...

~~ex x(A -> P(x)) -> ex x(A -> P(x)) by DNE
ex x(A -> P(x)) by MP
deduction-transformation

The oprion -p is to display the proof.

% epsilonproofchecker -p examples/ex03_identity.proof
-- Correct proof of ⊢ A -> A
A -> (A -> A) -> A by K
A -> A -> A by K
(A -> (A -> A) -> A) -> (A -> A -> A) -> A -> A by S
(A -> A -> A) -> A -> A by MP
A -> A by MP

One example making use of the proof transformation is the proof of the excluded middle, which is available as examples/ex16_excluded_middle.proof.

A | ~A

First step is to prove that A | ~A -> bot, A ⊢ bot which is done in the lines 1 to 5 in ex16_excluded_middle.proof.

% cat examples/examples/ex16_excluded_middle.proof 
A | ~A -> bot by Asm
A by Asm
A -> A | ~A by DisjI1
A | ~A by MP
bot by MP
deduction-transformation
~A -> A | ~A by DisjI2
A | ~A by MP
bot by MP
deduction-transformation
~~(A | ~A) -> A | ~A by DNE
A | ~A by MP

By deduction-transformation, the first five lines, the proof of A | ~A -> bot, A ⊢ bot, is used to generate a new proof of A | ~A -> bot ⊢ ~A. The proof scripts from the line 6 to 8 are added to the end of the new proof, and this establishes another proof concluding A | ~A -> bot ⊢ bot by the line 9. Another deduction transformation is applied to generate a proof of ⊢ ~~(A | ~A), which yields the goal ⊢ A | ~A by DNE and MP.

In lines after deduction-transformation, the original proof and tags there are not accessible. The command line option -p may be helpful to get to know how the generated proof looks.

By issueing the following command, it shows the following output, which means that the proof of A | ~A has been checked.

% epsilonproofchecker examples/ex16_excluded_middle.proof
-- Correct proof of ⊢ A | ~A

It is also possible to print out the final proof by the option -p.

% epsilonproofchecker -p examples/ex16_excluded_middle.proof

The outcome consists of 64 lines, and would be hard without relying on the proof transformation, although there can be a clever idea for a shorter proof.

A proof script (it is exaxtly one file on the computer) may contain multiple proofs. Completed proofs can be later used in other proofs. By end-proof, the current proof is concluded and a new proof can start from the next line. If you give also a name, end-proof Lemma1 for example, this proof will be available in the future work via Use with the specified name, by Use(Lemma1) for example. Use command makes the proven formula fit the target formula. It means that a previously proven formula A | ~A is applicable to prove ex x P(x) | ~ex x P(x) via Use which finds and applies the suibstitution of ex x P(x) for A.

If the last step of the proof is Asm, the assumed formula is the conclusion of the proof. If one wants to write a proof whose conclusion is directly from an assumption which is not the last Asm, one can use Ref to make a claim referring to an assumption. An example is found in examples/ex07_assumption.proof which generates a proof of ⊢ A -> B -> A from another proof of A, B ⊢ A.

% cat examples/ex07_assumption.proof 
A by Asm
B by Asm
A by Ref
deduction-transformation
deduction-transformation

On the other hand, it is also possible to make use of an external automated theorem prover. For this moment, the epsilon proof assistant supports automation for predicate calculus and its subsystems due to Microsoft's Z3 (https://github.com/Z3Prover/z3). Microsoft's Z3 is supposed to be installed and be avaialble from your command line via a command z3.

% z3 -version
Z3 version 4.12.3 - 64 bit
% epsilonproofchecker -p examples/ex15_peirce_auto.proof 
-- Correct proof of ⊢ ((A -> B) -> A) -> A
((A -> B) -> A) -> A by Auto

Microsoft Z3 does not supply a syntactic proof of the claimed formula, but it just says "yes" or "no" as a result of determining the provability of the claimed formula. There is no means for the proof assistant epsilon to verify the response from such an external prover, and the proof assistant epsilon simply accepts what the external prover said, in stead of performing a syntactic proof checking. It implies that the correctness of a proof involving Auto totally relies on the correctness of the external prover, and the epsiolon proof assistant does not guarantee anything.

The proof transformation feature does not maintain the tagged inference rules. All tags are erased before transformation.

The next section provides sufficient information to start writing your own proofs.

Syntax for proof scripts

The proof assistant epsilon processes a proof script which is stored as a file in the system. The main content of a proof script is a proof, which is a list of proof steps, each of which consists of the following ingredients.

  1. A formula to claim
  2. A reason of claiming the formula
  3. Optional tag for future reference to this proof step

Formula is what we saw in the previous section of this documentation. A reason is either a name of an axiom, an assumption, or an inference rule which may come with an additional parameters. A tag is a reference name, which is a string starting with #, given to the proof step, which can be used to point this proof step later on.

The follwoing variable names, constant names, and predicate names are available by default.

Kind Default names
Variable x, y, z, u, v
Nullary constant a, b, c
Unary function constant f, g
Binary function constant h
Propositional variable A, B, C
Unary predicate variable P, Q
Binary predicate variable R

Any of the above can be followed by an optional number as an index. For example, x, x1, x2 are all distinct variables. Binary and unary constants and predicate variables should have a suitable number of arguments, which is a comma separated list with outer parentheses. For example, R(f(x), c) is a well-formed formula, and on the other hand, P is not; P is a unary predicate variable and one argument is required, but it is missing.

Custom decalarations for variable names, constant names, and predicate names are available. A declaration for variable names is done by a keyword variables followed by a space separated list of variable names, eg.

variables x y u v

For predicates and constants, one has to specify the arity in addition to the names. For any natural number n, a declarations starts with nary-predicates or nary-constants and followed by a space separated list of names, eg.

0ary-predicates A B
1ary-predicates P Q
2ary-predicates R
0ary-constants c a
1ary-constants f g

All the declarations in a proof script must precede any proofs, namely, it is not allowed to put a declaration below the first proof step in a proof script. A custom declaration makes the default names unavailable. For example, assume one made a custom declaration for variables and didn't make any custom declarations for constants names nor predicate names. In this case, the default variable names are gone, while the default constant names and predicate names are still there.

Command name Example Note
variables variables x y Takes at least one variable name
nary-constants 0ary-constants c A natural number should be substituted for n
nary-predicates 2ary-predicates R S A natural number should be substituted for n
deduction-translation Applies the deduction translation to the
current proof
end-proof end-proof Lemma123 Ends the current proof.
A lemma is saved, provided an argument given

Assume E(x) is a formula and X is some name of axiom or inference rule, the syntax of the proof step is given as follows

E(x) by X

and also one can give a tag to this proof step.

E(x) by X #myproofstep

The proof assistant epsilon supports the following axioms.

Axiom name Scheme Note
S (A -> B -> C) -> (A -> B) -> A -> C -> associates to the right
K A -> B -> A
C E(t) -> E(eps x E(x)) t is an arbitrary term in this whole table
ConjI A -> B -> A & B & associates to the left and has a higher
priority than ->
ConjE1 A & B -> A
ConjE2 A & B -> B
DisjI1 A -> A | B | associates to the left and has a priority
between -> and &
DisjI2 B -> A | B
DisjE A | B -> (A -> C) -> (B -> C) -> C
ExI E(t) -> ex x E(x)
ExE all x(A(x) -> B) -> (ex y A(y) -> B) x ∉ FV(B) and (x=y or y ∉ FV(A(x)))
AllE all x E(x) -> E(t)
AllShift all x(B -> A(x)) -> (B -> all y A(y)) x ∉ FV(B) and (x=y or y ∉ FV(A(x)))
EFQ bot -> A
DNE ~~A -> A ~ has a higher priority than any of ->,
| and &

In order to pose an assumption, Asm is used as the reason. Whereever the assumption is witten in the proof, either top, middle, or the bottom, it does not make any difference. If a proof comes with assumptions, those assumptions are displayed in the left hand side of

The inference rule MP derives A1, ..., Ak ⊢ B from A1, ..., Ak ⊢ A -> B and A1, ..., Ak ⊢ A, two of which should be somewhere in previous proof steps. Note that the formula A is distinct from any indexed ones A1, ..., Ak. The inference rule Gen derives A1, ..., Ak ⊢ all x E(x) from A1, ..., Ak ⊢ E(x) which should be a previous proof step, under the condition that x doesn't have a free occurrrence in any of the assumptions A1, ..., Ak. The search for suitable proof steps for those inference rules is done automatically. If one wants to explicitly specify the two proof steps, tagged by #one and #two, the arguments should be fed as MP(#one, #two), which is order insensitive.

Inference rule name Note
MP Infers Γ ⊢ B from Γ ⊢ A -> B and Γ ⊢ A
Gen Infers Γ ⊢ all x A(x) from Γ ⊢ A(x), provided x∉FV(Γ)

Other than the axioms and inference rules, there are the following reasons which can be given after by.

Reason name Example Note
Asm A -> A by Asm Makes an assumption.
Taken as a claim if a proof ends with it.
Ref A by Ref To refer to an assumption.
Auto Requires Microsoft's Z3
Use A -> A by Use(identity) A name of a suitable lemma required

Example proofs are found in the examples directory.

To do list

About

Proof assistant for Hilbert style proof systems including predicate calculus and epsilon calculus


Languages

Language:Haskell 100.0%