I’m trying to create a simple scheduler in Prolog that takes a bunch of courses along with the semesters they’re offered and a user’s ranking of the courses. These inputs get turned into facts like
course('CS 4812','Quantum Information Processing',1.0882353,s2012).
course('Math 6110','Real Analysis I',0.5441176,f2011).
where the third entry is a score. Currently, my database is around 60 classes, but I’d like the program to eventually be able to handle more. I’m having trouble getting my DP implementation to work on a nontrivial input. The answers are correct, but the time spent is on the same order as the brute force algorithm. I handle memoization with a dynamic predicate:
:- dynamic(stored/6).
memo(Result,Schedule,F11,S12,F12,S13) :-
stored(Result,Schedule,F11,S12,F12,S13) -> true;
dpScheduler(Result,Schedule,F11,S12,F12,S13),
assertz(stored(Result,Scheduler,F11,S12,F12,S13)).
The arguments to dpScheduler are the optimal schedule (a tuple of a list of classes and its score), the classes chosen so far, and how many classes are remaining to be chosen for the Fall 2011, Spring 2012, Fall 2012, and Spring 2013 semesters. Once the scheduler has a compete schedule, it gets the score with evalSchedule, which just sums up the scores of the classes.
dpScheduler((Acc,X),Acc,0,0,0,0) :-
!, evalSchedule(X,Acc).
I broke up dpScheduler up for each semester, but they all pretty much look the same. Here is the clause for Fall 2011, the first semester chosen.
dpScheduler(Answer,Acc,N,B,C,D) :-
!, M is N - 1,
getCourses(Courses,f2011,Acc),
lemma([Head|Tail],Courses,Acc,M,B,C,D),
findBest(Answer,Tail,Head).
The lemma predicate computes all the subgoals.
lemma(Results,Courses,Acc,F11,S12,F12,S13) :-
findall(Result,
(member(Course,Courses), memo(Result,[Course|Acc],F11,S12,F12,S13)),
Results).
My performance has been horrendous, and I’d be grateful for any pointers on how to improve it. Also, I’m a new Prolog programmer, and I haven’t spent much time reading others’ Prolog code, so my program is probably unidiomatic. Any advice on that would be much appreciated as well.
There are a couple of reasons for bad performance:
First of all, assert/3 is not very fast so you spend a lot of time there if there are a lot of asserts.
Then, prolog uses a hash table based on the first argument to match clauses. In your case, yhe first argument is the Result which is uninstantiated when it’s called so I think you would have a performance penalty because of that. You could solve this be reordering the arguments. I thought that you could change the argument on which the hash table is based but i dont see how in the swi-prolog manual :/
Also, prolog isnt really renowned for great performance xd
I suggest to use XSB (if possible) which offers automatic memoization (tabling); you simply write
:-table(my_predicate/42) and it takes care of everything. I think that it’s a bit faster than swipl too.
Other than that, you could try to use a list with all the calculated values and pass it around; maybe an association list.
edit: i dont really see where you call the memoization predicate