2

I'm new to Prolog and have some doubts.

I need to write a function form_equiv(A,B) that tells us if if B is equivalent to A where A and B are supposed to be propositions.

I know that two propositions are equivalent if

tautology (A iff B) = TRUE

But how could I make a function that checks when a formula is a tautology.

By the way, I cannot use built-in function just AND, OR and NOT.

Right now this what I have so far:


and(P,Q) :- P, Q, !.
or(P,Q) :- (P; Q), !.
impl(P,Q) :- or(not(P),Q).
syss(P,Q) :- and(impl(P,Q),impl(Q,P)).

t.
f :- fail.

t(_).
f(_) :- fail.



:- op(400,xf,not).
:- op(500,xfx,and).
:- op(500,xfx,or).
:- op(600,xfx,impl).
:- op(700,xfx,syss).

I've done a program similar in Haskell but I'm really new to Prolog.

Can anyone help me write a function to check if a formula is a tautology?

Thanks in advance...

1 Answers1

6

First to the logical part: two formulas A and B are equivalent if A → B ∧ B → A holds. If you can prove this formula you are done.

Now to the prolog part:

  • You are mixing up the predicate and the term level: and(A, B) will fail with the error message that A is not sufficiently instantiated. It is much easier to create a predicate eval and define eval(and(A,B)), eval(or(A,B)), etc.
  • You don't have a representation of logical variables. Say my formula is just A. How do I find out if A may be take on true / false? I would propose to explicitly wrap a variable into a a constructor var(Truthvalue) to distinguish it from logical operators during pattern matching. Otherwise, your proof search will try to expand a variable to a more complex formula which is obviously not helpful.
  • You use cuts / fail where they are not necessary: there is only one definition of and(P,Q) such that the cut does not do anything. Similarly, fail makes a rule fail as if it were not there - so you can just delete those rules (unless you use the extralogical features of cut, that is). Remember that cut does not behave like a logical construct and should be avoided as much as possible.
  • You have problems with formulating negation: you have no explicit rule for negation and your predicates for ⊤ and ⊥ are not connected to the rest of your rules (assuming you wanted to define ¬A as A → ⊥). This is due to the assymetry of Prolog rules: deriving a predicate means it is true but non-derivability is not a good means for defining logically false statements. In this case, we can make it explicit, what it means that a formula is false: e.g. A ∧ B is false if A is false or B is false (or both). Let's split eval into two parts then: eval_tt(X) is derivable is if X is true and eval_ff(X) is derivable if X is false.

Putting all these notes together, this a minimal complete calculus for ∧ and ¬ only:

eval_tt(var(true)).
eval_tt(and(A,B)) :-
    eval_tt(A),
    eval_tt(B).
eval_tt(not(A)) :-
    eval_ff(A).


eval_ff(var(false)).
eval_ff(and(A,_B)) :-
    eval_ff(A).
eval_ff(and(_A,B)) :-
    eval_ff(B).
eval_ff(not(A)) :-
    eval_tt(A).

We can query the models for ¬(A ∧ ¬B) with the query:

?- eval_tt(not(and(var(A), not(var(B))))).
A = false ;
B = true.

Would we have used cut or negation as failure, we would probably nothave found both solutions.

Also A ∧ ¬A is unsatisfiable, as expected:

?- eval_tt(and(var(A), not(var(A)))).
false.

Now you just need to extend this minimal calculus by other operators you would like to have (disjunction, implication, equivalence, etc.). Btw. if you have seen sequent calculus, you might recognize some of the ideas :)

Edit: I haven't explained how to get from satisfiability to validity. The problem is the following: an answer substitution from a query to eval_tt(X) only tells us that X is satisfiable. In logic, we usually define X as valid in terms of ¬X being unsatisfiable. This can be expressed in Prolog with negation as failure by defining:

valid(X) :-
  \+ eval_ff(X).

What's the problem here? We check a formula for satisfiability, e.g.

?- valid2(not(and(var(X),not(var(X))))).
true.

but we don't get an answer substitution. In particular if the query is not sufficiently instantiated, we get wrong results:

?- valid(X).
false.

But there's certainly a valid formula - we tried one above. I have not found a good solution yet that can enumerate all the valid formulas.

lambda.xy.x
  • 4,918
  • 24
  • 35
  • 1
    Nice answer! I doubt you can define validity without alluding to the negation being unsatisfiable; though I'm curious if there's a trick to do so as well. – alias Jan 25 '22 at 16:27
  • 1
    Thank you very much! So now I just have to see if eval_tt(and(impl(A,B), impl(B,A)) is true, right? – Massimo2015MX Jan 25 '22 at 20:58
  • 1
    or for example in this case like if i want check A v B is equivalent to B v A, how would I do it? – Massimo2015MX Jan 25 '22 at 21:35
  • 1
    @alias not easily - one could reify the _tt / _ff, collect the truth assignments in a list and saturate that. Doing this naively would certainly degrade performance horribly. – lambda.xy.x Jan 25 '22 at 21:37
  • 2
    @Massimo2015MX I'd formulate `¬(A v B ≡ B ∨ A)` and check for unsatisfiability (otherwise i'd need to check if all truth assignments occur - i mean, `setof(X, eval_ff(A v B ≡ B ∨ A), [])` would also do the trick but setof is again implemented via cut, which I wanted to avoid :) -- I have the short definition of `valid/1` at the end of the answer too. It works, I'm just not very happy with it... – lambda.xy.x Jan 25 '22 at 21:39
  • Thank you very much again @lambda.xy.x, just a last question – Massimo2015MX Jan 26 '22 at 00:50
  • 1
    Is there way where I can put like or(p, q), I mean without having to put var(P) and var(Q) – Massimo2015MX Jan 26 '22 at 00:51
  • 1
    You can try `eval_tt(or(X,Y))` and see what happens - in principle it works but `X` and `Y` are unified with possible formula shapes (that become arbitrarily deep). The best way to avoid this is to introduce a different functor (we want to avoid so called [defaulty](https://www.metalevel.at/prolog/data#clean) datastructures - in Haskell you would also define a data-structure `data Formula = And Formula Formula | Not Formula | Var String Bool` - if you try to omit the `Var` type constructor it's going to lead to trouble) . – lambda.xy.x Jan 26 '22 at 11:10
  • Oh! Thank you very much, it was just for more "aesthetics" purposes, but I really do apreciatte the help! Thank you so much. – Massimo2015MX Jan 27 '22 at 05:33
  • Oh I just saw you wanted constants instead of variables? Then we'd need to change the representation of a variable to var(name) and add a context with mappings name -> truthvalue, e.g. abusing the `-` functor as tuples we create a list `[ p - true, q - false]` and only continue as long as we don't need to assign inconsistent values to variables in the context. – lambda.xy.x Jan 27 '22 at 20:04