-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathsifdecoder.jl
More file actions
395 lines (361 loc) · 12.5 KB
/
sifdecoder.jl
File metadata and controls
395 lines (361 loc) · 12.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
"""
set_mastsif(set::String="sifcollection")
Set the environment variable `MASTSIF` to point to a set of SIF problems.
The supported sets are:
- `"sifcollection"`: the CUTEst NLP test set;
- `"maros-meszaros"`: the Maros-Meszaros QP test set;
- `"netlib-lp"`: the Netlib LP test set.
"""
function set_mastsif(set::String = "sifcollection")
if set == "sifcollection"
ENV["MASTSIF"] = joinpath(artifact"sifcollection", "optrove-sif-0b335a4b1a3c")
elseif set == "maros-meszaros"
ENV["MASTSIF"] = joinpath(artifact"maros-meszaros", "optrove-maros-meszaros-9adfb5707b1e")
elseif set == "netlib-lp"
ENV["MASTSIF"] = joinpath(artifact"netlib-lp", "optrove-netlib-lp-f83996fca937")
else
error("The set $set is not supported.")
end
return nothing
end
"""
problems = list_sif_problems()
# Output
- `problems::Vector{String}`: A list of SIF problems (without the extension `.SIF`) available in the current set.
See [`set_mastsif`](@ref) for more details about how to change the set of SIF files.
"""
function list_sif_problems()
problems = String[]
files = readdir(ENV["MASTSIF"])
for file in files
name, ext = splitext(file)
if ext == ".SIF"
push!(problems, name)
end
end
return problems
end
function _suffix_precision(precision::Symbol)
if precision == :single
return "_s"
elseif precision == :double
return ""
elseif precision == :quadruple
return "_q"
else
error("The $precision precision is not supported.")
end
end
function _decoder_precision(precision::Symbol)
if precision == :single
return "-sp"
elseif precision == :double
return "-dp"
elseif precision == :quadruple
return "-qp"
else
error("The $precision precision is not supported.")
end
end
function _name_outsdif(name::String, precision::Symbol)
sifname, extension = basename(name) |> splitext
suffix = _suffix_precision(precision)
name = "OUTSDIF_$(sifname)$(suffix).d"
return name
end
function _name_libsif(name::String, precision::Symbol; standalone::Bool = false)
sifname, extension = basename(name) |> splitext
if standalone
return "lib$(sifname)_$(precision)_standalone.$dlext"
else
return "lib$(sifname)_$(precision).$dlext"
end
end
"""
sifdecoder(name::String, args...; verbose::Bool=false,
precision::Symbol=:double, libsif_folder=libsif_path)
Decodes a SIF problem, converting it into a format suitable for further processing.
# Arguments
- `name::String`: The path or name of the SIF problem, with or without the extension `.SIF`.
- `args...`: Additional arguments passed directly to the SIF decoder. Relevant for problems with variable sizes.
- `verbose::Bool`: If `true`, enables verbose output during the decoding process. Defaults to `false`.
- `precision::Symbol`: The desired precision for the problem. Can be `:single`, `:double` (default), or `:quadruple`.
- `libsif_folder::String`: The directory where the generated files (`*.f` and `*.d`) will be stored. Defaults to `libsif_path`.
```julia
sifdecoder("HS1.SIF", precision=:single)
sifdecoder("DIXMAANJ", "-param", "M=30"; precision=:double, verbose=true)
sifdecoder("/home/alexis/CUTEst/BROWNDEN.SIF", precision=:quadruple)
```
"""
function sifdecoder(
name::String,
args...;
verbose::Bool = false,
precision::Symbol = :double,
libsif_folder::String = libsif_path,
)
outsdif = _name_outsdif(name, precision)
prec = _decoder_precision(precision)
suffix = _suffix_precision(precision)
pname, sif = basename(name) |> splitext
if length(name) < 4 || name[(end - 3):end] != ".SIF"
name = "$name.SIF"
end
if isfile(name)
path_sifname = abspath(name)
else
path_sifname = joinpath(ENV["MASTSIF"], name)
if !isfile(path_sifname)
error("$name not found")
end
end
outlog = tempname()
errlog = tempname()
cd(libsif_folder) do
delete_temp_files(pname, suffix)
isfile(outsdif) && rm(outsdif, force = true)
run(
pipeline(
Cmd(
`$(SIFDecode_jll.sifdecoder()) $(args) $(prec) -suffix $(path_sifname)`,
ignorestatus = true,
),
stdout = outlog,
stderr = errlog,
),
)
error_str = read(errlog, String)
if length(error_str) > 0
println(error_str)
error("Unable to compile a shared library for the problem $name.")
end
if verbose
output_str = read(outlog, String)
println(output_str)
end
end
rm(outlog)
rm(errlog)
return nothing
end
"""
build_libsif(name::String; precision::Symbol=:double, standalone::Bool=false,
libsif_folder::String=libsif_path)
Builds a shared library from a decoded SIF problem.
# Arguments
- `name::String`: The path or name of the SIF problem, with or without the extension `.SIF`.
- `precision::Symbol`: The desired precision of the problem. Can be `:single`, `:double` (default), or `:quadruple`.
- `standalone::Bool`: If `true`, creates a standalone shared library for the SIF problem without requiring CUTEst. Only relevant to GALAHAD.jl. Defaults to `false`.
- `libsif_folder::String`: The directory where the compiled library will be stored. Defaults to `libsif_path`.
!!! warning
We expect that the SIF problem has been decoded in `libsif_folder` and contains the generated files (`*.f` and `*.d`).
```julia
build_libsif("HS1.SIF", precision=:single)
build_libsif("DIXMAANJ", precision=:double)
build_libsif("/home/alexis/CUTEst/BROWNDEN.SIF", precision=:quadruple)
```
"""
function build_libsif(
name::AbstractString;
precision::Symbol = :double,
standalone::Bool = false,
libsif_folder::String = libsif_path,
)
pname, sif = basename(name) |> splitext
libsif_name = _name_libsif(pname, precision; standalone)
suffix = _suffix_precision(precision)
libcutest = joinpath(libcutest_path, "libcutest_$precision.a")
cd(libsif_folder) do
if isfile("ELFUN_$pname$suffix.f")
object_files = String[]
for file in (
"ELFUN",
"GROUP",
"RANGE",
"ELFUNF",
"ELFUND",
"GROUPF",
"GROUPD",
"SETTYP",
"EXTER",
"EXTERA",
)
fname = "$(file)_$pname$suffix"
if isfile("$fname.f")
@static if Sys.iswindows()
mingw = Int == Int64 ? "mingw64" : "mingw32"
bindir = joinpath(artifact"mingw-w64", mingw, "bin")
gfortran = joinpath(bindir, "gfortran.exe")
withenv("PATH" => string(bindir, ';', ENV["PATH"])) do
run(`$gfortran -O3 -c -fPIC $fname.f`)
end
else
run(`gfortran -O3 -c -fPIC $fname.f`)
end
push!(object_files, "$fname.o")
end
end
#! format: off
if Sys.isapple()
if standalone
run(`gfortran -dynamiclib -o $libsif_name $object_files`)
else
run(`gfortran -dynamiclib -o $libsif_name $object_files -Wl,-all_load $libcutest`)
end
elseif Sys.iswindows()
@static if Sys.iswindows()
mingw = Int == Int64 ? "mingw64" : "mingw32"
bindir = joinpath(artifact"mingw-w64", mingw, "bin")
gfortran = joinpath(bindir, "gfortran.exe")
if standalone
withenv("PATH" => string(bindir, ';', ENV["PATH"])) do
run(`$gfortran -shared -o $libsif_name $object_files`)
end
else
withenv("PATH" => string(bindir, ';', ENV["PATH"])) do
run(`$gfortran -shared -o $libsif_name $object_files -Wl,--whole-archive $libcutest -Wl,--no-whole-archive`)
end
end
end
else
if standalone
run(`gfortran -shared -o $libsif_name $object_files`)
else
run(`gfortran -shared -o $libsif_name $object_files -Wl,--whole-archive $libcutest -Wl,--no-whole-archive`)
end
end
#! format: on
delete_temp_files(pname, suffix)
else
error("The file $pname.SIF was not decoded in the folder $libsif_folder.")
end
end
return nothing
end
function delete_temp_files(pname::String, suffix::String)
for file in (
"ELFUN",
"ELFUNF",
"ELFUND",
"RANGE",
"GROUP",
"GROUPF",
"GROUPD",
"SETTYP",
"EXTER",
"EXTERA",
)
for ext in ("f", "o")
fname = "$(file)_$pname$suffix.$ext"
isfile(fname) && rm(fname, force = true)
end
end
nothing
end
"""
clear_libsif()
Removes all compiled libraries and data files associated with SIF problems.
"""
function clear_libsif()
total_nbytes = 0
for file in readdir(libsif_path, join = true, sort = false)
total_nbytes += filesize(file)
rm(file, force = true)
end
println(
"All files generated by CUTEst.jl have been deleted for a total of $(total_nbytes |> format_bytes).",
)
end
"""
manage_libsif(; sort_by::Symbol=:name, rev::Bool=false)
Opens a prompt allowing the user to selectively remove compiled libraries for SIF problems.
Data files `OUTSDIF_*.d`, which store preprocessed information required for automatic differentiation within CUTEst, are also removed.
By default, the problems are sorted by name.
Alternatively, you can sort them by file size on disk by specifying `sort_by=:size`.
Use `rev=true` to reverse the sort order.
Note: Shared libraries for SIF problems compiled with `standalone = true` can only be removed using the function [`clear_libsif`](@ref).
"""
function manage_libsif(; sort_by::Symbol = :name, rev::Bool = false)
# Get all installed libsif / outsdif
sif_problems = Set{String}()
sif_precision = Dict{String, Vector{String}}()
sif_sizes = Dict{String, Int}()
files = readdir(libsif_path)
for file in files
for precision in ("single", "double", "quadruple")
suffix = "_$precision.$dlext"
if startswith(file, "lib") && endswith(file, suffix)
sif_problem = file[4:(end - length(suffix))]
push!(sif_problems, sif_problem)
if !haskey(sif_precision, sif_problem)
sif_precision[sif_problem] = String[]
sif_sizes[sif_problem] = 0
end
push!(sif_precision[sif_problem], precision)
libsif = joinpath(libsif_path, file)
name_outsdif = _name_outsdif(sif_problem, precision |> Symbol)
outsdif = joinpath(libsif_path, name_outsdif)
sif_sizes[sif_problem] += filesize(libsif)
sif_sizes[sif_problem] += filesize(outsdif)
end
end
end
if isempty(sif_problems)
println("No problems to remove. All SIF libraries have already been cleared.")
else
# Compute the size in bytes associated to each problems
sif_problems = collect(sif_problems)
sif_sizes = [sif_sizes[sif_problem] for sif_problem in sif_problems]
# Sort sif_problems and sif_sizes
if sort_by === :name
perm = sortperm(sif_problems; rev)
elseif sort_by == :size
perm = sortperm(sif_sizes; rev)
else
error("unsupported sort value: :$sort_by (allowed: :name, :size)")
end
sif_problems = sif_problems[perm]
sif_sizes = sif_sizes[perm]
# Build menu items
menu_items = similar(sif_problems)
for i in eachindex(sif_problems, sif_sizes)
menu_items[i] = @sprintf("%-10s (%s)", sif_problems[i], sif_sizes[i] |> Base.format_bytes)
end
# Prompt user
ts = @sprintf("%s", sum(sif_sizes) |> Base.format_bytes)
manage_sif_menu = TerminalMenus.request(
"Which problems should be removed (total size on disk: $ts)?",
TerminalMenus.MultiSelectMenu(menu_items; pagesize = 10, charset = :ascii),
)
# Handle no selection
if isempty(manage_sif_menu)
println("No problems have been removed.")
else
# Otherwise prompt for confirmation
println("\nThe following problems have been marked for removal:\n")
index_items = Int.(manage_sif_menu)
for item in menu_items[sort(index_items)]
println(" ", item)
end
print("\nAre you sure that these should be removed? [Y/n]: ")
answer = strip(readline()) |> lowercase
# If removal is confirmed, deleting the relevant files
if isempty(answer) || answer == "yes" || answer == "y"
for index_item in index_items
sif_problem = sif_problems[index_item]
for precision in sif_precision[sif_problem]
suffix = "_$precision.$dlext"
libsif = joinpath(libsif_path, "lib$(sif_problem)$(suffix)")
rm(libsif, force = true)
name_outsdif = _name_outsdif(sif_problem, precision |> Symbol)
outsdif = joinpath(libsif_path, name_outsdif)
rm(outsdif, force = true)
end
end
println("Removed ", length(manage_sif_menu), " problems.")
else
println("Removed 0 problems.")
end
end
end
end