Boolean unification in Prolog? - prolog

I am struggling alreay for a long time with the
following problem. I want to make the usual unification
of Prolog more smart.
Basically I want that certain variables understand that
for example 0 = ~1 and 1 = ~0. This doesn't work normally:
?- op(300, fy, ~).
true.
?- X = ~Y, Y = 0.
X = ~0,
Y = 0.
I know that that CLP(B) can do it:
Welcome to SWI-Prolog (threaded, 64 bits, version 8.3.7)
:- use_module(library(clpb)).
true.
?- sat(X=:= ~Y), Y = 0.
X = 1,
Y = 0.
But I require something more lightweight than loading a full CLP(B) library. Any ideas?

It seems that SWI-Prologs when/2 does the job. I am using
the Quine algorithm from here to partially evaluate a boolean expression.
I can then define a Boolean let/2 predicate:
let(X, Y) :-
eval(Y, H),
term_variables(H, L),
( L== [] -> X = H;
cond(L, R),
when(R, let(X, H))).
cond([X], nonvar(X)) :- !.
cond([X,Y|Z], (nonvar(X);T)) :-
cond([Y|Z], T).
The above predicate will reevaluate the expression assigned
to the variable X when ever some variable inside the expression
changes. The expression is associate with the variable X through when/2. Since we see when/2 in the top-level, we see the associated expression:
?- let(X, ~Y).
when(nonvar(Y), let(X, ~Y))
?- let(X, Y+Z).
when((nonvar(Y); nonvar(Z)), let(X, Y+Z))
And we can also do our use case and even more:
?- let(X, ~Y), Y = 0.
X = 1,
Y = 0.
?- let(X, Y+Z), Y = 0.
Y = 0,
when(nonvar(Z), let(X, Z))
?- let(X, Y+Z), Y = 0, Z = 1.
X = 1,
Y = 0,
Z = 1

Related

How to bi-unify a term's operator in Prolog?

I just asked the question in How to unify a term's operator in Prolog? and got good answers.
However, it doesn't completely solve my problem, i.e. =.. only works for single-side.
For example,
test(Expr) :-
1 + 2 = Expr.
test(Expr) :-
1 * 2 = Expr.
?- E =.. [Op, X, Y], test(E). % E can not be a variable!
ERROR: Arguments are not sufficiently instantiated
I hope the query returns
Op = (+),
X = 1,
Y = 2;
Op = (*),
X = 1,
Y = 2.
Very thanks.
expr_op_x_y(Expr, Op, X, Y) :-
when((nonvar(Expr) ; nonvar(Op)), Expr =.. [Op, X, Y]).
?- expr_op_x_y(Expr, Op, X, Y).
when((nonvar(Expr);nonvar(Op)),Expr=..[Op,X,Y]).
?- expr_op_x_y(Expr, Op, X, Y), Op = (+).
Expr = X+Y, Op = (+).
?- expr_op_x_y(Expr, Op, X, Y), Expr = 1+2.
Expr = 1+2, Op = (+), X = 1, Y = 2.
This could also be called a boomy univ for entirely historical reasons.

Prolog, about how to form better clauses

I have following clauses:
num_parent(adam, X) :- !, X = 0.
num_parent(eve, X) :- !, X = 0.
num_parent(X, 2).
When I typed the query:
num_parent(eve,X).
It only returns:
X = 0.
which is what I want.
But when I typed this query:
num_parent(X,0).
it only returns:
X = adam.
So how can I modify the clauses to make it return:
X = adam;
X = eve.
Thanks
First, try to formulate what you want in plain English. You probably want to say:
Everyone has two parents except Adam and Eve who have none.
What about Lilith? Never mind, let's stick to your reading.
num_parent(Person, 2) :-
dif(Person, adam),
dif(Person, eve).
num_parent(adam, 0).
num_parent(eve, 0).
As you can see, it is a bit cumbersome to define this: You have to mention each exceptional person twice. Easy to make an error.
With if_/3 available in library(reif)
for
SICStus and
SWI
you can write more succinctly:
num_parent(Person, Num) :-
if_( ( Person = adam ; Person = eve ), Num = 0, Num = 2 ).
And now some uses:
?- num_parent(eve, Num).
Num = 0.
?- num_parent(X, 0).
X = adam
; X = eve
; false.
?- num_parent(X, 2).
dif(X, eve), dif(X, adam).
?- num_parent(lilith, 2).
true.
The programs returns only X = adam because you inserted the cut !. The cut is used when you have found the right rules and you don't need to do further evaluations, but it cuts all the other solutions.
In your case
num_parent(adam, X) :- !, X = 0.
num_parent(eve, X) :- !, X = 0.
num_parent(_, 2). %replaced num_parent(X, 2) with num_parent(_, 2) to avoid singleton variable
num_parent(X, 0) returns only X = adam.
If you write
num_parent(adam, X) :- X = 0.
num_parent(eve, X) :- !, X = 0.
num_parent(_, 2).
the solution will be X = adam and X = eve, and in this case:
num_parent(adam, X) :- X = 0.
num_parent(eve, X) :- X = 0.
num_parent(_, 2).
the solution will be X = adam, X = eve and false because the query num_parent(X, 0) doesn't unify with num_parent(_, 2).
You can better see this behavior using the tracer.

Predicate in prolog which is true if M and N differ more than X

First of all I am completely new to prolog and I am trying to write a predicate length(M,X,N) which is true, if M differs from N more than X.
I wrote the following testcase which is true if M(=dec.5) and N(=dec.2) differ more than X(=dec.2). And it is true in this case because 5 and 2 have a difference of 3 which is more than 2:
?- length(s(s(s(s(s(0))))), s(s(0)), s(s(0))).
true .
I know that prolog works recursively so I am wondering if I can construct such a predicate with conditions (for example <,>) like in languages like C, or if there is another way to do this in prolog. Sorry for this simple question but I just started with prolog.
You could construct predicates for greater or less. For example:
greater_than(s(_), 0).
greater_than(s(X), s(Y)) :-
greater_than(X, Y).
And similarly:
less_than(0, s(_)).
less_than(s(X), s(Y)) :-
less_than(X, Y).
If you want to find the absolute difference, you could do something like this:
abs_diff(0, 0, 0).
abs_diff(s(X), 0, s(X)).
abs_diff(0, s(X), s(X)).
abs_diff(s(X), s(Y), D) :-
abs_diff(X, Y, D).
Those concepts should help kick start some ideas for how to solve the rest of the problem.
This answer follows up on #lurker's fine answer and improves the determinism of the auxiliary predicate abs_diff/3 by utilizing
first argument clause indexing.
Introducing x_y_dist/3:
x_y_dist(0, Y, Y).
x_y_dist(s(X), Y, Z) :-
y_sx_dist(Y, X, Z).
y_sx_dist(0, X, s(X)).
y_sx_dist(s(Y), X, Z) :-
x_y_dist(X, Y, Z).
Sample query:
?- x_y_dist(X, Y, s(s(0))). % |X-Y| = 2
( X = 0 , Y = s(s(0)) % |0-2| = 2
; X = s(s(0)) , Y = 0 % |2-0| = 2
; X = s(0) , Y = s(s(s(0))) % |1-3| = 2
; X = s(s(s(0))) , Y = s(0) % |3-1| = 2
; X = s(s(0)) , Y = s(s(s(s(0)))) % |2-4| = 2
; X = s(s(s(s(0)))) , Y = s(s(0)) % |4-2| = 2
; X = s(s(s(0))) , Y = s(s(s(s(s(0))))) % |3-5| = 2
; X = s(s(s(s(s(0))))), Y = s(s(s(0))) % |5-3| = 2
; X = s(s(s(s(0)))) , Y = s(s(s(s(s(s(0)))))) % |4-6| = 2
; .........
)
Try this:
?- length(s(s(s(s(s(0))))), s(s(0)), s(s(0))).
length(s(_),0,0).
length(s(M),s(X),s(N)) :- length(M,X,N).
Do keep in mind that Prolog's predicates do not return values - so they don't return true or false. They either succeed or they don't. The interpreter is just telling you if your program succeeds or not.

Prolog X=f(X). does not evalute true for ?-X==f(X)

I know I am missing something very simple, but this bothers me a lot.
I want to test how Prolog would handle the below
X=f(X).
X=Z:- X==Y,Y==Z.
?- X==f(f(X)).
but obviously I am missing very basic as even ?-X=f(X). returns "false".
Could you please point out where I am wrong.
Thank you!
Use =/2 for unification:
?- X = X.
true.
?- X = f(X).
X = f(X).
?- f(f(X)) = X.
X = f(f(X)).
?- X = Y, X = Z.
X = Y, Y = Z.
As long as you have a free variable on one side of the unification, it will succeed.
Equivalence, with ==/2, is different. It only succeeds if both sides are the same variable or the same ground value:
?- X == X.
true.
?- Y == Z.
false.
?- X = Y, X = Z, Y == Z.
X = Y, Y = Z.
?- X == 3.
false.
?- X = 3, X == 3.
X = 3.

Prolog - if condition 1 AND condition 2 do x

I want to write the equivalent psudo-function in prolog:
function underhundred(X){
if (X >= 0 && X <=100) return 1;
else return 0;
}
I tried writing this but it does not compile:
underhundred(X,L) :- L is (X => 0, X =< 100 -> L = 1; L = 0) .
What would be the proper way of writing this without using prolog between predicate?
If you indeed want to use the goals L=1 and L=0 and X is an integer, use clpfd!
:- use_module(library(clpfd)).
Reification works like a charm!
?- X in 0..100 #<==> L.
L in 0..1, X in 0..100#<==>L.
What if X gets instantiated?
?- X in 0..100 #<==> L, X=(-7).
L = 0, X = -7. % out of bounds: -7 < 0 =< 100
?- X in 0..100 #<==> L, X=55.
L = 1, X = 55. % within bounds: 0 =< 55 =< 100
?- X in 0..100 #<==> L, X=111.
L = 0, X = 111. % out of bounds: 0 =< 100 < 111
A Prolog query succeeds or fails. If it succeeds it will return the bindings it made to be true.
You can write this predicate using clpfd as:
:-use_module(library(clpfd)).
under_hundred_clpfd(X):-
X in 0..100.
(You might prefer a name such as between_0_100?, if you literally want under 100 then you can use X in inf..99).
Some queries:
?-under_hundred_clpfd(5).
true.
?-under_hundred_clpfd(101).
false.
?-under_hundred_clpfd(X).
X in 0..100.
A traditional way to write this is:
under_hundred(X):-
X>=0,
X=<100.
But this way does not work for uninstantiated variables.
?-under_hundred(X).
ERROR: >/2: Arguments are not sufficiently instantiated
So like you say you might have to put a between/3 or length/2 goal to get a solution or similar construct.
underhundred(X):-
length(_,X),
X>=0,
X=<100.
This is not a very good solution as on back tracking it will get into an infinite loop. between/3 behaves better but you don't want it :).
If the main point of the question is how to write an if-then-else construct in Prolog, then a reasonable answer along the lines of the proposed definition is:
underhundred(X,L) :-
( X >= 0, X =< 100 )
-> L = 1
; L = 0.
This will only be useful if underhundred(X,L) is called after X has been sufficiently instantiated.

Resources