Prolog - questions related to graph traversal - prolog

I'm fairly new to prolog and logic programming in general and took up this example to learn but I'm having a hard time with the solution. So, if someone can help me out or point me in the right direction, that would be really great.
The following is a list of predicates flight(city-a,city-b,airline,distance,time,cost) and country(city,state)
flight(london,dublin,aerlingus,500,45,150).
flight(rome,london,ba,1500,150,400).
flight(rome,paris,airfrance,1200,120,500).
flight(paris,dublin,airfrance,600,60,200).
flight(berlin,moscow,lufthansa,3000,300,900).
flight(paris,amsterdam,airfrance,400,30,100).
flight(berlin,dublin,lufthansa,1200,120,900).
flight(london,newyork,ba,5000,700,1100).
flight(dublin,newyork,aerlingus,4500,360,800).
flight(dublin,cork,ryanair,300,50,50).
flight(dublin,rome,ryanair,2000,150,70).
flight(dublin,chicago,aerlingus,5500,480,890).
flight(amsterdam,hongkong,klm,7000,660,750).
flight(london,hongkong,ba,7500,700,1000).
flight(dublin,amsterdam,ryanair,1000,90,60).
flight(moscow,newyork,aerflot,9000,720,1000).
flight(moscow,hongkong,aerflot,5500,420,500).
flight(newyork,chicago,aa,3000,240,430).
flight(dublin,london,aerlingus,500,45,150).
flight(london,rome,ba,1500,150,400).
flight(paris,rome,airfrance,1200,120,500).
flight(dublin,paris,airfrance,600,60,200).
flight(moscow,berlin,lufthansa,3000,300,900).
flight(amsterdam,paris,airfrance,400,30,100).
flight(dublin,berlin,lufthansa,1200,120,900).
flight(newyork,london,ba,5000,700,1100).
flight(newyork,dublin,aerlingus,4500,360,800).
flight(cork,dublin,ryanair,300,50,50).
flight(rome,dublin,ryanair,2000,150,70).
flight(chicago,dublin,aerlingus,5500,480,890).
flight(hongkong,amsterdam,klm,7000,660,750).
flight(hongkong,london,ba,7500,700,1000).
flight(amsterdam,dublin,ryanair,1000,90,60).
flight(newyork,moscow,aerflot,9000,720,1000).
flight(hongkong,moscow,aerflot,5500,420,500).
flight(chicago,newyork,aa,3000,240,430).
flight(dublin,sao_paulo,airfrance,10000,900,800).
flight(sao_paulo,newyork,airfrance,7000,840,650).
flight(rio,berlin,lufthansa,11000,1200,1100).
country(dublin,ireland).
country(cork,ireland).
country(london,uk).
country(rome,italy).
country(moscow,russia).
country(hongkong,china).
country(amsterdam,holland).
country(berlin,germany).
country(paris,france).
country(newyork,usa).
country(chicago,usa).
country(sao_paulo,brazil).
country(rio,brazil).
I need to design the following predicates -
1) trip(X,Y,T) where X and Y are two cities and T is the list of all cities connecting X and Y. eg. trip(rome,dublin,T) = [rome,london,dublin].
2) all_trip(X,Y,T) where X and Y are two cities and T is the list all possible solutions of predicate 1.
3) trip_dist(X,Y,[T,D]) where X and Y are two cities and W is a list [T|D] where the first element T is the trip from predicate 1 and D is the distance.
4) trip_cost(X,Y,[T,C]) same as predicate 3 but C is the total cost of the trip.

Related

How to maximize the goal in prolog?

I am trying to solve the knapsack problem in prolog. Following is my implementation.
% 'ks' is compound term which has 4 argumets
% 1 - List of items to be chosen from.
% 2 - Maximum weight a knapsack can carry.
% 3 - Selected items which sum of weights is less than or equal to knapsack capacity.
% 4 - The gain after choosing the selected item.
% base conditions where input list contains only one items and
% it is either selected or excluded.
ks([item(W1, V1)], W, [item(W1, V1)], V1):- W1 =< W.
ks([item(W1, _)], W, [], 0):- W1 > W.
% An item from the input list is chosen in the knapsack.
% In that case, we recurse with smaller list with reduced weight constraint.
ks(ItemList, MaxWeight, SelectItems, Gain) :-
append(Prefix, [item(W1, V1)|Suffix], ItemList),
append(Prefix, Suffix, RemList),
NewWeight is MaxWeight - W1,
W1 =< MaxWeight,
append([item(W1, V1)], SelectItems1, SelectItems),
ks(RemList, NewWeight, SelectItems1, Gain1),
Gain is V1 + Gain1.
% An item from the input list is not chosen in the knapsack.
% In that case, we recurse with smaller list but with the same weight constraint.
ks(ItemList, MaxWeight, SelectItems, Gain) :-
append([P1|Prefix], [item(W1, V1)|Suffix], ItemList),
append([P1|Prefix], Suffix, RemList),
not(member(item(W1, V1), SelectItems)),
ks(RemList, MaxWeight, SelectItems, Gain).
The input to the program will be list of items as below. in term item(W, V) W is weight of the item while V is value of the item. Goal to maximize the value for the given weight constraint.
ks([item(2,3), item(3,4), item(4,5), item(5,8), item(9,10)], 20, List, Gain).
List = [item(2, 3), item(3, 4), item(4, 5), item(5, 8)],
Gain = 20 ;
While I am able to generate all the combinations of items with above program, I am not able to code to find out the maximum gain only.
Could any one please point me the right direction?
Thanks.
I think that to find reusable abstractions it's an important point of studying programming. If we have a subset_set/2 that yields on backtracking all subsets, ks/4 becomes really simple:
subset_set([], _).
subset_set([H|T], Set) :-
append(_, [H|Rest], Set),
subset_set(T, Rest).
ks(Set, Limit, Choice, Gain) :-
subset_set(Choice, Set),
aggregate((sum(W), sum(G)), member(item(W, G), Choice), (TotWeight, Gain)),
TotWeight =< Limit.
and then
ks_max(Items, Limit, Sel, WMax) :-
aggregate(max(W,I), ks(Items,Limit,I,W), max(WMax,Sel)).
despite its simplicity, subset_set/2 is not really easy to code, and library available alternatives (subset/2, ord_subset/2) don't enumerate, but only check for the relation.
There are at least two things you can do, depending on how you want to approach this.
You could simply collect all solutions and find the maximum. Something along the lines of:
?- Items = [item(2,3), item(3,4), item(4,5), item(5,8), item(9,10)],
findall(Gain-List, ks(Items, 20, List, Gain), Solutions),
sort(Solutions, Sorted),
reverse(Sorted, [MaxGain-MaxList|_]).
% ...
MaxGain = 26,
MaxList = [item(9, 10), item(5, 8), item(4, 5), item(2, 3)].
So you find all solutions, sort them by Gain, and take the last. This is just one way to do it: if you don't mind collecting all solutions, it is up to you how you want to pick out the solution you need from the list. You might also want to find all maximum solutions: see this question and answers for ideas how to do that.
The cleaner approach would be to use constraints. As the comment to your questions points out, it is not very clear what you are actually doing, but the way to go would be to use a library like CLP(FD). With it, you could simply tell labeling/2 to look for the maximum Gain first (once you have expressed your problem in terms of constraints).
greedy Approximation algorithm :
pw((P,W),Res) :- PW is P/W, Res=(PW,P,W).
pws(Ps_Ws,PWs) :- maplist(pw,Ps_Ws,PWs).
sort_desc(List,Desc_list) :-
sort(List,Slist),
reverse(Slist,Desc_list).
ransack_([],_,_,[]).
ransack_([(_,P,W)|PWs],Const,Sum,Res) :-
Sum1 is W+Sum,
Sum1 < Const ->
Res=[(P,W)|Res1],
ransack_(PWs,Const,Sum1,Res1)
;ransack_(PWs,Const,Sum,Res).
% ransack(+[(P,W)|..],+W,,Res)
ransack(L_PWs,W,Res) :-
pws(L_PWs,Aux),
sort_desc(Aux,PWs),
ransack_(PWs,W,0,Res).
Test
item(W, V)-->(V,W)
| ?- ransack([(3,2),(4,3),(5,4),(8,5),(10,9)],20,Res).
Res = [(8,5),(3,2),(4,3),(5,4)] ? ;
no

Seating chart starts to output wrong permutations in Prolog

I have a homework assignment where I must write a predicate seatingChart(X):- which will have 8 seats. The rules are:
Adjacent seating partners must be of the opposite gender.
Adjacent seating partners must share at least one of the same hobby.
I thought I wrote the code below to create the correct case.
person(jim,m).
person(tom,m).
person(joe,m).
person(bob,m).
person(fay,f).
person(beth,f).
person(sue,f).
person(cami,f).
% Database of hobbies
% hobbies(name,hobby). -> People can have multiple hobbies)
hobbies(jim, sup).
hobbies(jim, fish).
hobbies(jim, kayak).
hobbies(tom, hike).
hobbies(tom, fish).
hobbies(tom, ski).
hobbies(joe, gamer).
hobbies(joe, chess).
hobbies(joe, climb).
hobbies(bob, paint).
hobbies(bob, yoga).
hobbies(bob, run).
hobbies(fay, sup).
hobbies(fay, dance).
hobbies(fay, run).
hobbies(beth, climb).
hobbies(beth, cycle).
hobbies(beth, fish).
hobbies(sue, yoga).
hobbies(sue, skate).
hobbies(sue, ski).
hobbies(cami, run).
hobbies(cami, kayak).
hobbies(cami, gamer).
%% ANSWER %%
% return a pair of opposite gender people
gender(PersonX, PersonY):-
person(PersonX,GenderX),
person(PersonY,GenderY),
GenderX \= GenderY.
% return the pair of similar interests.
similarHobbies(PersonX, PersonY):-
hobbies(PersonX, HobbyX),
hobbies(PersonY, HobbyY),
HobbyX == HobbyY.
% Create the rules for our seating chart list
seatingRules([P1,P2,P3,P4,P5,P6,P7,P8|_]):-
% Have each adjacent person be of the opposite gender
gender(P1,P2),
gender(P3,P4),
gender(P5,P6),
gender(P7,P8),
gender(P8,P1),
% Have each adjacent person have at least one of the same hobby
similarHobbies(P1,P2),
similarHobbies(P3,P4),
similarHobbies(P5,P6),
similarHobbies(P7,P8).
% Generate a list of all the names from person(...)
people(P):-
findall(X, person(X,_), P).
% Generate a list of permutations of people
permPeople([P1,P2,P3,P4,P5,P6,P7,P8]):-
permutation([P1,P2,P3,P4,P5,P6,P7,P8],
[jim,tom,joe,bob,fay,beth,sue,cami]),
\+error([P1,P2,P3,P4,P5,P6,P7,P8]).
error([P1,P2,P3,P4,P5,P6,P7,P8]):-
\+seatingRules([P1,P2,P3,P4,P5,P6,P7,P8]).
seatingChart(X):-
permPeople(X).
When I run this using seatingChart(X). in SWI-Prolog I get the following answer first:
X = [jim, fay, tom, beth, joe, cami, bob, sue] ;
However, my subsequent permutations seem to be flat out wrong.. after hitting ; a few more times this says it's a valid answer:
X = [jim, beth, sue, tom, joe, cami, bob, fay] .
What am I doing wrong? Or what is causing my permutations to start not following the seating chart rules?
Shouldn't the seating rule predicate contain all pairs?
% Create the rules for our seating chart list
seatingRules([P1,P2,P3,P4,P5,P6,P7,P8|_]):-
% Have each adjacent person be of the opposite gender
gender(P1,P2),
gender(P2,P3),
gender(P3,P4),
gender(P4,P5),
gender(P5,P6),
gender(P6,P7),
gender(P7,P8),
gender(P8,P1),
% Have each adjacent person have at least one of the same hobby
similarHobbies(P1,P2),
similarHobbies(P2,P3),
similarHobbies(P3,P4),
similarHobbies(P4,P5),
similarHobbies(P5,P6),
similarHobbies(P6,P7),
similarHobbies(P7,P8),
similarHobbies(P8,P1).

Prolog Loops until True

I'm pretty new to Prolog but I'm trying to get this program to give me the first set of twin primes that appears either at or above N.
twins(M) :-
M2 is M + 2,
twin_prime(M, M2),
write(M),
write(' '),
write(M2).
M3 is M + 1,
twins(M3).
However, I'm not completely sure how to go about getting it to loop and repeat until it's true. I've tried using the repeat/0 predicate but I just get stuck in an infinite loop. Does anyone have any tips I could try? I'm pretty new to Prolog.
You're on the right track using tail recursion and #Jake Mitchell's solution works swell. But here are some tips that might help clarify a few basic concepts in Prolog:
First, it seems like your predicate twins/1 is actually defining a relationship between 2 numbers, namely, the two twin primes. Since Prolog is great for writing very clear, declarative, relational programs, you might make the predicate more precise and explicit by making it twin_primes/2. (That this should be a binary predicate is also pretty clear from your name for the predicate, since one thing cannot be twins...)
One nice bonus of explicitly working with a binary predicate when describing binary relations is that we no longer have to fuss with IO operations to display our results. We'll simply be able to query twin_primes(X,Y) and have the results returned as Prolog reports back on viable values of X and Y.
Second, and more importantly, your current definition of twins/1 wants to describe a disjunction: "twins(M) is true if M and M + 2 are both prime or if M3 is M + 3 and twins(M3) is true". The basic way of expressing disjunctions like this is by writing multiple clauses. A single clause of the form <Head> :- <Body> declares that the Head is true if all the statements composing the Body are true. Several clauses with the same head, like <Head> :- <Body1>. <Head> :- <Body2>. ..., declare that Head is true if Body1 is true or if Body2 is true. (Note that a series of clauses defining rules for a predicate are evaluated sequentially, from top to bottom. This is pretty important, since it introduces non-declarative elements into the foundations of our programs, and it can be exploited to achieve certain results.)
In fact, you are only a small step from declaring a second rule for twins/1. You just tried putting both clause-bodies under the same head instance. Prolog requires the redundant measure of declaring two different rules in cases like this. Your code should be fine (assuming your definition of twin_prime/2 works), if you just change it like so:
twins(M) :-
M2 is M + 2,
twin_prime(M, M2),
write(M),
write(' '),
write(M2).
twins(M) :-
\+twin_prime(M, M2), %% `\+` means "not"
M3 is M + 1,
twins(M3).
Note that if you take advantage of Prolog's back-tracking, you often don't actually need to effect loops through tail recursion. For example, here's an alternative approach, taking into account some of what I advised previously and using a quick (but not as in "efficient" or "fast") and dirty predicate for generating primes:
prime(2).
prime(P) :-
between(2,inf,P),
N is (P // 2 + 1),
forall(between(2,N,Divisor), \+(0 is P mod Divisor)).
twin_primes(P1, P2) :-
prime(P1),
P2 is P1 + 2,
prime(P2).
twin_primes/2 gets a prime number from prime/1, then calculates P2 and checks if it is prime. Since prime/1 will generate an infinite number of primes on backtracking, twin_primes/2 will just keep asking it for numbers until it finds a satisfactory solution. Note that, if called with two free variables, this twin_primes/2 will generate twin primes:
?- twin_primes(P1, P2).
P1 = 3,
P2 = 5 ;
P1 = 5,
P2 = 7 ;
P1 = 11,
P2 = 13 ;
P1 = 17,
P2 = 19 ;
P1 = 29,
P2 = 31 ;
But it will also verify if two numbers are twin primes if queried with specific values, or give you the twin of a prime, if it exists, if you give a value for P1 but leave P2 free:
?- twin_primes(3,Y). Y = 5.
There's a handy if-then-else operator that works well for this.
twin_prime(3,5).
twin_prime(5,7).
twin_prime(11,13).
next_twin(N) :-
A is N+1,
B is N+2,
(twin_prime(N,B) ->
write(N),
write(' '),
write(B)
;
next_twin(A)).
And a quick test:
?- next_twin(5).
5 7
true.
?- next_twin(6).
11 13
true.

using arithmetic operations in Prolog

I have the following code:
position(0,0).
move(f):-
position(X,Y),
number(X),
number(Y),
Y is Y+1,
X is X+1.
but when i call move(f) it returns false. number(X) and number(Y) returns true but whem i add the other two lines the function doesn't work. what's the problem?
Elaborating on some of the comments your question has received, variables in Prolog stand for a possible instantiation of a single value, just like variables in mathematics and mathematical logic, and once they are instantiated within a context they must remain consistent. If we're dealing with a formula 0 = (a + b) - (a + b), we know that it can only express its intended sense if any value assigned to the first a is also assigned to the second. That is, we can substitute any value for a, but it must be the same value throughout. Prolog works with variables in this same way. If x = x + 1, then 2 = 3; but then math would be broken.
Addressing mat's caution against using dynamic predicates, here is a possible way of handling moves, but accomplished by passing around a list of previous moves. With this method, the most recent move will always be the first element of List in the compound term moves(List).
Supposing the current history of moves is as follows:
moves([position(0,0), position(0,1), position(1,1)]).
move/3 takes a direction, a complex term representing the previous moves, and tells us what the updated list of moves is.
move(Direction, moves([From|Ms]), moves([To,From|Ms])) :-
move_in_direction(Direction,From,To).
move_in_direction/3 takes a direction, and a position, and tells us what the next position in that direction is:
move_in_direction(left, position(X1,Y1), position(X2,Y1)) :- X2 is X1 - 1.
move_in_direction(right, position(X1,Y1), position(X2,Y1)) :- X2 is X1 + 1.
move_in_direction(up, position(X1,Y1), position(X1,Y2)) :- Y2 is Y1 + 1.
move_in_direction(down, position(X1,Y1), position(X1,Y2)) :- Y2 is Y1 - 1.
Notice that, using this method, you get a back-trackable history of moves for free. I'd imagine you could use this in interesting ways -- e.g. having the player explore possible series of moves until a certain condition is met, at which point it commits or backtracks. I'd be interested to know what kind of solution you end up going with.

Some explanation about 8 queen solution that use permutation

I am studyin Prolog for an universitary exam on SWI Prolog implementation.
I have some doubt about how work this version of 8 Queen problem that solve the problem using the permutations:
solution(Queens) :-
permutation([1,2,3,4,5,6,7,8], Queens),
safe(Queens).
permutation([],[]).
permutation([Head|Tail],PermList) :-
permutation(Tail,PermTail),
del(Head,PermList,PermTail).
del(Item,[Item|List],List).
del(Item,[First|List],[First|List1]) :-
del(Item,List,List1).
safe([]).
safe([Queen|Others]) :-
safe(Others),
noattack(Queen,Others,1).
noattack(_,[],_).
noattack(Y,[Y1|Ylist],Xdist) :-
Y1-Y=\=Xdist,
Y-Y1=\=Xdist,
Dist1 is Xdist + 1,
noattack(Y,Ylist,Dist1).
In a previous resolution I used this solution template: [1\Y1, 2\Y2, 3\Y3, 4\Y4, 5\Y5, 6\Y6, 7\Y7, 8\Y8] that simply say that every queen have to be on different rows the X calue could be constrained.
This version of the program it's pretty difference because we can observe that to prevent vertical attack all the queens have to be on different rows, so I have a queen for each row.
So without losing information so I can say that the solution will be a permutation of the list: [1,2,3,4,5,6,7,8] in wich every element rappresent the Y coordinate of a queen.
so in this case I am rappresenting a queen position only by its Y coordinate (its row index)
So I have the solution relation than say that a list Queens is a solution if Queens is a premutation of [1,2,3,4,5,6,7,8] original list and if this permutation is safe (every queen in this permutation list don't attack the others queen).
Ok, this is clear...now it is defined the safe relation.
There is a base case that say that if the list is empty then this is safe because there is no attack.
And there is a general case in wich the list is not empty. If the list is not empty I can divide it in [Queen|Others] where Queen is the first queen in the list and Others is the remaining sublist...so the original list [Queen|Others] is safe if the sublist Others it is itself a solution (if it is safe, if there is no attack in Others) and if the first item Queen do not attack the other queen in others sublist...
Ok...until now I think that it is clear for me...
Now I have some problems with the definition of noattack relation !!!
The problem is that I rappresent a queen position only by its Y coordinate and the X coordinate it is
not explicity present.
I have understand that for circumvent this limitation of the rappresentation there is the folowing generalizzation (I hope to have understood well...I am not so sure...): ** I know also that there must be a queen on each column of the board, so the X distance from the first queen in the list (Queen) and the sublist Others must be 1**
Where the distance from the first item Queen and the sublist Others is the X distance from the first item Queen and the queen nearest to it, is it righ my reasoning?
So the noattack relation is TRUE if the queen are on different columns (always true for construction) and I can express that have to be on different row sayng that the X distance from Queen and the nearest queen in the Others sublist is 1.
But, if my reasoning is truem I am finding many trouble trying to understand how rappresent this thing in this part of code:
noattack(Y,[Y1|Ylist],Xdist) :-
Y1-Y=\=Xdist,
Y-Y1=\=Xdist,
Dist1 is Xdist + 1,
noattack(Y,Ylist,Dist1).
i think your problem is only these 2 lines:
Y-Y1=/=Xdist,
Y1-Y=/=Xdist,
it checks whether the queen which has Y, attacks the queen in the row with a distance of Xdist in a diagonal way or not. (|Y - Y1| = Xdist --> diagonal attack ).
the other way to attack other queens is to qttack them in the same row, that doesn't happen simply because the solution is a permutation of [1,2,3,4,5,6,7,8]. so something like [1,1,3,4,5,6,7,8] never happens and thats enough to check diagonals.
I hope it solved the problem.
p.s. note that Y1 is the Y coordinate of the head of the Others from the rule Safe/1. so it is a queen with a Xdist of 1 at first, then it backtracks to other rows.
clarification
we are talking about noattack/3. you give it three arguments:
first: Y coordinate of current queen,
second: a right-most list [Y1| Ylist] the begins somewhere after where Y is in the list, and continues to the end of the primary list.(yes this is a sublist of the solution), and
third: Xdist is the index-distance between current queen (which has Y) and the queen which is gonna be checked with the current queen (which has Y1 and is the head of second argument).
third argument is necessary because without it we can not check diagonal interaction between the current queen and and the queen which has Y1. it is really basic mathematics, you have 2 points in the coordinate system with only natural numbers. lets say these 2 points attack each other in a diagonal way if and only-if abs(x1 - x2) = abs(y1 - y2).
Example #1 . and if you understood my explanations well, check it as Accepted.
P1 = (3, 4) and P2 = (1, 2) --> these points have a diagonal attack because abs(3-1) = abs(4-2) = 2
Example #2
P1 = (3, 4) and P2 = (7, 0) --> these points have a diagonal attack because abs(3-7) = abs(4-0) = 4
this is why we check both Y1-Y =\= Xdist and Y-Y1 =\= Xdist . because whenever there is a diagonal attack at least one of them is gonna be true. when non of them was true, it means that there is no diagonal attack between the queen with Y and the queen with Y1. so we can proceed to checking the next queen which is the head of Ylist.
I hope that's enough. this is a very easy problem, Just read it carefully and if you couldn't understand again try to trace the algorithm on a piece of paper yourself. that's a way always works.
I have the same confusion when solving the same problem in recursion using C. There are two possible directions for diagonal attacks. Number each square with x,y coordinates with top-left being 0,0. You will then see that these two attacking diagonal detection or calculation satisfies the conditions:
x1 - y1 == x2 - y2
x1 + y1 == x2 + y2
I annotated the comparison above in C equality notation and not Prolog notation. Between two points (x1,y1) and (x2,y2), there is queen diagonal attack if one of conditions above is meet.

Resources