KindAlien commited on
Commit
7e1dc8d
·
verified ·
1 Parent(s): c8e1d7a

Update constraint_engine.py

Browse files
Files changed (1) hide show
  1. constraint_engine.py +32 -0
constraint_engine.py CHANGED
@@ -315,3 +315,35 @@ class ConstraintEngine:
315
  for lit in at_slot: self.model.AddImplication(is_active.Not(), lit.Not())
316
  literals.append(is_active)
317
  self.model.Add(sum(literals) <= self.max_continuous_stretch)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
315
  for lit in at_slot: self.model.AddImplication(is_active.Not(), lit.Not())
316
  literals.append(is_active)
317
  self.model.Add(sum(literals) <= self.max_continuous_stretch)
318
+
319
+ def _apply_no_first_hour_free(self):
320
+ """
321
+ HARD CONSTRAINT:
322
+ Ensures that the first hour (slot 0) of every day is never free for any section.
323
+ Every section must have at least one class starting at slot 0 every day.
324
+ """
325
+ from collections import defaultdict
326
+ import constants as const
327
+
328
+ tasks_by_sec = defaultdict(list)
329
+ for task in self.tasks:
330
+ tasks_by_sec[task.section.section_id].append(task)
331
+
332
+ for sec_id, sec_tasks in tasks_by_sec.items():
333
+ if not sec_tasks: continue
334
+
335
+ for day in range(const.NUM_WORKING_DAYS):
336
+ first_slot = day * const.NUM_TEACHING_SLOTS_PER_DAY
337
+
338
+ starts_at_first = []
339
+ for task in sec_tasks:
340
+ start_var = self.task_vars[task.task_id][0]
341
+ # We create a boolean variable that is True if start_var == first_slot
342
+ at_first = self.model.NewBoolVar(f"hard_at1st_{task.task_id}_d{day}")
343
+ self.model.Add(start_var == first_slot).OnlyEnforceIf(at_first)
344
+ self.model.Add(start_var != first_slot).OnlyEnforceIf(at_first.Not())
345
+ starts_at_first.append(at_first)
346
+
347
+ if starts_at_first:
348
+ # At least one task MUST start at the first slot
349
+ self.model.AddBoolOr(starts_at_first)