Search code examples
pythonlinear-programmingpulp

LP in pulp - problem with decision variable


I wish my decision variable to depend on the previous period’s starting variables which is changed by another decision variable. The line I get an error is:

my_problem += lineup_wk1[i] == starting_lineup[i] + trans_in[i] - trans_out[i] for i in players

where it says

  File "<ipython-input-21-42d75f1d2bc3>", line 4
    my_problem += lineup_wk1[i] == starting_lineup[i] + trans_in[i] - trans_out[i] for i in players
                                                                                     ^
SyntaxError: invalid syntax

The full code is:

players = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
points = [4.2, 5.3, 6.0, 5.5, 4.5, 5.7, 4.8, 6.9]
cost = [4.5, 5.0, 5.1, 4.9, 5.2, 5.7, 5.2, 6.0]
starting_team = [1,0,1,0,1,0,1,0]

player_points = dict(zip(players, points))
player_cost = dict(zip(players, cost))
starting_lineup = dict(zip(players, starting_team))

my_problem = pulp.LpProblem('LP_model', pulp.LpMaximize)
lineup_wk1 = pulp.LpVariable.dict('lineup_wk1_ % s', players, lowBound=0, upBound=1, cat=pulp.LpBinary)
trans_in  = pulp.LpVariable.dict('trans_in_ % s', players, lowBound=0, upBound=1, cat=pulp.LpBinary)
trans_out  = pulp.LpVariable.dict('trans_out_ % s', players, lowBound=0, upBound=1, cat=pulp.LpBinary)

my_problem += sum([player_points[i] * (lineup_wk1[i]) for i in players])

my_problem += sum([lineup_wk1[i] for i in players]) <= 4
my_problem += sum(trans_in[i] for i in players) <=2
my_problem += sum(trans_in[i] for i in players) == sum(trans_out[i] for i in players)
my_problem += lineup_wk1[i] == starting_lineup[i] + trans_in[i] - trans_out[i] for i in players

my_problem.solve()

Solution

  • So the basic thing that is hanging you up is formulating a correct pulp expression for your constraint. You are confusing summation / generator type of expressions when you want to make a constraint for each player. So you are not summing or able to do for i in players in one line. Instead, anytime your pulp model needs a "for each" constraint, you will need to make a loop (easiest way) and use the loop to make the full set of constraints. In my example below, the loop will create 8 separate constraints, one for each player.

    Note: I'm not QA'ing the logic of your model, so if there is a math error, you'll need to dig. You have a good framework here, but I would strongly suggest you rework the model to make this a simple knapsack problem (you already have the parts) to construct the best team possible of limited size and limited cost. Those constraints are very straightforward. If you can get that working, you can then start to venture out to this more creative in-out constraint. :)

    # pulp player & team
    
    import pulp
    
    players = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
    points = [4.2, 5.3, 6.0, 5.5, 4.5, 5.7, 4.8, 6.9]
    cost = [4.5, 5.0, 5.1, 4.9, 5.2, 5.7, 5.2, 6.0]
    starting_team = [1,0,1,0,1,0,1,0]
    
    player_points = dict(zip(players, points))
    player_cost = dict(zip(players, cost))
    starting_lineup = dict(zip(players, starting_team))
    
    my_problem = pulp.LpProblem('LP_model', pulp.LpMaximize)
    lineup_wk1 = pulp.LpVariable.dict('lineup_wk1_ % s', players, lowBound=0, upBound=1, cat=pulp.LpBinary)
    trans_in  = pulp.LpVariable.dict('trans_in_ % s', players, lowBound=0, upBound=1, cat=pulp.LpBinary)
    trans_out  = pulp.LpVariable.dict('trans_out_ % s', players, lowBound=0, upBound=1, cat=pulp.LpBinary)
    
    my_problem += sum([player_points[i] * (lineup_wk1[i]) for i in players])
    
    my_problem += sum([lineup_wk1[i] for i in players]) <= 4
    my_problem += sum(trans_in[i] for i in players) <=2
    my_problem += sum(trans_in[i] for i in players) == sum(trans_out[i] for i in players)
    # this will make a "for each player" constraint.  There is no summation... 
    # note the change from i -> "player" which makes this more readable.
    for player in players:  
       my_problem += lineup_wk1[player] == starting_lineup[player] + trans_in[player] - trans_out[player] 
    
    my_problem.solve()