Two different paths from X to Y in a graph - prolog

I am stuck with the following Prolog question:
Given the edges of a graph with no cycles as facts. e.g:
edge(a, b).
edge(b, c).
edge(c, d).
edge(c, e).
...
I have to write a predicate that tests whether there are two different paths between vertices X and Y. e.g the call two_paths(a, c). should return true if there are two different paths from node a to node c. I know how to check whether there is a path between two vertices:
path(X, Y) :- edge(X, Y).
path(X, Y) :- edge(X, Z), path(Z, Y).
But how should I do this to check for two distinct paths? Thank you very much for your help.

An idea might be to create a predicate path/3 that returns the constructed path, and then query for two paths that are different. Something like:
path(X,Y,[X,Y]) :- edge(X,Y).
path(X,Y,[X|T]) :- edge(X,Z), path(Z,Y,T).
Now path(a,c,T) will show you the path:
?- path(a,c,L).
L = [a, b, c] ;
false.
Now you could construct a predicate:
two_paths(X,Y) :-
path(X,Y,La),
path(X,Y,Lb),
dif(La,Lb).
In other words, you ask Prolog to construct for you a path La, next construct for you a path Lb and then check if they are not equal (dif(La,Lb)). The first constructed Lb will be equivalent to La, but due to Prologs backtracking mechanism, it will try to construct you another path for which the condition might succeed. This is a rather pure Prolog implementation (with no cut (!), once/1, etc.). More efficient approaches exists since here you will "redo" the work in your second call.
A more efficient approach could be to construct a predicate path_avoid/3 (or path_avoid/4) where you feed the first constructed path to the predicate and thus force your program to at least at some point perform a different step from the one presented. I leave this open as a potential exercise.

Related

Prolog seemingly ending recursive call without going back up

I've got the following two edges defined:
edge(a,b).
edge(b,c).
add(X, L, [X | L]).
Now I'm trying to recursively build the path from a to c (a,b,c) using this:
path(FROM,TO,W):-
edge(FROM,TO),
add(TO, [], X),
add(FROM, X, W).
path(FROM,TO,W):-
edge(FROM,Y),
path(Y,TO, W),
add(FROM, W, _).
It seems to work fine in the base case as path(a,b,X) will output X = [a,b].
However, path(a,c,X) only outputs X = [b,c], as if it just gets to the base case and ends it there rather than going back up the recursive call.
Ultimately, I would like it to output X = [a,b,c] but I'm out of ideas.
FYI I'm using SWI-Prolog.
How can you identify the problem yourself? There is an easy way to do so. First, identify the cases you expect to be true and cases you expect it to fail. Here are some plus some auxiliary definitions to ease debugging
:- initialization(path(a,b,[a,b])).
:- initialization(path(a,c,[a,b,c])). % intended to be true, initially false
:- initialization(\+path(a,b,[a,_,c])).
:- initialization(\+path(a,d,_)). % no node d, thus no path
:- op(950, fy, *).
:- meta_predicate(*(0)).
*_G_0. % this permits us to remove goals by adding a * in front.
Now, save the program ..and say [program]. You will get a warning like
* user:user:path(a,c,[a,b,c]) - goal failed
So we know that we have not solved the problem.
Now, try to generalize the program by adding * in front of a goal. You will note that for each and every such generalization there will be more errors appearing (and in one case even non-termination). Except for the last goal where everything remains the same. So removing or replacing that goal seems to be a good idea.
Sooner or later you will encounter cycles. For acyclic paths, use path/4:
?- path(edge, Path, A, B).
Your code does not work correctly because it discards the result produced by the subgoal add(FROM, W, _), in the second clause of the predicate path/3. To solve the problem, you need to modify your code as following:
path(From, To, W):-
edge(From, To),
add(To, [], X),
add(From, X, W).
path(From, To, PATH):-
edge(From, Y),
path(Y,To, W),
add(From, W, PATH). % <== get PATH
An even better version of this code is as follows:
path(From, To, [From, To]) :-
edge(From, To).
path(From, To, [From|Path]) :-
edge(From, X),
path(X, To, Path).

Creating Metavariables in Prolog

I am working with implementing a unification algorithm for a term rewriting system in Prolog. To fully implement this, I need a predicate substituting out a given subterm for another term. Unfortunately, the way that Prolog instantiates fresh variables prevents the system from successfully be able to achieve this. I have some built in operators, star and divi (really just representing the * and / symbols, but in prefix form). My substitute predicate is made up of the following predicates:
replace(A,B, [], []).
replace(A,B, [H|T], [B|T2]) :- (H==A)->replace(A, B, T, T2).
replace(A,B, [H|T], [H|T2]) :- (H\==A)->replace(A, B, T, T2).
replace_lst([], [H|T], [H2|T2]).
replace_lst([H1|T1], [H|T], [H2|T2]) :-
arg(1,H1,X),
arg(2,H1,Y),
replace(X,Y,[H|T],[H2|T2]),
replace_lst(T1,[H|T],[H2|T2]).
substitute([H|T],A,X):-
A=..List,
replace_lst([H|T],List,C),
X=..C.
Where this runs into trouble is that, for instance, the terms star(X,X) and star(Y,Y), are, by the logic of my rewrite system, structurally equivalent and require no such substitution. However, comparing these two terms using the unifiable predicate will lead Prolog to attempting unification for the two, and the resulting term is no longer equivalent in structure to the original star(X,X) structure. Therefore, I attempted to check for term equality through their structure, but this leads to another can of worms in which, for instance, my rewrite system contains the rewrite rule:
star(X,X)==>X.
However,attempting to substitute based on the variant equality =#= operator leads to the issue of Prolog seeing two differently instantiated terms with the same structure as the same term. Therefore, defining a variant-based subtitution predicate like so:
variant_replace(A,B, [], []).
variant_replace(A,B, [H|T], [B|T2]) :- (H=#=A)->variant_replace(A, B, T, T2).
variant_replace(A,B, [H|T], [H|T2]) :- (H\=#=A)->variant_replace(A, B, T, T2).
variant_replace_lst([], [H|T], [H2|T2]).
variant_replace_lst([H1|T1], [H|T], [H2|T2]) :-
arg(1,H1,X),
arg(2,H1,Y),
variant_replace(X,Y,[H|T],[H2|T2]),
variant_replace_lst(T1,[H|T],[H2|T2]).
variant_substitute([H|T],A,X):-
A=..List,
variant_replace_lst([H|T],List,C),
X=..C.
Leads to an issue where if I have some term:
star((star(X,Y),star(A,B))
and I want to substitute the star(X,Y) subterm with the following predicate:
?- variant_substitute([star(X,Y)=hole],star(star(X,Y),star(A,B)),D).
D = star(hole, hole) .
We can see Prolog, by the logic of the variant substitution predicate, will simply check for terms of a given structure, disregarding the actual variable instantiation. Therefore, I need a way to declare variables. What I desire is to have a system that is able to use metavariables declared in such a way each given term has up to N unique variables, ranging in value from V(0) to V(N-1). Ideally, such a system of metavariables would look like so:
substitute([star(v(0),v(1))=hole],star(star(v(0),v(1)),star(v(2),v(3))),D).
D = star(hole, star(v(2), v(3)))
I need Prolog to see terms denoted with v(#) as variables since I will need to use the unifiable predicate down the road to compare them to the original declaration of my rewrite rules, which is declared like so :
star(X,X) ==> X.
divi(X,X) ==> X.
divi(star(X,Y),Y) ==> X.
star(divi(X,Y),Y) ==> X.
star(X, star(Y,Z)) ==> star(star(divi(X,Z),Y),Z).
divi(X, star(Y,Z)) ==> star(divi(divi(X,Z),Y),Z).
star(X, divi(Y,Z)) ==> divi(star(star(X,Z),Y),Z).
divi(X, divi(Y,Z)) ==> divi(divi(star(X,Z),Y),Z).
What would be the best way to implement this format of metavariable in Prolog?

Printing the shortest path in a directed graph

I have a directed, cyclic graph without weights. I want to find the shortest route between A and B (i.e. the one with the least hops).
This is the code i got so far:
path(A,B) :- walk(A,B,[]).
walk(A,B,V) :- edge(A,X), not(member(X,V)), (B=X); walk(X,B,[A|V]).
edge(a, b).
edge(b, c).
edge(a, d).
edge(c, a).
This code prints true, once for every route it finds. How can i print the path? And what would i have to do to find the path with the least hops?
You need to unify an extra argument with what you have accumulated in V once you reach your termination condition:
path(A,B,P) :- walk(A,B,[],P).
walk(B,B,V,P) :- reverse(V,P).
walk(A,B,V,P) :- dif(A,B), edge(A,X), maplist(dif(X),V), walk(X,B,[A|V],P).
Once A and B are the same, it means that we don't have to walk the graph anymore. In that case we reverse the path that we accumulated in V as P, which gets "returned" from path/3.
Note: it is almost always clearer to put the code that checks the recursion termination as a separate rule, instead of using ;.
To find the path with the least hops, you can find all paths from the two points you want, and then take the smallest one:
shortest_path(A,B,S) :-
findall(P, path(A,B,P), Ps),
maplist(prepend_length, Ps, Ls),
sort(Ls, [[_,S]|_]).
prepend_length(P, [L,P]) :-
length(P,L).
To yield the path, you'd need to add an argument to keep track of it; for example:
path(A,B,P) :- walk(A,B,[],P).
walk(A,B,V,[A,B]) :- edge(A,X), not(member(X,V)), (B=X).
(I'll leave the recursive case as an exercise.)
To find the shortest path, you could find them all (findall) & pick out the shortest.

Printing path in Prolog

I want to print the path of nodes in a directed graph.
This code works properly for an edge but didn't work for the whole path.
It returns false when it comes to path.
Here is my code but it is only running for just an edge and not for the whole path.
Kindly help me out.
Here is my code:
path(Node1, Node2, X) :-
edge(Node1, Node2),
append([Node1], [Node2], X).
path(Node1, Node2, X, N) :-
edge(Node1, SomeNode),
append([Node1], [SomeNode], X),
path(SomeNode, Node2, X, N),
append([], [Node2], X).
X is a list.
While #WouterBeek already pinpointed your problems, Wouter's statement
Without running this code you can already observe that the second clause will always fail, since a list of length 2 cannot be unified with a list of length 1
merits some elaboration. For an experienced Prolog programmer it is easy to spot such problems. But what can beginners do? They can apply the following technique: Generalize your program and if the generalized program is still too specialized, there must be an error in the remaining part.
Generalizing a pure Prolog program
There are several ways to generalize a pure Prolog program: Either remove goals, or remove subterms in arguments of the head or a goal. To remove goals, I will add a * in front of a goal, using:
:- op(950,fy, *).
*_.
path(Node1, Node2, X) :-
* edge(Node1, Node2),
append([Node1], [Node2], X).
path(Node1, Node2, X) :-
* edge(Node1, SomeNode),
append([Node1], [SomeNode], X),
* path(SomeNode, Node2, X),
append([], [Node2], X).
Now we can ask the most general query of this new predicate:
?- path(N1, N2, P).
P = [N1,N2]
; false.
Therefore: Although this definition is now an (over-) generalization, it still admits only paths of length 2. The problem is completely independent of the definition of edge/3, only the remaining part is responsible. So look at the remaining part to fix the problem!
In your second clause you have the following two statements:
append([Node1], [SomeNode], X),
append([], [Node2], X).
Notice that variable X occurs in both statements, and this must be instantiated to the same list. This means that [Node1]+[SomeNode] = []+[Node2] or [Node1,SomeNode]=[Node2].
Without running this code you can already observe that the second clause will always fail, since a list of length 2 cannot be unified with as a list of length 1.
Another point: the two clauses do not belong to the same predicate, since the former has arity 3 while the latter has arity 4. Typically, for calculating paths or arbitrary depth, you need a predicate that consists of two clauses that belong together: a base case and a recursive case. For the recursive case it is a common practice to use the head/tail notation to construct the path: [FromNode,ToNode|RestOfPath].
Hope this helps!

Return shortest path with Breadth-first search in prolog

I would like to find the shortest route from station A to station B in prolog in bidirectional graph(if A is connected to B than B is connected to A),the graph has no weights on branches. The question is posted like this
solve(Start,End,Path).
Start-starting station.
End-Destination station.
Path-List of all stations passed with the shortest route. The distance between any two directly connected stations in the graph is equal.
fact in base are like this:
fact("Staion1","metroline","Station2","metroline").
metro line is the number of line that connects the two staions directly. If 2nd and 4th argument are the same the stations are connected directly.
line("Abbesses","12","Pigalle","12").
line("Abbesses","12","Lamarck Caulaincourt","12").
line("Ale'sia","4","Mouton Duvernet","4").
line("Ale'sia","4","Porte d'Orle'ans","4").
line("Alexandre Dumas","2","Philippe Auguste","2").
line("Alexandre Dumas","2","Avron","2").
line("Alma Marcesu","9","Ie'na","9").
EDIT:
I tried to solve the problem and I figure out that it would work faster if use BFS.
here is the solution that I wrote:
solve(Start,End,Path):-solve1([Start],End,[Start],Path).
solve1([P|O],End,Visited,[End|?]):-children(P,S),member(End,S),!.
solve1([P|O],End,Visited,Path):-(not(member(P,Visited)),children(P,S),append(O,S,O1),solve1(O1,End,Visited,Path));
(solve1(O,End,Visited,Path)).
?-should be the list with path to destination Node
The only problem is that i don't know how to return the path to the destination node.
Thank's ahead.
You can use Dijkstra's algorithm.
http://en.wikipedia.org/wiki/Dijkstra's_algorithm
It begs the question, whether a breadth first algorithm has any advantage over a depth first algorithm. You anyway detect cycles via the member/2 predicate, so there is no issue of completness.
Lets say we have this graph, without any cycles:
Which can be represented in Prolog as these facts:
% edge(-Vertex, -Vertex)
edge(a, b). edge(a, c).
edge(a, d). edge(b, c).
edge(c, d). edge(c, e).
edge(d, e).
Then tabling with aggregate functions does the job:
% path(+Vertex, +Vertex, -Integer)
:- table path(_,_,min).
path(X, X, 0).
path(X, Y, N) :-
edge(X, Z), path(Z, Y, M), N is M+1.
Here are some example runs:
?- path(a, e, X).
X = 2
?- path(a, e, 3).
No
You can also modify the code, so that it detects cycles and/or returns paths. What is helpful for the later, is using custom aggregate functions.
Disclaimer: For practical purposes you wouldn't use something that boils down to Dijkstra's algorithm. You would rather use something from the A* algorithm family.

Resources