7

I am trying to solve MILP in puLP (Python), and I keep getting the following error:

Traceback (most recent call last):
  File "main_lp.py", line 63, in <module>
    ans = solve_lp(C)
  File "/home/ashwin/Documents/Williams/f2014/math317_or/project/solve_lp.py", line 36, in solve_lp
    prob.solve()
  File "/usr/local/lib/python2.7/dist-packages/PuLP-1.5.6-py2.7.egg/pulp/pulp.py", line 1619, in solve
    status = solver.actualSolve(self, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/PuLP-1.5.6-py2.7.egg/pulp/solvers.py", line 1283, in actualSolve
    return self.solve_CBC(lp, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/PuLP-1.5.6-py2.7.egg/pulp/solvers.py", line 1346, in solve_CBC
    raise PulpSolverError("Pulp: Error while executing "+self.path)
pulp.solvers.PulpSolverError: Pulp: Error while executing /usr/local/lib/python2.7/dist-packages/PuLP-1.5.6-py2.7.egg/pulp/solverdir/cbc-32

For my linear programming problem, I am attempting to take sums of different vectors as the constraint, and I think I must have done that wrong somehow, because a much simpler problem works with no hitches. I have attached the code (C is an N by N numpy array).

def solve_lp(C):
    N = len(C)
    prob=LpProblem('Scheduling',LpMinimize)

    X = [[LpVariable('X' + str(i+1) + str(j+1), 0, C[i,j],LpBinary)
          for j in range(N)] for i in range(N)]
    X = np.array(X)
    X_o = [LpVariable('X0' + str(i), 0, None, LpBinary) for i in range(N)]
    X_t = [LpVariable('X' + str(i) + 't', 0, None, LpBinary) for i in range(N)]

    # Objective Function                                                                                                                                                
    ones_vec = list(np.ones(len(X_o)))
    prob += lpDot(ones_vec,X_o), 'Minimize Buses'

    # Constraints                                                                                                                                                       
    for i in range(N):
        row = list(X[i,:]) + [X_t[i]]
        ones_vec = list(np.ones(len(row)))
        prob += lpDot(ones_vec, row) == 1, 'Only one destination for ' + str(i)

    for j in range(N):
        col = list(X[:,j]) + [X_o[j]]
        ones_vec = list(np.ones(len(col)))
        prob += lpDot(ones_vec,col) == 1, 'Only one source for ' + str(j)

    prob.solve()
    return X, value(prob.objective)
anar
  • 343
  • 2
  • 4
  • 11
  • The PuLP syntax looks good. I didn't write out the complete formulation so I'd double check that. Is the problem solveable? How big is your data set? I would try solving on a very small data set. If it doesn't work I would think there is something wrong with your formulation – sedavidw Dec 10 '14 at 17:32
  • Quick side note, you don't have to use lpDot with a vector of one's, you can use lpSum – sedavidw Dec 10 '14 at 17:47
  • On a second look you do `X = np.array(X)`. That could be messing with something – sedavidw Dec 10 '14 at 18:01

8 Answers8

10

Make sure you don't have duplicate LpVariable names, and watch out for LpVariable names with the unsupported characters -+[] ->/ since all of those characters are silently converted to underscores _

Setting LpSolverDefault.msg = 1 before calling prob.solve() may help by printing the solvers output to the console.

levis501
  • 3,753
  • 20
  • 22
  • Please answer this question https://datascience.stackexchange.com/questions/49792/adding-constraints-in-pulp-optimization-problems-in-python?noredirect=1#comment56928_49792 – KHAN irfan Apr 24 '19 at 14:27
6

I recently had a similar problem due to Nan inputs in the model. I had the data in a DataFrame where some the cells should not bee converted to variables to improve performance. However, upon creating the objective function and the constraints, I noticed the presence of Nan and when I changed them it worked perfectly.

Pedro Esmeriz
  • 61
  • 1
  • 1
5

I think that you have duplicate LpVariable names. I just had the same problem and saw it thanks to levis501's answer. Here:

X = [[LpVariable('X' + str(i+1) + str(j+1), 0, C[i,j],LpBinary)
      for j in range(N)] for i in range(N)]

X contains some variables with the same name. For example for i = 0 and j = 10 you get 'X111', and for i = 10 and j = 0 you also get 'X111'.

aleon
  • 61
  • 1
  • 1
3

I had a similar problem, and indeed with duplicate LpVariable names just like levis501's answer and aleon answered.

my code was:

var = [[pulp.LpVariable(f'x{i}{j}', lowBound=0, cat=pulp.LpInteger) for j in range(col)] for i in range(row)]

when i=1 j=11, x would be x111, and when i=11 j=1, x also would be x111

I changed to:

var = [[pulp.LpVariable(f'x{i}_{j}', lowBound=0, cat=pulp.LpInteger) for j in range(col)] for i in range(row)]

After changing, it runs successfully.

Thilina Nakkawita
  • 1,302
  • 3
  • 19
  • 33
xiaoxia
  • 31
  • 2
2

I recently had the same error raised aswell. The reason this error code was raised in my case was that my data frame was not correctly populated. i incorrectly had NaN on the RHS on some of my constraints

What is had was something like:

Matrix = df.pivot(1st_dimension, 2nd_dimension, value)

This operation automatically puts NaN for the instances that are not in the original dataframe.

in my case the NaN'sare replaced with 0 which is what i was expecting it to be:

Matrix = Matrix.fillna(0)

1

I experienced the same issue when launching multiple instances of the LPSolver class. As fmars stated, the problem is that the path 'tmpSol' does not exist, which is defined in the following lines of code within the solvers.py file of pulp:

pid = os.getpid()
tmpLp = os.path.join(self.tmpDir, "%d-pulp.lp" % pid)
tmpMps = os.path.join(self.tmpDir, "%d-pulp.mps" % pid)
tmpSol = os.path.join(self.tmpDir, "%d-pulp.sol" % pid)

This bit of code appears in every solver. The problem is that these paths are deleted later on, but may coincide for different instances of the LPSolver class (as the variable pid is not unique).

The solution is to get a unique path for each instance of LPSolver, using, for example, the current time. Replacing the above lines by the following four will do the trick.

currentTime = time()
tmpLp = os.path.join(self.tmpDir, "%f3-pulp.lp" % currentTime)
tmpMps = os.path.join(self.tmpDir, "%f3-pulp.mps" % currentTime)
tmpSol = os.path.join(self.tmpDir, "%f3-pulp.sol" % currentTime)

Don't forget to

from time import time

Cheers, Tim

  • I came across the same problem and I believe your answer is correct. However, I don't understand what you meant by "The problem is that these paths are deleted later on, but may coincide for different instances of the LPSolver class (as the variable pid is not unique)." Why this bug only applies to some rare cases? Some infeasible formulation never triggers this bug and just returns a status 0. – MIMIGA Jun 18 '19 at 23:16
0

I have met some similar problem which because of some PuLP's bug. When some problem is infeasible and the solver failed to solve the problem, PuLP raise an exception rather than return status equals to infeasible. Below is the reason.

(You may first want to check out latest codebase of PuLP because the line number you paste doesn't match the latest one. I'll explain based on latest one but you can look at yours very simply.)

https://github.com/coin-or/pulp/blob/master/src/pulp/solvers.py#L1405-L1406 This is where exception prompt.

   if not os.path.exists(tmpSol):
      raise PulpSolverError("Pulp: Error while executing "+self.path) 

tmpSol is the temporary file where stores the solution. If PuLP cannot find such solution file, it will throw the exception you saw. And the bug I mentioned above is, if the problem itself is infeasible, then PuLP won't be able to generate such temporary file. So it will always throw such exception.

One thing you can do is, send a pull request to PuLP repo and fix it. And a simple workaround is, rather than directly invoke

 prob.solve()

in your code, you should always do

try:
     prob.solve()
except Exception:
     logger.debug('Problem infeasible')
fmars
  • 109
  • 4
  • This assumes that any failure to solve is because of infeasibility. Most of the time, trying to solve an infeasible problem will not result in an exception; it will just result in a problem whose status is infeasible: prob.status == -1 after calling prob.solve(). – GrayOnGray Oct 21 '18 at 02:06
  • Why this bug only applies to some infeasible cases? Some infeasible formulation never triggers this bug and just returns a status 0. – MIMIGA Jun 18 '19 at 23:17
0

I had this problem today, and it was because the temporary files for CBC were trying to be written in a location whose path had spaces in it, and the command that pulp passes to subprocess.Popen() to run CBC does not use quotes, and thus the command was misinterpreted and CBC couldn't find the location to create temp files.

For this I found two solutions:

(1) Explicitly set a temporary file directory that doesn't have spaces in it,

pulp.LpSolverDefault.tmpDir = self.tmp_file_dir  # tmp_file_dir can't have spaces!
prob.solve()

or (2) don't use CBC (my problem is small)

prob.solve(pulp.GLPK_CMD())

I have a work-related constraint that a lot of my work is stuck in directories that have spaces.

GrayOnGray
  • 295
  • 3
  • 11