Spaces:
Sleeping
Sleeping
Update objective_engine.py
Browse files- objective_engine.py +42 -54
objective_engine.py
CHANGED
|
@@ -108,7 +108,7 @@ class ObjectiveEngine:
|
|
| 108 |
self._avoid_late_heavy_subjects()
|
| 109 |
self._minimize_faculty_gaps()
|
| 110 |
self._minimize_campus_movement()
|
| 111 |
-
self.
|
| 112 |
self._minimize_student_gaps()
|
| 113 |
|
| 114 |
# Summation of all penalties
|
|
@@ -457,63 +457,51 @@ class ObjectiveEngine:
|
|
| 457 |
self.penalties.append(both_occupied * max(1, weight // 2))
|
| 458 |
|
| 459 |
def _penalize_first_hour_free(self):
|
|
|
|
| 460 |
"""
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
|
| 465 |
-
Rewritten to reuse cached day booleans and daily slot vars for efficiency.
|
| 466 |
"""
|
| 467 |
-
weight = self.weights.get("
|
| 468 |
-
if weight == 0:
|
| 469 |
-
return
|
| 470 |
|
| 471 |
-
|
| 472 |
-
|
| 473 |
for task in self.tasks:
|
| 474 |
-
|
| 475 |
-
parent = sid.split('-')[0] if '-' in sid else sid
|
| 476 |
-
tasks_by_parent[parent].append(task)
|
| 477 |
|
| 478 |
-
for sec_id, sec_tasks in
|
| 479 |
-
|
| 480 |
-
for task in sec_tasks:
|
| 481 |
-
self._ensure_day_bools(task)
|
| 482 |
|
| 483 |
for day in range(const.NUM_WORKING_DAYS):
|
| 484 |
-
|
| 485 |
-
|
| 486 |
-
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
|
| 491 |
-
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
start_var
|
| 496 |
-
|
| 497 |
-
start_var
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
self.model.
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
|
| 514 |
-
self.model.Add(
|
| 515 |
-
first_free == 1).OnlyEnforceIf(any_at_first.Not())
|
| 516 |
-
self.model.Add(
|
| 517 |
-
first_free == 0).OnlyEnforceIf(any_at_first)
|
| 518 |
-
|
| 519 |
-
self.penalties.append(first_free * weight)
|
|
|
|
| 108 |
self._avoid_late_heavy_subjects()
|
| 109 |
self._minimize_faculty_gaps()
|
| 110 |
self._minimize_campus_movement()
|
| 111 |
+
self._pack_morning_session()
|
| 112 |
self._minimize_student_gaps()
|
| 113 |
|
| 114 |
# Summation of all penalties
|
|
|
|
| 457 |
self.penalties.append(both_occupied * max(1, weight // 2))
|
| 458 |
|
| 459 |
def _penalize_first_hour_free(self):
|
| 460 |
+
def _pack_morning_session(self):
|
| 461 |
"""
|
| 462 |
+
Penalizes any free slot in the first 4 hours (morning session) for any section.
|
| 463 |
+
This strongly forces the solver to pack the morning tightly, leaving afternoons free if necessary,
|
| 464 |
+
and completely eliminating gaps in the first 4 hours.
|
|
|
|
|
|
|
| 465 |
"""
|
| 466 |
+
weight = self.weights.get("pack_morning", 500)
|
| 467 |
+
if weight == 0: return
|
|
|
|
| 468 |
|
| 469 |
+
from collections import defaultdict
|
| 470 |
+
tasks_by_sec = defaultdict(list)
|
| 471 |
for task in self.tasks:
|
| 472 |
+
tasks_by_sec[task.section.section_id].append(task)
|
|
|
|
|
|
|
| 473 |
|
| 474 |
+
for sec_id, sec_tasks in tasks_by_sec.items():
|
| 475 |
+
if not sec_tasks: continue
|
|
|
|
|
|
|
| 476 |
|
| 477 |
for day in range(const.NUM_WORKING_DAYS):
|
| 478 |
+
for slot in range(4): # First 4 hours
|
| 479 |
+
abs_slot = day * const.NUM_TEACHING_SLOTS_PER_DAY + slot
|
| 480 |
+
slot_covs = []
|
| 481 |
+
|
| 482 |
+
for task in sec_tasks:
|
| 483 |
+
start_var = self.ce.task_vars[task.task_id][0]
|
| 484 |
+
cov = self.model.NewBoolVar(f"morn_cov_{task.task_id}_s{abs_slot}")
|
| 485 |
+
b1 = self.model.NewBoolVar(f"mb1_{task.task_id}_s{abs_slot}")
|
| 486 |
+
b2 = self.model.NewBoolVar(f"mb2_{task.task_id}_s{abs_slot}")
|
| 487 |
+
|
| 488 |
+
self.model.Add(start_var <= abs_slot).OnlyEnforceIf(b1)
|
| 489 |
+
self.model.Add(start_var > abs_slot).OnlyEnforceIf(b1.Not())
|
| 490 |
+
self.model.Add(start_var + task.duration > abs_slot).OnlyEnforceIf(b2)
|
| 491 |
+
self.model.Add(start_var + task.duration <= abs_slot).OnlyEnforceIf(b2.Not())
|
| 492 |
+
|
| 493 |
+
self.model.AddBoolAnd([b1, b2]).OnlyEnforceIf(cov)
|
| 494 |
+
self.model.AddBoolOr([b1.Not(), b2.Not()]).OnlyEnforceIf(cov.Not())
|
| 495 |
+
slot_covs.append(cov)
|
| 496 |
+
|
| 497 |
+
is_active = self.model.NewBoolVar(f"morn_act_{sec_id}_d{day}_s{slot}")
|
| 498 |
+
self.model.AddBoolOr(slot_covs).OnlyEnforceIf(is_active)
|
| 499 |
+
for c in slot_covs:
|
| 500 |
+
self.model.AddImplication(is_active.Not(), c.Not())
|
| 501 |
+
|
| 502 |
+
# We penalize if is_active is False
|
| 503 |
+
is_free = self.model.NewBoolVar(f"morn_free_{sec_id}_d{day}_s{slot}")
|
| 504 |
+
self.model.Add(is_active == 0).OnlyEnforceIf(is_free)
|
| 505 |
+
self.model.Add(is_active == 1).OnlyEnforceIf(is_free.Not())
|
| 506 |
+
|
| 507 |
+
self.penalties.append(is_free * weight)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|