Example #1
0
def test(cas_conn):

    m = so.Model(name='nlpse02', session=cas_conn)
    N = m.add_parameter(name='N', init=1000)
    x = m.add_variables(so.exp_range(1, N), name='x', init=1)
    m.set_objective(so.quick_sum(-4 * x[i] + 3
                                 for i in so.exp_range(1, N - 1)) +
                    so.quick_sum((x[i]**2 + x[N]**2)**2
                                 for i in so.exp_range(1, N - 1)),
                    name='f',
                    sense=so.MIN)

    m.add_statement('print x;', after_solve=True)
    m.solve(options={'with': 'nlp'}, verbose=True)
    print(m.response['Print3.PrintTable'])

    # Model 2
    so.reset_globals()
    m = so.Model(name='nlpse02_2', session=cas_conn)
    N = m.add_parameter(name='N', init=1000)
    x = m.add_variables(so.exp_range(1, N), name='x', lb=1, ub=2)
    m.set_objective(so.quick_sum(
        sm.cos(-0.5 * x[i + 1] - x[i]**2) for i in so.exp_range(1, N - 1)),
                    name='f2',
                    sense=so.MIN)
    m.add_statement('print x;', after_solve=True)
    m.solve(verbose=True, options={'with': 'nlp', 'algorithm': 'activeset'})
    print(m.get_solution_summary())

    return m.get_objective_value()
Example #2
0
def test(cas_conn, sols=False):

    m = so.Model(name='multiobjective', session=cas_conn)

    x = m.add_variables([1, 2], lb=0, ub=5, name='x')

    f1 = m.set_objective((x[1] - 1)**2 + (x[1] - x[2])**2,
                         name='f1',
                         sense=so.MIN)
    f2 = m.append_objective((x[1] - x[2])**2 + (x[2] - 3)**2,
                            name='f2',
                            sense=so.MIN)

    m.solve(verbose=True,
            options={
                'with': 'blackbox',
                'obj': (f1, f2),
                'logfreq': 50
            })

    print('f1', f1.get_value())
    print('f2', f2.get_value())

    if sols:
        return dict(solutions=cas_conn.CASTable('allsols').to_frame(),
                    x=x,
                    f1=f1,
                    f2=f2)
    else:
        return f1.get_value()
def test(cas_conn, num_guests=20, max_table_size=3, max_tables=None):

    m = so.Model("wedding", session=cas_conn)

    # Check max. tables
    if max_tables is None:
        max_tables = math.ceil(num_guests / max_table_size)

    # Sets
    guests = range(1, num_guests + 1)
    tables = range(1, max_tables + 1)
    guest_pairs = [[i, j] for i in guests
                   for j in range(i + 1, num_guests + 1)]

    # Variables
    x = m.add_variables(guests, tables, vartype=so.BIN, name="x")
    unhappy = m.add_variables(tables, name="unhappy", lb=0)

    # Objective
    m.set_objective(unhappy.sum('*'), sense=so.MIN, name="obj")

    # Constraints
    m.add_constraints((x.sum(g, '*') == 1 for g in guests), name="assigncon")
    m.add_constraints((x.sum('*', t) <= max_table_size for t in tables),
                      name="tablesizecon")
    m.add_constraints((unhappy[t] >= abs(g - h) * (x[g, t] + x[h, t] - 1)
                       for t in tables for [g, h] in guest_pairs),
                      name="measurecon")

    # Solve
    res = m.solve(options={
        'with': 'milp',
        'decomp': {
            'method': 'set'
        },
        'presolver': 'none'
    })

    if res is not None:

        print(so.get_solution_table(x))

        # Print assignments
        for t in tables:
            print('Table {} : [ '.format(t), end='')
            for g in guests:
                if x[g, t].get_value() == 1:
                    print('{} '.format(g), end='')
            print(']')

    return m.get_objective_value()
Example #4
0
def test(cas_conn, data=None):

    # Use default data if not passed
    if data is None:
        data = pd.DataFrame([
            [4, 8, 43.71],
            [62, 5, 351.29],
            [81, 62, 2878.91],
            [85, 75, 3591.59],
            [65, 54, 2058.71],
            [96, 84, 4487.87],
            [98, 29, 1773.52],
            [36, 33, 767.57],
            [30, 91, 1637.66],
            [3, 59, 215.28],
            [62, 57, 2067.42],
            [11, 48, 394.11],
            [66, 21, 932.84],
            [68, 24, 1069.21],
            [95, 30, 1770.78],
            [34, 14, 368.51],
            [86, 81, 3902.27],
            [37, 49, 1115.67],
            [46, 80, 2136.92],
            [87, 72, 3537.84],
        ],
                            columns=['x1', 'x2', 'y'])

    m = so.Model(name='least_squares', session=cas_conn)

    # Regression model: L(a,b,c) = a * x1 + b * x2 + c * x1 * x2
    a = m.add_variable(name='a')
    b = m.add_variable(name='b')
    c = m.add_variable(name='c')

    x1 = data['x1']
    x2 = data['x2']
    y = data['y']

    err = m.add_implicit_variable(
        (y[i] - (a * x1[i] + b * x2[i] + c * x1[i] * x2[i])
         for i in data.index),
        name='error')
    m.set_objective(so.expr_sum(err[i]**2 for i in data.index),
                    sense=so.MIN,
                    name='total_error')
    m.solve(verbose=True, options={'with': 'nlp'})
    return m.get_objective_value()
Example #5
0
def test(cas_conn):

    m = so.Model(name='nlpse01', session=cas_conn)
    x = m.add_variables(range(1, 9), lb=0.1, ub=10, name='x')

    f = so.Expression(0.4 * (x[1] / x[7])**0.67 + 0.4 * (x[2] / x[8])**0.67 +
                      10 - x[1] - x[2],
                      name='f')
    m.set_objective(f, sense=so.MIN, name='f1')

    m.add_constraint(1 - 0.0588 * x[5] * x[7] - 0.1 * x[1] >= 0, name='c1')
    m.add_constraint(1 - 0.0588 * x[6] * x[8] - 0.1 * x[1] - 0.1 * x[2] >= 0,
                     name='c2')
    m.add_constraint(1 - 4 * x[3] / x[5] - 2 / (x[3]**0.71 * x[5]) - 0.0588 *
                     (x[7] / x[3]**1.3) >= 0,
                     name='c3')
    m.add_constraint(1 - 4 * x[4] / x[6] - 2 / (x[4]**0.71 * x[6]) - 0.0588 *
                     (x[8] / x[4]**1.3) >= 0,
                     name='c4')
    m.add_constraint(f == [0.1, 4.2], name='frange')

    x[1].set_init(6)
    x[2].set_init(3)
    x[3].set_init(0.4)
    x[4].set_init(0.2)
    x[5].set_init(6)
    x[6].set_init(6)
    x[7].set_init(1)
    x[8].set_init(0.5)

    m.solve(verbose=True, options={'with': 'nlp', 'algorithm': 'activeset'})

    print(m.get_problem_summary())
    print(m.get_solution_summary())
    if m.get_session_type() == 'CAS':
        print(m.get_solution()[['var', 'value']])

    return m.get_objective_value()
Example #6
0
def solve_maximin_problem(gw, options=None):

    if options is None:
        options = dict()

    data = get_multistage_data(gw, n=1)
    base_folder = pathlib.Path().resolve()
    output_folder = pathlib.Path(base_folder / f"build/work/")
    output_folder.mkdir(parents=True, exist_ok=True)
    options['output_folder'] = output_folder
    
    gw = data['gw']
    players = data['elements']
    types = data['types']
    types_df = data['types_df']
    next_week_df = data['element_gameweek_df']
    team_codes = data['team_codes']
    element_df = data['element_df']
    element_df['value'] = [next_week_df.loc[i, gw]['points_md'] / element_df.loc[i]['now_cost'] * 10 for i in element_df.index]
    sorted_elements = element_df.sort_values(by=['element_type', 'id'], ascending=[True, True], ignore_index=False)

    max_xp = next_week_df['points_md'].max()
    
    m = so.Model(name='o_ring_max_min_problem', session=None)
    x = m.add_variables(players, name='lineup', vartype=so.BIN)
    z = m.add_variables(players, name='squad', vartype=so.BIN)

    m.add_constraint(so.quick_sum(x[i] for i in players) == 11, name='lineup_limit')
    m.add_constraints((
        so.quick_sum(x[i] for i in players if next_week_df.loc[i, gw]['element_type'] == et) >= types_df.loc[et]['squad_min_play']
        for et in types), name='squad_min')
    m.add_constraints((
        so.quick_sum(x[i] for i in players if next_week_df.loc[i, gw]['element_type'] == et) <= types_df.loc[et]['squad_max_play']
        for et in types), name='squad_max')

    # Limit constraints
    m.add_constraints((
        so.quick_sum(z[i] for i in players if next_week_df.loc[i, gw]['element_type'] == et) == types_df.loc[et]['squad_select']
        for et in types), name='squad_exact')
    m.add_constraint(so.quick_sum(z[i] for i in players) == 15, name='squad_limit')
    m.add_constraints(
        (so.quick_sum(z[i] for i in players if next_week_df.loc[i, gw]['team_code'] == j) <= 3 for j in team_codes),
        name='player_team_limit')
    m.add_constraint(
        so.quick_sum(z[i] * next_week_df.loc[i, gw]['now_cost'] for i in players) <= 1000,
        name='total_cost_100')
    m.add_constraints(
        (x[i] <= z[i] for i in players), name='lineup_squad_con')

    total_lineup_xp = so.quick_sum(next_week_df.loc[i, gw]['points_md'] * x[i] for i in players)
    # total_squad_xp = so.quick_sum(next_week_df.loc[i, gw]['points_md'] * z[i] for i in players)    
    total_eff_value = so.quick_sum(element_df.loc[i]['value'] * x[i] for i in players)
    total_ownership = so.quick_sum(element_df.loc[i]['selected_by_percent'] * x[i] for i in players)
    total_xp_per_type = [so.quick_sum(next_week_df.loc[i, gw]['points_md'] * x[i] for i in players if element_df.loc[i]['element_type'] == k) for k in types]

    min_lineup_xp = lambda: min(next_week_df.loc[i, gw]['points_md'] for i in players if x[i].get_value() > 0.5)
    min_eff_value = lambda: min(element_df.loc[i]['value'] for i in players if x[i].get_value() > 0.5)
    min_ownership = lambda: min(element_df.loc[i]['selected_by_percent'] for i in players if x[i].get_value() > 0.5)
    min_xp_per_type = lambda: [round(min(next_week_df.loc[i, gw]['points_md'] for i in players if element_df.loc[i]['element_type'] == k and x[i].get_value() > 0.5),2) for k in types]
    get_lineup = lambda: ', '.join([sorted_elements.loc[e].web_name for e in sorted_elements.index if x[e].get_value() > 0.5])

    get_sol_summary = lambda: {
        'problem_name': problem_name,
        'total_lineup_xp': round(total_lineup_xp.get_value(), 3),
        'total_eff_value': round(total_eff_value.get_value(), 3),
        'total_ownership': round(total_ownership.get_value(), 1),
        'total_xp_per_type': [round(i.get_value(),3) for i in total_xp_per_type],
        'min_linuep_xp': round(min_lineup_xp(), 3),
        'min_eff_value': round(min_eff_value(), 3),
        'min_ownership': round(min_ownership(), 1),
        'min_xp_per_type': min_xp_per_type(),
        'lineup': get_lineup()}

    comp_values = []

    # Stage 0 - Lineup maximization

    m.set_objective(-total_lineup_xp, sense='N', name='regular_obj')
    problem_name = f"GW{gw}_maximin_0_base"
    solve_and_save_to_file(m, problem_name, data, options)
    comp_values.append(get_sol_summary())
    print(comp_values)

    # Problem 1 - Stage 1 - Max-min

    for i in players:
        x[i].set_value(0)
        z[i].set_value(0)

    m1 = so.Model(name='maximin')
    m1.include(m)
    w = m1.add_variable(name='weakest_link',vartype=so.CONT)
    m1.add_constraints(
        (w <= x[i] * next_week_df.loc[i, gw].points_md + (1-x[i]) * max_xp for i in players), name='weakest_link_con')
    m1.set_objective(-w, sense='N', name='max_min_obj')
    problem_name = f"GW{gw}_maximin_1_1_maximin"
    solve_and_save_to_file(m1, problem_name, data, options)
    comp_values.append(get_sol_summary())
    print(comp_values)

    # Problem 1 - Stage 2 - Max Lineup xP

    for i in players:
        x[i].set_value(0)
        z[i].set_value(0)

    m1.add_constraint(w >= w.get_value(), name='w_lb')
    m1.set_objective(-total_lineup_xp, sense='N', name='max_lineup')

    problem_name = f"GW{gw}_maximin_1_2_maximin"
    solve_and_save_to_file(m1, problem_name, data, options)
    comp_values.append(get_sol_summary())
    print(comp_values)

    # # Problem 1 - Stage 3 - Max Squad xP

    # m.add_constraints((x[i] >= round(x[i].get_value()) for i in players), name='fix_lineup')

    # for i in players:
    #     x[i].set_value(0)
    #     z[i].set_value(0)

    # m.set_objective(-total_squad_xp, sense='N', name='max_squad')
    # problem_name = f"GW{gw}_maximin_1_3_maximin"
    # solve_and_save_to_file(m, problem_name, data, options)

    # Problem 2 - Maximin per position

    for i in players:
        x[i].set_value(0)
        z[i].set_value(0)

    m2 = so.Model(name='maximin_per_position')
    m2.include(m)
    w = m2.add_variable('w', vartype=so.CONT)

    # w_type = m2.add_variables(types, name='weakest_link_pos')
    # m2.add_constraints(
    #     (w_type[k] <= x[i] * next_week_df.loc[i, gw].points_md + (1-x[i]) * max_xp for i in players for k in types if element_df.loc[i]['element_type'] == k), name='weakest_link_type_con')
    # m2.set_objective(-so.quick_sum(w_type[k] for k in types), sense='N', name='type_max')

    m2.add_constraints(
        (w <= so.quick_sum(x[i] * next_week_df.loc[i, gw].points_md for i in players if element_df.loc[i]['element_type'] == k) for k in types if k != 1), name='weakest_link_type_con')
    m2.set_objective(-w, sense='N', name='type_max')
    problem_name = f"GW{gw}_maximin_2_1_pos"
    solve_and_save_to_file(m2, problem_name, data, options)
    comp_values.append(get_sol_summary())
    print(comp_values)

    # Problem 2 - Stage 2

    for i in players:
        x[i].set_value(0)
        z[i].set_value(0)
    
    m2.add_constraint(w >= w.get_value(), name='w_lb')
    m2.set_objective(-total_lineup_xp, sense='N', name='max_lineup')

    problem_name = f"GW{gw}_maximin_2_2_pos"
    solve_and_save_to_file(m2, problem_name, data, options)
    comp_values.append(get_sol_summary())
    print(comp_values)


    # Problem 3 - Stage 1 - Maximin for Value

    for i in players:
        x[i].set_value(0)
        z[i].set_value(0)

    m3 = so.Model(name='maximin_per_position')
    m3.include(m)
    w = m3.add_variable(name='lowest_val',vartype=so.CONT)
    max_val = element_df['value'].max()
    m3.add_constraints(
        (w <= x[i] * element_df.loc[i].value + (1-x[i]) * max_val for i in players), name='lowest_val')
    m3.set_objective(-w, sense='N', name='max_min_value')
    problem_name = f"GW{gw}_maximin_3_1_val"
    solve_and_save_to_file(m3, problem_name, data, options)
    comp_values.append(get_sol_summary())
    print(comp_values)

    # Problem 3 - Stage 2

    for i in players:
        x[i].set_value(0)
        z[i].set_value(0)

    m3.add_constraint(w >= w.get_value(), name='w_lb')
    m3.set_objective(-total_lineup_xp, sense='N', name='max_min_value')
    problem_name = f"GW{gw}_maximin_3_2_val"
    solve_and_save_to_file(m3, problem_name, data, options)
    comp_values.append(get_sol_summary())
    print(comp_values)



    # Problem 4 - Stage 1 - Ownership

    for i in players:
        x[i].set_value(0)
        z[i].set_value(0)

    m4 = so.Model(name='maximin_per_position')
    m4.include(m)
    w = m4.add_variable(name='lowest_own',vartype=so.CONT)
    max_own = element_df['selected_by_percent'].max()
    m4.add_constraints(
        (w <= x[i] * element_df.loc[i].selected_by_percent + (1-x[i]) * max_own for i in players), name='lowest_owned')
    m4.set_objective(-w, sense='N', name='max_min_owner')
    problem_name = f"GW{gw}_maximin_4_1_own"
    solve_and_save_to_file(m4, problem_name, data, options)
    comp_values.append(get_sol_summary())
    print(comp_values)

    # Problem 4 - Stage 2

    for i in players:
        x[i].set_value(0)
        z[i].set_value(0)

    m4.add_constraint(w >= w.get_value(), name='w_lb')
    m4.set_objective(-total_lineup_xp, sense='N', name='max_min_value')
    problem_name = f"GW{gw}_maximin_4_2_own"
    solve_and_save_to_file(m4, problem_name, data, options)
    comp_values.append(get_sol_summary())
    print(comp_values)


    comp_values_df = pd.DataFrame(comp_values)
    comp_values_df.to_excel(output_folder / f"GW{gw}_o_ring_summary.xlsx")
    print(comp_values_df)

    return 0
Example #7
0
def solve_best_set_and_forget(input_folder, output_folder):
    """Solves the budget (1000) and team limited best squad (lineup+subs) with weighted bench total of 8 gameweeks"""
    df = pd.read_csv(input_folder / 'element_gameweek.csv')
    # next_week = df['event'].min()
    # next_week_df = df[df['event'] == next_week].copy()
    df = df.groupby(['player_id', 'event'], as_index=False).first()
    next_week_df = df.groupby(['player_id', 'web_name', 'team'])['points_md'].sum()
    next_week_df = next_week_df.reset_index()

    df = pd.read_csv(input_folder / 'element.csv')
    next_week_df = pd.merge(left=next_week_df, right=df, how='inner', left_on=['player_id'], right_on=['id'], suffixes=('', '_extra'))
    players = next_week_df['player_id'].unique().tolist()
    next_week_df.set_index('player_id', inplace=True, drop=True)

    team_codes = next_week_df['team_code'].unique().tolist()
    
    types_df = pd.read_csv(input_folder / 'element_type.csv')
    types = types_df['id'].to_list()
    types_df.set_index('id', inplace=True, drop=True)

    m = so.Model(name='limited_best_15_weighted', session=None)
    x = m.add_variables(players, name='lineup', vartype=so.BIN)
    y = m.add_variables(players, name='captain', vartype=so.BIN)
    z = m.add_variables(players, name='squad', vartype=so.BIN)

    m.add_constraint(so.quick_sum(x[i] for i in players) == 11, name='lineup_limit')
    m.add_constraints((
        so.quick_sum(x[i] for i in players if next_week_df.loc[i]['element_type'] == et) >= types_df.loc[et]['squad_min_play']
        for et in types), name='squad_min')
    m.add_constraints((
        so.quick_sum(x[i] for i in players if next_week_df.loc[i]['element_type'] == et) <= types_df.loc[et]['squad_max_play']
        for et in types), name='squad_max')
    m.add_constraint(so.quick_sum(y[i] for i in players) == 1, name='single_captain')
    m.add_constraints((y[i] <= x[i] for i in players), name='captain_should_play')

    # Limit constraints
    m.add_constraints((
        so.quick_sum(z[i] for i in players if next_week_df.loc[i]['element_type'] == et) == types_df.loc[et]['squad_select']
        for et in types), name='squad_exact')
    m.add_constraint(so.quick_sum(z[i] for i in players) == 15, name='squad_limit')
    m.add_constraints(
        (so.quick_sum(z[i] for i in players if next_week_df.loc[i]['team_code'] == j) <= 3 for j in team_codes),
        name='player_team_limit')
    m.add_constraint(
        so.quick_sum(z[i] * next_week_df.loc[i]['now_cost'] for i in players) <= 1000,
        name='total_cost_100')
    m.add_constraints(
        (x[i] <= z[i] for i in players), name='lineup_squad_con')

    m.set_objective(so.quick_sum(
        -next_week_df.loc[i]['points_md'] * (x[i]+y[i]+0.1*(z[i]-x[i])) for i in players
    ), sense='N', name='maximize_points')

    mps_str = get_mps_string(m)

    with open(output_folder / "limited_best_set_and_forget.mps", "w") as f:
        f.write(mps_str)
    
    filename = str(output_folder / "limited_best_set_and_forget.mps")
    solutionname = str(output_folder / "limited_best_set_and_forget.sol")
    csvname = str(output_folder / "limited_best_set_and_forget.csv")
    
    solve_and_get_solution(filename, solutionname, m)
    
    # Parse solution
    selected_players = []
    for i in players:
        if z[i].get_value() > 0.5 and x[i].get_value() < 0.5:
            selected_players.append([i, 0, False, 0])
        elif x[i].get_value() > 0.5 and y[i].get_value() < 0.5:
            selected_players.append([i, 1, False, 1])
        elif x[i].get_value() > 0.5 and y[i].get_value() > 0.5:
            selected_players.append([i, 2, True, 1])
    results = pd.DataFrame(selected_players, columns=['player_id', 'multiplier', 'is_captain', 'starting_lineup'])
    result_df = pd.merge(next_week_df, results, on='player_id', how='inner')
    result_df = result_df[['player_id', 'web_name', 'team_code', 'element_type', 'now_cost', 'points_md', 'is_captain', 'multiplier', 'starting_lineup', 'selected_by_percent']].copy()
    result_df['points_md'] = result_df['points_md'] / 8
    result_df['gw_points'] = result_df['multiplier'] * result_df['points_md']
    result_df = result_df.sort_values(by=['starting_lineup', 'element_type', 'player_id'], ascending=[False, True, True], ignore_index=True)
    result_df.reset_index(drop=True, inplace=True)
    result_df.to_csv(csvname, encoding='utf-8')
    with pd.option_context('display.max_rows', None, 'display.max_columns', None, 'display.encoding', 'UTF-8'): 
        print(result_df)
    print(m.get_objective_value())
Example #8
0
def solve_iterative_squads(input_folder, output_folder, total_iter=50):
    """Solves for best squads iteratively for generating an interactive list"""
    df = pd.read_csv(input_folder / 'element_gameweek.csv')
    next_week = df['event'].min()
    element_gameweek_df = df[df['event'] < next_week+3].copy()

    sum_md_df = element_gameweek_df.groupby(['player_id', 'web_name'])['points_md'].sum()
    sum_md_df.sort_values(inplace=True, ascending=False)
    sum_md_df = sum_md_df.reset_index().set_index('player_id').copy()

    df = pd.read_csv(input_folder / 'element.csv')
    element_df = df.copy().set_index('id')
    element_df['ict_sum'] = element_df['influence'] + element_df['creativity'] + element_df['threat']
    element_gameweek_df = pd.merge(left=element_gameweek_df, right=df, how='inner', left_on=['player_id'], right_on=['id'], suffixes=('', '_extra'))
    element_gameweek_df['rawxp'] = element_gameweek_df.apply(lambda r: r['points_md'] * 90 / max(1, r['xmins_md']), axis=1)
    elements = element_gameweek_df['player_id'].unique().tolist()
    gameweeks = element_gameweek_df['event'].unique().tolist()
    total_weeks = len(gameweeks)
    element_gameweek_df.set_index(['player_id', 'event'], inplace=True, drop=True)
    popular_element_df = element_df.sort_values(by=['selected_by_percent'], ascending=False)[['web_name', 'selected_by_percent']]

    team_codes = element_gameweek_df['team_code'].unique().tolist()
    
    types_df = pd.read_csv(input_folder / 'element_type.csv')
    types = types_df['id'].to_list()
    types_df.set_index('id', inplace=True, drop=True)

    position = [1,2,3,4]
    element_gameweek = [(e, g) for e in elements for g in gameweeks]

    m = so.Model(name='iterative_model', session=None)
    lineup = m.add_variables(elements, gameweeks, name='lineup', vartype=so.BIN)
    captain = m.add_variables(elements, gameweeks, name='captain', vartype=so.BIN)
    squad = m.add_variables(elements, name='squad', vartype=so.BIN)
    bench = m.add_variables(elements, gameweeks, position, name='bench', vartype=so.BIN)

    m.add_constraints(
        (so.quick_sum(lineup[e, g] for e in elements) == 11 for g in gameweeks),
        name='lineup_limit_per_week')

    m.add_constraints((
        so.quick_sum(lineup[e, g] for e in elements if element_df.loc[e]['element_type'] == et) >= types_df.loc[et]['squad_min_play']
        for et in types for g in gameweeks), name='squad_min')

    m.add_constraints((
        so.quick_sum(lineup[e, g] for e in elements if element_df.loc[e]['element_type'] == et) <= types_df.loc[et]['squad_max_play']
        for et in types for g in gameweeks), name='squad_max')

    m.add_constraints((so.quick_sum(captain[e, g] for e in elements) == 1 for g in gameweeks), name='single_captain')
    m.add_constraints((captain[e, g] <= lineup[e, g] for e in elements for g in gameweeks), name='captain_should_play')

    # Limit constraints
    m.add_constraints((
        so.quick_sum(squad[e] for e in elements if element_df.loc[e]['element_type'] == et) == types_df.loc[et]['squad_select']
        for et in types), name='squad_exact')
    m.add_constraint(so.quick_sum(squad[e] for e in elements) == 15, name='squad_limit')
    m.add_constraints(
        (so.quick_sum(squad[e] for e in elements if element_df.loc[e]['team_code'] == j) <= 3 for j in team_codes),
        name='player_team_limit')
    # m.add_constraint(
    #     so.quick_sum(squad[i] * next_week_df.loc[i]['now_cost'] for i in players) <= 1000,
    #     name='total_cost_100')
    m.add_constraints(
        (lineup[e, g] <= squad[e] for e in elements for g in gameweeks), name='lineup_squad_con')
    m.add_constraints(
        (bench[e, g, p] <= squad[e] for e in elements for g in gameweeks for p in position), name='bench_squad_con')
    m.add_constraints(
        (so.quick_sum(bench[e, g, p] for e in elements) == 1 for g in gameweeks for p in position), name='bench_position_con')
    m.add_constraints(
        (so.quick_sum(bench[e, g, 1] for e in elements if element_df.loc[e]['element_type'] == 1) == 1 for g in gameweeks), name='only_gk_bench1')
    m.add_constraints(
        (lineup[e, g] + so.quick_sum(bench[e, g, p] for p in position) == squad[e] for e in elements for g in gameweeks), name='lineup_plus_bench_equal_squad')

    # Budget Constraint
    budget_con = m.add_constraint(so.quick_sum(squad[e] * element_df.loc[e]['now_cost'] for e in elements) <= 1000, name='budget_con')

    # Every player should play at least once
    m.add_constraints((so.quick_sum(lineup[e,g] for g in gameweeks) >= squad[e] for e in elements), name='play_at_least_once')

    total_points = so.quick_sum(element_gameweek_df.loc[e, g]['points_md'] * (lineup[e, g]+ captain[e,g] + so.quick_sum(bench[e,g,p]*10**min(-p+1, -1) for p in position)) for e in elements for g in gameweeks) / total_weeks / element_gameweek_df['points_md'].max()
    cost_points = so.quick_sum(element_df.loc[e]['now_cost'] * squad[e] for e in elements) / element_df['now_cost'].max()
    xmin_points = so.quick_sum(element_gameweek_df.loc[e, g]['xmins_md']*lineup[e,g] for e in elements for g in gameweeks) / total_weeks / element_gameweek_df['xmins_md'].max()
    ep_points = so.quick_sum(element_df.loc[e]['ep_this'] * lineup[e, next_week] for e in elements) / element_df['ep_this'].max()
    form_points = so.quick_sum(element_df.loc[e]['form'] * lineup[e, next_week] for e in elements) / element_df['form'].max()
    ppg_points = so.quick_sum(element_df.loc[e]['points_per_game'] * lineup[e, next_week] for e in elements) / element_df['points_per_game'].max()
    bps_points = so.quick_sum(element_df.loc[e]['bps'] * lineup[e, next_week] for e in elements) / element_df['bps'].max()
    ict_points = so.quick_sum(element_df.loc[e]['ict_sum'] * lineup[e, next_week] for e in elements) / element_df['ict_sum'].max()
    rawxP_points = so.quick_sum(element_gameweek_df.loc[e, g]['rawxp'] * lineup[e, g] for e in elements for g in gameweeks) / total_weeks / element_gameweek_df['rawxp'].max()
    ownership_points = so.quick_sum(element_gameweek_df.loc[e, g]['points_md']*(1-lineup[e, g])*element_df.loc[e]['selected_by_percent']/100.0 for e in elements for g in gameweeks) / total_weeks / element_gameweek_df['points_md'].max()

    m.set_objective(-total_points,
        sense='N', name='maximize_points')

    name = "iterative_model"

    filename = str(output_folder / f"{name}.mps")
    solutionname = str(output_folder / f"{name}.sol")
    csvname = str(output_folder / f"{name}.csv")
    
    all_solutions = []
    w1 = 1
    w2 = w3 = w4 = w5 = w6 = w7 = w8 = w9 = w10 = 0

    random.seed(42)

    for iteration in range(total_iter):

        if iteration > 0:
            # add squad cut
            m.add_constraint(so.quick_sum(squad[e] for e in elements if squad[e].get_value() > 0.5) <= 12, name=f"cutoff_{iteration}")

            # change objective
            w1, w2, w3, w4, w5, w6, w7, w8, w9 = (random.random() for _ in range(9))
            w10 = random.random()*2-1
            # m.set_objective(- (
            #     w1 * total_points + w2 * cost_points + w3 * xmin_points + w4 * ep_points + w5 * form_points + w6 * ppg_points + w7 * bps_points + w8 * ict_points + w9 * rawxP_points + w10 * ownership_points),
            #     sense='N', name=f'obj_{iteration}')

            # # Idea 1: Discard most popular/owned 1 out of 3 players
            # selected_ids = [e for e in elements if squad[e].get_value() > 0.5]
            # selected_els_df = popular_element_df[popular_element_df.index.isin(selected_ids)]
            # top3_els = selected_els_df.index[:3].to_list()
            # print('Popular cut', selected_els_df.loc[top3_els]['web_name'])
            # m.add_constraint(so.quick_sum(squad[e] for e in top3_els) <= 2, name=f'popular_cut_{iteration}')

            # # Idea 2: Discard player with most return 1 out of 3 players
            # selected_els_df = sum_md_df[sum_md_df.index.isin(selected_ids)]
            # top3_els = selected_els_df.index[:3].to_list()
            # print('Return cut', selected_els_df.loc[top3_els]['web_name'])
            # m.add_constraint(so.quick_sum(squad[e] for e in top3_els) <= 2, name=f'return_cut_{iteration}')

            # # Idea 3: Discard 3 players each time


            # # add position cuts
            # # m.add_constraints((so.quick_sum(squad[e] for e in elements if squad[e].get_value() > 0.5 and element_df.loc[e]['element_type'] == et) <= types_df.loc[et]['squad_select']-1 for et in types), name=f"el_type_cutoff_{iteration}")
            # # add squad cut
            # # m.add_constraint(so.quick_sum(squad[e] for e in elements if squad[e].get_value() > 0.5) <= 14, name=f"cutoff_{iteration}")
            # # budget_adj = iteration // 5
            # # budget_con.set_rhs(1050 - budget_adj*10)
            # # m.set_objective(total_points - ownership_weight*missed_ownership_points - budget_weight*cost_value_points, sense='N', name=f'obj_b{budget_weight}_o{ownership_weight}')
            # # m.set_objective(total_points - ownership_weight*missed_ownership_points, sense='N', name=f'obj_o{ownership_weight}')


        mps_str = get_mps_string(m)
        with open(output_folder / f"{name}.mps", "w") as f:
            f.write(mps_str)
        
        for v in m.get_variables():
            v.set_value(0)

        solve_and_get_solution(filename, solutionname, m)
        
        print(f"DONE {iteration}")

        # Parse solution
        solution = {"id": iteration, "squad":[], "lineup": {g:[] for g in gameweeks}, "bench": {g:{} for g in gameweeks}, "captain": {g: 0 for g in gameweeks}, "ownership": {}}
        for e in elements:
            if squad[e].get_value() > 0.5:
                solution['squad'].append(e)
        for e in elements:
            for g in gameweeks:
                if lineup[e, g].get_value() > 0.5:
                    solution['lineup'][g].append(e)
                elif squad[e].get_value() > 0.5:
                    for p in position:
                        if bench[e, g, p].get_value() > 0.5:
                            solution['bench'][g][p] = e
                if captain[e, g].get_value() > 0.5:
                    solution['captain'][g] = e
        # solution["params"] = {'ownership_weight': ownership_weight} # 'cost_weight': budget_weight,
        solution["params"] = {'pts_weight': w1, 'cost_weight': w2, 'xmin_weight': w3, 'ep_weight': w4, 'form_weight': w5, 'ppg_weight': w6, 'bps_weight': w7, 'ict_weight': w8, 'rawxp_weight': w9, 'ownership_weight': w10}
        solution["players"] = [element_df.loc[e]['web_name'] for e in solution['squad']]
        solution["xP"] = {g: [element_gameweek_df.loc[e, g]['points_md'] for e in solution['squad']] for g in gameweeks}
        solution["obj"] = {"overall": m.get_objective_value(),
        "total_points": total_points.get_value(), "cost_points": cost_points.get_value(),
        "xmin_points": xmin_points.get_value(), "ep_points": ep_points.get_value(),
        "form_points": form_points.get_value(), "ppg_points": ppg_points.get_value(),
        "bps_points": bps_points.get_value(), "ict_points": ict_points.get_value(),
        "rawxP_points": rawxP_points.get_value(), "ownership_points": ownership_points.get_value()} #, "missed_ownership_points": missed_ownership_points.get_value()} #, "budget_points": cost_value_points.get_value()}
        solution["cost"] = [float(element_df.loc[e]['now_cost']) for e in solution['squad']]
        solution["total_cost"] = sum(solution["cost"])
        solution["ownership"]["squad"] = [float(element_df.loc[e]["selected_by_percent"]) for e in solution['squad']]
        solution["ownership"]["sum"] = sum(solution["ownership"]["squad"])
        solution["element_type"] = [int(element_df.loc[e]['element_type']) for e in solution['squad']]
        solution["team_code"] = [int(element_df.loc[e]['team_code']) for e in solution['squad']]
        solution["weeks"] = gameweeks
        for g in gameweeks:
            solution["obj"][g] = so.quick_sum(-element_gameweek_df.loc[e, g]['points_md'] * (lineup[e, g]+ captain[e,g] + so.quick_sum(bench[e,g,p]*10**min(-p+1, -1) for p in position)) for e in elements).get_value()

        print(solution)
        print(m.get_objective_value())
        all_solutions.append(solution)

        with open(output_folder / "iterative_model.json", "w") as f:
            json.dump(all_solutions, f)
    
    print(all_solutions)
Example #9
0
def test(cas_conn):

    m = so.Model(name='economic_planning', session=cas_conn)

    industry_data = pd.DataFrame([
        ['coal', 150, 300, 60],
        ['steel', 80, 350, 60],
        ['transport', 100, 280, 30]
        ], columns=['industry', 'init_stocks', 'init_productive_capacity',
                    'demand']).set_index(['industry'])

    production_data = pd.DataFrame([
        ['coal', 0.1, 0.5, 0.4],
        ['steel', 0.1, 0.1, 0.2],
        ['transport', 0.2, 0.1, 0.2],
        ['manpower', 0.6, 0.3, 0.2],
        ], columns=['input', 'coal',
                    'steel', 'transport']).set_index(['input'])

    productive_capacity_data = pd.DataFrame([
        ['coal', 0.0, 0.7, 0.9],
        ['steel', 0.1, 0.1, 0.2],
        ['transport', 0.2, 0.1, 0.2],
        ['manpower', 0.4, 0.2, 0.1],
        ], columns=['input', 'coal',
                    'steel', 'transport']).set_index(['input'])

    manpower_capacity = 470
    num_years = 5

    YEARS = list(range(1, num_years+1))
    YEARS0 = [0] + list(YEARS)
    INDUSTRIES = industry_data.index.tolist()
    [init_stocks, init_productive_capacity, demand] = so.read_frame(
        industry_data)
    # INPUTS = production_data.index.tolist()
    production_coeff = so.flatten_frame(production_data)
    productive_capacity_coeff = so.flatten_frame(productive_capacity_data)

    static_production = m.add_variables(INDUSTRIES, lb=0,
                                        name='static_production')
    m.set_objective(0, sense=so.MIN, name='Zero')
    m.add_constraints((static_production[i] == demand[i] +
                       so.quick_sum(
                           production_coeff[i, j] * static_production[j]
                           for j in INDUSTRIES) for i in INDUSTRIES),
                      name='static_con')

    m.solve()
    print(so.get_solution_table(static_production, sort=True))

    final_demand = so.get_solution_table(
        static_production, sort=True)['static_production']
    # Alternative way
    # final_demand = {}
    # for i in INDUSTRIES:
    #     final_demand[i] = static_production.get_value()

    production = m.add_variables(INDUSTRIES, range(0, num_years+2), lb=0,
                                 name='production')
    stock = m.add_variables(INDUSTRIES, range(0, num_years+2), lb=0,
                            name='stock')
    extra_capacity = m.add_variables(INDUSTRIES, range(1, num_years+3), lb=0,
                                     name='extra_capacity')
    productive_capacity = {}
    for i in INDUSTRIES:
        for year in range(1, num_years+2):
            productive_capacity[i, year] = init_productive_capacity[i] +\
                so.quick_sum(extra_capacity[i, y] for y in range(2, year+1))
    for i in INDUSTRIES:
        production[i, 0].set_bounds(ub=0)
        stock[i, 0].set_bounds(lb=init_stocks[i], ub=init_stocks[i])

    total_productive_capacity = sum(productive_capacity[i, num_years]
                                    for i in INDUSTRIES)
    total_production = so.quick_sum(production[i, year] for i in INDUSTRIES
                                    for year in [4, 5])
    total_manpower = so.quick_sum(production_coeff['manpower', i] *
                                  production[i, year+1] +
                                  productive_capacity_coeff['manpower', i] *
                                  extra_capacity[i, year+2]
                                  for i in INDUSTRIES for year in YEARS)

    continuity_con = m.add_constraints((
        stock[i, year] + production[i, year] ==
        (demand[i] if year in YEARS else 0) +
        so.quick_sum(production_coeff[i, j] * production[j, year+1] +
                     productive_capacity_coeff[i, j] *
                     extra_capacity[j, year+2] for j in INDUSTRIES) +
        stock[i, year+1]
        for i in INDUSTRIES for year in YEARS0), name='continuity_con')

    manpower_con = m.add_constraints((
        so.quick_sum(production_coeff['manpower', j] * production[j, year] +
                     productive_capacity_coeff['manpower', j] *
                     extra_capacity[j, year+1]
                     for j in INDUSTRIES)
        <= manpower_capacity for year in range(1, num_years+2)),
        name='manpower_con')

    capacity_con = m.add_constraints((production[i, year] <=
                                      productive_capacity[i, year]
                                      for i in INDUSTRIES
                                      for year in range(1, num_years+2)),
                                     name='capacity_con')

    for i in INDUSTRIES:
        production[i, num_years+1].set_bounds(lb=final_demand[i])

    for i in INDUSTRIES:
        for year in [num_years+1, num_years+2]:
            extra_capacity[i, year].set_bounds(ub=0)

    problem1 = so.Model(name='Problem1', session=cas_conn)
    problem1.include(production, stock, extra_capacity,
                     continuity_con, manpower_con, capacity_con)
    problem1.set_objective(total_productive_capacity, sense=so.MAX,
                           name='total_productive_capacity')
    problem1.solve()
    productive_capacity_fr = so.dict_to_frame(productive_capacity,
                                              cols=['productive_capacity'])
    print(so.get_solution_table(production, stock, extra_capacity,
                                productive_capacity_fr, sort=True))
    print(so.get_solution_table(manpower_con.get_expressions(), sort=True))

    # Problem 2

    problem2 = so.Model(name='Problem2', session=cas_conn)
    problem2.include(problem1)
    problem2.set_objective(total_production, name='total_production',
                           sense=so.MAX)
    for i in INDUSTRIES:
        for year in YEARS:
            continuity_con[i, year].set_rhs(0)
    problem2.solve()
    print(so.get_solution_table(production, stock, extra_capacity,
                                productive_capacity, sort=True))
    print(so.get_solution_table(manpower_con.get_expressions(), sort=True))

    # Problem 3

    problem3 = so.Model(name='Problem3', session=cas_conn)
    problem3.include(production, stock, extra_capacity, continuity_con,
                     capacity_con)
    problem3.set_objective(total_manpower, sense=so.MAX, name='total_manpower')
    for i in INDUSTRIES:
        for year in YEARS:
            continuity_con[i, year].set_rhs(demand[i])
    problem3.solve()
    print(so.get_solution_table(production, stock, extra_capacity,
                                productive_capacity, sort=True))
    print(so.get_solution_table(manpower_con.get_expressions(), sort=True))

    return problem3.get_objective_value()
Example #10
0
def solve_no_limit_best_11(input_folder, output_folder):
    """Solves the no limit (money/team) best 11 (no full squad) problem"""
    df = pd.read_csv(input_folder / 'element_gameweek.csv')
    next_week = df['event'].min()
    next_week_df = df[df['event'] == next_week].copy()

    df = pd.read_csv(input_folder / 'element.csv')
    next_week_df = next_week_df.groupby(['player_id'], as_index=False).first()
    next_week_df = pd.merge(left=next_week_df, right=df, how='inner', left_on=['player_id'], right_on=['id'], suffixes=('', '_extra'))
    players = next_week_df['player_id'].unique().tolist()
    next_week_df.set_index('player_id', inplace=True, drop=True)
    
    types_df = pd.read_csv(input_folder / 'element_type.csv')
    types = types_df['id'].to_list()
    types_df.set_index('id', inplace=True, drop=True)

    m = so.Model(name='no_limit_best11', session=None)
    x = m.add_variables(players, name='lineup', vartype=so.BIN)
    y = m.add_variables(players, name='captain', vartype=so.BIN)

    m.add_constraint(so.quick_sum(x[i] for i in players) == 11, name='lineup_limit')
    m.add_constraints((
        so.quick_sum(x[i] for i in players if next_week_df.loc[i]['element_type'] == et) >= types_df.loc[et]['squad_min_play']
        for et in types), name='squad_min')
    m.add_constraints((
        so.quick_sum(x[i] for i in players if next_week_df.loc[i]['element_type'] == et) <= types_df.loc[et]['squad_max_play']
        for et in types), name='squad_max')
    m.add_constraint(so.quick_sum(y[i] for i in players) == 1, name='single_captain')
    m.add_constraints((y[i] <= x[i] for i in players), name='captain_should_play')

    m.set_objective(so.quick_sum(
        -next_week_df.loc[i]['points_md'] * (x[i]+y[i]) for i in players
    ), sense='N', name='maximize_points')

    mps_str = get_mps_string(m)

    with open(output_folder / "no_limit_best_11.mps", "w") as f:
        f.write(mps_str)
    
    filename = str(output_folder / "no_limit_best_11.mps")
    solutionname = str(output_folder / "no_limit_best_11.sol")
    csvname = str(output_folder / "no_limit_best_11.csv")
    
    solve_and_get_solution(filename, solutionname, m)
    
    # Parse solution
    selected_players = []
    for i in players:
        if x[i].get_value() > 0.5 and y[i].get_value() < 0.5:
            selected_players.append([i, 1, False])
        elif x[i].get_value() > 0.5 and y[i].get_value() > 0.5:
            selected_players.append([i, 2, True])
    results = pd.DataFrame(selected_players, columns=['player_id', 'multiplier', 'is_captain'])
    result_df = pd.merge(next_week_df, results, on='player_id', how='inner')
    result_df = result_df[['player_id', 'web_name', 'team_code', 'element_type', 'now_cost', 'event', 'points_md', 'is_captain', 'multiplier', 'selected_by_percent']].copy()
    result_df['gw_points'] = result_df['multiplier'] * result_df['points_md']
    result_df = result_df.sort_values(by=['element_type', 'player_id'], ignore_index=True)
    result_df.reset_index(drop=True, inplace=True)
    result_df.to_csv(csvname, encoding='utf-8')
    with pd.option_context('display.max_rows', None, 'display.max_columns', None, 'display.encoding', 'UTF-8'): 
        print(result_df)
    print(m.get_objective_value())
    def solve_multi_week(self, ft, horizon, decay_base=1.0):
        
        # ToDo: absorb optimal squad method into this one
        #       compare your own team outcomes with the optimal squad
        #       useful for deciding when to wildcard
        # ToDo: add parameter for helping to decide on when to use bench boost
        '''
        Solves multi-objective FPL problem with transfers
        Parameters
        ----------
        ft: integer
            Number of available free transfers (currently)
        horizon: integer
            Number of weeks to consider in optimization
        decay_base: float
            Base for the decay function, default of 1 means no decay
        '''

        # Data
        players = self.forecasts.index
        positions = self.positions.index
        teams = self.teams.index
        current_squad = self.current_squad['player_id'].tolist()
        itb = self.bank
        gameweeks = list(range(self.gw, self.gw+horizon))
        all_gw = [self.gw-1] + gameweeks

        # Model
        model_name = f'w{self.gw}_h{horizon}_d{decay_base}'
        model = so.Model(model_name)

        # Variables
        squad = model.add_variables(
            players, all_gw, name='squad', vartype=so.binary)
        lineup = model.add_variables(
            players, gameweeks, name='lineup', vartype=so.binary)
        captain = model.add_variables(
            players, gameweeks, name='captain', vartype=so.binary)
        vicecap = model.add_variables(
            players, gameweeks, name='vicecap', vartype=so.binary)
        transfer_in = model.add_variables(
            players, gameweeks, name='transfer_in', vartype=so.binary)
        transfer_out = model.add_variables(
            players, gameweeks, name='transfer_out', vartype=so.binary)
        in_the_bank = model.add_variables(
            all_gw, name='itb', vartype=so.continuous, lb=0)
        free_transfers = model.add_variables(
            all_gw, name='ft', vartype=so.integer, lb=1, ub=2)
        penalized_transfers = model.add_variables(
            gameweeks, name='pt', vartype=so.integer, lb=0)
        # artificial binary variable to handle transfer logic
        aux = model.add_variables(
            gameweeks, name='aux', vartype=so.binary)
        
        # Dictionaries
        # sell prices of all players
        sell_price = self.forecasts['sv'].to_dict()
        # buy prices of all players
        buy_price = self.forecasts['bv'].to_dict()
        # total bank earned from selling players across gameweeks
        sold_amount = {w: so.expr_sum(sell_price[p] * transfer_out[p,w]
                                    for p in players)
                    for w in gameweeks}
        # total bank spent on buying players across gameweeks
        bought_amount = {w: so.expr_sum(buy_price[p] * transfer_in[p,w]
                                        for p in players)
                        for w in gameweeks}
        # player weekly forecast points
        points_player_week = {(p,w): self.forecasts.loc[p, f'{w}_pts']
                            for p in players for w in gameweeks}
        # number of transfers made each week
        number_of_transfers = {w: so.expr_sum(transfer_out[p,w] for p in players)
                            for w in gameweeks}
        # assume one transfer was made last week ?? why ??
        number_of_transfers[self.gw-1] = 1
        transfer_diff = {w: number_of_transfers[w] - free_transfers[w]
                        for w in gameweeks}

        # Initial conditions
        # set squad = 1 for all players currently in squad at previous GW deadline
        model.add_constraints(
            (squad[p, self.gw-1] == 1 for p in current_squad),
            name='initial_squad_players')
        # set squad = 0 for all other players
        model.add_constraints(
            (squad[p, self.gw-1] == 0 for p in players if p not in current_squad),
            name='initial_squad_others')
        # add current bank value
        model.add_constraint(in_the_bank[self.gw-1] == itb, name='initial_itb')
        # add current free transfers
        model.add_constraint(free_transfers[self.gw-1] == ft, name='initial_ft')

        # Constraints (per week)
        # 15 players in squad
        squad_count = {
            w: so.expr_sum(squad[p, w] for p in players)
            for w in gameweeks}
        model.add_constraints(
            (squad_count[w] == 15 for w in gameweeks), name='squad_count')
        # 11 players in starting lineup
        model.add_constraints(
            (so.expr_sum(lineup[p,w] for p in players) == 11 for w in gameweeks), 
            name='lineup_count')
        # 1 captain
        model.add_constraints(
            (so.expr_sum(captain[p,w] for p in players) == 1 for w in gameweeks), 
            name='captain_count')
        # 1 vice-captain
        model.add_constraints(
            (so.expr_sum(vicecap[p,w] for p in players) == 1 for w in gameweeks), 
            name='vicecap_count')
        # players in starting lineup must also be in squad
        model.add_constraints(
            (lineup[p,w] <= squad[p,w] for p in players for w in gameweeks), 
            name='lineup_squad_rel')
        # captain must come from within squad
        model.add_constraints(
            (captain[p,w] <= lineup[p,w] for p in players for w in gameweeks), 
            name='captain_lineup_rel')
        # vice-captain must come from within squad
        model.add_constraints(
            (vicecap[p,w] <= lineup[p,w] for p in players for w in gameweeks), 
            name='vicecap_lineup_rel')
        # captain and vice-captain can't be same person
        model.add_constraints(
            (captain[p,w] + vicecap[p,w] <= 1 for p in players for w in gameweeks), 
            name='cap_vc_rel')
        # count of each player per position in starting lineup
        lineup_type_count = {
            (t,w): so.expr_sum(lineup[p,w] for p in players
                               if self.forecasts.loc[p, 'position_id'] == t)
            for t in positions for w in gameweeks}
        # count of all players in lineup must be at least 'squad_min_play'
        # and no more than 'squad_max_play' for each position type
        model.add_constraints(
            (
                lineup_type_count[t,w] == [
                    self.positions.loc[t, 'squad_min_play'],
                    self.positions.loc[t, 'squad_max_play']] 
                for t in positions for w in gameweeks),
            name='valid_formation')
        # count of each player per position in squad
        squad_type_count = {
            (t,w): so.expr_sum(squad[p,w] for p in players
                               if self.forecasts.loc[p, 'position_id'] == t)
            for t in positions for w in gameweeks}
        # count of all players in squad must be equal to 'squad_select'
        # for each position type
        model.add_constraints(
            (
                squad_type_count[t,w] == self.positions.loc[t, 'squad_select']
                for t in positions for w in gameweeks),
            name='valid_squad')
        # no more than 3 players per team
        model.add_constraints(
            (
                so.expr_sum(squad[p,w] for p in players
                            if self.forecasts.loc[p, 'team_id'] == t)
                <= 3 for t in teams for w in gameweeks),
            name='team_limit')
        # Transfer constraints
        # squad is equal to squad from previous week, minus transfers out, plus in
        model.add_constraints(
            (
                squad[p,w] == squad[p,w-1] + transfer_in[p,w] - transfer_out[p,w]
                for p in players for w in gameweeks),
            name='squad_transfer_rel')
        # handles running bank balance (assumes no changes in player values)
        model.add_constraints(
            (
                in_the_bank[w] == in_the_bank[w-1] + sold_amount[w] - bought_amount[w]
                for w in gameweeks),
            name='cont_budget')
        # Free transfer constraints
        # 1 free transfer per week
        model.add_constraints(
            (free_transfers[w] == aux[w] + 1 for w in gameweeks),
            name='aux_ft_rel')
        # no more than 2 free transfers per week
        model.add_constraints(
            (
                free_transfers[w-1] - number_of_transfers[w-1] <= 2 * aux[w]
                for w in gameweeks),
            name='force_aux_1')
        # cannot make more than 14 penalized transfers in a week
        model.add_constraints(
            (
                free_transfers[w-1] - number_of_transfers[w-1] >= aux[w]
                + (-14)*(1-aux[w]) 
                for w in gameweeks),
            name='force_aux_2')
        # not sure what this does ??
        model.add_constraints(
            (penalized_transfers[w] >= transfer_diff[w] for w in gameweeks),
            name='pen_transfer_rel')

        # Objectives
        # sum of starting 11 players, plus double captain score 
        # and upweight vice-captain
        gw_xp = {
            w: so.expr_sum(points_player_week[p,w]
                        * (lineup[p,w] + captain[p,w]
                        + 0.1*vicecap[p,w]) for p in players)
            for w in gameweeks}
        # subtract transfer costs
        gw_total = {w: gw_xp[w] - 4 * penalized_transfers[w] for w in gameweeks}
        total_xp = so.expr_sum(
            gw_total[w] * pow(decay_base, w-self.gw) for w in gameweeks)
        model.set_objective(-total_xp, sense='N', name='total_xp')

        # Solve
        model.export_mps(f'{model_name}.mps')
        command = f'cbc {model_name}.mps solve solu {model_name}.txt'
        process = Popen(command, stdout=DEVNULL, shell=False) # DEVNULL: nologs
        process.wait()

        # Parsing
        with open(f'{model_name}.txt', 'r') as f:
            for line in f:
                if 'objective value' in line:
                    continue
                words = line.split()
                var = model.get_variable(words[1])
                var.set_value(float(words[2]))

        # DataFrame generation
        picks = []
        for w in gameweeks:
            for p in players:
                if squad[p,w].get_value() + transfer_out[p,w].get_value() > .5:
                    lp = self.forecasts.loc[p]
                    is_captain = 1 if captain[p,w].get_value() > .5 else 0
                    is_lineup = 1 if lineup[p,w].get_value() > .5 else 0
                    is_vice = 1 if vicecap[p,w].get_value() > .5 else 0
                    is_transfer_in = 1 if transfer_in[p,w].get_value() > .5 else 0
                    is_transfer_out = 1 if transfer_out[p,w].get_value() > .5 else 0
                    position = self.positions.loc[lp['position_id'], 'position_name']
                    team = self.teams.loc[lp['team_id'], 'team_name']
                    picks.append([
                        w, lp['web_name'], lp['position_id'], position, team, 
                        buy_price[p], sell_price[p], round(points_player_week[p,w],2),
                        is_lineup, is_captain, is_vice, is_transfer_in, is_transfer_out
                    ])

        picks_df = pd.DataFrame(
            picks,
            columns=['GW', 'Name', 'Pos_id', 'Pos', 'Team', 'BV', 'SV', 'xP',
                    'lineup', 'captain', 'vicecaptain', 'transfer_in', 'transfer_out']
        ).sort_values(
            by=['GW', 'lineup', 'Pos_id', 'xP'],
            ascending=[True, False, True, True])
        total_xp = so.expr_sum(
            (lineup[p,w] + captain[p,w]) * points_player_week[p,w]
            for p in players for w in gameweeks
        ).get_value()


        print('SUMMARY OF ACTIONS', '-----------', sep='\n')
        for w in gameweeks:
            print(f'GW {w}:')
            print(f'ITB {in_the_bank[w].get_value()}:',
                  f'FT={free_transfers[w].get_value()}',
                  f'PT={penalized_transfers[w].get_value()}')
            for p in players:
                if transfer_in[p,w].get_value() > .5:
                    print(f'   Buy {p} - {self.forecasts["web_name"][p]}')
                if transfer_out[p,w].get_value() > .5:
                    print(f'   Sell {p} - {self.forecasts["web_name"][p]}')
        print(f'\nTotal expected value: {total_xp:.2f} ({total_xp/horizon:.2f} / week)')

        os.remove(f'{model_name}.mps')
        os.remove(f'{model_name}.txt')

        return picks_df
Example #12
0
def test(cas_conn):

    # Problem data
    OILS = ['veg1', 'veg2', 'oil1', 'oil2', 'oil3']
    PERIODS = range(1, 7)
    cost_data = [[110, 120, 130, 110, 115], [130, 130, 110, 90, 115],
                 [110, 140, 130, 100, 95], [120, 110, 120, 120, 125],
                 [100, 120, 150, 110, 105], [90, 100, 140, 80, 135]]
    cost = pd.DataFrame(cost_data, columns=OILS, index=PERIODS).transpose()
    hardness_data = [8.8, 6.1, 2.0, 4.2, 5.0]
    hardness = {OILS[i]: hardness_data[i] for i in range(len(OILS))}

    revenue_per_ton = 150
    veg_ub = 200
    nonveg_ub = 250
    store_ub = 1000
    storage_cost_per_ton = 5
    hardness_lb = 3
    hardness_ub = 6
    init_storage = 500
    max_num_oils_used = 3
    min_oil_used_threshold = 20

    # Problem initialization
    m = so.Model(name='food_manufacture_2', session=cas_conn)

    # Problem definition
    buy = m.add_variables(OILS, PERIODS, lb=0, name='buy')
    use = m.add_variables(OILS, PERIODS, lb=0, name='use')
    manufacture = m.add_implicit_variable((use.sum('*', p) for p in PERIODS),
                                          name='manufacture')
    last_period = len(PERIODS)
    store = m.add_variables(OILS, [0] + list(PERIODS),
                            lb=0,
                            ub=store_ub,
                            name='store')
    for oil in OILS:
        store[oil, 0].set_bounds(lb=init_storage, ub=init_storage)
        store[oil, last_period].set_bounds(lb=init_storage, ub=init_storage)
    VEG = [i for i in OILS if 'veg' in i]
    NONVEG = [i for i in OILS if i not in VEG]
    revenue = so.quick_sum(revenue_per_ton * manufacture[p] for p in PERIODS)
    rawcost = so.quick_sum(cost.at[o, p] * buy[o, p] for o in OILS
                           for p in PERIODS)
    storagecost = so.quick_sum(storage_cost_per_ton * store[o, p] for o in OILS
                               for p in PERIODS)
    m.set_objective(revenue - rawcost - storagecost,
                    sense=so.MAX,
                    name='profit')

    # Constraints
    m.add_constraints((use.sum(VEG, p) <= veg_ub for p in PERIODS),
                      name='veg_ub')
    m.add_constraints((use.sum(NONVEG, p) <= nonveg_ub for p in PERIODS),
                      name='nonveg_ub')
    m.add_constraints((store[o, p - 1] + buy[o, p] == use[o, p] + store[o, p]
                       for o in OILS for p in PERIODS),
                      name='flow_balance')
    m.add_constraints(
        (so.quick_sum(hardness[o] * use[o, p]
                      for o in OILS) >= hardness_lb * manufacture[p]
         for p in PERIODS),
        name='hardness_ub')
    m.add_constraints(
        (so.quick_sum(hardness[o] * use[o, p]
                      for o in OILS) <= hardness_ub * manufacture[p]
         for p in PERIODS),
        name='hardness_lb')

    # Additions to the first problem
    isUsed = m.add_variables(OILS, PERIODS, vartype=so.BIN, name='is_used')
    for p in PERIODS:
        for o in VEG:
            use[o, p].set_bounds(ub=veg_ub)
        for o in NONVEG:
            use[o, p].set_bounds(ub=nonveg_ub)
    m.add_constraints((use[o, p] <= use[o, p]._ub * isUsed[o, p] for o in OILS
                       for p in PERIODS),
                      name='link')
    m.add_constraints(
        (isUsed.sum('*', p) <= max_num_oils_used for p in PERIODS),
        name='logical1')
    m.add_constraints((use[o, p] >= min_oil_used_threshold * isUsed[o, p]
                       for o in OILS for p in PERIODS),
                      name='logical2')
    m.add_constraints((isUsed[o, p] <= isUsed['oil3', p]
                       for o in ['veg1', 'veg2'] for p in PERIODS),
                      name='logical3')

    res = m.solve()
    if res is not None:
        print(so.get_solution_table(buy, use, store, isUsed))

    return m.get_objective_value()
def solve_problem(objective, options=None):
    if options is None:
        options = dict()

    car_type = options.get('car_type', 'diesel')
    day_limit = options.get('day_limit', None)
    emission_limit = options.get('emission_limit', None)
    cost_limit = options.get('cost_limit', None)

    print('Connecting to CAS')
    hostname = os.getenv('CASHOST')
    port = os.getenv('CASPORT')
    cas = CAS(hostname, port)
    m = so.Model(name='european_trip_2020', session=cas)

    print('Parsing data')
    game_data, arcs_data, travel_data = prep_data(car_type)
    source = 0
    sink = 999

    GAMES = list(range(1, 52))
    RAW_ARCS = [(i.id_x, i.id_y) for i in arcs_data.itertuples()]
    ARCS = [(source, g) for g in GAMES] + [(g, sink) for g in GAMES] + RAW_ARCS
    CITIES = list(game_data['city'].unique())
    CONNECTION = [(city1, city2, method)
                  for city1 in CITIES for city2 in CITIES
                  if city1 != city2
                  for method in travel_data.loc[(city1, city2)].index]

    game_city = game_data['city'].to_dict()
    method_dict = travel_data.reset_index().groupby(['city_x', 'city_y']).apply(
        lambda i: i.method.to_list()).to_dict()

    use_arc = m.add_variables(ARCS, name='use_arc', vartype=so.BIN)
    use_method = m.add_variables(CONNECTION, name='use_method', vartype=so.BIN)

    total_emission = so.quick_sum(travel_data.loc[i].emission * use_method[i]
                                  for i in CONNECTION)
    total_trip_time = \
        so.quick_sum(
            use_arc[g, sink] * game_data.loc[g]['ts'] for g in GAMES) - \
        so.quick_sum(
            use_arc[source, g] * game_data.loc[g]['ts'] for g in GAMES)
    total_duration = so.quick_sum(
        travel_data.loc[i].duration.total_seconds() * use_method[i]
        for i in CONNECTION)
    total_km = so.quick_sum(
        travel_data.loc[i].car * use_method[i] for i in CONNECTION)

    total_expense = so.quick_sum(
        travel_data.loc[i].cost * use_method[i] for i in CONNECTION)

    if objective == 'least_emission':
        m.set_objective(total_emission, name='total_emission', sense=so.MIN)
    elif objective == 'shortest_tour':
        # Total trip time
        m.set_objective(total_trip_time, name='total_trip_time', sense=so.MIN)
    elif objective == 'total_duration':
        m.set_objective(total_duration, name='total_duration', sense=so.MIN)
    elif objective == 'total_km':
        m.set_objective(total_km, name='total_km', sense=so.MIN)
    elif objective == 'least_expensive':
        m.set_objective(total_expense, name='total_expense', sense=so.MIN)

    # Balance constraints
    m.add_constraints(
        (so.quick_sum(use_arc[g1, g] for (g1, g2) in ARCS if g2 == g) -
         so.quick_sum(use_arc[g, g2] for (g1, g2) in ARCS if g1 == g) ==
         (-1 if g == source else (1 if g == sink else 0))
         for g in GAMES + [source, sink]), name='balance'
    )

    # Visit each city once
    m.add_constraints(
        (so.quick_sum(use_arc[g1, g2] for (g1, g2) in ARCS
                      if g1 != source and game_data.loc[g1]['city'] == CITIES[i])
         == 1 for i in range(12)), name='visit')

    # Must use a travel method
    m.add_constraints(
        (so.quick_sum(
             use_method[game_data.loc[i].city, game_data.loc[j].city, method]
             for method in travel_data.loc[(game_data.loc[i].city, game_data.loc[j].city)].index.to_list()
         ) == 1 * use_arc[i, j] for (i, j) in RAW_ARCS
         if game_data.loc[i].city != game_data.loc[j].city), name='travel_method'
    )

    # Catch game before it starts
    m.add_constraints((
        so.quick_sum(
            use_method[game_city[i], game_city[j], k] * travel_data.loc[game_city[i], game_city[j], k].duration.total_seconds() for k in method_dict[game_city[i], game_city[j]]
        ) + datetime.timedelta(hours=4).total_seconds() * use_arc[i, j]
        <= (game_data.loc[j, 'dt'] - game_data.loc[i, 'dt']).total_seconds()
        for (i, j) in RAW_ARCS), name='catch_game'
    )

    # Day limit
    if day_limit:
        second_limit = datetime.timedelta(days=day_limit).total_seconds()
        m.add_constraint(total_trip_time <= second_limit, name='time_limit')

    if emission_limit:
        m.add_constraint(total_emission <= emission_limit, name='emission_limit')

    if cost_limit:
        m.add_constraint(total_expense <= cost_limit, name='cost_limit')

    print('Submitting')
    m.solve()

    steps = []
    schedule = []
    for (g1, g2) in ARCS:
        if use_arc[g1, g2].get_value() > 0.5 and g1 != source and g2 != sink:
            if g1 not in schedule:
                schedule.append(g1)
            if g2 not in schedule:
                schedule.append(g2)
            steps.append((g1, g2))

    methods = travel_data[travel_data.apply(
        lambda i: use_method[i.name].get_value() > 0.5, axis=1)].copy()
    methods = methods.reset_index('method')

    # Sort the schedule and print information
    schedule = sorted(schedule, key=lambda i: game_data.loc[i, 'ts'])
    if objective == 'least_emission':
        objective += car_type
    result = print_final_schedule(
        objective, schedule, methods, game_data, arcs_data, options)
    return result
Example #14
0
def solve_custom_problem(input_folder, output_folder, options):
    """Solves a custom optimization problem"""

    problem_name = options['name']

    df = pd.read_csv(input_folder / 'element_gameweek.csv')
    target_week = options.get('target_week', df['event'].min())
    target_week_df = df[df['event'] == target_week].copy()

    df = pd.read_csv(input_folder / 'element.csv')
    target_week_df = pd.merge(left=target_week_df, right=df, how='inner', left_on=['player_id'], right_on=['id'], suffixes=('', '_extra'))
    players = target_week_df['player_id'].unique().tolist()
    target_week_df.set_index('player_id', inplace=True, drop=True)
    target_week_df = target_week_df.groupby(target_week_df.index).first()

    team_codes = target_week_df['team_code'].unique().tolist()
    
    types_df = pd.read_csv(input_folder / 'element_type.csv')
    types = types_df['id'].to_list()
    types_df.set_index('id', inplace=True, drop=True)

    m = so.Model(name=problem_name, session=None)
    x = m.add_variables(players, name='lineup', vartype=so.BIN)
    y = m.add_variables(players, name='captain', vartype=so.BIN)
    z = m.add_variables(players, name='squad', vartype=so.BIN)

    m.add_constraint(so.quick_sum(x[i] for i in players) == 11, name='lineup_limit')
    m.add_constraints((
        so.quick_sum(x[i] for i in players if target_week_df.loc[i]['element_type'] == et) >= types_df.loc[et]['squad_min_play']
        for et in types), name='squad_min')
    m.add_constraints((
        so.quick_sum(x[i] for i in players if target_week_df.loc[i]['element_type'] == et) <= types_df.loc[et]['squad_max_play']
        for et in types), name='squad_max')
    m.add_constraint(so.quick_sum(y[i] for i in players) == 1, name='single_captain')
    m.add_constraints((y[i] <= x[i] for i in players), name='captain_should_play')

    # Limit constraints
    m.add_constraints((
        so.quick_sum(z[i] for i in players if target_week_df.loc[i]['element_type'] == et) == types_df.loc[et]['squad_select']
        for et in types), name='squad_exact')
    m.add_constraint(so.quick_sum(z[i] for i in players) == 15, name='squad_limit')
    m.add_constraints(
        (so.quick_sum(z[i] for i in players if target_week_df.loc[i]['team_code'] == j) <= 3 for j in team_codes),
        name='player_team_limit')
    m.add_constraint(
        so.quick_sum(z[i] * target_week_df.loc[i]['now_cost'] for i in players) <= options['budget']*10,
        name='total_cost_100')
    m.add_constraints(
        (x[i] <= z[i] for i in players), name='lineup_squad_con')

    obj_type = options['objective']

    if obj_type == 'lineup':
        m.set_objective(so.quick_sum(
            -target_week_df.loc[i]['points_md'] * (x[i]+y[i]) for i in players
        ), sense='N', name='maximize_lineup_points')
    elif obj_type == 'bb':
        m.set_objective(so.quick_sum(
            -target_week_df.loc[i]['points_md'] * (z[i]+y[i]) for i in players
        ), sense='N', name='maximize_bb_points')

    mps_str = get_mps_string(m)

    with open(output_folder / f"{problem_name}.mps", "w") as f:
        f.write(mps_str)
    
    filename = str(output_folder / f"{problem_name}.mps")
    solutionname = str(output_folder / f"{problem_name}.sol")
    csvname = str(output_folder / f"{problem_name}.csv")
    xlsname = str(output_folder / f"{problem_name}.xlsx")
    
    solve_and_get_solution(filename, solutionname, m)
    
    # Parse solution
    selected_players = []
    for i in players:
        if z[i].get_value() > 0.5 and x[i].get_value() < 0.5:
            selected_players.append([i, 0, False, 0])
        elif x[i].get_value() > 0.5 and y[i].get_value() < 0.5:
            selected_players.append([i, 1, False, 1])
        elif x[i].get_value() > 0.5 and y[i].get_value() > 0.5:
            selected_players.append([i, 2, True, 1])
    results = pd.DataFrame(selected_players, columns=['player_id', 'multiplier', 'is_captain', 'starting_lineup'])
    result_df = pd.merge(target_week_df, results, on='player_id', how='inner')
    result_df = result_df[['player_id', 'web_name', 'team_code', 'element_type', 'now_cost', 'event', 'points_md', 'is_captain', 'multiplier', 'starting_lineup', 'selected_by_percent']].copy()
    result_df['gw_points'] = result_df['multiplier'] * result_df['points_md']
    result_df = result_df.sort_values(by=['starting_lineup', 'element_type', 'player_id'], ascending=[False, True, True], ignore_index=True)
    result_df.reset_index(drop=True, inplace=True)
    result_df.to_csv(csvname, encoding='utf-8')
    result_df.to_excel(xlsname, encoding='utf-8')
    with pd.option_context('display.max_rows', None, 'display.max_columns', None, 'display.encoding', 'UTF-8'): 
        print(result_df)
    print(m.get_objective_value())
Example #15
0
def test(cas_conn):

    # Problem data
    OILS = ['veg1', 'veg2', 'oil1', 'oil2', 'oil3']
    PERIODS = range(1, 7)
    cost_data = [[110, 120, 130, 110, 115], [130, 130, 110, 90, 115],
                 [110, 140, 130, 100, 95], [120, 110, 120, 120, 125],
                 [100, 120, 150, 110, 105], [90, 100, 140, 80, 135]]
    cost = pd.DataFrame(cost_data, columns=OILS, index=PERIODS).transpose()
    hardness_data = [8.8, 6.1, 2.0, 4.2, 5.0]
    hardness = {OILS[i]: hardness_data[i] for i in range(len(OILS))}

    revenue_per_ton = 150
    veg_ub = 200
    nonveg_ub = 250
    store_ub = 1000
    storage_cost_per_ton = 5
    hardness_lb = 3
    hardness_ub = 6
    init_storage = 500

    # Problem initialization
    m = so.Model(name='food_manufacture_1', session=cas_conn)

    # Problem definition
    buy = m.add_variables(OILS, PERIODS, lb=0, name='buy')
    use = m.add_variables(OILS, PERIODS, lb=0, name='use')
    manufacture = m.add_implicit_variable((use.sum('*', p) for p in PERIODS),
                                          name='manufacture')
    last_period = len(PERIODS)
    store = m.add_variables(OILS, [0] + list(PERIODS),
                            lb=0,
                            ub=store_ub,
                            name='store')
    for oil in OILS:
        store[oil, 0].set_bounds(lb=init_storage, ub=init_storage)
        store[oil, last_period].set_bounds(lb=init_storage, ub=init_storage)
    VEG = [i for i in OILS if 'veg' in i]
    NONVEG = [i for i in OILS if i not in VEG]
    revenue = so.quick_sum(revenue_per_ton * manufacture[p] for p in PERIODS)
    rawcost = so.quick_sum(cost.at[o, p] * buy[o, p] for o in OILS
                           for p in PERIODS)
    storagecost = so.quick_sum(storage_cost_per_ton * store[o, p] for o in OILS
                               for p in PERIODS)
    m.set_objective(revenue - rawcost - storagecost,
                    sense=so.MAX,
                    name='profit')

    # Constraints
    m.add_constraints((use.sum(VEG, p) <= veg_ub for p in PERIODS),
                      name='veg_ub')
    m.add_constraints((use.sum(NONVEG, p) <= nonveg_ub for p in PERIODS),
                      name='nonveg_ub')
    m.add_constraints((store[o, p - 1] + buy[o, p] == use[o, p] + store[o, p]
                       for o in OILS for p in PERIODS),
                      name='flow_balance')
    m.add_constraints(
        (so.quick_sum(hardness[o] * use[o, p]
                      for o in OILS) >= hardness_lb * manufacture[p]
         for p in PERIODS),
        name='hardness_ub')
    m.add_constraints(
        (so.quick_sum(hardness[o] * use[o, p]
                      for o in OILS) <= hardness_ub * manufacture[p]
         for p in PERIODS),
        name='hardness_lb')

    # Solver call
    res = m.solve()

    # With other solve options
    m.solve(options={'with': 'lp', 'algorithm': 'PS'})
    m.solve(options={'with': 'lp', 'algorithm': 'PS'})
    m.solve(options={'with': 'lp', 'algorithm': 'PS'})

    if res is not None:
        print(so.get_solution_table(buy, use, store))

    return m.get_objective_value()
Example #16
0
def test(cas_conn):

    m = so.Model(name='factory_planning_2', session=cas_conn)

    # Input data
    product_list = ['prod{}'.format(i) for i in range(1, 8)]
    product_data = pd.DataFrame([10, 6, 8, 4, 11, 9, 3],
                                columns=['profit'], index=product_list)
    demand_data = [
        [500, 1000, 300, 300,  800, 200, 100],
        [600,  500, 200,   0,  400, 300, 150],
        [300,  600,   0,   0,  500, 400, 100],
        [200,  300, 400, 500,  200,   0, 100],
        [0,    100, 500, 100, 1000, 300,   0],
        [500,  500, 100, 300, 1100, 500,  60]]
    demand_data = pd.DataFrame(
        demand_data, columns=product_list, index=range(1, 7))
    machine_type_product_data = [
        ['grinder', 0.5,  0.7,  0,    0,    0.3,  0.2, 0.5],
        ['vdrill',  0.1,  0.2,  0,    0.3,  0,    0.6, 0],
        ['hdrill',  0.2,  0,    0.8,  0,    0,    0,   0.6],
        ['borer',   0.05, 0.03, 0,    0.07, 0.1,  0,   0.08],
        ['planer',  0,    0,    0.01, 0,    0.05, 0,   0.05]]
    machine_type_product_data = \
        pd.DataFrame(machine_type_product_data, columns=['machine_type'] +
                     product_list).set_index(['machine_type'])
    machine_types_data = [
        ['grinder', 4, 2],
        ['vdrill',  2, 2],
        ['hdrill',  3, 3],
        ['borer',   1, 1],
        ['planer',  1, 1]]
    machine_types_data = pd.DataFrame(machine_types_data, columns=[
        'machine_type', 'num_machines', 'num_machines_needing_maintenance'])\
        .set_index(['machine_type'])

    store_ub = 100
    storage_cost_per_unit = 0.5
    final_storage = 50
    num_hours_per_period = 24 * 2 * 8

    # Problem definition
    PRODUCTS = product_list
    profit = product_data['profit']
    PERIODS = range(1, 7)
    MACHINE_TYPES = machine_types_data.index.tolist()

    num_machines = machine_types_data['num_machines']

    make = m.add_variables(PRODUCTS, PERIODS, lb=0, name='make')
    sell = m.add_variables(PRODUCTS, PERIODS, lb=0, ub=demand_data.transpose(),
                           name='sell')

    store = m.add_variables(PRODUCTS, PERIODS, lb=0, ub=store_ub, name='store')
    for p in PRODUCTS:
        store[p, 6].set_bounds(lb=final_storage, ub=final_storage)

    storageCost = so.quick_sum(
        storage_cost_per_unit * store[p, t] for p in PRODUCTS for t in PERIODS)
    revenue = so.quick_sum(profit[p] * sell[p, t]
                           for p in PRODUCTS for t in PERIODS)
    m.set_objective(revenue-storageCost, sense=so.MAX, name='total_profit')

    num_machines_needing_maintenance = \
        machine_types_data['num_machines_needing_maintenance']
    numMachinesDown = m.add_variables(MACHINE_TYPES, PERIODS, vartype=so.INT,
                                      lb=0, name='numMachinesDown')

    production_time = machine_type_product_data
    m.add_constraints((
        so.quick_sum(production_time.at[mc, p] * make[p, t] for p in PRODUCTS)
        <= num_hours_per_period *
        (num_machines[mc] - numMachinesDown[mc, t])
        for mc in MACHINE_TYPES for t in PERIODS), name='machine_hours_con')

    m.add_constraints((so.quick_sum(numMachinesDown[mc, t] for t in PERIODS) ==
                       num_machines_needing_maintenance[mc]
                       for mc in MACHINE_TYPES), name='maintenance_con')

    m.add_constraints(((store[p, t-1] if t-1 in PERIODS else 0) + make[p, t] ==
                      sell[p, t] + store[p, t]
                      for p in PRODUCTS for t in PERIODS),
                      name='flow_balance_con')

    res = m.solve()
    if res is not None:
        print(so.get_solution_table(make, sell, store))
        print(so.get_solution_table(numMachinesDown).unstack(level=-1))

    print(m.get_solution_summary())
    print(m.get_problem_summary())

    return m.get_objective_value()
Example #17
0
def test(cas_conn):

    m = so.Model(name='mining_optimization', session=cas_conn)

    mine_data = pd.DataFrame([
        ['mine1', 5, 2, 1.0],
        ['mine2', 4, 2.5, 0.7],
        ['mine3', 4, 1.3, 1.5],
        ['mine4', 5, 3, 0.5],
        ], columns=['mine', 'cost', 'extract_ub', 'quality']).\
        set_index(['mine'])

    year_data = pd.DataFrame([
        [1, 0.9],
        [2, 0.8],
        [3, 1.2],
        [4, 0.6],
        [5, 1.0],
    ],
                             columns=['year',
                                      'quality_required']).set_index(['year'])

    max_num_worked_per_year = 3
    revenue_per_ton = 10
    discount_rate = 0.10

    MINES = mine_data.index.tolist()
    cost = mine_data['cost']
    extract_ub = mine_data['extract_ub']
    quality = mine_data['quality']
    YEARS = year_data.index.tolist()
    quality_required = year_data['quality_required']

    isOpen = m.add_variables(MINES, YEARS, vartype=so.BIN, name='isOpen')
    isWorked = m.add_variables(MINES, YEARS, vartype=so.BIN, name='isWorked')
    extract = m.add_variables(MINES, YEARS, lb=0, name='extract')
    [extract[i, j].set_bounds(ub=extract_ub[i]) for i in MINES for j in YEARS]

    extractedPerYear = {j: extract.sum('*', j) for j in YEARS}
    discount = {j: 1 / (1 + discount_rate)**(j - 1) for j in YEARS}

    totalRevenue = revenue_per_ton *\
        so.quick_sum(discount[j] * extractedPerYear[j] for j in YEARS)
    totalCost = so.quick_sum(discount[j] * cost[i] * isOpen[i, j]
                             for i in MINES for j in YEARS)
    m.set_objective(totalRevenue - totalCost, sense=so.MAX, name='totalProfit')

    m.add_constraints((extract[i, j] <= extract[i, j]._ub * isWorked[i, j]
                       for i in MINES for j in YEARS),
                      name='link')

    m.add_constraints(
        (isWorked.sum('*', j) <= max_num_worked_per_year for j in YEARS),
        name='cardinality')

    m.add_constraints(
        (isWorked[i, j] <= isOpen[i, j] for i in MINES for j in YEARS),
        name='worked_implies_open')

    m.add_constraints((isOpen[i, j] <= isOpen[i, j - 1] for i in MINES
                       for j in YEARS if j != 1),
                      name='continuity')

    m.add_constraints((so.quick_sum(quality[i] * extract[i, j] for i in MINES)
                       == quality_required[j] * extractedPerYear[j]
                       for j in YEARS),
                      name='quality_con')

    res = m.solve()
    if res is not None:
        print(so.get_solution_table(isOpen, isWorked, extract))
        quality_sol = {
            j: so.quick_sum(quality[i] * extract[i, j].get_value()
                            for i in MINES) / extractedPerYear[j].get_value()
            for j in YEARS
        }
        qs = so.dict_to_frame(quality_sol, ['quality_sol'])
        epy = so.dict_to_frame(extractedPerYear, ['extracted_per_year'])
        print(so.get_solution_table(epy, qs, quality_required))

    return m.get_objective_value()
Example #18
0
def test(cas_conn):
    # Data generation
    n = 100
    p = 0.02

    random.seed(1)

    ARCS = {}
    for i in range(0, n):
        for j in range(0, n):
            if random.random() < p:
                ARCS[i, j] = random.random()

    max_length = 10

    # Model
    model = so.Model("kidney_exchange", session=cas_conn)

    # Sets
    NODES = set().union(*ARCS.keys())
    MATCHINGS = range(1, int(len(NODES) / 2) + 1)

    # Variables
    UseNode = model.add_variables(NODES,
                                  MATCHINGS,
                                  vartype=so.BIN,
                                  name="usenode")
    UseArc = model.add_variables(ARCS,
                                 MATCHINGS,
                                 vartype=so.BIN,
                                 name="usearc")
    Slack = model.add_variables(NODES, vartype=so.BIN, name="slack")

    print('Setting objective...')

    # Objective
    model.set_objective(so.quick_sum(
        (ARCS[i, j] * UseArc[i, j, m] for [i, j] in ARCS for m in MATCHINGS)),
                        name="total_weight",
                        sense=so.MAX)

    print('Adding constraints...')
    # Constraints
    Node_Packing = model.add_constraints(
        (UseNode.sum(i, '*') + Slack[i] == 1 for i in NODES),
        name="node_packing")
    Donate = model.add_constraints((UseArc.sum(i, '*', m) == UseNode[i, m]
                                    for i in NODES for m in MATCHINGS),
                                   name="donate")
    Receive = model.add_constraints((UseArc.sum('*', j, m) == UseNode[j, m]
                                     for j in NODES for m in MATCHINGS),
                                    name="receive")
    Cardinality = model.add_constraints(
        (UseArc.sum('*', '*', m) <= max_length for m in MATCHINGS),
        name="cardinality")

    # Solve
    model.solve(options={'with': 'milp', 'maxtime': 300})

    # Define decomposition blocks
    for i in NODES:
        for m in MATCHINGS:
            Donate[i, m].set_block(m - 1)
            Receive[i, m].set_block(m - 1)
    for m in MATCHINGS:
        Cardinality[m].set_block(m - 1)

    model.solve(verbose=True,
                options={
                    'with': 'milp',
                    'maxtime': 300,
                    'presolver': 'basic',
                    'decomp': {
                        'method': 'user'
                    }
                })

    return model.get_objective_value()
def solve_optimal_transfer_problem(team_name, options=None):

    print('\n *** Solving problem for ', team_name, '\n')

    if options is None:
        options = {'age_limit': 33}

    team_info = get_team_info(team_name)

    if os.path.isfile('playerdb.pickle'):
        player_db = pd.read_pickle('playerdb.pickle')
    else:
        player_db = get_player_db()

    player_db['player_id'] = player_db.index
    player_db = player_db.set_index(['link'])
    # Remove duplicate rows
    player_db = player_db[~player_db.index.duplicated(keep='last')]

    if options and 'budget_limit' in options:
        budget = options['budget_limit']
    else:
        budget = team_info['budget']

    eligible = []
    for i, pos in enumerate(team_info['positions']):
        print('Filtering eligible candidates for', pos)
        if pos in ('RCB', 'LCB'):
            filter_pos = 'CB'
        elif pos in ('RCM', 'LCM'):
            filter_pos = 'CM'
        elif pos in ('LS', 'RS'):
            filter_pos = 'ST'
        elif pos in ('LDM', 'RDM'):
            filter_pos = 'DM'
        else:
            filter_pos = pos
        # Can play for position
        filtered = player_db[player_db['pos'].str.contains(filter_pos)]
        # Has no missing value
        filtered = filtered[filtered['value'] > 0]
        # Under budget
        filtered = filtered[filtered['value'] < budget]
        # Better than current squad member
        filtered = filtered[filtered['overall'] > team_info['ratings'][i]]
        if options and 'age_limit' in options:
            # Age limit
            filtered = filtered[filtered['age'] <= options['age_limit']]

        for id in filtered['player_id'].tolist():
            eligible.append([id, i, pos])

    eligible = pd.DataFrame(eligible,
                            columns=['player_id', 'position', 'pos_str'])
    print('Total number of eligible transfers:', len(eligible))

    squad = []
    for i in range(11):
        try:
            current_member = player_db.loc[team_info['players'][i]]
        except:
            # If user is not in db, fetch player page
            print('Found a player who is not in DB!')
            current_member = get_player_info(team_info['players'][i])
            current_member['player_id'] = len(player_db['name'])
            player_db.loc[team_info['players'][i]] = [
                current_member['name'],
                None,  # img
                None,  # age
                [],  # pos
                0,  # value
                current_member['overall'],
                current_member['potential'],  # potential
                player_db['player_id'].max() + 1
            ]

        print('Current squad member for ', team_info['positions'][i], ':',
              current_member['name'], ', rating', current_member['overall'])
        squad.append([
            i, current_member['player_id'], team_info['positions'][i],
            current_member['overall']
        ])

    squad = pd.DataFrame(
        squad, columns=['position', 'player_id', 'pos_str', 'overall'])
    player_db = player_db[player_db['player_id'].isin(
        eligible['player_id'].tolist() + squad['player_id'].tolist())]

    # Modeling
    so.reset_globals()
    m = so.Model(name='optimal_squad_1', session=session)

    PLAYERS, (name, age, value, overall, potential) = m.read_table(
        player_db,
        key=['player_id'],
        columns=['name', 'age', 'value', 'overall', 'potential'],
        col_types={'name': 'str'},
        upload=False,
        casout='player_list')

    ELIG, _ = m.read_table(eligible,
                           key=['player_id', 'position'],
                           upload=False,
                           casout='eligible_list')

    POSITIONS, (member, pos_str, c_overall) = m.read_table(
        squad,
        key=['position'],
        columns=['player_id', 'pos_str', 'overall'],
        col_types={'pos_str': 'str'},
        upload=False,
        casout='squad_list')

    rating = m.add_variables(POSITIONS, name='rating')
    transfer = m.add_variables(ELIG, name='transfer', vartype=so.BIN)

    m.set_objective(so.quick_sum(rating[j] for j in POSITIONS),
                    name='total_rating',
                    sense=so.MAX)

    m.add_constraint(so.quick_sum(transfer[i, j] * value[i]
                                  for (i, j) in ELIG) <= budget,
                     name='budget_con')

    m.add_constraints(
        (rating[j] == overall[member[j]] +
         so.quick_sum(transfer[i, j] * (overall[i] - overall[member[j]])
                      for (i, j2) in ELIG if j == j2) for j in POSITIONS),
        name='transfer_con')

    # Potential rating alternative

    # m.add_constraints(
    #     (rating[j] == potential[member[j]] + so.quick_sum(
    #          transfer[i, j] * (potential[i] - potential[member[j]]) for (i, j2) in ELIG if j==j2) for j in POSITIONS), name='potential_con')

    m.add_constraints((so.quick_sum(transfer[i, j]
                                    for (i2, j) in ELIG if i == i2) <= 1
                       for i in PLAYERS),
                      name='only_one_position')

    m.add_constraints((so.quick_sum(transfer[i, j]
                                    for (i, j2) in ELIG if j == j2) <= 1
                       for j in POSITIONS),
                      name='only_one_transfer')

    for j in POSITIONS:
        print(j, pos_str[j], len([1 for (i, j2) in ELIG if j == j2]))

    if team_name is None:
        m.add_constraints((so.quick_sum(transfer[i, j]
                                        for (i, j2) in ELIG if j == j2) == 1
                           for j in POSITIONS),
                          name='transfer_one')

    m.solve()

    old_rating = sum(c_overall)
    print('Original squad rating:', old_rating)
    new_rating = rating.sum('*').get_value()
    print('New squad rating:', new_rating)
    new_players = []
    final_team = []
    for i, pos in enumerate(POSITIONS):
        for player in PLAYERS:
            if (player, pos) not in ELIG:
                continue
            if transfer[player, pos].get_value() > 0.5:
                print(
                    '{}: {} ({}, pot:{}), previous: {} ({}), paid: {}'.format(
                        pos_str[pos], name[player], overall[player],
                        potential[player], name[member[pos]], c_overall[pos],
                        value[player]))
                new_players.append(name[player])
                final_team.append([
                    pos_str[pos], name[member[pos]], c_overall[pos],
                    potential[member[pos]], name[player], overall[player],
                    potential[player], value[player]
                ])
                break
        else:
            print('{}: {} ({})'.format(pos_str[pos], name[member[pos]],
                                       c_overall[pos]))
            final_team.append([
                pos_str[pos], name[member[pos]], c_overall[pos],
                potential[member[pos]], name[member[pos]], c_overall[pos],
                potential[member[pos]], 0
            ])

    final_team = pd.DataFrame(final_team,
                              columns=[
                                  'Pos', 'Old', 'Old.R', 'Old.Pot', 'New',
                                  'New.R', 'New.Pot', 'Paid'
                              ])
    final_team = final_team.append(final_team.sum(numeric_only=True),
                                   ignore_index=True)

    money_spent = so.quick_sum(value[i] * transfer[i, j]
                               for (i, j) in ELIG).get_value()
    efficiency = (new_rating - old_rating) / (money_spent /
                                              1e6) if money_spent > 0 else 0
    print('Rating increase per million euro:', efficiency)
    transfer_list = ', '.join(new_players)
    return (team_name, options['age_limit'], old_rating,
            round(old_rating / 11.0,
                  3), round(new_rating), round(new_rating / 11.0, 3), budget,
            money_spent, efficiency, transfer_list, final_team)
Example #20
0
def test(cas_conn, **kwargs):

    m = so.Model(name='refinery_optimization', session=cas_conn)

    crude_data = pd.DataFrame([
        ['crude1', 20000],
        ['crude2', 30000]
        ], columns=['crude', 'crude_ub']).set_index(['crude'])

    arc_data = pd.DataFrame([
        ['source', 'crude1', 6],
        ['source', 'crude2', 6],
        ['crude1', 'light_naphtha', 0.1],
        ['crude1', 'medium_naphtha', 0.2],
        ['crude1', 'heavy_naphtha', 0.2],
        ['crude1', 'light_oil', 0.12],
        ['crude1', 'heavy_oil', 0.2],
        ['crude1', 'residuum', 0.13],
        ['crude2', 'light_naphtha', 0.15],
        ['crude2', 'medium_naphtha', 0.25],
        ['crude2', 'heavy_naphtha', 0.18],
        ['crude2', 'light_oil', 0.08],
        ['crude2', 'heavy_oil', 0.19],
        ['crude2', 'residuum', 0.12],
        ['light_naphtha', 'regular_petrol', np.nan],
        ['light_naphtha', 'premium_petrol', np.nan],
        ['medium_naphtha', 'regular_petrol', np.nan],
        ['medium_naphtha', 'premium_petrol', np.nan],
        ['heavy_naphtha', 'regular_petrol', np.nan],
        ['heavy_naphtha', 'premium_petrol', np.nan],
        ['light_naphtha', 'reformed_gasoline', 0.6],
        ['medium_naphtha', 'reformed_gasoline', 0.52],
        ['heavy_naphtha', 'reformed_gasoline', 0.45],
        ['light_oil', 'jet_fuel', np.nan],
        ['light_oil', 'fuel_oil', np.nan],
        ['heavy_oil', 'jet_fuel', np.nan],
        ['heavy_oil', 'fuel_oil', np.nan],
        ['light_oil', 'light_oil_cracked', 2],
        ['light_oil_cracked', 'cracked_oil', 0.68],
        ['light_oil_cracked', 'cracked_gasoline', 0.28],
        ['heavy_oil', 'heavy_oil_cracked', 2],
        ['heavy_oil_cracked', 'cracked_oil', 0.75],
        ['heavy_oil_cracked', 'cracked_gasoline', 0.2],
        ['cracked_oil', 'jet_fuel', np.nan],
        ['cracked_oil', 'fuel_oil', np.nan],
        ['reformed_gasoline', 'regular_petrol', np.nan],
        ['reformed_gasoline', 'premium_petrol', np.nan],
        ['cracked_gasoline', 'regular_petrol', np.nan],
        ['cracked_gasoline', 'premium_petrol', np.nan],
        ['residuum', 'lube_oil', 0.5],
        ['residuum', 'jet_fuel', np.nan],
        ['residuum', 'fuel_oil', np.nan],
        ], columns=['i', 'j', 'multiplier']).set_index(['i', 'j'])

    octane_data = pd.DataFrame([
        ['light_naphtha', 90],
        ['medium_naphtha', 80],
        ['heavy_naphtha', 70],
        ['reformed_gasoline', 115],
        ['cracked_gasoline', 105],
        ], columns=['i', 'octane']).set_index(['i'])

    petrol_data = pd.DataFrame([
        ['regular_petrol', 84],
        ['premium_petrol', 94],
        ], columns=['petrol', 'octane_lb']).set_index(['petrol'])

    vapour_pressure_data = pd.DataFrame([
        ['light_oil', 1.0],
        ['heavy_oil', 0.6],
        ['cracked_oil', 1.5],
        ['residuum', 0.05],
        ], columns=['oil', 'vapour_pressure']).set_index(['oil'])

    fuel_oil_ratio_data = pd.DataFrame([
        ['light_oil', 10],
        ['cracked_oil', 4],
        ['heavy_oil', 3],
        ['residuum', 1],
        ], columns=['oil', 'coefficient']).set_index(['oil'])

    final_product_data = pd.DataFrame([
        ['premium_petrol', 700],
        ['regular_petrol', 600],
        ['jet_fuel', 400],
        ['fuel_oil', 350],
        ['lube_oil', 150],
        ], columns=['product', 'profit']).set_index(['product'])

    vapour_pressure_ub = 1
    crude_total_ub = 45000
    naphtha_ub = 10000
    cracked_oil_ub = 8000
    lube_oil_lb = 500
    lube_oil_ub = 1000
    premium_ratio = 0.40

    ARCS = arc_data.index.tolist()
    arc_mult = arc_data['multiplier'].fillna(1)

    FINAL_PRODUCTS = final_product_data.index.tolist()
    final_product_data['profit'] = final_product_data['profit'] / 100
    profit = final_product_data['profit']

    ARCS = ARCS + [(i, 'sink') for i in FINAL_PRODUCTS]
    flow = m.add_variables(ARCS, name='flow', lb=0)
    NODES = np.unique([i for j in ARCS for i in j])

    m.set_objective(so.expr_sum(profit[i] * flow[i, 'sink']
                                 for i in FINAL_PRODUCTS
                                 if (i, 'sink') in ARCS),
                    name='totalProfit', sense=so.MAX)

    m.add_constraints((so.expr_sum(flow[a] for a in ARCS if a[0] == n) ==
                      so.expr_sum(arc_mult[a] * flow[a]
                                   for a in ARCS if a[1] == n)
                      for n in NODES if n not in ['source', 'sink']),
                      name='flow_balance')

    CRUDES = crude_data.index.tolist()
    crudeDistilled = m.add_variables(CRUDES, name='crudesDistilled', lb=0)
    crudeDistilled.set_bounds(ub=crude_data['crude_ub'])
    m.add_constraints((flow[i, j] == crudeDistilled[i]
                      for (i, j) in ARCS if i in CRUDES), name='distillation')

    OILS = ['light_oil', 'heavy_oil']
    CRACKED_OILS = [i+'_cracked' for i in OILS]
    oilCracked = m.add_variables(CRACKED_OILS, name='oilCracked', lb=0)
    m.add_constraints((flow[i, j] == oilCracked[i] for (i, j) in ARCS
                      if i in CRACKED_OILS), name='cracking')

    octane = octane_data['octane']
    PETROLS = petrol_data.index.tolist()
    octane_lb = petrol_data['octane_lb']
    vapour_pressure = vapour_pressure_data['vapour_pressure']

    m.add_constraints((so.expr_sum(octane[a[0]] * arc_mult[a] * flow[a]
                                    for a in ARCS if a[1] == p)
                       >= octane_lb[p] *
                      so.expr_sum(arc_mult[a] * flow[a]
                                   for a in ARCS if a[1] == p)
                      for p in PETROLS), name='blending_petrol')

    m.add_constraint(so.expr_sum(vapour_pressure[a[0]] * arc_mult[a] * flow[a]
                                  for a in ARCS if a[1] == 'jet_fuel') <=
                     vapour_pressure_ub *
                     so.expr_sum(arc_mult[a] * flow[a]
                                  for a in ARCS if a[1] == 'jet_fuel'),
                     name='blending_jet_fuel')

    fuel_oil_coefficient = fuel_oil_ratio_data['coefficient']
    sum_fuel_oil_coefficient = sum(fuel_oil_coefficient)
    m.add_constraints((sum_fuel_oil_coefficient * flow[a] ==
                      fuel_oil_coefficient[a[0]] * flow.sum('*', ['fuel_oil'])
                      for a in ARCS if a[1] == 'fuel_oil'),
                      name='blending_fuel_oil')

    m.add_constraint(crudeDistilled.sum('*') <= crude_total_ub,
                     name='crude_total_ub')

    m.add_constraint(so.expr_sum(flow[a] for a in ARCS
                                  if a[0].find('naphtha') > -1 and
                                  a[1] == 'reformed_gasoline')
                     <= naphtha_ub, name='naphtba_ub')

    m.add_constraint(so.expr_sum(flow[a] for a in ARCS if a[1] ==
                                  'cracked_oil') <=
                     cracked_oil_ub, name='cracked_oil_ub')

    m.add_constraint(flow['lube_oil', 'sink'] == [lube_oil_lb, lube_oil_ub],
                     name='lube_oil_range')

    m.add_constraint(flow.sum('premium_petrol', '*') >= premium_ratio *
                     flow.sum('regular_petrol', '*'), name='premium_ratio')

    res = m.solve(**kwargs)
    if res is not None:
        print(so.get_solution_table(crudeDistilled))
        print(so.get_solution_table(oilCracked))
        print(so.get_solution_table(flow))

        octane_sol = []
        for p in PETROLS:
            octane_sol.append(so.expr_sum(octane[a[0]] * arc_mult[a] *
                                           flow[a].get_value() for a in ARCS
                                           if a[1] == p) /
                              sum(arc_mult[a] * flow[a].get_value()
                                  for a in ARCS if a[1] == p))
        octane_sol = pd.Series(octane_sol, name='octane_sol', index=PETROLS)
        print(so.get_solution_table(octane_sol, octane_lb))
        print(so.get_solution_table(vapour_pressure))
        vapour_pressure_sol = sum(vapour_pressure[a[0]] *
                                  arc_mult[a] *
                                  flow[a].get_value() for a in ARCS
                                  if a[1] == 'jet_fuel') /\
            sum(arc_mult[a] * flow[a].get_value() for a in ARCS
                if a[1] == 'jet_fuel')
        print('Vapour_pressure_sol: {:.4f}'.format(vapour_pressure_sol))

        num_fuel_oil_ratio_sol = [arc_mult[a] * flow[a].get_value() /
                                  sum(arc_mult[b] *
                                      flow[b].get_value()
                                      for b in ARCS if b[1] == 'fuel_oil')
                                  for a in ARCS if a[1] == 'fuel_oil']
        num_fuel_oil_ratio_sol = pd.Series(num_fuel_oil_ratio_sol,
                                           name='num_fuel_oil_ratio_sol',
                                           index=[a[0] for a in ARCS
                                                  if a[1] == 'fuel_oil'])
        print(so.get_solution_table(fuel_oil_coefficient,
                                    num_fuel_oil_ratio_sol))

    return m.get_objective_value()
Example #21
0
def solve_gain_loss_problem_1GW(gw, weights, options=None):

    if options is None:
        options = dict()

    data = get_multistage_data(gw, n=1)
    base_folder = pathlib.Path().resolve()
    output_folder = pathlib.Path(base_folder / f"build/work/")
    output_folder.mkdir(parents=True, exist_ok=True)
    
    gw = data['gw']
    players = data['elements']
    types = data['types']
    types_df = data['types_df']
    next_week_df = data['element_gameweek_df']
    team_codes = data['team_codes']
    element_df = data['element_df']
    
    m = so.Model(name='gainloss', session=None)
    x = m.add_variables(players, name='lineup', vartype=so.BIN)
    y = m.add_variables(players, name='captain', vartype=so.BIN)
    z = m.add_variables(players, name='squad', vartype=so.BIN)

    m.add_constraint(so.quick_sum(x[i] for i in players) == 11, name='lineup_limit')
    m.add_constraints((
        so.quick_sum(x[i] for i in players if next_week_df.loc[i, gw]['element_type'] == et) >= types_df.loc[et]['squad_min_play']
        for et in types), name='squad_min')
    m.add_constraints((
        so.quick_sum(x[i] for i in players if next_week_df.loc[i, gw]['element_type'] == et) <= types_df.loc[et]['squad_max_play']
        for et in types), name='squad_max')
    m.add_constraint(so.quick_sum(y[i] for i in players) == 1, name='single_captain')
    m.add_constraints((y[i] <= x[i] for i in players), name='captain_should_play')

    # Limit constraints
    m.add_constraints((
        so.quick_sum(z[i] for i in players if next_week_df.loc[i, gw]['element_type'] == et) == types_df.loc[et]['squad_select']
        for et in types), name='squad_exact')
    m.add_constraint(so.quick_sum(z[i] for i in players) == 15, name='squad_limit')
    m.add_constraints(
        (so.quick_sum(z[i] for i in players if next_week_df.loc[i, gw]['team_code'] == j) <= 3 for j in team_codes),
        name='player_team_limit')
    m.add_constraint(
        so.quick_sum(z[i] * next_week_df.loc[i, gw]['now_cost'] for i in players) <= 1000,
        name='total_cost_100')
    m.add_constraints(
        (x[i] <= z[i] for i in players), name='lineup_squad_con')

    gain_weight = weights['gain']
    loss_weight = weights['loss']

    total_gain = so.quick_sum(next_week_df.loc[e, gw]['points_md']*(1-element_df.loc[e]['selected_by_percent']/100.0)*x[e] for e in players)
    total_loss = so.quick_sum(next_week_df.loc[e, gw]['points_md']*(element_df.loc[e]['selected_by_percent']/100.0)*(1-x[e]) for e in players)
    weighted_net = gain_weight * total_gain - loss_weight * total_loss
    overall_net = total_gain - total_loss

    if options.get('overall_net_lb') is not None:
        m.add_constraint(overall_net >= options['overall_net_lb'], name='overall_net_lb_con')

    m.set_objective(
        # so.quick_sum(-next_week_df.loc[i, gw]['points_md'] * (x[i]+y[i]) for i in players)
        - weighted_net, sense='N', name='maximize_points')

    mps_str = get_mps_string(m)

    problem_name = f"GW{gw}_gain_loss_{gain_weight:.3f}_{loss_weight:.3f}"

    if options.get('name') is not None:
        problem_name += '_' + options['name']

    with open(output_folder / f"{problem_name}.mps", "w") as f:
        f.write(mps_str)
    
    filename = str(output_folder / f"{problem_name}.mps")
    solutionname = str(output_folder / f"{problem_name}.sol")
    csvname = str(output_folder / f"{problem_name}.csv")
    
    solve_and_get_solution(filename, solutionname, m)

    selected_players = []
    for i in players:
        if z[i].get_value() > 0.5 and x[i].get_value() < 0.5:
            selected_players.append([i, gw, 0, False, 0])
        elif x[i].get_value() > 0.5 and y[i].get_value() < 0.5:
            selected_players.append([i, gw, 1, False, 1])
        elif x[i].get_value() > 0.5 and y[i].get_value() > 0.5:
            selected_players.append([i, gw, 2, True, 1])
    results = pd.DataFrame(selected_players, columns=['player_id', 'event', 'multiplier', 'is_captain', 'starting_lineup'])
    result_df = pd.merge(next_week_df, results, on=['player_id', 'event'], how='inner')
    result_df = result_df[['player_id', 'web_name', 'team_code', 'element_type', 'now_cost', 'event', 'points_md', 'is_captain', 'multiplier', 'starting_lineup', 'selected_by_percent']].copy()
    result_df['gw_points'] = result_df['multiplier'] * result_df['points_md']
    result_df = result_df.sort_values(by=['starting_lineup', 'element_type', 'player_id'], ascending=[False, True, True], ignore_index=True)
    result_df.reset_index(drop=True, inplace=True)
    result_df.to_csv(csvname, encoding='utf-8')
    lineup_players = result_df['web_name'].tolist()[0:11]
    with pd.option_context('display.max_rows', None, 'display.max_columns', None, 'display.encoding', 'UTF-8'): 
        print(result_df)
    print(weights, )
    print(m.get_objective_value())
    
    return {"gain": total_gain.get_value(), "loss": total_loss.get_value(), "obj": weighted_net.get_value(), "net": overall_net.get_value(), "lineup": ', '.join(lineup_players)}
Example #22
0
def solve_multiperiod(input_folder, output_folder, options):
    """Solves for best squads iteratively for generating an interactive list"""
    df = pd.read_csv(input_folder / 'element_gameweek.csv')
    if options.get('target_weeks') is None:
        next_week = df['event'].min()
        element_gameweek_df = df[df['event'] < next_week+3].copy()
    else:
        element_gameweek_df = df[df['event'].isin(options.get('target_weeks'))].copy()

    df = pd.read_csv(input_folder / 'element.csv')
    element_df = df.copy().set_index('id')
    element_gameweek_df = pd.merge(left=element_gameweek_df, right=df, how='inner', left_on=['player_id'], right_on=['id'], suffixes=('', '_extra'))
    element_gameweek_df['rawxp'] = element_gameweek_df.apply(lambda r: r['points_md'] * 90 / max(1, r['xmins_md']), axis=1)
    elements = element_gameweek_df['player_id'].unique().tolist()
    gameweeks = element_gameweek_df['event'].unique().tolist()
    total_weeks = len(gameweeks)
    copy_gw_df = element_gameweek_df.copy()
    copy_gw_df = copy_gw_df.groupby(['player_id', 'event']).first().reset_index()
    element_gameweek_df.set_index(['player_id', 'event'], inplace=True, drop=True)
    element_gameweek_df = element_gameweek_df.groupby(element_gameweek_df.index).first()
    

    team_codes = element_gameweek_df['team_code'].unique().tolist()
    
    types_df = pd.read_csv(input_folder / 'element_type.csv')
    types = types_df['id'].to_list()
    types_df.set_index('id', inplace=True, drop=True)

    position = [1,2,3,4]
    element_gameweek = [(e, g) for e in elements for g in gameweeks]

    m = so.Model(name='iterative_model', session=None)
    lineup = m.add_variables(elements, gameweeks, name='lineup', vartype=so.BIN)
    captain = m.add_variables(elements, gameweeks, name='captain', vartype=so.BIN)
    squad = m.add_variables(elements, name='squad', vartype=so.BIN)
    bench = m.add_variables(elements, gameweeks, position, name='bench', vartype=so.BIN)

    m.add_constraints(
        (so.quick_sum(lineup[e, g] for e in elements) == 11 for g in gameweeks),
        name='lineup_limit_per_week')

    m.add_constraints((
        so.quick_sum(lineup[e, g] for e in elements if element_df.loc[e]['element_type'] == et) >= types_df.loc[et]['squad_min_play']
        for et in types for g in gameweeks), name='squad_min')

    m.add_constraints((
        so.quick_sum(lineup[e, g] for e in elements if element_df.loc[e]['element_type'] == et) <= types_df.loc[et]['squad_max_play']
        for et in types for g in gameweeks), name='squad_max')

    m.add_constraints((so.quick_sum(captain[e, g] for e in elements) == 1 for g in gameweeks), name='single_captain')
    m.add_constraints((captain[e, g] <= lineup[e, g] for e in elements for g in gameweeks), name='captain_should_play')

    # Limit constraints
    m.add_constraints((
        so.quick_sum(squad[e] for e in elements if element_df.loc[e]['element_type'] == et) == types_df.loc[et]['squad_select']
        for et in types), name='squad_exact')
    m.add_constraint(so.quick_sum(squad[e] for e in elements) == 15, name='squad_limit')
    m.add_constraints(
        (so.quick_sum(squad[e] for e in elements if element_df.loc[e]['team_code'] == j) <= 3 for j in team_codes),
        name='player_team_limit')
    m.add_constraints(
        (lineup[e, g] <= squad[e] for e in elements for g in gameweeks), name='lineup_squad_con')
    m.add_constraints(
        (bench[e, g, p] <= squad[e] for e in elements for g in gameweeks for p in position), name='bench_squad_con')
    m.add_constraints(
        (so.quick_sum(bench[e, g, p] for e in elements) == 1 for g in gameweeks for p in position), name='bench_position_con')
    m.add_constraints(
        (so.quick_sum(bench[e, g, 1] for e in elements if element_df.loc[e]['element_type'] == 1) == 1 for g in gameweeks), name='only_gk_bench1')
    m.add_constraints(
        (lineup[e, g] + so.quick_sum(bench[e, g, p] for p in position) == squad[e] for e in elements for g in gameweeks), name='lineup_plus_bench_equal_squad')

    # Budget Constraint
    budget_con = m.add_constraint(so.quick_sum(squad[e] * element_df.loc[e]['now_cost'] for e in elements) <= options.get('budget', 100) * 10, name='budget_con')

    total_points = so.quick_sum(element_gameweek_df.loc[[(e,g)]].iloc[0]['points_md'] * (lineup[e, g]+ captain[e,g]) for e in elements for g in gameweeks)
    
    m.set_objective(-total_points,
        sense='N', name='maximize_points')

    name = options['name']

    filename = str(output_folder / f"{name}.mps")
    solutionname = str(output_folder / f"{name}.sol")
    csvname = str(output_folder / f"{name}.csv")
    xlsname = str(output_folder / f"{name}.xlsx")
    
    mps_str = get_mps_string(m)
    with open(output_folder / f"{name}.mps", "w") as f:
        f.write(mps_str)

    for v in m.get_variables():
        v.set_value(0)

    solve_and_get_solution(filename, solutionname, m)

    # Parse solution
    selected_players = []
    solution = {"squad":[], "lineup": {g:[] for g in gameweeks}, "bench": {g:{} for g in gameweeks}, "captain": {g: 0 for g in gameweeks}, "ownership": {}}
    for e in elements:
        if squad[e].get_value() > 0.5:
            solution['squad'].append(e)
    for g in gameweeks:
        for e in elements:
            if squad[e].get_value() > 0.5:
                if captain[e, g].get_value() > 0.5:
                    solution['captain'][g] = e
                    selected_players.append([g, e, 2, True, 1])
                elif lineup[e, g].get_value() > 0.5:
                    solution['lineup'][g].append(e)
                    selected_players.append([g, e, 1, False, 1])
                elif squad[e].get_value() > 0.5:
                    for p in position:
                        if bench[e, g, p].get_value() > 0.5:
                            solution['bench'][g][p] = e
                            selected_players.append([g, e, 0, False, 0])

    print(solution)
    results = pd.DataFrame(selected_players, columns=['event', 'player_id', 'multiplier', 'is_captain', 'starting_lineup'])
    result_df = pd.merge(copy_gw_df, results, on=['player_id', 'event'], how='inner')
    result_df = result_df[['event', 'player_id', 'web_name', 'team_code', 'element_type', 'now_cost', 'points_md', 'is_captain', 'multiplier', 'starting_lineup', 'selected_by_percent']].copy()
    result_df = result_df.sort_values(by=['event', 'starting_lineup', 'element_type', 'player_id'], ascending=[True, False, True, True], ignore_index=True)
    result_df.reset_index(drop=True, inplace=True)
    result_df.to_csv(csvname, encoding='utf-8')
    result_df.to_excel(xlsname, encoding='utf-8')
    with pd.option_context('display.max_rows', None, 'display.max_columns', None, 'display.encoding', 'UTF-8'): 
        print(result_df)
    print(m.get_objective_value())
Example #23
0
def test(cas_conn):

    m = so.Model(name='factory_planning_1', session=cas_conn)

    # Input data
    product_list = ['prod{}'.format(i) for i in range(1, 8)]
    product_data = pd.DataFrame([10, 6, 8, 4, 11, 9, 3],
                                columns=['profit'], index=product_list)
    demand_data = [
        [500, 1000, 300, 300,  800, 200, 100],
        [600,  500, 200,   0,  400, 300, 150],
        [300,  600,   0,   0,  500, 400, 100],
        [200,  300, 400, 500,  200,   0, 100],
        [0,    100, 500, 100, 1000, 300,   0],
        [500,  500, 100, 300, 1100, 500,  60]]
    demand_data = pd.DataFrame(
        demand_data, columns=product_list, index=range(1, 7))
    machine_types_data = [
        ['grinder', 4],
        ['vdrill', 2],
        ['hdrill', 3],
        ['borer', 1],
        ['planer', 1]]
    machine_types_data = pd.DataFrame(machine_types_data, columns=[
        'machine_type', 'num_machines']).set_index(['machine_type'])
    machine_type_period_data = [
        ['grinder', 1, 1],
        ['hdrill',  2, 2],
        ['borer',   3, 1],
        ['vdrill',  4, 1],
        ['grinder', 5, 1],
        ['vdrill',  5, 1],
        ['planer',  6, 1],
        ['hdrill',  6, 1]]
    machine_type_period_data = pd.DataFrame(machine_type_period_data, columns=[
        'machine_type', 'period', 'num_down'])
    machine_type_product_data = [
        ['grinder', 0.5,  0.7,  0,    0,    0.3,  0.2, 0.5],
        ['vdrill',  0.1,  0.2,  0,    0.3,  0,    0.6, 0],
        ['hdrill',  0.2,  0,    0.8,  0,    0,    0,   0.6],
        ['borer',   0.05, 0.03, 0,    0.07, 0.1,  0,   0.08],
        ['planer',  0,    0,    0.01, 0,    0.05, 0,   0.05]]
    machine_type_product_data = \
        pd.DataFrame(machine_type_product_data, columns=['machine_type'] +
                     product_list).set_index(['machine_type'])
    store_ub = 100
    storage_cost_per_unit = 0.5
    final_storage = 50
    num_hours_per_period = 24 * 2 * 8

    # Problem definition
    PRODUCTS = product_list
    PERIODS = range(1, 7)
    MACHINE_TYPES = machine_types_data.index.values

    num_machine_per_period = pd.DataFrame()
    for i in range(1, 7):
        num_machine_per_period[i] = machine_types_data['num_machines']
    for _, row in machine_type_period_data.iterrows():
        num_machine_per_period.at[row['machine_type'],
                                  row['period']] -= row['num_down']

    make = m.add_variables(PRODUCTS, PERIODS, lb=0, name='make')
    sell = m.add_variables(PRODUCTS, PERIODS, lb=0, ub=demand_data.transpose(),
                           name='sell')

    store = m.add_variables(PRODUCTS, PERIODS, lb=0, ub=store_ub, name='store')
    for p in PRODUCTS:
        store[p, 6].set_bounds(lb=final_storage, ub=final_storage+1)

    storageCost = storage_cost_per_unit * store.sum('*', '*')
    revenue = so.expr_sum(product_data.at[p, 'profit'] * sell[p, t]
                           for p in PRODUCTS for t in PERIODS)
    m.set_objective(revenue-storageCost, sense=so.MAX, name='total_profit')

    production_time = machine_type_product_data
    m.add_constraints((
        so.expr_sum(production_time.at[mc, p] * make[p, t] for p in PRODUCTS)
        <= num_hours_per_period * num_machine_per_period.at[mc, t]
        for mc in MACHINE_TYPES for t in PERIODS), name='machine_hours')
    m.add_constraints(((store[p, t-1] if t-1 in PERIODS else 0) + make[p, t] ==
                      sell[p, t] + store[p, t] for p in PRODUCTS
                      for t in PERIODS),
                      name='flow_balance')

    res = m.solve()
    if res is not None:
        print(so.get_solution_table(make, sell, store))

    return m.get_objective_value()
Example #24
0
def test(cas_conn):

    m = so.Model(name='decentralization', session=cas_conn)

    DEPTS = ['A', 'B', 'C', 'D', 'E']
    CITIES = ['Bristol', 'Brighton', 'London']

    benefit_data = pd.DataFrame(
        [['Bristol', 10, 15, 10, 20, 5], ['Brighton', 10, 20, 15, 15, 15]],
        columns=['city'] + DEPTS).set_index('city')

    comm_data = pd.DataFrame(
        [['A', 'B', 0.0], ['A', 'C', 1.0], ['A', 'D', 1.5], ['A', 'E', 0.0],
         ['B', 'C', 1.4], ['B', 'D', 1.2], ['B', 'E', 0.0], ['C', 'D', 0.0],
         ['C', 'E', 2.0], ['D', 'E', 0.7]],
        columns=['i', 'j', 'comm']).set_index(['i', 'j'])

    cost_data = pd.DataFrame(
        [['Bristol', 'Bristol', 5], ['Bristol', 'Brighton', 14],
         ['Bristol', 'London', 13], ['Brighton', 'Brighton', 5],
         ['Brighton', 'London', 9], ['London', 'London', 10]],
        columns=['i', 'j', 'cost']).set_index(['i', 'j'])

    max_num_depts = 3

    benefit = {}
    for city in CITIES:
        for dept in DEPTS:
            try:
                benefit[dept, city] = benefit_data.loc[city, dept]
            except:
                benefit[dept, city] = 0

    comm = {}
    for row in comm_data.iterrows():
        (i, j) = row[0]
        comm[i, j] = row[1]['comm']
        comm[j, i] = comm[i, j]

    cost = {}
    for row in cost_data.iterrows():
        (i, j) = row[0]
        cost[i, j] = row[1]['cost']
        cost[j, i] = cost[i, j]

    assign = m.add_variables(DEPTS, CITIES, vartype=so.BIN, name='assign')
    IJKL = [(i, j, k, l) for i in DEPTS for j in CITIES for k in DEPTS
            for l in CITIES if i < k]
    product = m.add_variables(IJKL, vartype=so.BIN, name='product')

    totalBenefit = so.expr_sum(benefit[i, j] * assign[i, j] for i in DEPTS
                               for j in CITIES)

    totalCost = so.expr_sum(comm[i, k] * cost[j, l] * product[i, j, k, l]
                            for (i, j, k, l) in IJKL)

    m.set_objective(totalBenefit - totalCost, name='netBenefit', sense=so.MAX)

    m.add_constraints((so.expr_sum(assign[dept, city] for city in CITIES) == 1
                       for dept in DEPTS),
                      name='assign_dept')

    m.add_constraints((so.expr_sum(assign[dept, city]
                                   for dept in DEPTS) <= max_num_depts
                       for city in CITIES),
                      name='cardinality')

    product_def1 = m.add_constraints(
        (assign[i, j] + assign[k, l] - 1 <= product[i, j, k, l]
         for (i, j, k, l) in IJKL),
        name='pd1')

    product_def2 = m.add_constraints(
        (product[i, j, k, l] <= assign[i, j] for (i, j, k, l) in IJKL),
        name='pd2')

    product_def3 = m.add_constraints(
        (product[i, j, k, l] <= assign[k, l] for (i, j, k, l) in IJKL),
        name='pd3')

    m.solve()
    print(m.get_problem_summary())

    m.drop_constraints(product_def1)
    m.drop_constraints(product_def2)
    m.drop_constraints(product_def3)

    m.add_constraints(
        (so.expr_sum(product[i, j, k, l]
                     for j in CITIES if (i, j, k, l) in IJKL) == assign[k, l]
         for i in DEPTS for k in DEPTS for l in CITIES if i < k),
        name='pd4')

    m.add_constraints(
        (so.expr_sum(product[i, j, k, l]
                     for l in CITIES if (i, j, k, l) in IJKL) == assign[i, j]
         for k in DEPTS for i in DEPTS for j in CITIES if i < k),
        name='pd5')

    m.solve()
    print(m.get_problem_summary())
    totalBenefit.set_name('totalBenefit')
    totalCost.set_name('totalCost')
    print(so.get_solution_table(totalBenefit, totalCost))
    print(so.get_solution_table(assign).unstack(level=-1))

    return m.get_objective_value()
Example #25
0
def test(cas_conn):
    # Input data
    demand_data = pd.DataFrame([
        [0, 2000, 1500, 1000],
        [1, 1000, 1400, 1000],
        [2, 500, 2000, 1500],
        [3, 0, 2500, 2000]
        ], columns=['period', 'unskilled', 'semiskilled', 'skilled'])\
        .set_index(['period'])
    worker_data = pd.DataFrame(
        [['unskilled', 0.25, 0.10, 500, 200, 1500, 50, 500],
         ['semiskilled', 0.20, 0.05, 800, 500, 2000, 50, 400],
         ['skilled', 0.10, 0.05, 500, 500, 3000, 50, 400]],
        columns=[
            'worker', 'waste_new', 'waste_old', 'recruit_ub',
            'redundancy_cost', 'overmanning_cost', 'shorttime_ub',
            'shorttime_cost'
        ]).set_index(['worker'])
    retrain_data = pd.DataFrame([
        ['unskilled', 'semiskilled', 200, 400],
        ['semiskilled', 'skilled', math.inf, 500],
        ], columns=['worker1', 'worker2', 'retrain_ub', 'retrain_cost']).\
        set_index(['worker1', 'worker2'])
    downgrade_data = pd.DataFrame(
        [['semiskilled', 'unskilled'], ['skilled', 'semiskilled'],
         ['skilled', 'unskilled']],
        columns=['worker1', 'worker2'])

    semiskill_retrain_frac_ub = 0.25
    downgrade_leave_frac = 0.5
    overmanning_ub = 150
    shorttime_frac = 0.5

    # Sets
    WORKERS = worker_data.index.tolist()
    PERIODS0 = demand_data.index.tolist()
    PERIODS = PERIODS0[1:]
    RETRAIN_PAIRS = [i for i, _ in retrain_data.iterrows()]
    DOWNGRADE_PAIRS = [(row['worker1'], row['worker2'])
                       for _, row in downgrade_data.iterrows()]

    waste_old = worker_data['waste_old']
    waste_new = worker_data['waste_new']
    redundancy_cost = worker_data['redundancy_cost']
    overmanning_cost = worker_data['overmanning_cost']
    shorttime_cost = worker_data['shorttime_cost']
    retrain_cost = retrain_data['retrain_cost'].unstack(level=-1)

    # Initialization
    m = so.Model(name='manpower_planning', session=cas_conn)

    # Variables
    numWorkers = m.add_variables(WORKERS, PERIODS0, name='numWorkers', lb=0)
    demand0 = demand_data.loc[0]
    for w in WORKERS:
        numWorkers[w, 0].set_bounds(lb=demand0[w], ub=demand0[w])
    numRecruits = m.add_variables(WORKERS, PERIODS, name='numRecruits', lb=0)
    worker_ub = worker_data['recruit_ub']
    for w in WORKERS:
        for p in PERIODS:
            numRecruits[w, p].set_bounds(ub=worker_ub[w])
    numRedundant = m.add_variables(WORKERS, PERIODS, name='numRedundant', lb=0)
    numShortTime = m.add_variables(WORKERS, PERIODS, name='numShortTime', lb=0)
    shorttime_ub = worker_data['shorttime_ub']
    for w in WORKERS:
        for p in PERIODS:
            numShortTime.set_bounds(ub=shorttime_ub[w])
    numExcess = m.add_variables(WORKERS, PERIODS, name='numExcess', lb=0)

    retrain_ub = pd.DataFrame()
    for i in PERIODS:
        retrain_ub[i] = retrain_data['retrain_ub']
    numRetrain = m.add_variables(RETRAIN_PAIRS,
                                 PERIODS,
                                 name='numRetrain',
                                 lb=0,
                                 ub=retrain_ub)

    numDowngrade = m.add_variables(DOWNGRADE_PAIRS,
                                   PERIODS,
                                   name='numDowngrade',
                                   lb=0)
    # Constraints
    m.add_constraints(
        (numWorkers[w, p] - (1 - shorttime_frac) * numShortTime[w, p] -
         numExcess[w, p] == demand_data.loc[p, w] for w in WORKERS
         for p in PERIODS),
        name='demand')
    m.add_constraints(
        (numWorkers[w, p] == (1 - waste_old[w]) * numWorkers[w, p - 1] +
         (1 - waste_new[w]) * numRecruits[w, p] +
         (1 - waste_old[w]) * numRetrain.sum('*', w, p) +
         (1 - downgrade_leave_frac) * numDowngrade.sum('*', w, p) -
         numRetrain.sum(w, '*', p) - numDowngrade.sum(w, '*', p) -
         numRedundant[w, p] for w in WORKERS for p in PERIODS),
        name='flow_balance')
    m.add_constraints((numRetrain['semiskilled', 'skilled', p] <=
                       semiskill_retrain_frac_ub * numWorkers['skilled', p]
                       for p in PERIODS),
                      name='semiskill_retrain')
    m.add_constraints(
        (numExcess.sum('*', p) <= overmanning_ub for p in PERIODS),
        name='overmanning')
    # Objectives
    redundancy = so.Expression(numRedundant.sum('*', '*'), name='redundancy')
    cost = so.Expression(
        so.expr_sum(redundancy_cost[w] * numRedundant[w, p] +
                    shorttime_cost[w] * numShortTime[w, p] +
                    overmanning_cost[w] * numExcess[w, p] for w in WORKERS
                    for p in PERIODS) +
        so.expr_sum(retrain_cost.loc[i, j] * numRetrain[i, j, p]
                    for i, j in RETRAIN_PAIRS for p in PERIODS),
        name='cost')

    m.set_objective(redundancy, sense=so.MIN, name='redundancy_obj')
    res = m.solve()
    if res is not None:
        print('Redundancy:', redundancy.get_value())
        print('Cost:', cost.get_value())
        print(
            so.get_solution_table(numWorkers, numRecruits, numRedundant,
                                  numShortTime, numExcess))
        print(so.get_solution_table(numRetrain))
        print(so.get_solution_table(numDowngrade))

    m.set_objective(cost, sense=so.MIN, name='cost_obj')
    res = m.solve()
    if res is not None:
        print('Redundancy:', redundancy.get_value())
        print('Cost:', cost.get_value())
        print(
            so.get_solution_table(numWorkers, numRecruits, numRedundant,
                                  numShortTime, numExcess))
        print(so.get_solution_table(numRetrain))
        print(so.get_solution_table(numDowngrade))

    return m.get_objective_value()
    def solve_optimal_squad(self, budget=100):
        
        players = self.forecasts.index
        positions = self.positions.index
        teams = self.teams.index

        model_name = f'gw{self.gw}_{budget}budget'
        model = so.Model(model_name)

        # Variables
        squad   = model.add_variables(players,name='squad',
                                      vartype=so.binary)
        lineup  = model.add_variables(players, name='lineup', 
                                      vartype=so.binary)
        captain = model.add_variables(players, name='captain', 
                                      vartype=so.binary)
        vicecap = model.add_variables(players, name='vicecap', 
                                      vartype=so.binary)

        # Constraints
        # 15 players in squad
        squad_count = so.expr_sum(squad[p] for p in players)
        model.add_constraint(squad_count == 15, name='squad_count')
        # 11 players in starting lineup
        model.add_constraint(so.expr_sum(lineup[p] for p in players) == 11,
                             name='lineup_count')
        # 1 captain
        model.add_constraint(so.expr_sum(captain[p] for p in players) == 1,
                             name='captain_count')
        # 1 vice-captain
        model.add_constraint(so.expr_sum(vicecap[p] for p in players) == 1,
                             name='vicecap_count')
        # players in starting lineup must also be in squad
        model.add_constraints((lineup[p] <= squad[p] for p in players),
                              name='lineup_squad_rel')
        # captain must come from within squad
        model.add_constraints((captain[p] <= lineup[p] for p in players),
                              name='captain_lineup_rel')
        # vice-captain must come from within squad
        model.add_constraints((vicecap[p] <= lineup[p] for p in players),
                              name='vicecap_lineup_rel')
        # captain and vice-captain can't be same person
        model.add_constraints((captain[p] + vicecap[p] <= 1 for p in players),
                              name='cap_vc_rel')
        # count of each player per position in starting lineup
        lineup_type_count = {
            t: so.expr_sum(lineup[p] for p in players
                           if self.forecasts.loc[p, 'position_id'] == t)
            for t in positions}
        # count of all players in lineup must be at least 'squad_min_play'
        # and no more than 'squad_max_play' for each position type 
        model.add_constraints(
            (lineup_type_count[t] == [self.positions.loc[t, 'squad_min_play'],
                                      self.positions.loc[t, 'squad_max_play']]
             for t in positions),
            name='valid_formation')
        # count of each player per position in squad
        squad_type_count = {
            t: so.expr_sum(squad[p] for p in players
                           if self.forecasts.loc[p, 'position_id'] == t)
            for t in positions}
        # count of all players in squad must be equal to 'squad_select'
        # for each position type 
        model.add_constraints(
            (squad_type_count[t] == self.positions.loc[t, 'squad_select']
             for t in positions),
            name='valid_squad')
        # total value of squad cannot exceed budget
        price = so.expr_sum(
            self.forecasts.loc[p, 'bv'] * squad[p] for p in players)
        model.add_constraint(price <= budget, name='budget_limit')
        # no more than 3 players per team
        model.add_constraints(
            (
                so.expr_sum(squad[p] for p in players
                            if self.forecasts.loc[p, 'team_id'] == t)
                <= 3 for t in teams),
            name='team_limit'
        )
        # sum of starting 11 players, plus double captain score
        # and upweight vice-captain
        total_points = so.expr_sum(self.forecasts.loc[p, f'{self.gw}_pts']
                                   * (lineup[p] + captain[p] + 0.1 * vicecap[p])
                                   for p in players)

        # Objective
        model.set_objective(-total_points, sense='N', name='total_xp')
        model.export_mps(f'{model_name}.mps')
        command = f'cbc {model_name}.mps solve solu {model_name}.txt'

        Popen(command, shell=False, stdout=DEVNULL).wait()
        for v in model.get_variables():
            v.set_value(0)
        with open(f'{model_name}.txt', 'r') as f:
            for line in f:
                if 'objective value' in line:
                    continue
                words = line.split()
                var = model.get_variable(words[1])
                var.set_value(float(words[2]))

        picks = []
        for p in players:
            if squad[p].get_value() > .5:
                lp = self.forecasts.loc[p]
                is_captain = 1 if captain[p].get_value() > .5 else 0
                is_lineup = 1 if lineup[p].get_value() > .5 else 0
                is_vice = 1 if vicecap[p].get_value() > .5 else 0
                position = self.positions.loc[lp['position_id'], 'position_name']
                team = self.teams.loc[lp['team_id'], 'team_name']
                picks.append([lp['web_name'], lp['position_id'], position, team,
                              lp['bv'], round(lp[f'{self.gw}_pts'], 2),
                              is_lineup, is_captain, is_vice])

        picks_df = pd.DataFrame(
            picks,
            columns=['Name', 'Pos_id', 'Pos', 'Team', 'Price', 'xP', 'lineup',
                     'captain', 'vicecaptain']
        ).sort_values(by=['lineup', 'Pos_id', 'xP'], ascending=[False, True, True])
        
        total_xp = so.expr_sum((lineup[p] + captain[p])
                                * self.forecasts.loc[p, f'{self.gw}_pts']
                                for p in players).get_value()

        print(f'Total expected value for budget {budget:.1f}: {total_xp:.2f}')

        os.remove(f'{model_name}.mps')
        os.remove(f'{model_name}.txt')

        return picks_df
Example #27
0
def test(cas_conn):

    m = so.Model(name='farm_planning', session=cas_conn)

    # Input Data

    cow_data_raw = []
    for age in range(12):
        if age < 2:
            row = {
                'age': age,
                'init_num_cows': 10,
                'acres_needed': 2 / 3.0,
                'annual_loss': 0.05,
                'bullock_yield': 0,
                'heifer_yield': 0,
                'milk_revenue': 0,
                'grain_req': 0,
                'sugar_beet_req': 0,
                'labour_req': 10,
                'other_costs': 50
            }
        else:
            row = {
                'age': age,
                'init_num_cows': 10,
                'acres_needed': 1,
                'annual_loss': 0.02,
                'bullock_yield': 1.1 / 2,
                'heifer_yield': 1.1 / 2,
                'milk_revenue': 370,
                'grain_req': 0.6,
                'sugar_beet_req': 0.7,
                'labour_req': 42,
                'other_costs': 100
            }
        cow_data_raw.append(row)
    cow_data = pd.DataFrame(cow_data_raw).set_index(['age'])
    grain_data = pd.DataFrame([['group1', 20, 1.1], ['group2', 30, 0.9],
                               ['group3', 20, 0.8], ['group4', 10, 0.65]],
                              columns=['group', 'acres',
                                       'yield']).set_index(['group'])
    num_years = 5
    num_acres = 200
    bullock_revenue = 30
    heifer_revenue = 40
    dairy_cow_selling_age = 12
    dairy_cow_selling_revenue = 120
    max_num_cows = 130
    sugar_beet_yield = 1.5
    grain_cost = 90
    grain_revenue = 75
    grain_labour_req = 4
    grain_other_costs = 15
    sugar_beet_cost = 70
    sugar_beet_revenue = 58
    sugar_beet_labour_req = 14
    sugar_beet_other_costs = 10
    nominal_labour_cost = 4000
    nominal_labour_hours = 5500
    excess_labour_cost = 1.2
    capital_outlay_unit = 200
    num_loan_years = 10
    annual_interest_rate = 0.15
    max_decrease_ratio = 0.50
    max_increase_ratio = 0.75

    # Sets

    AGES = cow_data.index.tolist()
    init_num_cows = cow_data['init_num_cows']
    acres_needed = cow_data['acres_needed']
    annual_loss = cow_data['annual_loss']
    bullock_yield = cow_data['bullock_yield']
    heifer_yield = cow_data['heifer_yield']
    milk_revenue = cow_data['milk_revenue']
    grain_req = cow_data['grain_req']
    sugar_beet_req = cow_data['sugar_beet_req']
    cow_labour_req = cow_data['labour_req']
    cow_other_costs = cow_data['other_costs']

    YEARS = list(range(1, num_years + 1))
    YEARS0 = [0] + YEARS

    # Variables

    numCows = m.add_variables(AGES + [dairy_cow_selling_age],
                              YEARS0,
                              lb=0,
                              name='numCows')
    for age in AGES:
        numCows[age, 0].set_bounds(lb=init_num_cows[age],
                                   ub=init_num_cows[age])
    numCows[dairy_cow_selling_age, 0].set_bounds(lb=0, ub=0)

    numBullocksSold = m.add_variables(YEARS, lb=0, name='numBullocksSold')
    numHeifersSold = m.add_variables(YEARS, lb=0, name='numHeifersSold')

    GROUPS = grain_data.index.tolist()
    acres = grain_data['acres']
    grain_yield = grain_data['yield']
    grainAcres = m.add_variables(GROUPS, YEARS, lb=0, name='grainAcres')
    for group in GROUPS:
        for year in YEARS:
            grainAcres[group, year].set_bounds(ub=acres[group])
    grainBought = m.add_variables(YEARS, lb=0, name='grainBought')
    grainSold = m.add_variables(YEARS, lb=0, name='grainSold')

    sugarBeetAcres = m.add_variables(YEARS, lb=0, name='sugarBeetAcres')
    sugarBeetBought = m.add_variables(YEARS, lb=0, name='sugarBeetBought')
    sugarBeetSold = m.add_variables(YEARS, lb=0, name='sugarBeetSold')

    numExcessLabourHours = m.add_variables(YEARS,
                                           lb=0,
                                           name='numExcessLabourHours')
    capitalOutlay = m.add_variables(YEARS, lb=0, name='capitalOutlay')

    yearly_loan_payment = (annual_interest_rate * capital_outlay_unit) /\
                          (1 - (1+annual_interest_rate)**(-num_loan_years))

    # Objective function

    revenue = {
        year: bullock_revenue * numBullocksSold[year] +
        heifer_revenue * numHeifersSold[year] +
        dairy_cow_selling_revenue * numCows[dairy_cow_selling_age, year] +
        so.expr_sum(milk_revenue[age] * numCows[age, year]
                    for age in AGES) + grain_revenue * grainSold[year] +
        sugar_beet_revenue * sugarBeetSold[year]
        for year in YEARS
    }

    cost = {
        year: grain_cost * grainBought[year] +
        sugar_beet_cost * sugarBeetBought[year] + nominal_labour_cost +
        excess_labour_cost * numExcessLabourHours[year] +
        so.expr_sum(cow_other_costs[age] * numCows[age, year]
                    for age in AGES) +
        so.expr_sum(grain_other_costs * grainAcres[group, year]
                    for group in GROUPS) +
        sugar_beet_other_costs * sugarBeetAcres[year] +
        so.expr_sum(yearly_loan_payment * capitalOutlay[y]
                    for y in YEARS if y <= year)
        for year in YEARS
    }
    profit = {year: revenue[year] - cost[year] for year in YEARS}

    totalProfit = so.expr_sum(profit[year] - yearly_loan_payment *
                              (num_years - 1 + year) * capitalOutlay[year]
                              for year in YEARS)

    m.set_objective(totalProfit, sense=so.MAX, name='totalProfit')

    # Constraints

    m.add_constraints(
        (so.expr_sum(acres_needed[age] * numCows[age, year] for age in AGES) +
         so.expr_sum(grainAcres[group, year]
                     for group in GROUPS) + sugarBeetAcres[year] <= num_acres
         for year in YEARS),
        name='num_acres')

    m.add_constraints((numCows[age + 1, year + 1]
                       == (1 - annual_loss[age]) * numCows[age, year]
                       for age in AGES if age != dairy_cow_selling_age
                       for year in YEARS0 if year != num_years),
                      name='aging')

    m.add_constraints((numBullocksSold[year] == so.expr_sum(
        bullock_yield[age] * numCows[age, year] for age in AGES)
                       for year in YEARS),
                      name='numBullocksSold_def')

    m.add_constraints((numCows[0, year]
                       == so.expr_sum(heifer_yield[age] * numCows[age, year]
                                      for age in AGES) - numHeifersSold[year]
                       for year in YEARS),
                      name='numHeifersSold_def')

    m.add_constraints((so.expr_sum(numCows[age, year] for age in AGES) <=
                       max_num_cows + so.expr_sum(capitalOutlay[y]
                                                  for y in YEARS if y <= year)
                       for year in YEARS),
                      name='max_num_cows_def')

    grainGrown = {(group, year): grain_yield[group] * grainAcres[group, year]
                  for group in GROUPS for year in YEARS}
    m.add_constraints(
        (so.expr_sum(grain_req[age] * numCows[age, year] for age in AGES) <=
         so.expr_sum(grainGrown[group, year]
                     for group in GROUPS) + grainBought[year] - grainSold[year]
         for year in YEARS),
        name='grain_req_def')

    sugarBeetGrown = {(year): sugar_beet_yield * sugarBeetAcres[year]
                      for year in YEARS}
    m.add_constraints(
        (so.expr_sum(sugar_beet_req[age] * numCows[age, year] for age in AGES)
         <= sugarBeetGrown[year] + sugarBeetBought[year] - sugarBeetSold[year]
         for year in YEARS),
        name='sugar_beet_req_def')

    m.add_constraints((so.expr_sum(cow_labour_req[age] * numCows[age, year]
                                   for age in AGES) +
                       so.expr_sum(grain_labour_req * grainAcres[group, year]
                                   for group in GROUPS) +
                       sugar_beet_labour_req * sugarBeetAcres[year] <=
                       nominal_labour_hours + numExcessLabourHours[year]
                       for year in YEARS),
                      name='labour_req_def')
    m.add_constraints((profit[year] >= 0 for year in YEARS), name='cash_flow')

    m.add_constraint(so.expr_sum(numCows[age, num_years]
                                 for age in AGES if age >= 2) /
                     sum(init_num_cows[age] for age in AGES if age >= 2) == [
                         1 - max_decrease_ratio, 1 + max_increase_ratio
                     ],
                     name='final_dairy_cows_range')

    res = m.solve()

    if res is not None:
        so.pd.display_all()
        print(so.get_solution_table(numCows))
        revenue_df = so.dict_to_frame(revenue, cols=['revenue'])
        cost_df = so.dict_to_frame(cost, cols=['cost'])
        profit_df = so.dict_to_frame(profit, cols=['profit'])
        print(
            so.get_solution_table(numBullocksSold, numHeifersSold,
                                  capitalOutlay, numExcessLabourHours,
                                  revenue_df, cost_df, profit_df))
        gg_df = so.dict_to_frame(grainGrown, cols=['grainGrown'])
        print(so.get_solution_table(grainAcres, gg_df))
        sbg_df = so.dict_to_frame(sugarBeetGrown, cols=['sugerBeetGrown'])
        print(
            so.get_solution_table(grainBought, grainSold, sugarBeetAcres,
                                  sbg_df, sugarBeetBought, sugarBeetSold))
        num_acres = m.get_constraint('num_acres')
        na_df = num_acres.get_expressions()
        max_num_cows_con = m.get_constraint('max_num_cows_def')
        mnc_df = max_num_cows_con.get_expressions()
        print(so.get_solution_table(na_df, mnc_df))

    return m.get_objective_value()
Example #28
0
def test(cas_conn, sols=False):

    # Upload data to server first
    xy_raw = pd.DataFrame([
        [0.0, 1.0],
        [0.5, 0.9],
        [1.0, 0.7],
        [1.5, 1.5],
        [1.9, 2.0],
        [2.5, 2.4],
        [3.0, 3.2],
        [3.5, 2.0],
        [4.0, 2.7],
        [4.5, 3.5],
        [5.0, 1.0],
        [5.5, 4.0],
        [6.0, 3.6],
        [6.6, 2.7],
        [7.0, 5.7],
        [7.6, 4.6],
        [8.5, 6.0],
        [9.0, 6.8],
        [10.0, 7.3]
        ], columns=['x', 'y'])
    xy_data = cas_conn.upload_frame(xy_raw, casout={'name': 'xy_data',
                                                    'replace': True})

    # Read observations
    POINTS, (x, y), xy_table_ref = so.read_table(xy_data, columns=['x', 'y'])

    # Parameters and variables
    order = so.Parameter(name='order')
    beta = so.VariableGroup(so.exp_range(0, order), name='beta')
    estimate = so.ImplicitVar(
        (beta[0] + so.quick_sum(beta[k] * x[i] ** k
                                for k in so.exp_range(1, order))
         for i in POINTS), name='estimate')

    surplus = so.VariableGroup(POINTS, name='surplus', lb=0)
    slack = so.VariableGroup(POINTS, name='slack', lb=0)

    objective1 = so.Expression(
        so.quick_sum(surplus[i] + slack[i] for i in POINTS), name='objective1')
    abs_dev_con = so.ConstraintGroup(
        (estimate[i] - surplus[i] + slack[i] == y[i] for i in POINTS),
        name='abs_dev_con')

    minmax = so.Variable(name='minmax')
    objective2 = so.Expression(minmax + 0.0, name='objective2')
    minmax_con = so.ConstraintGroup(
        (minmax >= surplus[i] + slack[i] for i in POINTS), name='minmax_con')

    order.set_init(1)
    L1 = so.Model(name='L1', session=cas_conn)
    L1.set_objective(objective1, sense=so.MIN)
    L1.include(POINTS, x, y, xy_table_ref)
    L1.include(order, beta, estimate, surplus, slack, abs_dev_con)
    L1.add_statement('print x y estimate surplus slack;', after_solve=True)

    L1.solve(verbose=True)
    sol_data1 = L1.response['Print3.PrintTable'].sort_values('x')
    print(so.get_solution_table(beta))
    print(sol_data1.to_string())

    Linf = so.Model(name='Linf', session=cas_conn)
    Linf.include(L1, minmax, minmax_con)
    Linf.set_objective(objective2, sense=so.MIN)

    Linf.solve()
    sol_data2 = Linf.response['Print3.PrintTable'].sort_values('x')
    print(so.get_solution_table(beta))
    print(sol_data2.to_string())

    order.set_init(2)

    L1.solve()
    sol_data3 = L1.response['Print3.PrintTable'].sort_values('x')
    print(so.get_solution_table(beta))
    print(sol_data3.to_string())

    Linf.solve()
    sol_data4 = Linf.response['Print3.PrintTable'].sort_values('x')
    print(so.get_solution_table(beta))
    print(sol_data4.to_string())

    if sols:
        return (sol_data1, sol_data2, sol_data3, sol_data4)
    else:
        return Linf.get_objective_value()
Example #29
0
def tbfp(distance_data, game_data, venue_data,
         start_date=datetime.date(2019, 3, 28),
         end_date=datetime.date(2019, 10, 1),
         obj_type=0):

    # Define a CAS session
    cas_session = CAS(your_cas_server, port=your_cas_port)
    m = so.Model(name='tbfp', session=cas_session)

    t0 = time.time()
    # Discard games outside of the selected interval
    game_data = game_data[game_data['START'] >= start_date]
    game_data = game_data[game_data['END'] <= end_date]

    # Numerical assignment for source and sink
    source = 0
    sink = 9999

    # Define sets
    STADIUMS = sorted(venue_data.index.tolist())
    game_data = game_data[game_data['VENUE'].isin(STADIUMS)]
    GAMES = game_data.index.tolist()
    NODES = GAMES + [source, sink]

    # Define parameters
    away = game_data['AWAY']
    home = game_data['HOME']
    start = game_data['START']
    end = game_data['END']
    location = game_data['VENUE']
    city = game_data['CITY']
    driving = distance_data['minutes']
    distance = distance_data['miles']
    lat = venue_data['lat']
    lon = venue_data['lon']

    min_dist = {s: 0 for s in STADIUMS}
    arg_min = {s: 0 for s in STADIUMS}

    print('Numer of GAMES: {}'.format(len(GAMES)))

    # Define all possible arcs in the network model
    ARCS = []
    for g1 in GAMES:
        for s in STADIUMS:
            min_dist[s] = datetime.datetime(2020, 1, 1)
            arg_min[s] = -1
        for g2 in GAMES:
            if location[g1] != location[g2]:
                time_between = driving[location[g1], location[g2]]
                driving_time = datetime.timedelta(minutes=float(time_between))
                if end[g1] + driving_time <= start[g2] and min_dist[location[g2]] > start[g2]:
                    min_dist[location[g2]] = start[g2]
                    arg_min[location[g2]] = g2
        for s in STADIUMS:
            if arg_min[s] != -1:
                ARCS.append((g1, arg_min[s]))

    ARCS = ARCS + [(source, g) for g in GAMES] + [(g, sink) for g in GAMES]
    print('Number of ARCS: {}'.format(len(ARCS)))

    cost = {}
    for (g1, g2) in ARCS:
        if g1 != source and g2 != sink:
            cost[g1, g2] = (end[g2] - end[g1]).total_seconds()/86400.0
        elif g2 != sink and g1 == source:
            cost[g1, g2] = (end[g2]-start[g2]).total_seconds()/86400.0
        else:
            cost[g1, g2] = 0

    t1 = time.time()
    data_time = t1-t0

    # Add variables
    use_arc = m.add_variables(ARCS, vartype=so.BIN, name='use_arc')

    # Define expressions for the objectives
    total_time = so.quick_sum(
        cost[g1, g2] * use_arc[g1, g2] for (g1, g2) in ARCS)
    total_distance = so.quick_sum(
        distance[location[g1], location[g2]] * use_arc[g1, g2]
        for (g1, g2) in ARCS if g1 != source and g2 != sink)
    total_cost = total_time * 130 + total_distance * 0.25

    # Set objectives
    if obj_type == 0:
        m.set_objective(total_time, sense=so.MIN)
    elif obj_type == 1:
        m.set_objective(total_cost, sense=so.MIN)

    # Balance constraint
    m.add_constraints((
        so.quick_sum(use_arc[g, g2] for (gx, g2) in ARCS if gx == g) -
        so.quick_sum(use_arc[g1, g] for (g1, gx) in ARCS if gx == g)
        == (1 if g == source else (-1 if g == sink else 0))
        for g in NODES),
        name='balance')

    # Visit once constraint
    visit_once = so.ConstraintGroup((
        so.quick_sum(
            use_arc[g1, g2]
            for (g1, g2) in ARCS if g2 != sink and location[g2] == s) == 1
        for s in STADIUMS), name='visit_once')
    m.include(visit_once)

    prep_mark = time.time()
    prep_time = prep_mark - t1
    # Send the problem to SAS Viya solvers and solve the problem
    m.solve(milp={'concurrent': True}, frame=True)

    solve_time = time.time() - prep_mark

    # Parse the results
    schedule = []
    for (g1, g2) in ARCS:
        if (use_arc[g1, g2].get_value() > 0.5 and g1 != source and g2 != sink):
            if g1 not in schedule:
                schedule.append(g1)
            if g2 not in schedule:
                schedule.append(g2)

    # Sort the schedule and print information
    schedule = sorted(schedule, key=lambda i: start[i])
    route = []
    shortest_dist = [distance[location[schedule[0]],
                              location[schedule[1]]], 1, 2]
    longest_dist = shortest_dist
    shortest_time = [
        (start[schedule[1]] - end[schedule[0]]).total_seconds() / 60.0, 1, 2]
    longest_time = shortest_time
    most_critical = [shortest_time[0] - shortest_dist[0], 1, 2]
    c_game = -1
    print('{:3} {:<30} {:<12} {:<12} {:<20} {:<19} {:<6} {:<6}'.format(
        'Obs', 'Location', 'Away', 'Home', 'City', 'Time', 'Lat', 'Lon'))
    for i, g in enumerate(schedule):
        route.append([i+1, location[g], away[g], home[g], city[g], start[g],
                      lat[location[g]], lon[location[g]]])
        print('{:3d} {:<30} {:<12} {:<12} {:<20} {} {:6.3f} {:6.3f}'.format(
            *route[-1]))
        if c_game != -1:
            c_dis = distance[location[c_game], location[g]]
            c_driv = driving[location[c_game], location[g]]
            c_tim = (start[g] - end[c_game]).total_seconds() / 60.0
            if c_dis > longest_dist[0]:
                longest_dist = [c_dis, i, i+1]
            if c_dis < shortest_dist[0]:
                shortest_dist = [c_dis, i, i+1]
            if c_tim > longest_time[0]:
                longest_time = [c_tim, i, i+1]
            if c_tim < shortest_time[0]:
                shortest_time = [c_tim, i, i+1]
            if c_tim - c_driv < most_critical[0]:
                most_critical = [c_tim - c_driv, i, i+1]
        c_game = g

    print('Total time: {}'.format(end[schedule[-1]] - start[schedule[0]]))

    # Save the resulting schedules and information into a file for notebook
    n_months = (end_date-start_date).total_seconds()/(60.0*60.0*24*30)
    out = '''objt: {}
sdat: {}
edat: {}
mont: {:.1f}
time: {:.3f} days
dist: {:.3f} miles
cost: {:.3f} USD
gams: {}
vars: {}
cons: {}
data: {:.3f} secs
prep: {:.3f} secs
solv: {:.3f} secs
sdis: {} {}-{}
ldis: {} {}-{}
stim: {} {}-{}
ltim: {} {}-{}
mcri: {} {}-{}
schd: [
'''.format(obj_type, start_date, end_date, n_months,
           total_time.get_value(), total_distance.get_value(),
           total_cost.get_value(),
           len(GAMES), len(m.get_variables()), len(m.get_constraints()),
           data_time, prep_time, solve_time,
           *shortest_dist, *longest_dist, *shortest_time, *longest_time,
           *most_critical)
    out += '{}'.format('\n'.join([','.join([str(j) for j in i])
                                  for i in route]))
    out += '\n]'
    file = open('results/{}.txt'.format(id(m)), 'w')
    file.write(out)
    file.close()
    print(out)