Skip to content

Commit 7458b67

Browse files
committed
Add Mittelmann and clean decompression
1 parent 85ecaf3 commit 7458b67

File tree

11 files changed

+207
-198
lines changed

11 files changed

+207
-198
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ DataDeps = "124859b0-ceae-595e-8997-d05f6a7a8dfe"
1212
GZip = "92fee26a-97fe-5a0c-ad85-20a5f3185b63"
1313
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
1414
QPSReader = "10f199a5-22af-520b-b891-7ce84a7b1bd0"
15+
Scratch = "6c6a2e73-6563-6170-7368-637461726353"
1516

1617
[compat]
1718
CodecBzip2 = "0.8.5"
1819
DataDeps = "0.7.13"
1920
GZip = "0.6.2"
2021
Logging = "1.10.0"
2122
QPSReader = "0.2.1"
23+
Scratch = "1.3.0"
2224
julia = "1.10"

README.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,26 @@
22

33
A Julia package for automatic download and parsing of linear, quadratic and integer programming instances.
44

5-
## Principle
6-
75
Supported datasets:
86

97
- [x] [Netlib](https://www.netlib.org/lp/data/index.html)
108
- [x] [MIPLIB 2017](https://miplib.zib.de/index.html)
11-
- [ ] [Mittelmann benchmark](https://plato.asu.edu/ftp/lptestset/)
12-
13-
The source files are downloaded automatically thanks to [DataDeps.jl](https://github.com/oxinabox/DataDeps.jl).
14-
The returned problem format is `QPSData` from [QPSReader.jl](https://github.com/JuliaSmoothOptimizers/QPSReader.jl).
9+
- [x] [Mittelmann LP benchmark](https://plato.asu.edu/ftp/lptestset/)
1510

1611
## Getting started
1712

18-
To see which instances are available, call `list_instances(dataset, name)`.
13+
1. To see which instances are available, call `list_instances(dataset, name)`.
14+
2. To read a specific instance, call `read_instance(dataset, name)`.
15+
3. The returned problem format is `QPSData` from [QPSReader.jl](https://github.com/JuliaSmoothOptimizers/QPSReader.jl).
16+
17+
More details are available in the docstrings.
18+
19+
## Tips
1920

20-
To read a specific instance, call `read_instance(dataset, name)`.
21+
The problem source files are downloaded automatically thanks to [DataDeps.jl](https://github.com/oxinabox/DataDeps.jl).
22+
Note that each download has to be validated manually from the REPL.
23+
This doesn't work well when the triggering line of code is executed with VSCode's Julia extension, better run it in the REPL directly.
24+
An alternative is to set `ENV["DATADEPS_ALWAYS_ACCEPT"] = true`.
2125

22-
More details are available in the docstrings of these functions.
26+
The decompressed instances can be rather large (over 80 GB for the complete MIPLIB 2017 collection).
27+
If you need to clean up some space, you can delete unneeded files inside the folder located at `MathProgBenchmarks.MPS_SCRATCH`.

src/MathProgBenchmarks.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ using DataDeps
55
import GZip
66
using Logging
77
using QPSReader
8+
using Scratch
89

910
include("datasets.jl")
1011
include("instances.jl")

src/init.jl

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
MPS_SCRATCH = ""
2+
13
function __init__()
4+
global MPS_SCRATCH = @get_scratch!("mps_scratch")
25
# MIPLIB 2017
36
register(
47
DataDep(
58
"miplib2017-collection",
69
"""
7-
All instances in the MIPLIB 2017 collection set (size: 3.5 GB).
8-
Source: https://miplib.zib.de/index.html.""",
10+
All instances in the MIPLIB 2017 collection set.
11+
Source: https://miplib.zib.de/index.html.
12+
Compressed download size: 3.5 GB
13+
Decompressed size: ?? GB""",
914
"https://miplib.zib.de/downloads/collection.zip",
1015
post_fetch_method = unpack,
1116
),
@@ -14,11 +19,13 @@ function __init__()
1419
DataDep(
1520
"miplib2017-benchmark",
1621
"""
17-
All instances in the MIPLIB 2017 benchmark set (size: 317 MB).
18-
Source: https://miplib.zib.de/index.html.""",
22+
All instances in the MIPLIB 2017 benchmark set.
23+
Source: https://miplib.zib.de/index.html.
24+
Compressed download size: 317 MB
25+
Decompressed size: 639 MB""",
1926
"https://miplib.zib.de/downloads/benchmark.zip",
27+
"c756eefd544d83b31809306b45d3549a1a5b9378e6aa78b68738b1a3b6a418fa",
2028
post_fetch_method = unpack,
21-
"c756eefd544d83b31809306b45d3549a1a5b9378e6aa78b68738b1a3b6a418fa"
2229
),
2330
)
2431
register(
@@ -42,26 +49,19 @@ function __init__()
4249
),
4350
)
4451
# Mittelmann
45-
for (folder, names) in pairs(MITTELMANN_LP_INSTANCES)
46-
for name in names
47-
@info "$folder / $name"
48-
datadep_name = "mittelmann-lp-$name"
49-
# TODO: some paths are just .bz2, following no visible pattern
50-
if isnothing(folder)
51-
datadep_msg = """
52-
Instance $name from the Mittelmann LP benchmark set.
53-
Source: https://plato.asu.edu/ftp/lptestset/."""
54-
datadep_url = "https://plato.asu.edu/ftp/lptestset/$name.mps.bz2"
55-
else
56-
datadep_msg = """
57-
Instance $name from the Mittelmann LP benchmark set (folder $folder).
58-
Source: https://plato.asu.edu/ftp/lptestset/."""
59-
datadep_url = "https://plato.asu.edu/ftp/lptestset/$folder/$name.mps.bz2"
60-
end
61-
register(
62-
DataDep(datadep_name, datadep_msg, datadep_url)
63-
)
64-
end
65-
end
52+
register(
53+
DataDep(
54+
"mittelmann-lp",
55+
"""
56+
All instances in the Mittelmann LP benchmark set.
57+
Source: https://plato.asu.edu/ftp/lptestset/.
58+
Compressed download size: ??
59+
Decompressed size: 2.21 GB""",
60+
map(
61+
path -> "https://plato.asu.edu/ftp/lptestset/$path.bz2",
62+
MITTELMANN_LP_INSTANCES
63+
),
64+
)
65+
)
6666
return nothing
6767
end

src/instances.jl

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
"""
22
read_instance(dataset::Dataset, name::String)
33
4-
Read the instance identified by `name` from a given `dataset` and return a `QPSReader.QPSData` object.
4+
Read the instance identified by `name` from a given `dataset`.
5+
6+
Return a tuple `(data, path)` where `data isa QPSReader.QPSData` is an object created by [QPSReader.jl](https://github.com/JuliaSmoothOptimizers/QPSReader.jl) and `path` is the path to the MPS file.
57
68
# See also
79
@@ -25,7 +27,7 @@ function read_miplib2017_instance(name::String)
2527
end
2628
name = lowercase(name)
2729
mps_gz_path = joinpath(folder, "$name.mps.gz")
28-
return read_mps(mps_gz_path)
30+
return read_mps(mps_gz_path; scratch_subfolder = "miplib2017")
2931
end
3032

3133
function read_netlib_instance(name::String)
@@ -38,35 +40,41 @@ function read_netlib_instance(name::String)
3840
end
3941
netlib_path = fetch_netlib()
4042
sif_path = joinpath(netlib_path, "$name.SIF")
41-
return read_mps(sif_path; mpsformat)
43+
return read_mps(sif_path; scratch_subfolder = "netlib", mpsformat)
4244
end
4345

4446
function read_mittelmann_lp_instance(name::String)
45-
folder_path = @datadep_str("mittelmann-lp-$name")
46-
path1 = joinpath(folder_path, "$name.mps.bz2")
47-
path2 = joinpath(folder_path, "$name.bz2")
48-
if ispath(path1)
49-
return read_mps(path1)
47+
folder_path = @datadep_str("mittelmann-lp")
48+
mps_bz2_path1 = joinpath(folder_path, "$name.mps.bz2")
49+
mps_bz2_path2 = joinpath(folder_path, "$name.bz2")
50+
if ispath(mps_bz2_path1)
51+
return read_mps(mps_bz2_path1; scratch_subfolder = "mittelman-lp")
5052
else
51-
return read_mps(path2)
53+
return read_mps(mps_bz2_path2; scratch_subfolder = "mittelman-lp")
5254
end
5355
end
5456

55-
function read_mps(path::String; mpsformat::Symbol = :free)
56-
if endswith(path, ".mps.gz")
57-
contents = GZip.open(path, "r") do f
58-
read(f, String)
59-
end
60-
mps_path = string(tempname(), ".mps")
61-
open(mps_path, "w") do f
62-
write(f, contents)
63-
end
64-
elseif endswith(path, ".bz2") || endswith(path, ".mps.bz2")
65-
compressed = CodecBzip2.read(path)
66-
contents = String(CodecBzip2.transcode(CodecBzip2.Bzip2Decompressor, compressed))
67-
mps_path = string(tempname(), ".mps")
68-
open(mps_path, "w") do f
69-
write(f, contents)
57+
function read_mps(path::String; scratch_subfolder::String, mpsformat::Symbol = :free)
58+
name = splitext(splitext(splitpath(path)[end])[1])[1]
59+
if !isdir(joinpath(MPS_SCRATCH, scratch_subfolder))
60+
mkdir(joinpath(MPS_SCRATCH, scratch_subfolder))
61+
end
62+
if endswith(path, ".gz") || endswith(path, ".bz2")
63+
mps_path = joinpath(MPS_SCRATCH, scratch_subfolder, "$name.mps")
64+
if !ispath(mps_path)
65+
if endswith(path, ".mps.gz")
66+
contents = GZip.open(path, "r") do f
67+
read(f, String)
68+
end
69+
elseif endswith(path, ".mps.bz2")
70+
compressed = CodecBzip2.read(path)
71+
contents = String(CodecBzip2.transcode(CodecBzip2.Bzip2Decompressor, compressed))
72+
elseif endswith(path, ".bz2")
73+
throw(ArgumentError("File at $path not supported"))
74+
end
75+
open(mps_path, "w") do f
76+
write(f, contents)
77+
end
7078
end
7179
else
7280
@assert endswith(path, ".mps") || endswith(path, ".SIF")
@@ -76,5 +84,5 @@ function read_mps(path::String; mpsformat::Symbol = :free)
7684
qps_data = with_logger(NullLogger()) do
7785
readqps(mps_path; mpsformat)
7886
end
79-
return qps_data
87+
return qps_data, mps_path
8088
end

src/lists.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,6 @@ function list_netlib_instances()
3636
end
3737

3838
function list_mittelmann_lp_instances()
39-
return reduce(vcat, values(MITTELMANN_LP_INSTANCES))
39+
all_names = reduce(vcat, MITTELMANN_LP_INSTANCES)
40+
return map(n -> string(chopsuffix(split(n, "/")[end], ".mps")), all_names)
4041
end

0 commit comments

Comments
 (0)