PuLP integration, cbcModel.status, variable names, readMPS, etc.
I'm trying to integrate this package as an optional dependency of PuLP, as an alternative to distribute the cbc binary.
You can see the progress here: https://github.com/coin-or/pulp/blob/60579dc516c55c63b603f139e2b823de6a1b2d31/pulp/apis/coin_api.py#L872
I've been encountering a number of issues.
I've tried to follow the documentation on CyClpSimplex and CyCbcModel.
It seems that cbcModel.status returns "relaxation infeasible" on optimal problems, where the log says is Optimal.
What's the correct way to get the correct status after solving a problem? I attach a few mps with
filename: test_continuous-pulp.txt cbc logs say: Optimal cbc.status returns: relaxation infeasible cbc.solve() returns: 0
The return code from cbcModel.solve() returns 0 (which I interpret as Optimal) when the log status shows "Linear relaxation infeasible".
filename: test_infeasible-pulp.txt cbc logs says: Linear relaxation infeasible cbc.status returns: relaxation infeasible cbc.solve() returns: 0
Result - Linear relaxation infeasible
Also, how do I solve maximization problems? Do I need to invert the objective functional myself? Or is there a way to communicate this when loading the MPS file?
filename: test_continuous_max-pulp.txt
Finally, how do I access the variables by name when I load the MPS file as input with CyClpSimplex.readMps?
CyClpSimplex.variables returns an error
CyClpSimplex.variableNames is empty
test_infeasible-pulp.txt test_continuous-pulp.txt test_continuous_max-pulp.txt
I'll have to do a little checking to make sure when I have a little time to devote to this, but off the top of my head, the return code from cbc.solve() is probably not a status and might not be that meaningful. Your results above look correct and consistent. It's possible that the variable names don't get properly set when reading from an MPS file, since this is not how people usually use CyLP. That could be a bug, but there should be a fix.
thanks. Let me know if you need anything else from my side. I'm excited to have the possibility to add callbacks to cbc.
Just looking at this briefly, it does seem that the variable names are not extracted when reading an MPS file. I think it's not too hard to see how this all hangs together. The files that would need to be modified to add this capability are (I think):
https://github.com/coin-or/CyLP/blob/master/cylp/cpp/ICoinMpsIO.hpp
https://github.com/coin-or/CyLP/blob/master/cylp/cpp/ICoinMpsIO.cpp
https://github.com/coin-or/CyLP/blob/master/cylp/cy/CyCoinMpsIO.pyx
What's needed is to expose the relevant functions in the CoinMpsIO class of CoinUtils and then use them within the readMps method of the CyCoinMpsIO class to populate the relevant Python data structures. A PR would be great. Otherwise, it may be a bit of time before I get to adding this.
There seems to be a bit of a disconnect in how this is implemented. The query method for variable names seems to be querying Clp, which would be fine if we were using Clp's readMps method (which wraps the one in CoinUtils), but we are directly using the CoinUtils method.
thanks for checking @tkralphs . I'd love to help with a PR but my C++ skills are very rusty.
What about the first issue I mentioned. Where the log says "Optimal" (and it's the correct message) but the cbc.status property returns "relaxation infeasible".
Thanks again.
Looking at this more closely, there is a simpler fix. CyClpSimplex.readMps() has an optional argument keepNames that is False by default. The following works:
from cylp.cy import CyClpSimplex
s = CyClpSimplex()
s.readMps('rentacar.mps', True)
s.variableNames
We should probably just make it True by default, since it doesn't hurt unless you are really trying to economize on memory. Also, we could pull the names into the CyLP model that can be constructed after reading the MPS file, which I don't think is done currently.
For your other question, the return code of cbc.solve() is not a status and shouldn't be interpreted as such. Your previous results look correct and consistent.