Representing uniqueness on a restricted domain - prolog
Given some functions:
define foo(x,y) (x > y) end.
define bar(x) (foo x x) end.
define baz(x,y) (foo x y) end.
I am not interested in the exact values that a function takes/returns however I would like to know about how the sign of an input affects the output.
To model the greater than function I use the atoms zero_int which is interpreted as 0, pos_int which is interpreted as all integers greater than 0 and neg_int interpreted as all integers less than 0.
%% pos_int greater then ...
%% i.e forall n,m : (n > 0) & (m = 0) => n is_greater_than m
gt(pos_int,zero_int,true).
gt(pos_int,neg_int,true).
gt(pos_int, pos_int, X) :- boolean(X). % return either true or false.
%% zero greater than ...
gt(zero_int, pos_int, false).
gt(zero_int,zero_int,false).
gt(zero_int, neg_int, true).
%% neg int greater than...
gt(neg_int, pos_int, false).
gt(neg_int, zero_int, false).
gt(neg_int, neg_int, X) :- boolean(X).
the boolean/1 predicate is used to return either true or false when there is a choice point. i.e comparing n > m where n > 0 and m > 0 may be be either true or false. As we do not know the actual (integer) values of n and m assume that both cases are true.
%% define booleans
boolean(true).
boolean(false).
Now I encode the functions in the following way:
foo(X,Y,Return) :- gt(X,Y,Return).
bar(X,Return) :- foo(X,X,Return).
baz(X,Y,Return) :- foo(X,Y,Return).
testing foo (and baz) we get expected results:
?- foo(X,Y,Return).
X = pos_int,
Y = zero_int,
Return = true ;
X = pos_int,
Y = neg_int,
Return = true ;
X = Y, Y = pos_int,
Return = true ;
X = Y, Y = pos_int,
Return = false ;
X = zero_int,
Y = pos_int,
Return = false ;
...
My issue is that because bar calls gt with a single value - I would like for it to always return false as it is never the case than n > n
?- bar(X,Return).
X = pos_int,
Return = true ;
X = pos_int,
Return = false ;
X = zero_int,
Return = false ;
X = neg_int,
Return = true ;
X = neg_int,
Return = false.
I am interested in how one might encode this. I have explored using pairs of variables as inputs that would allow for labels which could be compared.
You could try something like
boolean(true).
boolean(false).
gt2((_, pos_int), (_, zero_int), (_,true)).
gt2((_, pos_int), (_, neg_int), (_,true)).
gt2((L1, pos_int), (L2, pos_int), (_,X)) :- not(L1 == L2), boolean(X).
%% zero greater than ...
gt2((_, zero_int), (_, pos_int), (_,false)).
gt2((_, zero_int), (_, zero_int), (_,false)).
gt2((_, zero_int), (_, neg_int), (_,true)).
%% neg int greater than...
gt2((_, neg_int), (_, pos_int), (_,false)).
gt2((_, neg_int), (_, zero_int), (_,false)).
gt2((L1, neg_int), (L2, neg_int), (_,X)) :- not(L1 == L2), boolean(X).
gt2((L,pos_int),(L,pos_int),(_,false)).
gt2((L,neg_int),(L,neg_int),(_,false)).
foo(X,Y,Return) :- gt2(X,Y,Return).
bar(X,Return) :- foo(X,X,Return).
baz(X,Y,Return) :- foo(X,Y,Return).
This uses the idea of labels which allow us to tag inputs such that we can encode equality. Basically if the labels match then the input is strictly equal. (ie given two inputs that within the set of positive integers, if they have the same label, then they are the same integer.
which would give the results:
?- bar(X,R).
X = (_31408, zero_int),
R = (_31414, false) ;
X = (_31408, pos_int),
R = (_31414, false) ;
X = (_31408, neg_int),
R = (_31414, false).
?- foo(X,Y,R).
X = (_31852, pos_int),
Y = (_31858, zero_int),
R = (_31864, true) ;
X = (_31852, pos_int),
Y = (_31858, neg_int),
R = (_31864, true) ;
X = (_31852, pos_int),
Y = (_31858, pos_int),
R = (_31864, true) ;
X = (_31852, pos_int),
Y = (_31858, pos_int),
R = (_31864, false) ;
X = (_31852, zero_int),
Y = (_31858, pos_int),
R = (_31864, false) ;
X = (_31852, zero_int),
Y = (_31858, zero_int),
R = (_31864, false) ;
X = (_31852, zero_int),
Y = (_31858, neg_int),
R = (_31864, true) ;
X = (_31852, neg_int),
Y = (_31858, pos_int),
R = (_31864, false) ;
X = (_31852, neg_int),
Y = (_31858, zero_int),
R = (_31864, false) ;
X = (_31852, neg_int),
Y = (_31858, neg_int),
R = (_31864, true) ;
X = (_31852, neg_int),
Y = (_31858, neg_int),
R = (_31864, false) ;
X = Y, Y = (_31852, pos_int),
R = (_31864, false) ;
X = Y, Y = (_31852, neg_int),
R = (_31864, false).
notice that foo generates an extra input for when the labels are the same.
Related
What minimal change to my code would make it preserve logical purity?
I posted the code below as an answer to this question and user "repeat" answered and commented that it's not logically pure and "if you are interested in a minimal change to your code that makes it preserve logical-purity, I suggest posting a new question about that. I'd be glad to answer it :)". % minset_one(1 in D1, 1 in D2, D1, D2, D1Len, D2Len, T). minset_one_(true, false, D1, _, _, _, D1). minset_one_(false, true, _, D2, _, _, D2). minset_one_(true, true, _, D2, D1Len, D2Len, D2) :- D1Len >= D2Len. minset_one_(true, true, D1, _, D1Len, D2Len, D1) :- D1Len < D2Len. minset_one(D1, D2, T) :- (member(1, D1) -> D1check = true ; D1check = false), (member(1, D2) -> D2check = true ; D2check = false), length(D1, D1Len), length(D2, D2Len), minset_one_(D1check, D2check, D1, D2, D1Len, D2Len, T). e.g. ?- D1 = [X,Y,Z], D2 = [U,V], minset_one(D1,D2,T). D1 = [1, Y, Z], D2 = T, T = [1, V], U = X, X = 1 ; false there are more solutions possible. member(1, D1) is not backtracking through [1, Y, Z], then [X, 1, Z] then [X, Y, 1].
The Problem with (->)/2 (and friends) Consider the following goal: (member(1,D1) -> D1check = true ; D1check = false) (->)/2 commits to the first answer of member(1,D1)—other answers are disregarded. Can alternatives to (->)/2—like (*->)/2 (SWI, GNU) or if/3 (SICStus)—help us here? No. These do not ignore alternative answers to make member(1,D1) succeed, but they do not consider that the logical negation of member(1,D1) could also have succeeded. Back to basics: "If P then Q else R" ≡ "(P ∧ Q) ∨ (¬P ∧ R)" So let's rewrite (If -> Then ; Else) as (If, Then ; Not_If, Else): (member(1,D1), D1check = true ; non_member(1,D1), D1check = false) How should we implement non_member(X,Xs)—can we simply write \+ member(X,Xs)? No! To preserve logical purity we better not build upon "negation as finite failure". Luckily, combining maplist/2 and dif/2 does the job here: non_member(X,Xs) :- maplist(dif(X),Xs). Putting it all together So here's the minimum change I propose: minset_one_(true, false, D1, _, _, _, D1). minset_one_(false, true, _, D2, _, _, D2). minset_one_(true, true, _, D2, D1Len, D2Len, D2) :- D1Len >= D2Len. minset_one_(true, true, D1, _, D1Len, D2Len, D1) :- D1Len < D2Len. non_member(X,Xs) :- maplist(dif(X),Xs). minset_one(D1, D2, T) :- (member(1,D1), D1check = true ; non_member(1,D1), D1check = false), (member(1,D2), D2check = true ; non_member(1,D2), D2check = false), length(D1, D1Len), length(D2, D2Len), minset_one_(D1check, D2check, D1, D2, D1Len, D2Len, T). Running the sample query we now get: ?- D1 = [X,Y,Z], D2 = [U,V], minset_one(D1,D2,T). D1 = [1,Y,Z], X = U, U = 1, D2 = T, T = [1,V] ; D1 = [1,Y,Z], X = V, V = 1, D2 = T, T = [U,1] ; D1 = T, T = [1,Y,Z], X = 1, D2 = [U,V], dif(U,1), dif(V,1) ; D1 = [X,1,Z], Y = U, U = 1, D2 = T, T = [1,V] ; D1 = [X,1,Z], Y = V, V = 1, D2 = T, T = [U,1] ; D1 = T, T = [X,1,Z], Y = 1, D2 = [U,V], dif(U,1), dif(V,1) ; D1 = [X,Y,1], Z = U, U = 1, D2 = T, T = [1,V] ; D1 = [X,Y,1], Z = V, V = 1, D2 = T, T = [U,1] ; D1 = T, T = [X,Y,1], Z = 1, D2 = [U,V], dif(U,1), dif(V,1) ; D1 = [X,Y,Z], D2 = T, T = [1,V], U = 1, dif(X,1), dif(Y,1), dif(Z,1) ; D1 = [X,Y,Z], D2 = T, T = [U,1], V = 1, dif(X,1), dif(Y,1), dif(Z,1) ; false. Better. Sure looks to me like there's nothing missing.
I think it would be: add: :- use_module(library(reif)). ... and replace: %(member(1, D1) -> D1check = true ; D1check = false), %(member(1, D2) -> D2check = true ; D2check = false), memberd_t(1, D1, D1check), memberd_t(1, D2, D2check), Example of the difference between member and memberd_t: ?- member(X, [A, B, C]). X = A ; X = B ; X = C. ?- memberd_t(X, [A, B, C], IsMember). X = A, IsMember = true ; X = B, IsMember = true, dif(A,B) ; X = C, IsMember = true, dif(A,C), dif(B,C) ; IsMember = false, dif(A,X), dif(B,X), dif(C,X). ?- memberd_t(X, [A, B, C], IsMember), X = 5, A = 5, C = 5. X = A, A = C, C = 5, IsMember = true ; false. So, memberd_t is itself adding the dif/2 constraints. To aid performance slightly, it loops through the list only once. The definition of memberd_t is at e.g. https://github.com/meditans/reif/blob/master/prolog/reif.pl#L194 and https://www.swi-prolog.org/pack/file_details/reif/prolog/reif.pl?show=src
Prolog, return only smallest result
For example my output is like this ?- sroute(india,england,X). X = 2481 ; X = 3438 ; X = 1931 ; X = 3762 ; X = 3840 ; X = 1922 ; X = 2668 ; X = 2677 ; X = 4184 ; X = 3227 ; X = 2000 ; false. I'm assuming I found all routes and their distances(recursively) in my code and they are all true, but i want to see only "X = 1922 ." when i ask sroute(india,england,X). How i can do this, thanks for helping.
You can call setof/3 to get a sorted set and take only the first element of the resulting set: setof(X, sroute(india,england,X), [Min|_]).
How to check if names of datatypes are the same
I wonder if there is a quick way to check whether 2 datatypes are the same. Example - let's say I have a list like this: [odd(1), odd(3), even(2), odd(5), even(4)] Now if I pull 2 elements from the list (index 0 and index 1) I want to know if they are both of the same data type or different (i.e. odd()).
If X and Y are pulled from the list, you can check literally, X = odd(_), Y = odd(_) ; X = even(_), Y = even(_). Or more generally, you can check... functor(X, Functor, ArgCount), functor(Y, Functor, ArgCount). The above will succeed if X and Y have the same functor (even or odd, for example) and whether they have the same number of arguments. functor/3 is an ISO Prolog predicate, so it works on any ISO compliant Prolog. You can do a little test at the Prolog prompt that illustrates. Note that only successful results are when the "data types" of the chosen list members match. | ?- List = [odd(1), odd(3), even(2), odd(5), even(4)], member(X, List), member(Y, List), functor(X, F, A), functor(Y, F, A). A = 1 F = odd List = [odd(1),odd(3),even(2),odd(5),even(4)] X = odd(1) Y = odd(1) ? ; A = 1 F = odd List = [odd(1),odd(3),even(2),odd(5),even(4)] X = odd(1) Y = odd(3) ? ; A = 1 F = odd List = [odd(1),odd(3),even(2),odd(5),even(4)] X = odd(1) Y = odd(5) ? ; A = 1 F = odd List = [odd(1),odd(3),even(2),odd(5),even(4)] X = odd(3) Y = odd(1) ? ; A = 1 F = odd List = [odd(1),odd(3),even(2),odd(5),even(4)] X = odd(3) Y = odd(3) ? a A = 1 F = odd List = [odd(1),odd(3),even(2),odd(5),even(4)] X = odd(3) Y = odd(5) A = 1 F = even List = [odd(1),odd(3),even(2),odd(5),even(4)] X = even(2) Y = even(2) A = 1 F = even List = [odd(1),odd(3),even(2),odd(5),even(4)] X = even(2) Y = even(4) A = 1 F = odd List = [odd(1),odd(3),even(2),odd(5),even(4)] X = odd(5) Y = odd(1) A = 1 F = odd List = [odd(1),odd(3),even(2),odd(5),even(4)] X = odd(5) Y = odd(3) A = 1 F = odd List = [odd(1),odd(3),even(2),odd(5),even(4)] X = odd(5) Y = odd(5) A = 1 F = even List = [odd(1),odd(3),even(2),odd(5),even(4)] X = even(4) Y = even(2) A = 1 F = even List = [odd(1),odd(3),even(2),odd(5),even(4)] X = even(4) Y = even(4) yes | ?-
how to avoid repeating answers in PROLOG
Here is My Question : ?- rule7(X,Y,t3). X = s10, Y = s11 ; X = s12, Y = s10 ; X = s12, Y = s10 ; X = s12, Y = s11 ; X = s12, Y = s11 ; X = s12, Y = s13 ; X = s12, Y = s13 ..... It will repeat it lot of times. How can I reduce these repeatings... The Code : rule7(X,Y,T) :- course(C,X), course(C1,Y), course_lec(C1,T), course_lec(C,T), copy_rec(Y,X).
Unwanted evaluation on prolog
I was making a prolog knowledge base to implement geometry rules. When testing if a rectangle had a right angle, I found two answers. ?- rect_tri(triangle(line(point(0,0),point(0,1)),line(point(0,1),point(1,0)),line(point(1,0),point(0,0)))). true ; false. Here is the kwnoledge base: point(X,Y). line(X,Y) :- X = point(A,B), Y = point(C,D), not(X = Y). len(X,R) :- X = line(P,Q), P = point(A,B), Q = point(C,D), not(P = Q), R is sqrt((A - C) * (A - C) + (B - D) * (B - D)). triangle(X,Y,Z) :- X = point(A,B), Y = point(C,D), Z = point(E,F), not(X = Y), not(X = Z), not(Y = Z), L1 = line(X,Y), L2 = line(X,Z), L3 = line(Y,Z), len(L1,G), len(L2,H), len(L3,I), G + H > I, G + I > H, H + I > G. triangle(X,Y,Z) :- X = line(A,B), Y = line(B,C), line(A,C), len(X,G), len(Y,H), len(Z,I), G + H > I, G + I > H, H + I > G. rect_tri(X) :- X = triangle(A,B,C), len(A,G), len(B,H), len(C,I), (G is sqrt(H * H + I * I); H is sqrt(G * G + I * I); I is sqrt(H * H + G * G)). When tracing, I found that the answer true comes when prolog hits the line H is sqrt(G * G + I * I), and false when it evaluates the last line. I don't want the last evaluation to occur, because I want it to exit when a true has been found.
Daniel comment probably shows the most sensible way to solve your problem. Some other option... in modern compilers there is the if/then/else construct: rect_tri(X) :- X = triangle(A,B,C), len(A,G), len(B,H), len(C,I), ( G is sqrt(H * H + I * I) -> true ; H is sqrt(G * G + I * I) -> true ; I is sqrt(H * H + G * G) ). You could as well use cuts (old fashioned way, somewhat more readable here): rect_tri(X) :- X = triangle(A,B,C), len(A,G), len(B,H), len(C,I), ( G is sqrt(H * H + I * I), ! ; H is sqrt(G * G + I * I), ! ; I is sqrt(H * H + G * G) ).