2
$\begingroup$

When I solve the following code, the binaries for the lines_selection variable is not respected by PuLP. Can anyone please point me out to the reason why this could occur?

I've also attached the output LP file for reference.

import pandas as pd
plants = {1:('Plant 1'), 2: ('Plant 2')}
customers = {1: ('Customer 1'), 2: ('Customer 2'), 3: ('Customer 3')}
demands = {1: 50, 2: 50, 3: 25}
line_options = [1, 2]#{1:('Base'), 2: ('Double Base')}
line_options_cost = {1: 0, 2: 10}
line_options_capacity = {1: 50, 2: 100}
costs = {(1, 1): 1, (1, 2): 1.5, (1,3): 1.5, (2,1): 1, (2,2): 1, (2,3): 1}

#Declare the problem
Investment_Decisions_Problem = LpProblem("Investment_Decisions", LpMinimize)

#Variable - Config of plant lines
lines_selection = LpVariable.dicts("Line Selection", [(i, j) for i in line_options for j in plants], cat='binary')
#lines_selection = LpVariable.dicts("Line Selection", [(i, j) for i in line_options for j in plants], lowBound = 0, upBound = 1, cat='interger')

#Variable - Flow of product from plant to customer
flow_vars = LpVariable.dicts("Flow", [(j, k) for j in plants for k in customers], lowBound = 0, cat = 'continuous')

#Adding an internal flow variable
internal_flow = LpVariable.dicts("Flow_Internal", [(j) for j in plants], lowBound = 0, cat = 'continuous')
#Setting objective
total_cost = lpSum(flow_vars[j,k] * costs[j,k] for j in plants for k in customers) + lpSum(lpSum(lines_selection[i,j] * line_options_cost[i] for i in line_options) for j in plants)

# Constraints
# C1 - All customer demand must be satisfied
for k in customers:                      
    Investment_Decisions_Problem += LpConstraint(e = lpSum([flow_vars[j,k] for j in plants]),
                                        sense = LpConstraintEQ,
                                        name = 'Demand_Satisfied_'+str(k),
                                        rhs = demands[k])

# C2 - Flow must be less than capacity
for j in plants:
    Investment_Decisions_Problem += LpConstraint(e = lpSum([flow_vars[j,k] for k in customers]) - internal_flow[j],
                                                 sense = LpConstraintEQ,
                                                 name = 'Equalizing_multi_echlon_flow_'+str(j),
                                                 rhs = 0)

# C3 - Balancing internal flow
for j in plants:
    Investment_Decisions_Problem += LpConstraint(e = lpSum([lines_selection[i,j] * line_options_capacity[i] for i in line_options]) - internal_flow[j],
                                                 sense = LpConstraintEQ,
                                                 name = 'Multi_Echelon_Flow_vs_capacity'+str(j),
                                                 rhs = 0)

# C4 - Only one line options is possible
for j in plants:
    Investment_Decisions_Problem += LpConstraint(e = lpSum([lines_selection[i,j] for i in line_options]),
                                                 sense = LpConstraintEQ,
                                                 name = 'Only one option'+str(j),
                                                 rhs = 1)
# Setting problem objective
Investment_Decisions_Problem.setObjective(total_cost)

# The problem data is written to an .lp file
Investment_Decisions_Problem.writeLP("Investment_Decisions_Flow.lp")
    
# The problem is solved using PuLP's choice of Solver
Investment_Decisions_Problem.solve()


Output LP file:

* Investment_Decisions *
Minimize OBJ: Flow_(1,1) + 1.5 Flow(1,2) + 1.5 Flow(1,3) + Flow(2,_1)

  • Flow_(2,2) + Flow(2,3) + 10 Line_Selection(2,_1)
  • 10 Line_Selection_(2,2) Subject To Demand_Satisfied_1: Flow(1,1) + Flow(2,1) = 50 Demand_Satisfied_2: Flow(1,2) + Flow(2,2) = 50 Demand_Satisfied_3: Flow(1,3) + Flow(2,3) = 25 Equalizing_multi_echlon_flow_1: Flow(1,1) + Flow(1,2) + Flow(1,_3)
  • Flow_Internal_1 = 0 Equalizing_multi_echlon_flow_2: Flow_(2,1) + Flow(2,2) + Flow(2,_3)
  • Flow_Internal_2 = 0 Multi_Echelon_Flow_vs_capacity1: - Flow_Internal_1 + 50 Line_Selection_(1,_1)
  • 100 Line_Selection_(2,1) = 0 Multi_Echelon_Flow_vs_capacity2: - Flow_Internal_2 + 50 Line_Selection(1,_2)
  • 100 Line_Selection_(2,2) = 0 Only_one_option1: Line_Selection(1,1) + Line_Selection(2,1) = 1 Only_one_option2: Line_Selection(1,2) + Line_Selection(2,2) = 1 Bounds 0 <= Flow(1,1) 0 <= Flow(1,2) 0 <= Flow(1,3) 0 <= Flow(2,1) 0 <= Flow(2,2) 0 <= Flow(2,3) 0 <= Flow_Internal_1 0 <= Flow_Internal_2 Line_Selection(1,1) free Line_Selection(1,2) free Line_Selection(2,1) free Line_Selection(2,_2) free End

Clearly mentions that

Line_Selection_(1,1) free Line_Selection(1,2) free Line_Selection(2,1) free Line_Selection(2,_2) free

are all free or unrestricted variables. Any idea what I am doing wrong? TIA

$\endgroup$
1
  • $\begingroup$ In variable definition try making cat='Binary' and see. It may not recognize 'binary' and so treating it as continuous by default. $\endgroup$ Commented Mar 12, 2023 at 15:28

3 Answers 3

4
$\begingroup$

You need to capitalize the first letter in the cat option:

# Variable - Config of plant lines
lines_selection = LpVariable.dicts("Line Selection", [(i, j) for i in line_options for j in plants], cat='Binary')
$\endgroup$
2
  • $\begingroup$ Sorry this does not solve it. $\endgroup$
    – lalsriv
    Commented Mar 12, 2023 at 15:43
  • 3
    $\begingroup$ It should solve the issue of not recognizing "Line selection" as binary. For the general problem of your mathematical model see the answer of @A.Omidi and consider posing a new specific question. $\endgroup$
    – PeterD
    Commented Mar 12, 2023 at 15:53
3
$\begingroup$

Some points should be considered.

  • First, you defined the variable $FlowInternal_{j}$ as the slack variables on the constraints $C_2$ and $C_3$ but, forgot to limit it on the objective function. (For example, by a Big_M cost).
  • Second, as this slack variable appears on the two different constraints, its values might be different which causes infeasibility.

One possible way would be by relaxing those constraints from $=$ to $\leq$ to achieve what you want. Also, by solving the above problem based on the mentioned points, the solution and its corresponding LP file would be:

---- VAR objective      -INF  10125.000     +INF       .         

---- VAR x  

                       LOWER     LEVEL     UPPER   

Base       .plant_1      .        1.000     1.000       
Base       .plant_2      .        1.000     1.000       
double_Base.plant_1      .         .        1.000      
double_Base.plant_2      .         .        1.000      

---- VAR flow  

                      LOWER     LEVEL     UPPER    

plant_1.customer_1      .       50.000     +INF               
plant_1.customer_2      .         .        +INF           
plant_1.customer_3      .         .        +INF           
plant_2.customer_1      .         .        +INF           
plant_2.customer_2      .       50.000     +INF               
plant_2.customer_3      .       25.000     +INF              

---- VAR flow_internal  

           LOWER     LEVEL     UPPER    

plant_1      .       25.000     +INF                
plant_2      .       25.000     +INF         

LP File:

objf..  objective - 10*x(double_Base,plant_1) - 10*x(double_Base,plant_2)
     
      - flow(plant_1,customer_1) - 1.5*flow(plant_1,customer_2)
     
      - 1.5*flow(plant_1,customer_3) - flow(plant_2,customer_1)
     
      - flow(plant_2,customer_2) - flow(plant_2,customer_3)
     
      - 200*flow_internal(plant_1) - 200*flow_internal(plant_2) =E= 0 ;

---- e1

e1(plant_1)..  x(Base,plant_1) + x(double_Base,plant_1) =E= 1 ;
e1(plant_2)..  x(Base,plant_2) + x(double_Base,plant_2) =E= 1 ;


---- e2 

e2(customer_1)..  flow(plant_1,customer_1) + flow(plant_2,customer_1) =E= 50 ;     
e2(customer_2)..  flow(plant_1,customer_2) + flow(plant_2,customer_2) =E= 50 ; 
e2(customer_3)..  flow(plant_1,customer_3) + flow(plant_2,customer_3) =E= 25 ;     


---- e3 

e3(plant_1)..  flow(plant_1,customer_1) + flow(plant_1,customer_2)
     
      + flow(plant_1,customer_3) - 3*flow_internal(plant_1) =L= 0 ; 
     
e3(plant_2)..  flow(plant_2,customer_1) + flow(plant_2,customer_2)
     
      + flow(plant_2,customer_3) - 3*flow_internal(plant_2) =L= 0 ; 
     

---- e4  =L=  

e4(plant_1)..  50*x(Base,plant_1) + 100*x(double_Base,plant_1)
     
      - 2*flow_internal(plant_1) =L= 0 ; 
     
e4(plant_2)..  50*x(Base,plant_2) + 100*x(double_Base,plant_2)
     
      - 2*flow_internal(plant_2) =L= 0 ;

    
$\endgroup$
0
1
$\begingroup$

Changing 'binary' to 'Binary' in the lines_selection definition gave me this in the LP file

\* Investment_Decisions *\
Minimize
OBJ: Flow_(1,_1) + 1.5 Flow_(1,_2) + 1.5 Flow_(1,_3) + Flow_(2,_1)
 + Flow_(2,_2) + Flow_(2,_3) + 10 Line_Selection_(2,_1)
 + 10 Line_Selection_(2,_2)
Subject To
Demand_Satisfied_1: Flow_(1,_1) + Flow_(2,_1) = 50
Demand_Satisfied_2: Flow_(1,_2) + Flow_(2,_2) = 50
Demand_Satisfied_3: Flow_(1,_3) + Flow_(2,_3) = 25
Equalizing_multi_echlon_flow_1: Flow_(1,_1) + Flow_(1,_2) + Flow_(1,_3)
 - Flow_Internal_1 = 0
Equalizing_multi_echlon_flow_2: Flow_(2,_1) + Flow_(2,_2) + Flow_(2,_3)
 - Flow_Internal_2 = 0
Multi_Echelon_Flow_vs_capacity1: - Flow_Internal_1 + 50 Line_Selection_(1,_1)
 + 100 Line_Selection_(2,_1) = 0
Multi_Echelon_Flow_vs_capacity2: - Flow_Internal_2 + 50 Line_Selection_(1,_2)
 + 100 Line_Selection_(2,_2) = 0
Only_one_option1: Line_Selection_(1,_1) + Line_Selection_(2,_1) = 1
Only_one_option2: Line_Selection_(1,_2) + Line_Selection_(2,_2) = 1
Bounds
 0 <= Flow_(1,_1)
 0 <= Flow_(1,_2)
 0 <= Flow_(1,_3)
 0 <= Flow_(2,_1)
 0 <= Flow_(2,_2)
 0 <= Flow_(2,_3)
 0 <= Flow_Internal_1
 0 <= Flow_Internal_2
Binaries
Line_Selection_(1,_1)
Line_Selection_(1,_2)
Line_Selection_(2,_1)
Line_Selection_(2,_2)
End
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.