Rule for amount of facts - prolog

For a school project I want to make a planning maker in Prolog. I want Prolog to make a planning for every day. On each day a certain amount of employees are needed and employees can only work on certain days. I want Prolog to make a planning that plans the right amount of people on each day. For that I wrote the following code:
workingday_employeesneeded(monday, 2).
workingday_employeesneeded(tuesday, 1).
workingday_employeesneeded(wednesday, 2).
employee_availability(tom, monday).
employee_availability(thomas, monday).
employee_availability(timme, monday).
employee_availability(timo, monday).
employee_availability(tom, tuesday).
planning(Employee, Day) :-
workingday_employeesneeded(Day, Amount),
employee_availability(Employee, Day).
planning(Employee, Day) :-
aggregate_all(count, planning(Employee, Day), Count),
workingday_employeesneeded(Day, Amount),
Count <= Amount.
However, I can't get Prolog to give me the right result, as I query the following Prolog gives me all the options, not regarding the amount of employees needed.
?- planning(X, Y).
X = tom,
Y = monday ;
X = thomas,
Y = monday ;
X = timme,
Y = monday ;
X = timo,
Y = monday ;
X = tom,
Y = tuesday ;
false.
Can you guy's see what I'm doing wrong? Thanks in advance!
EDIT:
I thought it might be handy to make a list of employees for each day in the planning. So I edited the code to the following (also fixing some syntax errors pointed out in the comments);
planning_on_day(Day, Employees) :-
workingday_employeesneeded(Day, Amount),
findall(E, employee_availability(E, Day), Employees),
length(Employees, Amount).
The following problem still exists; if there are more employees available than needed the program does not print the planning for that day instead of only picking the first N employees.
Do you guys have suggestions to fix that problem?

Simply your predicate fails because first you use findall/3 then you constraint the length of the list. For instance, for monday there are 4 employees available, you find all of them with findall/3 and store into Employees. Then you check the lenght of the list and it fails. To solve it you need find all the available employees and then find a subset of the list with desired length. So your code will be:
subset([], []).
subset([E|Tail], [E|NTail]):-
subset(Tail, NTail).
subset([_|Tail], NTail):-
subset(Tail, NTail).
planning_on_day(Day, Employees) :-
workingday_employeesneeded(Day, Amount),
findall(E, employee_availability(E, Day), E),
length(Employees,Amount),
subset(E,Employees).
?- planning_on_day(monday,P).
P = [tom, thomas]
P = [tom, timme]
P = [tom, timo]
P = [thomas, timme]
P = [thomas, timo]
P = [timme, timo]
false
?- planning_on_day(tuesday,P).
P = [tom]
false
?- planning_on_day(wednesday,P).
false
Then, if you want find the plan of the week you can add:
isDifferent(_, []).
isDifferent(X, [H | T]) :-
X \= H,
isDifferent(X, T).
allDifferent([]).
allDifferent([H | T]) :-
isDifferent(H, T),
allDifferent(T).
solve([],Plan,Plan):-
flatten(Plan,P),
allDifferent(P).
solve([Day|T],LT,Plan):-
workingday_employeesneeded(Day, Amount),
planning_on_day(Day,PlanD),
length(A,Amount),
subset(PlanD,A),
append(LT,[PlanD],LT1),
solve(T,LT1,Plan).
?- solve([monday,tuesday],[],L).
L = [[thomas, timme], [tom]]
L = [[thomas, timo], [tom]]
L = [[timme, timo], [tom]]

Related

why backtracking does / does not happen (ending search with false or not)?

This is a question asking why backtracking happens in one simple program and not another.
Example 1.
% water/2 relates temperature to state
water(Temp, solid) :- Temp =< 0.
water(Temp, liquid) :- Temp > 0, Temp < 100.
water(Temp, gas) :- Temp >= 100.
?- water(50,X).
X = liquid
false
Example 2.
% parent facts
parent(john, jane).
parent(john, james).
parent(sally, jane).
parent(martha, sally).
?- parent(X, jane).
X = john
X = sally
In example 1, prolog finds X=liquid as one solution, then prompts to search for more solutions. When none are found it returns false.
In example 2, prolog correctly finds X=john, prompts to continue the search, and then finds X=sally, but then does not prompt to continue searching. It does not finish with a false to indicate it tried the remaining rules (eg parent(martha,sally) the last rule) and failed.
Question: Why does the first example finish with a false, but the second does not?
Simply write a rule which disallows water to be in more than one state. There's several methods (mostly using e.g. cut), here's one:
water_temp_state(Temp, State) :-
( Temp =< 0 -> State = solid
; Temp < 100 -> State = liquid
; State = gas
).
Results:
?- water_temp_state(-5, S).
S = solid.
?- water_temp_state(5, S).
S = liquid.
?- water_temp_state(500, S).
S = gas.
Use multiple predicates when there is an actual possibility of multiple states.
Here's another method, just for fun:
water_state_comparison(solid, =<, 0).
water_state_comparison(liquid, <, 100).
water_state_comparison(gas, >=, 100).
water_temp_state_compare(Temp, State) :-
water_state_comparison(State, Comp, Num),
Pred =.. [Comp, Temp, Num],
call(Pred),
!.
... with same results.

Prolog beginner, combining rules question?

I'm trying to write a program thats based on these facts:
Boyle was born in 1627 and died in 1691.
Newton was born in 1642 and died in 1727.
(and so on)
I want to create a rule that determines if a person was alive during a specified year. Here is what I've come up with so far:
scientist(boyle, 1627, 1691).
scientist(newton, 1642, 1727).
alive_after(X) :- scientist(A, B, C), B < X.
alive_before(X) :- scientist(A, B, C), C > X.
alive_during(X, Year) :- alive_after(X), alive_before(X).
I believe that my first two rules are correct, but they don't seem to be working as intended when I combine them for my alive_during rule. When I run my code with this as my input:
alive_during(1628).
X = boyle
It doesn't work.
What am I missing here?
Prolog cannot unify variables that are hidden inside predicate bodies. There is no relationship between the A in alive_after/1 and the A in alive_before/1. Prolog actually told you that it didn't know what you were doing when it reported these warnings: `
|: alive_after(X) :- scientist(A, B, C), B < X.
Warning: user://2:19:
Singleton variables: [A,C]
|: alive_before(X) :- scientist(A, B, C), C > X.
Warning: user://2:23:
Singleton variables: [A,B]
|: alive_during(X, Year) :- alive_after(X), alive_before(X).
Warning: user://2:27:
Singleton variables: [Year]
It is extremely important that you read these messages as if they are errors, especially when you are new to Prolog!
The solution is to make it so that Prolog is able to unify the scientists across these predicates:
alive_after(Scientist, Year) :- scientist(Scientist, Birth, _), Year > Birth.
alive_before(Scientist, Year) :- scientist(Scientist, _, Death), Year < Death.
alive_during(Scientist, Year) :-
alive_before(Scientist, Year), alive_after(Scientist, Year).
You may also find that it is a little easier to follow the logic when you give your variables meaningful names. I am guilty of using extremely terse variable names when writing very general predicates, but these are actually very specific predicates and a good name can help you understand the structure of what you're doing. It would be, I think a little harder to see how this is more correct than what you wrote:
alive_after(A, X) :- scientist(A, B, _), X > B.
alive_before(A, X) :- scientist(A, _, C), X < C.
alive_during(A, X) :- alive_before(A, X), alive_after(A, X).
With better names, it is much easier to see why your original code is incorrect, because the scientist is not actually shared between the alive_before/2 and alive_after/2 calls.
Another hint that you were confused was that this response to a query makes no sense:
?- alive_during(1628).
X = boyle
Where did X come from? Variables get unified with values from the query, they do not arrive from inside predicate bodies.
An even more direct solution would be to use Prolog's built-in between/3 predicate:
alive_during(Scientist, Year) :-
scientist(Scientist, Birth, Death),
between(Birth, Death, Year).
This has the added advantage that it will actually generate solutions for you:
?- alive_during(boyle, X).
X = 1627 ;
X = 1628 ;
X = 1629 ;
X = 1630 ;
X = 1631 ;
The other solution does not have this property. There are interesting predicates you can write if you do have this generating property, such as contemporaries/2:
contemporaries(S1, S2) :-
alive_during(S1, Y),
alive_during(S2, Y),
dif(S1, S2).
Which generates a lot of not interesting copies of solutions, but you can get rid of them by using setof/3:
?- setof(X-Y, contemporaries(X, Y), Contemporaries).
Contemporaries = [boyle-newton, newton-boyle].

Rule and query Help in prolog

There are facts like :
student(ram, cs). // ram is student of cs branch
student(kiri,it).
student(akshay,cs).
student(sanjay,me).
I want to write a rule to find out the classmates in any branch AND a query to list out students in a branch say cs. Please help.
what query I had to run if i had to find classmates of akshay?
Two students are classmates if they are participating the same course.
classmates(X, Y) :- student(X, A), student(Y, A), X #< Y.
#</2 here is for suppressing duplicates. I.e it is enough to have only (A,B) without (B,A), (A,A) and (B,B).
?- classmates(X, Y).
X = akshay,
Y = ram ;
false.
To list out all students in a branch cs:
?- student(X, cs).
X = ram ;
X = akshay.
this being a follow up of this previous question, let's keep on the same mood...
classmates(Classmates) :-
aggregate(set(P), B^Q^(student(P,B), student(Q,B), P\=Q), Classmates).
yields
?- classmates(L).
L = [akshay, ram].

Prolog sum of numbers not in a list

In gnu Prolog I'm trying to collect the sum of all college credits in an College Adviser program.
Currently, it returns the number of credits like such:
| ?- totalCredits(joe, X).
X = 3 ? ;
X = 3 ? ;
X = 3 ? ;
X = 1 ? ;
X = 3 ? ;
X = 3 ? ;
no
And this is that code sample:
totalCredits(Student, Credits) :-
class(Class, _, _),
creditFor(Student, Class, _),
class(Class, _, Credits).
So my question is how can I sum up all of those results? I'm completely new to prolog and have only used functional languages before and so maybe I am missing something completely.
You can collect a list of credits and then sum it:
totalCredits(Student, Total) :-
findall(Credits, creditFor(Student, _Class, Credits), ListOfCredits),
sum_list(ListOfCredits, Total).
edit after comment, a correction: join the relations!
totalCredits(Student, Total) :-
findall(Credits,
(creditFor(Student, Class, _),
class(Class, _, Credits)), ListOfCredits),
sum_list(ListOfCredits, Total).
Use findall to get all the credits, then sum those.
findall(C, creditFor(Student, _, C), Credits),
sum(Credits, Sum).
(I hope I've understood the meaning of your creditFor predicate correctly.)
Here, sum is a summation predicate that I'll leave to you; it should be very similar to its functional counterpart.

Prolog query in code

How do you execute a query in the code?
For example:
person(abe,instructor).
person(bob,student).
person(cindy,student).
person(david,student).
person(abe,student).
% Like this, but this does not work
% :- person(X,Y).
After loading the program, I can run the following query: person(X,Y).
How can I run this query as part of the program itself so once the program loads, it will run the query and output:
X = abe,
Y = instructor ;
X = bob,
Y = student ;
X = cindy,
Y = student ;
X = david,
Y = student ;
X = abe,
Y = student.
You could just create a new predicate.. 2 different ways here. The first finds all person(X,Y), puts them into a list AllPeople, then writes it out.
The second is a 'failure driven loop' which does the first match, writes it out, then tells prolog to try again ie' fail', which continues until there are no more matches, and then matches the second predicate of the same name, to ensure that the predicate finally returns true.
showpeople1 :-
findall(X/Y, person(X,Y), AllPeople),
write(AllPeople).
showpeople2 :-
person(X, Y),
write(X), write(','), write(Y), nl,
fail.
showpeople2 :- true.
?- showpeople1.
[abe/instructor,bob/student,cindy/student,david/student,abe/student]
true.
?- showpeople2.
abe,instructor
bob,student
cindy,student
david,student
abe,student
true.

Resources