How to find a path with limited depth in graph? - prolog

link(a, b).
link(a, c).
link(b, c).
link(b, e).
link(c, f).
link(c, g).
link(c, d).
symlink(F1, F2) :-
link(F1, F2).
symlink(F1, F2) :-
link(F2, F1).
profile(a,box). %Tag it is the same as box for a.
find(Start, Tag, Rpath) :
find2(Start, Tag, 0, [], Rpath).
find2(Step, Tag, Count, Path, Rpath) :-
C is Count +1,
C < 5,
symlink(Step, A),
compat(A,Tag), % Compatible means the distance between the tag of A
% and the Tag that is given as argument should be maximum 1.
append(Path, [A|E],Rpath), %This part i want make my final path in Rpath.
not(member(Step,Path)),
find2(A, Tag, C, [Step|Path], Rpath).

You are quite close to a working predicate here. I've include a code snippet that hopefully solves a few of the small mistakes you make. Notice that find/3 is the predicate you would actually use (from the outside), a so-called wrapper clause.
find/4 works in the following way:
The first clause is only used to detect a transgression of the maximum depth.
The second clause is only used to detect a goal node, i.e. one that matches the given tag.
The third clause does the real job of finding a symmetric link in the graph.
Some small things to note:
Renamed symlink/2 to symmetric_link/2 to avoid confusion with symbolic links.
Used \+ instead of not for negation (the former is more common I believe).
Used tag/2 for tagging nodes, not profile/2 to avoid confusion with the act of profiling/analyzing code performance.
Code snippet:
link(a, b).
link(a, c).
link(b, c).
link(b, d).
link(b, e).
link(c, f).
link(c, g).
link(c, d).
tag(a, box).
symmetric_link(F1, F2) :-
link(F1, F2).
symmetric_link(F1, F2) :-
link(F2, F1).
maximum_depth(5).
find(Start, End, Path):-
find(Start, End, 0, [Start], Path).
find(_, _, Depth, _, _):-
maximum_depth(Max),
Depth > Max, !,
fail.
find(Node, Tag, _, _, [Node]):-
tag(Node, Tag), !.
find(Node1, Tag, Depth1, History, [Node1|Path]):-
symmetric_link(Node1, Node2),
\+ memberchk(Node2, History),
Depth2 is Depth1 + 1,
find(Node2, Tag, Depth2, [Node2|History], Path).
Example of usage:
?- find(g, box, Path).
Path = [g, c, d, b, a] ;
Path = [g, c, a] ;
Path = [g, c, b, a].
I have not fully tested this predicate and would advice you to write a unit test for these kinds of predicates. I use plUnit for this, which runs on SWI-Prolog and SICStus Prolog, but there may be others as well.
Hope this helps!

Related

Calling facts in query

This is my code:
sentence([['o'],['m','e','n','i','n','o'],['a','l','e','g','r','e']]).
lastWord(X,[X]).
lastWord(X,[_|Z]) :- lastWord(X,Z).
If I try lastWord(X,[1,2,3]). or even lastWord(X,[['o'],['m','e','n','i','n','o'],['a','l','e','g','r','e']]). I get what I want (which is, of course, the last element of the list (in the examples, 3 and ['a','l','e','g','r','e'].
But if I try lastWord(X, sentence). or lastWord(X, sentence(Y)). I get false.
How can I "call" the defined list (in this case, 'sentence') in lastWord?
Prolog is not a functional language. Thus, in goals such as lastWord(X, sentence) or lastWord(X, sentence(Y)) is not going to replace sentence or sentence(Y) by the argument of the sentence/1 predicate. Try instead:
?- sentence(List), lastWord(Last, List).
List = [[o], [m, e, n, i, n, o], [a, l, e, g, r, e]],
Last = [a, l, e, g, r, e] ;
false.
Note there's a spurious choice point left for the query. You can eliminate it by rewriting the definition of the predicate lastWord/2. A similar predicate, usually named last/2, is often available from libraries:
last([Head| Tail], Last) :-
last(Tail, Head, Last).
last([], Last, Last).
last([Head| Tail], _, Last) :-
last(Tail, Head, Last).
Note the different argument order (Prolog coding guidelines suggest to have input arguments preceding output arguments). Using this predicate instead:
?- sentence(List), last(List, Last).
List = [[o], [m, e, n, i, n, o], [a, l, e, g, r, e]],
Last = [a, l, e, g, r, e].

Program doesnt work. Prolog

I have some problem whith this code. The func3 was never invoked :
technology(board, saw, table).
technology(wood, sanded, board).
technology(water, grow, tree).
material(table, board, 20).
material(table, tree, 5).
material(wood, water, 100).
equipment(table,saw, cut, 10).
equipment(board, plane, polish, 7).
equipment(tree, watering, growing, 100).
specialization(saw, wood).
specialization(plane, wood).
specialization(watering, forestry).
plan_vypusku(table,10).
potreba_u_zahotovkah1(M, V):-
write(M + V),
nl,
    technology(F, _, M),
material(M, F, C),
Z is V * C,
write(F - Z),
nl.
func3([A, B], C):-
write("InF3"),
nl,
potreba_u_zahotovkah1(A, C),
func3(B, C).
func2([A, B], C):-
write("InF2"),
nl,
findall(M, equipment(M, A, _, _), ML),
write(ML),
nl,
func3(ML, C),
func2(B, C).
potreba_u_zahotovkah(C, G):-
findall(X, specialization(X, C), XL),
write(XL),
nl,
plan_vypusku(G, S),
func2(XL, S).
Result:
?- potreba_u_zahotovkah(wood,table).
[saw,plane]
InF2
[table]
false.
Help PLS!
I don't know what you're up to, but I have an explanation of the unexpected failure you observed.
The query you made wrote the following lines by side-effect (write/1 and nl/0) and then failed:
?- potreba_u_zahotovkah(wood,table).
[saw,plane]
InF2
[table]
false.
The highlighted line was output by the following highlighted write/1 and nl/0:
func2([A, B], C):-
write("InF2"),
nl,
findall(M, equipment(M, A, _, _), ML),
write(ML),
nl,
func3(ML, C),
func2(B, C).
So above variable ML was bound to [table] when the goal func3(ML, C) was called.
Looking at your definition of func3/2 the reason for failure becomes apparent:
func3([A, B], C):-
write("InF3"),
nl,
potreba_u_zahotovkah1(A, C),
func3(B, C).
The clause head of func3/2 demands that the first argument is a list having exactly two elements. The list [table], however, has exactly one element, not two!
As no more choicepoint are open, the goal potreba_u_zahotovkah(wood,table) fails.

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.

Graph path define issue

The solution
ppath(X,Y,M,Path,[Y|Path]) :- edge(X,Y,M),\+ memberchk(Y,Path).
path(X,Y,P,SoFar,Path) :- edge(X,W,M), \+ memberchk(W,SoFar),
path(W,Y,N,[W|SoFar],Path), P is M+N.
pravilo(X,Y,Z) :-
aggregate(min(W), P^path(X,Y,W,[],P),Z).
Here is the code i have. The question is that starting point is a, and ending point is z.
There is an error after execution, the result is displayed like [z, c, h, b]. But the correct answer should [a,b,c,z].
Please help to fix my problem.
library(aggregate) allows for a witness on min/max scalar operations. We can use that feature to report back the path as well as the travel length:
path(X,Y,M,Path,FullPath) :-
edge(X,Y,M), \+ memberchk(Y,Path),
reverse([Y|Path], FullPath).
path(X,Y,P,SoFar,Path) :-
edge(X,W,M), \+ memberchk(W,SoFar),
path(W,Y,N,[W|SoFar],Path), P is M+N.
pravilo(X,Y,Z,Path) :-
aggregate(min(W,P), P^path(X,Y,W,[X],P), min(Z,Path)).
Note there is a typo in edge/3: edge(b,e,16 should be edge(b,e,16)..
Once corrected the DB, I get
pravilo(a,z,M,P).
M = 16,
P = [a, b, h, c, z].

advice on commutative and transitive equivalence implementation‏ in Prolog

I'd like to simulate the equivalence in Prolog with the properties of being commutative and transitive, here is what I did: equal/2 will be supplying as facts.
symmetricEqual(A,B):- equal(A,B).
symmetricEqual(A,B):- equal(B,A).
transitiveEqualPath(A,B,_) :- symmetricEqual(A,B).
transitiveEqualPath(B,C,IntermediateNodes) :-
symmetricEqual(A,B),
\+ member(C,IntermediateNodes),
transitiveEqualPath(A,C,[B|IntermediateNodes]), B\==C.
transitiveEqual(A,B) :- transitiveEqualPath(A,B,[]).
But I am running into performance issues with the above solution to try to compute transitiveEqual/2 (it has taken roughly 20mins), I have around 2K symmetricalEqual/2 facts computed pretty fast from equal/2, so it must be the cause of rules for transitiveEqual/2, anybody can suggest any improvement on this?
Thanks very much.
Courtesy of the approach from here:
symmetricEquals(X,Y) :- equal(X,Y).
symmetricEquals(X,Y) :- equal(Y,X).
transitiveEqual(A, B) :-
% look for an equality path from A to B
path(A, B, _Path).
path(A, B, Path) :-
% build a path from A to B
path(A, B, [A], Path).
path(A, B, _Acc, [B]) :-
symmetricEquals(A, B).
path(A, B, Visited, [C|Path]) :-
symmetricEquals(A, C),
C \== B,
\+ memberchk(C, Visited),
path(C, B, [C|Visited], Path).
Note that path/3,4 will backtrack to enumerate all possible paths between any ground or variable A to B. This could be quite expensive if the graph implied by your equal/2 facts is large, contains many disconnected components, and/or you're looking for all combinations.

Resources