Prolog - Path finding and length given Relation - prolog

I just began learning Prolog and I wanted to understand Pathfinding better. I have a few examples of relationships, however, I don't know how to find the path and length of a relationships when the relationships are cyclical. I've been trying to create a list that documents visited nodes, but I keep receiving errors.
Below are a few examples as well as my attempt to find path given the relationship, source, target, pathlist, and length):
is_a(parallelogram, quadrilateral).
is_a(trapezoid, quadrilateral).
is_a(rhombus, parallelogram).
is_a(rectangle, parallelogram).
is_a(square, rhombus).
is_a(square, rectangle).
edge(a, b).
edge(b, c).
edge(c, d).
edge(d, a).
friend(alice, bob).
friend(bob, carol).
friend(carol, daniel).
friend(carol, eve).
friends(A,B) :-
friend(A,B);
friend(B,A).
transit(Rel, S, T) :-
call(Rel, S, X),
(X = T; transit(Rel, X, T)).
path_(Rel,Source,Target,Path,Len) :-
path_(Rel,Source,Target,Path,Len,[]).
path_(Rel,Source,Target,Path,Len,Visited) :-
transit(Rel,Source,Target),
transit(Rel,Source,Mid),
Mid == Target, !,
append(Visited,[Source,Target],Path),
length(Path,L),
Len is L+1.
path_(Rel,Source,Target,Path,Len,Visited) :-
transit(Rel,Source,Target),
transit(Rel,Source,Mid),
not(member(Mid,Visited)),
path_(Rel,Mid,Target,Path,Len,[Source|Visited]).
The above is my second attempt, but I receive errors on everything. My first attempt only worked with non-cyclical paths, such as for the is_a relationships, which is noted below:
path0(Rel,From,To,[From,To],2) :-
transit(Rel,From,To),
call(Rel, From, To).
path0(Rel,From,To,[From|Xs],Len) :-
transit(Rel,From,X),
call(Rel,From,X),
path0(Rel,X,To,Xs,_),
length(Xs, L),
Len is L+1.

Related

Prevent cycle in Depth first search using prolog

Is there any way to prevent cycle in this code.
move(a,b).
move(b,a).
move(a,c).
move(b,d).
move(b,e).
move(d,h).
move(d,i).
move(e,j).
move(e,k).
move(c,f).
move(c,g).
move(f,l).
move(f,m).
move(g,n).
move(g,o).
goal(n).
goSolveTheMaze(Start,Way) :-
dfs(Start, Way),!.
dfs(Goal, [Goal]) :-
goal(Goal),!.
dfs(Start, [Start|Way]) :-
move(Start, N),
dfs(N, Way).
so when move(a,b) move to (b,c) dont go back to (b,a),
when run goSolveTheMaze(a,path).
The output should be path=[a,c,g,n].
What if you add a third argument to dfs which is a list of where you've already visited? You could then use \+/1 and member/2 to avoid returning to places you've already been.
For example, if you use the following:
move(a,b).
move(b,a).
move(a,c).
move(b,d).
move(b,e).
move(d,h).
move(d,i).
move(e,j).
move(e,k).
move(c,f).
move(c,g).
move(f,l).
move(f,m).
move(g,n).
move(g,o).
goal(n).
goSolveTheMaze(Start,Way) :-
dfs(Start, Way, [Start]),!.
dfs(Goal, [Goal], _) :-
goal(Goal),!.
dfs(Start, [Start|Way], Visited) :-
move(Start, N),
\+ member(N, Visited),
dfs(N, Way, [N|Visited]).
Then the query:
?- goSolveTheMaze(a, X).
Will produce the result:
X = [a, c, g, n]
Update in response to comment "can you tell me what \+ do?":
The \+ predicate is true when its argument cannot be proven. Therefore in the above example the line \+ member(N, Visited) means "when N is not a member of the list Visited".
See: https://www.swi-prolog.org/pldoc/man?predicate=%5C%2B/1

How do I see a detailed order (execution) for a Prolog query?

Let's say I have this Prolog program:
loves(vincent, mia).
loves(marcellus, mia).
jealous(A, B) :- loves(A, C), loves(B, C).
With query jealous(A,B). I'm very new to Prolog and I'd like to know how is it possible to see the exact order the program will be running and taking its ways for this query? I have tried using trace, jealous(A,B). command but it has only given me that:
Isn't there any more detailed solution for that? :/
Have you seen the Prolog Visualizer?
When you get to the page be sure to click on the icons in the upper right to learn more.
Enjoy.
Screenshot after step 10 of 49.
Screenshot for example given after all steps.
The Prolog Visualizer uses a slightly nonstandard way to enter a query by ending the query with a question mark (?), e.g.
jealous(A,B)?
If you do not post a query in the input area on the left you will receive an error, e.g.
The input for the Prolog Visualizer for your example is
loves(vincent, mia).
loves(marcellus, mia).
jealous(A, B) :- loves(A, C), loves(B, C).
jealous(A,B)?
When the Prolog Visualizer completes your example, notice the four results in green on the right
If you are using SWI-Prolog and after you understand syntactic unification, backtracking and write more advanced code you will find this of use:
Overview of the SWI Prolog Graphical Debugger
For other useful Prolog references see: Useful Prolog references
If the Prolog system has callable_property/2 and sys_rule/3, then one can code
a smart "unify" port as follows, showing most general unifiers (mgu's`):
:- op(1200, fx, ?-).
% solve(+Goal, +Assoc, +Integer, -Assoc)
solve(true, L, _, L) :- !.
solve((A, B), L, P, R) :- !, solve(A, L, P, H), solve(B, H, P, R).
solve(H, L, P, R) :- functor(H, F, A), sys_rule(F/A, J, B),
callable_property(J, sys_variable_names(N)),
number_codes(P, U), atom_codes(V, [0'_|U]), shift(N, V, W),
append(L, W, M), H = J, reverse(M, Z), triage(M, Z, I, K),
offset(P), write_term(I, [variable_names(Z)]), nl,
O is P+1, solve(B, K, O, R).
% triage(+Assoc, +Assoc, -Assoc, -Assoc)
triage([V=T|L], M, R, [V=T|S]) :- var(T), once((member(W=U, M), U==T)), W==V, !,
triage(L, M, R, S).
triage([V=T|L], M, [V=T|R], S) :-
triage(L, M, R, S).
triage([], _, [], []).
% shift(+Assoc, +Atom, -Assoc)
shift([V=T|L], N, [W=T|R]) :-
atom_concat(V, N, W),
shift(L, N, R).
shift([], _, []).
% offset(+Integer)
offset(1) :- !.
offset(N) :- write('\t'), M is N-1, offset(M).
% ?- Goal
(?- G) :-
callable_property(G, sys_variable_names(N)),
shift(N, '_0', M),
solve(G, M, 1, _).
Its not necessary to modify mgu's retrospectively, since a solution to a
Prolog query is the sequential composition of mgu's. Here is an example run:
?- ?- jealous(A,B).
[A_0 = X_1, B_0 = Y_1]
[H_1 = mia, X_1 = vincent]
[Y_1 = vincent]
A = vincent,
B = vincent ;
[Y_1 = marcellus]
A = vincent,
B = marcellus ;
Etc..
This is a preview of Jekejeke Prolog 1.5.0 the new
predicate sys_rule/3, its inspired by the new
predicate rule/2 of SWI-Prolog, but keeps the
clause/2 argument of head and body and uses a predicate
indicator.

Finding the path length of an Acyclic Graph in Prolog

Okay, so I have the graph:
and I want to be able to create a rule to find all the paths from X to Y and their lengths (number of edges). For
example, another path from a to e exists via d, f, and g. Its length is 4.
So far my code looks like this:
edge(a,b).
edge(b,e).
edge(a,c).
edge(c,d).
edge(e,d).
edge(d,f).
edge(d,g).
path(X, Y):-
edge(X, Y).
path(X, Y):-
edge(X, Z),
path(Z, Y).
I am a bit unsure how I should approach this. I've entered a lot of rules in that don't work and I am now confused. So, I thought I would bring it back to the basics and see what you guys could come up with. I would like to know why you done what you done also if that's possible. Thank you in advance.
This situation has been asked several times already. Firstly, your edge/2 predicates are incomplete, missing edges like edge(c,d), edge(f,g), or edge(g,e).
Secondly, you need to store the list of already visited nodes to avoid creating loops.
Then, when visiting a new node, you must check that this new node is not yet visited, in the Path variable. However, Path is not yet instanciated. So you need a delayed predicate to check looping (all_dif/1). Here is a simplified version using the lazy implementation by https://stackoverflow.com/users/4609915/repeat.
go(X, Y) :-
path(X, Y, Path),
length(Path, N),
write(Path), write(' '), write(N), nl.
path(X, Y, [X, Y]):-
edge(X, Y).
path(X, Y, [X | Path]):-
all_dif(Path),
edge(X, Z),
path(Z, Y, Path).
%https://stackoverflow.com/questions/30328433/definition-of-a-path-trail-walk
%which uses a dynamic predicate for defining path
%Here is the lazy implementation of loop-checking
all_dif(Xs) :- % enforce pairwise term inequality
freeze(Xs, all_dif_aux(Xs,[])). % (may be delayed)
all_dif_aux([], _).
all_dif_aux([E|Es], Vs) :-
maplist(dif(E), Vs), % is never delayed
freeze(Es, all_dif_aux(Es,[E|Vs])). % (may be delayed)
Here are some executions with comments
?- go(a,e).
[a,b,e] 3 %%% three nodes: length=2
true ;
[a,c,d,f,g,e] 6
true ;
[a,c,f,g,e] 5
true ;
[a,d,f,g,e] 5
true ;
false. %%% no more solutions
Is this a reply to your question ?

Memorising (and caching) solutions found in a Prolog query?

In this question on StackExchange I've asked (and it has been solved) about a Prolog program I have been trying to create. But while it works in principle, it doesn't scale to my real world need.
Before I start learning yet another language (Datalog), I'd like to try my already done work and know how I can implement in Prolog a way to memorise results from earlier queries such that the same query is only executed once. So, I'm looking for a way to add the result of a successful query to a List and if that same query is asked again, it doesn't redo the calculation, but uses the remembered result.
My main problem is that I cannot find a way to keep the result of a successful query in a list that is passed 'up the chain'.
In
% get this out of the way, quickly
isARelation( Source, Relation, Target, _) :-
isADirectRelation( Source, Relation, Target).
% Structural Chains
isARelation( Source, Relation, Target, Visited) :-
\+ member( [Source,Relation,Target], Visited),
structuralOrDependencyRelation( RelationOne),
structuralOrDependencyRelation( RelationTwo),
weakest( Relation, RelationOne, RelationTwo),
isADirectRelation( Source, RelationOne, Intermediate),
isARelation( Intermediate, RelationTwo, Target, [[Source,RelationOne,Intermediate]|Visited]).
isARelation( Source, Relation, Target, Visited) :-
\+ member( [Source,Relation,Target], Visited),
structuralOrDependencyRelation( RelationOne),
structuralOrDependencyRelation( RelationTwo),
weakest( Relation, RelationOne, RelationTwo),
isADirectRelation( Source, RelationTwo, Intermediate),
isARelation( Intermediate, RelationOne, Target, [[Source,RelationTwo,Intermediate]|Visited]).
How do I implement that the first call
isARelation(A, B, C, []).
does the calculation of the results, and a second call
isARelation(A, B, C, []).
uses the earlier found result, which is kept 'globally'?
This is not really an answer to your question :(
The other answer has the right idea, but the implementation has a problem. Let's say we want to make a memoized version of squaring a number:
:- dynamic mem_square/2.
square(N, S) :-
( mem_square(N, S)
; S is N*N,
assertz(mem_square(N, S))
).
BTW, the parentheses in the other answer are completely unnecessary. These are also unnecessary, but this is how you usually wrap a disjunction, just in case it is part of a conjunction. In other words, this: a ; (b, c) is the same as a ; b, c, but (a ; b), c is not the same.
Now, if I load this program from the toplevel and query:
?- square(3, S).
S = 9. % first time it's fine
?- square(3, S).
S = 9 ;
S = 9. % now there's two
?- square(3, S).
S = 9 ;
S = 9 ;
S = 9. % now three
If you keep on querying a memoized fact, and you backtrack into it, you will keep on computing again and again and adding more and more identical copies of it. Instead, you can try for example this:
:- dynamic mem_square/2.
square(N, S) :-
( mem_square(N, S)
-> true
; S is N*N,
assertz(mem_square(N, S))
).
Now, there is no choice point.
This is still not a clean implementation if you are meant to have choice multiple solutions. Any solutions after the first will be cut by the ->.
This is advice on how to generically do what tabling does. I haven't followed this advice in ages myself so there may be inaccuracies here. Hopefully the rest of the gang will show up and correct me if I'm off-base.
You have a predicate foo/4 that is inefficient.
Add this to your file:
:- dynamic(cached_foo/4).
Rename foo/4 to compute_foo/4 or something.
Make a new predicate foo/4 that looks like this:
foo(X, Y, Z, Q) :-
cached_foo(X, Y, Z, Q) ;
(
compute_foo(X, Y, Z, Q),
assertz(cached_foo(X, Y, Z, Q))
).

Fastest way between two cities

I need to find the fastest way to travel from one city to another. I have something like
way(madrid, barcelona, 4).
way(barcelona, paris, 5).
way(madrid, londres, 3).
way(londres,paris,1).
I have come up with a predicate shortway(A,B,C,D) where C is the list of towns between A and B and D the distance.
so I have
shortway(A,B,C,D):-
way(A,B,_,_) , (A,_,C,D). D<C.
shortway(A,_,C).
I trying my best but I really cant get it to work!
You have a bunch of problems with your code! First of all, way/3 has arity 3, not 4, so calling way(A,B,_,_,) is clearly not going to do what you think. Second, I have no idea what you're trying to do with (A,_,C,D). The period after this signifies the end of the predicate! So the next line, D<C. is just a free-floating query that cannot be fulfilled. And then shortway(A,_,C) is basically a fact, with three singletons, but it would define a shortway/3 clause when the previous one is a shortway/4 clause.
There really isn't enough that's on the right track here to try and recover. It looks here like you are extremely confused about even the basics of Prolog. I would strongly encourage you to go back to the beginning and start over. You can't rush Prolog! And this code looks like you're trying to make a combustion engine by banging rocks together.
I wrote some line of code to help you, but as https://stackoverflow.com/users/812818/daniel-lyons said, it's better than you learn something easier before.
To solve your problem I advice you to read, at least, the first 3 chapters of this book: http://www.learnprolognow.org/lpnpage.php?pageid=online and do the practical session at paragraph 3.4.
Then, you could take a look at my code (you can find some explenation of it here:Out of local stack error in Prolog route planner .
Here the code
way(madrid, barcelona, 4).
way(barcelona, paris, 5).
way(madrid, londres, 3).
way(londres,paris,1).
shortway(From, To):- findall(Journey, travel(From, To, Journey, _) , Travels_list),
findall(Total_distance, travel(From, To, _, Total_distance) , Distances_list),
min_member(Y, Distances_list), find_minimum_index(Y, Distance_list, 1, Distance_index),
find_journey(Distance_index, Travels_list, 0, Shortest_path),
format('The shortest path is ~w', [Shortest_path]).
travel(From, To, Journey, Total_distance) :- dif(From, To),
AccDistance is 0,
path(From, To, [From], Journey, AccDistance, Total_distance).
path(From, To, Passed_cities, go(From, To), AccDistance, Total_distance) :- way(From, To, Way_distance),
Total_distance is AccDistance + Way_distance.
path(From, To, Passed_cities, go(From, Intermediate, GO), AccDistance, Total_distance) :- way(From, Intermediate, Way_distance),
dif(Intermediate, To),
\+ member(Intermediate, Passed_cities),
NewAccDistance is AccDistance + Way_distance,
path(Intermediate, To, [Intermediate|Passed_cities], GO, NewAccDistance, Total_distance).
min_member(Min, [H|T]) :- min_member_(T, H, Min).
min_member_([], Min, Min).
min_member_([H|T], Min0, Min) :-
( H >= Min0
-> min_member_(T, Min0, Min)
; min_member_(T, H, Min)
).
find_minimum_index(X, [], N, I) :- fail.
find_minimum_index(X, [X|T], N, I) :- I is N, !.
find_minimum_index(X, [H|T], N, I) :- H \= X, increment(N, N1), find_minimum_index(X, T, N1, I).
find_journey(I, [H|T], N, Elemento) :- N = I, Elemento = H, !.
find_journey(I, [H|T], N, Elemento) :- N \= I, increment(N, N1), find_journey(I, T, N1, Elemento).
increment(X, X1) :- X1 is X+1.
Then you call, for example
?:- shortway(madrid,paris).
and it will return
"The shortest path is go(madrid, londres, go(londres,paris))"
which distance is, 4
rather than
go(madrid, barcelona, go(barcelona, madrid)
which distance is 9.
Summing up: calling shortway/2, with the predicates findall/3 you'll find the lists of all possible pathes and their relative distances, respectively, then you'll browse the list of the distances to find the index of the minimum element and so using it to find the shortest path from the list of all pathes found previously.

Resources