275 lines
8.8 KiB
Python
Executable File
275 lines
8.8 KiB
Python
Executable File
# %%
|
|
PROJECT_PATH = '/home/md/Work/ligalytics/leagues_stable/'
|
|
import os, sys
|
|
sys.path.insert(0, PROJECT_PATH)
|
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "leagues.settings")
|
|
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
|
|
import random
|
|
import time
|
|
|
|
# XPRESS ENVIRONMENT
|
|
os.environ['XPRESSDIR'] = "/opt/xpressmp_8.4"
|
|
os.environ['XPRESS'] = "/opt/xpressmp_8.4/bin"
|
|
os.environ['LD_LIBRARY_PATH'] = os.environ['XPRESSDIR']+"/lib:"+os.environ['LD_LIBRARY_PATH']
|
|
os.environ['DYLD_LIBRARY_PATH'] = os.environ['XPRESSDIR']+"/lib:"
|
|
os.environ['SHLIB_PATH'] = os.environ['XPRESSDIR']+"/lib:"
|
|
os.environ['LIBPATH'] = os.environ['XPRESSDIR']+"/lib:"
|
|
# os.environ['PYTHONPATH'] =os.environ['XPRESSDIR']+"/lib:"+os.environ['PYTHONPATH']
|
|
os.environ['PYTHONPATH'] =os.environ['XPRESSDIR']+"/lib:"
|
|
os.environ['CLASSPATH'] =os.environ['XPRESSDIR']+"/lib/xprs.jar:"
|
|
os.environ['CLASSPATH'] =os.environ['XPRESSDIR']+"/lib/xprb.jar:"+os.environ['CLASSPATH']
|
|
os.environ['CLASSPATH'] =os.environ['XPRESSDIR']+"/lib/xprm.jar:"+os.environ['CLASSPATH']
|
|
os.environ['PATH'] =os.environ['XPRESSDIR']+"/bin:"+os.environ['PATH']
|
|
|
|
|
|
from leagues import settings
|
|
settings.DATABASES['default']['NAME'] = PROJECT_PATH+'/db.sqlite3'
|
|
|
|
import django
|
|
django.setup()
|
|
|
|
from scheduler.models import *
|
|
from pulp import *
|
|
import csv
|
|
from multiprocessing import Pool, cpu_count
|
|
|
|
import xpress as xp
|
|
# xp.controls.outputlog = 0
|
|
|
|
|
|
|
|
scenario = Scenario.objects.get(id=8)
|
|
|
|
# %%
|
|
|
|
teamObjects = Team.objects.filter(season=scenario.season,active=True)
|
|
teams = teamObjects.values('id','country','pot')
|
|
|
|
getTeamById = {}
|
|
for t in teamObjects:
|
|
getTeamById[t.id] = f"({t.pot}) {t.name}"
|
|
|
|
|
|
countries = list(set(teamObjects.values_list('country', flat=True)))
|
|
teams_from_country = {
|
|
c:[t for t in teams if t['country']==c] for c in countries
|
|
}
|
|
|
|
pot = {}
|
|
for i in teamObjects.values_list('pot',flat=True).distinct():
|
|
pot[i] = list(teams.filter(pot=i))
|
|
|
|
teams = list(teams)
|
|
|
|
def check_feasible(fixed_games):
|
|
model = xp.problem(name='Draws', sense=xp.minimize)
|
|
model.setControl ('outputlog', 0)
|
|
|
|
x = {}
|
|
for t1 in teams:
|
|
for t2 in teams:
|
|
if t1['country'] != t2['country']:
|
|
x[t1['id'], t2['id']] = xp.var(ub=1, vartype=xp.integer)
|
|
|
|
model.addVariable(x)
|
|
|
|
# REQUIREMENTS
|
|
for t in teams:
|
|
for r in range(1,5):
|
|
model.addConstraint(xp.Sum(x[t['id'],t2['id']] for t2 in pot[r] if (t['id'],t2['id']) in x.keys()) == 1)
|
|
model.addConstraint(xp.Sum(x[t2['id'],t['id']] for t2 in pot[r] if (t2['id'],t['id']) in x.keys()) == 1)
|
|
for c in countries:
|
|
if c != t['country']:
|
|
model.addConstraint(xp.Sum(x[t['id'],t2['id']]+x[t2['id'],t['id']] for t2 in teams_from_country[c]) <= 2)
|
|
|
|
# FIXATIONS
|
|
for (t1,t2) in fixed_games:
|
|
# print("FIXING",t1,t2)
|
|
model.addConstraint(x[t1,t2] == 1)
|
|
|
|
|
|
for (t1,t2) in x.keys():
|
|
model.addConstraint(x[t1,t2] + x[t2,t1] <= 1)
|
|
|
|
start_time = time()
|
|
model.solve()
|
|
comp_time = time()-start_time
|
|
|
|
if model.getProbStatus() != 6:
|
|
# print("INFEASIBLE FOUND")
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
# %%
|
|
|
|
|
|
|
|
|
|
def simulate_draws(filename,n):
|
|
|
|
print("RUNNING ASYNC",filename)
|
|
|
|
for i in range(1, n):
|
|
if i % 100 == 0:
|
|
print("RUNNING ASYNC",filename,i)
|
|
|
|
|
|
|
|
possible_opps = {}
|
|
for pos in range(8):
|
|
p = pos//2+1
|
|
teams_from_pot = list(teamObjects.filter(pot=p).values('id','country'))
|
|
possible_opps[pos] = {
|
|
t['id']: [t2['id'] for t2 in teams_from_pot if t2['country'] != t['country']] for t in teams
|
|
}
|
|
|
|
sol_opps = {
|
|
(t['id'],p):None
|
|
for t in teams for p in range(8)
|
|
}
|
|
fixed_games = []
|
|
feasible = True
|
|
|
|
num_combinatorial_clashes = {
|
|
(t['id'],p):0 for t in teams for p in range(8)
|
|
}
|
|
|
|
num_possible_opps = {
|
|
(t['id'],p):None for t in teams for p in range(8)
|
|
}
|
|
|
|
draw_index = 1
|
|
draw_index_dict = {
|
|
t['id']:None for t in teams
|
|
}
|
|
|
|
|
|
|
|
# for p in [1,2,3,4]:
|
|
for p in [4,3,2,1]:
|
|
currentPot = list(teamObjects.filter(pot=p).values_list('id', flat=True))
|
|
|
|
while(feasible and currentPot):
|
|
# Draw Team
|
|
new_team = currentPot.pop(random.randint(0,len(currentPot)-1))
|
|
draw_index_dict[new_team] = draw_index
|
|
draw_index += 1
|
|
|
|
# print("New team",new_team,getTeamById[new_team])
|
|
for pos in range(8):
|
|
# Skip if already drawn
|
|
if sol_opps[new_team,pos]:
|
|
continue
|
|
# Update possible opponents
|
|
opponent_pool = []
|
|
for new_opponent in possible_opps[pos][new_team]:
|
|
if sol_opps[new_opponent,(2*p-1)-(pos % 2)] != None:
|
|
continue
|
|
# print(f"---> ALREADY DRAWN {pos}: {getTeamById[new_opponent]}")
|
|
else:
|
|
new_game = (new_team,new_opponent) if pos % 2 == 0 else (new_opponent,new_team)
|
|
if check_feasible(fixed_games+[new_game]):
|
|
opponent_pool.append(new_opponent)
|
|
else:
|
|
# print(f"---> CANNOT DRAW {pos}: {[getTeamById[ttt] for ttt in new_game]}")
|
|
num_combinatorial_clashes[new_team,pos] += 1
|
|
num_possible_opps[new_team,pos] = len(opponent_pool)
|
|
# Draw random opponent
|
|
new_opponent = random.choice(opponent_pool)
|
|
new_game = (new_team,new_opponent) if pos % 2 == 0 else (new_opponent,new_team)
|
|
# if not check_feasible(fixed_games+[new_game]):
|
|
# print("INFEASIBLE")
|
|
# print("POSSOPS AFTER",[getTeamById[tt] for tt in possible_opps[pos][new_team]])
|
|
# print(f"{p} - {pos} - {getTeamById[sol_opps[new_opponent,(2*p-1)-(pos % 2)]]}")
|
|
# feasible = False
|
|
# break
|
|
|
|
sol_opps[new_team,pos] = new_opponent
|
|
sol_opps[new_opponent,(2*p-1)-(pos % 2)] = new_team
|
|
if pos % 2 == 0:
|
|
possible_opps[pos+1][new_team].remove(new_opponent)
|
|
fixed_games.append(new_game)
|
|
|
|
|
|
|
|
with open(filename+".csv", "a") as f:
|
|
for t in teams:
|
|
f.write(f"{i},{t['id']},{draw_index_dict[t['id']]},{';'.join([str(sol_opps[t['id'],p]) for p in range(8)])},{';'.join([str(num_possible_opps[t['id'],p]) for p in range(8)])},{';'.join([str(num_combinatorial_clashes[t['id'],p]) for p in range(8)])}\n")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# for game in fixed_games:
|
|
# if game[0] == new_team or game[1] == new_team:
|
|
# print("\t"," vs ".join([getTeamById[tt] for tt in game]))
|
|
# print(f"--- > {draw_index_dict[new_team]}")
|
|
# for pos in range(8):
|
|
# print(f"--- > {pos} - {num_possible_opps[new_team,pos]}")
|
|
|
|
# iterate over h/a-encounters
|
|
# for current_pos in range(8):
|
|
# while (sol_opps[new_team,current_pos] == None):
|
|
# # print(possible_opps[current_pos][new_team])
|
|
# # draw opponent
|
|
# new_opponent = random.choice(possible_opps[current_pos][new_team])
|
|
# print("\tNEW OPP",new_opponent,getTeamById[new_opponent])
|
|
# new_game = (new_team,new_opponent) if current_pos % 2 == 0 else (new_opponent,new_team)
|
|
# if check_feasible(fixed_games+[new_game]):
|
|
# # print("FEASIBLE",new_game,[getTeamById[tt] for tt in new_game])
|
|
# sol_opps[new_team,current_pos] = new_opponent
|
|
# sol_opps[new_opponent,(2*p-1)-(current_pos % 2)] = new_team
|
|
# fixed_games.append(new_game)
|
|
# possible_opps[current_pos][new_team].remove(new_opponent)
|
|
# else:
|
|
# possible_opps[current_pos][new_team].remove(new_opponent)
|
|
# %%
|
|
|
|
|
|
# # SOLUTION
|
|
# for g in fixed_games:
|
|
# print(getTeamById[g[0]],getTeamById[g[1]])
|
|
|
|
|
|
|
|
|
|
from multiprocessing import Pool, cpu_count
|
|
n = sys.maxsize
|
|
|
|
pool = Pool()
|
|
result = {}
|
|
answer = {}
|
|
n_threads = 8
|
|
|
|
for cpu in range(n_threads):
|
|
result[cpu] = pool.apply_async(simulate_draws, args=(f'thread_{cpu}', n,))
|
|
|
|
for cpu in range(n_threads):
|
|
answer[cpu] = result[cpu].get()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# %%
|
|
|
|
|
|
|
|
|
|
|
|
# %%
|