A toolkit for proving classical first order predicate logic and propositional logic formulas, answering questions and converting between different syntaxes for logic.
Think about logic as a generic way of writing rules: proving formulas and answering questions is a way of detecting what follows from the rules we wrote.
The predicate logic toolkit is built around a high-performance reasoner gkc (see the paper) and runs in the browser using Wasm without any server involvement. For more advanced use you may want to download the command-line version.
The educational propositional provers are self-contained, no-dependencies, easy-to-hack javascript code under the MIT licence. The focus is on simplicity, ease of use, hacking and experimenting, not state of the art efficiency-wise.
Use these tools for learning logic and experimenting with reasoners.
Contents:
A good way to learn syntax and meaning is to go through the explanations in the simple examples. Nevertheless, the following presents a compact description.
Gkc supports several syntaxes: the simple syntax used in most examples, TPTP syntax and JSON-LD-LOGIC syntax: see the separate Json menu for the examples and explanations.
The simple syntax is an extension of the TPTP FOF and CNF syntaxes as described in the TPTP technical manual, combining them into one and extending with a list type and a few convenience predicates and constructions.
The simple syntax input is a list of logical formulas, each terminated with a period. The whole list is interpreted as a big conjunction (and) of formulas.
Each formula is either a literal (a positive or a negative atom) or is constructed from simpler formulas using logical operators like
&
for conjunction (and),
|
for disjunction (or),
-
or ~
for negation (not),
=>
for implication (if-then),
! [variables] : formula
(example ! [X,Y] : p(X,Y)
)
? [variables] : formula
.
Additional logical operators will be described later.
An atom is a predicate symbol applied to a number of term arguments like
father(john,pete)
and denoting a relation between the arguments.
A negated atom has a minus sign prepended
like -father(john,pete)
.
Equality is a special predicate written like gender(john)=male
and meaning, as expected, that the term on the left side is equal to the term on the
right side. Inequality is another special predicate written like
gender(john)!=female
The terms denote objects. A simplest term is just a symbol/constant
like john
: think of it as a post-it note glued to some real thing or idea:
a real object may have many such notes glued to it. A variable like X
is also a term: think of it as placeholder which can be replaced by any other term.
Variables must start with an upper-case character, other symbols with a lower-case character. There are also other options for symbols, more about these below.
A variable which is not in the scope of a for all !
or
exists quantifier ?
is treated as if it is bound by the
for all quantifier. In fact, explicit quantifiers can be skipped
altogether: they are just a convenience construction.
Numbers like 12 or 3.45 can be also used as constant terms.
More complex terms can be constructed from simpler terms using functions like
age(mother(john))
or $sum(X,$multiply(2,X))
. A value of
such a function is not always defined: it can be used just as a constructor for
complex terms. A function may also be defined on some other terms, like $sum
is defined on numbers, but not on lists. You may define functions yourself using equality
like mother(john)=eve
or age(mother(john))=age(mother(pete))
.
Both line comments %
and multi-line comments /* ... */
are available.
Think about an atom like father(john,pete)
as saying
that there is a row with two cells john,pete
in a two-column
table father
. In fact, the core ideas of relational database systems
and SQL stem from this interpretation, although they branch off into specific
directions. For example, in conventional logic notation all the elements
in row columns are given positionally, one after another, while in SQL columns have names.
Each predicate like father
should be seen as giving rise
to two different tables: one for positive facts and another for negative
facts: -father(john,mike)
means that in the negative -father
table there is a row john,mike
. Importantly, the facts
we state only say that there are these-and-these rows, but they do not
say that there cannot be other rows, unknown to us. This inpreretation
is called open world
assumption contrasting the
closed world
assumption used in database systems and Prolog.
Logical formulas are then understood as rules for deriving new rows in
the tables. For example. father(X,Y) => child(Y,X)
means that
if there is a row in the father
table, the corresponding row with
arguments exchanged must be put into the child
table.
A rule like -father(X,X)
can be seen as saying that the
negative -father
table contains rows like object_n,object_n
for all objects. Since
we could potentially have infinitely many objects (think numbers and terms
constructed from functions) we cannot always physically fill in all the rows.
A contradiction occurs when a row in the positive table also occurs
in the corresponding negative table. For example, saying father(john,john)
would contradict the row john,john
in the negative -father
table, given the rule -father(X,X)
.
Finding a contradiction is used as one convenient way of finding whether some
fact/row follows from our rules. Suppose we have two statements
father(john,pete)
and
father(X,Y) => child(Y,X)
and we want to prove an obvious fact that child(pete,john)
follows
from these two. Let us denote these three statements as a, b, c. Then
we really want to prove that a & b => c
is true,
which is equivalent to the question of whether the negated form
-(a & b => c)
is contradictory, which is equivalent
to the question of whether the conjunction
a & b & -c
is contradictory. Hence we could add
the negated form -child(pete,john)
to our two initial statements
and check whether a contradiction can be found.
Finally, an important principle of logic as used here is that no common knowledge
- or rather, no knowledge at all - is assumed. Everything we want to use
must be written explicitly as logical facts and rules. The names of the predicates
mean nothing in itself: a reasoner knows nothing about the father
predicate except what is explicitly written in the problem statement. You could
replace all the nice human-understandable names with meaningless names and the
logical meaning of the problem statement would be exactly the same.
Ordinary symbols must start with lower-case character and
must not contain any whitespace or non-alphanumeric characters
like (, -, ~, =, "
etc: however, underscore _
and dollar $
are allowed.
You can put whitespace or any symbol except a single quote
into symbols by surrounding the symbol with single quote symbols like this:
'John Smith'
. Internal quotes must be prefixed by a backslash
\
.
Additionally you can make a symbol variable by prefixing it with a question
mark like this: ?smth
.
Double quotes surrounding a symbol like "John Smith"
have a special meaning: the symbol is treated as distinct (essentially,
a string) meaning that it is known to be unequal to any syntactically
different distinct symbols, numbers and lists. This has real effect for
equality and string predicates, but not otherwise.
In addition to the typically used constructions &
,|
,
-
/~
, =>
,
!
, ?
the syntax provides a number of additional infix logical
constructions with two arguments. The full list is:
|
for disjunction,
&
for conjunction,
<=>
for equivalence,
=>
for implication,
<=
for reverse implication,
<~>
for non-equivalence (XOR),
~|
for negated disjunction (NOR),
~&
for negated conjunction (NAND),
@
for application, used mainly in the higher-order context in TPTP.
$
.
$ans
is used for finding the actual terms we look for as answers: see the
answers example.
The numbers and arithmetic functions and predicates are defined following the TPTP arithmetic system plus a few convenience operators for writing infix terms:
Note: these comparison predicates and arithmetic functions take exactly two arguments.
Example: $less($sum(1,$to_int(2.1)),$product(3,3))
.
Example: $less(1+(1+2),(3*3))
Note: these convenience functions take also exactly two arguments.
NB! Do not use a variable or a non-numeric constant as a first element of the
infix arithmetic expression like p(X*2)
, otherwise
the whole expression will be parsed as a single variable X*2. No such restrictions
apply for the prefix form.
You can use the list syntax like []
for the empty list and [a,b,c]
for a three-element list. The bracket notation is syntactic sugar for
the constant $nil
and functional list-constructing term
$list(a,$list(b,$list(c,$nil)))
, respectively.
Observe that $list(X0,X1)
constructs a list by prepending X0
to the list X1
,
which is generally different from a two-element list [X0,X1]
.
Terms constructed using $list
or $nil
are interpreted as having a
list type:
$is_list(L)
evaluates to true if A is a list and false is A
is a number or a distinct symbol.
$first(L)
returns the first element of the list.
$rest(L)
returns the rest of the list, i.e. the result of removing the first element.
Symbols in double quotes like in "person"
stand for
for distinct symbols
which can be viewed as a string type. A distinct symbol is not equal to any
other syntactically different symbol and not equal to any numbers or lists.
Gkc defines a function and three predicates on distinct symbols:
$strlen(S)
returns the integer length of a distinct symbol S as a string.
$substr(A,B)
evaluates to true if a distinct symbol A is a substring of a distinct symbol B,
and false otherwise, provided that A and B are distinct symbols.
$substrat(A,B,C)
evaluates to true if a distinct symbol A is a substring of a
distinct symbol B exactly at the integer position C (starting from 0), and false otherwise,
provided that A and B are distinct symbols and C is an integer.
$is_distinct(A)
evaluates to true if A is a distinct symbol and
false if A is a number or a list.
Automated reasoners prove logical theorems, answer questions and (sometimes) can detect unprovability. Basically, they are highly optimized search systems often encompassing a large number of different search strategies.
Most of the reasoners are specialized to perform well on some specific kinds of problems or are extended with additional special capabilities. For example, the gkc prover used in this site is specialized for very large problems, although this capability cannot be used well in the confined browser environment. Hint: you may want to download the command-line version for more advanced use.
A large special category of provers focuses on propositional logic. See the Propositional logic menu for checking out their core ideas. The easiest way to find top level propositional solvers is to check the The international SAT Competition: you will see the competition results for various problem categories, can download competition problems, source code and descriptions of the provers. A good alternative choice is to check out minisat: an easily extensible and hackable state of the art solver which is a basis for several other state of the art solvers. You can run crypto-minisat in your browser, compiled from C to javascript using LLVM.
Good reading material for predicate logic provers:
For the generic reasoners, mostly niche applications so far. The eighties-hype of expert systems and symbolic A.I. with little practical value lead to the famous A.I. winter. This said, the propositional and SMT provers are seriously used for verification, test generation and configuration management.
The main obstacles for wider use: (a) it is very hard, or practically impossible (although theoretically possible) to hand-encode most of human expert knowledge in logical rules, (b) proof search without intelligent guidance is really hard, (c) basic predicate logic without probabilities, default reasoning etc etc is extremely impractical for encoding commonsense knowledge.
There is hope for a new rise of reasoners as parts of a machine-learning-and-reasoning hybrid systems performing common sense reasoning. Check out a preprint proposing several mechanisms in this direction.
Use either a conventional formula syntax like
(a -> b) & a & -b
or a dimacs
version of the clause normal form syntax like
-1 2
1
-2
which is a conjunction of disjunction lines with numbers standing for variables:
the last example means simply (-x1 v x2) & x1 & -x2
For conventional formula syntax:
For dimacs you may use or skip the initial comment lines starting with c, the special p line and the final 0 symbols at the end of each disjunct. We allow the trailing 0-s only at the end of a line.
I.e. you may use the full dimacs version like
c comment
p cnf 2 3
-1 2 0
1 0
-2 0
or just
-1 2
1
-2
Solving a classical propositional formula means looking for such values of variables that the formula becomes true. For example, (a -> b) & a becomes true if and only if both a and b are assigned true.
You can select and try out several solver algorithms: the "DPLL better" is the best solver amongst the options offered on our page. Read the "Methods for solving formulas" section below about the differences between algorithms. One single suitable set of values is enough as a solution: our solver algorithms stop and do not try to find additional solutions. Some of the solver algorithms output the suitable values, but some do not, or output a partial set. It may also happen that the formula is false for all possible values of variables: if so, the solver algorithms report that after exhausting the search options. For example, (a -> b) & a & -b is always false. Notice that you can check whether some formula F is always true by trying to solve the negated formula -F: in case -F is always false, F must be always true. Our solvers may optionally output a trace of the search process. Select "html trace" to see the search process. For formula-syntax input our solvers first convert the formula to a clause normal form: for certain kinds of formulas this conversion step may create a huge output, but in most cases it is a sensible simplification before actual search.
The options for the type of a problem are:
For DPLL try out 200 variables or more. Truth table solvers start running into trouble with more than 20 variables. The resolution provers are a bit better than the truth table solvers, yet much worse than the DPLL solvers.
You can also browse and read the contents of a file into the input area: essentially copy-paste from a file.