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.