I have an assignment type problem in which there is a set of students, $S$, and a set of training classes $T$. Each training has a fixed start and end day and can accommodate at most 1 student. In addition, a training will give a student credit towards two types of requirements, and each training varies in how much credit is given for each type. A student must receive enough credit of type 1 and type 2 in order to be considered trained. For example, student ABC needs 2 credits of type 1, and 5 credits of type 2, and can be assigned to training 123, and receive 1 credit toward requirement 1, and 3 credits toward requirement 2.
Students become first available for training on different days (and have days off after too), and the model objective is to minimize the total wait time across all students, where wait time is defined as the number of days from when a student first becomes available to when they are finished with training.
In general the training dates can overlap, and a student can only take the class if they are available for the entire time of the class.
So far I have modeled the problem with arcs, where $a_{\text{st}} \in\{0,1\}$ is created if a student can possibly take the training (i.e., student is available during training). Each arc also has an attribute called wait_days
which is equal to the number of days from when the student first became available to the last day of the training on that arc.
I create sets of trainings that have overlapping dates and then create a constraint in the model so that for each student at most 1 training from each of these sets is assigned in order to avoid scheduling conflicts.
conflict_sets = set()
for t1 in trainings:
conflicts = [t1]
for t2 in trainings:
if t1.start <= t2.end and t1.start >= t2.start:
conflicts.append[t2]
conflict_sets.add(conflicts)
I have a model using the CP solver from Google OR-tools that assigns students to trainings so that the student completes both training requirements, there are no scheduling conflicts, and then it minimizes total wait time summed across all students.
from ortools.sat.python import cp_model
model = cp_model.CpModel()
arc_vars = {}
for arc in arcs:
arc_vars[arc] = model.NewBoolVar(f'arc_{arc}')
wait_vars = {}
# total wait is at most 31 days
for s in students:
wait_vars[s] = model.NewIntVar(0,31, f'wait_{s}')
for s in students:
# ensure student completes both types of necessary training
model.Add(sum(arc_vars[a] * a[1].type1 for a in arcs if a[0]==s) >= s.Type1_req)
model.Add(sum(arc_vars[a] * a[1].type2 for a in arcs if a[0]==s) >= s.Type2_req)
# student can't be in trainings that have overlapping times
for c_list in conflict_sets:
model.Add(sum(arc_vars[(s, t)] for t in c_list if (s,t) in arcs) <= 1)
# make wait variable the max waiting days
# across all arcs a student is assigned to
for a in arcs:
model.Add(wait_vars[a[0]] >= arc_vars[a]*arcs[a].waiting_days)
# minimize total wait time in system
model.Minimize(sum(wait_vars[s] for s in students))
Am I modeling this in an efficient way? I am new to constraint programming, and I believe this would be a good way to model it as an IP, but I'm not sure if this is true for CP. I switched to CP because it has been solving much faster than my original IP formulation. I was thinking maybe there would be a better to model the wait time or the training conflicts with a CP paradigm but I'm not sure.
max_end_of_all_trainings - min_end_of_all_trainings + 1 - sum_of_durations_of_all_trainings
. $\endgroup$