def build_best_formation_model(self, players, n_weeks, budget, existing_team, n_free_transf, n_max_transf=15): self.players = players model = ConcreteModel() model.W = RangeSet(0, n_weeks - 1) #Time index model.I = RangeSet(0, len(self.players) - 1) #Player index model.I_gkp = Set(initialize=[ i for i, p in enumerate(self.players) if p.position == "gkp" ]) #Goalkeeper index model.I_def = Set(initialize=[ i for i, p in enumerate(self.players) if p.position == "def" ]) #Defender index model.I_mid = Set(initialize=[ i for i, p in enumerate(self.players) if p.position == "mid" ]) #Midfielder index model.I_fwd = Set(initialize=[ i for i, p in enumerate(self.players) if p.position == "fwd" ]) #Forward index model.J = RangeSet(0, 7) #Formation index model.T = Set(initialize=list(set([p.team for p in self.players ]))) #Team index model.P = Set(initialize=list(set([p.position for p in self.players ]))) #Position index def init_formations(m, j): if j == 0: return (3, 5, 2) if j == 1: return (3, 4, 3) if j == 2: return (4, 5, 1) if j == 3: return (4, 4, 2) if j == 4: return (4, 3, 3) if j == 5: return (5, 4, 1) if j == 6: return (5, 3, 2) if j == 7: return (5, 2, 3) model.Formations = Param(model.J, initialize=init_formations, within=Any) def init_req_per_pos(m, p): if p == "gkp": return 2 if p == "fwd": return 3 return 5 model.ReqPos = Param(model.P, initialize=init_req_per_pos) def init_cost(m, i): return self.players[i].cost model.Cost = Param(model.I, initialize=init_cost) def init_score(m, i, w): return self.players[i].score[w] model.Score = Param(model.I, model.W, initialize=init_score) def init_weight(m, w): return 0.5**w model.Weight = Param(model.W, initialize=init_weight) def init_existing_team(m, i): p = self.players[i] if p in existing_team: return 1 return 0 model.ExistingTeam = Param(model.I, initialize=init_existing_team) model.in_squad = Var(model.I, model.W, within=Binary) #Players in squad (15) model.on_pitch = Var( model.I, model.J, model.W, within=Binary) #Players on the pitch in formation j model.cap = Var(model.I, model.W, within=Binary) #Captain model.form_points = Var(model.J, model.W, within=Reals) #Points in formation j model.best_form = Var( model.J, model.W, within=Binary) #The best formation to use (max points) model.n_changes = Var(model.W, bounds=( 0, n_max_transf)) #The number of changes from the previous team model.n_free = Var(model.W, bounds=(0, 15)) #The number of free changes this round model.n_costly = Var( model.W, bounds=(0, 15)) #The number of costly changes this round model.u = Var( model.W, within=Binary ) #1 if more than 1 change is made this round, 0 otherwise model.subbed_in = Var(model.I, model.W, bounds=(0, 1)) #Player subbed into team model.subbed_out = Var(model.I, model.W, bounds=(0, 1)) #Player subbed out of team def cost_constraint(m, w): return sum(m.Cost[i] * m.in_squad[i, w] for i in m.I) <= budget model.MaxCost = Constraint(model.W, rule=cost_constraint) def max_per_team_constraint(m, t, w): return sum(m.in_squad[i, w] for i in m.I if self.players[i].team == t) <= 3 model.MaxPerTeam = Constraint(model.T, model.W, rule=max_per_team_constraint) def req_per_pos(m, p, w): return sum(m.in_squad[i, w] for i in m.I if self.players[i].position == p) == m.ReqPos[p] model.ReqPosConstr = Constraint(model.P, model.W, rule=req_per_pos) def on_pitch_bounds(m, i, j, w): return m.on_pitch[i, j, w] <= m.in_squad[i, w] model.OnPitchBounds = Constraint(model.I, model.J, model.W, rule=on_pitch_bounds) def pick_gkp(m, j, w): return sum(m.on_pitch[i, j, w] for i in m.I_gkp) == 1 model.Pickgkp = Constraint(model.J, model.W, rule=pick_gkp) def pick_def(m, j, w): n_def, n_mid, n_fwd = m.Formations[j] return sum(m.on_pitch[i, j, w] for i in m.I_def) == n_def model.Pickdef = Constraint(model.J, model.W, rule=pick_def) def pick_mid(m, j, w): n_def, n_mid, n_fwd = m.Formations[j] return sum(m.on_pitch[i, j, w] for i in m.I_mid) == n_mid model.Pickmid = Constraint(model.J, model.W, rule=pick_mid) def pick_fwd(m, j, w): n_def, n_mid, n_fwd = m.Formations[j] return sum(m.on_pitch[i, j, w] for i in m.I_fwd) == n_fwd model.Pickfwd = Constraint(model.J, model.W, rule=pick_fwd) def cap_bounds(m, i, j, w): return m.cap[i, w] <= m.on_pitch[i, j, w] model.CapBounds = Constraint(model.I, model.J, model.W, rule=cap_bounds) def pick_cap(m, w): return sum(m.cap[i, w] for i in m.I) == 1 model.PickCap = Constraint(model.W, rule=pick_cap) def pick_formation(m, w): return sum(m.best_form[j, w] for j in m.J) == 1 model.PickFormation = Constraint(model.W, rule=pick_formation) def form_point_bound(m, j, w): return m.form_points[j, w] <= sum( m.Score[i, w] * m.on_pitch[i, j, w] for i in model.I) model.FormationPointBound = Constraint(model.J, model.W, rule=form_point_bound) def form_pick_bound(m, j, w): big_m = 15 * max([p.score[w] for p in self.players]) return m.form_points[j, w] <= big_m * m.best_form[j, w] model.FormationPickBounds = Constraint(model.J, model.W, rule=form_pick_bound) def sub_rule(m, i, w): if w == 0: prev_selection = m.ExistingTeam[i] else: prev_selection = m.in_squad[i, w - 1] return m.subbed_in[i, w] - m.subbed_out[i, w] == m.in_squad[ i, w] - prev_selection model.SubRule = Constraint(model.I, model.W, rule=sub_rule) def total_changes_rule(m, w): return m.n_changes[w] == sum(m.subbed_in[i, w] for i in m.I) model.TotalChanges = Constraint(model.W, rule=total_changes_rule) def costly_changes_rule(m, w): return m.n_costly[w] >= m.n_changes[w] - m.n_free[w] model.CostlyChanges = Constraint(model.W, rule=costly_changes_rule) def free_changes_rule(m, w): if w == 0: return m.n_free[w] == n_free_transf return m.n_free[w] == 2 - m.u[w] model.FreeChanges = Constraint(model.W, rule=free_changes_rule) def remove_free_change_rule(m, w): if w == 0: return m.u[w] == 0 return 15 * m.u[w] >= m.n_changes[w - 1] #- 1 model.RemoveFreeChange = Constraint(model.W, rule=remove_free_change_rule) def objective_rule(m): cap_score = sum(m.Weight[w] * m.Score[i, w] * m.cap[i, w] for i in m.I for w in m.W) form_score = sum(m.Weight[w] * m.form_points[j, w] for j in m.J for w in m.W) bench_score = sum(m.Weight[w] * m.Score[i, w] * (m.in_squad[i, w] - m.on_pitch[i, j, w]) for i in m.I for j in m.J for w in m.W) / len(m.J) change_ramping = sum(m.n_changes[w] for w in m.W) / len(m.W) #smoothing_score = 0.05*sum(m.Score[i]*m.in_squad[i] for i in m.I)/15 return cap_score + 0.99 * form_score + 0.01 * bench_score - 4 * sum( m.n_costly[w] for w in m.W) - 0.1 * change_ramping model.OBJ = Objective(rule=objective_rule, sense=maximize) self.model = model
def build_best_formation_model(self, players, budget=100.0, bench_boost=False): self.players = players model = ConcreteModel() model.I = RangeSet(0, len(self.players) - 1) model.I_gkp = Set(initialize=[ i for i, p in enumerate(self.players) if p.position == "gkp" ]) model.I_def = Set(initialize=[ i for i, p in enumerate(self.players) if p.position == "def" ]) model.I_mid = Set(initialize=[ i for i, p in enumerate(self.players) if p.position == "mid" ]) model.I_fwd = Set(initialize=[ i for i, p in enumerate(self.players) if p.position == "fwd" ]) model.J = RangeSet(0, 7) model.T = Set(initialize=list(set([p.team for p in self.players]))) model.P = Set(initialize=list(set([p.position for p in self.players]))) def init_formations(m, j): if j == 0: return (3, 5, 2) if j == 1: return (3, 4, 3) if j == 2: return (4, 5, 1) if j == 3: return (4, 4, 2) if j == 4: return (4, 3, 3) if j == 5: return (5, 4, 1) if j == 6: return (5, 3, 2) if j == 7: return (5, 2, 3) model.Formations = Param(model.J, initialize=init_formations, within=Any) def init_req_per_pos(m, p): if p == "gkp": return 2 if p == "fwd": return 3 return 5 model.ReqPos = Param(model.P, initialize=init_req_per_pos) def init_cost(m, i): return self.players[i].cost model.Cost = Param(model.I, initialize=init_cost) def init_score(m, i): return self.players[i].score model.Score = Param(model.I, initialize=init_score) model.in_squad = Var(model.I, within=Binary) #Players in squad (15) model.on_pitch = Var( model.I, model.J, within=Binary) #Players on the pitch in formation j model.cap = Var(model.I, within=Binary) #Captain model.form_points = Var(model.J, within=Reals) #Points in formation j model.best_form = Var( model.J, within=Binary) #The best formation to use (max points) model.n_keep = Var(within=Integers, bounds=( 0, 15)) #The number of players kept from the existing team (if given) model.n_changes = Var(within=Integers, bounds=( 0, 15)) #The number of changes from the existing team (if given) def cost_constraint(m): return sum(m.Cost[i] * m.in_squad[i] for i in m.I) <= budget model.MaxCost = Constraint(rule=cost_constraint) def max_per_team_constraint(m, t): return sum(m.in_squad[i] for i in m.I if self.players[i].team == t) <= 3 model.MaxPerTeam = Constraint(model.T, rule=max_per_team_constraint) def req_per_pos(m, p): return sum(m.in_squad[i] for i in m.I if self.players[i].position == p) == m.ReqPos[p] model.ReqPosConstr = Constraint(model.P, rule=req_per_pos) def y_bounds(m, i, j): return m.on_pitch[i, j] <= m.in_squad[i] model.gkpBounds = Constraint(model.I, model.J, rule=y_bounds) def pick_gkp(m, j): return sum(m.on_pitch[i, j] for i in m.I_gkp) == 1 model.Pickgkp = Constraint(model.J, rule=pick_gkp) def pick_def(m, j): n_def, n_mid, n_fwd = m.Formations[j] return sum(m.on_pitch[i, j] for i in m.I_def) == n_def model.Pickdef = Constraint(model.J, rule=pick_def) def pick_mid(m, j): n_def, n_mid, n_fwd = m.Formations[j] return sum(m.on_pitch[i, j] for i in m.I_mid) == n_mid model.Pickmid = Constraint(model.J, rule=pick_mid) def pick_fwd(m, j): n_def, n_mid, n_fwd = m.Formations[j] return sum(m.on_pitch[i, j] for i in m.I_fwd) == n_fwd model.Pickfwd = Constraint(model.J, rule=pick_fwd) def cap_bounds(m, i, j): return m.cap[i] <= m.on_pitch[i, j] model.CapBounds = Constraint(model.I, model.J, rule=cap_bounds) def pick_cap(m): return sum(m.cap[i] for i in m.I) == 1 model.PickCap = Constraint(rule=pick_cap) def pick_formation(m): return sum(m.best_form[j] for j in m.J) == 1 model.PickFormation = Constraint(rule=pick_formation) def form_point_bound(m, j): return m.form_points[j] <= sum(m.Score[i] * m.on_pitch[i, j] for i in model.I) model.FormationPointBound = Constraint(model.J, rule=form_point_bound) def form_pick_bound(m, j): big_m = 15 * max([p.score for p in self.players]) return m.form_points[j] <= big_m * m.best_form[j] model.FormationPickBounds = Constraint(model.J, rule=form_pick_bound) def objective_rule(m): cap_score = sum(m.Score[i] * m.cap[i] for i in model.I) form_score = sum(m.form_points[j] for j in model.J) bench_score = sum(m.Score[i] * (m.in_squad[i] - m.on_pitch[i, j]) for i in model.I for j in model.J) / len(model.J) #smoothing_score = 0.05*sum(m.Score[i]*m.in_squad[i] for i in model.I)/15 if bench_boost: return cap_score + form_score + bench_score - 4 * m.n_changes else: return cap_score + 0.99 * form_score + 0.01 * bench_score - 4 * m.n_changes model.OBJ = Objective(rule=objective_rule, sense=maximize) self.model = model