Search code examples
pythonalgorithmschedulingor-tools

ORtools scheduling students to classes with conflicting schedules


In my problem, I have students who each need to be assigned to one class. Each student and each class has a schedule, and if a student is assigned to a class, the schedules cannot conflict.

As a toy example, say there are five time blocks. If a student's schedule is filled on the third time block, but free on all others, their schedule will look like [0,0,1,0,0]. Similarly, if a class takes place on the first and second time chunks, its schedule will look like [1,1,0,0,0]. Because this student's schedule does not conflict with this class' schedule, the student can be assigned to this class. However, if the student's schedule was [1,0,0,0,0], they could not be assigned to the class.

Using google's ortools, the constraints then seem pretty simple: only one class per student, and if we have 2 students schedules S and 3 class schedules C:

S = {0:[0,0,1,0,0],1:[0,1,0,1,0]}
C = {0:[1,1,0,0,0],1:[0,0,1,0,1],2:[1,0,0,0,0]}

Then student [i] can be assigned to class [j] only if np.dot(np.asarray(S[s]),np.asarray(C[c]))==0, i.e. the dot product of the schedules is zero.

Implementing the latter constraint hasn't worked for me. I've tried the following:

num_students = 2
    num_classes = 3
    all_students = range(num_students)
    all_classes = range(num_classes)

    S = {0:[0,0,1,0,0],1:[0,1,0,1,0]}
    C = {0:[1,1,0,0,0],1:[0,0,1,0,1],2:[1,0,0,0,0]}

    # Creates the model.
    model = cp_model.CpModel()

    # Creates scheduling variables.
    # shifts[(c, s)]: student c is assigned to class s.
    # 1 if true, 0 if not
    sched = {}
    for s in all_students:
        for c in all_classes:
                sched[(c,s)] = model.NewBoolVar('shift_c%is%i' % (c, s))

    # Each class is assigned to exactly one student in the schedule period.
    for s in all_students:
        model.Add(sum(sched[(c, s)] for c in all_classes) == 1)

    # The schedules cannot conflict
    for c in all_classes:
        for s in all_students:
            model.AddBoolOr([(sched[(c,s)] and np.dot(np.asarray(S[s]),np.asarray(C[c]))==0),
                            sched[(c,s)].Not()])

However, when I run this, I get the following error:

<ipython-input-41-9aad6f1c8113> in main()
     55         for s in all_students:
     56             model.AddBoolOr([(sched[(c,s)] and np.dot(np.asarray(S[s]),np.asarray(C[c]))==0),
---> 57                             sched[(c,s)].Not()])
     58 
     59 

/anaconda3/lib/python3.6/site-packages/ortools/sat/python/cp_model.py in AddBoolOr(self, literals)
   1162         model_ct = self.__model.constraints[ct.Index()]
   1163         model_ct.bool_or.literals.extend(
-> 1164             [self.GetOrMakeBooleanIndex(x) for x in literals])
   1165         return ct
   1166 

/anaconda3/lib/python3.6/site-packages/ortools/sat/python/cp_model.py in <listcomp>(.0)
   1162         model_ct = self.__model.constraints[ct.Index()]
   1163         model_ct.bool_or.literals.extend(
-> 1164             [self.GetOrMakeBooleanIndex(x) for x in literals])
   1165         return ct
   1166 

/anaconda3/lib/python3.6/site-packages/ortools/sat/python/cp_model.py in GetOrMakeBooleanIndex(self, arg)
   1408         else:
   1409             raise TypeError('NotSupported: model.GetOrMakeBooleanIndex(' +
-> 1410                             str(arg) + ')')
   1411 
   1412     def GetIntervalIndex(self, arg):

TypeError: NotSupported: model.GetOrMakeBooleanIndex(True)

Is this just a matter of reformatting the AddBoolOr statement, or am I missing something bigger?

Any help appreciated!


Solution

    • You cannot use min, max, or, and with variables created by ortools.
    • AddBoolOr expects only BoolVars.

    With your problem, you can do one of the following:

    • Fix the value
    if np.dot(np.asarray(S[s]),np.asarray(C[c])) != 0:
        model.Add(sched[(c,s)] == 0)
    
    • Do not create the boolean if np.dot(np.asarray(S[s]),np.asarray(C[c]))!=0