Did you solve this?
I played around with it (I've decided to get up to speed on CP-SAT)
and the code you posted doesn't really work as is. I found some bugs
here and there, but maybe I'm just not understanding what you are doing?
I can post my code if you want, but since I changed things slightly,
I'm going to try first to make comments inline with your code, below:
Also, I don't yet see anything in your current code to show the "pots"
stuff.
...
Okay, I have an issue with this. Why are you doing
num_matches//num_matchdays in the following loops?
if num_matches is 16, num_matchdays is 10, then
num_matches//num_matchdays = 1
Don't you really want match in range(1,num_matches + 1) ??
> for match in range(1,num_matches//num_matchdays+1): # 16
> for md in range(1,num_matchdays+1): # 10
> int_home_x_match_md[(match, md)] = model.NewIntVar(1, num_teams,
> 'home match %i matchday %i ' % (match, md))
>
> # Away main variable
> int_away_x_match_md = {}
ditto
> for match in range(1,num_matches//num_matchdays+1): # 16
> for md in range(1,num_matchdays+1): # 10
> int_away_x_match_md[(match, md)] = model.NewIntVar(1, num_teams,
> 'away match %i matchday %i ' % (match, md))
>
>
> ########### Create the constraints
> #############################################################################################
>
> # Teams shouldn't play against itself
again, and every time, no idea why you are integer dividing
> for match in range(1,num_matches//num_matchdays+1):
> for md in range(1,num_matchdays+1):
> model.Add(int_home_x_match_md[(match, md)] !=
> int_away_x_match_md[(match, md)])
>
> # Make sure the 32 teams are different for each matchday
> # Aggregate home and away integer variables in a same vector
> (int_x_match_md) in order to eventually add an AllDifferent constraint
> int_x_match_md = {}
> for match in range(1,num_matches//num_matchdays+1):
> for md in range(1,num_matchdays+1):
> int_x_match_md[(match, md)] = model.NewIntVar(1, num_teams, 'match
> %i matchday %i ' % (match, md))
> int_x_match_md[(match+num_matches//num_matchdays, md)] =
> model.NewIntVar(1, num_teams, 'match %i matchday %i ' % (match, md))
> int_x_match_md[(match, md)] = int_home_x_match_md[(match, md)]
> int_x_match_md[(match+num_matches//num_matchdays, md)] =
> int_away_x_match_md[(match, md)]
> for md in range(1,num_matchdays+1):
> model.AddAllDifferent([int_x_match_md[(match, md)] for match in
> range(1,num_matches//num_matchdays+num_matches//num_matchdays+1)])
>
So below is another issue. The way you are making unique matches is
interesting, but doing it your way caused the "alternation" constraint
below to cause the solver to bail out with "INFEASIBLE". Instead, I
would recommend something like "home * 100 + away", so you get, for
example, 132 if team 1 is home, team 32 is away.
>
> # Make the matches (pairings) all different
> int_z_match_md = {} # to create a pairing of the 2 integer variables
> int_home_x_match_md and int_away_x_match_md
> for match in range(1,num_matches//num_matchdays+1): # 16
> for md in range(1,num_matchdays+1): # 10
> int_z_match_md[(match, md)] = model.NewIntVar(1,
> num_teams*num_teams, 'z variable match %i matchday %i ' % (match, md))
> model.Add(int_z_match_md[(match, md)] ==
> int_home_x_match_md[(match, md)] * num_teams + int_away_x_match_md[(match,
> md)])
> model.AddAllDifferent([int_z_match_md[(match, md)] for md in
> range(1,num_matchdays+1) for match in
> range(1,num_matches//num_matchdays+1)])
I replaced the above with:
for match in range(1,num_matches+1): # 16
for md in range(1,num_matchdays+1): # 10
int_z_match_md[(match, md)] = model.NewIntVar(1,
num_teams*100+num_teams+1, # probably +1 is superfluous
'z variable match %i matchday %i ' % (match, md))
model.Add( int_z_match_md[(match, md)] == int_home_x_match_md[(match, md)] * 100 + int_away_x_match_md[(match, md)])
model.AddAllDifferent([int_z_match_md[(match, md)] for md in range(1,num_matchdays+1) for match in range(1,num_matches+1)])
>
For the alternation constraint, I also tweaked your code. I think
setting constraints on both home and away is overkill, possibly
leading to conflicts.
I replaced the above with:
boo_home_team = {}
# for md in range(1,num_matchdays+1):
for md in [1,2,9,10]: # only care about alternating 1&2, 9&10
for t in range(1,num_teams+1):
for match in range(1,num_matches+1):
boo_home_team[(t,match,md)] = model.NewBoolVar('home_team_'+str(t)+' is playing match '+str(match)+' on matchday '+str(md))
model.Add(int_home_x_match_md[(match, md)] == t).OnlyEnforceIf(boo_home_team[t,match,md])
for t in range(1,num_teams+1):
home_away_alternation_md1_md2 = []
home_away_alternation_md9_md10 = []
# boo_home_away_alternation_md1_md2[t] = model.NewBoolVar('home_team_'+str(t)+' is alternating on matchdays 1-2')
for match in range(1,num_matches+1):
# alternate day 1, 2
home_away_alternation_md1_md2.append(boo_home_team[(t,match,1)])
home_away_alternation_md1_md2.append(boo_home_team[(t,match,2)])
# alternate day 9, 10
home_away_alternation_md9_md10.append(boo_home_team[(t,match,9)])
home_away_alternation_md9_md10.append(boo_home_team[(t,match,10)])
model.Add(sum(home_away_alternation_md1_md2) == 1)
model.Add(sum(home_away_alternation_md9_md10) == 1)
>
>
> solver = cp_model.CpSolver()
> solver.parameters.max_time_in_seconds = 5
> solution_printer = SolutionPrinter()
> status = solver.Solve(model)
In order to see the print out of the results, I added at the end:
print('Solve status: %s' % solver.StatusName(status))
print('Optimal objective value: %i' % solver.ObjectiveValue())
print('Statistics')
print(' - conflicts : %i' % solver.NumConflicts())
print(' - branches : %i' % solver.NumBranches())
print(' - wall time : %f s' % solver.WallTime())
for md in range(1,num_matchdays+1): # 10
for match in range(1,num_matches+1): # 16
print('day',md,'match',match,'home',solver.Value(int_home_x_match_md[(match, md)]),
'away',solver.Value(int_away_x_match_md[(match, md)]),
'z match',solver.Value(int_z_match_md[(match, md)]))
Again, I'm not yet touching your "pots" of play requirement, but the
solution so far looks like teams only play each other once, and from
day 1 to day 2 and day 9 to day 10 every team is home team just once
(and away the other day).
Regards,
James E. Marca
(I will try to attach my code as an attachment so that it doesn't get formatted by my editor)