Timetabler
parser.cpp
1 #include "parser.h"
2 
3 #include <cstdlib>
4 #include <iostream>
5 #include "utils.h"
6 
13 
19 void Parser::parseFields(std::string file) {
20  YAML::Node config = YAML::LoadFile(file);
21 
22  YAML::Node instructorsConfig = config["instructors"];
23  for (YAML::Node instructorNode : instructorsConfig) {
24  Instructor instructor(instructorNode.as<std::string>());
25  timetabler->data.instructors.push_back(instructor);
26  }
27 
28  YAML::Node classroomsConfig = config["classrooms"];
29  for (YAML::Node classroomNode : classroomsConfig) {
30  Classroom classroom(classroomNode["number"].as<std::string>(),
31  classroomNode["size"].as<unsigned>());
32  timetabler->data.classrooms.push_back(classroom);
33  }
34 
35  YAML::Node segmentsConfig = config["segments"];
36  unsigned segmentStart = segmentsConfig["start"].as<unsigned>();
37  unsigned segmentEnd = segmentsConfig["end"].as<unsigned>();
38  for (unsigned i = segmentStart; i <= segmentEnd; ++i) {
39  for (unsigned j = i; j <= segmentEnd; ++j) {
40  timetabler->data.segments.push_back(Segment(i, j));
41  }
42  }
43 
44  YAML::Node slotsConfig = config["slots"];
45  for (YAML::Node slotNode : slotsConfig) {
46  std::vector<SlotElement> slotElements;
47  IsMinor isMinor = (slotNode["is_minor"].as<bool>())
50  for (YAML::Node periodNode : slotNode["time_periods"]) {
51  Time start(periodNode["start"].as<std::string>());
52  Time end(periodNode["end"].as<std::string>());
53  Day day = getDayFromString(periodNode["day"].as<std::string>());
54  slotElements.push_back(SlotElement(start, end, day));
55  }
56  timetabler->data.slots.push_back(
57  Slot(slotNode["name"].as<std::string>(), isMinor, slotElements));
58  }
59 
62 
63  YAML::Node programsConfig = config["programs"];
64  for (YAML::Node programNode : programsConfig) {
65  std::string name = programNode.as<std::string>();
66  timetabler->data.programs.push_back(Program(name, CourseType::core));
68  }
69 
70  YAML::Node weightsConfig = config["weights"];
71  timetabler->data.existingAssignmentWeights[FieldType::instructor] =
72  weightsConfig["instructor"][0].as<int>();
73  timetabler->data.highLevelVarWeights[FieldType::instructor] =
74  2 * weightsConfig["instructor"][1].as<int>();
75  timetabler->data.existingAssignmentWeights[FieldType::segment] =
76  weightsConfig["segment"][0].as<int>();
77  timetabler->data.highLevelVarWeights[FieldType::segment] =
78  2 * weightsConfig["segment"][1].as<int>();
79  timetabler->data.existingAssignmentWeights[FieldType::isMinor] =
80  weightsConfig["is_minor"][0].as<int>();
81  timetabler->data.highLevelVarWeights[FieldType::isMinor] =
82  2 * weightsConfig["is_minor"][1].as<int>();
83  timetabler->data.existingAssignmentWeights[FieldType::program] =
84  weightsConfig["program"].as<int>();
85  timetabler->data.existingAssignmentWeights[FieldType::classroom] =
86  weightsConfig["classroom"][0].as<int>();
87  timetabler->data.highLevelVarWeights[FieldType::classroom] =
88  2 * weightsConfig["classroom"][1].as<int>();
89  timetabler->data.existingAssignmentWeights[FieldType::slot] =
90  weightsConfig["slot"][0].as<int>();
91  timetabler->data.highLevelVarWeights[FieldType::slot] =
92  2 * weightsConfig["slot"][1].as<int>();
93 
94  YAML::Node predefinedWeightsConfig = config["predefined_weights"];
95  for (YAML::Node predefinedWeightNode : predefinedWeightsConfig) {
96  unsigned clauseNo = predefinedWeightNode["clause"].as<int>();
97  int weight = predefinedWeightNode["weight"].as<int>();
98  timetabler->data.predefinedClausesWeights[clauseNo] = weight;
99  }
100 }
101 
112 Day Parser::getDayFromString(std::string day) {
113  if (day == "Monday") return Day::Monday;
114  if (day == "Tuesday") return Day::Tuesday;
115  if (day == "Wednesday") return Day::Wednesday;
116  if (day == "Thursday") return Day::Thursday;
117  if (day == "Friday") return Day::Friday;
118  if (day == "Saturday") return Day::Saturday;
119  if (day == "Sunday") return Day::Sunday;
120  assert(false && "Incorrect day");
121  return Day::Monday;
122 }
123 
129 void Parser::parseInput(std::string file) {
130  csv::Parser parser(file);
132  for (unsigned i = 0; i < parser.rowCount(); ++i) {
133  std::vector<std::vector<lbool>> assignmentsThisCourse(Global::FIELD_COUNT);
134 
135  std::string name = parser[i]["name"];
136  std::string classSizeStr = parser[i]["class_size"];
137  unsigned classSize = unsigned(std::stoi(classSizeStr));
138 
139  std::string instructorStr = parser[i]["instructor"];
140  int instructor = -1;
141  for (unsigned j = 0; j < timetabler->data.instructors.size(); j++) {
142  if (timetabler->data.instructors[j].getName() == instructorStr) {
143  instructor = j;
144  assignmentsThisCourse[FieldType::instructor].push_back(l_True);
145  continue;
146  }
147  assignmentsThisCourse[FieldType::instructor].push_back(l_False);
148  }
149  if (instructor == -1) {
150  LOG(ERROR) << "Input contains invalid Instructor name";
151  }
152  std::string segmentStr = parser[i]["segment"];
153  int segment = -1;
154  for (unsigned j = 0; j < timetabler->data.segments.size(); j++) {
155  if (timetabler->data.segments[j].getName() == segmentStr) {
156  segment = j;
157  assignmentsThisCourse[FieldType::segment].push_back(l_True);
158  continue;
159  }
160  assignmentsThisCourse[FieldType::segment].push_back(l_False);
161  }
162  if (segment == -1) {
163  LOG(ERROR) << "Input contains invalid Segment name";
164  }
165  std::string isMinorStr = parser[i]["is_minor"];
167  if (isMinorStr == "Yes" || isMinorStr == "Y") {
168  isMinor = MinorType::isMinorCourse;
169  assignmentsThisCourse[FieldType::isMinor].push_back(l_True);
170  } else if (isMinorStr == "No" || isMinorStr == "N" || isMinorStr == "") {
171  isMinor = MinorType::isNotMinorCourse;
172  assignmentsThisCourse[FieldType::isMinor].push_back(l_False);
173  } else {
174  LOG(ERROR) << "Input contains invalid IsMinor value (should be "
175  "'Yes' or 'No')";
176  }
177  Course course(name, classSize, instructor, segment, isMinor);
178 
179  for (unsigned j = 0; j < timetabler->data.programs.size(); j += 2) {
180  std::string s = timetabler->data.programs[j].getName();
181  if (parser[i][s] == "Core" || parser[i][s] == "C" ||
182  parser[i][s] == "Y") {
183  course.addProgram(j);
184  assignmentsThisCourse[FieldType::program].push_back(l_True);
185  assignmentsThisCourse[FieldType::program].push_back(l_False);
186  } else if (parser[i][s] == "Elective" || parser[i][s] == "E") {
187  course.addProgram(j + 1);
188  assignmentsThisCourse[FieldType::program].push_back(l_False);
189  assignmentsThisCourse[FieldType::program].push_back(l_True);
190  } else if (parser[i][s] == "No" || parser[i][s] == "N" ||
191  parser[i][s] == "") {
192  assignmentsThisCourse[FieldType::program].push_back(l_False);
193  assignmentsThisCourse[FieldType::program].push_back(l_False);
194  } else {
195  LOG(ERROR) << "Input contains invalid Program type (should be "
196  "'Core', 'Elective', or 'No')";
197  }
198  }
199 
200  std::string classroomStr = parser[i]["classroom"];
201  std::string slotStr = parser[i]["slot"];
202  bool foundClassroom = false;
203  bool foundSlot = false;
204  assignmentsThisCourse[FieldType::classroom].resize(
205  timetabler->data.classrooms.size(), l_Undef);
206  assignmentsThisCourse[FieldType::slot].resize(timetabler->data.slots.size(),
207  l_Undef);
208  if (classroomStr != "") {
209  for (unsigned j = 0; j < timetabler->data.classrooms.size(); j++) {
210  if (timetabler->data.classrooms[j].getName() == classroomStr) {
211  assignmentsThisCourse[FieldType::classroom][j] = l_True;
212  foundClassroom = true;
213  course.addClassroom(j);
214  continue;
215  }
216  assignmentsThisCourse[FieldType::classroom][j] = l_False;
217  }
218  if (!foundClassroom) {
219  LOG(ERROR) << "Input contains invalid Classroom name";
220  }
221  }
222  if (slotStr != "") {
223  for (unsigned j = 0; j < timetabler->data.slots.size(); j++) {
224  if (timetabler->data.slots[j].getName() == slotStr) {
225  assignmentsThisCourse[FieldType::slot][j] = l_True;
226  foundSlot = true;
227  course.addSlot(j);
228  continue;
229  }
230  assignmentsThisCourse[FieldType::slot][j] = l_False;
231  }
232  if (!foundSlot) {
233  LOG(ERROR) << "Input contains invalid Slot name";
234  }
235  }
236  timetabler->data.courses.push_back(course);
237  timetabler->data.existingAssignmentVars.push_back(assignmentsThisCourse);
238  }
239 }
240 
247  bool result = true;
248  for (auto course1 : timetabler->data.courses) {
249  if (course1.getIsMinor() == MinorType::isMinorCourse &&
250  course1.getSlot() != -1) {
251  if (timetabler->data.slots[course1.getSlot()].isMinorSlot()) {
253  [PredefinedClauses::minorInMinorTime] != 0) {
254  LOG(WARNING)
255  << course1.getName()
256  << " which is minor course is scheduled in non minor slot.";
257  }
259  [PredefinedClauses::minorInMinorTime] == -1) {
260  LOG(WARNING) << "Hard constraint unsatisfied";
261  result = false;
262  }
263  }
264  }
265 
266  for (auto course2 : timetabler->data.courses) {
267  if (course1.getName() == course2.getName()) continue;
268  bool segementIntersecting =
269  timetabler->data.segments[course1.getSegment()].isIntersecting(
270  timetabler->data.segments[course2.getSegment()]);
271  bool classroomSame = (course1.getClassroom() != -1 &&
272  course1.getClassroom() == course2.getClassroom());
273  // bool slotSame =
274  // (course1.getSlot() != -1 && course1.getSlot() ==
275  // course2.getSlot());
276  bool slotIntersecting =
277  (course1.getSlot() != -1 && course2.getSlot() != -1)
278  ? (timetabler->data.slots[course1.getSlot()].isIntersecting(
279  timetabler->data.slots[course2.getSlot()]))
280  : false;
281  if (segementIntersecting && slotIntersecting) {
282  if (course1.getInstructor() == course2.getInstructor()) {
284  [PredefinedClauses::instructorSingleCourseAtATime] != 0) {
285  LOG(WARNING) << course1.getName() << " and " << course2.getName()
286  << " having same instructor clash.";
287  }
289  [PredefinedClauses::instructorSingleCourseAtATime] == -1) {
290  LOG(WARNING) << "Hard constraint unsatisfied";
291  result = false;
292  }
293  }
294 
295  if (classroomSame) {
297  [PredefinedClauses::classroomSingleCourseAtATime] != 0) {
298  LOG(WARNING) << course1.getName() << " and " << course2.getName()
299  << " having same classroom clash.";
300  }
302  [PredefinedClauses::classroomSingleCourseAtATime] == -1) {
303  LOG(WARNING) << "Hard constraint unsatisfied";
304  result = false;
305  }
306  }
307 
308  for (auto program1 : course1.getPrograms()) {
309  for (auto program2 : course2.getPrograms()) {
310  if (program1 == program2) {
311  if (timetabler->data.programs[program1].isCoreProgram() &&
312  timetabler->data.programs[program2].isCoreProgram()) {
314  [PredefinedClauses::programSingleCoreCourseAtATime] !=
315  0) {
316  LOG(WARNING)
317  << course1.getName() << " and " << course2.getName()
318  << " which have common core program "
319  << timetabler->data.programs[program1].getName()
320  << " clash.";
321  }
323  [PredefinedClauses::programSingleCoreCourseAtATime] ==
324  -1) {
325  LOG(WARNING) << "Hard constraint unsatisfied";
326  result = false;
327  }
328  }
329  }
330  }
331  }
332  }
333  }
334  }
335  return result;
336 }
337 
343  for (Course c : timetabler->data.courses) {
344  std::vector<std::vector<Var>> courseVars;
345  courseVars.resize(Global::FIELD_COUNT);
346  for (Classroom cr : timetabler->data.classrooms) {
347  Var v = timetabler->newVar();
348  courseVars[FieldType::classroom].push_back(v);
349  }
351  Var v = timetabler->newVar();
352  courseVars[FieldType::instructor].push_back(v);
353  }
354  for (IsMinor i : timetabler->data.isMinors) {
355  Var v = timetabler->newVar();
356  courseVars[FieldType::isMinor].push_back(v);
357  }
358  for (Program p : timetabler->data.programs) {
359  Var v = timetabler->newVar();
360  courseVars[FieldType::program].push_back(v);
361  }
362  for (Segment s : timetabler->data.segments) {
363  Var v = timetabler->newVar();
364  courseVars[FieldType::segment].push_back(v);
365  }
366  for (Slot s : timetabler->data.slots) {
367  Var v = timetabler->newVar();
368  courseVars[FieldType::slot].push_back(v);
369  }
370  timetabler->data.fieldValueVars.push_back(courseVars);
371 
372  std::vector<Var> highLevelCourseVars;
373  for (unsigned i = 0; i < Global::FIELD_COUNT; ++i) {
374  Var v = timetabler->newVar();
375  highLevelCourseVars.push_back(v);
376  }
377  timetabler->data.highLevelVars.push_back(highLevelCourseVars);
378  }
379 
382  for (unsigned i = 0; i < Global::PREDEFINED_CLAUSES_COUNT; i++) {
383  if (i == PredefinedClauses::instructorSingleCourseAtATime ||
384  i == PredefinedClauses::classroomSingleCourseAtATime ||
385  i == PredefinedClauses::programSingleCoreCourseAtATime) {
386  Var v = timetabler->newVar();
387  timetabler->data.predefinedConstraintVars[i].push_back(v);
388  } else {
389  for (unsigned j = 0; j < timetabler->data.courses.size(); j++) {
390  Var v = timetabler->newVar();
391  timetabler->data.predefinedConstraintVars[i].push_back(v);
392  }
393  }
394  }
395 }
std::vector< int > predefinedClausesWeights
Definition: data.h:124
Class for a classroom.
Definition: classroom.h:13
Class for a program.
Definition: program.h:27
Class for a course.
Definition: course.h:16
Class for "is minor".
Definition: is_minor.h:32
Class for a slot element.
Definition: slot.h:59
Class for a time unit.
Definition: slot.h:30
std::vector< Segment > segments
Definition: data.h:52
void addClassroom(int)
Adds a classroom that is applicable to the Course.
Definition: course.cpp:71
std::vector< int > highLevelVarWeights
Definition: data.h:107
void addProgram(int)
Adds a program that is applicable to the Course.
Definition: course.cpp:64
std::vector< Classroom > classrooms
Definition: data.h:44
std::vector< Slot > slots
Definition: data.h:56
std::vector< IsMinor > isMinors
Definition: data.h:60
Day
Enum Class to represent a day of the week.
Definition: slot.h:15
Class for an instructor.
Definition: instructor.h:13
Class for time tabler.
Definition: timetabler.h:44
std::vector< Instructor > instructors
Definition: data.h:40
std::vector< std::vector< Var > > predefinedConstraintVars
Definition: data.h:85
std::vector< int > existingAssignmentWeights
Definition: data.h:116
MinorType
Enum to represent the types of "Is Minor".
Definition: is_minor.h:16
void parseFields(std::string file)
Parse the fields given in a file.
Definition: parser.cpp:19
Timetabler * timetabler
Definition: main.cpp:90
void parseInput(std::string file)
Parses the input given in a file.
Definition: parser.cpp:129
std::vector< std::vector< std::vector< lbool > > > existingAssignmentVars
Definition: data.h:98
Class for segment.
Definition: segment.h:13
void addSlot(int)
Adds a slot that is applicable to the Course.
Definition: course.cpp:78
std::vector< Course > courses
Definition: data.h:36
static const int PREDEFINED_CLAUSES_COUNT
Definition: global.h:41
bool verify()
Verifies if the input is valid.
Definition: parser.cpp:246
Class for a slot.
Definition: slot.h:82
std::vector< std::vector< Var > > highLevelVars
Definition: data.h:81
void addVars()
Requests for variables to be added to the solver and stores the data.
Definition: parser.cpp:342
static const int FIELD_COUNT
Definition: global.h:37
Parser(Timetabler *)
Constructs the Parser object.
Definition: parser.cpp:12
Var newVar()
Calls the formula to issue a new variable and returns it.
Definition: timetabler.cpp:262
Data data
Definition: timetabler.h:63
std::vector< Program > programs
Definition: data.h:48
std::vector< std::vector< std::vector< Var > > > fieldValueVars
Definition: data.h:73