undirected graph - prolog

How can I find all way to go end in undirected graph ?
Graph :
Node : S, Y, F, T visualization : S ----- Y ---- T
Edge : S --- Y \ /
Y --- F \ /
S --- F \ /
Y --- T F
Assume that
Start S
Finish F
after run go
result will be :
S F
S Y F
I do not want visit one node more than once. If I visit, this problem become one of NP problems.
EDIT:
input can be any form
example:
edge (S,Y). OR edge (Y,S).
edge (Y,F). edge (F,Y).
edge (S,F). edge (F,S).
edge (Y,T). edge (T,Y).
BUT OUTPUT MUST BE SAME

Keep a trace of the visited nodes and exclude those when adding the possible destinations in the next step.
I changed a few edges and added one, to make sure it also works when edges are listed in the 'wrong' order for going from S to F.
edge('S','Y'). visualization: S -- Y -- T
edge('F','Y'). / \ /
edge('S','F'). / \/
edge('Y','T'). A --- F
edge('A','S').
edge('F','A').
In prolog this should roughly look like this:
pathBetween(A,A,_):- !, fail.
pathBetween(S,F,Visited) :- (edge(S,F) ; edge(F,S)),
append(Visited,[S,F],L),
write(L).
pathBetween(S,F,Visited) :-
( edge(S,A) ; edge(A,S) ),
not(member(A,Visited)),
pathBetween(A,F,[S|Visited]).
You can use ; to manually find all the solutions, or findall.
?- findall(Visited, pathBetween('S', 'F', []), _).
[S,F][S,Y,F][S,A,F]
true.

Related

Prolog, give a path from point x to the goal

This is my code:
% A The first link as a predicate
link(1,2).
link(2,3).
link(3,4).
link(3,6).
link(6,7).
link(6,5).
So what we did with the path predicate is check from a given starting point check if there exists a path from that point to the goal (which is defined at the top). This gives the correct outcome for all possible values.
What I need to do now is, I know there is a valid path from 1 to the goal, my path() predicate told me so, now i need to return a list of nodes that shows the that path to the goal, so with using path(L), path([2,3,6,5]) is true.
If I understand your problem statement, you
Have a directed graph (a digraph) defined by link(From,To).
Wish to be able to traverse that digraph from some origin node to some destination node and identify the path[s] between the 2 nodes.
This is a pretty straightforward problem that doesn't require assert/1 or retract/1.
A common pattern in Prolog programming is the use of helper predicates that carry extra arguments that track the persistent state required to accomplish the task at hand.
Graph traversal, as an algorithm, is pretty easy (and nicely recursive):
You can travel from node A to node B if nodes A and B are directly connected, or
You can travel from node A to node B if node A is connected to some other node X such that you can travel from node X to node B.
The trick here is that you need to track what nodes you've visited (and their order), and you'd want to do that anyway, so that you can detect cycles in the graph. Trying to traverse a cyclic graph like this::
a β†’ b β†’ c β†’ b
leads to infinite recursion unless you check to see whether or not you've already visited node b.
That leads pretty direction to an implementation like this:
A traverse/3 predicate, traverse(Origin, Destination,Path), and,
A traverse/4 helper predicate, traverse(Origin,Destination,Visited,Path).
You can fiddle with it at https://swish.swi-prolog.org/p/oZUomEcK.pl
link(1,2).
link(2,3).
link(3,4).
link(3,6).
link(6,7).
link(6,5).
% ---------------------------------------------------------------------------
%
% traverse( Origin, Destination, ReversePath )
%
% traverse/3 traverses a directed graph defined by link(From,To).
% The path taken is listed in reverse order:
% [Destination, N3, N2, N1 , Origin]
%
% ---------------------------------------------------------------------------
traverse(A,B,P) :- % to traverse a graph
traverse(A,B, [], P) % - invoke the helper, seeding the visited list with the empty list
. %
% ---------------------------------------------------------------------------
% traverse( Origin, Destination, Visited, ReversePath )
% ---------------------------------------------------------------------------
traverse( A , B , V , [B,A|V] ) :- % Traversal of a graph from A to B,
link(A,B) % - succeeds if there exists an edge between A and B.
. %
traverse( A , B , V , P ) :- % otherwise, we can get from A to B if...
link(A,X) , % - an edge exists between A and some node X
\+ member(X,V) , % - that we have not yet visited,
traverse(X,B,[A|V],P) % - and we can get from X to B (adding A to the list of visited nodes
. %
Once you have that, invoking traverse/3 with all arguments unbound
?- traverse(A,B,P) .
results in finding all paths in the graph via backtracking.
If you want to know all the paths in the graph, beginning at a specific origin node, invoke it with the origin argument bound:
?- traverse(1,B,P) .
And if you want to know if two specific nodes are connected, invoke it with both origin and destination bound:
?- traverse(1,5,P) .
Note: because we're building the list of visited nodes by prepending them to the list of visited nodes, the path thus build is in reverse (destination-first) order. If you want the path in proper (origin-first) order, just use reverse/2 in the main predicate:
traverse(A,B,P) :- traverse(A,B, [], RP ), reverse(RP,P) .

Why does my implementation of Dijkstra's algorithm fail under this undefined case?

I am trying to write an implementation of Dijkstra's Algorithm in Prolog (specifically, GNU Prolog). To begin, I made a fact connection that describes the connected vertices of the graph below.
It now seems like my implementation works for around half of my test cases, namely these:
s, k
g, h
e, c
But for these,
h, g (this one has an empty Tentative_Connections)
c, e (this one too)
i, j (this one finds the incorrect path of [i, k, j])
it fails.
I based my program on the description given on the algorithm's wikipedia page. Here is some rough psuedocode:
for a current node, find its connecting nodes
remove the ones that are visited
make a new list of nodes that factors in a tentative distance
delete the current node from the list of unvisited nodes
if the destination node is not in the unvisited list, the current built-up path is final
otherwise, recur on the node that is the closest regarding the tentative distance
But no matter what, my code keeps failing (with the errors above). To elaborate on that, here is the failure for the h, g case:
Unchartered = []
Tentative = []
Destination not reached
No tentative connections!
Closest = _1232
Closest = _1232
Next_Path = [h,b,s,c,l,i,k,j]
uncaught exception: error(instantiation_error,(is)/2)
| ?-
It seems like at one point, there are no tentative points left to search. I do not know what to do from here, as Wikipedia does not elaborate on this issue. Does anyone who knows this algorithm well know what I can do to fix my code?
% https://www.youtube.com/watch?v=GazC3A4OQTE
% example graph
% cl; gprolog --consult-file dijkstra.pl --entry-goal main
connection(s, c, 3).
connection(c, l, 2).
connection(l, i, 4).
connection(l, j, 4).
connection(i, j, 6).
connection(i, k, 4).
connection(j, k, 4).
connection(k, e, 5).
connection(e, g, 2).
connection(g, h, 2).
connection(h, f, 3).
connection(f, d, 5).
connection(d, a, 4).
connection(b, d, 4).
connection(b, a, 3).
connection(b, s, 2).
connection(b, h, 1).
connection(a, s, 7).
get_vertices(_, Vertex_Count, Traversed, Traversed) :-
length(Traversed, Vertex_Count).
get_vertices([], _, Traversed, Traversed).
get_vertices([Vertex | Vertices], Vertex_Count, Traversed, Result) :-
get_vertices(Vertex, Vertex_Count, Traversed, More_Traversed),
get_vertices(Vertices, Vertex_Count, More_Traversed, Result).
get_vertices(Vertex, _, Traversed, Traversed) :-
atom(Vertex), member(Vertex, Traversed).
get_vertices(Vertex, Vertex_Count, Traversed, Result) :-
atom(Vertex),
findall(Connected, are_connected(Vertex, Connected, _), Vertices),
get_vertices(Vertices, Vertex_Count, [Vertex | Traversed], Result).
are_connected(A, B, S) :- connection(A, B, S) ; connection(B, A, S).
keep_unvisited([], _, []).
keep_unvisited([Connection | Connections], Unvisited, Result) :-
keep_unvisited(Connections, Unvisited, Tail_Result),
[Connection_Name, _] = Connection,
(member(Connection_Name, Unvisited) ->
Result = [Connection | Tail_Result];
Result = Tail_Result).
w_tentative_distances([], _, []).
w_tentative_distances([Connection | Connections], Node_Val, Result) :-
w_tentative_distances(Connections, Node_Val, Tail_Result),
[Connection_Name, Connection_Val] = Connection,
Tentative_Distance is Connection_Val + Node_Val,
(Connection_Val > Tentative_Distance ->
New_Distance = Tentative_Distance; New_Distance = Connection_Val),
Result = [[Connection_Name, New_Distance] | Tail_Result].
closest_node_([], Closest, Closest).
closest_node_([Node | Rest], Closest, Result) :-
[_, Node_Val] = Node,
[_, Closest_Val] = Closest,
(Node_Val < Closest_Val ->
closest_node_(Rest, Node, Result)
;
closest_node_(Rest, Closest, Result)).
closest_node([Node | Rest], Result) :-
closest_node_(Rest, Node, Result).
dijkstra([Node_Name, Node_Val], Unvisited, Dest_Node, Path, Final_Path) :-
findall([Connected, Dist], are_connected(Node_Name, Connected, Dist), Connections),
keep_unvisited(Connections, Unvisited, Unchartered_Connections),
w_tentative_distances(Unchartered_Connections, Node_Val, Tentative_Connections),
% trace,
delete(Unvisited, Node_Name, New_Unvisited),
format('Path = ~w\nNode_Name = ~w\nNode_Val = ~w\n', [Path, Node_Name, Node_Val]),
format('Unvisited = ~w\nNew_Unvisited = ~w\n', [Unvisited, New_Unvisited]),
% notrace,
format('Connections = ~w\n', [Connections]),
format('Unchartered = ~w\n', [Unchartered_Connections]),
format('Tentative = ~w\n', [Tentative_Connections]),
(member(Dest_Node, Unvisited) -> % destination has not been reached
write('Destination not reached\n'),
(closest_node(Tentative_Connections, Closest); write('No tentative connections!\n')),
format('Closest = ~w\n', [Closest]),
append(Path, [Node_Name], Next_Path),
format('Closest = ~w\nNext_Path = ~w\n', [Closest, Next_Path]),
dijkstra(Closest, New_Unvisited, Dest_Node, Next_Path, Final_Path);
write('The end was reached!\n'),
Final_Path = Path).
dijkstra_wrapper(Start, End, Vertex_Count, Path) :-
get_vertices(Start, Vertex_Count, [], Unvisited),
dijkstra([Start, 0], Unvisited, End, [], Path).
main :-
dijkstra_wrapper(h, g, 13, Path),
write(Path), nl.
The non-working examples have an ever-growing Unvisited list (for some recursive cases)
Path = [h,b,s,c,l,i,k]
Node_Name = j
Node_Val = 4
Unvisited = [g,e,j,a,d,f]
New_Unvisited = [g,e,a,d,f]
Connections = [[k,4],[l,4],[i,6]]
Unchartered = []
Tentative = []
Destination not reached
What do I do if there are no paths to travel down?
Another one.
It uses library(assoc) association lists to store the path associated to each vertex. This avoids having to code a list scan. The list of "vertex that shall be visited next" is still a vanilla list, but we need to eliminate duplicated vertex and retain those vertexes with minimal distances only after each visit, then sort the list by vertex distance so that the next vertex that shall be visitited is at the head.
(SWI-Prolog has a built-in associative data structure, the SWI-Prolog dict, which I'm not using here. Does library(assoc) exist in GNU Prolog? It should...)
?- main.
VisitThese is currently: [0-a]
Neighbors of vertex a : [s-7,d-4,b-3]
Dirty visitations : [7-s,4-d,3-b], Clean visitations : [3-b,4-d,7-s]
VisitThese is currently: [3-b,4-d,7-s]
Neighbors of vertex b : [d-4,a-3,s-2,h-1]
Dirty visitations : [5-s,4-h,4-d,7-s], Clean visitations : [4-d,4-h,5-s]
VisitThese is currently: [4-d,4-h,5-s]
Neighbors of vertex d : [a-4,f-5,b-4]
Dirty visitations : [9-f,4-h,5-s], Clean visitations : [4-h,5-s,9-f]
VisitThese is currently: [4-h,5-s,9-f]
Neighbors of vertex h : [f-3,g-2,b-1]
Dirty visitations : [7-f,6-g,5-s,9-f], Clean visitations : [5-s,6-g,7-f]
VisitThese is currently: [5-s,6-g,7-f]
Neighbors of vertex s : [c-3,b-2,a-7]
Dirty visitations : [8-c,6-g,7-f], Clean visitations : [6-g,7-f,8-c]
VisitThese is currently: [6-g,7-f,8-c]
Neighbors of vertex g : [h-2,e-2]
Dirty visitations : [8-e,7-f,8-c], Clean visitations : [7-f,8-c,8-e]
VisitThese is currently: [7-f,8-c,8-e]
Neighbors of vertex f : [d-5,h-3]
Dirty visitations : [8-c,8-e], Clean visitations : [8-c,8-e]
VisitThese is currently: [8-c,8-e]
Neighbors of vertex c : [l-2,s-3]
Dirty visitations : [10-l,8-e], Clean visitations : [8-e,10-l]
VisitThese is currently: [8-e,10-l]
Neighbors of vertex e : [g-2,k-5]
Dirty visitations : [13-k,10-l], Clean visitations : [10-l,13-k]
VisitThese is currently: [10-l,13-k]
Neighbors of vertex l : [i-4,j-4,c-2]
Dirty visitations : [14-i,14-j,13-k], Clean visitations : [13-k,14-i,14-j]
VisitThese is currently: [13-k,14-i,14-j]
Neighbors of vertex k : [e-5,i-4,j-4]
Dirty visitations : [14-i,14-j], Clean visitations : [14-i,14-j]
VisitThese is currently: [14-i,14-j]
Neighbors of vertex i : [j-6,k-4,l-4]
Dirty visitations : [14-j], Clean visitations : [14-j]
VisitThese is currently: [14-j]
Neighbors of vertex j : [k-4,l-4,i-6]
Dirty visitations : [], Clean visitations : []
Vertex a is at distance 0 via [a-0]
Vertex b is at distance 3 via [a-0,b-3]
Vertex c is at distance 8 via [a-0,b-3,s-5,c-8]
Vertex d is at distance 4 via [a-0,d-4]
Vertex e is at distance 8 via [a-0,b-3,h-4,g-6,e-8]
Vertex f is at distance 7 via [a-0,b-3,h-4,f-7]
Vertex g is at distance 6 via [a-0,b-3,h-4,g-6]
Vertex h is at distance 4 via [a-0,b-3,h-4]
Vertex i is at distance 14 via [a-0,b-3,s-5,c-8,l-10,i-14]
Vertex j is at distance 14 via [a-0,b-3,s-5,c-8,l-10,j-14]
Vertex k is at distance 13 via [a-0,b-3,h-4,g-6,e-8,k-13]
Vertex l is at distance 10 via [a-0,b-3,s-5,c-8,l-10]
Vertex s is at distance 5 via [a-0,b-3,s-5]
As computed by:
% =========
% Graph definition
% =========
% ---------
% "Asymmetric connection relation"
% ---------
% "Connection relation" between vertices. Each edge is labeled with
% a "distance" (cost)
%
% connection(?VertexName1,?VertexName2,?Cost).
%
% This also indirectly defines the set of vertices which are simply
% given by their names, which are atoms.
%
% This relation is not symmetric. We make it symmetric by defining
% a symmetric relations "on top". Improvement: This relation could
% be made "canonical" in that a unique representation would be
% enforced by demanding that VertexName1 #=< VertexName (i.e. the
% vertex names would appear sorted by the standard order of terms).
connection(s, c, 3).
connection(c, l, 2).
connection(l, i, 4).
connection(l, j, 4).
connection(i, j, 6).
connection(i, k, 4).
connection(j, k, 4).
connection(k, e, 5).
connection(e, g, 2).
connection(g, h, 2).
connection(h, f, 3).
connection(f, d, 5).
connection(d, a, 4).
connection(b, d, 4).
connection(b, a, 3).
connection(b, s, 2).
connection(b, h, 1).
connection(a, s, 7).
% ---------
% "Symmetric connection relation"
% ---------
sym_connection(Vertex1,Vertex2,Cost) :- connection(Vertex1,Vertex2,Cost).
sym_connection(Vertex1,Vertex2,Cost) :- connection(Vertex2,Vertex1,Cost).
% =========
% Start measuring paths and their cost from vertex 'a'.
%
% Initially we only know about vertex 'a' itself
%
% - It is the only member in the list of vertices to be visited next,
% at distance/cost 0. This is represented by a single pair in list
% VisitThese: it is a list containing only [0-a].
% - We have a path to 'a' with overall distance/cost 0, containing only
% vertex 'a' found at cost 0: the path is [a-0].
% PathContainerIn maps 'a', the destination vertex, to that path
% [[a-0]].
% The container is implemented by an "association list" (a "map")
% from library(assoc); other abstract data types are possible, in
% particular SWI-Prolog's "dict" if this were running in SWI-Prolog.
% =========
main :-
list_to_assoc([a-[a-0]],PathContainerIn),
VisitThese=[0-a],
% Do it!
dijsktra(VisitThese,PathContainerIn,PathContainerOut),
% We are done! we just need to print out...
assoc_to_list(PathContainerOut,Pairs),
print_list(Pairs).
print_list([]).
print_list([Vertex-Path|More]) :-
reverse(Path,ReversedPath),
Path=[_-Dist|_],
format("Vertex ~q is at distance ~d via ~q~n",[Vertex,Dist,ReversedPath]),
print_list(More).
% =========
% dijsktra([CurCost-CurVertex|More],PathContainerIn,PathContainerOut).
%
% - The first argument is the "list of vertexes to be visited next", named
% "VisitThese" (i.e. the "boundary" of the search), sorted by the distance/cost
% of their path from the initial vertex, ascending (so we always need
% to just grab the head of "VisitThese" to find the vertex which is guaranteed
% nearest the start vertex on visit).
% - The second argument is the "container of the best-path-known-so-far to
% vertexes already seen (all of those visited or tentatively visited in
% handle_neighbors/7, for which a best-path-known-so-far could be determined)
% Note that a cheaper representation than keeping the full path would be
% to just keep the last edge of the path.
% The container is used to create a new container, accumulator-style, which
% is the third argument, which contains all the best paths to all the vertices
% at success-time.
% =========
dijsktra([],PathContainer,PathContainer) :- !.
dijsktra(VisitThese,PathContainerIn,PathContainerOut) :-
VisitThese = [CurCost-CurVertex|MoreToVisit],
format("VisitThese is currently: ~q ~n",[VisitThese]),
% bagof/3 fails if no neighbors, but that's only the case if the initial vertex is unconnected
bagof(LocalNeighbor-LocalCost,
sym_connection(CurVertex,LocalNeighbor,LocalCost),
Neighbors),
format("Neighbors of vertex ~q : ~q ~n",[CurVertex,Neighbors]),
get_assoc(CurVertex,PathContainerIn,CurPath),
% format("Found path for current vertex ~q: ~q~n",[CurVertex,CurPath]),
handle_neighbors(CurVertex,CurPath,CurCost,Neighbors,PathContainerIn,PathContainer2,VisitTheseToo),
append(VisitTheseToo,MoreToVisit,Dirty),
clean_visit_these_list(Dirty,Clean),
format("Dirty visitations : ~q, Clean visitations : ~q~n",[Dirty,Clean]),
dijsktra(Clean,PathContainer2,PathContainerOut).
% =========
% "Tentatively visit" all the neighbors of "CurVertex" ("CurVertex" can be reached through
% "CurPath" at cost "CurCost"), determining for each a path and overall cost
%
% We may reach a neighbor of "CurVertex":
%
% - for the first time if no path to it is stored in PathContainer yet.
% Then a new path is stored in PathContainer and the vertex is added to
% the list of vertexes-to-be-visted-next, "VisitThese" (which will have
% to be sorted by overall path cost before the recursive call to dijkstar/3)
% - for a not-the-first time if a path to it is stored in PathContainer yet
% (so the neighbor has been visited (and its old path is - by construction -
% cheaper and it shall not be visited again) or it has been only
% tentatively visited (and its old path *may* be costlier and thus demand
% replacement by the new path as well as addition of a cheaper
% entry in the list of vertexes to be visited next)
%
% Note that the list "Neighbors" also contains the neighboring vertex on
% the path "CurPath" through which "CurVertex" was reached in the first place.
% But there is no need to handle that case in a special way. This is just a
% vertex that has been visited previously and the old path is cheaper.
%
% handle_neighbors(+CurVertex,+CurPath,+CurCost,
% +Neighbors,
% +PathContainerIn,-PathContainerOut,
% -VisitThese)
%
% =========
% case of "all neighbor vertices handled, we are done"
handle_neighbors(_CurVertex,_CurPath,_CurCost,[],PathContainer,PathContainer,[]).
% case of "neighbor vertex already has a path but the new path is costlier
handle_neighbors(CurVertex,CurPath,CurCost,Neighbors,PathContainerIn,PathContainerOut,VisitThese) :-
Neighbors=[Vertex-LocalCost|More], % grab the next neighbor
get_assoc(Vertex,PathContainerIn,[Vertex-BestCostSoFar|_]), % grab its known path, if it exists (fails if not)
NewCost is CurCost+LocalCost,
NewCost >= BestCostSoFar, % the new path is costlier
!, % do nothing, move to next neighbor
handle_neighbors(CurVertex,CurPath,CurCost,More,PathContainerIn,PathContainerOut,VisitThese).
% case of "neighbor vertex already has a path but the new path is cheaper; note that it is added to the "VisitThese" list
handle_neighbors(CurVertex,CurPath,CurCost,Neighbors,PathContainerIn,PathContainerOut,[NewCost-Vertex|VisitThese]) :-
Neighbors=[Vertex-LocalCost|More], % grab the next neighbor
get_assoc(Vertex,PathContainerIn,[Vertex-BestCostSoFar|_]), % grab its known path, if it exists (fails if not)
NewCost is CurCost+LocalCost,
NewCost < BestCostSoFar, % new path is cheaper
!, % replace path, retain neighbor as "to be visited"
put_assoc(Vertex,PathContainerIn,[Vertex-NewCost|CurPath],PathContainer2),
handle_neighbors(CurVertex,CurPath,CurCost,More,PathContainer2,PathContainerOut,VisitThese).
% case of "neighbor vertex has no path yet"; note that it is added to the "VisitThese" list
handle_neighbors(CurVertex,CurPath,CurCost,Neighbors,PathContainerIn,PathContainerOut,[NewCost-Vertex|VisitThese]) :-
Neighbors=[Vertex-LocalCost|More], % grab the next neighbor
\+ get_assoc(Vertex,PathContainerIn,_), % vertex has no path yet
!, % the cut is not really needed
NewCost is CurCost+LocalCost,
put_assoc(Vertex,PathContainerIn,[Vertex-NewCost|CurPath],PathContainer2),
handle_neighbors(CurVertex,CurPath,CurCost,More,PathContainer2,PathContainerOut,VisitThese).
% ---
% Transfrom list of elements "Cost-Vertex"
%
% ... which is the list of vertexes and their "best cost yet" that
% shall be expanded by the Dijkstra algorithm, but in which several
% entries for the same "Vertex" may appear
% ... into a new list where there is always only exactly one entry for
% any "Vertex" appaearing in the original list, and the "Cost"
% associated to it is the minimum cost for that "Vertex".
% ---
clean_visit_these_list(Dirty,SortedByCostAsc) :-
predsort(sort_pairs,Dirty,SortedByVertexFirstCostSecond),
keep_best(SortedByVertexFirstCostSecond,DuplicatesRemovedMinCostRetained),
keysort(DuplicatesRemovedMinCostRetained,SortedByCostAsc).
sort_pairs(Order,Cost1-Vertex,Cost2-Vertex) :-
!, % Same vertex - order depends on associated cost
compare(Order,Cost1,Cost2).
sort_pairs(Order,_-Vertex1,_-Vertex2) :-
Vertex1 \== Vertex2,
!, % Different vertex - Sort lexicographically (cut is not needed actually)
compare(Order,Vertex1,Vertex2).
keep_best([Cost-Vertex,_-Vertex|More],Out) :-
!, % Vertex appears twice - retain only first entry (with smallest cost)
keep_best([Cost-Vertex|More],Out).
keep_best([Cost1-Vertex1,Cost2-Vertex2|More],[Cost1-Vertex1|Out]) :-
Vertex1 \== Vertex2,
!, % Different vertex - Retain first vertex and its cost, move on
keep_best([Cost2-Vertex2|More],Out).
keep_best([X],[X]). % Termination case #1
keep_best([],[]). % Termination case #2
% ---
% Test clean_visit_these_list/2
% ---
:- begin_tests(clean_visit_these_list).
test(1,true(Result == [])) :- clean_visit_these_list([],Result).
test(2,true(Result == [3-b])) :- clean_visit_these_list([3-b],Result).
test(2,true(Result == [3-b])) :- clean_visit_these_list([4-b,3-b,5-b],Result).
test(3,true(Result == [3-b,8-v,9-a,10-w])) :- clean_visit_these_list([8-v,10-w,9-a,3-b],Result).
test(4,true(Result == [2-w,3-b,8-v,9-a])) :- clean_visit_these_list([8-v,10-w,9-a,4-w,3-b,2-w,12-v],Result).
:- end_tests(clean_visit_these_list).
The main idea of dijkstras algorithm is to create a boundary between visited and unvisited nodes. Everything happens in the boundary. The boundary are simply pairs of nodes and their distances from the start node. In each step the node from the unvistied set with the smallest total distance from the start is transfered to the vistied set. To do this all "connections" from any visited nodes to unvisited nodes are checked: distance to the vistited node + connection value is the total distance. This way you can get the minimal distance from start to goal. Please note that this (mostly deterministic) algorithm works with those two sets rather than a concrete path. However if you want to have a path you have to keep track of the source node for each node in the visited set as well.
Your code seems to have multiple issues and I found it way easier just to implement my own solution. One of the problems is that the start node appears in the unvistied list. You can use the following code as a reference to debug your code:
minmember([A],A).
minmember([(E,N,S)|T],R):-
minmember(T,(Etmp,Ntmp,Stmp)),
( N>Ntmp
-> R=(Etmp,Ntmp,Stmp)
; R=(E,N,S)
).
boundary(Visited, (Connected, Dist, Node_Name), Unvisited):-
member((Node_Name, Value_to, _),Visited),
are_connected(Node_Name, Connected, Dist0),
member(Connected,Unvisited),
Dist is Dist0+Value_to.
boundary2path(B,Sink,[(Sink,Dist)]):-
member((Sink,Dist,start),B),
!.
boundary2path(B,Sink,[(Sink,Dist)|R]):-
member((Sink,Dist,Source),B),
boundary2path(B,Source,R).
dijkstra(Boundary, _, Dest_Node, Path):-
Boundary = [(Dest_Node,_,_)|_],
!,
format('Hit the end :) \n'),
boundary2path(Boundary,Dest_Node,Path),
format('Path found = ~w\n', [Path]).
dijkstra(Visited, Unvisited, Dest_Node, Path) :-
findall(E, boundary(Visited, E, Unvisited), Connections),
format('Connections = ~w\n', [Connections]),
minmember(Connections,(Node_Name,Dist,Source)),
format('Choosen = ~w\n', [(Node_Name,Dist,Source)]),
append(U1,[Node_Name|U2],Unvisited),
append(U1,U2,New_Unvisited),
format('New_Unvisited = ~w\n', [New_Unvisited]),
format('Visited = ~w\n', [[(Node_Name,Dist,Source)|Visited]]),
dijkstra([(Node_Name,Dist,Source)|Visited], New_Unvisited, Dest_Node, Path).
Now the output for:
?- dijkstra([(h, 0, start)], [b, g, e, k, j, i, l, c, s, a, d, f], g, L).
Connections = [(f,3,h),(g,2,h),(b,1,h)]
Choosen = b,1,h
New_Unvisited = [g,e,k,j,i,l,c,s,a,d,f]
Visited = [(b,1,h),(h,0,start)]
Connections = [(d,5,b),(a,4,b),(s,3,b),(f,3,h),(g,2,h)]
Choosen = g,2,h
New_Unvisited = [e,k,j,i,l,c,s,a,d,f]
Visited = [(g,2,h),(b,1,h),(h,0,start)]
Hit the end :)
Path found = [(g,2),(h,0)]
L = [(g,2), (h,0)] ;
false.
For the goal j no unvisited nodes are left:
?- dijkstra([(h, 0, start)], [b, g, e, k, j, i, l, c, s, a, d, f], j, L).
...
Visited = [(i,12,l),(k,9,e),(l,8,c),(c,6,s),(d,5,b),(a,4,b),(e,4,g),(f,3,h),(s,3,b),(g,2,h),(b,1,h),(h,0,start)]
Connections = [(j,18,i),(j,13,k),(j,12,l)]
Choosen = j,12,l
New_Unvisited = []
Visited = [(j,12,l),(i,12,l),(k,9,e),(l,8,c),(c,6,s),(d,5,b),(a,4,b),(e,4,g),(f,3,h),(s,3,b),(g,2,h),(b,1,h),(h,0,start)]
Hit the end :)
Path found = [(j,12),(l,8),(c,6),(s,3),(b,1),(h,0)]
L = [(j,12), (l,8), (c,6), (s,3), (b,1), (h,0)] ;
false.
​For the last query I made a sketch for the border:

How to tell if an edge is on some path

Given an undirected graph G=V,E, 2 vertices: x, y and an edge e,
I would like to check if there is a path from x to y that contains the given edge e.
What I thought: Solve this by defining a network flow where x and y are source and sink and check if the flow in e is more than 0 it means that there is a path.
BUT there are 2 problems:
I don't know how to direct to edges
What would be the capacity of every edge?
So I'm guessing it's not the right approach... If someone can give an idea it'd be great.
One approach could be the following:
Remove the edge e = (u,v) from E(G).
If any x-y path contains e then the path will either be one of the two following forms: (1) x *-> u -> v *-> y or (2) x *-> v -> u *-> y where *-> means reachable. Use this fact to check if either of the following cases holds true
2.1. x is reachable from u and y is reachable from v.
2.2. x is reachable from v and y is reachable from u.
We can use BFS to find reachablility.

Efficient Union Find with Existential Quantifier?

Is there a classical algorithm to solve the following problem.
Assume the union find algorithm without existential quantifiers
has the following input:
x1 = y1 /\ .. /\ xn = yn
It will then build some datastructure u, so that I can check
u.root(x)==u.root(y), to decide whether x and y are in the same
subgraph.
The input can be characterized by the following grammar:
Input :== Var = Var | Input /\ Input
Assume now we also allow existential quantifiers:
Input :== Var = Var | Input /\ Input | exists Var Input
What union find algorithm could deal with such an input.
I am still assuming that the algorithm builds some datastructure
u, where I can check via u.root(x)==u.root(y) whether x and
y are in the same subgraph.
Additionally u.root(x) should throw an exception when used
with a bound variable. These variables should all have been
eliminated and not anymore part of datastructure. Means
the subgraph should have been accordingly reduced, without
changing the validity of the result.
Bye
Here is a sketch of an algorithm. It will traverse the AST, and
feed a special union find algorithm. First the traversal:
traverse((X = Y)) :- add_conn(X, Y).
traverse(exists(X,I)) :- push_var(X), traverse(I), pop_var_remove_conn(X).
traverse((A /\ B)) :- traverse(A), traverse(B).
The special union find algorithm works with a list. This list defines
the weight of the nodes, the head of list has weight 0, the second element
weight 1, etc... add_conn(X,Y) first computes X'=root(X) and Y'=root(Y). The
less weighted of X' and Y' is connected to the more weighted one.
push_var(X) adds X to the front of the list. Making it the less weighted
node. pop_var_remove_conn(X) removes X again from the list, and removes a
possibly established connection from X to some other node as well.

Prolog Shortest Path using list of lists

Okay, so I've been trying to teach myself Prolog recently, and am having a hard time wrapping my head around finding a "Shortest Path" between two (defined) elements in a list of lists. It may not be the most effective way of representing a Grid or finding a Shortest Path, but I'd like to try it this way.
For example:
[[x,x,x,x,x,x,x],
[x,1,o,o,o,o,x],
[x,-,-,-,o,-,x],
[x,-,-,o,o,-,x],
[x,o,o,o,o,2,x],
[x,o,-,-,o,o,x],
[x,x,x,x,x,x,x]]
A few assumptions I can make (either given or based on checking before path-finding):
The grid is square
Their will always exist a path from 1 to 2
'1' can pass through anything except '-' (walls) or 'x' (borders)
The goal is for '1' to find a shortest path to '2'.
In the instance of:
[[x,x,x,x,x,x,x],
[x,o,o,1,o,o,x],
[x,-,o,o,o,-,x],
[x,-,o,-,o,-,x],
[x,o,o,2,o,o,x],
[x,o,-,-,-,o,x],
[x,x,x,x,x,x,x]]
Notice, there are two "Shortest paths":
[d,l,d,d,r]
and
[d,r,d,d,l]
In Prolog, I'm trying to make the function (if that's the proper name):
shortestPath(Grid,Path)
I've made a function to find elements '1' and '2', and a function that verifies that the grid is valid, but I can't even begin how to start constructing a function to find a shortest path from '1' to '2'.
Given a defined Grid, I'd like the output of Path to be the shortest path. Or, given a defined Grid AND a defined Path, I'd like to check if it's indeed a shortest path.
Help would be much appreciated! If I missed anything, or was unclear, let me know!
not optimized solution
shortestPath(G, S) :-
findall(L-P, (findPath(G,P), length(P,L)), All),
keysort(All, [_-S|_]).
findPath(G, Path) :-
pos(G, (Rs,Cs), 1),
findPath(G, [(Rs,Cs)], [], Path).
findPath(G, [Act|Rest], Trail, Path) :-
move(Act,Next,Move),
pos(G, Next, Elem),
( Elem == 2
-> reverse([Move|Trail], Path)
; Elem == o
-> \+ memberchk(Next, Rest),
findPath(G, [Next,Act|Rest], [Move|Trail], Path)
).
move((R,C), (R1,C1), M) :-
R1 is R-1, C1 is C , M = u;
R1 is R , C1 is C-1, M = l;
R1 is R+1, C1 is C , M = d;
R1 is R , C1 is C+1, M = r.
pos(G, (R,C), E) :- nth1(R, G, Row), nth1(C, Row, E).
grid(1,
[[x,x,x,x,x,x,x],
[x,1,o,o,o,o,x],
[x,-,-,-,o,-,x],
[x,-,-,o,o,-,x],
[x,o,o,o,o,2,x],
[x,o,-,-,o,o,x],
[x,x,x,x,x,x,x]]).
grid(2,
[[x,x,x,x,x,x,x],
[x,o,o,1,o,o,x],
[x,-,o,o,o,-,x],
[x,-,o,-,o,-,x],
[x,o,o,2,o,o,x],
[x,o,-,-,-,o,x],
[x,x,x,x,x,x,x]]).

Resources