This is an extension of what I did in Prolog class. In class, I was asked to write a predicate path(X, Y, N)
which returns true if and only there is a path from node X
to node Y
with length N
. What is given is the list of directed edges with corresponding weights, e.g. edge(a, b, 3)
and edge(c, d, 10)
.
The given problem is pretty straightforward (only some recursion and base cases). However, I thought that maybe I could extend this a little further. Given that the simple directed graph input may contain cycles and contains only non-negative weights, what is the length of the unique shortest path from given nodes A
and B
. (By unique, I mean that this predicate should return false if more than one shortest path exists from A
to B
).
Here is an example of the database which contains a cycle (a, b, c, e, a).
edge(a, b, 1).
edge(b, c, 2).
edge(c, d, 1).
edge(b, d, 4).
edge(c, e, 1).
edge(e, a, 10).
edge(e, d, 6).
I thought that to satisfy the unique condition, I thought that I should augment the original path/3
predicate to also contain the path information as a list (so that I could compare the path uniqueness later). This new augmentation is reflected in the new path/4
predicate.
path(X, X, 0, []).
path(X, Y, N, [Y]) :- edge(X, Y, N).
path(X, Y, N, [Z|T]) :- edge(X, Z, N1), N2 is N - N1, N2 > 0, path(Z, Y, N2, T).
path(X, Y, N) :- path(X, Y, N, _).
As of this code, I already found a problem: if I try to unify the predicate with path(a, b, N, Z)
, this will not work because N
will not be able to unify with N2 is N - N1
. However, if I change this part to N is N1 + N2
, this will still not work because N2
is still not unified. If I change the whole predicate line to:
path(X, Y, N, [Z|T]) :- edge(X, Z, N1), path(Z, Y, N2, T), N is N1 + N2.
This will then run endlessly because the number of paths are possibly infinite because the graph may contain loops (which I want to try to keep it that way as a challenge).
As for the shortestpath/3
predicate, I cannot find all paths and check whether all the paths are longer because the number of paths may be infinite due to having a cycle. Instead, I tried to find any paths which have length between 0 and the given N
; if no path exists, then this is definitely the shortest path.
countdown(N, N).
countdown(N, X) :- N1 is N - 1, N1 >= 0, countdown(N1, X).
shortestpath(A, B, N) :- path(A, B, N), \+((countdown(N, N1), N > N1, path(A, B, N1))).
However this doesn't address the N
given as a variable (because the countdown function wouldn't work), let alone the unique constraint.
So my question is, is there a way to make this question work or is it actually impossible to do so? If there is such a solution, please kindly provide it here (or if you think this is a "homework" question, please at least guide me in the correct direction).
Constraints:
I don't want to use any built-in predicates. Only 'simple' or 'core' predicates such as
\+
,is
,+
, for example.var
,nonvar
,asserta
and similar predicates are also somewhat acceptable (since there is no alternative which achieves the same functionality).I want it to be as general as possible; that is, any arguments for the predicate should be able to give as a variable. (or at least have the last argument of
shortestpath/3
, which is the length of the shortest path, a variable).
I have looked through the following questions already and it doesn't answers my situation:
Find the shortest path between two nodes in a graph in Prolog (Doesn't address weighted edges and also uses complex predicates (e.g.
path/4
).search all paths and the shortest path for a graph - Prolog (Doesn't address graphs with cycles).
Find the shortest path between two nodes in a graph in (Prolog) and display the result as array (Doesn't address weighted edges).
Please feel free to point me to any other question that address my question.