Sports scheduling

In sports scheduling we usually have a bunch of games which are basically tasks requiring the two competing teams and a field as resources, so lets formulate this:


In [1]:
import sys;sys.path.append('../src')
from pyschedule import Scenario, solvers, plotters, alt

n_teams = 12 # Number of teams
n_fields = int(n_teams/2) # Num of fields
n_rounds = n_teams-1 # Number of rounds

# Create scenario
S = Scenario('sport_scheduling',horizon=n_rounds)

# Game tasks
Games = { (i,j) : S.Task('Game_%i_%i'%(i,j)) for i in range(n_teams) 
                                             for j in range(n_teams) if i < j }

# Team and field resources
Teams =  [ S.Resource('Team_%i'%i) for i in range(n_teams) ]
Fields = [ S.Resource('Field_%i'%i) for i in range(n_fields) ]

# Resource requirements
for i,j in Games :
    Games[i,j] += [Teams[i], Teams[j]]
    Games[i,j] += alt( Fields )

if solvers.mip.solve(S,msg=1):
    %matplotlib inline
    plotters.matplotlib.plot(S,hide_resources=Teams,fig_size=(12,5))
else:
    print('no solution found')


INFO: execution time for solving mip (sec) = 1.1226463317871094
INFO: objective = 0.0

However, the fields might be quite different, and so it would be unfair if some team needs to play more than twice on any one. We can avoid this by assigning each game a parameter for each team that plays this game and set it to one (here we simply use integers as parameters, this could also be some string in case of more parameters). Finally, we restrict any team parameter to two on any field:


In [2]:
# Teams in games as task parameters
for i,j in Games :
    Games[i,j].teams = [i,j]

# Each team at most two times per field
for j in range(n_fields):
    for i in range(n_teams) :
        S += Fields[j][lambda T,i=i: i in T.teams] <= 2
        
if solvers.mip.solve(S,kind='CBC',msg=1): 
    plotters.matplotlib.plot(S,hide_resources=Teams,fig_size=(12,5))
else:
    print('no solution found')


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-16128ad9a132> in <module>()
      7 for j in range(n_fields):
      8     for i in range(n_teams) :
----> 9         S += Fields[j][lambda T,i=i: i in T.teams] <= 2
     10 
     11 if solvers.mip.solve(S,kind='CBC',msg=1):

TypeError: unorderable types: NoneType() <= int()

Sometimes we want to fix some games to specific rounds or fields. Here we fix all games in the middle round (n_fields-1):


In [ ]:
for i in range(n_fields):
    # Restrict to specific field
    Games[2*i,2*i+1] += Fields[i]
    # Start exactly in the middle round
    S += Games[2*i,2*i+1] >= n_fields-1


if solvers.mip.solve(S,msg=1):
    plotters.matplotlib.plot(S,hide_resources=Teams,fig_size=(12,5))
else:
    print('no solution found')