Prolog - 2 ways for progenitor predicate implementation - prolog

Given a set of facts that represent parent-child relationships via the predicate parent/2, what are the differences when defining the relation "progenitor"(ancestor) with the predicates pred1/2 and pred2/2 as shown below?
pred1(X,Z) :- parent(X,Z).
pred1(X,Z) :- parent(X,Y), pred1(Y,Z).
pred2(X,Z) :- parent(X,Z).
pred2(X,Z) :- parent(Y,Z), pred2(X,Y).

The major difference are the termination properties of both, provided there are some cycles in the relation. To understand this, I will use a failure-slice which cuts down the program to its essence w.r.t. termination.
pred1(X,Z) :- false, parent(X,Z).
pred1(X,Z) :- parent(X,Y), pred1(Y,Z). % Z has no influence
pred2(X,Z) :- false, parent(X,Z).
pred2(X,Z) :- parent(Y,Z), pred2(X,Y). % X has no influence
For pred1/2, the second argument has no influence on termination at all, whereas in pred2/2, the first argument has no influence. To see this, try the original definitions with the fact:
parent(a,a).
?- pred1(b, _), false.
false. % terminates
?- pred2(b, _), false.
loops.
?- pred1(_, b), false.
loops.
?- pred2(_, b), false.
false. % terminates
See closure/3 for a general approach to avoid such loops.
For completeness, here is another variation of transitive closure which has some conceptual advantages:
pred3(X,Z) :- parent(X,Z).
pred3(X,Z) :- pred3(X,Y), pred3(Y,Z).
Alas, its termination properties are worst. In fact, it never terminates, as is testified by the following fragment:
pred3(X,Z) :- false, parent(X,Z).
pred3(X,Z) :- pred3(X,Y), false, pred3(Y,Z).
In this fragment, the first argument is only passed on. So, no matter, what the arguments are, the program will always loop!
?- pred3(b,b), false.
loops.

Related

I have defined multiple predicates that seem to share a common form

All of these predicates are defined in pretty much the same way. The base case is defined for the empty list. For non-empty lists we unify in the head of the clause when a certain predicate holds, but do not unify if that predicate does not hold. These predicates look too similar for me to think it is a coincidence. Is there a name for this, or a defined abstraction?
intersect([],_,[]).
intersect(_,[],[]).
intersect([X|Xs],Ys,[X|Acc]) :-
member(X,Ys),
intersect(Xs,Ys,Acc).
intersect([X|Xs],Ys,Acc) :-
\+ member(X,Ys),
intersect(Xs,Ys,Acc).
without_duplicates([],[]).
without_duplicates([X|Xs],[X|Acc]) :-
\+ member(X,Acc),
without_duplicates(Xs,Acc).
without_duplicates([X|Xs],Acc) :-
member(X,Acc),
without_duplicates(Xs,Acc).
difference([],_,[]).
difference([X|Xs],Ys,[X|Acc]) :-
\+ member(X,Ys),
difference(Xs,Ys,Acc).
difference([X|Xs],Ys,Acc) :-
member(X,Ys),
difference(Xs,Ys,Acc).
delete(_,[],[]).
delete(E,[X|Xs],[X|Ans]) :-
E \= X,
delete(E,Xs,Ans).
delete(E,[X|Xs],Ans) :-
E = X,
delete(E,Xs,Ans).
There is an abstraction for "keep elements in list for which condition holds".
The names are inclide, exclude. There is a library for those in SWI-Prolog that you can use or copy. Your predicates intersect/3, difference/3, and delete/3 would look like this:
:- use_module(library(apply)).
intersect(L1, L2, L) :-
include(member_in(L1), L2, L).
difference(L1, L2, L) :-
exclude(member_in(L2), L1, L).
member_in(List, Member) :-
memberchk(Member, List).
delete(E, L1, L) :-
exclude(=(E), L1, L).
But please take a look at the implementation of include/3 and exclude/3, here:
https://www.swi-prolog.org/pldoc/doc/_SWI_/library/apply.pl?show=src#include/3
Also in SWI-Prolog, in another library, there are versions of those predicates called intersection/3, subtract/3, delete/3:
https://www.swi-prolog.org/pldoc/doc/_SWI_/library/lists.pl?show=src#intersection/3
https://www.swi-prolog.org/pldoc/doc/_SWI_/library/lists.pl?show=src#subtract/3
https://www.swi-prolog.org/pldoc/doc_for?object=delete/3
Those are similar in spirit to your solutions.
Your next predicate, without_duplicates, cannot be re-written like that with include/3 or exclude/3. Your implementation doesn't work, either. Try even something easy, like:
?- without_duplicates([a,b], L).
What happens?
But yeah, it is not the same as the others. To implement it correctly, depending on whether you need the original order or not.
If you don't need to keep the initial order, you can simply sort; this removes duplicates. Like this:
?- sort(List_with_duplicates, No_duplicates).
If you want to keep the original order, you need to pass the accumulated list to the recursive call.
without_duplicates([], []).
without_duplicates([H|T], [H|Result]) :-
without_duplicates_1(T, [H], Result).
without_duplicates_1([], _, []).
without_duplicates_1([H|T], Seen0, Result) :-
( memberchk(H, Seen0)
-> Seen = Seen0 , Result = Result0
; Seen = [H|Seen0], Result = [H|Result0]
),
without_duplicates_1(T, Seen, Result0).
You could get rid of one argument if you use a DCG:
without_duplicates([], []).
without_duplicates([H|T], [H|No_duplicates]) :-
phrase(no_dups(T, [H]), No_duplicates).
no_dups([], _) --> [].
no_dups([H|T], Seen) -->
{ memberchk(H, Seen) },
!,
no_dups(T, Seen).
no_dups([H|T], Seen) -->
[H],
no_dups(T, [H|Seen]).
Well, these are the "while loops" of Prolog on the one hand, and the inductive definitions of mathematical logic on the other hand (See also: Logic Programming, Functional Programming, and Inductive Definitions, Lawrence C. Paulson, Andrew W. Smith, 2001), so it's not surprising to find them multiple times in a program - syntactically similar, with slight deviations.
In this case, you just have a binary decision - whether something is the case or not - and you "branch" (or rather, decide to not fail the body and press on with the selected clause) on that. The "guard" (the test which supplements the head unification), in this case member(X,Ys) or \+ member(X,Ys) is a binary decision (it also is exhaustive, i.e. covers the whole space of possible X)
intersect([X|Xs],Ys,[X|Acc]) :- % if the head could unify with the goal
member(X,Ys), % then additionally check that ("guard")
(...action...). % and then do something
intersect([X|Xs],Ys,Acc) :- % if the head could unify with the goal
\+ member(X,Ys), % then additionally check that ("guard")
(...action...). % and then do something
Other applications may need the equivalent of a multiple-decision switch statement here, and so N>2 clauses may have to be written instead of 2.
foo(X) :-
member(X,Set1),
(...action...).
foo(X) :-
member(X,Set2),
(...action...).
foo(X) :-
member(X,Set3),
(...action...).
% inefficient pseudocode for the case where Set1, Set2, Set3
% do not cover the whole range of X. Such a predicate may or
% may not be necessary; the default behaviour would be "failure"
% of foo/1 if this clause does not exist:
foo(X) :-
\+ (member(X,Set1);member(X,Set2);member(X,Set3)),
(...action...).
Note:
Use memberchk/2 (which fails or succeeds-once) instead of member/2 (which fails or succeeds-and-then-tries-to-succeed-again-for-the-rest-of-the-set) to make the program deterministic in its decision whether member(X,L).
Similarly, "cut" after the clause guard to tell Prolog that if a guard of one clause succeeds, there is no point in trying the other clauses because they will all turn out false: member(X,Ys),!,...
Finally, use term comparison == and \== instead of unification = or unification failure \= for delete/3.

Is it possible to write an inconsistent Prolog program using only pure Prolog, cut and `false`?

This one tickled my interest in theory:
Is it possible to write an inconsistent Prolog program, i.e. a program that answers both false and true depending on how it is queried, using only pure Prolog, the cut, and false?
For example, one could query p(1) and the Prolog Processor would says false. But when one queries p(X) the Prolog Processor would give the set of answers 1, 2, 3.
This can be easily achieved with "computational state examination predicates" like var/1 (really better called fresh/1) + el cut:
p(X) :- nonvar(X),!,member(X,[2,3]).
p(X) :- member(X,[1,2,3]).
Then
?- p(1).
false.
?- p(X).
X = 1 ;
X = 2 ;
X = 3.
"Ouch time" ensues if this is high-assurance software. Naturally, any imperative program has no problem going off the rails like this on every other line.
So. can be done without those "computational state examination predicates"?
P.S.
The above illustrates that all the predicates of Prolog are really carrying a threaded hidden argument of the "computational state":
p(X,StateIn,StateOut).
which can be used to explain the behavour of var/1 and friends. The Prolog program is then "pure" when it only calls predicates that neither consult not modify that State. Well, at least that seems to be a good way to look at what is going on. I think.
Here's a very simple one:
f(X,X) :- !, false.
f(0,1).
Then:
| ?- f(0,1).
yes
| ?- f(X,1).
no
| ?- f(0,Y).
no
So Prolog claims there are no solutions to the queries with variables, although f(0,1) is true and would be a solution to both.
Here is one attempt. The basic idea is that X is a variable iff it can be unified with both a and b. But of course we can't write this as X = a, X = b. So we need a "unifiable" test that succeeds without binding variables like =/2 does.
First, we need to define negation ourselves, since it's impure:
my_not(Goal) :-
call(Goal),
!,
false.
my_not(_Goal).
This is only acceptable if your notion of pure Prolog includes call/1. Let's say that it does :-)
Now we can check for unifiability by using =/2 and the "not not" pattern to preserve success while undoing bindings:
unifiable(X, Y) :-
my_not(my_not(X = Y)).
Now we have the tools to define var/nonvar checks:
my_var(X) :-
unifiable(X, a),
unifiable(X, b).
my_nonvar(X) :-
not(my_var(X)).
Let's check this:
?- my_var(X).
true.
?- my_var(1).
false.
?- my_var(a).
false.
?- my_var(f(X)).
false.
?- my_nonvar(X).
false.
?- my_nonvar(1).
true.
?- my_nonvar(a).
true.
?- my_nonvar(f(X)).
true.
The rest is just your definition:
p(X) :-
my_nonvar(X),
!,
member(X, [2, 3]).
p(X) :-
member(X, [1, 2, 3]).
Which gives:
?- p(X).
X = 1 ;
X = 2 ;
X = 3.
?- p(1).
false.
Edit: The use of call/1 is not essential, and it's interesting to write out the solution without it:
not_unifiable(X, Y) :-
X = Y,
!,
false.
not_unifiable(_X, _Y).
unifiable(X, Y) :-
not_unifiable(X, Y),
!,
false.
unifiable(_X, _Y).
Look at those second clauses of each of these predicates. They are the same! Reading these clauses declaratively, any two terms are not unifiable, but also any two terms are unifiable! Of course you cannot read these clauses declaratively because of the cut. But I find this especially striking as an illustration of how catastrophically impure the cut is.

Expanding Prolog clauses without parameters

I am writing a program that transforms other programs by expanding predicates. I usually do this using clause/2, but it doesn't always expand a predicate if it has no parameters:
:- set_prolog_flag('double_quotes','chars').
:- initialization(main).
main :- clause(thing,C),writeln(C).
% this prints "true" instead of "A = 1"
thing :- A = 1.
Is it possible to expand predicates that have no parameters?
Some general remark: Note that this code is highly specific to SWI. In other systems which are ISO conforming you can only access definitions via clause/2, if that predicate happens to be dynamic.
For SWI, say listing. to see what is happening.
?- assert(( thing :- A = 1 )).
true.
?- listing(thing).
:- dynamic thing/0.
thing.
true.
?- assert(( thing :- p(A) = p(1) )).
true.
?- assert(( thing(X) :- Y = 2 )).
true.
?- listing(thing).
:- dynamic thing/0.
thing.
thing :-
p(_)=p(1).
:- dynamic thing/1.
thing(_).
true.
It all looks like some tiny source level optimization.

Why do i get a stack limit exceeded error when defining a predicate that convert the relation of two atoms?

I want to know why does the program goes in an infinite recursion in those cases:
?- love(kay, amanda).
and
?- love(rob, amanda).
And here is the code:
love(amanda, kay).
love(kay, geo).
love(geo, rob).
love(X, Y) :-
love(X, Z),
love(Z, Y).
love(X, Y) :-
love(Y, X).
First, your program always goes into an infinite loop. No matter what names you are using. Even ?- loves(amanda, kay). loops. To better see this rather ask ?- loves(amanda, kay), false. Why am I so sure that your program always loops?
To see this, we need some falsework. By adding goals false into your program, we get a new program requiring less (or equal) inferences.
love(amanda, kay) :- false.
love(kay, geo) :- false.
love(geo, rob) :- false.
love(X, Y) :-
love(X, Z), false,
love(Z, Y).
love(X, Y) :- false,
love(Y, X).
Because this fragment (called a failure-slice) does not terminate, your original program will not terminate. As you can see, all the persons have been removed. And thus actual names cannot influence the outcome.
If you want to fix commutativity, rather introduce a further predicate:
love2(X, Y) :- love1(X, Y).
love2(X, Y) :- love1(Y, X).
And to include the transitive closure, use closure/3:
love3(X, Y) :-
closure(love2, X, Y).

Prolog predicate - infinite loop

I need to create a Prolog predicate for power of 2, with the natural numbers.
Natural numbers are: 0, s(0), s(s(0)) ans so on..
For example:
?- pow2(s(0),P).
P = s(s(0));
false.
?- pow2(P,s(s(0))).
P = s(0);
false.
This is my code:
times2(X,Y) :-
add(X,X,Y).
pow2(0,s(0)).
pow2(s(N),Y) :-
pow2(N,Z),
times2(Z,Y).
And it works perfectly with the first example, but enters an infinite loop in the second..
How can I fix this?
Here is a version that terminates for the first or second argument being bound:
pow2(E,X) :-
pow2(E,X,X).
pow2(0,s(0),s(_)).
pow2(s(N),Y,s(B)) :-
pow2(N,Z,B),
add(Z,Z,Y).
You can determine its termination conditions with cTI.
So, how did I come up with that solution? The idea was to find a way how the second argument might determine the size of the first argument. The key idea being that for all i ∈ N: 2i > i.
So I added a further argument to express this relation. Maybe you can strengthen it a bit further?
And here is the reason why the original program does not terminate. I give the reason as a failure-slice. See tag for more details and other examples.
?- pow2(P,s(s(0))), false.
pow2(0,s(0)) :- false.
pow2(s(N),Y) :-
pow2(N,Z), false,
times2(Z,Y).
It is this tiny fragment which is the source for non-termination! Look at Z which is a fresh new variable! To fix the problem, this fragment has to be modified somehow.
And here is the reason why #Keeper's solution does not terminate for pow2(s(0),s(N)).
?- pow2(s(0),s(N)), false.
add(0,Z,Z) :- false.
add(s(X),Y,s(Z)) :-
add(X,Y,Z), false.
times2(X,Y) :-
add(X,X,Y), false.
pow2(0,s(0)) :- false.
pow2(s(N),Y) :- false,
var(Y),
pow2(N,Z),
times2(Z,Y).
pow2(s(N),Y) :-
nonvar(Y),
times2(Z,Y), false,
pow2(N,Z).
This happends because the of evaluation order of pow2.
If you switch the order of pow2, you'll have the first example stuck in infinite loop..
So you can check first if Y is a var or nonvar.
Like so:
times2(X,Y) :-
add(X,X,Y).
pow2(0,s(0)).
pow2(s(N),Y) :-
var(Y),
pow2(N,Z),
times2(Z,Y).
pow2(s(N),Y) :-
nonvar(Y),
times2(Z,Y),
pow2(N,Z).

Resources