Prolog: If Two Nodes Can Be Traversed To In A Graph - prolog

Here's the Question I was given:
Define a Prolog predicate path(X,Y,G), where path(-,-,+), which is
true when there is a path from node X to node Y in a directed graph G,
where the graph is represented by a list of edges, each represented by
a two-element list of the source and destination nodes.
Here's the sample output:
?- path(b,Y,[[a,b],[b,c],[b,d],[d,e]]).
Y = c ;
Y = d ;
Y = e ;
no
?- path(X,b,[[a,b],[b,c],[b,d],[d,e]]).
X = a ;
no
?- path(c,e,[[a,b],[b,c],[b,d],[d,e]]).
yes
This is different to other examples I've seen online where the node traversals would be facts such as:
canTraverse(a,b).
canTraverse(b,c).
etc.
So I'm pretty stumped with it.
This is what I've gotten out so far:
path(X, Y, G) :-
(
G = [CurrentPair | EverythingElse],
CurrentPair = [X1 , Y1| _],
=(X, X1),
=(Y, Y1)
)
;
path(X, Y, EverythingElse).
Which seemed to work if the two nodes X and Y were in a pair/list together. But I'm not sure how to get it to traverse across nodes.

The graph is directed and doesn't have cycles.
The base case could be path(X, Y, G) :- member([X,Y],G). and the recursive case can say for there to be a path from X to Y, take a step from X to some middle note then find a path from middle to Y.

Related

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.

Enumerating all walks in a graph

Given a term representation of an edge of a graph, i.e.:
edge(a, b).
edge(b, c).
I would like to construct a predicate path/1 which succeeds iff its sole argument is a valid path in this graph (that is, for every two adjacent terms X, Y, edge(X, Y) holds). Given a variable, it should enumerate all walks (which could have repeated nodes). My first try:
path([X, Y]) :- edge(X, Y).
path([X, Y, Z | T]) :-
path([Y, Z | T]),
edge(X, Y).
It works as intended except for the case where it is supplied an acyclic graph - path finds all solutions and then halts, unable to construct any other path. On the other hand, swapping first and second term will result in many walks being skipped, due to the DFS nature of Prolog resolution.
My second attempt:
path(P) :- length(P, L), L >= 2, (path(P, L) *-> true ; (!, fail)).
path([X, Y], 2) :- edge(X, Y).
path([X, Y, Z | T], L) :-
L >= 3,
L1 is L - 1,
edge(X, Y),
path([Y, Z | T], L1).
It works as intended, but using a soft cut feels a bit forced. I was wondering if there was an easier way to accomplish this, perhaps a simpler simulation of a soft cut is possible in this particular scenario?
Testing your first solution, after finding three paths ([a,b],[a,c],[a,b,c]), it loops. One super quick way to avoid this is to use tabling wich is available in XSB, SWI and YAP. In case of SWI just add :- table path/1. as first directive to avoid loops. Otherwise you need to remember all path and there are plenty of answers you can look at (like this).
Given your graph definition:
edge(a, b).
edge(b, c).
Something like ought to do you:
path(P) :-
node(X),
walk(X,_,P)
.
walk(A,B,P) :-
walk(A,B,[],V),
reverse(V,R),
P = [A|R]
.
walk( A, B, T, V ) :-
edge(A,X),
not( member(X,T) ),
(
( B = X , V = [B|T] )
;
walk( X, B, [A|T], V )
)
.
%
% enumerate the distinct nodes in edge/2 via backtracking
%
node(N) :-
setof( X , edge(X,_);edge(_,X) , Ns ),
node( Ns , N )
.
node( [N|_] , N ).
node( [_|Ns] , N ) :- ( Ns , N ).

Rule to test whether two lists contain same two elements fails due to uniqueness constraint

I'm trying to create a rule called redundancy that examines lists to see if two elements appear together in more than one list.
Here is my code:
columns([a,b,c]).
columns([b,c,d]).
in(X, [H|_]) :-
X = H.
in(X, [_|T]) :-
in(X, T).
redundancy(X, Y) :-
columns(A),
columns(B),
A \= B,
X \= Y,
in(X, A),
in(X, B),
in(Y, A),
in(Y, B).
The problem is the constraint X \= Y. I want it in there to exclude instances where X and Y are identical elements, which would be true for all single elements that appear in more than one list. But it only returns false for the given columns even though it should return permutations of b and c.
?- redundancy(U, T).
false.
If I comment out the constraint I get the expected elements along with the unwanted ones mentioned above.
?- redundancy(X, Y).
X = Y, Y = b ;
X = b,
Y = c ;
X = c,
Y = b ;
X = Y, Y = c ;
X = Y, Y = b ;
X = b,
Y = c ;
X = c,
Y = b ;
X = Y, Y = c ;
false.
Is there a way to enforce this constraint? I'm also interested in ideas to restrict results to a given combination of elements rather than permutations.
Simply move X \= Y to the last line of your predicate. also, see prolog-dif and instantiation-error.
The thing to avoid is using non-pure predicates with not-yet-instantiated logical variables (unless this is exactly what you intended, and you know what you're doing).
Another thing to notice is that X \= Y is not a constraint (that's dif), but a check.

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.

How to get strongest path prolog?

I'm trying to create a social graph and I have to write some Prolog in order to get the minimal and the strongest path.
My knowledge base only has the following statements:
edge(source, destination, weight)
example: (john, mary, 2).
The weights can only be 3 for now:
1 - Friend
2- Close friend
3 - Family
Here is my code to the minimal path (less weighted).
findapath(X, Y, W, [X,Y], _) :- edge(X, Y, W).
findapath(X, Y, W, [X|P], V) :- \+ member(X, V),
edge(X, Z, W1),
findapath(Z, Y, W2, P, [X|V]),
W is W1 + W2.
:-dynamic(solution/2).
findminpath(X, Y, W, P) :- \+ solution(_, _),
findapath(X, Y, W1, P1, []),
assertz(solution(W1, P1)),
!,
findminpath(X,Y,W,P).
findminpath(X, Y, _, _) :- findapath(X, Y, W1, P1, []),
solution(W2, P2),
W1 < W2,
retract(solution(W2, P2)),
asserta(solution(W1, P1)),
fail.
findminpath(_, _, W, P) :- solution(W,P), retract(solution(W,P)).
How to include a variable to count the number of paths traveled and then use that to get the strongest path?
The strongest path is path weight / number of paths traveled.
So for example,
Weight = 8
N Paths traveled = 3
8/3 = 2.67 strength
Which means that there are 3 people between me and my destination (this is a social graph) and their weighted sum is 8.
But in this case
Weight = 7
N Paths traveled = 7
This would be the minimal path instead, right? YES, because it's 7 and 7 < 8. However, it is NOT the strongest path because 7/7 = 1 and that means that I probably had loads of people between me and my destination that weren't as close to me as the other path.
How would I do this?

Resources