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,assertaand 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.