| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Scheduler.h | 
					
						
							|  |  |  |  * @brief an example how inference can be used for scheduling qualifiers | 
					
						
							|  |  |  |  * @date Mar 26, 2011 | 
					
						
							|  |  |  |  * @author Frank Dellaert | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-15 08:47:19 +08:00
										 |  |  | #include <gtsam_unstable/discrete/Scheduler.h>
 | 
					
						
							| 
									
										
										
										
											2012-04-16 07:12:17 +08:00
										 |  |  | #include <gtsam/discrete/DiscreteFactorGraph.h>
 | 
					
						
							|  |  |  | #include <gtsam/discrete/DiscreteSequentialSolver.h>
 | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | #include <gtsam/base/debug.h>
 | 
					
						
							|  |  |  | #include <gtsam/base/timing.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <boost/tokenizer.hpp>
 | 
					
						
							|  |  |  | #include <boost/foreach.hpp>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <fstream>
 | 
					
						
							|  |  |  | #include <iomanip>
 | 
					
						
							|  |  |  | #include <cmath>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace gtsam { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	using namespace std; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Scheduler::Scheduler(size_t maxNrStudents, const string& filename): | 
					
						
							|  |  |  | 			maxNrStudents_(maxNrStudents) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		typedef boost::tokenizer<boost::escaped_list_separator<char> > Tokenizer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// open file
 | 
					
						
							|  |  |  | 		ifstream is(filename.c_str()); | 
					
						
							| 
									
										
										
										
											2012-05-15 09:01:31 +08:00
										 |  |  | 		if (!is) { | 
					
						
							|  |  |  | 			cerr << "Scheduler: could not open file " << filename << endl; | 
					
						
							|  |  |  | 			throw runtime_error("Scheduler: could not open file " + filename); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		string line; // buffer
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// process first line with faculty
 | 
					
						
							|  |  |  | 		if (getline(is, line, '\r')) { | 
					
						
							|  |  |  | 			Tokenizer tok(line); | 
					
						
							|  |  |  | 			Tokenizer::iterator it = tok.begin(); | 
					
						
							|  |  |  | 			for (++it; it != tok.end(); ++it) | 
					
						
							|  |  |  | 				addFaculty(*it); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// for all remaining lines
 | 
					
						
							|  |  |  | 		size_t count = 0; | 
					
						
							|  |  |  | 		while (getline(is, line, '\r')) { | 
					
						
							|  |  |  | 			if (count++ > 100) throw runtime_error("reached 100 lines, exiting"); | 
					
						
							|  |  |  | 			Tokenizer tok(line); | 
					
						
							|  |  |  | 			Tokenizer::iterator it = tok.begin(); | 
					
						
							|  |  |  | 			addSlot(*it++); // add slot
 | 
					
						
							|  |  |  | 			// add availability
 | 
					
						
							|  |  |  | 			for (; it != tok.end(); ++it) | 
					
						
							|  |  |  | 				available_ += (it->empty()) ? "0 " : "1 "; | 
					
						
							|  |  |  | 			available_ += '\n'; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} // constructor
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** addStudent has to be called after adding slots and faculty */ | 
					
						
							|  |  |  | 	void Scheduler::addStudent(const string& studentName, | 
					
						
							|  |  |  | 			const string& area1, const string& area2, | 
					
						
							|  |  |  | 			const string& area3, const string& advisor) { | 
					
						
							|  |  |  | 		assert(nrStudents()<maxNrStudents_); | 
					
						
							|  |  |  | 		assert(facultyInArea_.count(area1)); | 
					
						
							|  |  |  | 		assert(facultyInArea_.count(area2)); | 
					
						
							|  |  |  | 		assert(facultyInArea_.count(area3)); | 
					
						
							|  |  |  | 		size_t advisorIndex = facultyIndex_[advisor]; | 
					
						
							|  |  |  | 		Student student(nrFaculty(), advisorIndex); | 
					
						
							|  |  |  | 		student.name_ = studentName; | 
					
						
							|  |  |  | 		// We fix the ordering by assigning a higher index to the student
 | 
					
						
							|  |  |  | 		// and numbering the areas lower
 | 
					
						
							|  |  |  | 		Index j = 3*maxNrStudents_ + nrStudents(); | 
					
						
							|  |  |  | 		student.key_ = DiscreteKey(j, nrTimeSlots()); | 
					
						
							|  |  |  | 		Index base = 3*nrStudents(); | 
					
						
							|  |  |  | 		student.keys_[0] = DiscreteKey(base+0, nrFaculty()); | 
					
						
							|  |  |  | 		student.keys_[1] = DiscreteKey(base+1, nrFaculty()); | 
					
						
							|  |  |  | 		student.keys_[2] = DiscreteKey(base+2, nrFaculty()); | 
					
						
							|  |  |  | 		student.areaName_[0] = area1; | 
					
						
							|  |  |  | 		student.areaName_[1] = area2; | 
					
						
							|  |  |  | 		student.areaName_[2] = area3; | 
					
						
							|  |  |  | 		students_.push_back(student); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** get key for student and area, 0 is time slot itself */ | 
					
						
							|  |  |  | 	const DiscreteKey& Scheduler::key(size_t s, boost::optional<size_t> area) const { | 
					
						
							|  |  |  | 		return area ? students_[s].keys_[*area] : students_[s].key_; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const string& Scheduler::studentName(size_t i) const { | 
					
						
							|  |  |  | 		assert(i<nrStudents()); | 
					
						
							|  |  |  | 		return students_[i].name_; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const DiscreteKey& Scheduler::studentKey(size_t i) const { | 
					
						
							|  |  |  | 		assert(i<nrStudents()); | 
					
						
							|  |  |  | 		return students_[i].key_; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const string& Scheduler::studentArea(size_t i, size_t area) const { | 
					
						
							|  |  |  | 		assert(i<nrStudents()); | 
					
						
							|  |  |  | 		return students_[i].areaName_[area]; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** Add student-specific constraints to the graph */ | 
					
						
							|  |  |  | 	void Scheduler::addStudentSpecificConstraints(size_t i, boost::optional<size_t> slot) { | 
					
						
							|  |  |  | 		bool debug = ISDEBUG("Scheduler::buildGraph"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		assert(i<nrStudents()); | 
					
						
							|  |  |  | 		const Student& s = students_[i]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!slot && !slotsAvailable_.empty()) { | 
					
						
							|  |  |  | 			if (debug) cout << "Adding availability of slots" << endl; | 
					
						
							| 
									
										
										
										
											2012-05-15 17:51:26 +08:00
										 |  |  | 			CSP::add(s.key_, slotsAvailable_); | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// For all areas
 | 
					
						
							|  |  |  | 		for (size_t area = 0; area < 3; area++) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			DiscreteKey areaKey = s.keys_[area]; | 
					
						
							|  |  |  | 			const string& areaName = s.areaName_[area]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (debug) cout << "Area constraints " << areaName << endl; | 
					
						
							| 
									
										
										
										
											2012-05-15 17:51:26 +08:00
										 |  |  | 			CSP::add(areaKey, facultyInArea_[areaName]); | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if (debug) cout << "Advisor constraint " << areaName << endl; | 
					
						
							| 
									
										
										
										
											2012-05-15 17:51:26 +08:00
										 |  |  | 			CSP::add(areaKey, s.advisor_); | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if (debug) cout << "Availability of faculty " << areaName << endl; | 
					
						
							|  |  |  | 			if (slot) { | 
					
						
							|  |  |  | 				// get all constraints then specialize to slot
 | 
					
						
							|  |  |  | 				DiscreteKey dummy(0, nrTimeSlots()); | 
					
						
							|  |  |  | 				Potentials::ADT p(dummy & areaKey, available_); | 
					
						
							|  |  |  | 				Potentials::ADT q = p.choose(0, *slot); | 
					
						
							| 
									
										
										
										
											2012-05-25 23:10:13 +08:00
										 |  |  | 				DiscreteFactor::shared_ptr f(new DecisionTreeFactor(areaKey, q)); | 
					
						
							| 
									
										
										
										
											2012-05-15 17:51:26 +08:00
										 |  |  | 				CSP::push_back(f); | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2012-05-15 17:51:26 +08:00
										 |  |  | 				CSP::add(s.key_, areaKey, available_); | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// add mutex
 | 
					
						
							|  |  |  | 		if (debug) cout << "Mutex for faculty" << endl; | 
					
						
							|  |  |  | 		addAllDiff(s.keys_[0] & s.keys_[1] & s.keys_[2]); | 
					
						
							| 
									
										
										
										
											2012-05-15 17:51:26 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** Main routine that builds factor graph */ | 
					
						
							|  |  |  | 	void Scheduler::buildGraph(size_t mutexBound) { | 
					
						
							|  |  |  | 		bool debug = ISDEBUG("Scheduler::buildGraph"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (debug) cout << "Adding student-specific constraints" << endl; | 
					
						
							|  |  |  | 		for (size_t i = 0; i < nrStudents(); i++) | 
					
						
							|  |  |  | 			addStudentSpecificConstraints(i); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// special constraint for MN
 | 
					
						
							| 
									
										
										
										
											2012-05-15 17:51:26 +08:00
										 |  |  | 		if (studentName(0) == "Michael N") CSP::add(studentKey(0), | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | 				"0 0 0 0  1 1 1 1  1 1 1 1  1 1 1 1  1 1 1 1"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!mutexBound) { | 
					
						
							|  |  |  | 			DiscreteKeys dkeys; | 
					
						
							|  |  |  | 			BOOST_FOREACH(const Student& s, students_) | 
					
						
							|  |  |  | 				dkeys.push_back(s.key_); | 
					
						
							|  |  |  | 			addAllDiff(dkeys); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			if (debug) cout << "Mutex for Students" << endl; | 
					
						
							|  |  |  | 			for (size_t i1 = 0; i1 < nrStudents(); i1++) { | 
					
						
							|  |  |  | 				// if mutexBound=1, we only mutex with next student
 | 
					
						
							|  |  |  | 				size_t bound = min((i1 + 1 + mutexBound), nrStudents()); | 
					
						
							|  |  |  | 				for (size_t i2 = i1 + 1; i2 < bound; i2++) { | 
					
						
							|  |  |  | 					addAllDiff(studentKey(i1), studentKey(i2)); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} // buildGraph
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** print */ | 
					
						
							|  |  |  | 	void Scheduler::print(const string& s) const { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		cout << s << " Faculty:" << endl; | 
					
						
							|  |  |  | 		BOOST_FOREACH(const string& name, facultyName_) | 
					
						
							|  |  |  | 						cout << name << '\n'; | 
					
						
							|  |  |  | 		cout << endl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		cout << s << " Slots:\n"; | 
					
						
							|  |  |  | 		size_t i = 0; | 
					
						
							|  |  |  | 		BOOST_FOREACH(const string& name, slotName_) | 
					
						
							|  |  |  | 						cout << i++ << " " << name << endl; | 
					
						
							|  |  |  | 		cout << endl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		cout << "Availability:\n" << available_ << '\n'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		cout << s << " Area constraints:\n"; | 
					
						
							|  |  |  | 		BOOST_FOREACH(const FacultyInArea::value_type& it, facultyInArea_) | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						cout << setw(12) << it.first << ": "; | 
					
						
							|  |  |  | 						BOOST_FOREACH(double v, it.second) | 
					
						
							|  |  |  | 										cout << v << " "; | 
					
						
							|  |  |  | 						cout << '\n'; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 		cout << endl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		cout << s << " Students:\n"; | 
					
						
							|  |  |  | 		BOOST_FOREACH (const Student& student, students_) | 
					
						
							|  |  |  | 						student.print(); | 
					
						
							|  |  |  | 		cout << endl; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-15 17:51:26 +08:00
										 |  |  | 		CSP::print(s + " Factor graph"); | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | 		cout << endl; | 
					
						
							|  |  |  | 	} // print
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** Print readable form of assignment */ | 
					
						
							|  |  |  | 	void Scheduler::printAssignment(sharedValues assignment) const { | 
					
						
							|  |  |  | 		// Not intended to be general! Assumes very particular ordering !
 | 
					
						
							|  |  |  | 		cout << endl; | 
					
						
							|  |  |  | 		for (size_t s = 0; s < nrStudents(); s++) { | 
					
						
							|  |  |  | 			Index j = 3*maxNrStudents_ + s; | 
					
						
							|  |  |  | 			size_t slot = assignment->at(j); | 
					
						
							|  |  |  | 			cout << studentName(s) << " slot: " << slotName_[slot] << endl; | 
					
						
							|  |  |  | 			Index base = 3*s; | 
					
						
							|  |  |  | 			for (size_t area = 0; area < 3; area++) { | 
					
						
							|  |  |  | 				size_t faculty = assignment->at(base+area); | 
					
						
							|  |  |  | 				cout << setw(12) << studentArea(s,area) << ": " << facultyName_[faculty] | 
					
						
							|  |  |  | 						<< endl; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			cout << endl; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** Special print for single-student case */ | 
					
						
							|  |  |  | 	void Scheduler::printSpecial(sharedValues assignment) const { | 
					
						
							|  |  |  | 		Values::const_iterator it = assignment->begin(); | 
					
						
							|  |  |  | 		for (size_t area = 0; area < 3; area++, it++) { | 
					
						
							|  |  |  | 			size_t f = it->second; | 
					
						
							|  |  |  | 			cout << setw(12) << it->first << ": " << facultyName_[f] << endl; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		cout << endl; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** Accumulate faculty stats */ | 
					
						
							|  |  |  | 	void Scheduler::accumulateStats(sharedValues assignment, vector< | 
					
						
							|  |  |  | 			size_t>& stats) const { | 
					
						
							|  |  |  | 		for (size_t s = 0; s < nrStudents(); s++) { | 
					
						
							|  |  |  | 			Index base = 3*s; | 
					
						
							|  |  |  | 			for (size_t area = 0; area < 3; area++) { | 
					
						
							|  |  |  | 				size_t f = assignment->at(base+area); | 
					
						
							|  |  |  | 				assert(f<stats.size()); | 
					
						
							|  |  |  | 				stats[f]++; | 
					
						
							|  |  |  | 			} // area
 | 
					
						
							|  |  |  | 		} // s
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** Eliminate, return a Bayes net */ | 
					
						
							|  |  |  | 	DiscreteBayesNet::shared_ptr Scheduler::eliminate() const { | 
					
						
							|  |  |  | 		tic(1, "my_solver"); | 
					
						
							|  |  |  | 		DiscreteSequentialSolver solver(*this); | 
					
						
							|  |  |  | 		toc(1, "my_solver"); | 
					
						
							|  |  |  | 		tic(2, "my_eliminate"); | 
					
						
							|  |  |  | 		DiscreteBayesNet::shared_ptr chordal = solver.eliminate(); | 
					
						
							|  |  |  | 		toc(2, "my_eliminate"); | 
					
						
							|  |  |  | 		return chordal; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** Find the best total assignment - can be expensive */ | 
					
						
							|  |  |  | 	Scheduler::sharedValues Scheduler::optimalAssignment() const { | 
					
						
							|  |  |  | 		DiscreteBayesNet::shared_ptr chordal = eliminate(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (ISDEBUG("Scheduler::optimalAssignment")) { | 
					
						
							|  |  |  | 			DiscreteBayesNet::const_reverse_iterator it = chordal->rbegin(); | 
					
						
							|  |  |  | 			const Student & student = students_.front(); | 
					
						
							|  |  |  | 			cout << endl; | 
					
						
							|  |  |  | 			(*it)->print(student.name_); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		tic(3, "my_optimize"); | 
					
						
							|  |  |  | 		sharedValues mpe = optimize(*chordal); | 
					
						
							|  |  |  | 		toc(3, "my_optimize"); | 
					
						
							|  |  |  | 		return mpe; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** find the assignment of students to slots with most possible committees */ | 
					
						
							|  |  |  | 	Scheduler::sharedValues Scheduler::bestSchedule() const { | 
					
						
							|  |  |  | 		sharedValues best; | 
					
						
							|  |  |  | 		throw runtime_error("bestSchedule not implemented"); | 
					
						
							|  |  |  | 		return best; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** find the corresponding most desirable committee assignment */ | 
					
						
							|  |  |  | 	Scheduler::sharedValues Scheduler::bestAssignment( | 
					
						
							|  |  |  | 			sharedValues bestSchedule) const { | 
					
						
							|  |  |  | 		sharedValues best; | 
					
						
							|  |  |  | 		throw runtime_error("bestAssignment not implemented"); | 
					
						
							|  |  |  | 		return best; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } // gtsam
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 |