Is it possible to read an MPS model?
It looks like CBC has a function for this purpose Cbc_readMps, a similar function in ruby-cbc would make it possible to call problem.find_conflict with an MPS file.
It is not possible to get the problem from a file (MPS or LP).
I totally get your usecase and why it would be helpful. The issue I see is that ruby-cbc uses its own data model, and transmits it to cbc only at the moment it wants a computation done. The only communication from Cbc to Ruby is the result values, not the problem itself.
If I want to add something to read a MPS file, I see 2 options:
- Write the complete parser and not rely on Cbc to populate the ruby model.
- Find how to get the model from Cbc and map it to the ruby model, to be able to use
Cbc_readMps.
Neither of these 2 options seem easy, and I don't think I will have time to do it. You would be very welcome to propose something though :)
I'll try and check how feasible it would be to have solution 2 done, but I can't promise anything.
I got some time tonight and implemented a MPS reader: I really like the idea of initializing a problem from any file, it would allow someone who generated such a file to benefit from the conflict finder.
It is not fully tested and is probably inefficient at loading a file, but it seems to work well on the file I tested.
You can try the branch feature/fromfile #26 :
model = Cbc::Utils::Mps.new("path_to_file.mps").to_model
p = model.to_problem
p.solve #should be infeasible
p.proven_infeasible? # => true
p.find_conflict
Tell me if that works for you!
@gverger thanks a lot for the quick response and even quicker dev, I will try this afternoon.
@gverger hello again, I am not sure what went wrong but on my Mac, cbs-wrapper cannot find the cbc library. I guess something is changed since this comment and brew install cbc doesn't install the library files 🤷
Edit: I am actually not sure if the reason was brew install not installing the libraries or if it was due to gem install cbc-wrapper not being able to find the cbc libs -- because I continued to have problems after re-installing the cbc via coin-or-tools/coinor/cbc (which is the suggested way for Mac) which definitely installs the libcbcsolver.so. So I had to manually create a config set for cbc-wrapper installation and set the with-cbc-dir directory so that cbc-warpper can be installed -- i.e., bundle config set --global build.cbc-wrapper --with-cbc-dir=/opt/homebrew/opt/cbc.
Hello, thanks for the feedback. Unfortunately I am in the dark with the macos installation: I don't have a mac anymore...
It seems that for mac (Apple Silicon) the homebrew directory has changed compared to mac Intel: see here
I managed to get my setup working (sorry! my previous message didn't make that clear).
I just read my infeasible model; however, I am getting the following error even though the objective function is defined and OBJ exists in the model
No MAX/MIN found after OBJSENSE
No match for row OBJ at line #...
...
Coin0008I no_name read with 28 errors
/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/bundler/gems/ruby-cbc-6b1bab73cdbd/lib/ruby-cbc/problem.rb:102:
in `create_cbc_problem': undefined method `kind' for nil:NilClass (NoMethodError)
I think it should be like
OBJSENSE
MAX
ROWS
...
EDIT: but it seems cbc ignores OBJSENSE
MIN found after OBJSENSE - Coin ignores
MAX found after OBJSENSE - Coin ignores
It turns out OBJSENSE is a solver dependent construct and Gurobi implements it as a single line ref, Cplex does two lines ref (at least it used to do it like that) and it looks like CBC "fudges" it altogether (determining the sense and then discarding it is quite weird 🤷). This is quite okay for infeasibility detection but not very cool for reading MPS files dumped by other solvers.
If I remove OBJSENSE find_conflict returns the conflicting constraint correctly so the reader is working ✅ Thanks for the dev! Sorry I wasn't of much help.
Regarding OBJSENSE incompatibility, I guess a solution would be reading the MPS file and if it has OBJSENSE and it is maximization*, setting the direction via setObjSense function after CBC reader is completed. Or keeping this info inside the model side and passing it back again to the problem. Don't know what would be the easiest or if it would worth it 🤷
*: At least Cplex, Gurobi and CBC are on the same page about the default objective function direction being minimization I believe.
Yes, it seems OBJSENSE is an extension of the original MPS format and they couldn't agree on how it should be written.
I'll think about how I want it to be dealt with here.
My first instinct would be to just use Cbc to read the MPS file since ruby-cbc is supposed to wrap cbc.
But then it is good if it can read models from Gurobi and CPLEX because if you use the mps file as an input, there are chances that it comes from either of these solvers.
Thanks for reporting the issue, I don't work on this gem a lot since I don't use it anymore, but I am very happy to keep it alive :)
I'll finalize the mps reader at some point and close this issue.
Ah. The file is dumped by Gurobi solver 🤷
Wikipedia says that the objective function direction is not in the original MPS format and different solvers implement it differently.
I guess supporting newline and whitespace possibilities would increase the compatibility with different MPS flavours unless it is too much of a hassle.
In any case, I will be turning that whitespace into a newline and then try again tomorrow.
On Tue, 20 Sep 2022, 18:52 gverger, @.***> wrote:
I think it should be like
OBJSENSE MAX ROWS ...
— Reply to this email directly, view it on GitHub https://github.com/gverger/ruby-cbc/issues/25#issuecomment-1252633597, or unsubscribe https://github.com/notifications/unsubscribe-auth/AEZ226JBKKARU3DVOBOKHPTV7HTVPANCNFSM6AAAAAAQQCUENY . You are receiving this because you authored the thread.Message ID: @.***>
Halil Sen linkedin.com/in/halilsen