Timetabler
timetabler.cpp
1 #include "timetabler.h"
2 
3 #include <cstdlib>
4 #include <fstream>
5 #include <iostream>
6 #include <vector>
7 #include "MaxSATFormula.h"
8 #include "cclause.h"
9 #include "clauses.h"
10 #include "core/SolverTypes.h"
11 #include "mtl/Vec.h"
12 #include "tsolver.h"
13 #include "utils.h"
14 
15 using namespace NSPACE;
16 
21  solver = new TSolver(1, _CARD_TOTALIZER_);
22  formula = new MaxSATFormula();
23  formula->setProblemType(_WEIGHTED_);
24 }
25 
35 void Timetabler::addClauses(const std::vector<CClause> &clauses, int weight) {
36  for (unsigned i = 0; i < clauses.size(); i++) {
37  vec<Lit> clauseVec;
38  std::vector<Lit> clauseVector = clauses[i].getLits();
39  for (unsigned j = 0; j < clauseVector.size(); j++) {
40  clauseVec.push(clauseVector[j]);
41  }
42  addToFormula(clauseVec, weight);
43  }
44 }
45 
51  for (unsigned i = 0; i < Global::FIELD_COUNT; i++) {
52  for (unsigned j = 0; j < data.highLevelVars.size(); j++) {
53  vec<Lit> highLevelClause;
54  highLevelClause.clear();
55  highLevelClause.push(mkLit(data.highLevelVars[j][i], false));
56  addToFormula(highLevelClause, data.highLevelVarWeights[i]);
57  }
58  }
59 }
60 
73  const int course) {
74  if (course == -1) {
75  assert(data.predefinedConstraintVars[clauseType].size() == 1);
76  Lit l = mkLit(data.predefinedConstraintVars[clauseType][0], false);
77  if (data.predefinedClausesWeights[clauseType] != 0) {
78  addToFormula(l, data.predefinedClausesWeights[clauseType]);
79  } else {
80  addToFormula(l, -1);
81  }
82  } else {
83  assert(data.predefinedConstraintVars[clauseType].size() ==
84  data.courses.size());
85  Lit l = mkLit(data.predefinedConstraintVars[clauseType][course], false);
86  if (data.predefinedClausesWeights[clauseType] != 0) {
87  addToFormula(l, data.predefinedClausesWeights[clauseType]);
88  } else {
89  addToFormula(l, -1);
90  }
91  }
92 }
93 
106  Lit l = mkLit(data.customConstraintVars[index], false);
107  if (weight != 0) {
108  addToFormula(l, weight);
109  } else {
110  addToFormula(l, -1);
111  }
112 }
113 
119  for (unsigned i = 0; i < data.existingAssignmentVars.size(); i++) {
120  for (unsigned j = 0; j < data.existingAssignmentVars[i].size(); j++) {
121  for (unsigned k = 0; k < data.existingAssignmentVars[i][j].size(); k++) {
122  if (data.existingAssignmentVars[i][j][k] == l_Undef) {
123  continue;
124  }
125  vec<Lit> clause;
126  clause.clear();
127  if (data.existingAssignmentVars[i][j][k] == l_True) {
128  clause.push(mkLit(data.fieldValueVars[i][j][k]));
129  } else {
130  clause.push(~mkLit(data.fieldValueVars[i][j][k]));
131  }
132  addToFormula(clause, data.existingAssignmentWeights[j]);
133  }
134  }
135  }
136 }
137 
147 void Timetabler::addToFormula(vec<Lit> &input, int weight) {
148  if (weight < 0) {
149  formula->addHardClause(input);
150  } else if (weight > 0) {
151  formula->addSoftClause(weight, input);
152  }
153 }
154 
161 void Timetabler::addToFormula(Lit input, int weight) {
162  vec<Lit> inputLits;
163  inputLits.push(input);
164  addToFormula(inputLits, weight);
165 }
166 
176 void Timetabler::addClauses(const Clauses &clauses, int weight) {
177  addClauses(clauses.getClauses(), weight);
178 }
179 
186  solver->loadFormula(formula);
187  model = solver->tSearch();
188  if (model.size() == 0) {
189  return SolverStatus::Unsolved;
190  }
191  if (checkAllTrue(Utils::flattenVector<Var>(data.highLevelVars)) &&
192  checkAllTrue(data.predefinedConstraintVars) &&
193  checkAllTrue(data.customConstraintVars)) {
194  return SolverStatus::Solved;
195  }
197 }
198 
207 bool Timetabler::checkAllTrue(const std::vector<Var> &inputs) {
208  if (model.size() == 0) {
209  return false;
210  }
211  for (unsigned i = 0; i < inputs.size(); i++) {
212  if (model[inputs[i]] == l_False) {
213  return false;
214  }
215  }
216  return true;
217 }
218 
227 bool Timetabler::checkAllTrue(const std::vector<std::vector<Var>> &inputs) {
228  if (model.size() == 0) {
229  return false;
230  }
231  for (auto &i : inputs) {
232  for (auto &j : i) {
233  if (model[j] == l_False) return false;
234  }
235  }
236  return true;
237 }
238 
247 bool Timetabler::isVarTrue(const Var &v) {
248  if (model.size() == 0) {
249  return false;
250  }
251  if (model[v] == l_False) {
252  return false;
253  }
254  return true;
255 }
256 
263  Var var = formula->nVars();
264  formula->newVar();
265  return var;
266 }
267 
276 Lit Timetabler::newLiteral(bool sign) {
277  Lit p = mkLit(formula->nVars(), sign);
278  formula->newVar();
279  return p;
280 }
281 
286  if (status == SolverStatus::Solved) {
287  LOG(INFO) << "All high level clauses were satisfied";
288  displayChangesInGivenAssignment();
289  displayTimeTable();
290  } else if (status == SolverStatus::HighLevelFailed) {
291  LOG(WARNING) << "Some high level clauses were not satisfied";
292  displayUnsatisfiedOutputReasons();
293  displayChangesInGivenAssignment();
294  displayTimeTable();
295  } else {
296  LOG(WARNING) << "Not Solved";
297  }
298 }
299 
305  for (unsigned i = 0; i < data.existingAssignmentVars.size(); i++) {
306  for (unsigned j = 0; j < data.existingAssignmentVars[i].size(); j++) {
307  for (unsigned k = 0; k < data.existingAssignmentVars[i][j].size(); k++) {
308  if (data.existingAssignmentVars[i][j][k] == l_True &&
309  model[data.fieldValueVars[i][j][k]] == l_False) {
310  LOG(WARNING) << "Value of field "
311  << Utils::getFieldTypeName(FieldType(j)) << " "
312  << Utils::getFieldName(FieldType(j), k, data)
313  << " for course " << data.courses[i].getName()
314  << " changed from 'True' to 'False'";
315  } else if (data.existingAssignmentVars[i][j][k] == l_False &&
316  model[data.fieldValueVars[i][j][k]] == l_True) {
317  LOG(WARNING) << "Value of field "
318  << Utils::getFieldTypeName(FieldType(j)) << " "
319  << Utils::getFieldName(FieldType(j), k, data)
320  << " for course " << data.courses[i].getName()
321  << " changed from 'False' to 'True'";
322  }
323  }
324  }
325  }
326 }
327 
332  for (unsigned i = 0; i < data.courses.size(); i++) {
333  LOG(INFO) << "Course : " << data.courses[i].getName();
334  for (unsigned j = 0; j < data.fieldValueVars[i][FieldType::slot].size();
335  j++) {
336  if (isVarTrue(data.fieldValueVars[i][FieldType::slot][j])) {
337  LOG(INFO) << "Slot : " << data.slots[j].getName();
338  }
339  }
340  for (unsigned j = 0;
341  j < data.fieldValueVars[i][FieldType::instructor].size(); j++) {
342  if (isVarTrue(data.fieldValueVars[i][FieldType::instructor][j])) {
343  LOG(INFO) << "Instructor : " << data.instructors[j].getName();
344  }
345  }
346  for (unsigned j = 0;
347  j < data.fieldValueVars[i][FieldType::classroom].size(); j++) {
348  if (isVarTrue(data.fieldValueVars[i][FieldType::classroom][j])) {
349  LOG(INFO) << "Classroom : " << data.classrooms[j].getName();
350  }
351  }
352  for (unsigned j = 0; j < data.fieldValueVars[i][FieldType::segment].size();
353  j++) {
354  if (isVarTrue(data.fieldValueVars[i][FieldType::segment][j])) {
355  LOG(INFO) << "Segment : " << data.segments[j].getName();
356  }
357  }
358  for (unsigned j = 0; j < data.fieldValueVars[i][FieldType::isMinor].size();
359  j++) {
360  if (isVarTrue(data.fieldValueVars[i][FieldType::isMinor][j])) {
361  LOG(INFO) << "Is Minor : " << data.isMinors[j].getName();
362  }
363  }
364  for (unsigned j = 0; j < data.fieldValueVars[i][FieldType::program].size();
365  j++) {
366  if (isVarTrue(data.fieldValueVars[i][FieldType::program][j])) {
367  LOG(INFO) << "Program : " << data.programs[j].getNameWithType();
368  }
369  }
370  LOG(INFO) << "";
371  }
372 }
373 
379 void Timetabler::writeOutput(std::string fileName) {
380  std::ofstream fileObject;
381  fileObject.open(fileName);
382  fileObject << "name,class_size,instructor,segment,is_minor,";
383  for (unsigned i = 0; i < data.programs.size(); i += 2) {
384  fileObject << data.programs[i].getName() << ",";
385  }
386  fileObject << "classroom,slot" << std::endl;
387  for (unsigned i = 0; i < data.courses.size(); i++) {
388  fileObject << data.courses[i].getName() << ","
389  << data.courses[i].getClassSize() << ",";
390  for (unsigned j = 0;
391  j < data.fieldValueVars[i][FieldType::instructor].size(); j++) {
392  if (isVarTrue(data.fieldValueVars[i][FieldType::instructor][j])) {
393  fileObject << data.instructors[j].getName();
394  }
395  }
396  fileObject << ",";
397  for (unsigned j = 0; j < data.fieldValueVars[i][FieldType::segment].size();
398  j++) {
399  if (isVarTrue(data.fieldValueVars[i][FieldType::segment][j])) {
400  fileObject << data.segments[j].getName();
401  }
402  }
403  fileObject << ",";
404  for (unsigned j = 0; j < data.fieldValueVars[i][FieldType::isMinor].size();
405  j++) {
406  if (isVarTrue(data.fieldValueVars[i][FieldType::isMinor][j])) {
407  fileObject << data.isMinors[j].getName();
408  }
409  }
410  fileObject << ",";
411  for (unsigned j = 0; j < data.fieldValueVars[i][FieldType::program].size();
412  j += 2) {
413  if (isVarTrue(data.fieldValueVars[i][FieldType::program][j])) {
414  fileObject << data.programs[j].getCourseTypeName() << ",";
415  } else if (isVarTrue(data.fieldValueVars[i][FieldType::program][j + 1])) {
416  fileObject << data.programs[j + 1].getCourseTypeName() << ",";
417  } else {
418  fileObject << "No,";
419  }
420  }
421  for (unsigned j = 0;
422  j < data.fieldValueVars[i][FieldType::classroom].size(); j++) {
423  if (isVarTrue(data.fieldValueVars[i][FieldType::classroom][j])) {
424  fileObject << data.classrooms[j].getName();
425  }
426  }
427  fileObject << ",";
428  for (unsigned j = 0; j < data.fieldValueVars[i][FieldType::slot].size();
429  j++) {
430  if (isVarTrue(data.fieldValueVars[i][FieldType::slot][j])) {
431  fileObject << data.slots[j].getName();
432  }
433  }
434  fileObject << std::endl;
435  }
436  fileObject.close();
437 }
438 
444  for (unsigned i = 0; i < data.highLevelVars.size(); i++) {
445  for (unsigned j = 0; j < data.highLevelVars[i].size(); j++) {
446  if (!isVarTrue(data.highLevelVars[i][j])) {
447  LOG(WARNING) << "Field : " << Utils::getFieldTypeName(FieldType(j))
448  << " of Course : " << data.courses[i].getName()
449  << " could not be satisfied";
450  }
451  }
452  }
453  for (unsigned i = 0; i < data.predefinedConstraintVars.size(); i++) {
454  if (!checkAllTrue(data.predefinedConstraintVars[i]) &&
455  data.predefinedClausesWeights[i] != 0) {
456  LOG(WARNING) << "Predefined Constraint : "
457  << Utils::getPredefinedConstraintName(PredefinedClauses(i))
458  << " could not be satisfied";
459  }
460  }
461  for (unsigned i = 0; i < data.customConstraintVars.size(); i++) {
462  if (!isVarTrue(data.customConstraintVars[i])) {
463  LOG(WARNING) << "Custom Constraint : " << i + 1 << " for course "
464  << data.courses[data.customMap[i + 1]].getName()
465  << " could not be satisfied";
466  }
467  }
468 }
469 
473 Timetabler::~Timetabler() { delete solver; }
void displayUnsatisfiedOutputReasons()
Displays the reasons due to which the formula could not be satisfied, if applicable.
Definition: timetabler.cpp:443
void addExistingAssignments()
Adds unit clauses corresponding to existing assignments given in the input to the solver...
Definition: timetabler.cpp:118
bool isVarTrue(const Var &)
Determines if a given variable is true in the model returned by the solver.
Definition: timetabler.cpp:247
Lit newLiteral(bool sign=false)
Calls the formula to issue a new literal and returns it.
Definition: timetabler.cpp:276
void addHighLevelCustomConstraintClauses(int, int)
Adds high level custom constraint clauses.
Definition: timetabler.cpp:105
std::vector< CClause > getClauses() const
Gets the clauses in this object.
Definition: clauses.cpp:264
~Timetabler()
Destroys the object, and deletes the solver.
Definition: timetabler.cpp:473
void addHighLevelClauses()
Adds unit soft clauses for the high level variables to the solver.
Definition: timetabler.cpp:50
bool checkAllTrue(const std::vector< Var > &)
Checks if a given set of variables are true in the model returned by the solver.
Definition: timetabler.cpp:207
void displayChangesInGivenAssignment()
Displays the changes that have been made to the default assignment given by the user as input by the ...
Definition: timetabler.cpp:304
PredefinedClauses
Enum that represents all the predefined constraints.
Definition: global.h:14
void writeOutput(std::string)
Writes the generated time table to a CSV file.
Definition: timetabler.cpp:379
FieldType
Enum that represents all the field types.
Definition: global.h:9
void addToFormula(vec< Lit > &, int)
Add a given vec of literals with the given weight to the formula.
Definition: timetabler.cpp:147
void printResult(SolverStatus)
Prints the result of the problem.
Definition: timetabler.cpp:285
Timetabler()
Constructs the Timetabler object.
Definition: timetabler.cpp:20
void displayTimeTable()
Displays the generated time table.
Definition: timetabler.cpp:331
Class for solver.
Definition: tsolver.h:25
void addHighLevelConstraintClauses(PredefinedClauses, const int course)
Adds high level clauses for predefined constraints.
Definition: timetabler.cpp:72
static const int FIELD_COUNT
Definition: global.h:37
Class for representing a set of clauses.
Definition: clauses.h:23
SolverStatus
Enum to store the solver status.
Definition: timetabler.h:19
SolverStatus solve()
Calls the solver to solve for the constraints.
Definition: timetabler.cpp:185
Var newVar()
Calls the formula to issue a new variable and returns it.
Definition: timetabler.cpp:262
void addClauses(const std::vector< CClause > &, int)
Adds clauses to the solver with specified weights.
Definition: timetabler.cpp:35