Timetabler
custom_parser.cpp
1 #include "custom_parser.h"
2 
3 #include <algorithm>
4 #include <cstdlib>
5 #include <iostream>
6 #include <string>
7 #include <tao/pegtl.hpp>
8 #include <vector>
9 #include "clauses.h"
10 #include "global.h"
11 #include "utils.h"
12 
21 Clauses makeAntecedent(Object &obj, int course) {
22  Clauses ante, clause;
23  if (obj.instructorValues.size() > 0) {
24  clause = obj.constraintEncoder->hasFieldTypeListedValues(
25  course, FieldType::instructor, obj.instructorValues);
26  ante = ante & clause;
27  }
28  if (obj.programValues.size() > 0) {
29  clause = obj.constraintEncoder->hasFieldTypeListedValues(
30  course, FieldType::program, obj.programValues);
31  ante = ante & clause;
32  }
33  if (obj.segmentValues.size() > 0) {
34  clause = obj.constraintEncoder->hasFieldTypeListedValues(
35  course, FieldType::segment, obj.segmentValues);
36  ante = ante & clause;
37  }
38  if (obj.isMinorValues.size() > 0) {
39  clause = obj.constraintEncoder->hasFieldTypeListedValues(
40  course, FieldType::isMinor, obj.isMinorValues);
41  ante = ante & clause;
42  }
43  return ante;
44 }
45 
55 Clauses makeConsequent(Object &obj, int course, int i) {
56  Clauses cons, clause;
57  if (obj.classSame) {
58  for (unsigned j = i + 1; j < obj.courseValues.size(); j++) {
59  Clauses a = makeAntecedent(obj, obj.courseValues[j]);
60  Clauses b = obj.constraintEncoder->hasSameFieldTypeAndValue(
61  course, obj.courseValues[j], FieldType::classroom);
62  a = a >> b;
63  cons = cons & a;
64  }
65  }
66  if (obj.classNotSame) {
67  for (unsigned j = i + 1; j < obj.courseValues.size(); j++) {
68  Clauses a = makeAntecedent(obj, obj.courseValues[j]);
69  Clauses b = obj.constraintEncoder->hasSameFieldTypeAndValue(
70  course, obj.courseValues[j], FieldType::classroom);
71  a = a >> (~b);
72  cons = cons & a;
73  }
74  }
75  if (obj.slotSame) {
76  for (unsigned j = i + 1; j < obj.courseValues.size(); j++) {
77  Clauses a = makeAntecedent(obj, obj.courseValues[j]);
78  Clauses b = obj.constraintEncoder->hasSameFieldTypeAndValue(
79  course, obj.courseValues[j], FieldType::slot);
80  a = a >> b;
81  cons = cons & a;
82  }
83  }
84  if (obj.slotNotSame) {
85  for (unsigned j = i + 1; j < obj.courseValues.size(); j++) {
86  Clauses a = makeAntecedent(obj, obj.courseValues[j]);
87  Clauses b = obj.constraintEncoder->hasSameFieldTypeAndValue(
88  course, obj.courseValues[j], FieldType::slot);
89  a = a >> (~b);
90  cons = cons & a;
91  }
92  }
93  if (obj.classValues.size() > 0) {
94  clause = obj.constraintEncoder->hasFieldTypeListedValues(
95  course, FieldType::classroom, obj.classValues);
96  cons = cons & clause;
97  }
98  if (obj.slotValues.size() > 0) {
99  clause = obj.constraintEncoder->hasFieldTypeListedValues(
100  course, FieldType::slot, obj.slotValues);
101  cons = cons & clause;
102  }
103  return cons;
104 }
105 
106 namespace pegtl = tao::TAO_PEGTL_NAMESPACE;
107 
108 namespace custom_constraint_grammar {
109 
110 template <typename Rule>
111 struct action : pegtl::nothing<Rule> {};
112 
116 struct integer
117  : pegtl::seq<pegtl::opt<pegtl::one<'-'>>, pegtl::plus<pegtl::digit>> {};
118 template <>
119 struct action<integer> {
120  template <typename Input>
121  static void apply(const Input &in, Object &obj) {
122  obj.integer = std::stoi(in.string());
123  }
124 };
125 
129 struct instr : TAO_PEGTL_KEYWORD("IN") {};
130 
135 struct notstr : TAO_PEGTL_KEYWORD("NOT") {};
136 template <>
137 struct action<notstr> {
138  template <typename Input>
139  static void apply(const Input &in, Object &obj) {
140  obj.isNot = true;
141  }
142 };
143 
147 struct andstr : TAO_PEGTL_KEYWORD("AND") {};
148 
152 struct orstr : TAO_PEGTL_KEYWORD("OR") {};
153 
157 struct exceptstr : TAO_PEGTL_KEYWORD("EXCEPT") {};
158 
163 struct classroomstr : TAO_PEGTL_KEYWORD("CLASSROOM") {};
164 template <>
166  template <typename Input>
167  static void apply(const Input &in, Object &obj) {
168  obj.fieldType = FieldValuesType::CLASSROOM;
169  }
170 };
171 
175 struct slotstr : TAO_PEGTL_KEYWORD("SLOT") {};
176 template <>
177 struct action<slotstr> {
178  template <typename Input>
179  static void apply(const Input &in, Object &obj) {
180  obj.fieldType = FieldValuesType::SLOT;
181  }
182 };
183 
187 struct coursestr : TAO_PEGTL_KEYWORD("COURSE") {};
188 template <>
189 struct action<coursestr> {
190  template <typename Input>
191  static void apply(const Input &in, Object &obj) {
192  obj.fieldType = FieldValuesType::COURSE;
193  }
194 };
195 
199 struct instructorstr : TAO_PEGTL_KEYWORD("INSTRUCTOR") {};
200 template <>
202  template <typename Input>
203  static void apply(const Input &in, Object &obj) {
204  obj.fieldType = FieldValuesType::INSTRUCTOR;
205  }
206 };
207 
211 struct segmentstr : TAO_PEGTL_KEYWORD("SEGMENT") {};
212 template <>
214  template <typename Input>
215  static void apply(const Input &in, Object &obj) {
216  obj.fieldType = FieldValuesType::SEGMENT;
217  }
218 };
219 
223 struct isminorstr : TAO_PEGTL_KEYWORD("ISMINOR") {};
224 template <>
226  template <typename Input>
227  static void apply(const Input &in, Object &obj) {
228  obj.fieldType = FieldValuesType::ISMINOR;
229  }
230 };
231 
235 struct programstr : TAO_PEGTL_KEYWORD("PROGRAM") {};
236 template <>
238  template <typename Input>
239  static void apply(const Input &in, Object &obj) {
240  obj.fieldType = FieldValuesType::PROGRAM;
241  }
242 };
243 
247 struct unbundlestr : TAO_PEGTL_KEYWORD("UNBUNDLE") {};
248 
249 
253 struct bundlestr : TAO_PEGTL_KEYWORD("BUNDLE") {};
254 
258 struct weightstr : TAO_PEGTL_KEYWORD("WEIGHT") {};
259 
264 struct fieldtype
265  : pegtl::sor<instructorstr, segmentstr, isminorstr, programstr> {};
266 template <>
267 struct action<fieldtype> {
268  template <typename Input>
269  static void apply(const Input &in, Object &obj) {
270  obj.isNot = false;
271  obj.classSame = false;
272  obj.slotSame = false;
273  obj.classNotSame = false;
274  obj.slotNotSame = false;
275  }
276 };
277 
281 struct value
282  : pegtl::plus<pegtl::sor<pegtl::range<'a', 'z'>, pegtl::range<'A', 'Z'>,
283  pegtl::digit, pegtl::one<'.'>, pegtl::one<'-'>,
284  pegtl::one<'@'>, pegtl::one<'<'>, pegtl::one<'>'>,
285  pegtl::space>> {};
286 template <>
287 struct action<value> {
288  template <typename Input>
289  static void apply(const Input &in, Object &obj) {
290  std::string val = in.string();
291  bool found = false;
292  if (obj.fieldType == FieldValuesType::INSTRUCTOR) {
293  for (unsigned i = 0; i < obj.timetabler->data.instructors.size(); i++) {
294  if (obj.timetabler->data.instructors[i].getName() == val) {
295  found = true;
296  obj.instructorValues.push_back(i);
297  break;
298  }
299  }
300  if (!found) {
301  LOG(ERROR) << "Instructor " << val << " does not exist.";
302  }
303  found = false;
304  } else if (obj.fieldType == FieldValuesType::COURSE) {
305  for (unsigned i = 0; i < obj.timetabler->data.courses.size(); i++) {
306  if (obj.timetabler->data.courses[i].getName() == val) {
307  found = true;
308  obj.courseValues.push_back(i);
309  break;
310  }
311  }
312  if (!found) {
313  LOG(ERROR) << "Course " << val << " does not exist.";
314  }
315  found = false;
316  } else if (obj.fieldType == FieldValuesType::SEGMENT) {
317  for (unsigned i = 0; i < obj.timetabler->data.segments.size(); i++) {
318  if (obj.timetabler->data.segments[i].getName() == val) {
319  found = true;
320  obj.segmentValues.push_back(i);
321  break;
322  }
323  }
324  if (!found) {
325  LOG(ERROR) << "Segment " << val << " does not exist.";
326  }
327  found = false;
328  } else if (obj.fieldType == FieldValuesType::PROGRAM) {
329  for (unsigned i = 0; i < obj.timetabler->data.programs.size(); i++) {
330  if (obj.timetabler->data.programs[i].getNameWithType() == val) {
331  found = true;
332  obj.programValues.push_back(i);
333  break;
334  }
335  }
336  if (!found) {
337  LOG(ERROR) << "Program " << val << " does not exist.";
338  }
339  found = false;
340  } else if (obj.fieldType == FieldValuesType::ISMINOR) {
341  for (unsigned i = 0; i < obj.timetabler->data.isMinors.size(); i++) {
342  if (obj.timetabler->data.isMinors[i].getName() == val) {
343  found = true;
344  obj.isMinorValues.push_back(i);
345  break;
346  }
347  }
348  if (!found) {
349  LOG(ERROR) << "IsMinor " << val << " does not exist.";
350  }
351  found = false;
352  } else if (obj.fieldType == FieldValuesType::CLASSROOM) {
353  for (unsigned i = 0; i < obj.timetabler->data.classrooms.size(); i++) {
354  if (obj.timetabler->data.classrooms[i].getName() == val) {
355  found = true;
356  obj.classValues.push_back(i);
357  break;
358  }
359  }
360  if (!found) {
361  LOG(ERROR) << "Classroom " << val << " does not exist.";
362  }
363  found = false;
364  } else if (obj.fieldType == FieldValuesType::SLOT) {
365  for (unsigned i = 0; i < obj.timetabler->data.slots.size(); i++) {
366  if (obj.timetabler->data.slots[i].getName() == val) {
367  found = true;
368  obj.slotValues.push_back(i);
369  break;
370  }
371  }
372  if (!found) {
373  LOG(ERROR) << "Slot " << val << " does not exist.";
374  }
375  found = false;
376  }
377  }
378 };
379 
383 struct allvalues : pegtl::pad<pegtl::one<'*'>, pegtl::space> {};
384 template <>
385 struct action<allvalues> {
386  template <typename Input>
387  static void apply(const Input &in, Object &obj) {
388  std::string val = in.string();
389  if (obj.fieldType == FieldValuesType::INSTRUCTOR) {
390  for (unsigned i = 0; i < obj.timetabler->data.instructors.size(); i++) {
391  obj.instructorValues.push_back(i);
392  }
393  } else if (obj.fieldType == FieldValuesType::COURSE) {
394  for (unsigned i = 0; i < obj.timetabler->data.courses.size(); i++) {
395  obj.courseValues.push_back(i);
396  }
397  } else if (obj.fieldType == FieldValuesType::SEGMENT) {
398  for (unsigned i = 0; i < obj.timetabler->data.segments.size(); i++) {
399  obj.segmentValues.push_back(i);
400  }
401  } else if (obj.fieldType == FieldValuesType::PROGRAM) {
402  for (unsigned i = 0; i < obj.timetabler->data.programs.size(); i++) {
403  obj.programValues.push_back(i);
404  }
405  } else if (obj.fieldType == FieldValuesType::ISMINOR) {
406  for (unsigned i = 0; i < obj.timetabler->data.isMinors.size(); i++) {
407  obj.isMinorValues.push_back(i);
408  }
409  } else if (obj.fieldType == FieldValuesType::CLASSROOM) {
410  for (unsigned i = 0; i < obj.timetabler->data.classrooms.size(); i++) {
411  obj.classValues.push_back(i);
412  }
413  } else if (obj.fieldType == FieldValuesType::SLOT) {
414  for (unsigned i = 0; i < obj.timetabler->data.slots.size(); i++) {
415  obj.slotValues.push_back(i);
416  }
417  }
418  }
419 };
420 
425 struct sameval : pegtl::pad<TAO_PEGTL_KEYWORD("SAME"), pegtl::space> {};
426 template <>
427 struct action<sameval> {
428  template <typename Input>
429  static void apply(const Input &in, Object &obj) {
430  if (obj.fieldType == FieldValuesType::CLASSROOM) {
431  obj.classSame = true;
432  } else if (obj.fieldType == FieldValuesType::SLOT) {
433  obj.slotSame = true;
434  }
435  }
436 };
437 
442 struct notsameval : pegtl::pad<TAO_PEGTL_KEYWORD("NOTSAME"), pegtl::space> {};
443 template <>
445  template <typename Input>
446  static void apply(const Input &in, Object &obj) {
447  if (obj.fieldType == FieldValuesType::CLASSROOM) {
448  obj.classNotSame = true;
449  } else if (obj.fieldType == FieldValuesType::SLOT) {
450  obj.slotNotSame = true;
451  }
452  }
453 };
454 
459  : pegtl::seq<pegtl::pad<pegtl::one<'{'>, pegtl::space>,
460  pegtl::list<value, pegtl::one<','>, pegtl::space>,
461  pegtl::pad<pegtl::one<'}'>, pegtl::space>> {};
462 
466 struct values : pegtl::sor<allvalues, listvalues, sameval, notsameval> {};
467 
472  : pegtl::seq<pegtl::pad<classroomstr, pegtl::space>, values> {};
473 
477 struct slotdecl : pegtl::seq<pegtl::pad<slotstr, pegtl::space>, values> {};
478 
483  : pegtl::seq<pegtl::pad<coursestr, pegtl::space>, values> {};
484 template <>
486  template <typename Input>
487  static void apply(const Input &in, Object &obj) {
488  obj.courseExcept = false;
489  }
490 };
491 
496  : pegtl::seq<pegtl::pad<coursestr, pegtl::space>,
497  pegtl::pad<exceptstr, pegtl::space>, values> {};
498 template <>
500  template <typename Input>
501  static void apply(const Input &in, Object &obj) {
502  obj.courseExcept = true;
503  }
504 };
505 
509 struct coursedecl : pegtl::sor<coursenoexceptdecl, courseexceptdecl> {};
510 template <>
512  template <typename Input>
513  static void apply(const Input &in, Object &obj) {
514  obj.isNot = false;
515  }
516 };
517 
521 struct decl : pegtl::sor<slotdecl, classroomdecl> {};
522 
526 struct decls : pegtl::list<decl, andstr, pegtl::space> {};
527 
531 struct fielddecl : pegtl::seq<pegtl::pad<fieldtype, pegtl::space>, values> {};
532 
536 struct fielddecls : pegtl::opt<pegtl::list<fielddecl, andstr, pegtl::space>> {};
537 
542  : pegtl::seq<coursedecl, pegtl::pad<unbundlestr, pegtl::space>, fielddecls,
543  pegtl::opt<notstr>, pegtl::pad<instr, pegtl::space>, decl,
544  pegtl::pad<weightstr, pegtl::space>,
545  pegtl::pad<integer, pegtl::space>> {};
546 template <>
548  template <typename Input>
549  static void apply(const Input &in, Object &obj) {
550  if (obj.courseExcept) {
551  std::vector<int> courseVals;
552  for (unsigned i = 0; i < obj.timetabler->data.courses.size(); i++) {
553  if (std::find(obj.courseValues.begin(), obj.courseValues.end(), i) ==
554  obj.courseValues.end()) {
555  courseVals.push_back(i);
556  }
557  }
558  obj.courseValues = courseVals;
559  }
560  for (unsigned i = 0; i < obj.courseValues.size(); i++) {
561  int course = obj.courseValues[i];
562  Clauses ante, cons, clause;
563  ante = makeAntecedent(obj, course);
564  cons = makeConsequent(obj, course, i);
565  if (obj.isNot) {
566  cons = ~cons;
567  }
568  clause = ante >> cons;
569  obj.constraint = clause;
570  obj.timetabler->data.customConstraintVars.push_back(
571  obj.timetabler->newVar());
572  int index = obj.timetabler->data.customConstraintVars.size() - 1;
573  if (obj.integer != 0) {
574  Clauses hardConsequent =
575  CClause(obj.timetabler->data.customConstraintVars[index]) >>
576  obj.constraint;
577  obj.timetabler->addClauses(hardConsequent, -1);
578  }
579  obj.timetabler->data.customMap[index] = course;
580  obj.timetabler->addHighLevelCustomConstraintClauses(index, obj.integer);
581  }
582  obj.courseValues.clear();
583  obj.instructorValues.clear();
584  obj.isMinorValues.clear();
585  obj.programValues.clear();
586  obj.segmentValues.clear();
587  obj.classValues.clear();
588  obj.slotValues.clear();
589  obj.isNot = false;
590  obj.classSame = false;
591  obj.slotSame = false;
592  obj.classNotSame = false;
593  obj.slotNotSame = false;
594  }
595 };
596 
601  : pegtl::seq<coursedecl, pegtl::pad<bundlestr, pegtl::space>, fielddecls,
602  pegtl::opt<notstr>, pegtl::pad<instr, pegtl::space>, decl,
603  pegtl::pad<weightstr, pegtl::space>,
604  pegtl::pad<integer, pegtl::space>> {};
605 template <>
607  template <typename Input>
608  static void apply(const Input &in, Object &obj) {
609  Clauses clauses;
610  if (obj.courseExcept) {
611  std::vector<int> courseVals;
612  for (unsigned i = 0; i < obj.timetabler->data.courses.size(); i++) {
613  if (std::find(obj.courseValues.begin(), obj.courseValues.end(), i) ==
614  obj.courseValues.end()) {
615  courseVals.push_back(i);
616  }
617  }
618  obj.courseValues = courseVals;
619  }
620  for (unsigned i = 0; i < obj.courseValues.size(); i++) {
621  int course = obj.courseValues[i];
622  Clauses ante, cons, clause;
623  ante = makeAntecedent(obj, course);
624  cons = makeConsequent(obj, course, i);
625  if (obj.isNot) {
626  cons = ~cons;
627  }
628  clause = ante >> cons;
629  clauses = clauses & clause;
630  }
631 
632  obj.constraint = clauses;
633 
634  obj.timetabler->data.customConstraintVars.push_back(
635  obj.timetabler->newVar());
636  int index = obj.timetabler->data.customConstraintVars.size() - 1;
637  if (obj.integer != 0) {
638  Clauses hardConsequent =
639  CClause(obj.timetabler->data.customConstraintVars[index]) >>
640  obj.constraint;
641  obj.timetabler->addClauses(hardConsequent, -1);
642  }
643  obj.timetabler->addHighLevelCustomConstraintClauses(index, obj.integer);
644 
645  obj.courseValues.clear();
646  obj.instructorValues.clear();
647  obj.isMinorValues.clear();
648  obj.programValues.clear();
649  obj.segmentValues.clear();
650  obj.classValues.clear();
651  obj.slotValues.clear();
652  obj.isNot = false;
653  obj.classSame = false;
654  obj.slotSame = false;
655  obj.classNotSame = false;
656  obj.slotNotSame = false;
657  }
658 };
659 
663 struct grammar
664  : pegtl::try_catch<
665  pegtl::must<pegtl::star<pegtl::sor<constraint_bundle, constraint_unbundle>>,
666  pegtl::eof>> {};
667 
668 template <typename Rule>
669 struct control : pegtl::normal<Rule> {
670  template <typename Input, typename... States>
671  static void raise(const Input &in, States &&...) {
672  LOG(ERROR) << in.position() << " Error parsing custom constraints";
673  }
674 };
675 
676 } // namespace custom_constraint_grammar
677 
686 void parseCustomConstraints(std::string file,
687  ConstraintEncoder *constraintEncoder,
689  Object obj;
690  obj.constraintEncoder = constraintEncoder;
691  obj.timetabler = timetabler;
692  pegtl::file_input<> in(file);
696 }
697 
702  isNot = false;
703  classSame = false;
704  slotSame = false;
705  classNotSame = false;
706  slotNotSame = false;
707 }
Parse "NOTSAME": Used to specify constraints on courses with different field values.
Parse "SEGMENT": Similar to classroom.
Parse "SAME": Used to specify constraints on courses with same field values.
Parse "NOT": Store that NOT keyword is present in the custom constraint.
Parse courses declaration without except keyword.
Class for representing a clause.
Definition: cclause.h:21
void addHighLevelCustomConstraintClauses(int, int)
Adds high level custom constraint clauses.
Definition: timetabler.cpp:105
Parse a constraint with unbundle keyword.
Object()
Initialize object members.
std::vector< Segment > segments
Definition: data.h:52
Clauses hasSameFieldTypeAndValue(int, int, FieldType)
Gives Clauses that represent that a pair of courses have the same field value for a given FieldType...
Constraint is on one of the instructor, segment, isminor, program. isNot, classSame, slotSame, classNotSame, slotNotSame are reset.
std::vector< Classroom > classrooms
Definition: data.h:44
Parse integer: Store the integer in the object.
std::vector< Slot > slots
Definition: data.h:56
Parse "SLOT": Similar to classroom.
std::vector< Var > customConstraintVars
Definition: data.h:89
std::vector< IsMinor > isMinors
Definition: data.h:60
Parse a value of the field that is specified in the constraint.
Class for time tabler.
Definition: timetabler.h:44
Parse multi decls in consequent of the constraint.
std::vector< Instructor > instructors
Definition: data.h:40
Class for constraint encoder.
Struct for the type used by actions in the parser.
Definition: custom_parser.h:27
Clauses hasFieldTypeListedValues(int, FieldType, std::vector< int >)
Gives Clauses that represent that a Course has a field value for a given FieldType out of a list of p...
Timetabler * timetabler
Definition: main.cpp:90
Parse courses declaration with except keyword.
Parse single decl in consequent of the constraint.
Parse * as all values of the specified field.
Parse "PROGRAM": Similar to classroom.
Parse multi field decls in antecedent of the constraint.
Parse "INSTRUCTOR": Similar to classroom.
Parse a constraint with bundle keyword.
std::map< int, unsigned > customMap
Definition: data.h:128
Parse list of values of a field specified in the constraint.
std::vector< Course > courses
Definition: data.h:36
Parse "CLASSROOM": Store the field type to be classroom which will be used while forming the custom c...
Class for representing a set of clauses.
Definition: clauses.h:23
Parse "ISMNOR": Similar to classroom.
Var newVar()
Calls the formula to issue a new variable and returns it.
Definition: timetabler.cpp:262
Parse fieldd decl in antecedent of the constraint.
void parseCustomConstraints(std::string file, ConstraintEncoder *constraintEncoder, Timetabler *timetabler)
Parses custom constraints given in a file and adds them to the solver.
void addClauses(const std::vector< CClause > &, int)
Adds clauses to the solver with specified weights.
Definition: timetabler.cpp:35
Data data
Definition: timetabler.h:63
std::vector< Program > programs
Definition: data.h:48
Parse "COURSE": Similar to classroom.
Parse constraints from the file, generate error on failure.