advice on commutative and transitive equivalence implementation‏ in Prolog - prolog

I'd like to simulate the equivalence in Prolog with the properties of being commutative and transitive, here is what I did: equal/2 will be supplying as facts.
symmetricEqual(A,B):- equal(A,B).
symmetricEqual(A,B):- equal(B,A).
transitiveEqualPath(A,B,_) :- symmetricEqual(A,B).
transitiveEqualPath(B,C,IntermediateNodes) :-
symmetricEqual(A,B),
\+ member(C,IntermediateNodes),
transitiveEqualPath(A,C,[B|IntermediateNodes]), B\==C.
transitiveEqual(A,B) :- transitiveEqualPath(A,B,[]).
But I am running into performance issues with the above solution to try to compute transitiveEqual/2 (it has taken roughly 20mins), I have around 2K symmetricalEqual/2 facts computed pretty fast from equal/2, so it must be the cause of rules for transitiveEqual/2, anybody can suggest any improvement on this?
Thanks very much.

Courtesy of the approach from here:
symmetricEquals(X,Y) :- equal(X,Y).
symmetricEquals(X,Y) :- equal(Y,X).
transitiveEqual(A, B) :-
% look for an equality path from A to B
path(A, B, _Path).
path(A, B, Path) :-
% build a path from A to B
path(A, B, [A], Path).
path(A, B, _Acc, [B]) :-
symmetricEquals(A, B).
path(A, B, Visited, [C|Path]) :-
symmetricEquals(A, C),
C \== B,
\+ memberchk(C, Visited),
path(C, B, [C|Visited], Path).
Note that path/3,4 will backtrack to enumerate all possible paths between any ground or variable A to B. This could be quite expensive if the graph implied by your equal/2 facts is large, contains many disconnected components, and/or you're looking for all combinations.

Related

Prolog - Path finding and length given Relation

I just began learning Prolog and I wanted to understand Pathfinding better. I have a few examples of relationships, however, I don't know how to find the path and length of a relationships when the relationships are cyclical. I've been trying to create a list that documents visited nodes, but I keep receiving errors.
Below are a few examples as well as my attempt to find path given the relationship, source, target, pathlist, and length):
is_a(parallelogram, quadrilateral).
is_a(trapezoid, quadrilateral).
is_a(rhombus, parallelogram).
is_a(rectangle, parallelogram).
is_a(square, rhombus).
is_a(square, rectangle).
edge(a, b).
edge(b, c).
edge(c, d).
edge(d, a).
friend(alice, bob).
friend(bob, carol).
friend(carol, daniel).
friend(carol, eve).
friends(A,B) :-
friend(A,B);
friend(B,A).
transit(Rel, S, T) :-
call(Rel, S, X),
(X = T; transit(Rel, X, T)).
path_(Rel,Source,Target,Path,Len) :-
path_(Rel,Source,Target,Path,Len,[]).
path_(Rel,Source,Target,Path,Len,Visited) :-
transit(Rel,Source,Target),
transit(Rel,Source,Mid),
Mid == Target, !,
append(Visited,[Source,Target],Path),
length(Path,L),
Len is L+1.
path_(Rel,Source,Target,Path,Len,Visited) :-
transit(Rel,Source,Target),
transit(Rel,Source,Mid),
not(member(Mid,Visited)),
path_(Rel,Mid,Target,Path,Len,[Source|Visited]).
The above is my second attempt, but I receive errors on everything. My first attempt only worked with non-cyclical paths, such as for the is_a relationships, which is noted below:
path0(Rel,From,To,[From,To],2) :-
transit(Rel,From,To),
call(Rel, From, To).
path0(Rel,From,To,[From|Xs],Len) :-
transit(Rel,From,X),
call(Rel,From,X),
path0(Rel,X,To,Xs,_),
length(Xs, L),
Len is L+1.

Reversing a list

I need help reversing a list.
fun(a, [b, d]).
fun(b, [c]).
fun(c, []).
fun(d, [e]).
fun(e, [f]).
fun(f, [g]).
fun(g, []).
xyz(X, Y):-
fun(X, Z) -> findall([A|B], (member(A, Z), xyz(A, B)), L),
flatten(L, F), sort(F, Y); Y = [].
The query xyz(a,X). gives me X = [b,c,d,e,f,g].
However, I would like it to give me X = [g,f,e,d,c,b].
I have tried different attempts at reversing the list, but I am not having any luck.
I have tried adding an additional predicate right after this, but it didn't work either:
xyz2(X,Y):-
xyz(X,Y),
reverse(Y,Z),
Z\=0.
Credit goes to CapelliC for the approach to the implementation above found at my other post here.
Recursion in PROLOG?
You can avoid some difficult programming, and make your program easier to read by re-defining your problem. Say the f/2 describes a directed graph, with edges from the first argument to each of the elements in the second argument, so:
a ---> b
a ---> d
b ---> c
% etc
Then, your question is, which nodes in the graph are reachable from a given node? You can define the solution with the help of library(ugraphs).
To make all edges from an f/2:
edge(From-To) :-
f(From, L),
member(To, L).
You can now collect the edges, make a graph, and find which nodes are reachable from a starting node:
foo(X, L) :-
findall(E, edge(E), Edges),
vertices_edges_to_ugraph([], Edges, G),
reachable(X, G, All),
once(select(X, All, R)), % remove the node you start from
reverse(R, L).
Per definition, a node is always reachable from itself, so you need to pick it out of the list of reachable nodes.
?- foo(a, X).
X = [g, f, e, d, c, b].
?- foo(e, X).
X = [g, f].
?- foo(g, X).
X = [].
I don't exactly understand why the order of the elements is significant. This feels a bit like a code smell.

Prolog : eliminating cycles from indirect relation

I have a list of user facts defined as:
user(#michael).
user(#ana).
user(#bob).
user(#george).
user(#john).
and so on. Furthermore, I have a set of facts as:
follows(#michael,#ana).
follows(#ana,#bob).
follows(#bob,#michael).
I am trying to write a relation indirect(user1,user1) which will tell me if user1 indirectly follows user2.
However, I am not able to do away with cyclic relations.
Like in the given example, michael -> ana -> bob -> michael will cause a cycle.
What is the best way to eliminate these cycles from the result of indirect(user1,user2)?
You can make a rule that passes an extra list of users that you have "seen" so far, and ignore follows originating from these users: follows(A, B, Seen).
To do that, define a "follow transitive" rule that wraps the actual rule, like this:
follows_tx(A, B) :- follows(A, B, []).
Now you can define follows/3 rule this way:
follows(A, B, Seen) :-
not_member(B, Seen),
follows(A, B).
follows(A, B, Seen) :-
follows(A, X),
not_member(X, Seen),
follows(X, B, [A|Seen]).
The base clause says that if there is a fact about A following B, we consider the predicate proven as long as we have not seen B before.
Otherwise, we find someone who follows A, check that we have not seen that user yet by checking not_member/2, and finally see if that user follows B, directly or indirectly.
Finally, here is how you can define not_member:
not_member(_, []).
not_member(X, [H|T]) :- dif(X, H), not_member(X, T).
Demo.
indirect( A0,A) :-
closure(follows, A0,A).
See for a definition of closure/3.

How to find a path with limited depth in graph?

link(a, b).
link(a, c).
link(b, c).
link(b, e).
link(c, f).
link(c, g).
link(c, d).
symlink(F1, F2) :-
link(F1, F2).
symlink(F1, F2) :-
link(F2, F1).
profile(a,box). %Tag it is the same as box for a.
find(Start, Tag, Rpath) :
find2(Start, Tag, 0, [], Rpath).
find2(Step, Tag, Count, Path, Rpath) :-
C is Count +1,
C < 5,
symlink(Step, A),
compat(A,Tag), % Compatible means the distance between the tag of A
% and the Tag that is given as argument should be maximum 1.
append(Path, [A|E],Rpath), %This part i want make my final path in Rpath.
not(member(Step,Path)),
find2(A, Tag, C, [Step|Path], Rpath).
You are quite close to a working predicate here. I've include a code snippet that hopefully solves a few of the small mistakes you make. Notice that find/3 is the predicate you would actually use (from the outside), a so-called wrapper clause.
find/4 works in the following way:
The first clause is only used to detect a transgression of the maximum depth.
The second clause is only used to detect a goal node, i.e. one that matches the given tag.
The third clause does the real job of finding a symmetric link in the graph.
Some small things to note:
Renamed symlink/2 to symmetric_link/2 to avoid confusion with symbolic links.
Used \+ instead of not for negation (the former is more common I believe).
Used tag/2 for tagging nodes, not profile/2 to avoid confusion with the act of profiling/analyzing code performance.
Code snippet:
link(a, b).
link(a, c).
link(b, c).
link(b, d).
link(b, e).
link(c, f).
link(c, g).
link(c, d).
tag(a, box).
symmetric_link(F1, F2) :-
link(F1, F2).
symmetric_link(F1, F2) :-
link(F2, F1).
maximum_depth(5).
find(Start, End, Path):-
find(Start, End, 0, [Start], Path).
find(_, _, Depth, _, _):-
maximum_depth(Max),
Depth > Max, !,
fail.
find(Node, Tag, _, _, [Node]):-
tag(Node, Tag), !.
find(Node1, Tag, Depth1, History, [Node1|Path]):-
symmetric_link(Node1, Node2),
\+ memberchk(Node2, History),
Depth2 is Depth1 + 1,
find(Node2, Tag, Depth2, [Node2|History], Path).
Example of usage:
?- find(g, box, Path).
Path = [g, c, d, b, a] ;
Path = [g, c, a] ;
Path = [g, c, b, a].
I have not fully tested this predicate and would advice you to write a unit test for these kinds of predicates. I use plUnit for this, which runs on SWI-Prolog and SICStus Prolog, but there may be others as well.
Hope this helps!

Prolog - union doesn't check for duplicates or that the list is in order

I'm trying to write a union function in Prolog and I'm running into some trouble. I have looked up examples and listed the built in example for union but I am writing my own for an assignment. I have noticed some strange results when a list has duplicate values and/or when the order of the list is not ascending.
The following is the built in union code:
union([], A, A) :- !.
union([A|C], B, D) :-
memberchk(A, B), !,
union(C, B, D).
union([A|B], C, [A|D]) :-
union(B, C, D).
I believe that the pseudocode here is that we're looking to find all of list 1 inside of our result and once it's exhausted, we compare list 2 and list 3. They should be the same. However, this does not check for order.
30 ?- union([1,2,3],[],[3,2,1]).
false.
Why is this false? List 1 and List 3 are the same set even though the order is different!
24 ?- union([a],[a,a],[a,a]).
true.
25 ?- union([a,a],[a],[a,a]).
false.
What is different between these two? They should yield the same result. However, due to the way that the function is written, in the end we just compare list 2 and list 3 which are different for line 25.
My question is. . . Is there a better way to write these functions such that duplicates are handled properly and order does not matter? One would assume that the built in methods would do the trick but no dice.
First, it's easier to read a code written with consistent naming:
union([], A, A) :- !.
union([A|B], C, D) :-
memberchk(A, B), !,
union(B, C, D).
union([A|B], C, [A|D]) :-
union(B, C, D).
What does it do? Given two lists A and B, it produces the result with a prefix of all elements of A not present in B, and then B itself. So, union([1,2,3],[],[1,2,3]). And also union([1,2,3],[2],[1,3,2]). You see that order is preserved here. So if you want to compare lists regardless of their order, you should write a special, additional predicate for that: union([1,2,3],[],X), regardless(X,[3,2,1]) should work then.
Same with the duplicates - your set equality predicate should disregard any. union as presented above OTOH is not about sets, but about lists. There are many different lists representing the same set, but as lists they are considered different.
In your 25 you hit the issue of duplicates: union([a,a],[a],X) produces X=[a]. Again, as set it is the same as [a,a], but as lists they are different.
One strategy for coping with this is to represent sets as strictly increasing lists - if your elements have order well defined for them. Then we can write union such that given two sets according to that definition, it will also produce a set, and it will work efficiently at that:
union([],X,X):-!.
union(X,[],X):-!.
union([A|B],[C|D],[H|T]):-
A #< C -> H=A, union(B,[C|D],T) ;
C #< A -> H=C, union([A|B],D,T) ;
H=A, union(B,D,T).
We will have to define make_set/2 predicate here too. Even better (in some respects) is representing sets (again, of comparable elements) by self-balancing binary trees.
you are implementing a concept using the 'tools' that the language (Prolog, in your case) put in your hands. Then you should better define (in natural language, first) what's your target, considering that also append/3 could fit the union concept.
you can call list C a union among lists A,B if....
I would fill the ellipsis this way
each A element appears once in C and
each B element appears once in C and
each C element appears in A or B
If you agree on this definition, then an implementation could be
union(A, B, C) :-
elems_once(A, [], C1),
elems_once(A, C1, C2),
each_elems(C2, A, B, C).
That is far less efficient that the library code shown, but it's the price of generality...
To have a union(?A, ?B, ?C) (that is, you can use any variable as input or output) I create a new union
uniao(?A, ?B, ?C)
that uses list:union among other stuff, as:
uniao([], [], []) :- !.
uniao(X, [], X) :- !.
uniao([], X, X) :- !.
uniao(X, Y, Z) :-
nonvar(X),
nonvar(Y),
var(Z),
list_to_set(X, Xs),
list_to_set(Y, Ys),
union(Xs, Ys, Z),
!.
uniao(X, Y, Z) :-
nonvar(X),
var(Y),
nonvar(Z),
list_to_set(X, Xs),
list_to_set(Z, Zs),
subset(Xs, Zs),
subtract(Zs, Xs, Y),
!.
uniao(X, Y, Z) :-
var(X),
nonvar(Y),
nonvar(Z),
list_to_set(Y, Ys),
list_to_set(Z, Zs),
subset(Ys, Zs),
subtract(Zs, Ys, X),
!.
uniao(X, Y, Z) :-
nonvar(X),
nonvar(Y),
nonvar(Z),
list_to_set(X, Xs),
list_to_set(Y, Ys),
list_to_set(Z, Zs),
union(Xs, Ys, Ts),
subtract(Zs, Ts, []),
subtract(Ts, Zs, []),
!.

Resources