Prolog backtracks and loses all values - prolog

I have a few prolog predicates which calculate the cost of given cities. The process begins with a command like: best_route([std, lhr, bud, dse], 2013-5-5, X).
best_route(Cities, StartDate, Cost):-
begin_routing(Cities, StartDate, Cost, []).
begin_routing(Cities, StartDate, Cost, CostList):-
route(Cities, StartDate, CostList),
min_list(CostList, Cost).
route(Cities, StartDate, Costing):-
% stop if all cities have been covered once.
length(Cities, Stop),
length(Costing, Stop);
[Origin, Dest|_] = Cities,
flights(Origin, Dest, StartDate, Costing, Cities, [Cities, Origin, StartDate]).
Using the trace function in SWI-Prolog, I found that once the route predicate - length(Costing, Stop) is satisfied i.e., length of Costing List is equal to Stop. Prolog instead of stopping there, and proceeding with min_list(CostList, Cost), instead backtracks until CostLost loses all its values again. Once it finishes that, it goes to min_list when the list is [].
I am not sure why this might be happening. Any help is appreciated.
EDIT:
flights(..):-
% Code omitted.
get_next_date(OriginalDate, NextDate),
route(Cities, NextDate, [DayCost|Costing]).
% where DayCost is a simple integer calculated before this is added to the current Costing list
Towards the end, the last correct call is route([std, lhr, bud, dse], 2013-5-6, [329, 499, 323, 311]).

It seems that the intention of CostList is to record the costs of different routes and then to select the one with the smallest cost. However, you initialize CostList as [], and while the recursion is building CostList you do not provide means of communicating it back when the recursion returns. A possible solution would be to add a new argument FinalCostList that is being simply passed through the recursion until the termination clause. Alternatively, you can use difference lists.
To illustrate this point, consider the following example:
p :- q([]).
q(X) :- go(X), !, q([a|X]).
q(X) :- stop(X).
with some mutually exclusive go and stop. The desired outcome (q with all as as long as go) is computed but not returned. A better solution would be
p(Y) :- q([],Y).
q(X,Y) :- go(X), !, q([a|X],Y).
q(X,X) :- stop(X).
As said above difference lists can also be used.

Related

How I get most repetitive element of this? (Prolog - Logic Paradigm)

In predicate "sample", the first element is your id and the others, are the result of the sample.
sample(1,04,05,30,33,41,52).
sample(2,05,37,39,41,43,49).
sample(3,05,11,29,30,36,47).
sample(4,01,05,06,27,42,59).
I need to get the most repeated value of sample... In this case is the "05" and I don't have idea how I do this. How I can do this?
If the id's are consecutive and start with 1, then this is a possible structure for a solution:
I would first change the format of the represented samples:
:- dynamic([sample/2,sample/7]).
represent_samples:-
retract(sample(I,S1,S2,S3,S4,S5,S6)),
assert(sample(I,[S1,S2,S3,S4,S5,S6])),
fail.
represent_samples.
The first line :- dynamic([sample/2,sample/7]). declares the predicates sample/2 and sample/7 to be dynamic predicates; that means that clauses of them can be added and retracted from your knowledge base. The retract line retracts a sample/7 fact from the knowlege, while the next line starting with assert adds it sample/2 representation. The line fail ensures that this is done as long as there are sample/7 facts in the knowledge base and the single represent_samples. fact guarantees that your predicate represent_samples/0 succeeds.
You can check the effect of this predicate by the following sequence of queries:
?- listing([sample/2,sample/7]).
?- represent_samples.
?- listing([sample/2,sample/7]).
After re-representing the samples one can go through them (guided by the id).
most_rep(N):-
most_rep(1,[],N).
most_rep(I,Acc,N):-
sample(I,L),
append(L,Acc,NewAcc),
I1 is I+1,
most_rep(I1,NewAcc,N).
most_rep(_I,Acc,N):-
choose_most_freq(Acc,N).
Now we only have to write the predicate choose_most_freq/2 that identifies the most frequent element in a list.
For this problem you could use the solution posted on prolog: maximally repeated element in a list
choose_most_freq(L, M) :-
setof(I-E, C^(aggregate(count, member(E, L), C), I is -C), [_-M|_]).

Comparing list element structures to each other in Prolog

I am working through sample questions while studying, using SWI-Prolog. I have reached the last section of this question, where I have to recursively (I hope) compare elements of a list containing 'researcher' structures to determine whether or not the researchers have the same surname, and, if they do, return the Forename and Surname of the group leader for that list.
There is only one list that meets this criteria and it has four members, all with the same surname. However, the correct answer is returned FOUR times. I feel my solution is inelegant and is lacking. Here is the question:
The following Prolog database represents subject teaching teams.
% A research group structure takes the form
% group(Crew, Leader, Assistant_leader).
%
% Crew is a list of researcher structures,
% but excludes the researcher structures for Leader
% and Assistant_leader.
%
% researcher structures take the form
% researcher(Surname, First_name, expertise(Level, Area)).
group([researcher(giles,will,expertise(3,engineering)),
researcher(ford,bertha,expertise(2,computing))],
researcher(mcelvey,bob,expertise(5,biology)),
researcher(pike,michelle,expertise(4,physics))).
group([researcher(davis,owen,expertise(4,mathematics)),
researcher(raleigh,sophie,expertise(4,physics))],
researcher(beattie,katy,expertise(5,engineering)),
researcher(deane,fergus,expertise(4,chemistry))).
group([researcher(hardy,dan,expertise(4,biology))],
researcher(mellon,paul,expertise(4,computing)),
researcher(halls,antonia,expertise(3,physics))).
group([researcher(doone,pat,expertise(2,computing)),
researcher(doone,burt,expertise(5,computing)),
researcher(doone,celia,expertise(4,computing)),
researcher(doone,norma,expertise(2,computing))],
researcher(maine,jack,expertise(3,biology)),
researcher(havilland,olive,expertise(5,chemistry))).
Given this information, write Prolog rules (and any additional predicates required) that can be used to return the following:
the first name and surname of any leader whose crew members number more than one and who all have the same surname. [4 marks]
This is the solution I presently have using recursion, though it's unnecessarily inefficient as for every member of the list, it compares that member to every other member. So, as the correct list is four members long, it returns 'jack maine' four times.
surname(researcher(S,_,_),S).
checkSurname([],Surname):-
Surname==Surname. % base case
checkSurname([Researcher|List],Surname):-
surname(Researcher,SameSurname),
Surname == SameSurname,
checkSurname(List,SameSurname).
q4(Forename,Surname):-
group(Crew,researcher(Surname,Forename,_),_),
length(Crew,Length),
Length > 1,
member(researcher(SameSurname,_,_),Crew),
checkSurname(Crew,SameSurname).
How could I do this without the duplicate results and without redundantly comparing each member to every other member each time? For every approach I've taken I am snagged each time with 'SameSurname' being left as a singleton, hence having to force use of it twice in the q4 predicate.
Current output
13 ?- q4(X,Y).
X = jack,
Y = maine ; x4
A compact and efficient solution:
q4(F, S) :-
group([researcher(First,_,_), researcher(Second,_,_)| Crew], researcher(S, F, _), _),
\+ (member(researcher(Surname, _, _), [researcher(Second,_,_)| Crew]), First \== Surname).
Example call (resulting in a single solution):
?- q4(X,Y).
X = jack,
Y = maine.
You are doing it more complicated than it has to be. Your q4/2 could be even simpler:
q4(First_name, Surname) :-
group(Crew, researcher(Surname, First_name, _E), _A),
length(Crew, Len), Len > 1,
all_same_surname(Crew).
Now you only need to define all_same_surname/1. The idea is simple: take the surname of the first crew member and compare it to the surnames of the rest:
all_same_surname([researcher(Surname, _FN, _E)|Rest]) :-
rest_same_surname(Rest, Surname).
rest_same_surname([], _Surname).
rest_same_surname([researcher(Surname, _FN, _E)|Rest), Surname) :-
rest_same_surname(Rest, Surname).
(Obviously, all_same_surname/1 fails immediately if there are no members of the crew)
This should be it, unless I misunderstood the problem statement.
?- q4(F, S).
F = jack,
S = maine.
How about that?
Note: The solution just takes the most straight-forward approach to answering the question and being easy to write and read. There is a lot of stuff that could be done otherwise. Since there is no reason not to, I used pattern matching and unification in the heads of the predicates, and not comparison in the body or extra predicates for extracting arguments from the compound terms.
P.S. Think about what member/2 does (look up its definition in the library, even), and you will see where all the extra choice points in your solution are coming from.
Boris did answer this question already, but I want to show the most concise solution I could come with. It's just for the educational purposes (promoting findall/3 and maplist/2):
q4(F, S) :-
group(Crew, researcher(S, F, _), _),
findall(Surname, member(researcher(Surname, _, _), Crew), Surnames),
Surnames = [ First, Second | Rest ],
maplist(=(First), [ Second | Rest ]).

Prolog - Recursive Function Abs / Create new list

I want a function that given a Point, and a List of points it calculates:
the absolute value of (Point - PointofList), for every point of the list.
I have this code so far, but I seem to be failing on the recursive part.
absL((X,Y),[],Result) :- Result.
absL((X,Y),[(X2,Y2)|Z], R) :- ABSX is abs(X-X2),
ABSY is abs(Y-Y2),
append([(ABSX,ABSY)], NL, R),
absL((X,Y),Z,NL).
You have defined the base case wrong.
When there are no more points in the list of points, the resulting list should be an empty list, but you are just leaving an uninstantiated variable:
So, your base case should read:
absL((_,_),[],[]).
I left the first argument with a structure using unnamed variables because the coordinates for the given point are not needed for the base case.
Your recursive clause is a bit more complex than it should. You really don't need to use append/3 to build the resulting list. You can build the list directly in the head of the clause:
absL((X,Y),[(X2,Y2)|Z], [(ABSX,ABSY)|NL]) :-
ABSX is abs(X-X2),
ABSY is abs(Y-Y2),
absL((X,Y),Z,NL).
The recursive step will add the new distance before the recursive call and just insert this values when unifying the third argument at return of the recursive call.
this clause absL((X,Y),[],Result) :- Result. attempt to call Result, at least in Prologs that accept such syntax.
since #gusbro already shows the required correction, I'll show an alternative:
absL((X,Y), L_In, L_Out) :-
maplist(absPoint((X,Y), L_In, L_Out).
absPoint((X, Y), (X2,Y2), (ABSX, ABSY)) :-
ABSX is abs(X-X2),
ABSY is abs(Y-Y2).

Prolog Summing Fields in a Database

I am learning Prolog and I understand how to calculate the sum of a list but I can't figure out how to compute the sum of the fields of a database.
Sample database:
tastiness(bacon,100,200,300,400,500).
tastiness(lettuce,3,5,6,7,12).
Sample output
(bacon,1500).
(lettuce,33).
Here's how to sum the values of a list in standard Prolog:
sumlist([], 0).
sumlist([X|Xs], Sum) :-
sumlist(Xs, SumTail),
Sum is X + SumTail.
If you have something like
bacon(100).
bacon(200).
bacon(300).
bacon(400).
bacon(500).
you could then use the findall predicate. The findall predicate works as follows: If you want Z = [100, 200, 300, 400, 500] (the list of all bacon numbers) you write findall(X, bacon(X), Z).
Here's how to sum all bacon numbers:
| ?- findall(X, bacon(X), AllBacon), sumlist(AllBacon, SumBacon).
AllBacon = [100,200,300,400,500]
SumBacon = 1500
yes
As a side note, the sum computation proposed by #aioobe is not optimal because on a very large list, you will run out of call stack memory.
A particular technique is to put the recursive call of the predicate as the last element of your predicate. This way, all preceding things being already computed, Prolog can flush the current context of the predicate while making the recursive call. On a list with 1M elements, that means than you will run with 1 context being kept instead of up to one million.
While it may not seem important to you for this particular exercise, the tail call optimization is what makes recursion as powerful as iteration, if you take space consumption into consideration. It's worth learning!
Here is a version on which Tail Call Optimization is performable:
sumlist(List, Result) :-
sumlist(List, 0, Result).
sumlist([], Acc, Acc).
sumlist([Item|List], Acc, Result) :-
NewAcc is Acc + Item.
sumlist(List, NewAcc, Result).
It makes use of an idiom you will encounter a lot in declarative programming: an accumulator (here named Acc). Its purpose is to hold the resulting value "up until now" during the recursion.

Simplified Travelling Salesman in Prolog

I've looked through the similar questions but can't find anything that's relevant to my problem. I'm struggling to find an algorithm or set of 'loops' that will find a path from CityA to CityB, using a database of
distance(City1,City2,Distance)
facts. What I've managed to do so far is below, but it always backtracks at write(X), and then completes with the final iteration, which is what I want it to do but only to a certain extent.
For example, I don't want it to print out any city names that are dead ends, or to use the final iteration. I want it to basically make a path from CityA to CityB, writing the name of the cities it goes to on the path.
I hope somebody can help me!
all_possible_paths(CityA, CityB) :-
write(CityA),
nl,
loop_process(CityA, CityB).
loop_process(CityA, CityB) :-
CityA == CityB.
loop_process(CityA, CityB) :-
CityA \== CityB,
distance(CityA, X, _),
write(X),
nl,
loop_process(X, CityB).
I tried to demonstrate how you can achieve what you're working on so that you can understand better how it works. So since your OP wasn't very complete, I took some liberties ! Here are the facts I'm working with :
road(birmingham,bristol, 9).
road(london,birmingham, 3).
road(london,bristol, 6).
road(london,plymouth, 5).
road(plymouth,london, 5).
road(portsmouth,london, 4).
road(portsmouth,plymouth, 8).
Here is the predicate we will call to find our paths, get_road/4. It basically calls the working predicate, that has two accumulators (one for the points already visited and one for the distance we went through).
get_road(Start, End, Visited, Result) :-
get_road(Start, End, [Start], 0, Visited, Result).
Here is the working predicate,
get_road/6 : get_road(+Start, +End, +Waypoints, +DistanceAcc, -Visited, -TotalDistance) :
The first clause tells that if there is a road between our first point and our last point, we can end here.
get_road(Start, End, Waypoints, DistanceAcc, Visited, TotalDistance) :-
road(Start, End, Distance),
reverse([End|Waypoints], Visited),
TotalDistance is DistanceAcc + Distance.
The second clause tells that if there is a road between our first point and an intermediate point, we can take it and then solve get_road(intermediate, end).
get_road(Start, End, Waypoints, DistanceAcc, Visited, TotalDistance) :-
road(Start, Waypoint, Distance),
\+ member(Waypoint, Waypoints),
NewDistanceAcc is DistanceAcc + Distance,
get_road(Waypoint, End, [Waypoint|Waypoints], NewDistanceAcc, Visited, TotalDistance).
Usage is as follows :
?- get_road(portsmouth, plymouth, Visited, Distance).
And yields :
Visited = [portsmouth, plymouth],
Distance = 8 ;
Visited = [portsmouth, london, plymouth],
Distance = 9 ;
Visited = [portsmouth, plymouth, london, plymouth],
Distance = 18 ;
false.
I hope it will be helpful to you.
Please separate the pure part from the impure (I/O, like write/1, nl/0 but also (==)/2 and (\==)/2). As long as they are entirely interlaced with your pure code you cannot expect much.
Probably you want a relation between a starting point, an end point and a path in between.
Should that path be acyclic or do you permit cycles?
To ensure that an element X does not occur in a list Xs use the goal maplist(dif(X),Xs).
You do not need any further auxiliary predicates to make this a nice relation!
You should return a successful list as an Out variable in all_possible_paths. Then write out that list. Don't do both in the same procedure.

Resources