Prolog: How to find all reachable node from a given node? - prolog

Given facts:
edges(a,[b,c]).
edges(b,[d]).
edges(c,[a]).
edges(d,[e]).
For now, I can write following predicate:
find(F, L) :-
edges(F, Nodes) ->
findall([X|Y], (member(X, Nodes), find(X, Y)), L);
L = [].
It works fine when there is no cycle, for example, find(b,L). gives me d and e. But it's not working when cycle exists. So how can I modify my code to handle the cycle? e.g find(c,L) will output a, b, c, d, e as well as find(a,L).
Any helps are appreciated.

You could opt to use an accumulator to keep track of the nodes you visited. In order to do this you need a list as an additional argument. Since this lists is empty at the beginning of your search, you'd always call the predicate with [], so you might as well hide it by using a calling predicate, let's maybe call it start_dest/2:
start_dest(S,D) :-
dif(S,D), % start and destination nodes are different
start_dest_(S,D,[]). % actual relation called with empty accumulator
The first goal dif/2 is only necessary in order to prevent solutions where the start node and the destination node are the same. If you want to permit such solutions just remove that goal. The actual relation will search for reachable nodes by traversing the graph node by node. You can distinguish two cases.
If the two nodes are equal you found a possible destination node.
If the nodes are different, there has to be to be an intermediate node in the adjacence list of the node you are currently at. The current node must not have been visited in the search so far (to avoid cycles). There has to be a path from the intermediate node to the destination and the current node must not appear in that path, so it has to be added to the list of visited nodes.
You can express these two cases in Prolog like so:
start_dest_(D,D,_Visited). % case 1: destination found
start_dest_(S,D,Visited) :- % case 2:
maplist(dif(S),Visited), % S has not been visited yet
edges(S,Reachable), % Reachable is the adjacence list
member(X,Reachable), % that has to contain the intermediate node X
start_dest_(X,D,[S|Visited]). % there has to be a path from X to D that
% does not include S
Your example queries yield the desired result:
?- start_dest(b,N).
N = d ;
N = e ;
false.
?- start_dest(c,N).
N = a ;
N = b ;
N = d ;
N = e ;
false.
If you remove the first goal (dif(S,D)) in start_dest/2, you get an additional solution. This corresponds to the view that every node is reachable from itself.
?- start_dest(b,N).
N = b ;
N = d ;
N = e ;
false.
Note that this predicate can be used in all directions, e.g. From which nodes can e be reached?:
?- start_dest(S,e).
S = a ;
S = b ;
S = c ;
S = d ;
false.
Or the most general query: Which nodes are reachable from any node?:
?- start_dest(S,D).
S = a,
D = b ;
S = a,
D = d ;
S = a,
D = e ;
S = a,
D = c ;
S = b,
D = d ;
S = b,
D = e ;
S = c,
D = a ;
S = c,
D = b ;
S = c,
D = d ;
S = c,
D = e ;
S = d,
D = e ;
false.
As opposed to your predicate find/2, start_dest/2 gives you the reachable nodes one at a time. If you want to get all reachable nodes in a list, you can use predicates like findall/3, bagof/3 and setof/3 as you did in find/2, e.g.:
?- bagof(N, start_dest(b,N), Reachable).
Reachable = [d, e].
?- bagof(N, start_dest(c,N), Reachable).
Reachable = [a, b, d, e].
If you intend to always search for all reachable nodes but do not want to query with bagof/3 all the time, you can write a calling predicate like:
reachable_from(Reachable,Start) :-
bagof(N, start_dest(Start,N), Reachable).
?- reachable_from(Reachable,Start).
Reachable = [b, d, e, c],
Start = a ;
Reachable = [d, e],
Start = b ;
Reachable = [a, b, d, e],
Start = c ;
Reachable = [e],
Start = d.

Here is a possibility :
% we get a list of all edges
get_all_edges(Edges) :-
bagof(edges(X,Y), edges(X,Y), Edges).
% main predicate
find(F, L) :-
get_all_edges(Edges),
find(Edges, F, Out),
% the result you get is for example [[a, [b, [d, [e|e], [e]]], [c]]]
flatten(Out, FOut),
list_to_set(FOut, L).
% no more edges, work is done
find([], L, L).
find(Edges, F, L) :-
% we get the good nodes
select(edges(F, Nodes), Edges, Rest)
-> findall([X|Y], (member(X, Nodes), find(Rest, X, Y)), L)
; L = [].
Result :
?- find(c, L).
L = [a, b, d, e, c].

After you'll have learned the basic way, take a look to what libraries have to offer:
?- findall(V-U,(edges(V,Us),member(U,Us)),Es),
vertices_edges_to_ugraph([],Es,G),
reachable(a,G,Rs).
Es = [a-b, a-c, b-d, c-a, d-e],
G = [a-[b, c], b-[d], c-[a], d-[e], e-[]],
Rs = [a, b, c, d, e].
You could be tempted to go directly from your edges/2 to ugraph format, but better make use of predefined functionality (i.e. vertices_edges_to_ugraph/3)

I found this freaking great algorithm in SWI-prolog's source code.
It's so elegant that I want to share it with you.
This snippet is indeed a BFS algorithm.
%! reachable(+Vertex, +UGraph, -Vertices)
%
% True when Vertices is an ordered set of vertices reachable in
% UGraph, including Vertex. Example:
%
% ?- reachable(1,[1-[3,5],2-[4],3-[],4-[5],5-[]],V).
% V = [1, 3, 5]
reachable(N, G, Rs) :-
reachable([N], G, [N], Rs).
reachable([], _, Rs, Rs).
reachable([N|Ns], G, Rs0, RsF) :-
neighbours(N, G, Nei),
ord_union(Rs0, Nei, Rs1, D),
append(Ns, D, Nsi),
reachable(Nsi, G, Rs1, RsF).

Related

Incorrect output when trying to query in a depth first search implementation for prolog

I just can't seem to get the correct output - I am supposed to get -
?- dfs([a], X).
X = [a, f, i] ;
false.
But I get -
?- dfs([a], X).
X = [a|f] ;
% Representation of a tree
% choose initial state a
arc(a, b).
arc(a, f).
arc(b, c).
arc(b, d).
arc(b, e).
arc(f, g).
arc(f, i).
arc(i, j).
arc(i, k).
% the goal
goal(i).
dfs([Node|_], [Node|X]) :-
goal(X).
dfs([Node|_], [Node|X]) :-
expands([Node|_], NewNode),
append([Node|_], NewNode, appendedN),
dfs(appendedN, X).
% expands(+Path, ?NewNode).
% -- Path: is a list of nodes of the form Path=[Node|Nodes], where
% Node is the node we want to expand and Nodes is a list
% of remaining nodes already expanded and containing the root.
% -- NewNode: is a constant representing the node we want to go to,
% as there is an link to it from where we are currently.
%
expands([Node|_], NewNode):-
arc(Node, NewNode).
Your program matches the first clause, dfs([Node|_], [Node|X]), and nothing else, producing X = [a|i] .
Here's a working version.
% Representation of a tree
% choose initial state a
arc(a, b).
arc(a, f).
arc(b, c).
arc(b, d).
arc(b, e).
arc(f, g).
arc(f, i).
arc(i, j).
arc(i, k).
% the goal
goal(i).
% You can expand a starting symbol S to a list L if G is your goal, S expands
% to G in list L1, and you append the two lists.
dfs([S], L) :-
goal(G),
expands(S, G, L1),
append([S], L1, L).
% X expands to Y in list [Y] if there's a direct arc from X to Y (base case).
expands(X, Y, [Y]) :-
arc(X, Y).
% X expands to Z in list [Y|L] if there's a direct arc from X to Y and Y
% expands to Z in list L (recursive case).
expands(X, Z, [Y|L]) :-
arc(X, Y),
expands(Y, Z, L).
In this version, expands() produces all of the lists that start with a:
?- expands(a, X, L).
X = b,
L = [b] ;
X = f,
L = [f] ;
X = c,
L = [b, c] ;
X = d,
L = [b, d] ;
X = e,
L = [b, e] ;
X = g,
L = [f, g] ;
X = i,
L = [f, i] ;
X = j,
L = [f, i, j] ;
X = k,
L = [f, i, k] ;
false.
Then dfs() confirms that the goal i has been reached and adds the start symbol a to the head of the list:
?- dfs([a], X).
X = [a, f, i] ;
false.

GProlog reachable elements in a graph

I have an easy problem that I'm having trouble with although I think the answer is quite easy.
Here it goes:
Assume a weighted
directed graph is described by means of a
predicate edge/3, such that edge(X,Y,C)
is true is there is an edge from vertex X to
vertex Y of cost C. For instance, to the right
is a graph and its description using edge/3:
edge(a, c,1).
edge(a,d,3).
edge(b,d,2).
edge(c,e,5).
edge(e, c,2).
edge(e,f,2).
edge(d,f,10).
I have to . Define a predicate reachable/2 that computes the list of nodes that can be reached
from a given node. For instance, to the query reachable(a, L) Prolog should answer L=[c,e,d,f]
(in any order). (Remember findall.)
Here is what I wrote for the moment
path(X,Y):-edge(X,Y,_).
path(X,Y):-edge(X,Z,_),path(Z,Y).
reachable(X,L):-findall(Y,path(X,Y),L).
I can't see what is wrong but it's going in circles and stopping because of a memory issue.
Any ideas how to solve that?
Please that would creatly help!
If your graph has an edge, like c → e → c → e → …, then there is nothing that stops path from each time walking from e to c and back. Unless we add something to prevent this.
We can make use of a list that contains all the elements already visited, and prevent from visiting these another time:
path(X, Y) :-
path(X, Y, [X]).
path(X, Y, V) :-
edge(X, Y, _),
\+ member(Y, V).
path(X, Y, V) :-
edge(X,Z,_),
\+ member(Z, V),
path(Z, Y, [Z|V]).
we thus start with a list that contains only X. Each time when we take an edge, we check if the target (Y or Z) is not a member of V, and in case of recursion, we add Z to the list.
For the given graph:
this thus produces:
?- path(X, Y).
X = a,
Y = c ;
X = a,
Y = d ;
X = b,
Y = d ;
X = c,
Y = e ;
X = e,
Y = c ;
X = e,
Y = f ;
X = d,
Y = f ;
X = a,
Y = e ;
X = a,
Y = f ;
X = a,
Y = f ;
X = b,
Y = f ;
X = c,
Y = f ;
false.

Prolog: Debugging Recursive Source Removal Algorithm

I am currently working on implementing a source-removal topological sorting algorithm for a directed graph. Basically the algorithm goes like this:
Find a node in a graph with no incoming edges
Remove that node and all edges coming out from it and write its value down
Repeat 1 and 2 until you eliminate all nodes
So, for example, the graph
would have a topological sort of a,e,b,f,c,g,d,h. (Note: topological sorts aren't unique and thus there can be a different topological sort as well)
I am currently working on a Prolog implementation of this with the graph being represented in list form as follows:
[ [a,[b,e,f]], [b,[f,g]], [c,[g,d]], [d,[h]], [e,[f]], [f,[]],
[g,[h]], [h,[]] ]
Where the [a, [b,e,f] ] term for example represents the edges going from a to b, e, and f respectively, and the [b, [f,g] ] term represents the edges going from b to f and g. In other words, the first item in the array "tuple" is the "from" node and the following array contains the destinations of edges coming from the "from" node.
I am also operating under assumption that there is one unique name for each vertex and thus when I find it, I can delete it without worrying about any potential duplicates.
I wrote the following code
% depends_on shows that D is adjacent to A, i.e. I travel from A to D on the graph
% returns true if A ----> D
depends_on(G,A,D) :- member([A,Ns],G), member(D,Ns).
% doesnt_depend_on shows that node D doesnt have paths leading to it
doesnt_depend_on(G, D) :- \+ depends_on(G, _, D).
% removes node from a graph with the given value
remove_graph_node([ [D,_] | T], D, T). % base case -- FOUND IT return the tail only since we already popped it
remove_graph_node([ [H,Ns] | T], D, R) :- \+ H=D,remove_graph_node( T, D, TailReturn), append([[H,Ns]], TailReturn, R).
%----------------------------------------------------
source_removal([], []]). % Second parameter is empty list due to risk of a cycle
source_removal(G,Toposort):-member([D,_], G),
doesnt_depend_on(G,D),
remove_graph_node(G,D,SubG),
source_removal(SubG, SubTopoSort),
append([D], SubTopoSort, AppendResult),
Toposort is AppendResult.
And I tested the depends_on, doesnt_depend_on, and remove_graph_node by hand using the graph [ [a,[b,e,f]], [b,[f,g]], [c,[g,d]], [d,[h]], [e,[f]], [f,[]], [g,[h]], [h,[]] ] and manually changing the parameter variables (especially when it comes to node names like a, b, c and etc). I can vouch after extensive testing that they work.
However, my issue is debugging the source_removal command. In it, I repeatedly remove a node with no directed edge pointing towards it along with its outgoing edges and then try to add the node's name to the Toposort list I am building.
At the end of the function's running, I expect to get an array of output like [a,e,b,f,c,g,d,h] for its Toposort parameter. Instead, I got
?- source_removal([ [a,[b,e,f]], [b,[f,g]], [c,[g,d]], [d,[h]], [e,[f]], [f,[]], [g,[h]], [h,[]] ], Result).
false.
I got false as an output instead of the list I am trying to build.
I have spent hours trying to debug the source_removal function but failed to come up with anything. I would greatly appreciate it if anyone would be willing to take a look at this with a different pair of eyes and help me figure out what the issue in the source_removal function is. I would greatly appreciate it.
Thanks for the time spent reading this post and in advance.
The first clause for source_removal/2 contained a typo (one superfluous closing square bracket).
The last line for the second clause in your code says Toposort is AppendResult. Note that is is used in Prolog to denote the evaluation of an arithmetic expression, e.g., X is 3+4 yields X = 7 (instead of just unifying variable X with the term 3+4). When I change that line to use = (assignment, more precisely unification) instead of is (arithmetic evaluation) like so
source_removal([], []). % Second parameter is empty list due to risk of a cycle
source_removal(G,Toposort):-member([D,_], G),
doesnt_depend_on(G,D),
remove_graph_node(G,D,SubG),
source_removal(SubG, SubTopoSort),
append([D], SubTopoSort, AppendResult),
Toposort = AppendResult.
I get the following result:
?- source_removal([ [a,[b,e,f]], [b,[f,g]], [c,[g,d]], [d,[h]], [e,[f]], [f,[]], [g,[h]], [h,[]] ], Result).
Result = [a, b, c, d, e, f, g, h] ;
Result = [a, b, c, d, e, g, f, h] ;
Result = [a, b, c, d, e, g, h, f] ;
Result = [a, b, c, d, g, e, f, h] ;
Result = [a, b, c, d, g, e, h, f] ;
Result = [a, b, c, d, g, h, e, f] ;
Result = [a, b, c, e, d, f, g, h] ;
Result = [a, b, c, e, d, g, f, h] ;
Result = [a, b, c, e, d, g, h, f] ;
Result = [a, b, c, e, f, d, g, h] ;
Result = [a, b, c, e, f, g, d, h] ;
...
Result = [c, d, a, e, b, g, h, f] ;
false.
(Shortened, it shows 140 solutions in total.)
Edit: I didn't check all the solutions, but among the ones it finds is the one you gave in your example ([a,e,b,f,c,g,d,h]), and they look plausible in the sense that each either starts with a or with c.

Prolog Out of stack error

I'm working on Problem 26 from 99 Prolog Problems:
P26 (**) Generate the combinations of K distinct objects chosen from
the N elements of a list
Example:
?- combination(3,[a,b,c,d,e,f],L).
L = [a,b,c] ;
L = [a,b,d] ;
L = [a,b,e] ;
So my program is:
:- use_module(library(clpfd)).
combination(0, _, []).
combination(Tot, List, [H|T]) :-
length(List, Length), Tot in 1..Length,
append(Prefix, [H], Stem),
append(Stem, Suffix, List),
append(Prefix, Suffix, SubList),
SubTot #= Tot-1,
combination(SubTot, SubList, T).
My query result starts fine but then returns a Global out of stack error:
?- combination(3,[a,b,c,d,e,f],L).
L = [a, b, c] ;
L = [a, b, d] ;
L = [a, b, e] ;
L = [a, b, f] ;
Out of global stack
I can't understand why it works at first, but then hangs until it gives Out of global stack error. Happens on both SWISH and swi-prolog in the terminal.
if you try to input, at the console prompt, this line of your code, and ask for backtracking:
?- append(Prefix, [H], Stem).
Prefix = [],
Stem = [H] ;
Prefix = [_6442],
Stem = [_6442, H] ;
Prefix = [_6442, _6454],
Stem = [_6442, _6454, H] ;
...
maybe you have a clue about the (main) problem. All 3 vars are free, then Prolog keeps on generating longer and longer lists on backtracking. As Boris already suggested, you should keep your program far simpler... for instance
combination(0, _, []).
combination(Tot, List, [H|T]) :-
Tot #> 0,
select(H, List, SubList),
SubTot #= Tot-1,
combination(SubTot, SubList, T).
that yields
?- aggregate(count,L^combination(3,[a,b,c,d,e],L),N).
N = 60.
IMHO, library(clpfd) isn't going to make your life simpler while you're moving your first steps into Prolog. Modelling and debugging plain Prolog is already difficult with the basic constructs available, and CLP(FD) is an advanced feature...
I can't understand why it works at first, but then hangs until it gives Out of global stack error.
The answers Prolog produces for a specific query are shown incrementally. That is, the actual answers are produced lazily on demand. First, there were some answers you expected, then a loop was encountered. To be sure that a query terminates completely you have to go through all of them, hitting SPACE/or ; all the time. But there is a simpler way:
Simply add false at the end of your query. Now, all the answers are suppressed:
?- combination(3,[a,b,c,d,e,f],L), false.
ERROR: Out of global stack
By adding further false goals into your program, you can localize the actual culprit. See below all my attempts: I started with the first attempt, and then added further false until I found a terminating fragment (failure-slice).
combination(0, _, []) :- false. % 1st
combination(Tot, List, [H|T]) :-
length(List, Length), Tot in 1..Length, % 4th terminating
append(Prefix, [H], Stem), false, % 3rd loops
append(Stem, Suffix, List), false, % 2nd loops
append(Prefix, Suffix, SubList),
SubTot #= Tot-1, false, % 1st loops
combination(SubTot, SubList, T).
To remove the problem with non-termination you have to modify something in the remaining visible part. Evidently, both Prefix and Stem occur here for the first time.
The use of library(clpfd) in this case is very suspicious. After length(List, Length), Length is definitely bound to a non-negative integer, so why the constraint? And your Tot in 1..Length is weird, too, since you keep on making a new constrained variable in every step of the recursion, and you try to unify it with 0. I am not sure I understand your logic overall :-(
If I understand what the exercise is asking for, I would suggest the following somewhat simpler approach. First, make sure your K is not larger than the total number of elements. Then, just pick one element at a time until you have enough. It could go something like this:
k_comb(K, L, C) :-
length(L, N),
length(C, K),
K =< N,
k_comb_1(C, L).
k_comb_1([], _).
k_comb_1([X|Xs], L) :-
select(X, L, L0),
k_comb_1(Xs, L0).
The important message here is that it is the list itself that defines the recursion, and you really don't need a counter, let alone one with constraints on it.
select/3 is a textbook predicate, I guess you should find it in standard libraries too; anyway, see here for an implementation.
This does the following:
?- k_comb(2, [a,b,c], C).
C = [a, b] ;
C = [a, c] ;
C = [b, a] ;
C = [b, c] ;
C = [c, a] ;
C = [c, b] ;
false.
And with your example:
?- k_comb(3, [a,b,c,d,e,f], C).
C = [a, b, c] ;
C = [a, b, d] ;
C = [a, b, e] ;
C = [a, b, f] ;
C = [a, c, b] ;
C = [a, c, d] ;
C = [a, c, e] ;
C = [a, c, f] ;
C = [a, d, b] ;
C = [a, d, c] ;
C = [a, d, e] ;
C = [a, d, f] ;
C = [a, e, b] ;
C = [a, e, c] . % and so on
Note that this does not check that the elements of the list in the second argument are indeed unique; it just takes elements from distinct positions.
This solution still has problems with termination but I don't know if this is relevant for you.

Some doubts about how work this particular query of a delete from a list predicate

I have a doubt about how work this query for this del/3 predicate:
/* BASE CASE: If I delete X from List and X is the HEAD of List, NewList is
the Tail of List
*/
del(X, [X|Tail], Tail).
/* GENERAL CASE: If the head of List is not X then the program have to delete
X in the Tail of List
*/
del(X, [Y|Tail], [Y|Tail1]) :- del(X, Tail, Tail1).
The predicate logic is very simple: delete X item form a list creating a new list without X: if X is in the head of the list the newlist is its tail. Otherwise, if the X item is not in the head of the list, try to find it (and delete) in the Tail creating a new tail Tail1.
Ok, so I have no problem with the predicate logic but I am having some problem trying to understand how work this query (I have to use it in another program):
del([Top1|Stack1], [[a,b,c],[],[]], Stacks1).
So this query have to delete [Top1|Stack1] from [[a,b,c],[],[]] that is a list of stacks (in this particular case I have 3 stacks: [a,b,c] and 2 empty stacks: []) generating so a new list of stacks named Stacks1
If I try to execute a trace of the query I obtain this:
[trace] ?- del([Top1|Stack1], [[a,b,c],[],[]], Stacks1).
Call: (7) del([_G389|_G390], [[a, b, c], [], []], _G412) ? creep
Exit: (7) del([a, b, c], [[a, b, c], [], []], [[], []]) ? creep
Top1 = a,
Stack1 = [b, c],
Stacks1 = [[], []] .
I have some difficulties to understand why: [Top1|Stack1] is unified with the first stack [a, b, c]
EDIT:
I think that maybe work in this way: The list of Stacks is: [[a,b,c],[],[]] that is a list of lists wherein the first list is: [a,b,c] (that is the head of this list of list**
So when I write: [Top1|Stack1] happens that:
Top1 = [a,b,c]
*Stack1 = [[],[]]*
So happens that Top1 is the first stack in the stacks list and Stack1 is the list of others stacks.
So when I write the predicate:
del([Top1|Stack1], Stacks, Stacks1).
(where, for example: Stacks = [[a,b,c],[],[]])
It work in this way:
It unifiest Top1 with the first stack in stack list: [a,b,c] and delete it from Stacks list...
My dount is related on Prolog semantic viz when I execute a simple query as:
del(b, [a,b,c], NewList).
it delete b item from the list and NewList=[a,c]
but when I have that the field of the item that have be delete is something like: [Head|Tail] is it the Head item that have be to deleted?
The query D=[[a,b,c],[],[]], del([A|B], D, C) picks any list matching [A|B] from among D's elements. The only possibility here is [A|B]=[a,b,c] and the leftovers are C=[[],[]].
In general del fully backtracks, finding all possibilities one by one. Here there is only one possibility.
To better understand del, try this:
2 ?- del(X,[A,B,C],D).
X = A,
D = [B, C] ;
X = B,
D = [A, C] ;
X = C,
D = [A, B] ;
false.
It isn't trying to "find" X; it's just picking it one by one from among the possibilities (the 2nd argument). That's what the predicate says that it is / does.
Of course if the lists are instantiated to ground terms, some might not match and will be rejected, creating an impression of a value being searched for:
4 ?- del(b, [a,b,c,d], R).
R = [a, c, d] ;
false.
5 ?- del(b, [a,b,X,d], R).
R = [a, X, d] ;
X = b,
R = [a, b, d] ;
false.
The term [A|B] just matches any non-empty list (or a logical variable):
6 ?- del([A|B], [[a],b,X,d], R).
A = a,
B = [],
R = [b, X, d] ;
X = [A|B],
R = [[a], b, d] ;
false.
7 ?- del([A|B], [[a],b,[],d], R).
A = a,
B = [],
R = [b, [], d] ;
false.
So e.g. del([A|B], [[1,2,3], [4,5], [], [6]], R) will pick any non-empty list from the 4 lists given in the 2nd argument of this sample call, and while it does that, it will bind A to the head element and B to the rest of elements of the list that was picked.
This predicate is known as select/3 in the wild. :)
Illustration:
del(X, [X|Tail], Tail).
X X
--------------------
T T
a a
i i
l l
del(X, [Y|Tail], [Y|Tail1]) :- del(X, Tail, Tail1).
Y Y
--------------------
T T
/ . a
X - . i
\ . l
. 1
l

Resources