1419 lines
56 KiB
Python
Executable File
1419 lines
56 KiB
Python
Executable File
|
|
# %%
|
|
import os, sys
|
|
from dotenv import load_dotenv
|
|
load_dotenv()
|
|
|
|
# %%
|
|
|
|
if os.environ.get('SERVER', None):
|
|
PROJECT_PATH = '/home/django/leagues/'
|
|
else:
|
|
PROJECT_PATH = '/home/md/Work/ligalytics/leagues_stable/'
|
|
|
|
import os, sys
|
|
import json
|
|
from numpyencoder import NumpyEncoder
|
|
|
|
|
|
sys.path.insert(0, PROJECT_PATH)
|
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "leagues.settings")
|
|
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
|
|
|
|
from leagues import settings
|
|
# settings.DATABASES['default']['NAME'] = PROJECT_PATH+'/db.sqlite3'
|
|
if os.environ.get('SERVER', None):
|
|
settings.DATABASES['default']['ENGINE'] = 'django.db.backends.postgresql'
|
|
settings.DATABASES['default']['HOST'] = '0.0.0.0'
|
|
settings.DATABASES['default']['PORT'] = '5433'
|
|
settings.DATABASES['default']['USER'] = 'leagues_user'
|
|
settings.DATABASES['default']['PASSWORD'] = 'ligalytics'
|
|
settings.DATABASES['default']['NAME'] = 'prod_16'
|
|
else:
|
|
settings.DATABASES['default']['NAME'] = PROJECT_PATH+'/db.sqlite3'
|
|
settings.DATABASES['default']['ENGINE'] = 'django.db.backends.postgresql'
|
|
settings.DATABASES['default']['HOST'] = '0.0.0.0'
|
|
settings.DATABASES['default']['PORT'] = '5432'
|
|
settings.DATABASES['default']['USER'] = 'postgres'
|
|
settings.DATABASES['default']['PASSWORD'] = 'secret123'
|
|
settings.DATABASES['default']['NAME'] = 'mypgsqldb'
|
|
settings.DATABASES['default']['ATOMIC_REQUESTS'] = False
|
|
settings.DATABASES['default']['AUTOCOMMIT'] = True
|
|
settings.DATABASES['default']['CONN_MAX_AGE'] = 0
|
|
settings.DATABASES['default']['CONN_HEALTH_CHECKS'] = False
|
|
settings.DATABASES['default']['OPTIONS'] = {}
|
|
|
|
|
|
os.environ["XPRESSDIR"] = "/opt/xpressmp"
|
|
os.environ["XPRESS"] = "/opt/xpressmp/bin"
|
|
os.environ["LD_LIBRARY_PATH"] = os.environ["XPRESSDIR"] + "/lib"
|
|
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["CLASSPATH"] = os.environ["XPRESSDIR"] + "/lib/xprs.jar"
|
|
os.environ["CLASSPATH"] = os.environ["XPRESSDIR"] + "/lib/xprb.jar" + os.pathsep + os.environ["CLASSPATH"]
|
|
os.environ["CLASSPATH"] = os.environ["XPRESSDIR"] + "/lib/xprm.jar" + os.pathsep + os.environ["CLASSPATH"]
|
|
os.environ["PATH"] = os.environ["XPRESSDIR"] + "/bin" + os.pathsep + os.environ["PATH"]
|
|
|
|
|
|
import django
|
|
django.setup()
|
|
|
|
|
|
# %%
|
|
|
|
from scheduler.models import *
|
|
from scheduler.helpers import *
|
|
|
|
# from scheduler.solver.optimizer import optimize_2phases
|
|
from scheduler.solver.tasks.optimize import optimize
|
|
|
|
|
|
|
|
# %%
|
|
|
|
import copy
|
|
from collections import defaultdict
|
|
from unittest import TextTestResult
|
|
from ortools.sat.python import cp_model
|
|
from pulp import *
|
|
import random
|
|
import os
|
|
import time
|
|
import pandas as pd
|
|
from math import sqrt, sin, cos, atan2, pi
|
|
import numpy as np
|
|
|
|
# XPRESS ENVIRONMENT
|
|
os.environ['XPRESSDIR'] = "/opt/xpressmp_9.2.5"
|
|
os.environ['XPRESS'] = "/opt/xpressmp_9.2.5/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']
|
|
|
|
import xpress as xp
|
|
xp.controls.outputlog = 0
|
|
|
|
|
|
|
|
import datetime
|
|
from dateutil.parser import parse
|
|
|
|
def Availabilities(scenario_id,showSolution = "0"):
|
|
tt = time.time()
|
|
sc = Scenario.objects.get(id=scenario_id)
|
|
teams = Team.objects.filter(season=sc.season,active=True).order_by('name').exclude(name='-')
|
|
realteams= [ t.id for t in teams]
|
|
getTeamById= { t.id : t for t in teams}
|
|
t_short= { t.id : t.shortname for t in teams}
|
|
blockings = Blocking.objects.filter(scenario=sc,team__active=True).exclude(day__round=0).order_by('day', 'team').annotate(
|
|
dayID=F('day__id'),
|
|
teamID=F('team__id'),
|
|
)
|
|
days = Day.objects.filter(season=sc.season).order_by('day','round')
|
|
|
|
getDay = { d.id : d for d in days}
|
|
getDateTimeDay = { d.id : '' for d in days}
|
|
for d in days:
|
|
if d.day[0]!='R' :
|
|
getDateTimeDay[d.id] = parse(d.day)
|
|
|
|
getDayByDateTime = { getDateTimeDay[d.id] : d for d in days}
|
|
daysSorted =[]
|
|
for dt in sorted([ getDateTimeDay[d.id] for d in days]):
|
|
daysSorted.append ( getDayByDateTime[dt] )
|
|
|
|
usedDays =getDateTimeDay.values()
|
|
for d in daysSorted:
|
|
d.firstDayInSequence = (getDateTimeDay[d.id] - datetime.timedelta(days=1)) not in usedDays
|
|
|
|
nRounds = sc.season.nRounds
|
|
rounds=range(1,nRounds+1)
|
|
breaks = Break.objects.filter(season=sc.season).values()
|
|
|
|
# SLOWING VIEW DOWN
|
|
# for bl in blockings:
|
|
# if bl.day.round==0 or not bl.team.active:
|
|
# bl.delete()
|
|
# print ("blocking not assigned to round: deleting " , bl)
|
|
blockings.filter(day__round=0).exclude(team__active=False).delete()
|
|
|
|
blockRefreshNecessary = False
|
|
blockDict = defaultdict(dict)
|
|
for bl in blockings:
|
|
if not blockDict[(bl.dayID,bl.teamID)]:
|
|
blockDict[(bl.dayID,bl.teamID)]=bl
|
|
else:
|
|
if (blockDict[(bl.dayID,bl.teamID)].type=="Home" and bl.type=="Away") or (blockDict[(bl.dayID,bl.teamID)].type=="Away" and bl.type=="Home"):
|
|
blockDict[(bl.dayID,bl.teamID)].type="Game"
|
|
blockDict[(bl.dayID,bl.teamID)].tag=None
|
|
blockDict[(bl.dayID,bl.teamID)].save()
|
|
bl.delete()
|
|
blockRefreshNecessary = True
|
|
|
|
if blockRefreshNecessary:
|
|
blockings = Blocking.objects.filter(scenario=sc, team__active=True).exclude(day__round=0).order_by('day', 'team').annotate(
|
|
dayID=F('day__id'),
|
|
teamID=F('team__id'),
|
|
)
|
|
|
|
isBlocked = { (d.id,t.id,ha) : False for t in teams for d in days for ha in ['H','A'] }
|
|
isBlockedTags = { (d.id,t.id,ha) : [] for t in teams for d in days for ha in ['H','A'] }
|
|
blockComment = defaultdict(dict)
|
|
for bl in blockings:
|
|
if bl.tag:
|
|
if bl.tag.blocking_weight <0.0:
|
|
continue
|
|
isBlockedTags[ (bl.day.id, bl.team.id, "H")].append(bl.tag.name)
|
|
if bl.comment not in ["","-"]:
|
|
blockComment[int(bl.day.id)][int(bl.team.id)] = bl.comment
|
|
if bl.type!="Home":
|
|
isBlocked[ (int(bl.day.id), int(bl.team.id), "A")]=True
|
|
if bl.type!="Away":
|
|
isBlocked[ (bl.day.id, bl.team.id, "H")]=True
|
|
|
|
# print ("isBlocked",isBlocked)
|
|
|
|
for b in blockings.exclude(comment__in=["","-"]):
|
|
blockComment[int(b.day.id)][int(b.team.id)] = b.comment
|
|
|
|
stadiums = Stadium.objects.filter(season=sc.season)
|
|
stadiumblockings = StadiumBlocking.objects.filter(scenario=sc, stadium__in=stadiums)
|
|
|
|
# print ("stadiumblockings",stadiumblockings)
|
|
|
|
groups = Conference.objects.filter(scenario=sc,display_group=True).order_by('name')
|
|
# firstTeams = [ g.teams.all().order_by('name').first().id for g in groups]
|
|
firstTeams = []
|
|
for g in groups:
|
|
tms = [(t.name, t.id ) for t in g.teams.all()]
|
|
tms.sort()
|
|
firstTeams.append(tms[0][1])
|
|
|
|
|
|
wd = {"Mondays":0 , "Tuesdays":1 , "Wednesdays":2 , "Thursdays":3 , "Fridays":4 , "Saturdays":5 , "Sundays":6}
|
|
t_possible_weekdays={ t.id : [] for t in teams}
|
|
t_available_slot_per_weekday={ (t.id,w) : 0 for t in teams for w in wd.keys() }
|
|
t_stadiums = { t.id : [] for t in teams}
|
|
t_site_available = { (t.id,d.id):0 for t in teams for d in days }
|
|
for t in teams:
|
|
# print ("checking stadium availabilities for " , t.name)
|
|
t_stadiums[t.id] = [sts.stadium for sts in t.stadiumpreferences.all()]
|
|
for stsp in t.stadiumTimeSlotPreferences.all():
|
|
t_possible_weekdays[t.id].append(wd[stsp.stadiumTimeSlot.weekday])
|
|
blockedDays = [ sb.day for sb in stsp.stadiumTimeSlot.stadiumtimeslotblockings.all().exclude(homeTeam_id__in=teams)]
|
|
for d in days:
|
|
if getDateTimeDay[d.id].weekday() == wd[stsp.stadiumTimeSlot.weekday] and d.day not in blockedDays:
|
|
t_site_available[(t.id,d.id)]+=1
|
|
stadiumBlocks = [ (tid,did) for (tid,did) in t_site_available.keys() if t_site_available[(tid,did)]==0 and t_possible_weekdays[tid]!=[]]
|
|
# for t,d in stadiumBlocks:
|
|
# print (getDateTimeDay[d], [ t2.name for t2 in teams if t2.id==t])
|
|
|
|
competitions = InternationalGame.objects.filter(season=sc.season, active=True)
|
|
playDays = [day.id for day in days if day.maxGames>0 and day.round>0]
|
|
compDays = {}
|
|
competitionBlocks = []
|
|
for c in competitions:
|
|
c.blocking_weight = 0.5
|
|
compDays[c.id] = []
|
|
dayBefore=datetime.timedelta(days=c.nDaysNotPlayBefore)
|
|
dayAfter=datetime.timedelta(days=c.nDaysNotPlayAfter)
|
|
for d in c.days.all():
|
|
firstDay = getDateTimeDay[d.id]-dayBefore
|
|
lastDay = getDateTimeDay[d.id]+dayAfter
|
|
compDays[c.id] += [ d2 for d2 in playDays if firstDay<= getDateTimeDay[d2] <= lastDay ]
|
|
compDays[c.id] = list(set(compDays[c.id]))
|
|
for t in c.teams.all():
|
|
for d in compDays[c.id]:
|
|
competitionBlocks.append((t.id,d))
|
|
for tp in ["H", "A"]:
|
|
isBlocked[ (d, t.id, tp)]=True
|
|
isBlockedTags[ (d, t.id, tp)].append(c.name)
|
|
|
|
if sc.season.useFeatureKickOffTime:
|
|
currentSolution= [ (int(sl[1]),int(sl[2]),int(sl[3]),int(sl[0]),sl[4]) for sl in sc.solutionlist() if len(sl)>4]
|
|
else:
|
|
currentSolution= [ (int(sl[1]),int(sl[2]),int(sl[3]),int(sl[0])) for sl in sc.solutionlist() if len(sl)>3]
|
|
|
|
currentSolutionOfDay = { d.id: [] for d in days }
|
|
currentSolutionInRound = { (t,r): "" for t in realteams for r in rounds }
|
|
currentSolutionAtDay = { (t,d.id): "" for t in realteams for d in days }
|
|
currentSolutionTimeAtDay = { (t,d.id): "" for t in realteams for d in days }
|
|
currentSolutionTimeAtDayCode = { (t,d.id): "" for t in realteams for d in days }
|
|
currentSolutionDayOfTeamRound = { (t,r) : 0 for t in realteams for r in rounds }
|
|
games={}
|
|
shortGames = defaultdict(dict)
|
|
violations =[]
|
|
fullHaName = {"H":"Home", "A":"Away"}
|
|
for cs in currentSolution:
|
|
(t1,t2,r,d)=(cs[0],cs[1],cs[2],cs[3])
|
|
if d in currentSolutionOfDay.keys():
|
|
currentSolutionInRound[(t1,r)]="H"
|
|
currentSolutionInRound[(t2,r)]="A"
|
|
currentSolutionAtDay[(t1,d)]="H"
|
|
currentSolutionAtDay[(t2,d)]="A"
|
|
currentSolutionOfDay[d].append((t1,t2,r))
|
|
currentSolutionDayOfTeamRound[(t1,r)]= d
|
|
currentSolutionDayOfTeamRound[(t2,r)]= d
|
|
if sc.season.useFeatureKickOffTime :
|
|
currentSolutionTimeAtDay[(t1,d)]=cs[4]
|
|
currentSolutionTimeAtDay[(t2,d)]=cs[4]
|
|
ts = TimeSlot.objects.filter(season=sc.season,name=cs[4]).first()
|
|
if ts:
|
|
currentSolutionTimeAtDayCode[(t1,d)]=ts.code
|
|
currentSolutionTimeAtDayCode[(t2,d)]=ts.code
|
|
for t3 in [t1,t2]:
|
|
color = "red" if isBlocked[ (d, t3, currentSolutionAtDay[(t3,d)])] else ""
|
|
games[ str(d)+"_"+str(t3)]= (currentSolutionAtDay[(t3,d)]+currentSolutionTimeAtDayCode[(t3,d)] , color)
|
|
shortGames[d][t3] = t_short[t1] +"-"+ t_short[t2]
|
|
if color=="red":
|
|
violations.append( ( getTeamById[t3], getDay[d] , fullHaName[currentSolutionAtDay[(t3,d)]] , currentSolutionTimeAtDay[(t3,d)] , ", ".join(isBlockedTags[(d,t3,currentSolutionAtDay[(t3,d)])]) ))
|
|
|
|
for bl in blockings:
|
|
if bl.tag and bl.tag.blocking_weight <0.0 and currentSolutionAtDay[(bl.team.id,bl.day.id)]!="H":
|
|
violations.append( ( getTeamById[bl.team.id], getDay[bl.day.id] , "Home" , "" , bl.tag.name ))
|
|
|
|
redStripes = []
|
|
missingBreaks = []
|
|
for bl in breaks :
|
|
for t in realteams:
|
|
if currentSolutionInRound[(t,bl['round1'])]== currentSolutionInRound[(t,bl['round2'])] and currentSolutionInRound[(t,bl['round1'])] in ["H","A"]:
|
|
redStripes.append(str(currentSolutionDayOfTeamRound[(t,bl['round1'])])+'_'+str(t) +'__'+str(currentSolutionDayOfTeamRound[(t,bl['round2'])])+'_'+str(t))
|
|
else:
|
|
missingBreaks.append(t)
|
|
|
|
if showSolution=="0":
|
|
games=[]
|
|
redStripes=[]
|
|
violations=[]
|
|
shortGames={}
|
|
|
|
context = {
|
|
# 'blockings1' : blockings1,
|
|
# 'games' : games,
|
|
# 'shortGames' : shortGames,
|
|
'violations' : violations,
|
|
# 'firstTeams' : firstTeams,
|
|
# 'blockings' : blockings,
|
|
# 'stadiumBlocks' : stadiumBlocks,
|
|
# 'competitionBlocks' : competitionBlocks,
|
|
# 'groups' : groups,
|
|
# 'days' : daysSorted,
|
|
# 'teams' : teams,
|
|
# 'comments': blockComment,
|
|
# 'redStripes': redStripes,
|
|
# 'tags': Tag.objects.filter(season=sc.season).exclude(blocking_weight=0),
|
|
# 'stadiums' : stadiums,
|
|
# 'stadiumblockings' : stadiumblockings,
|
|
# 'showStadiums' : showSolution=="0",
|
|
'missingBreaks': missingBreaks,
|
|
}
|
|
|
|
|
|
return context
|
|
|
|
|
|
|
|
class Draw_Simulator:
|
|
def __init__(self,algorithm, opponent_func, html_output, use_db=False):
|
|
|
|
|
|
self.pots = ['D','C','B','A']
|
|
|
|
if use_db:
|
|
scenario_id = 10081
|
|
scenario = Scenario.objects.get(id=scenario_id)
|
|
|
|
self.basepots = {
|
|
pot : [
|
|
{
|
|
'id': team.id,
|
|
'pot': pot,
|
|
'name': team.name,
|
|
'country': team.countryObj.name,
|
|
'coeff': team.attractivity,
|
|
'lat': team.latitude,
|
|
'lon': team.longitude,
|
|
}
|
|
for team in Team.objects.filter(season=scenario.season,pot=pot_n+1,active=True)
|
|
]
|
|
for pot_n,pot in enumerate(self.pots)
|
|
}
|
|
|
|
df_rank = pd.read_csv('rankings_2.0.csv')
|
|
for pot in self.basepots:
|
|
for team in self.basepots[pot]:
|
|
ranking = df_rank[df_rank['TEAM'] == team['name']].iloc[0]
|
|
team['coeff'] = ranking['RANK']
|
|
|
|
else:
|
|
self.basepots = {
|
|
'A': [
|
|
{'id': 0, 'pot': 'A', 'name': 'Rögle Ängelholm', 'country': 'Sweden'},
|
|
{'id': 1, 'pot': 'A', 'name': 'Färjestad Karlstad', 'country': 'Sweden'},
|
|
{'id': 2, 'pot': 'A', 'name': 'EV Zug', 'country': 'Switzerland'},
|
|
{'id': 3, 'pot': 'A', 'name': 'Eisbären Berlin', 'country': 'Germany'},
|
|
{'id': 4, 'pot': 'A', 'name': 'Tappara Tampere', 'country': 'Finland'},
|
|
{'id': 5, 'pot': 'A', 'name': 'Oceláři Třinec',
|
|
'country': 'Czech Republic'},
|
|
],
|
|
'B': [
|
|
{'id': 6, 'pot': 'B', 'name': 'Red Bull Salzburg', 'country': 'Austria'},
|
|
{'id': 7, 'pot': 'B', 'name': 'Lulea Hockey', 'country': 'Sweden'},
|
|
{'id': 8, 'pot': 'B', 'name': 'Fribourg-Gottéron',
|
|
'country': 'Switzerland'},
|
|
{'id': 9, 'pot': 'B', 'name': 'Red Bull Munich', 'country': 'Germany'},
|
|
{'id': 10, 'pot': 'B', 'name': 'Jukurit Mikkeli', 'country': 'Finland'},
|
|
{'id': 11, 'pot': 'B', 'name': 'Mountfield HK',
|
|
'country': 'Czech Republic'},
|
|
],
|
|
'C': [
|
|
{'id': 12, 'pot': 'C', 'name': 'VS Villach',
|
|
'country': 'Austria'},
|
|
{'id': 13, 'pot': 'C', 'name': 'ZSC Lions Zürich',
|
|
'country': 'Switzerland'},
|
|
{'id': 14, 'pot': 'C', 'name': 'Grizzlys Wolfsburg', 'country': 'Germany'},
|
|
{'id': 15, 'pot': 'C', 'name': 'Ilves Tampere',
|
|
'country': 'Finland'},
|
|
{'id': 16, 'pot': 'C', 'name': 'Sparta Prague',
|
|
'country': 'Czech Republic'},
|
|
{'id': 17, 'pot': 'C', 'name': 'Fehérvár AV19',
|
|
'country': 'Austria'},
|
|
],
|
|
'D': [
|
|
{'id': 18, 'pot': 'D', 'name': 'Belfast Giants',
|
|
'country': 'United Kingdom'},
|
|
{'id': 19, 'pot': 'D', 'name': 'Grenoble', 'country': 'France'},
|
|
{'id': 20, 'pot': 'D', 'name': 'GKS Katowice', 'country': 'Poland'},
|
|
{'id': 21, 'pot': 'D', 'name': 'Aalborg Pirates', 'country': 'Denmark'},
|
|
{'id': 22, 'pot': 'D', 'name': 'Stavanger Oilers', 'country': 'Norway'},
|
|
{'id': 23, 'pot': 'D', 'name': 'Slovan Bratislava', 'country': 'Slovakia'},
|
|
]
|
|
}
|
|
|
|
df_rank = pd.read_csv('rankings.csv')
|
|
df_geo = pd.read_csv('geocoord.csv')
|
|
for pot in self.basepots:
|
|
for team in self.basepots[pot]:
|
|
ranking = df_rank[df_rank['TEAM'] == team['name']].iloc[0]
|
|
team['coeff'] = ranking['RANK']
|
|
geo = df_geo[df_geo['TEAM'] == team['name']].iloc[0]
|
|
team['lat'] = geo['LAT']
|
|
team['lon'] = geo['LON']
|
|
|
|
self.groups = range(1, len(self.basepots[self.pots[0]])+1)
|
|
self.empty_groups = {
|
|
g: {b: None for b in self.pots} for g in self.groups
|
|
}
|
|
|
|
teams = []
|
|
id = 0
|
|
for _, plist in self.basepots.items():
|
|
for team in plist:
|
|
teams.append(team)
|
|
id += 1
|
|
|
|
self.teams = teams
|
|
self.countries = set([t['country'] for t in teams])
|
|
self.teams_by_country = {
|
|
c: [v for v in teams if v['country'] == c] for c in self.countries
|
|
}
|
|
|
|
self.distance_matrix = {
|
|
(t1['id'],t2['id']): Draw_Simulator.distanceInKmByGPS(t1['lat'],t1['lon'],t2['lat'],t2['lon'])
|
|
for t1 in self.teams for t2 in self.teams
|
|
}
|
|
|
|
|
|
self.opponent_func = opponent_func
|
|
self.opponents, self.homeGames, self.awayGames = opponent_func(self)
|
|
|
|
self.algorithm = algorithm
|
|
|
|
self.html_output = html_output
|
|
|
|
@staticmethod
|
|
def distanceInKmByGPS(lat1, lon1, lat2, lon2):
|
|
def degreesToRadians(degrees):
|
|
return degrees * pi / 180
|
|
|
|
earthRadiusKm = 6371
|
|
dLat = degreesToRadians(lat2-lat1)
|
|
dLon = degreesToRadians(lon2-lon1)
|
|
lat1 = degreesToRadians(lat1)
|
|
lat2 = degreesToRadians(lat2)
|
|
a = sin(dLat/2) * sin(dLat/2) + sin(dLon/2) * \
|
|
sin(dLon/2) * cos(lat1) * cos(lat2)
|
|
c = 2 * atan2(sqrt(a), sqrt(1-a))
|
|
return int(earthRadiusKm * c)
|
|
|
|
@staticmethod
|
|
def heatmap_color_for(value):
|
|
r = 255
|
|
g = 255
|
|
if not value:
|
|
return "rgb(255,255,255)"
|
|
if value == 0:
|
|
return "rgb(255,255,255)"
|
|
if value <= 0.5:
|
|
g = 256
|
|
r = 2 * max(0,value) * 256
|
|
if value > 0.5:
|
|
g = 2*(1-min(1,value))*256
|
|
r = 256
|
|
return f"rgb({r},{g},{0})"
|
|
|
|
|
|
def groups_6_4(self):
|
|
"""
|
|
6 groups of 4 teams
|
|
"""
|
|
opponents = defaultdict(lambda: [])
|
|
homeGames = defaultdict(lambda:[])
|
|
awayGames = defaultdict(lambda:[])
|
|
for i, g in enumerate(self.groups):
|
|
for p in self.pots:
|
|
other_pots = [k for k in self.pots if k != p]
|
|
for l in other_pots:
|
|
opponents[g, p].append((g, l))
|
|
homeGames[g, p].append((g, l))
|
|
awayGames[g, l].append((g, p))
|
|
|
|
return opponents, homeGames, awayGames
|
|
|
|
|
|
def groups_3_8(self):
|
|
"""
|
|
3 groups of 8 teams
|
|
"""
|
|
opponents = defaultdict(lambda: [])
|
|
homeGames = defaultdict(lambda:[])
|
|
awayGames = defaultdict(lambda:[])
|
|
for i, g in enumerate(self.groups):
|
|
if g % 2 == 0:
|
|
other_group = self.groups[i-1]
|
|
else:
|
|
other_group = self.groups[i+1]
|
|
for p in self.pots:
|
|
other_pots = [k for k in self.pots if k != p]
|
|
for l in other_pots:
|
|
opponents[g, p].append((g, l))
|
|
opponents[g, p].append((other_group, l))
|
|
if p == 'A' and l in ['B','C','D']:
|
|
awayGames[g, p].append((g, l))
|
|
homeGames[g, l].append((g, p))
|
|
homeGames[g, p].append((other_group, l))
|
|
awayGames[other_group, l].append((g, p))
|
|
if p == 'B' and l in ['C','D']:
|
|
awayGames[g, p].append((g, l))
|
|
homeGames[g, l].append((g, p))
|
|
homeGames[g, p].append((other_group, l))
|
|
awayGames[other_group, l].append((g, p))
|
|
if p == 'C' and l in ['D']:
|
|
awayGames[g, p].append((g, l))
|
|
homeGames[g, l].append((g, p))
|
|
homeGames[g, p].append((other_group, l))
|
|
awayGames[other_group, l].append((g, p))
|
|
|
|
return opponents, homeGames, awayGames
|
|
|
|
def groups_3_8_stephan(self):
|
|
"""
|
|
3 groups of 8 teams - stephan
|
|
"""
|
|
opponents = defaultdict(lambda: [])
|
|
homeGames = defaultdict(lambda:[])
|
|
awayGames = defaultdict(lambda:[])
|
|
|
|
for i,g in enumerate(self.groups):
|
|
if g % 2 == 0:
|
|
other_group = self.groups[i-1]
|
|
else:
|
|
other_group = self.groups[i+1]
|
|
for i2,p in enumerate(self.pots):
|
|
other_pots = [k for k in self.pots]
|
|
for j2,l in enumerate(other_pots):
|
|
if p == l:
|
|
opponents[g, p].append((other_group, l))
|
|
elif i2+j2 == 3:
|
|
opponents[g, p].append((g, l))
|
|
else:
|
|
opponents[g, p].append((g, l))
|
|
opponents[g, p].append((other_group, l))
|
|
|
|
|
|
if g % 2 == 0:
|
|
awayGames[g, 'A'] += [(g, 'D'),(g, 'B'),(g, 'C')]
|
|
homeGames[g, 'A'] += [(other_group, 'A'),(other_group, 'B'),(other_group, 'C')]
|
|
|
|
awayGames[g, 'B'] += [(other_group, 'A'),(g, 'C'),(g, 'D')]
|
|
homeGames[g, 'B'] += [(g, 'A'),(other_group, 'B'),(other_group, 'D')]
|
|
|
|
awayGames[g, 'C'] += [(other_group, 'A'),(other_group, 'C'),(g, 'D')]
|
|
homeGames[g, 'C'] += [(g, 'B'),(other_group, 'D'),(g, 'A')]
|
|
|
|
awayGames[g, 'D'] += [(other_group, 'D'),(other_group, 'C'),(other_group, 'B')]
|
|
homeGames[g, 'D'] += [(g, 'A'),(g, 'B'),(g, 'C')]
|
|
|
|
else:
|
|
awayGames[g, 'A'] += [(other_group, 'A'),(g, 'B'),(g, 'C')]
|
|
homeGames[g, 'A'] += [(g, 'D'),(other_group, 'B'),(other_group, 'C')]
|
|
|
|
awayGames[g, 'B'] += [(other_group, 'B'),(other_group, 'A'),(g, 'D')]
|
|
homeGames[g, 'B'] += [(g, 'A'),(g, 'C'),(other_group, 'D')]
|
|
|
|
awayGames[g, 'C'] += [(g, 'B'),(other_group, 'A'),(g, 'D')]
|
|
homeGames[g, 'C'] += [(g, 'A'),(other_group, 'C'),(other_group, 'D')]
|
|
|
|
awayGames[g, 'D'] += [(g, 'A'),(other_group, 'C'),(other_group, 'B')]
|
|
homeGames[g, 'D'] += [(g, 'B'),(g, 'C'),(other_group, 'D')]
|
|
|
|
|
|
# if p == 'A':
|
|
# if l == 'A':
|
|
# awayGames[g, p].append((g, l))
|
|
# if l in ['B','C']:
|
|
# awayGames[g, p].append((g, l))
|
|
# homeGames[g, l].append((g, p))
|
|
# homeGames[g, p].append((other_group, l))
|
|
# awayGames[other_group, l].append((g, p))
|
|
# if p == 'B' and l in ['C','D']:
|
|
# awayGames[g, p].append((g, l))
|
|
# homeGames[g, l].append((g, p))
|
|
# homeGames[g, p].append((other_group, l))
|
|
# awayGames[other_group, l].append((g, p))
|
|
# if p == 'C' and l in ['D']:
|
|
# awayGames[g, p].append((g, l))
|
|
# homeGames[g, l].append((g, p))
|
|
# homeGames[g, p].append((other_group, l))
|
|
# awayGames[other_group, l].append((g, p))
|
|
|
|
return opponents, homeGames, awayGames
|
|
|
|
|
|
def groups_2_12_martin(self):
|
|
"""
|
|
2 groups of 12 teams
|
|
"""
|
|
opponents = defaultdict(lambda: [])
|
|
homeGames = defaultdict(lambda:[])
|
|
awayGames = defaultdict(lambda:[])
|
|
for i, g in enumerate(self.groups):
|
|
half = len(self.groups)//2
|
|
if g <= half:
|
|
left_group = self.groups[:half][i-1]
|
|
right_group = self.groups[:half][(i+1) % half]
|
|
else:
|
|
left_group = self.groups[half:][(i-half-1)]
|
|
right_group = self.groups[half:][(i-half+1) % half]
|
|
for p in self.pots:
|
|
other_pots = [k for k in self.pots if k != p]
|
|
for l in other_pots:
|
|
opponents[g, p].append((right_group, l))
|
|
awayGames[g, p].append((right_group, l))
|
|
opponents[g, p].append((left_group, l))
|
|
homeGames[g, p].append((left_group, l))
|
|
|
|
return opponents, homeGames, awayGames
|
|
|
|
|
|
def groups_2_12_stephan(self):
|
|
"""
|
|
2 groups of 12 teams
|
|
"""
|
|
opponents = defaultdict(lambda: [])
|
|
homeGames = defaultdict(lambda:[])
|
|
awayGames = defaultdict(lambda:[])
|
|
for i, g in enumerate(self.groups):
|
|
half = len(self.groups)//2
|
|
if g <= half:
|
|
left_group = self.groups[:half][i-1]
|
|
right_group = self.groups[:half][(i+1) % half]
|
|
else:
|
|
left_group = self.groups[half:][(i-half-1)]
|
|
right_group = self.groups[half:][(i-half+1) % half]
|
|
for p in self.pots:
|
|
other_pots = [k for k in self.pots if k != p]
|
|
for l in other_pots:
|
|
opponents[g, p].append((g, l))
|
|
if p == "A" and l in ['B', 'C']:
|
|
opponents[g, p].append((right_group, l))
|
|
awayGames[g, p].append((g, l))
|
|
homeGames[g, p].append((right_group, l))
|
|
elif p == "A" and l in ['D']:
|
|
opponents[g, p].append((left_group, l))
|
|
homeGames[g, p].append((g, l))
|
|
awayGames[g, p].append((left_group, l))
|
|
elif p == "B" and l in ['C', 'D']:
|
|
opponents[g, p].append((right_group, l))
|
|
awayGames[g, p].append((g, l))
|
|
homeGames[g, p].append((right_group, l))
|
|
elif p == "B" and l in ['A']:
|
|
opponents[g, p].append((left_group, l))
|
|
awayGames[g, p].append((left_group, l))
|
|
homeGames[g, p].append((g, l))
|
|
elif p == "C" and l in ['D']:
|
|
opponents[g, p].append((right_group, l))
|
|
awayGames[g, p].append((g, l))
|
|
homeGames[g, p].append((right_group, l))
|
|
elif p == "C" and l in ['A', 'B']:
|
|
opponents[g, p].append((left_group, l))
|
|
awayGames[g, p].append((left_group, l))
|
|
homeGames[g, p].append((g, l))
|
|
elif p == "D" and l in ['A']:
|
|
opponents[g, p].append((right_group, l))
|
|
homeGames[g, p].append((right_group, l))
|
|
awayGames[g, p].append((g, l))
|
|
elif p == "D" and l in ['B', 'C']:
|
|
opponents[g, p].append((left_group, l))
|
|
awayGames[g, p].append((left_group, l))
|
|
homeGames[g, p].append((g, l))
|
|
|
|
return opponents, homeGames, awayGames
|
|
|
|
|
|
def groups_1_24_martin(self):
|
|
"""
|
|
1 groups of 24 teams
|
|
"""
|
|
opponents = defaultdict(lambda: [])
|
|
homeGames = defaultdict(lambda:[])
|
|
awayGames = defaultdict(lambda:[])
|
|
for i, g in enumerate(self.groups):
|
|
left_group = self.groups[i-1]
|
|
right_group = self.groups[(i+1) % len(self.groups)]
|
|
for p in self.pots:
|
|
other_pots = [k for k in self.pots if k != p]
|
|
for l in other_pots:
|
|
opponents[g, p].append((right_group, l))
|
|
awayGames[g, p].append((right_group, l))
|
|
opponents[g, p].append((left_group, l))
|
|
homeGames[g, p].append((left_group, l))
|
|
|
|
return opponents, homeGames, awayGames
|
|
|
|
|
|
def groups_1_24_stephan(self):
|
|
"""
|
|
1 groups of 24 teams
|
|
"""
|
|
opponents = defaultdict(lambda: [])
|
|
homeGames = defaultdict(lambda:[])
|
|
awayGames = defaultdict(lambda:[])
|
|
for i, g in enumerate(self.groups):
|
|
left_group = self.groups[i-1]
|
|
right_group = self.groups[(i+1) % len(self.groups)]
|
|
for p in self.pots:
|
|
other_pots = [k for k in self.pots if k != p]
|
|
for l in other_pots:
|
|
opponents[g, p].append((g, l))
|
|
if p == "A" and l in ['B', 'C']:
|
|
opponents[g, p].append((right_group, l))
|
|
awayGames[g, p].append((g, l))
|
|
homeGames[g, p].append((right_group, l))
|
|
elif p == "A" and l in ['D']:
|
|
opponents[g, p].append((left_group, l))
|
|
homeGames[g, p].append((g, l))
|
|
awayGames[g, p].append((left_group, l))
|
|
elif p == "B" and l in ['C', 'D']:
|
|
opponents[g, p].append((right_group, l))
|
|
awayGames[g, p].append((g, l))
|
|
homeGames[g, p].append((right_group, l))
|
|
elif p == "B" and l in ['A']:
|
|
opponents[g, p].append((left_group, l))
|
|
awayGames[g, p].append((left_group, l))
|
|
homeGames[g, p].append((g, l))
|
|
elif p == "C" and l in ['D']:
|
|
opponents[g, p].append((right_group, l))
|
|
awayGames[g, p].append((g, l))
|
|
homeGames[g, p].append((right_group, l))
|
|
elif p == "C" and l in ['A', 'B']:
|
|
opponents[g, p].append((left_group, l))
|
|
awayGames[g, p].append((left_group, l))
|
|
homeGames[g, p].append((g, l))
|
|
elif p == "D" and l in ['A']:
|
|
opponents[g, p].append((right_group, l))
|
|
homeGames[g, p].append((right_group, l))
|
|
awayGames[g, p].append((g, l))
|
|
elif p == "D" and l in ['B', 'C']:
|
|
opponents[g, p].append((left_group, l))
|
|
awayGames[g, p].append((left_group, l))
|
|
homeGames[g, p].append((g, l))
|
|
|
|
return opponents, homeGames, awayGames
|
|
|
|
|
|
def check_feasibility(self, fixed_groups, nSim):
|
|
|
|
model_time = time.time()
|
|
|
|
sol_dict = {
|
|
g: {b: None for b in self.pots} for g in self.groups
|
|
}
|
|
|
|
if self.algorithm == 'SAT':
|
|
model_sat = cp_model.CpModel()
|
|
|
|
x_sat = {}
|
|
for g in self.groups:
|
|
for t in self.teams:
|
|
t_id = t['id']
|
|
x_sat[t_id, g] = model_sat.NewBoolVar(f'var_{t_id}_{g}')
|
|
|
|
# one group for each team
|
|
for t in self.teams:
|
|
model_sat.AddExactlyOne(x_sat[t['id'], g] for g in self.groups)
|
|
|
|
# one team from each pot
|
|
for g in self.groups:
|
|
for p in self.pots:
|
|
model_sat.AddExactlyOne(x_sat[t['id'], g]
|
|
for t in self.teams if t['pot'] == p)
|
|
|
|
# no country conflicts
|
|
for c in self.countries:
|
|
# print(c)
|
|
for i, t1 in enumerate(self.teams_by_country[c]):
|
|
for t2 in self.teams_by_country[c][i+1:]:
|
|
for (g1, p1), other_games in self.opponents.items():
|
|
if p1 == t1['pot']:
|
|
for (g2, p2) in other_games:
|
|
if p2 == t2['pot']:
|
|
model_sat.AddAtMostOne(
|
|
x_sat[t1['id'], g1], x_sat[t2['id'], g2])
|
|
|
|
# add fixations
|
|
nFixed = 0
|
|
for g in self.groups:
|
|
for p in self.pots:
|
|
if fixed_groups[g][p]:
|
|
nFixed += 1
|
|
model_sat.Add(x_sat[fixed_groups[g][p]['id'], g] >= 1)
|
|
|
|
|
|
if self.algorithm == 'MIP':
|
|
model_mip = LpProblem(
|
|
"Xpress", LpMinimize)
|
|
|
|
x_mip = {}
|
|
for g in self.groups:
|
|
for t in self.teams:
|
|
t_id = t['id']
|
|
x_mip[t_id, g] = LpVariable(f'var_{t_id}_{g}',
|
|
lowBound=0, upBound=1, cat=LpContinuous)
|
|
|
|
# one group for each team
|
|
for t in self.teams:
|
|
model_mip += lpSum([x_mip[t['id'], g] for g in self.groups]) == 1
|
|
|
|
# one team from each pot
|
|
for g in self.groups:
|
|
for p in self.pots:
|
|
model_mip += lpSum([x_mip[t['id'], g]
|
|
for t in self.teams if t['pot'] == p]) == 1
|
|
|
|
# no country conflicts
|
|
for c in self.countries:
|
|
# print(c)
|
|
for i, t1 in enumerate(self.teams_by_country[c]):
|
|
for t2 in self.teams_by_country[c][i+1:]:
|
|
for (g1, p1), other_games in self.opponents.items():
|
|
if p1 == t1['pot']:
|
|
for (g2, p2) in other_games:
|
|
if p2 == t2['pot']:
|
|
model_mip += x_mip[t1['id'],
|
|
g1] + x_mip[t2['id'], g2] <= 1
|
|
|
|
# add fixations
|
|
nFixed = 0
|
|
for g in self.groups:
|
|
for p in self.pots:
|
|
if fixed_groups[g][p]:
|
|
nFixed += 1
|
|
model_mip += x_mip[fixed_groups[g][p]['id'], g] >= 1
|
|
|
|
model_mip += lpSum([x_mip[key] for key in x_mip.keys()])
|
|
|
|
|
|
if self.algorithm == 'XP':
|
|
model_xp = xp.problem(name='Xpress', sense=xp.minimize)
|
|
|
|
x_xp = {}
|
|
for g in self.groups:
|
|
for t in self.teams:
|
|
t_id = t['id']
|
|
x_xp[t_id, g] = xp.var(ub=10, vartype=xp.integer)
|
|
|
|
model_xp.addVariable(x_xp)
|
|
|
|
# one group for each team
|
|
for t in self.teams:
|
|
model_xp.addConstraint(xp.Sum([x_xp[t['id'], g] for g in self.groups]) == 1)
|
|
|
|
# one team from each pot
|
|
for g in self.groups:
|
|
for p in self.pots:
|
|
model_xp.addConstraint(
|
|
xp.Sum([x_xp[t['id'], g] for t in self.teams if t['pot'] == p]) == 1)
|
|
|
|
# no country conflicts
|
|
for c in self.countries:
|
|
for i, t1 in enumerate(self.teams_by_country[c]):
|
|
for t2 in self.teams_by_country[c][i+1:]:
|
|
for (g1, p1), other_games in self.opponents.items():
|
|
if p1 == t1['pot']:
|
|
for (g2, p2) in other_games:
|
|
if p2 == t2['pot']:
|
|
model_xp.addConstraint(
|
|
x_xp[t1['id'], g1] + x_xp[t2['id'], g2] <= 1)
|
|
|
|
# add fixations
|
|
nFixed = 0
|
|
for g in self.groups:
|
|
for p in self.pots:
|
|
if fixed_groups[g][p]:
|
|
nFixed += 1
|
|
model_xp.addConstraint(x_xp[fixed_groups[g][p]['id'], g] >= 1)
|
|
|
|
model_xp.setObjective(xp.Sum([x_xp[key]
|
|
for key in x_xp.keys()]), sense=xp.minimize)
|
|
|
|
|
|
|
|
if self.algorithm == 'SAT':
|
|
solver = cp_model.CpSolver()
|
|
start_time = time.time()
|
|
status = solver.Solve(model_sat)
|
|
comp_time = time.time()-start_time
|
|
model_time = time.time()-model_time-comp_time
|
|
# solver.parameters.enumerate_all_solutions = True
|
|
# solver.parameters.log_search_progress = True
|
|
|
|
if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
|
|
if self.html_output:
|
|
for g in self.groups:
|
|
for p in self.pots:
|
|
for t in self.basepots[p]:
|
|
if solver.BooleanValue(x_sat[t['id'], g]):
|
|
sol_dict[g][p] = t
|
|
self.html_solution(nSim, sol_dict, fixed_groups)
|
|
return True, comp_time, model_time, sol_dict
|
|
else:
|
|
return False, comp_time, model_time, {}
|
|
elif self.algorithm == "MIP":
|
|
# with open ("model2_xpress.txt", "w") as f:
|
|
# f.write(model_mip.__repr__())
|
|
start_time = time.time()
|
|
model_mip.solve(PULP_CBC_CMD(msg=0))
|
|
comp_time = time.time()-start_time
|
|
model_time = time.time()-model_time-comp_time
|
|
|
|
if model_mip.status == 1:
|
|
if self.html_output:
|
|
for g in self.groups:
|
|
for p in self.pots:
|
|
for t in self.basepots[p]:
|
|
if x_mip[t['id'], g].value() > 0:
|
|
sol_dict[g][p] = t
|
|
self.html_solution(nSim, sol_dict, fixed_groups)
|
|
return True, comp_time, model_time, sol_dict
|
|
else:
|
|
return False, comp_time, model_time, {}
|
|
elif self.algorithm == "XP":
|
|
# with open ("model2_xpress.txt", "w") as f:
|
|
# f.write(model_mip.__repr__())
|
|
start_time = time.time()
|
|
model_xp.solve()
|
|
comp_time = time.time()-start_time
|
|
model_time = time.time()-model_time-comp_time
|
|
|
|
if model_xp.getProbStatus() == 6:
|
|
if self.html_output:
|
|
for g in self.groups:
|
|
for p in self.pots:
|
|
for t in self.basepots[p]:
|
|
if model_xp.getSolution(x_xp[t['id'], g]) > 0:
|
|
sol_dict[g][p] = t
|
|
self.html_solution(nSim, sol_dict, fixed_groups)
|
|
|
|
return True, comp_time, model_time, sol_dict
|
|
|
|
else:
|
|
return False, comp_time, model_time, {}
|
|
|
|
|
|
def team_travel(self,team,sol):
|
|
|
|
|
|
return 0
|
|
|
|
|
|
def team_coefficients(self,team,sol):
|
|
return 0
|
|
|
|
|
|
|
|
def simulate(self, nTimes):
|
|
|
|
if nTimes < 1:
|
|
return None
|
|
|
|
tmp_stats = defaultdict(lambda: {})
|
|
|
|
for n in range(nTimes):
|
|
print(f"{self.opponent_func.__name__} - Simulation {n}")
|
|
ttt =time.time()
|
|
|
|
sol_dict = {
|
|
g: {b: None for b in self.pots} for g in self.groups
|
|
}
|
|
|
|
pots = copy.deepcopy(self.basepots)
|
|
infeasible = False
|
|
conflicts = defaultdict(lambda: 0)
|
|
total_time = 0
|
|
total_model_time = 0
|
|
total_computations = 0
|
|
|
|
|
|
feasible = False
|
|
for p in self.pots:
|
|
pot = pots[p]
|
|
random.shuffle(pot)
|
|
|
|
while (pot):
|
|
r = random.choice(pot)
|
|
|
|
for g in self.groups:
|
|
if sol_dict[g][p]:
|
|
continue
|
|
else:
|
|
sol_dict[g][p] = r
|
|
feasible, comptime, modeltime, _ = self.check_feasibility(
|
|
sol_dict, nSim=n)
|
|
total_computations += 1
|
|
total_time += comptime
|
|
total_model_time += modeltime
|
|
if feasible:
|
|
feasible = True
|
|
break
|
|
else:
|
|
# print("\tCONFLICT: skipping group {} for team {}".format(g,r))
|
|
conflicts[p] += 1
|
|
sol_dict[g][p] = None
|
|
|
|
if not feasible:
|
|
infeasible = True
|
|
break
|
|
|
|
pot.remove(r)
|
|
|
|
|
|
travel = defaultdict(lambda:0)
|
|
coefficients = defaultdict(lambda:0)
|
|
visited_countries = defaultdict(lambda:set({}))
|
|
if not infeasible:
|
|
for g in self.groups:
|
|
for p in self.pots:
|
|
t1 = sol_dict[g][p]
|
|
for ag in self.awayGames[g, p]:
|
|
t2 = sol_dict[ag[0]][ag[1]]
|
|
travel[t1['id']] += self.distance_matrix[t1['id'],t2['id']]
|
|
for op in self.opponents[g,p]:
|
|
t2 = sol_dict[op[0]][op[1]]
|
|
if self.opponent_func.__name__ == 'groups_6_4':
|
|
coefficients[t1['id']] += 2*(abs(t1['coeff']-t2['coeff']))
|
|
else:
|
|
coefficients[t1['id']] += abs(t1['coeff']-t2['coeff'])
|
|
visited_countries[t1['id']].add(t2['country'])
|
|
|
|
|
|
blockings, breaks = self.create_schedule(sol_dict, n)
|
|
|
|
|
|
|
|
|
|
|
|
tmp_stats[n] = {
|
|
'conflicts': conflicts,
|
|
'infeasible': infeasible,
|
|
'comptime': total_time,
|
|
'modeltime': total_model_time,
|
|
'total_computations': total_computations,
|
|
'travel':travel,
|
|
'coefficients':coefficients,
|
|
'visited_countries':{t['id']:len(visited_countries[t['id']]) for t in self.teams},
|
|
'blockings':blockings,
|
|
'breaks':breaks,
|
|
}
|
|
|
|
with open(f'json/{self.opponent_func.__name__}_{n}.json', 'w') as f:
|
|
json.dump(tmp_stats, f, cls=NumpyEncoder)
|
|
|
|
|
|
nInfeasible = sum(s['infeasible'] for s in tmp_stats.values()) > 0
|
|
nComputation = sum(s['total_computations'] for s in tmp_stats.values())
|
|
tComputation = sum(s['comptime'] for s in tmp_stats.values())
|
|
conflicts_dict = {
|
|
p: round(sum(s['conflicts'][p] for s in tmp_stats.values())/nTimes,2)
|
|
for p in self.pots
|
|
}
|
|
conflicts_dict['total'] = round(sum(conflicts_dict[p] for p in self.pots),2)
|
|
travel_stats = {
|
|
t['id']: {
|
|
'mean': round(np.mean([s['travel'][t['id']] for s in tmp_stats.values()]),2),
|
|
'std': round(np.std([s['travel'][t['id']] for s in tmp_stats.values()]),2),
|
|
'var': round(np.var([s['travel'][t['id']] for s in tmp_stats.values()]),2),
|
|
}
|
|
for t in self.teams
|
|
}
|
|
travel_stats['total'] = {
|
|
'mean': round(np.mean([travel_stats[t['id']]['mean'] for t in self.teams]),2),
|
|
'std': round(np.mean([travel_stats[t['id']]['std'] for t in self.teams]),2),
|
|
'var': round(np.mean([travel_stats[t['id']]['var'] for t in self.teams]),2),
|
|
}
|
|
coefficient_stats = {
|
|
t['id']: {
|
|
'mean': round(np.mean([s['coefficients'][t['id']] for s in tmp_stats.values()]),2),
|
|
'std': round(np.std([s['coefficients'][t['id']] for s in tmp_stats.values()]),2),
|
|
'var': round(np.var([s['coefficients'][t['id']] for s in tmp_stats.values()]),2),
|
|
}
|
|
for t in self.teams
|
|
}
|
|
coefficient_stats['total'] = {
|
|
'mean': round(np.mean([coefficient_stats[t['id']]['mean'] for t in self.teams]),2),
|
|
'std': round(np.mean([coefficient_stats[t['id']]['std'] for t in self.teams]),2),
|
|
'var': round(np.mean([coefficient_stats[t['id']]['var'] for t in self.teams]),2),
|
|
}
|
|
|
|
visited_countries = {
|
|
t['id']: {
|
|
'mean': round(np.mean([s['visited_countries'][t['id']] for s in tmp_stats.values()]),2),
|
|
}
|
|
for t in self.teams
|
|
}
|
|
visited_countries['total'] = {
|
|
'mean': round(np.mean([visited_countries[t['id']]['mean'] for t in self.teams]),2),
|
|
}
|
|
|
|
blockings = {
|
|
t['id']: {
|
|
'mean': round(np.mean([s['blockings'][t['id']] for s in tmp_stats.values()]),3),
|
|
}
|
|
for t in self.teams
|
|
}
|
|
blockings['total'] = {
|
|
'sum': round(np.sum([blockings[t['id']]['mean'] for t in self.teams]),3),
|
|
}
|
|
|
|
|
|
breaks = {
|
|
t['id']: {
|
|
'mean': round(np.mean([s['breaks'][t['id']] for s in tmp_stats.values()])*0.5,3),
|
|
}
|
|
for t in self.teams
|
|
}
|
|
breaks['total'] = {
|
|
'sum': round(np.sum([breaks[t['id']]['mean'] for t in self.teams]),3),
|
|
}
|
|
|
|
|
|
stats = {
|
|
'nInfeasible':nInfeasible,
|
|
'nComputation':nComputation,
|
|
'tComputation':tComputation,
|
|
'conflicts_dict':conflicts_dict,
|
|
'conflicts_dict':conflicts_dict,
|
|
'travel_stats':travel_stats,
|
|
'coefficient_stats':coefficient_stats,
|
|
'visited_countries':visited_countries,
|
|
'blockings':blockings,
|
|
'breaks':breaks,
|
|
}
|
|
|
|
|
|
|
|
|
|
return stats
|
|
|
|
|
|
|
|
def create_schedule(self, sol_dict, nSim):
|
|
|
|
# for g in self.groups:
|
|
# for p in self.pots:
|
|
# print(g,p,sol_dict[g][p]['name'],sol_dict[g][p]['country'])
|
|
# for game in self.homeGames[g, p]:
|
|
# print("\t",game[0],game[1],sol_dict[game[0]][game[1]])
|
|
# for game in self.awayGames[g, p]:
|
|
# print("\t",game[0],game[1],sol_dict[game[0]][game[1]])
|
|
base_scenario_id = 10081
|
|
base_scenario = Scenario.objects.get(id=base_scenario_id)
|
|
|
|
scenario = copy_scenario(base_scenario,f" - {self.opponent_func.__name__} - {nSim}")
|
|
scenario.base_scenario = base_scenario
|
|
scenario.save()
|
|
|
|
|
|
GameRequirement.objects.filter(scenario=scenario).delete()
|
|
games = []
|
|
for g in self.groups:
|
|
for p in self.pots:
|
|
# print(g,p,sol_dict[g][p]['name'],sol_dict[g][p]['country'])
|
|
for game in self.homeGames[g, p]:
|
|
# print("\t",game[0],game[1],sol_dict[game[0]][game[1]])
|
|
games.append(GameRequirement(number=1, scenario=scenario, team1=Team.objects.get(id=sol_dict[g][p]['id']), team2=Team.objects.get(id=sol_dict[game[0]][game[1]]['id'])))
|
|
# for game in self.awayGames[g, p]:
|
|
# print("\t",game[0],game[1],sol_dict[game[0]][game[1]])
|
|
# games.append(GameRequirement(number=1, scenario=scenario, team1=Team.objects.get(id=sol_dict[game[0]][game[1]]['id']), team2=Team.objects.get(id=sol_dict[g][p]['id'])))
|
|
GameRequirement.objects.bulk_create(games)
|
|
|
|
|
|
s2 = scenario.id
|
|
user_name = 'md'
|
|
user_is_staff = True
|
|
runMode = 'New'
|
|
localsearch_time = 0
|
|
RUN_ENV = 'celery'
|
|
SOLVER = 'xpress'
|
|
|
|
sol = optimize(task=None, s2=s2, user_name=user_name, user_is_staff=user_is_staff, runMode=runMode, localsearch_time=localsearch_time, RUN_ENV=RUN_ENV, solver=SOLVER)
|
|
|
|
|
|
kpis = Availabilities(scenario.id,showSolution=1)
|
|
|
|
blockings = defaultdict(lambda:0)
|
|
breaks = defaultdict(lambda:0)
|
|
for v in kpis['violations']:
|
|
blockings[v[0].id] += 1
|
|
for b in kpis['missingBreaks']:
|
|
breaks[b] += 1
|
|
|
|
|
|
return blockings, breaks
|
|
|
|
|
|
|
|
|
|
def html_solution(self, nSim, sol_dict, fixed_groups):
|
|
|
|
sol = " \
|
|
<style> \
|
|
table, th, td { \
|
|
border: 1px solid black; \
|
|
border-collapse: collapse; \
|
|
} \
|
|
</style> \
|
|
"
|
|
sol += "<table style='border:1px solid black'>\n"
|
|
sol += "<thead>\n"
|
|
sol += "<tr><th></th>"
|
|
for g in self.groups:
|
|
sol += f"<th>{g}</th>"
|
|
sol += "</tr>"
|
|
sol += "</thead>\n"
|
|
sol += "<tbody>\n"
|
|
nFixed = 0
|
|
for p in self.pots:
|
|
sol += f"<tr><td>{p}</td>"
|
|
for g in self.groups:
|
|
if fixed_groups[g][p]:
|
|
color = 'lightgreen'
|
|
nFixed += 1
|
|
else:
|
|
color = 'lightyellow'
|
|
tpot = sol_dict[g][p]['pot']
|
|
tname = sol_dict[g][p]['name']
|
|
tcountry = sol_dict[g][p]['country']
|
|
sol += f"<td style='background-color:{color}'>({tpot}){tname}({tcountry})</td>"
|
|
sol += "</tr>"
|
|
sol += "</tbody>\n"
|
|
sol += "</table>\n"
|
|
sol += "<br><br><br>\n"
|
|
|
|
for (g1, p1), other_games in self.opponents.items():
|
|
for (g2, p2) in other_games:
|
|
tpot = sol_dict[g1][p1]['pot']
|
|
tname = sol_dict[g1][p1]['name']
|
|
tcountry = sol_dict[g1][p1]['country']
|
|
sol += f'({tpot}){tname}({tcountry})'
|
|
sol += " vs "
|
|
tpot = sol_dict[g2][p2]['pot']
|
|
tname = sol_dict[g2][p2]['name']
|
|
tcountry = sol_dict[g2][p2]['country']
|
|
sol += f'({tpot}){tname}({tcountry})'
|
|
sol += "<br>"
|
|
sol += "<br>"
|
|
sol += "<br>"
|
|
with open(f'html/{self.algorithm}_{self.opponent_func.__name__}_{nSim}_{nFixed}_sol.html', 'w') as f:
|
|
f.write(sol)
|
|
|
|
|
|
|
|
|
|
# %%
|
|
|
|
|
|
# simulator = Draw_Simulator(algorithm='XP', opponent_func = Draw_Simulator.groups_2_12_stephan, html_output=False)
|
|
# nSim = 10
|
|
|
|
|
|
|
|
# %%
|
|
|
|
funcs = [
|
|
# Draw_Simulator.groups_6_4,
|
|
# Draw_Simulator.groups_3_8,
|
|
Draw_Simulator.groups_1_24_martin,
|
|
Draw_Simulator.groups_2_12_martin,
|
|
# Draw_Simulator.groups_2_12_stephan,
|
|
# Draw_Simulator.groups_1_24_stephan,
|
|
Draw_Simulator.groups_3_8_stephan,
|
|
]
|
|
|
|
|
|
scenario_id = 10081
|
|
scenario = Scenario.objects.get(id=scenario_id)
|
|
Scenario.objects.filter(base_scenario=scenario).delete()
|
|
|
|
|
|
stats = {}
|
|
for func in funcs:
|
|
simulator = Draw_Simulator(algorithm='XP', opponent_func = func, html_output=True,use_db=True)
|
|
nSim = 1000
|
|
stats[func.__name__] = simulator.simulate(nSim)
|
|
|
|
|
|
# %%
|
|
|
|
sol = " \
|
|
<style> \
|
|
table, th, td { \
|
|
border: 1px solid black; \
|
|
border-collapse: collapse; \
|
|
text-align: center; \
|
|
min-width:50px; \
|
|
padding:5px; \
|
|
margin: 5px; \
|
|
} \
|
|
</style> \
|
|
"
|
|
sol += "<table style='border:1px solid black'>\n"
|
|
sol += "<thead>\n"
|
|
sol += f"<tr><td rowspan='4'>n={nSim}</td>"
|
|
sol += f"<td colspan='{len(simulator.pots)}'>Conflicts</td>"
|
|
sol += f"<td colspan='8' rowspan='2'>Total</td>"
|
|
sol += f"<td colspan='{5*len(simulator.teams)}'>Teams</td>"
|
|
sol += "</tr>"
|
|
sol += "<tr>"
|
|
for p in simulator.pots:
|
|
sol += f"<td rowspan='3'>{p}</td>"
|
|
for t in simulator.teams:
|
|
sol+= f"<td colspan='7'>{t['name']}</td>"
|
|
sol += "</tr>"
|
|
sol += "<tr>"
|
|
sol += "<td colspan='1' rowspan='2'>Cfl.</td>"
|
|
sol += "<td colspan='2'>Trav.</td>"
|
|
sol += "<td colspan='2'>Coe.</td>"
|
|
sol += "<td colspan='1' rowspan='2'>Block</td>"
|
|
sol += "<td colspan='1' rowspan='2'>No Travel</td>"
|
|
sol += "<td colspan='1' rowspan='2'>Countr.</td>"
|
|
for t in simulator.teams:
|
|
sol += "<td colspan='2' rowspan='1'>Trav.</td>"
|
|
sol += "<td colspan='2' rowspan='1'>Coe.</td>"
|
|
sol += "<td colspan='1' rowspan='2'>Block</td>"
|
|
sol += "<td colspan='1' rowspan='2'>No Travel</td>"
|
|
sol += "<td colspan='1' rowspan='2'>Countr.</td>"
|
|
sol += "</tr>"
|
|
sol += "<tr>"
|
|
sol += "<td>M</td>"
|
|
sol += "<td>S</td>"
|
|
sol += "<td>M</td>"
|
|
sol += "<td>S</td>"
|
|
for t in simulator.teams:
|
|
sol += "<td>M</td>"
|
|
sol += "<td>S</td>"
|
|
sol += "<td>M</td>"
|
|
sol += "<td>S</td>"
|
|
sol += "</tr>"
|
|
sol += "</thead>\n"
|
|
sol += "<tbody>\n"
|
|
for func in funcs:
|
|
sol += "<tr>"
|
|
sol += f"<td>{func.__name__}</td>"
|
|
for p in simulator.pots:
|
|
sol += f"<td>{stats[func.__name__]['conflicts_dict'][p]}</td>"
|
|
sol += f"<td>{stats[func.__name__]['conflicts_dict']['total']}</td>"
|
|
ttmean = stats[func.__name__]['travel_stats']['total']['mean']
|
|
ttstd = stats[func.__name__]['travel_stats']['total']['std']
|
|
ctmean = stats[func.__name__]['coefficient_stats']['total']['mean']
|
|
ctstd = stats[func.__name__]['coefficient_stats']['total']['std']
|
|
sol += f"<td>{stats[func.__name__]['travel_stats']['total']['mean']}</td>"
|
|
sol += f"<td>{stats[func.__name__]['travel_stats']['total']['std']}</td>"
|
|
sol += f"<td>{stats[func.__name__]['coefficient_stats']['total']['mean']}</td>"
|
|
sol += f"<td>{stats[func.__name__]['coefficient_stats']['total']['std']}</td>"
|
|
sol += f"<td>{stats[func.__name__]['blockings']['total']['sum']}</td>"
|
|
sol += f"<td>{stats[func.__name__]['breaks']['total']['sum']}</td>"
|
|
sol += f"<td>{stats[func.__name__]['visited_countries']['total']['mean']}</td>"
|
|
for t in simulator.teams:
|
|
tmean = stats[func.__name__]['travel_stats'][t['id']]['mean']
|
|
tstd = stats[func.__name__]['travel_stats'][t['id']]['std']
|
|
cmean = stats[func.__name__]['coefficient_stats'][t['id']]['mean']
|
|
cstd = stats[func.__name__]['coefficient_stats'][t['id']]['std']
|
|
blmean = stats[func.__name__]['blockings'][t['id']]['mean']
|
|
brmean = stats[func.__name__]['breaks'][t['id']]['mean']
|
|
visited = stats[func.__name__]['visited_countries'][t['id']]['mean']
|
|
color = Draw_Simulator.heatmap_color_for(abs(tmean-ttmean)/ttmean)
|
|
sol += f"<td style='background-color:{color}'>{tmean}</td>"
|
|
color = Draw_Simulator.heatmap_color_for(abs(tstd-ttstd)/ttstd)
|
|
sol += f"<td style='background-color:{color}'>{tstd}</td>"
|
|
color = Draw_Simulator.heatmap_color_for(abs(cmean-ctmean)/ctmean)
|
|
sol += f"<td style='background-color:{color}'>{cmean}</td>"
|
|
color = Draw_Simulator.heatmap_color_for(abs(cstd-ctstd)/ctstd)
|
|
sol += f"<td style='background-color:{color}'>{cstd}</td>"
|
|
sol += f"<td >{blmean}</td>"
|
|
sol += f"<td >{brmean}</td>"
|
|
sol += f"<td >{visited}</td>"
|
|
|
|
sol += "</tr>"
|
|
sol += "</tbody>\n"
|
|
sol += "</table>\n"
|
|
|
|
|
|
|
|
with open(f'stats.html', 'w') as f:
|
|
f.write(sol)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# %%
|
|
|
|
# %%
|
|
|
|
with open(f'color.html', 'w') as f:
|
|
table = f"<table><tr>"
|
|
for i in range(1,11):
|
|
color = Draw_Simulator.heatmap_color_for(i/10)
|
|
table += f"<td style='background-color:{color}'>{color}</td>"
|
|
|
|
table += f"</tr></table>"
|
|
f.write(table)
|
|
|
|
|
|
# %%
|