Optapy having problems with constraints (groupBy and sum)

Just starting to test optapy, I have an error trying to use optapy, groupBy and sum:

TypeError: 'function' object is not iterable"

probably for the argument of sum. Any help?

def lecturer_teaching_load(constraint_factory: ConstraintFactory):
    print("Restricting by Teaching Load")
    return constraint_factory.forEach(SubjectClass) \
        .groupBy(lambda subject: subject.lecturer, sum(lambda subject: subject.teaching_load)) \
        .filter(lambda lecturer, load: lecturer.real_cap > load) \
        .penalize("Capacity conflict", HardSoftScore.ONE_HARD)

Idea is to penalize if the subjects planned for the same lecturer are too many.


Solution 1:

The main problem here is you are using the python built-in sum function (which takes an iterable, and return a number), not the ConstraintCollectors.sum function (which takes a function, and return a ConstraintCollector).

JPype currently cannot distinguish between functional overloads without explicit casts, so currently you need to cast the lambda inside the sum to the proper type (in this case, java.util.function.ToIntFunction). This is done automatically for Joiners, but not for ConstraintCollectors (which use a variety of different types in different places, making it harder to automate the process without duplicating the API). This code should work:

from optapy.constraint import ConstraintCollectors
from java.util.function import ToIntFunction

def lecturer_teaching_load(constraint_factory: ConstraintFactory):
    print("Restricting by Teaching Load")
    return constraint_factory.forEach(SubjectClass) \
        .groupBy(lambda subject: subject.lecturer, ConstraintCollectors.sum(ToIntFunction @ (lambda subject: subject.teaching_load))) \
        .filter(lambda lecturer, load: lecturer.real_cap > load) \
        .penalize("Capacity conflict", HardSoftScore.ONE_HARD)