Prolog reasoning about predicates - prolog

Assume someone wrote the following huge list of clauses:
loves(me, wife).
loves(me, dog).
loves(wife, dog).
hates(me, enemy).
attracts(iron, magnet).
...
Now I want to automatically generate reciprocal clauses given some higher-order predicate and rule similar to:
reciprocal([loves, hates, attracts, ...]).
some_conclusion :- some_premises.
so that I have the following expected result:
?- loves(wife, me).
true.
To keep things simple, I ignored the list argument and instead defined the simple clause reciprocal(loves). with some complex rule using reciprocal(X), but I can't seem to assert a rule with success.
I've tried different variations and orderings of
assert(
Y :- (reciprocal(P), Y =.. [P, B, A], X =.. [P, A, B], call(X))
).
or (to add the deduced clauses themselves)
assert(Y), reciprocal(P), Y =.. [P, B, A], X =.. [P, A, B], call(X)
but I've only got false or errors like Arguments are not sufficiently instantiated using SWI-Prolog.
My question is (obviously): how can I make this rule work? I don't care whether the rule is part of the database or just a preprocessor to add the actual clauses to the database (although the former would be preferable), I just want to learn how to reason about predicates (i.e. how to use higher-order predicates).
Note: I've learned logic programming since a month only, and I want to try out some ideas from Notation3 and N-triples etc.
EDIT:
The missing part, the absolute cherry on the cake, is some dynamic solution using a rule similar to
Y :- (reciprocal(P), call(P, A, B), Y =.. [P, B, A]).
If anyone has some solution for this one, please post it!

I found the way to add clauses one by one to the database as such (MVP):
?- [user].
|: loves(me, wife).
|: loves(me, dog).
|: loves(wife, dog).
|: reciprocal(loves).
|: (Ctrl-Z)
?- dynamic loves/2 %! seems necessary for next clause...
true.
?- reciprocal(P), X =.. [P, A, B], Y =.. [P, B, A], assert(X :- (Y, !)).
P = loves,
X = loves(A, B),
Y = loves(B, A).
?- loves(wife, me).
true.
It seems I didn't arrange the 'rule' in this way which is procedurally sound.
Now I'll focus on findall to avoid requiring user input (the ; after each solution for P).
EDIT:
I completed all but one feature... Given a file db.pl containing
loves(me, wife).
loves(me, dog).
loves(wife, dog).
attracts(iron, magnets).
and a file rules.pl containing
reciprocal([
loves,
attracts
]).
parse_reciprocal([]).
parse_reciprocal([H|T]) :-
X =.. [H, A, B], Y =.. [H, B, A], dynamic(H/2), assert(X :- (Y, !)),
parse_reciprocal(T).
:- initialization reciprocal(L), parse_reciprocal(L).
I succeed at my first goal
?- [db].
true.
?- [rules].
true.
?- loves(dog, wife).
true.
?- attracts(magnets, iron).
true.
The missing part, the absolute cherry on the cake, is some dynamic solution using a rule similar to
Y :- (reciprocal(P), call(P, A, B), Y =.. [P, B, A]).

Related

Is there a non-unifying alternative to member/2 in SWI-Prolog?

In prolog, the difference between A = B and A == B is that = tries to unify A with B, while == will only succeed if A and B are already unified.
member/2 does seem to perform unification.
Example session:
?- A = B.
A = B.
?- A == B.
false.
?- member(A, [B]).
A = B.
I have been looking but I can't find a non-unifying alternative to member/2, but not found anything. Is there something built in or do I have to invent my own thing? As I'm rather new to Prolog I don't trust myself with writing a performant version of this.
EDIT:
I came up with the following, though I don't know if the cut is correct. Without it, it seems to deliver two answers for that branch of the code (true, followed by false) though. I'd also still like to know if there is a standard library function for this.
member_eq(_, []) :-
false.
member_eq(X, [H|_]) :-
X == H,
!.
member_eq(X, [_|T]) :-
member_eq(X, T).
You may slightly modify builtin predicate member/2 to use ==/2 instead of unification:
member_not_bind(X, [H|T]) :-
member_not_bind_(T, X, H).
member_not_bind_(_, X, Y):- X==Y.
member_not_bind_([H|T], X, _) :-
member_not_bind_(T, X, H).
Sample run:
?- L=[a,b,c(E)], member_not_bind(A, L).
false.
?- A=c(E),L=[a,b,c(E)], member_not_bind(A, L).
A = c(E),
L = [a, b, c(E)].
I leave this here as it solves a related question (checking if X may unify with any item in L without actually performing the bindings)
You can use double negation like this:
member_not_bind(X, L):- \+(\+(member(X, L))).
Sample runs:
?- A=c(e),L=[a,b,c(E)], member_not_bind(A, L).
A = c(e),
L = [a, b, c(E)].
?- A=d(E),L=[a,b,c(E)], member_not_bind(A, L).
false.

The unification algorithm in Prolog

I'm trying to program the unification algorithm in Prolog to verify if two expressions can unify by returning boolean True/False:
EDIT.
I found this implementation usefull:
from: http://kti.mff.cuni.cz/~bartak/prolog/data_struct.html
unify(A,B):-
atomic(A),atomic(B),A=B.
unify(A,B):-
var(A),A=B. % without occurs check
unify(A,B):-
nonvar(A),var(B),A=B. % without occurs check
unify(A,B):-
compound(A),compound(B),
A=..[F|ArgsA],B=..[F|ArgsB],
unify_args(ArgsA,ArgsB).
unify_args([A|TA],[B|TB]):-
unify(A,B),
unify_args(TA,TB).
unify_args([],[]).```
Here is a partial implementation of something like the Martelli and Montanari unification algorithm described at https://en.wikipedia.org/wiki/Unification_(computer_science)#A_unification_algorithm. The comments for each part refer to the corresponding rewrite rule from the algorithm. Note that there is no need for an explicit conflict rule, we can just fail if no other rule applies.
% assuming a universe with function symbols g/2, p/2, q/2
% identical terms unify (delete rule)
unify(X, Y) :-
X == Y,
!.
% a variable unifies with anything (eliminate rule)
unify(X, Y) :-
var(X),
!,
X = Y.
% an equation Term = Variable can be solved as Variable = Term (swap rule)
unify(X, Y) :-
var(Y),
!,
unify(Y, X).
% given equal function symbols, unify the arguments (decompose rule)
unify(g(A, B), g(X, Y)) :-
unify(A, X),
unify(B, Y).
unify(p(A, B), p(X, Y)) :-
unify(A, X),
unify(B, Y).
unify(q(A, B), q(X, Y)) :-
unify(A, X),
unify(B, Y).
Examples:
?- unify(q(Y,g(a,b)), p(g(X,X),Y)).
false.
?- unify(q(Y,g(a,b)), q(g(X,X),Y)).
false.
?- unify(q(Y,g(a,a)), q(g(X,X),Y)).
Y = g(a, a),
X = a.
One or two things remain for you to do:
Generalize the decompose rule to deal with arbitrary terms. You might find the =.. operator useful. For example:
?- Term = r(a, b, c), Term =.. FunctorAndArgs, [Functor | Args] = FunctorAndArgs.
Term = r(a, b, c),
FunctorAndArgs = [r, a, b, c],
Functor = r,
Args = [a, b, c].
You will need to check if two terms have the same functor and the same number of arguments, and whether all corresponding pairs of arguments unify.
Find out if your professor would like you to implement the occurs check, and if yes, implement it.

Adding to a list of lists in Prolog

I am currently attempting to write a Prolog program which will add a given character to the end of a list. The list's I want to append are elements within a list. This is what I currently have.
extends(X, [], []).
extends(X, [[Head]|Lists], Y):-
append([X], [Head], Y),
extends(X, Lists, [Y]).
Here I'm attempting to concatenate X and Head, storing it in Y. However I want Y to be a list of lists, so when it repeats the process again the next concatenation will be stored also in Y. So at the end of the program Y would store the results of all the concatenations. I would want the result to look like as follows.
?- extends(a, [[b,c], [d,e,f], [x,y,z]], Y).
Y = [[b,c,a], [d,e,f,a], [x,y,z,a]].
Could anyone help me out with this?
You want to apply some operation to corresponding elements of two lists. That operation talks about lists itself. It's easy to get confused with the nested levels of lists, so let's try not to think in those terms. Instead, define first a predicate that does the extension of one list:
element_list_extended(Element, List, Extended) :-
append(List, [Element], Extended).
This behaves as follows, using cases from your example:
?- element_list_extended(a, [b, c], Extended).
Extended = [b, c, a].
?- element_list_extended(a, List, [x, y, z, a]).
List = [x, y, z] ;
false.
Looks good so far. All we need to do is to apply this operation to corresponding elements of two lists:
extends(_Element, [], []).
extends(Element, [Xs | Xss], [Ys | Yss]) :-
element_list_extended(Element, Xs, Ys),
extends(Element, Xss, Yss).
And this works:
?- extends(a, [[b,c], [d,e,f], [x,y,z]], Y).
Y = [[b, c, a], [d, e, f, a], [x, y, z, a]] ;
false.
The key to making it work was to decompose the problem into two parts and to solve those simpler parts separately.
Now, if we like, since the definition of element_list_extended/3 is a single clause containing a single goal, we might decide to do without it and inline its definition into extends/3:
extends(_Element, [], []).
extends(Element, [Xs | Xss], [Ys | Yss]) :-
append(Xs, [Element], Ys),
extends(Element, Xss, Yss).
As you can see, you were quite close! You just had some superfluous brackets because you got confused about list nesting. That's precisely where decomposing the problem helps.
(As the other answer said, SWI-Prolog has some useful libraries that allow you to express even this in even shorter code.)
extends(PostFix, ListIn, ListOut) :-
maplist({PostFix}/[In,Out]>>append(In,[PostFix],Out),ListIn, ListOut).
This is using library(yall) a maplist/3 and append/3.

(SWI)Prolog: Order of sub-goals

I have two, slightly different, implementations of a predicate, unique_element/2, in Prolog. The predicate succeeds when given an element X and a list L, the element X appears only once in the list. Below are the implementations and the results:
Implementation 1:
%%% unique_element/2
unique_element(Elem, [Elem|T]) :-
not(member(Elem, T)).
unique_element(Elem, [H|T]) :-
member(Elem, T),
H\==Elem,
unique_element(Elem, T),
!.
Results:
?- unique_element(X, [a, a, b, c, c, b]).
false.
?- unique_element(X, [a, b, c, c, b, d]).
X = a ;
X = d.
Implementation 2:
%%% unique_element/2
unique_element(Elem, [Elem|T]) :-
not(member(Elem, T)).
unique_element(Elem, [H|T]) :-
H\==Elem,
member(Elem, T),
unique_element(Elem, T),
!.
In case you didn't notice at first sight: H\==Elem and member(Elem, T) are flipped on the 2nd impl, rule 2.
Results:
?- unique_element(X, [a, a, b, c, c, b]).
X = a.
?- unique_element(X, [a, b, c, c, b, d]).
X = a ;
X = d.
Question: How does the order, in this case, affect the result? I realize that the order of the rules/facts/etc matters. The two specific rules that are flipped though, don't seem to be "connected" or affect each other somehow (e.g. a cut in the wrong place/order).
Note: We are talking about SWI-Prolog here.
Note 2: I am aware of, probably different and better implementations. My question here is about the order of sub-goals being changed.
H\==Elem is testing for syntactic inequality at the point in time when the goal is executed. But later unification might make variables identical:
?- H\==Elem, H = Elem.
H = Elem.
?- H\==Elem, H = Elem, H\==Elem.
false.
So here we test if they are (syntactically) different, and then they are unified nevertheless and thus are no longer different. It is thus just a temporary test.
The goal member(Elem, T) on the other hand is true if that Elem is actually an element of T. Consider:
?- member(Elem, [X]).
Elem = X.
Which can be read as
(When) does it hold that Elem is an element of the list [X]?
and the answer is
It holds under certain circumstances, namely when Elem = X.
If you now mix those different kinds of goals in your programs you get odd results that can only explained by inspecting your program in detail.
As a beginner, it is best to stick to the pure parts of Prolog only. In your case:
use dif/2 in place of \==
do not use cuts - in your case it limits the number of answers to two. As in
unique_element(X, [a,b,c])
do not use not/1 nor (\+)/1. It produces even more incorrectness. Consider unique_element(a,[a,X]),X=b. which incorrectly fails while X=b,unique_element(a,[a,X]) correctly succeeds.
Here is a directly purified version of your program. There is still room for improvement!
non_member(_X, []).
non_member(X, [E|Es]) :-
dif(X, E),
non_member(X, Es).
unique_element(Elem, [Elem|T]) :-
non_member(Elem, T).
unique_element(Elem, [H|T]) :-
dif(H,Elem),
% member(Elem, T), % makes unique_element(a,[b,a,a|Xs]) loop
unique_element(Elem, T).
?- unique_element(a,[a,X]).
dif(X, a)
; false. % superfluous
?- unique_element(X,[E1,E2,E3]).
X = E1, dif(E1, E3), dif(E1, E2)
; X = E2, dif(E2, E3), dif(E1, E2)
; X = E3, dif(E2, E3), dif(E1, E3)
; false.
Note how the last query reads?
When is X a unique element of (any) list [E1,E2,E3]?
The answer is threefold. Considering one element after the other:
X is E1 but only if it is different to E2 and E3
etc.
TL;DR: Read the documentation and figure out why:
?- X = a, X \== a.
false.
?- X \== a, X = a.
X = a.
I wonder why you stop so close from figuring it out yourself ;-)
There are too many ways to compare things in Prolog. At the very least, you have unification, which sometimes can compare, and sometimes does more; than you have equvalence, and its negation, the one you are using. So what does it do:
?- a \== b. % two different ground terms
true.
?- a \== a. % the same ground term
false.
Now it gets interesting:
?- X \== a. % a free variable and a ground term
true.
?- X \== X. % the same free variable
false.
?- X \== Y. % two different free variables
true.
I would suggest that you do the following: figure out how member/2 does its thing (does it use unification? equivalence? something else?) then replace whatever member/2 is using in all the examples above and see if the results are any different.
And since you are trying to make sure that things are different, try out what dif/2 does. As in:
?- dif(a, b).
or
?- dif(X, X).
or
?- dif(X, a).
and so on.
See also this question and answers: I think the answers are relevant to your question.
Hope that helps.
Here is another possibility do define unique_element/2 using if_/3 and maplist/2:
:- use_module(library(apply)).
unique_element(Y,[X|Xs]) :-
if_(Y=X,maplist(dif(Y),Xs),unique_element(Y,Xs)).
In contrast to #user27815's very elegant solution (+s(0)) this version does not build on clpfd (used by tcount/3). The example queries given by the OP work as expected:
?- unique_element(a,[a, a, b, c, c, b]).
no
?- unique_element(X,[a, b, c, c, b, d]).
X = a ? ;
X = d ? ;
no
The example provided by #false now succeeds without leaving a superfluous choicepoint:
?- unique_element(a,[a,X]).
dif(a,X)
The other more general query yields the same results:
?- unique_element(X,[E1,E2,E3]).
E1 = X,
dif(X,E3),
dif(X,E2) ? ;
E2 = X,
dif(X,E3),
dif(X,E1) ? ;
E3 = X,
dif(X,E2),
dif(X,E1) ? ;
no
Can you not define unique_element like tcount Prolog - count repetitions in list
unique_element(X, List):- tcount(=(X),List,1).

PROLOG - clause returns true for constants, but won't find solution for variable?

After many years of abstinence of the PROLOG programming language, I'm trying to get into it again.
And promptly there, something confused me.
(I am using SWI prolog 6.4.1. on windows)
Consider the following defined:
father(jack, clara).
father(jack, sophie).
mother(angela,clara).
mother(angela,sophie).
parent(A, B) :- father(A, B).
parent(A, B) :- mother(A, B).
sibling( A, B ) :-
A \= B,
parent(P, A),
parent(P, B).
Now, if I "ask" the interpreter:
sibling(clara, sophie).
true is the answer.
But if I try to get the siblings of e.g. clara:
sibling(clara, X).
The answer is just false.
Just as
findall( X, sibling(clara, X ), L ).
returns an empty list.
Why?
To prove sibling(clara, X), you first need to prove clara \= x. But that doesn't work because it reduces to \+ clara = X, were \+ is the infamous negation as failure: Prolog tries to prove clara = X, which succeeds, and concludes that clara \= X must therefore be false.
You should either reorder your program to do the \= check last instead of first, or use dif(clara, X).

Resources