3
$\begingroup$

Is there any way I could have Gurobi/CPLEX or any other package write/export MPS and LP files in the standard LP construct loaded with slack variables and transformations required for unsigned vars?

Here, by 'standard LP construct', I am referring to the LP formation ie. $\{ \min ~ c^\top x : Ax = b, x \geq 0 \}$.

EDIT: I'd also appreciate if you could share knowledge about some utility/function from Coin-OR or AMPL that can do this.

$\endgroup$
2
  • 1
    $\begingroup$ Do i understand it correctly that you are looking for a modeling software that automtically creates slack variables for linear problems when variables require them and prduces a MPS or LP file? $\endgroup$ Sep 27 at 16:35
  • $\begingroup$ yes, that is correct $\endgroup$
    – mohit-mhjn
    Sep 28 at 18:21
5
$\begingroup$

I am not aware of anything in the Coin-OR or AMPL ecosystem that does that as i am not familiar with those ecosystems. However one can convince the open source modeling language JuMP to do what you want with a few lines of Julia.

I assume you have a Julia installed and JuMP installed into your working (or global) environment.

Let's first create a model:

using JuMP #assumes you did ]add JuMP [enter] [waiting] [backspace] once

m = Model()

@variable(m, x[1:2])

@constraint(m, x[1] + x[2] <= 1)
@constraint(m, x[1] - x[2] <= 1)
@constraint(m, -x[1] + x[2] <= 1)
@constraint(m, -x[1] - x[2] <= 1)

@objective(m, Max, pi*x[1] -x[2])

If we know wanted to write that into an MPS or LP file it would be as easy as write_to_file(m, "test.lp") write_to_file(m, "test.mps") however that would not be in form you want. So let's define that form by defining a new model type:

MOI.Utilities.@model(
    MyStandardForm,
    (),
    (MOI.EqualTo,),
    (MOI.Nonnegatives,),
    (),
    (),
    (MOI.ScalarAffineFunction,),
    (MOI.VectorOfVariables,),
    (),
    false,
)

function MOI.supports_add_constrained_variables(
    ::MyStandardForm, 
    ::Type{MOI.Reals},
)
    return false
end

function MOI.supports_add_constrained_variable(
    ::MyStandardForm,
    ::Type{S},
) where {S<:MOI.AbstractScalarSet}
    return false
end

function MOI.supports_add_constrained_variables(
    ::MyStandardForm,
    ::Type{MOI.Nonnegatives},
)
    return true
end

function bridged_copy_to(dest, src)
    MOI.copy_to(MOI.Bridges.full_bridge_optimizer(dest, Float64), src)
    return dest
end

This model type uses the backend MathOptInterface to do all the transformations by only accepting a "standard LP construct". Now we got to copy our model $m$ into a model build from our newly defined type.

std_form = MyStandardForm{Float64}()
bridged_copy_to(std_form, backend(model))

However due to it being a user defined model the exporting is a bit more incovinient as we got to convert it first into a model type MathOptInterface defined.

MOI.write_to_file(bridged_copy_to(MOI.FileFormats.LP.Model(), std_form_m), "std_form.lp")

or for mps:

 MOI.write_to_file(bridged_copy_to(MOI.FileFormats.MPS.Model(), std_form_m), "std_form.mps")

which gives puts these files into your pwd():

maximize
obj: 3.141592653589793 x1 - 3.141592653589793 x3 - 1 x2 + 1 x4
subject to
c1: -1 x1 - 1 x2 + 1 x3 + 1 x4 - 1 x5 = -1
c2: -1 x1 + 1 x2 + 1 x3 - 1 x4 - 1 x6 = -1
c3: 1 x1 + 1 x2 - 1 x3 - 1 x4 - 1 x7 = -1
c4: 1 x1 - 1 x2 - 1 x3 + 1 x4 - 1 x8 = -1
Bounds
x1 >= 0
x2 >= 0
x3 >= 0
x4 >= 0
x5 >= 0
x6 >= 0
x7 >= 0
x8 >= 0
End

or in "std_form.mps":

NAME          
ROWS
 N  OBJ
 E  c1
 E  c2
 E  c3
 E  c4
COLUMNS
    x1        c1        -1
    x1        c2        -1
    x1        c3        1
    x1        c4        1
    x1        OBJ       -3.141592653589793 
    x2        c1        -1
    x2        c2        1
    x2        c3        1
    x2        c4        -1
    x2        OBJ       1
    x3        c1        1
    x3        c2        1
    x3        c3        -1
    x3        c4        -1
    x3        OBJ       3.141592653589793 
    x4        c1        1
    x4        c2        -1
    x4        c3        -1
    x4        c4        1
    x4        OBJ       -1
    x5        c1        -1
    x6        c2        -1
    x7        c3        -1
    x8        c4        -1
RHS
    rhs       c1        -1
    rhs       c2        -1
    rhs       c3        -1
    rhs       c4        -1
RANGES
BOUNDS
 LO bounds    x1        0
 PL bounds    x1
 LO bounds    x2        0
 PL bounds    x2
 LO bounds    x3        0
 PL bounds    x3
 LO bounds    x4        0
 PL bounds    x4
 LO bounds    x5        0
 PL bounds    x5
 LO bounds    x6        0
 PL bounds    x6
 LO bounds    x7        0
 PL bounds    x7
 LO bounds    x8        0
 PL bounds    x8
ENDATA

You can also read in existing files you then can copy to into a model instance of MyStandardFormFunctionConstraints and then copy that one in a new MOI.FileFormats.LP.Model()and write that to disk. If you wanted to do batch processing i would recommend looping over the files in Julia so you only have to pay the compilation overhead once and not per file.

If you want to use that Mixed-Integer problems MyStandardFormFunctionConstraints would need to be declared to also support those and an appropiate output format that also supports would need to be chosen.

$\endgroup$
2
  • $\begingroup$ Awesome! Thanks for the great solution. It's good to know that JuMP has a way to do this. I might utilize this as a sub-routine for transformation, however, I do not prefer to add dependencies in my repo so, until I find a better solution I can live with this. Thanks again for your help $\endgroup$
    – mohit-mhjn
    Sep 29 at 15:24
  • 1
    $\begingroup$ @mohit-mhjn I understand and know that Julia is a big dependency. (For that reason i only use Julia and it'S ecosystem ;-) ) In this case i would recommend you to create a Julia project in which stores the fact you use JuMP as in a toml file, you can also pin versions etc ... by default it will be pinned to the version you installed when creating the project. Julia actually has a language provided decent package manager. pkgdocs.julialang.org/v1.2/environments should cover what you need to know. You should also be aware that calling CPLEX/Gurobi/GLPK/CBC from JuMP is quiet easy. $\endgroup$ Sep 29 at 15:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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