Checking List in Prolog - prolog

I'm trying to do a schedule that has constraints in Prolog. Scheduling will be based on two constraints. The courses of the same semester and the courses taught by the same instructor cannot be scheduled to the same time slot.
course(ceng123,1).
Course code and semester of course.
slot(monday,1).
Day and hour on day.
teaches(jack,ceng123).
Teacher and course code.
course(cse111,1).
course(cse112,1).
course(cse113,1).
course(cse114,1).
course(cse115,2).
course(cse116,2).
course(cse117,2).
course(cse118,2).
slot(monday,1).
slot(monday,2).
slot(tuesday,1).
slot(tuesday,2).
teaches(erkan,cse111).
teaches(erkan,cse112).
teaches(erkan,cse113).
teaches(erkan,cse114).
teaches(merkan,cse115).
teaches(merkan,cse116).
teaches(kan,cse117).
teaches(kan,cse118).
The answer that I expect is:
?- schedule([cse111,cse112,cse113,cse114,cse115,cse116,cse117,cse118],X).
X = [cse111, monday, 1, cse112, monday, 2, cse113, tuesday, 1, cse114, tuesday, 2, cse115, monday, 1, cse116, monday, 2, cse117, tuesday, 1, cse118, tuesday, 2]
I wrote a code that has no constraints:
course(cse111,1).
course(cse112,1).
course(cse113,1).
course(cse114,1).
course(cse115,2).
course(cse116,2).
course(cse117,2).
course(cse118,2).
slot(monday,1).
slot(monday,2).
slot(tuesday,1).
slot(tuesday,2).
teaches(erkan,cse111).
teaches(erkan,cse112).
teaches(erkan,cse113).
teaches(erkan,cse114).
teaches(merkan,cse115).
teaches(merkan,cse116).
teaches(kan,cse117).
teaches(kan,cse118).
schedule([],[]).
schedule([Course|CourseTail],[Course,Day,Slot|ScheduleTail]):-
slot(Day,Slot),schedule(CourseTail,ScheduleTail).
There is no problem, but when i try this;
course(cse111,1).
course(cse112,1).
course(cse113,1).
course(cse114,1).
course(cse115,2).
course(cse116,2).
course(cse117,2).
course(cse118,2).
slot(monday,1).
slot(monday,2).
slot(tuesday,1).
slot(tuesday,2).
teaches(erkan,cse111).
teaches(erkan,cse112).
teaches(erkan,cse113).
teaches(erkan,cse114).
teaches(merkan,cse115).
teaches(merkan,cse116).
teaches(kan,cse117).
teaches(kan,cse118).
schedule([],[]).
schedule([Course|CourseTail],[Course,Day,Slot|ScheduleTail]):-
schedule(CourseTail,ScheduleTail), check(Course,Day,Slot,ScheduleTail).
check(_, _, _,[]).
check(Course,Day,Slot,[Course2,Day2,Slot2|Tail]):- check(Course,Day,Slot,Tail),
course(Course,Semester1),course(Course2,Semester2),Semester1=\=Semester2,
slot(Day,Slot),slot(Day2,Slot2).
I tried to write constraint but I took an error.
uncaught exception: error(syntax_error('user_input:1 (char:4) . or operator expected after expression'),read_term/3)
Can you see the mistake?

Singleton variable is a variable which is mentioned only once in the program: See wiki. You get that for line 27 which I presume is this one: check(Course,Day,Slot,[]). You can replace it with check(_, _, _,[]). ('_' means any variable. This means you can universally quantify the variables.)
You do not get an error. Prolog saying no just means that your constraints cannot be satisfied.

You should start by clearly defining your constraints.
From your comments, I read:
Two courses in an hour cannot have same semester number and same teacher's courses will not be in same hour.
Therefore you can assign a schedule to a course when it fulfills those requirements, hinting that you have to know which assignments have been already issued. So you can create a procedure that keeps the current schedule and only add a new assignment when your requirements are met.
So, start by defining your schedule/2 procedure that calls a new procedure schedule/3 with an empty list:
schedule(Courses,Schedule):-
schedule(Courses, [], Schedule).
Now, this procedure has on the first argument the list of courses to assign a schedule, keeps the current assignments con the second argument and unifies the third argument with the final Schedule (with the data you require, that is Course, Day and Slot).
I'll use a structure to represent each assignment schedule(Course, Day, Slot) because just mixing those datum in a list is not a good idea.
schedule([], _, []).
schedule([Course|CourseTail], Courses, [schedule(Course,Day,Slot)|ScheduleTail]):-
% Two courses in an hour cannot have same semester number and same teacher's courses will not be in same hour.
course(Course, Semester),
teaches(Teacher, Course),
slot(Day, Slot),
\+ member(s(Day, Slot, Semester, _), Courses),
\+ member(s(Day, Slot, _, Teacher), Courses),
schedule(CourseTail, [s(Day, Slot, Semester, Teacher)|Courses], ScheduleTail).
First clause is the base case. It states that when there are no more courses in the input list, then the schedule will be empty.
Second clause takes the first Course from the input list and computes a possible Semester, Theacher and Day/Slot for that course, and then just checks if the constraints are met. It tests whether there is already a schedule for that Day/Slot/Semester and whether there is already an assignment for the Teacher at that Day/Slot. If the requirements are met then we add this assignment to the list of current assignments and continue recursion.
Upon return, we add the Course/Day/Slot to the list of the final Schedule.

Related

Having trouble with Prolog

I am trying to implement a Prolog task in order to learn it:
num_cred(X,Y): this function should take a list of course
abbreviations as input (X), and output the number of credits
obtained when a student attended these courses (Y).
courses(X,Y): this function should take a student as input (X), and
output a list of all courses this student attended (Y).
pass2(X): this function should take a student as input, and output
true if the student has 30 or more ECTS based on num_cred(X).
fail2(X): this function should take a student as input, and output
true if the student has less than 30 ECTS based on num_cred(X).
Terminal input-output pairs examples:
Input Output
num_cred([pai,qf,d],X). 30
courses(alice,X). [pai,qf,d]
pass2(X). alice
fail2(X). bob
This is what I've done until now:
num_cred([], 0).
num_cred([H|T], Sum) :- num_cred(T, Rest), course(H, P, C), Sum is C + Rest.
courses(X,Y).
pass2(X).
fail2(X).
/*Level 2 tests*/
course(pai,inf,10).
course(qf,eco,10).
course(d,law,10).
student(alice,ss).
attend(alice,pai).
attend(alice,qf).
attend(alice,d).
student(bob,ss).
attend(bob,pai).
Do you have any idea why my num_cred rule don't work? When I test it with num_cred([qf,pai],0). I get: Arguments are not sufficiently instantiated.
Any help/ hints with these rules will be much appreciated.
Thanks a lot.

Prolog return list of free days

I have a question (again :)). let assume we have the following data:
the first number is the day second the month and third the event
day(1,1, 'New Year')
day(1,1, 'The day after the new year')
day(23,1, 'The day i will finally understand this language :)')
day(14,2, 'Valentin's day')
day(16,2, 'Family day')
day(22,2, 'hein.. dont now lol')
nday(1,31).
nday(2,28).
nday(3,31).
nday(4,30).
nday(5,31).
I'm asked to create a predicate such that given a Day, it returns a month (only if the given day do not have event. for instance freeDay(23,X). X should have the value 2. if X equals 4, it should holds 1 (and if we enter the semi colon, it will return 2 since day 4 doesn't have any event in February as well. i have many more data. So i did the following but i get the value 0.
freeDay(_, Month,0):- Month > 12, !.
freeDay(X,Answer):- freeDay(X, 1, Answer).
freeDay(Day, Month,X):-
day(Day, Month,_),
W is Month + 1,
freeDay(Day, W, X).
freeDay(Day, Month, X):-
W is Month +1,
freeDay(Day,W, X).
Can you tell me what i did wrong please??
What your code does:
By calling freeDay(23,X), freeDay(X,Answer) succeds which calls the freeDay(X, 1, Answer).
Prolog first checks which of the predicates can be executed with the current inputs.
It finds out that freeDay(_, Month,0):- Month > 12, !. is not applicable since the month is 1 and it moves to the next predicate.
freeDay(Day, Month,X) is applicable, and what it does is increasing the month and calling itself(recursion).
The month keeps increasing when finally is more than 12. Remember everytime a predicate is called prolog checks the first predicate that it is applicable. Therefore this time freeDay(_, Month,0):- Month > 12, ! succeeds.
freeDay(_, Month,0):- Month > 12, !. breaks the loop (recursion) by the use of the cut annotation ! and returns the Answer which is 0 in this case. (In plain english here you say no matter what day is if month is greater than 12 return 0. This is the last statement executed.)
I hope you understand what's wrong. Generally logic programming requires thinking outside of the box. You need first to understand the problem and then attempt to solve it, in a simplistic way.

Prolog return date and event associated with it

So hello guys, i have been working on this question for hours, but i can solve it, if you guys can give me hints that would be great!
The problem is long but i believe from what I can see that the solution won't be more than 5 lines.
So I'm given a bunch of Data, lets say:
day(1,1, 'New Year')
day(2,1, 'The day after the new year')
day(23,1, 'The day i will finally understand this language :)')
day(14,2, 'Valentin's day')
day(16,2, 'Family day')
day(22,2, 'hein.. dont now lol')
So the first number is the day, second month and third event, and
so on for the other months. each month also have a number a days:
nday(1,31).
nday(2,28).
nday(3,31).
nday(4,30).
nday(5,31).
If i enter DayofTheMonth(1,L), such that L is a List and 1 January,
i should return a list with all the days in that month and the events as well .
In this case it should be: [(1,'New year', (2,'The day after the new year'),
(23, 'The day i will finally understand this language :)')]. this is what i did: (i don't even know the basic case smh) I'm so sorry i know
this solution is wrong it doesn't even handle days that don't have event,
but i just don't know where to go, i just feed a feedback or something
dayoftheMonth(X, L):- nday(X,Answer),
dayMonth(X, Answer, L).
dayMonth(X,Y,[_|B]):-
X=<Y,
day(Day, X, Event),
W is Day + 1,
dayMonth(X,W,[(Day,Event)|B]).
You can use the predicate findall/3 for that. The predicate has three arguments:
The template, the format of the output
The goal, the predicate you call that should be matched
The bag, the returning list of predicates.
So dayofTheMonth simply reads:
dayofTheMonth(Month,List) :-
findall((Day,Event),day(Month,Day,Event),List).
If you want to solve it without a findall, you can solve it by iterating over all the days until the number of days. The basecase is thus when the day has passed the number of days of the month.
dayofTheMonth(Month,Day,[]) :-
nday(Month,Threshold),
Day > Threshold,
!.
The inductive case always looks if there is an event planned that day, if so, you put it as head of the list and append it with the events on the next day, if there is no such event, the resulting list is the list of the next days. So:
dayofTheMonth(Month,Day,[(Day,Event)|T]) :-
day(Day,Month,Event),
!,
Day1 is Day+1,
dayofTheMonth(Month,Day1,T).
dayofTheMonth(Month,Day,T) :-
Day1 is Day+1,
dayofTheMonth(Month,Day1,T).
Finally you only need to link the predicate dayOfTheMonth/2 with dayOfTheMonth/3 by starting - evidently - by day 1.
dayofTheMonth(Month,Events) :-
dayofTheMonth(Month,1,Events).
So the full code reads:
day(1,1, 'New Year').
day(2,1, 'The day after the new year').
day(23,1, 'The day i will finally understand this language :)').
day(14,2, 'Valentins day').
day(16,2, 'Family day').
day(22,2, 'hein.. dont now lol').
nday(1,31).
nday(2,28).
nday(3,31).
nday(4,30).
nday(5,31).
dayofTheMonth(Month,Day,[]) :-
nday(Month,Threshold),
Day > Threshold,
!.
dayofTheMonth(Month,Day,[(Day,Event)|T]) :-
day(Day,Month,Event),
!,
Day1 is Day+1,
dayofTheMonth(Month,Day1,T).
dayofTheMonth(Month,Day,T) :-
Day1 is Day+1,
dayofTheMonth(Month,Day1,T).
dayofTheMonth(Month,Events) :-
dayofTheMonth(Month,1,Events).

Finding all solutions in prolog

In prolog I'm trying to unify every valid pairing of needs with resources
needs([ece2090,1,m,13,16]).
needs([ece3520,1,tu,11,14]).
needs([ece4420,1,w,13,16]).
resources([joel, [ece2090,ece2010,ece3520,ece4420],[[m,13,16]]]).
resources([sam, [ece2010,ece4420],[]]).
resources([pete, [ece3520],[[w,13,16]]]).
using this formula
make_bid([Class,Sect,Day,Ts,Te],[Name,Cap,Unavail],[Class,Sect,Day,Ts,Te,Name,_]) :-
no_conflict_all_unavailable(Day,Ts,Te,Unavail),
course_capable(Class,Cap),
writef('%w %w %w\n',[Class,Sect,Name]),
fail.
and running this test.
test(Listing) :- needs(N), resources(R), make_bid(N,R,Listing).
The point of this part of the program is to pair every class with a teacher that both has the qualifications to teach the class and is not unavailable during that time. It's supposed to give a list.
?- test(Listing).
ece3520 1 joel
ece3520 1 pete
ece4420 1 joel
ece4420 1 sam
false.
When run, the above is generated. This is correct, but it's in a format that's useless to me, since I need it to be a variable of its own to do further computations. Then the solution is to use bagof or findall, right?
So I remove the fail clause from the main part of the program and then change the test to this
test(Bag) :- needs(N), resources(R), bagof(Listing,make_bid(N,R,Listing),Bag).
but it generates this
ece3520 1 joel
Bag = [[ece3520, 1, tu, 11, 14, joel, _G4310]]
If you look closely you'll see that there's no period at the end as well as a lack of a true/false statement. This would lead one to believe it is infinitely looping. This isn't the case however, as the Bag matrix is fully formed and I can simply type "." to end the program (instead of, you know, aborting it).
It only generates the first valid solution. Why is this happening?
You've structured your test predicate so that bagof/3 is called for every instance combination of needs(N) and resources(R) and so it collects each result of make_bid in it's own bagof/3 result:
ece3520 1 joel
Bag = [[ece3520, 1, tu, 11, 14, joel, _G4310]]
The first line is the write that is in make_bid predicate. The second line is the Bag result for the single query to make_bid for one pair of needs/resources. The last argument in the list, _G4310, occurs because your predicate uses _ and it's anonymous (never used/instantiated).
Your current make_bid is designed to write the results in a loop rather than instantiate them in multiple backtracks. So that could be changed to:
make_bid([Class, Sect, Day, Ts, Te], [Name, Cap, Unavail], [Class, Sect, Day, Ts, Te, Name, _]) :-
no_conflict_all_unavailable(Day, Ts, Te, Unavail),
course_capable(Class, Cap).
(NOTE: I'm not sure why you have _ at the end of the 3rd list argument. What does it represent?)
If you want to collect the whole result in one list, then you canb use findall/3:
findall([Class, Sect, Name], (needs(N), resources(R), make_bid(N, R, [Class, Sect, _, _, _, Name, _]), Listings).
This will collect a list of elements that look like, [Class, Sect, Name]. You could use bagof/3 here, but you'd need an existential quantifier for the variables in the make_bid/3 call that you don't want to bind.
If you wanted the entire Listing list, then:
findall(L, (needs(N), resources(R), make_bid(N, R, L)), Listings).
But each element of Listings will be a list whose last element is an anonymous variable, since that's how make_bid/3 is structured.

setof/3 and NAF

so I have a set of facts:
course(cs61, "intro to cs")
...
course(cs62b, "data structure")
...
grade(adam, cs61, spring11, "A")
...
grade(chrisitian, cs61, fall2010, "A-")
I need to define a predicate good_standing(Student) to determine whether the Student got A in every class he took. I must use 2 different approaches:
use setof/3
use NAF
on the 1st. I have tried to figure out get Result_list: the set of students that got A from every class he took. and then call member(Student, Result_list). But I don't know what to get the Result_list since the syntax is a bit strange to me.
1) For the NAF solution you should keep in mind that the good-standing student is the one with no grades lower than A, i.e., the one such that there is no course he/she took and the grade is lower than A. We also require this person to take at least one course.
good_standing(Student) :- grade(Student, _,_,_),
\+ (grade(Student, Course, _, Grade),
lower(Grade,"A")).
with lower/2 defining relation between the grades:
lower("A-","A").
lower("B","A").
...
The solution above works if students get only one grade per course.
2) For the setof solution you can find all grades per student and check that they are A.
grade(Student, Grade) :- grade(Student, _,_,Grade).
good_standing(Student) :- setof(Grade, grade(Student,Grade), ["A"]).
Alternatively, as suggested by false we can make the existential quantification explicit within setof:
good_standing(Student) :- setof(Grade,
Course^Period^grade(Student,Course,Period,Grade),
["A"]).

Resources