Counting Prolog elements that meet a certain requirement - prolog

I am working through sample questions while studying using SWI-Prolog. This isn't a homework question, just exam revision using practice questions.
In this question I have a number of provided structures (dept) which includes a list within it, and I have to find the initial and surname of any head curator in a department that contains a total of 2 or more staff members with early history or modern history as their specialism.
The following Prolog database represents departments in a museum.
% A museum department is represented by the structure:
% dept(Area, Head_curator, Deputy_curator, Floor_staff).
%
% Floor_staff is a (possibly empty) list of staff structures and excludes
% the staff for Head_curator and Deputy_curator.
%
% Structures representing staff take the form:
% staff(Initial, Surname,
% cv(Specialism,Second_specialism,Years_employed)).
... Removed db entries that don't satisfy the question
dept(history,staff(d,steele,cv(early_history,modern_history,40)),
staff(d,owen,cv(modern_history,early_history,35)),
[staff(c,freud,cv(early_history,modern_history,30))]).
I am finding it very difficult to determine how to ensure that at least two of the Head OR the Deputy OR any member of the floor staff has a 1st or 2nd specialisation in early or modern history. I have created the following rules to extract this information:
head_curator(X) :- dept(_,X,_,_).
deputy_curator(X) :- dept(_,_,X,_).
floor_staff(X) :- dept(_,_,_,List), member(X,List).
exists(X) :-
head_curator(X);
deputy_curator(X);
floor_staff(X).
first_specialism((staff(_,_,cv(S,_,_,_)),S).
second_specialism((staff(_,_,cv(_,S,_,_)),S).
And I have written PROLOG below to extract the head curator's Initial and Surname - however, I am unsure of how to implement a 'count' to ensure any two people have the required specialism
q2(Initial,Surname) :-
dept(_,HeadCurator,_,List),
% forcing the same dept to be checked
HeadCurator = exists(staff(Initial,Surname,_)),
% check first and second specialisations
% somehow iterate through each person in this dept and
% stop once requirement reaches two people
%
% e.g. (first_specialisation(X) = modern_history;
% first_specialisation(X) = early_history;)
%
% (second_specialisation(X) = modern_history;
% second_specialisation(X) = early_history;)
I have attempted to flesh out the pseudocode outlining what I WOULD attempt as a solution to let you know that I have thought about it! I am just unsure of how to implement checking:
That numerous people have that 1st or 2nd specialisation
Returning true once two people are determined to have that specialisation

Related

Prolog add function

I am a newbie in Prolog. I have this facts:
user(alice).
user(bob).
money(alice,10).
money(bob,20).
The facts means that alice have 10 dollars and bob 20 dollars. In order to learn Prolog, i want to develop a function to transfer money between two users. My function should take in input the name of the receiver and the amount and add the quantity to the receiver.
example: add(alice,20)
and the output should be 30.
Actually my function is:
add(X,Y,Z) :- Z is money(X,M) + Y.
but it doesn't work.
How i can implement it?
Thanks
First, as you want to modify the facts for the predicate money/2 as the users exchange money, you need to declare the predicate dynamic:
:- dynamic(money/2).
Second, as Prolog is a relational language, not a functional language, you cannot write expressions as Z is money(X,M). Instead you need to write something like:
add(User, Money) :-
% remove old clause and access how much money the user holds
retract(money(User, Current)),
% compute the updated amount of money the user will be holding
Updated is Current + Money,
% add updated fact
assertz(money(User, Updated).

Solve riddle in prolog

I want to solve this riddle in prolog:
The students Lily, Jack and Daisy go to the same university. All of them come from a different country and have different hobbies. They all go to a university in the USA, where one of them is living. Lily has better grades then the one who comes from Italy. Jack has better grades then the one who likes reading books. The best grades has the one who likes football. Jack comes from Germany and Daisy likes cooking.
Who is who (name, country, hobby, grades)?
The correct solution should be:
Lily, USA, Reading Books, Grade 2
Jack, Germany, Football, Grade 1
Daisy, Italy, Cooking, Grade 3
The Problem I have right now is that I don't know how I could solve this riddle. How should I define the facts and what's the best way to solve the riddle?
The trick to answer these puzzle questions in Prolog is to generate (retrieve) possible answers and then test them against the logical constraints. So, if Lily is person P1, then retrieve any person P2 and test if that person is from italy. And so forth with the other rules.
That means, in first instance, you need some clauses with possible countries, possible hobbies and possible grades. Not all possibilities are necessary, because some are already ruled out by the question.
The solution below, based on arbitrarily making Lily person 1, Jack person 2 and Daisy person 3.
Load in to Prolog and query who(P1,C1,H1,G1, P2,C2,H2,G2, P3,C3,H3,G3).
country(italy).
country(usa).
hobby(football).
hobby(reading).
grade(c:1).
grade(b:2).
grade(a:3).
who(lily,C1,H1,Grade1, jack,germany,H2,Grade2, daisy,C3,cooking,Grade3):-
country(C1), country(C3), C1 \= C3,
hobby(H1), hobby(H2), H1 \= H2,
grade(G1:Grade1), grade(G2:Grade2), grade(G3:Grade3),
G1 \= G2, G2 \= G3, G1 \= G3,
(C3=italy, G1#>G3),
(H1=reading, G2#>G1),
((H1=football, G1#>G2, G1#>G3); (H2=football, G2#>G1, G2#>G3)).
First, from filling in what we get from the first statement, we have the following.
(Lily, _, _, _)
(Jack,Germany, _, _)
(Daisy, _, Cooking, _)
Where the _ state we don't know something. I should also say that this isn't necessarily prolog, it's more common sense than anything.
We get the phrase "Lily has better grades then the one who comes from Italy", this means that Daisy is from Germany and Lily is from the USA- since Jack's from Germany.
(Lily, USA, _, Grade>Daisy)
(Jack,Germany, _, _)
(Daisy, Italy, Cooking, Grade<Lily)
Next, we have "Jack has better grades then the one who likes reading books", which gives us the fact that he would be the football player, and the next line tells us he has the best grade. We can then promptly fill up the remained and we get:
(Lily, USA, Reading, Grade2)
(Jack,Germany, Football, Grade1)
(Daisy, Italy, Cooking, Grade3)
There could be a program written in prolog that can solve this puzzle in a very roundabout way, but this puzzle is more specific than a general case.
Here is my take. It is essentially what #RdR has, just that I broke up the logic into more predicates, also to have a less overloaded who() main predicate.
name(lily). % (1)
name(jack).
name(daisy).
country(italy).
country(usa).
country(germany).
hobby(football).
hobby(cooking).
hobby(reading).
grade(1).
grade(2).
grade(3).
student(N,C,H,G):- % (2)
name(N), country(C), hobby(H), grade(G).
permute(P,X,Y,Z):- (4)
call(P,X), call(P,Y), call(P,Z) % (6)
, X\=Y, Y\=Z, X\=Z.
students(A,B,C):- (3)
permute(name,N1,N2,N3) % (5)
, permute(country,C1,C2,C3)
, permute(hobby,H1,H2,H3)
, permute(grade,G1,G2,G3)
, A = student(N1,C1,H1,G1) % (7)
, B = student(N2,C2,H2,G2)
, C = student(N3,C3,H3,G3)
.
who(A,B,C):- % (8)
students(A,B,C)
, A = student(lily,C1,H1,G1) % (9)
, B = student(jack,C2,H2,G2)
, C = student(daisy,C3,H3,G3)
, C2 = germany % (10)
, H3 = cooking
, (( C2=italy -> G1 < G2) % (11)
;( C3=italy -> G1 < G3))
, (( H1=reading -> G2 < G1)
;( H3=reading -> G2 < G3))
, (( H1=football -> G1 < G2, G1 < G3)
;( H2=football -> G2 < G1, G2 < G3)
;( H3=football -> G3 < G1, G3 < G2))
.
% Running it:
% ?- who(A,B,C).
% A = student(lily, usa, reading, 2),
% B = student(jack, germany, football, 1),
% C = student(daisy, italy, cooking, 3) ;
% false.
Discussion
So there is quite a bit going on here (which intrigued me), and several choices that could be made differently (hence it is probably a nice contrast to #RdR's solution).
As others pointed out one aspect is how to encode the information that is
given in the problem description. You can express it very concretely (solving
just this case), or be more general (to e.g. allow extending the problem to
more than 3 students).
What makes this problem different from similar others is that you have a mix
of constraints that affect either a singel student ("Jack comes from
Germany"), affect two students ("Lily's grades are better than the one from
Italy"), or involves all of them ("The one liking football has the best
grades").
Moreover, you have disjunction contraints ("They are all from different
contries, and have different hobbies"). Prolog is very good at going through
all the possible instances of a fact, but it is more complicated to have it
pick one instance and leave this one out for the next call of the predicate.
This forces you to find a way to get a set of values from a fact that are
pairwise distinct. (E.g. when Prolog settles for Lily's hobby being reading,
it mustn't also assign reading as Jack's hobby).
So after listing all known facts and their possible values (1) I first
defined a predicate student/4 (2) to simply state that a student has these
4 properties. This produces all possible combination of students and their
attributes, also allowing that they all have the same name, come from the
same country, asf.
It is a good example how to create a result set in Prolog that is way too
large, and then try to narrow it further down (like someone else wrote).
Further predicates could make use of this "generator" and filter more and more
solutions from its result set. This is also easier to test, on each stage you
can check if the intermediate output makes sense.
In a next predicate, students/3 (3), I try exactly what I mentioned
earlier, creating student instances that at least don't use the same
attribute twice (like two students having the same hobby). To achive this I
have to run through all my attribute facts (name/1, country/1, ...), get
three values for each, and make sure they are pairwise distinct.
To not having to explicitly do this for each of the attributes where the
implementation would be always the same except for the name of the
attribute, I constructed a helper predicate permute/4 (4) that I can pass
the attribute name and it will look up the attribute as a fact three times,
and makes sure the bound values are all not the same.
So when I call permute(name,N1,N2,N3) in students/3 (5) it will result in
the lookups call(P,X), call(P,Y), call(P,Z) (6) which results in the same as
invoking name(X), name(Y), name(Z). (As I'm collecting 3 different values
from always 3 facts of the same attribute this is effectively the same as doing the 3-permutations
of a 3-value set, hence the name of helper predicate.)
When I get to (7) I know I have distinct values for each student attribute,
and I just distribute them across three student instances. (This should
actually work the same without the student/4 predicate as you can always
make up structured terms like this on the fly in Prolog. Having the student
predicate offers the additional benefit of checking that no foolish students
can be constructed, like "student(lily, 23, asdf, -7.4)".)
So :- students(A,B,C). produces all possible combinations of 3 students and
their attributes, without using any of the involved attributes twice. Nice.
It also wraps the (more difficult) student() structures in handy
single-letter variables which makes the interface much cleaner.
But aside from those disjointness constraints we haven't implemented any of
the other constraint. These now follow in the (less elegant) who/3
predicate (8). It basically uses students/3 as a generator and tries to
filter out all unwanted solutions by adding more constraints (Hence it has
basically the same signature as students/3.)
Now another interesting part starts, as we have to be able not only to filter
individual student instances, but also to refer to them individually
("Daisy", "Jack", ...) and their respective attributes ("Daisy's hobby"
etc.). So while binding my result variable A, B and C, I pattern-match on
particular names. Hence the literal names lily, jack asf from (9). This
relieves me from considering cases where Lily might come in first, or as
second, or as third (as students/3 would generate such permutations). So
all 3-sets of students that don't come in that order are discarded.
I could just as well have done this later in an explicit constraint like N1 =
lily asf. I do so now enforcing the simple facts that Jack is from Germany
and Daisy likes cooking (10). When those fail Prolog backtracks to the
initial call to students/3, to get another set of students it can try.
Now follow the additional known facts about Lily's grades, Jack's grades, and
the grades of the football lover (11). This is particularly ugly code.
For one, it would be nice to have helper predicate that would be able return
the answer to the query "the student with attribute X". It would take the
current selection of students, A, B and C, an attribute name ('country') and a
value ('italy') and would return the appropriate student. So you could just
query for the student from Italy, rather than assuming it must be the second
OR the third student (as the problem description suggests that Lily herself
is not from Italy).
So this hypothetical helper predicate, let's call it student_from_attribute
would need another helper that finds a value in a student structure by name
and return the corresponding value. Easy in all languages that support some
kind of object/named tuple/record where you can access fields within it by
name. But vanilla Prolog does not. So there would be some lifting to be done,
something that I cannot pull off of the top of my head.
Also the who/3 predicate could take advantage of this other helper, as you
would need a different attribute from the student returned from
student_from_attribute, like 'grade', and compare that to Lily's grade.
That would make all those constraints much nicer, like
student_from_attribute([A,B,C], country, italy, S), attrib_by_name(S, grade,
G), G1 < G. This could be applied the same way to the reading and football
constraint. That would not be shorter, but cleaner and more general.
I'm not sure anybody would read all this :-). Anyway, these considerations made the puzzle interesting for me.

Speed issue with code

I have an issue with a solution, which very fast becomes slow.
The code below determines if an array of "rules" are valid - and example could be
rules_valid([rule(2,[1,2,3]), rule(2,[1,2,3])],[])
which should be false as it is not possible to select 4 (2+2) distinct numbers from the lists, and
rules_valid([rule(2,[1,2,3]), rule(2,[3,4,5])],[])
is hence true.
For very small queries this performs fine, but it becomes slow very fast. Can anyone point me in a direction of how to speed up this code, if possible.
rules_valid([], _).
rules_valid( [ rule(RequiredUserNumber, UserIds) | RemainingRules ], UsedUserIds) :-
n_users_from_rule(RequiredUserNumber, UserIds, UsedUserIds, UpdatedUsedUserIds),
rules_valid(RemainingRules, UpdatedUsedUserIds).
n_users_from_rule(0, _, UsedUserIds, UsedUserIds).
n_users_from_rule(RequiredUserNumber, UserIds, UsedUserIds, UpdatedUsedUserIds) :-
0 < RequiredUserNumber,
UpdatedRequiredUserNumber is RequiredUserNumber - 1,
select(UserId, UserIds, UpdatedUserIds),
not(member(UserId, UsedUserIds)),
n_users_from_rule(UpdatedRequiredUserNumber, UpdatedUserIds, [ UserId | UsedUserIds ] , UpdatedUsedUserIds ).
UPDATE
So, switching to CLPFD for this piece of logic makes that part much faster. But, I cannot seem to wrap my head around how to make the rest of my application use CLPFD also so it will work for the whole application.
I have a list of userRequests:
userRequest(UserId, PrioritizedRequestList)
request(State, PeriodList)
period(FromDay, ToDay)
i.e.
userRequest( 1 , [request(State,[period(1,5)]),request(State,[period(1,2),period(1,5)])] )
and then I have a list of rule groups which is the structure from my problem wrapped in a
ruleGroup(Day, [rules])
So what I do is to change the state of a userRequest to approved is that I take the first request and approve it, and hence removing the userId from all ruleGroups that has the day overlapping the request because that user no longer is able to fulfill the rule that day.
I have big troubles seeing how I can update these domains removing the user from them.
The issue is that I have been working on lists and not domains, and have a lot of logic around them I have to change as well.
Check out CLP(FD) constraints. Many Prolog systems, including SICStus Prolog and also SWI, ship with a powerful constraint called all_distinct/1: It is true iff all variables from the given list can be assigned distinct integers.
For example, let us state your first query in terms of CLP(FD):
?- length(Ls, 4), Ls ins 1..3, all_distinct(Ls).
false.
From this, we see that there is no admissible solution.
In the second case though, we get:
?- length(Ls, 4), Ls ins 1..5, all_distinct(Ls).
Ls = [_G3409, _G3412, _G3415, _G3418],
_G3409 in 1..5,
all_distinct([_G3409, _G3412, _G3415, _G3418]),
_G3412 in 1..5,
_G3415 in 1..5,
_G3418 in 1..5.
i.e., a residual program that is declaratively equivalent to the original query, and from which in this particular case we know that there is indeed a solution. (Note: This is possible here because all_distinct/1 implements domain consistency.)
Hence, in your rule validation, you can write code that uses CLP(FD) constraints to detect inconsistencies, which is typically much more efficient than naive approaches. I leave implementing this translation as an easy exercise.

printing business cards - a kind of knapsack task

I am new to Prolog and I have some probably simple issue with a piece of code. This is a real world problem that arose last Friday and believe me this is not a CS Homework.
We want to print business cards and these can only be printed in blocks of 900 cards (100 sheets with 9 cards per sheet). The cards for anybody should not be distributed over several blocks. People ordered different amount of cards, E.G:
% "braucht" is german and means "needs"
braucht(anton,400).
braucht(berta,200).
braucht(claudia,400).
braucht(dorothee,100).
braucht(edgar,200).
braucht(frank,400).
braucht(georg,100).
I put together the following definition to find an appropriate block of 900 business cards:
block(0,[]).
block(N,[H|T]) :-
braucht(H,Nh),
% \+(member(H,T)),
D is N - Nh,
D >= 0,
block(D,T).
This produces a nice list of blocks of people whose cards fit together on a 900 cards block. But it stops working if I activate the commented line "\+member...." and just gives me a "false". But I need to assure that nobody is getting more than once on that block. What am I doing wrong here?
It seems that what you want to achieve is to set a constraint that H does not appear in the tail T of the list. However, T is still unbound when you call member/2, so that member(H, T) will succeed and hence \+ member(H,T) will fail.
If you don't want to use Constraint Programming, but use pure Prolog instead, you should use the check in the other direction and check whether H is already present in the list of people that has been aggregated up to that point. Something like:
block(0, List, List).
block(N, Rest, List) :-
braucht(H, Nh),
\+(memberchk(H, Rest)), % will fail when H is already in Rest
D is N-Nh,
D >= 0,
block(D, [H|Rest], List).
The predicate block/3 can be called from a predicate block/2:
block(N, List) :-
block(N, [], List).
If the second argument in your block predicate is the "output", then your problem is that T is a free variable, so member(_,T) will always succeed. For instance:
?- member(anton,T).
T = [anton|_]
T = [_,anton|_]
T = [_,_,anton|_]
and so on...

Prolog query question

I am new to prolog, and I have to write a program about the water
jugs. My problem is regarding the initial state of jugs and the query
formation. The query will be of the form:
?- myPredicate(args), filled(j1,1)
Meaning j1 filled with 1 gallon of water. j1 represents one of the jugs; the other is j2. Initally, I have
filled(j1,0)
filled(j2,5)
capacity(j1,2)
capacity(j2,5)
I would really be grateful if you provide me with information regarding the following:
Question A: Do I have to declare initial state of the j1 inside my program? filled(j1,0)
Question B: I need to make my program find a solution for
filled(j1,1). For that I have some ideas, but what I am not sure
about, is how to update filled(J,Volume) from query and
myPredicate.
I am very confused since I have the initial state filled(j1,0) and now
I have to create a filled(j1,1) in myPredicate. So I should have some form of filled(J,Volume) in myPredicate, so the query returns true instead of false.
How do I incorporate filled(J,Voume) inside myPredicate so when the above query is run, I can show correct answer?
Example program with a passed-in parameter, an initial fact and an iterative task to perform. Iteration is by means of recursion. Before each reentry the value related to certain a parameter can be effectively updated for the next pass.
my_loop(N) :- N > 10.
my_loop(N) :- N =< 10,
write(N), nl,
Nextin is N + 1,
my_loop(Nextin).
:- my_loop(1).
A:
The given information (facts) are needed by
the program. They can be made available from keyboard input, as initial
arguments to some predicate, or as facts in the database as you
suggested. Also, only suggested for special situations, the facts
can be hard-coded into some predicate or rule.
Below and above are example of passing the initial information in as parameters: :- my_predicate(args...).
If there are a lot of facts, the database is best. A few facts that
need to change every time is best gotten from the keyboard. Otherwise,
it probably doesn't matter.
:- my_predicate([fill(j1,0),fill(j2,5)], Answer),
write(Answer),
nl.
B:
See the my_ loop example:
In my_loop, the task of counting [1..10] is solved iteratively.
The givens: 1 is passed in as a parameter, mainly because the
program does the same thing over-and-over:
1. take a number (N); quit if it is too big. Otherwise...
2. print it.
3. calculate the next number (N+1)
4. repeat
10 is hard-coded. It could have been a fact: stop_after(10).
Now the data to be manipulated, the variable N in my_loop, and
{ j1,j2 } in myPredicate doesn't actually need to be re-assigned over-and-over again: See my_loop. Just re-enter
the computation when it's time to do the same
thing again, but with different parameters:
cap(j1,2).
cap(j2,5).
my_predicate(Status, Answer) :-
got_juice(Status,0),
Answer=Status.
%%% Instead of changing values, rerun comp. with new values
%%% based on a computation made from the old ones.
my_predicate([filled(j1,J1), filled(j2,J2)], Answer) :-
Used is J1 + J2,
got_juice(Used, J), J \= 0,
cap(j1,C1), cap(C2),
%% Use cap and filled to add more to filled..
NextJ1 is J1 + ...,
NextJ2 is J2 + ...,
my_predicate(filled(j1,NextJ1), filled(..., Answer).
NOTE:
The predicate above is just to demonstrate iteration in Prolog, using the parameters of "myProgaram". For an actual implementation see the program suggested in the comment from matcheek.

Resources