1

I am trying to add a constraint_factory using OptaPy, the Python variant of OptaPlanner, (using the School Timetabling quickstart as a base) for scheduling sports matches. Each match has 2 teams, so there are 2 variables in the Match class : team1 and team2 along with a time_slot and pitch.

How can I have a constraint where I can reward or penalise if a team (in either team1 or team2 variable) has been assigned more than 2 matches in a day?

Geoffrey De Smet
  • 26,223
  • 11
  • 73
  • 120
Hebbs
  • 17
  • 2

2 Answers2

2

I suggest to approach it differently. Start from Team, join with Match where either of the teams are equal, and count() that in a groupBy(). You will need to add a composite group key in that groupBy, your team and your match day.

This is how such a constraint would look in Java, the native language of OptaPlanner:

constraintFactory.forEach(Team.class)
    .join(Match.class, 
        Joiners.filtering((team, match) -> team == match.team1 || team == match.team2))
    .groupBy((team, match) -> team, 
         (team, match) -> match.day, 
         countBi())
    .filter((team, day, matchCount) -> matchCount > 2)
    .penalize(..., (team, day, matchCount) -> matchCount - 2);

It should be relatively easy to get a Python variant off of that; another answer may provide it.

Geoffrey De Smet
  • 26,223
  • 11
  • 73
  • 120
Lukáš Petrovický
  • 3,945
  • 1
  • 11
  • 20
  • Will this work by starting with the ProblemFact (Team) instead of the PlanningEntity (Match) ? What will get penalised? – Hebbs Dec 07 '21 at 18:59
  • Not sure I understand. Yes, the constraint will work as shown, and you can see the penalty at the end, too. What am I missing? – Lukáš Petrovický Dec 07 '21 at 19:52
1

Python variant of Lukas answer:

from org.optaplanner.core.api.score.stream.ConstraintCollectors import countBi
constraint_factory.forEach(TeamClass)
    .join(MatchClass, 
          Joiners.filtering(lambda team, match: team == match.team1 or team == match.team2)) \
    .groupBy(lambda team, match: team, 
             lambda team, match: match.day, 
             countBi()) \
    .filter(lambda team, day, matchCount: matchCount > 2) \
    .penalize(..., lambda team, day, matchCount: matchCount - 2)
Christopher Chianelli
  • 1,163
  • 1
  • 8
  • 8