Giter Club home page Giter Club logo

qpsreader.jl's Introduction

QPSReader

A package to read linear optimization problems in MPS format and quadratic optimization problems in QPS format.

DOI CI Build Status codecov.io Documentation/stable Documentation/dev

How to Cite

If you use QPSReader.jl in your work, please cite using the format given in CITATION.bib.

The problems represented by the QPS format have the form

optimize   c₀ + cᵀ x + ½ xᵀ Q x    subject to   L ≤ Ax ≤ U and ℓ ≤ x ≤ u,

where:

  • "optimize" means either "minimize" or "maximize"
  • c₀ ∈ ℝ is a constant term, c ∈ ℝⁿ is the linear term, Q = Qᵀ is the n×n quadratic term,
  • A is the m×n constraint matrix, L, U are constraint lower and upper bounds, respectively
  • , u are variable lower and upper bounds, respectively

Mixed-integer problems are supported, but semi-continuous and semi-integer variables are not.

Quick start

Installation

julia> ]
pkg> add QPSReader

Reading a file

This package exports the QPSData data type and the readqps() function. Because MPS is a subset of QPS, the readqps() function accepts both formats. Because the SIF is a superset of QPS, QPS problems implemented as SIF files (such as those from the Maros-Meszaros collection) are also supported.

Both fixed and free format are supported (see below for format conventions). To read a problem from a file:

julia> qps = readqps("Q25FV47.QPS")  # Free MPS is used by default
julia> qps = readqps("Q25FV47.QPS", mpsformat=:fixed)  # uses fixed MPS format
julia> qps = readqps("Q25FV47.QPS", mpsformat=:free)   # uses free MPS format

readqps also accepts an IO object as the first argument.

By default, a number of messages may be logged while reading. Log output can be suppressed as follows:

using QPSReader
using Logging

qps = with_logger(Logging.NullLogger()) do
    readqps("Q25FV47.QPS")
end

Problem representation

The QPSData data type is defined as follows:

mutable struct QPSData
    nvar::Int                        # number of variables
    ncon::Int                        # number of constraints
    objsense::Symbol                 # :min, :max or :notset
    c0::Float64                      # constant term in objective
    c::Vector{Float64}               # linear term in objective

    # Quadratic objective, in COO format
    qrows::Vector{Int}
    qcols::Vector{Int}
    qvals::Vector{Float64}

    # Constraint matrix, in COO format
    arows::Vector{Int}
    acols::Vector{Int}
    avals::Vector{Float64}

    lcon::Vector{Float64}            # constraints lower bounds
    ucon::Vector{Float64}            # constraints upper bounds
    lvar::Vector{Float64}            # variables lower bounds
    uvar::Vector{Float64}            # variables upper bounds
    name::Union{Nothing, String}     # problem name
    objname::Union{Nothing, String}  # objective function name
    rhsname::Union{Nothing, String}  # Name of RHS field
    bndname::Union{Nothing, String}  # Name of BOUNDS field
    rngname::Union{Nothing, String}  # Name of RANGES field
    varnames::Vector{String}         # variable names, aka column names
    connames::Vector{String}         # constraint names, aka row names

    # name => index mapping for variables
    # Variables are indexed from 1 and onwards
    varindices::Dict{String, Int}

    # name => index mapping for constraints
    # Constraints are indexed from 1 and onwards
    # Recorded objective function has index 0
    # Rim objective rows have index -1
    conindices::Dict{String, Int}

    # Variable types
    #  `VTYPE_Continuous`      <--> continuous
    #  `VTYPE_Integer`         <--> integer
    #  `VTYPE_Binary`          <--> binary
    #  `VTYPE_SemiContinuous`  <--> semi-continuous (not supported)
    #  `VTYPE_SemiInteger`     <--> semi-integer (not supported)
    vartypes::Vector{VariableType}

    # Indicates the sense of each row:
    # `RTYPE_Objective`    <--> objective row (`'N'`)
    # `RTYPE_EqualTo`      <--> equality constraint (`'E'`)
    # `RTYPE_LessThan`     <--> less-than constraint (`'L'`)
    # `RTYPE_GreaterThan`  <--> greater-than constraint (`'G'`)
    contypes::Vector{RowType}
end

Rows and variables are indexed in the order in which they are read. The matrix Q is zero when reading an MPS file.

Conventions

The file formats supported by QPSReader are described here:

The following conventions are enforced:

Rim data

  • Multiple objective rows

    • The first N-type row encountered in the ROWS section is recorded as the objective function, and its name is stored in objname.
    • If an additional N-type row is present, a warning-level log is displayed. Subsequent N-type rows are ignored.
    • Each time a rim objective row is encountered in the COLUMNS or RHS section, the corresponding coefficients are skipped, and an error-level log is displayed.
  • Multiple RHS / Range / Bound fields

    • The second field of the first card in the RHS section determines the name of the right-hand side, which is stored in rhsname. Same goes for the RANGES and BOUNDS sections, with the corresponding names being stored in rngname and bndname, respectively.
    • Any card whose second field does not match the expected name is then ignored. A warning-level log is displayed at the first such occurence.
    • In addition, any line or individual coefficient that is ignored triggers an error-level log.

Variable bounds

  • Default bounds for variables are [0, Inf), to exception of integer variables (see below).
  • If multiple bounds are specified for a given variable, only the most recent bound is recorded.

Integer variables

There are two ways of declaring integer variables:

  • Through markers in the COLUMNS section.

  • By specifying BV, LI or UI bounds in the BOUNDS section

  • The convention for integer variable bounds in as follows:

    Marker? BOUNDS fields Type Bounds reported
    Yes - Integer [0, 1]
    Yes BV Binary [0, 1]
    Yes (LI, l) Integer [l, Inf]
    Yes (UI, u) with u≥0 Integer [0, u]
    Yes (UI, u) with u<0 Integer [-Inf, u]
    Yes (LI, l) + (UI, u) Integer [l, u]
    No BV Binary [0, 1]
    No (LI, l) Integer [l, Inf]
    No (UI, u) with u≥0 Integer [0, u]
    No (UI, u) with u<0 Integer [-Inf, u]
    No (LI, l) + (UI, u) Integer [l, u]

    The LI/UI can be replaced by LO/UP in the table above, with no impact on bounds. Only the integrality of variables are affected. For continuous variables, follow the second half of the table, and replace LI/UI by LO/UP.

Errors

  • A row (resp. column) name that was not declared in the ROWS (resp. COLUMNS) section, appears elsewhere in the file. The only case where an error is not thrown is if said un-declared row or column appears in a rim line that is skipped.
  • An N-type row appears in the RANGES section

Problem Collections

Both the Netlib LP and Maros-Meszaros QP collections are provided as Julia artifacts (requires Julia 1.3). This package exports the fetch_netlib and fetch_mm functions that return the path to the Netlib and Maros-Meszaros collections, repectively

using QPSReader

netlib_path = fetch_netlib()
mm_path = fetch_mm()

qpsreader.jl's People

Contributors

abelsiqueira avatar dpo avatar egmara avatar geoffroyleconte avatar jsobot avatar juliatagbot avatar mlubin avatar monssaftoukal avatar mtanneau avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

qpsreader.jl's Issues

Release and register as Julia package?

Several Julia packages need to read MPS files, so having a stand-alone Julia reader for MPS files would avoid duplicating code.
For instance:

  • MathOptInterface.jl has an MPS reader/writer
  • Tulip.jl has its own MPS reader (that handles free MPS format)
  • SDDP.jl needs to parse MPS files when reading stochastic mps problem (I'm currently writing a parser for smps files and I'd like to avoid re-writing an MPS reader for the 3rd time)

Couple of questions:

  • Would there be license restrictions when using QPSReader within another Julia package? (e.g., is it OK to use QPSReader within an MIT-licensed package?)
  • For various reasons (especially in Tulip), I need constraint/objective matrices in COO format, not SparseCSC, so I would very much like to remove the COO -> SparseCSC conversion after parsing
  • Performance is not (yet) an issue, but the reader in Tulip is ~2-3x faster than QPSReader

(obviously, I can PR all this, just want the go-ahead before spending time on it)

TODO

  • Use COO format for matrices (#12)
  • Rim data
    • Choose conventions (#14)
    • Implement & update documentation (e.g. in README) (#18)
  • Performance evaluation (#15, #17, #20)
  • Free MPS format support (#21)

Convention for rim data?

MPS files may have multiple objective rows, right-hand sides, etc... which is currently not handled in the code.

  • The objective name is reset every time a N row is encountered, so only the last such row is recorded.
  • Names for right-hand side and bounds are ignored, which means that
RHS
    rhs1      r1                2.0    r2                6.0
    rhs2      r1               -2.0    r2               -6.0

would yield right-hand sides of -2 and -6 for rows r1 and r2, respectively.

Obviously, nobody follows the same convention, so I would propose to do as follows:

  • The first N row is selected as the objective. Other N rows are ignored.
    Consequently, entries in subsequent sections that correspond to unregistered rows should be ignored, not raise an error. If this sounds bad or if you really want to raise an error, then one should keep track of these extra N rows.
  • The first name encountered in RHS, BOUNDS, RANGES is selected; others (if any) are ignored
  • Duplicate coefficients in the COLUMNS and QUADOBJ sections are added together. This follows the default behavior of creating a sparse matrix from the coefficients list: duplicate entries are added.
  • Duplicate entries in RHS, BOUNDS, RANGES sections over-write previous entries.

Here's a quick and non-exhaustive overview of what others do:

  • lpsolve:

    The RHS section allows one or more right-hand-side vectors to be
    defined; most people don't bother having more than one.

  • CPLEX docs:

    The MPS format allows multiple righthand sides (RHSs), multiple bounds, and multiple range vectors. It also allows extra free rows. Together, these features are known as extra rim vectors. By default, the CPLEX MPS reader selects the first RHS, bound, and range definitions that it finds. The first free row (that is, N-type row) becomes the objective function, and the remaining free rows are discarded. The extra rim data are also discarded.

  • The Gurobi docs says

    The name of the RHS is specified in the first field (column 5 in fixed format), but this name is ignored by the Gurobi reader
    and when I test with Gurobi 9.0, duplicate rhs entries for a given row are ignored (i.e., only the first occurrence of each row is recorded).
    bound name (ignored)

  • Mosek docs do not mention how multiple entries are handled, however in practice it looks like the first occurrence of RHS is recorded, so in the example above, the line rhs2 ... would be ignored.
    If there are duplicate entries, only the last one is recorded, i.e.,

        rhs1      r1                2.0    r1                6.0
    

    would yield a right-hand side value of 6.0 for row r1.

Fails to read neos-5044663-wairoa

The neos-5044663-wairoa instance from MIPLIB 2017 fails to read.

julia> QPSReader.readqps(GZip.gzopen("neos-5044663-wairoa.mps.gz"), mpsformat=:fixed)
[ Info: Using 'neos-5044663-wairoa' as NAME (l. 8)
[ Info: Using 'R0000000' as objective (l. 10)
┌ Error: Ignoring marker  at line 230060
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 301671
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 301674
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 301707
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 301712
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 302575
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 302578
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 302793
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 302796
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 303257
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 303260
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 303353
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 303358
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 304105
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 304108
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 304155
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 304158
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 304469
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 304472
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 304565
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 304568
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 304601
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 304606
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 304699
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 304704
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 304797
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 304800
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 304881
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 304888
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 305027
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 305030
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 305485
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 305490
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 306351
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 306354
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 306593
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 306596
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 307447
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 307450
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 307635
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 307638
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 308283
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 308286
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 309055
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 309058
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 309275
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 309280
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 310407
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 310410
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 310543
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 310546
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 310731
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 310736
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 311137
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 311140
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 311315
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 311318
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 311365
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 311368
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 312335
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 312338
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 312523
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 312526
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 312849
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 312852
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 313155
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 313158
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 313481
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 313486
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 314337
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 314340
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 314479
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 314482
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 314979
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 314982
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 315477
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 315480
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 315619
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 315764
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
┌ Error: Ignoring marker  at line 319405
└ @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:852
ERROR: ArgumentError: cannot parse "lumn" as Float64
Stacktrace:
 [1] _parse_failure(T::Type, s::String, startpos::Int64, endpos::Int64) (repeats 2 times)
   @ Base ./parse.jl:373
 [2] #tryparse_internal#403
   @ ./parse.jl:369 [inlined]
 [3] tryparse_internal
   @ ./parse.jl:367 [inlined]
 [4] #parse#404
   @ ./parse.jl:379 [inlined]
 [5] parse
   @ ./parse.jl:379 [inlined]
 [6] read_columns_line!(qps::QPSReader.QPSData, card::QPSReader.MPSCard{QPSReader.FixedMPS}; integer_section::Bool)
   @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:487
 [7] readqps(qps::GZip.GZipStream; mpsformat::Symbol)
   @ QPSReader ~/.julia/packages/QPSReader/panNr/src/readqps.jl:856
 [8] top-level scope
   @ REPL[5]:1

The problematic line in the file is likely:

    C0063540  LB_full              0   $ empty column

I had never seen "$" in an MPS file before, but apparently it's valid comment in fixed format. IBM docs say:

If the first character in Field 3 or 5 is a dollar sign ($), the remaining characters in the record are treated as a comment. Another method for inserting comments is to place an asterisk (*) in column 1. Everything on such a line is treated as a comment.

TagBot trigger issue

This issue is used to trigger TagBot; feel free to unsubscribe.

If you haven't already, you should update your TagBot.yml to include issue comment triggers.
Please see this post on Discourse for instructions and more details.

If you'd like for me to do this for you, comment TagBot fix on this issue.
I'll open a PR within a few hours, please be patient!

Performance benchmark

I'll write a benchmark script before working on performance improvements.

I propose to use 3 benchmark sets:

  • The Netlib LPs (available here): these are small LPs, in .SIF files and fixed MPS format.
  • The Maros-Meszaros QPs (here)
  • The LPs of Hans Mittelmann's benchmark (see here). These are (much) larger LPs in .mps, some with integer variables, and some in free MPS format.

The reason for including a larger testset of instances would be to allow modifications that improve performance on large instances, while allowing a limited degradation on smaller instances.

For information, the readers in C/C++ solvers (commercial and open-source) are ~10-30x faster than the ones we have in Julia.

Error reading certain Netlib problems

julia> using QPSReader
julia> netlib_path = fetch_netlib()

julia> blend = readqps(joinpath(netlib_path, "BLEND.SIF"))
[ Info: Using 'BLEND' as NAME (l. 5)
[ Info: Using 'C' as objective (l. 98)
[ Info: Using '65' as RHS (l. 376)
ERROR: Unknown row 23.26.
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] read_rhs_line!(::QPSData, ::QPSReader.MPSCard{QPSReader.FreeMPS}) at /Users/dpo/dev/julia/JSO/QPSReader.jl/src/readqps.jl:498
 [3] readqps(::String; mpsformat::Symbol) at /Users/dpo/dev/julia/JSO/QPSReader.jl/src/readqps.jl:800
 [4] readqps(::String) at /Users/dpo/dev/julia/JSO/QPSReader.jl/src/readqps.jl:703
 [5] top-level scope at REPL[5]:1
 [6] eval(::Module, ::Any) at ./boot.jl:331
 [7] eval_user_input(::Any, ::REPL.REPLBackend) at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:86
 [8] run_backend(::REPL.REPLBackend) at /Users/dpo/.julia/packages/Revise/Pcs5V/src/Revise.jl:1073
 [9] top-level scope at none:0

julia> prob = readqps(joinpath(netlib_path, "DFL001.SIF"))
[ Info: Using 'DFL001' as NAME (l. 5)
[ Info: Using 'NIL' as objective (l. 6098)
[ Info: Using 'R0006' as RHS (l. 29101)
ERROR: ArgumentError: cannot parse "R0009" as Float64
Stacktrace:
 [1] _parse_failure(::Type{T} where T, ::String, ::Int64, ::Int64) at ./parse.jl:370 (repeats 2 times)
 [2] #tryparse_internal#347 at ./parse.jl:366 [inlined]
 [3] tryparse_internal at ./parse.jl:364 [inlined]
 [4] #parse#348 at ./parse.jl:376 [inlined]
 [5] parse at ./parse.jl:376 [inlined]
 [6] read_rhs_line!(::QPSData, ::QPSReader.MPSCard{QPSReader.FreeMPS}) at /Users/dpo/dev/julia/JSO/QPSReader.jl/src/readqps.jl:478
 [7] readqps(::String; mpsformat::Symbol) at /Users/dpo/dev/julia/JSO/QPSReader.jl/src/readqps.jl:800
 [8] readqps(::String) at /Users/dpo/dev/julia/JSO/QPSReader.jl/src/readqps.jl:703
 [9] top-level scope at REPL[6]:1
 [10] eval(::Module, ::Any) at ./boot.jl:331
 [11] eval_user_input(::Any, ::REPL.REPLBackend) at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:86
 [12] run_backend(::REPL.REPLBackend) at /Users/dpo/.julia/packages/Revise/Pcs5V/src/Revise.jl:1073
 [13] top-level scope at none:0

julia> prob = readqps(joinpath(netlib_path, "FORPLAN.SIF"))
[ Info: Using 'FORPLAN' as NAME (l. 5)
[ Info: Using 'OB1PNW20' as objective (l. 21)
ERROR: Duplicate row name DEDO3 at line 23
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] read_rows_line!(::QPSData, ::QPSReader.MPSCard{QPSReader.FreeMPS}) at /Users/dpo/dev/julia/JSO/QPSReader.jl/src/readqps.jl:363
 [3] readqps(::String; mpsformat::Symbol) at /Users/dpo/dev/julia/JSO/QPSReader.jl/src/readqps.jl:796
 [4] readqps(::String) at /Users/dpo/dev/julia/JSO/QPSReader.jl/src/readqps.jl:703
 [5] top-level scope at REPL[7]:1
 [6] eval(::Module, ::Any) at ./boot.jl:331
 [7] eval_user_input(::Any, ::REPL.REPLBackend) at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:86
 [8] run_backend(::REPL.REPLBackend) at /Users/dpo/.julia/packages/Revise/Pcs5V/src/Revise.jl:1073
 [9] top-level scope at none:0

julia> prob = readqps(joinpath(netlib_path, "GFRD-PNC.SIF"))
[ Info: Using 'GFRD-PNC' as NAME (l. 5)
[ Info: Using 'OBJ' as objective (l. 20)
[ Info: Using 'PAF' as RHS (l. 2821)
ERROR: ArgumentError: cannot parse "PEE" as Float64
Stacktrace:
 [1] _parse_failure(::Type{T} where T, ::String, ::Int64, ::Int64) at ./parse.jl:370 (repeats 2 times)
 [2] #tryparse_internal#347 at ./parse.jl:366 [inlined]
 [3] tryparse_internal at ./parse.jl:364 [inlined]
 [4] #parse#348 at ./parse.jl:376 [inlined]
 [5] parse at ./parse.jl:376 [inlined]
 [6] read_rhs_line!(::QPSData, ::QPSReader.MPSCard{QPSReader.FreeMPS}) at /Users/dpo/dev/julia/JSO/QPSReader.jl/src/readqps.jl:478
 [7] readqps(::String; mpsformat::Symbol) at /Users/dpo/dev/julia/JSO/QPSReader.jl/src/readqps.jl:800
 [8] readqps(::String) at /Users/dpo/dev/julia/JSO/QPSReader.jl/src/readqps.jl:703
 [9] top-level scope at REPL[8]:1
 [10] eval(::Module, ::Any) at ./boot.jl:331
 [11] eval_user_input(::Any, ::REPL.REPLBackend) at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:86
 [12] run_backend(::REPL.REPLBackend) at /Users/dpo/.julia/packages/Revise/Pcs5V/src/Revise.jl:1073
 [13] top-level scope at none:0

julia> prob = readqps(joinpath(netlib_path, "SIERRA.SIF"))
[ Info: Using 'SIERRA' as NAME (l. 5)
[ Info: Using 'OBJ' as objective (l. 1247)
[ Info: Using 'BB1R' as RHS (l. 6566)
ERROR: ArgumentError: cannot parse "BB2R" as Float64
Stacktrace:
 [1] _parse_failure(::Type{T} where T, ::String, ::Int64, ::Int64) at ./parse.jl:370 (repeats 2 times)
 [2] #tryparse_internal#347 at ./parse.jl:366 [inlined]
 [3] tryparse_internal at ./parse.jl:364 [inlined]
 [4] #parse#348 at ./parse.jl:376 [inlined]
 [5] parse at ./parse.jl:376 [inlined]
 [6] read_rhs_line!(::QPSData, ::QPSReader.MPSCard{QPSReader.FreeMPS}) at /Users/dpo/dev/julia/JSO/QPSReader.jl/src/readqps.jl:478
 [7] readqps(::String; mpsformat::Symbol) at /Users/dpo/dev/julia/JSO/QPSReader.jl/src/readqps.jl:800
 [8] readqps(::String) at /Users/dpo/dev/julia/JSO/QPSReader.jl/src/readqps.jl:703
 [9] top-level scope at REPL[9]:1
 [10] eval(::Module, ::Any) at ./boot.jl:331
 [11] eval_user_input(::Any, ::REPL.REPLBackend) at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:86
 [12] run_backend(::REPL.REPLBackend) at /Users/dpo/.julia/packages/Revise/Pcs5V/src/Revise.jl:1073
 [13] top-level scope at none:0

cc @geoffroyleconte

Incorrect bounds on binary variables

While integer variables are not supported, the code claims to at least set their bounds. However, the reader can silently give incorrect bounds.

The CPLEX MPS documentation says:

ILOG CPLEX accepts two commonly used ways of extending
the MPS file format to include integer variables: in the COLUMNS section or in the BOUNDS
section.
In the first way, integer variables are identified within the COLUMNS section of the MPS file
by marker lines.
...
If no bounds are specified for the variables within markers, bounds of 0 (zero) and 1 (one)
are assumed.

GLPK follows the CPLEX behavior.

However, QPSReader ignores markers, so it can't can't enforce this rule to set the correct variable bounds (it sets an upper bound of infinity in this case).

Drop Julia 1.1, 1.2 from CI

I think it would be enough to have

# .travis.yml
julia:
  - 1.0
  - 1
  - nightly

and

# .appveyor.yml
environment:
  matrix:
  - julia_version: 1.0
  - julia_version: 1
  - julia_version: nightly

since 1 should automatically point to the newest stable 1.Y.Z release.

Use CITATION.cff instead of CITATION.bib

I am reviewing the CITATION usage in the repos and in particular moving to CFF files. The bib can still be generated using Github menu.
Here are a couple of suggestions to adapt this repo:

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.