The LP file format
The purpose of this page is to document the LP file format, and the various differences that occur between solvers.
Resources
There are a bunch of different descriptions of the LP file format on the internet.
Grammar
This section gives the grammar of an LP file as we implement it. This grammar may be different to that of particular solvers.
The syntax rules for the grammar are:
<name>
: the name of a symbol in the grammarA :== B
: A is equivalent to BA | B
: either A or Bi""
: case insensitive string[A]
: A is optional(A)*
: there are 0 or more repeats of A(A)+
: there is at least one or more repeats of A
In addition to the grammar, there are the following rules:
- Comments begin with
\
, and run until the next\n
character - Whitespace is ignored
- Newlines are ignored, except where explicitly described
<lp-file> :==
<keyword-objective>\n
[<section-objective>\n]
<keyword-constraints>\n
(<constraint>)*
[<keyword-bounds>\n (<bound-expression>)+]
[<keyword-general>\n (<identifier>)+]
[<keyword-binary>\n (<identifier>)+]
[<keyword-sos>\n (<constraint-sos>)+]
[<keyword-end>]
<keyword-objective> :==
i"min" | i"minimum" | i"minimize" | i"minimise"
| i"max" | i"maximum" | i"maximize" | i"maximise"
<keyword-constraints> :==
(i"subject to" | i"st" | i"st." | i"s.t." | i"such that")[":"]
<keyword-bounds> :== i"bound" | i"bounds"
<keyword-general> :== i"gen" | i"general" | i"generals"
<keyword-binary> :== i"bin" | i"binary" | i"binaries"
<keyword-sos> :== i"sos"
<keyword-end> :== i"end"
<digit> :== "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
<char> :== a-z | A-Z | !"#$%&()/,;?@_'`|~
<identifier> :== <char> (<char> | <digit> | ".")*
<number> :==
"+" <number>
| "-" <number>
| <digit>+[.(<digit>)*][("e" | "E")("+" | "-")(<digit>)+]
| i"inf"
| i"infinity"
<quadratic-term> :==
"+" <quadratic-term>
| "-" <quadratic-term>
| [<number> ["*"]] <identifier> "^" "2"
| [<number> ["*"]] <identifier> "*" <identifier>
<quadratic-expression> :==
"[" <quadratic-term> (("+" | "-") <quadratic-term>)* "]" ["/" "2"]
<term> :==
"+" <term>
| "-" <term>
| <identifier>
| <number>
| <number> <identifier>
| <number> "*" <identifier>
| <quadratic-expression>
<expression> :== <term> (("+" | "-") <term>)*
<name> :== [<identifier>":"]
<objective> :== <name> [<expression>]
<inequality> :== "<" | "<=" | "=<" | ">" | ">=" | "=>" | "=" | "=="
<set-suffix> := <inequality> <number>
<set-prefix> := <number> <inequality>
<constraint-indicator> :== <identifier> "=" (0 | 1) "->" <expression> <set-suffix>
<constraint-sos> :==
"S1::" (<identifier>":"<number>)+\n
| "S2::" (<identifier>":"<number>)+\n
<constraint> :==
<name> <expression> <set-suffix>
| <name> <constraint-indicator>
| <name> <sos-constraint>
<bound-expression> :==
<identifier> i"free"
| <identifier> <set-suffix>
| <set-prefix> <identifier>
| <set-prefix> <identifier> <set-suffix>
Differences
There are many differences in how solvers parse an LP file.
The "integer" section
Consider the section:
integers
x
Gurobi will interpret this as x in MOI.Integer()
. FICO Xpress will interpret this as x in MOI.ZeroOne()
.
FICO document this behavior, but they're an outlier.
We choose to interpret integers
as MOI.Integer()
.
Whether variables can have the same name as a keyword
Consider the file
min
st
st
st >= 0
end
or even the horrific
min st st st >= 0 end
Gurobi will complain up front that the keyword st
appears twice, whereas Xpress will read the file as equivalent to :
minimize
x
subject to
x >= 0
end
We choose to allow variables to be named as keywords, and we use context to disambiguate.
Whitespace
Consider the file
minimize
2x
end
Gurobi will interpret this as a single variable with the name 2x
, where as Xpress will interpret this as the expression 2 * x
.
Gurobi document this behavior, saying that they require whitespace around all tokens, but they're an outlier.
We choose to allow juxtaposted tokens without whitespace.
Identifiers
In general, an identifier may contain the letters a-z, A-Z, the digits 0-9, and the characters !"#\$%&()/,.;?@_'`|~
.
Additional solvers put additional restrictions:
- In (all?) solvers except Gurobi, the identifier must not start with a digit or a
.
(in Gurobi, identifiers must be separated by whitespace) - Identifiers in Mosek and lpsolve may not start with the letter e or E
- Keywords must not be used as names in Gurobi or Mosek, but they may in Xpress
- Many solvers actually support reading any UTF-8 string as the identifier, but they will normalize to the legal letters on write
We choose to allow any valid UTF-8 names.