Skip to content

Commit

Permalink
[FileFormats.NL] add support for other variable dual suffixes (#2567)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Oct 27, 2024
1 parent ee313be commit f11a616
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 18 deletions.
51 changes: 33 additions & 18 deletions src/FileFormats/NL/sol.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,14 @@ struct SolFileResults <: MOI.ModelLike
end

"""
SolFileResults(filename::String, model::Model)
SolFileResults(
filename::String,
model::Model;
suffix_lower_bound_duals::Vector{String} =
["ipopt_zL_out", "lower_bound_duals"],
suffix_uuper_bound_duals::Vector{String} =
["ipopt_zU_out", "upper_bound_duals"],
)
Parse the `.sol` file `filename` created by solving `model` and return a
`SolFileResults` struct.
Expand All @@ -28,8 +35,8 @@ The returned struct supports the `MOI.get` API for querying result attributes
such as [`MOI.TerminationStatus`](@ref), [`MOI.VariablePrimal`](@ref), and
[`MOI.ConstraintDual`](@ref).
"""
function SolFileResults(filename::String, model::Model)
return open(io -> SolFileResults(io, model), filename, "r")
function SolFileResults(filename::String, model::Model; kwargs...)
return open(io -> SolFileResults(io, model; kwargs...), filename, "r")
end

"""
Expand All @@ -46,7 +53,8 @@ All other attributes are un-set.
"""
function SolFileResults(
raw_status::String,
termination_status::MOI.TerminationStatusCode,
termination_status::MOI.TerminationStatusCode;
kwargs...,
)
return SolFileResults(
nothing,
Expand Down Expand Up @@ -261,7 +269,18 @@ end

_readline(io::IO, T) = parse(T, _readline(io))

function SolFileResults(io::IO, model::Model)
function SolFileResults(
io::IO,
model::Model;
suffix_lower_bound_duals::Vector{String} = [
"ipopt_zL_out",
"lower_bound_duals",
],
suffix_upper_bound_duals::Vector{String} = [
"ipopt_zU_out",
"upper_bound_duals",
],
)
# This function is based on a Julia translation of readsol.c, available at
# https://github.com/ampl/asl/blob/64919f75fa7a438f4b41bce892dcbe2ae38343ee/src/solvers/readsol.c
# and under the following license:
Expand Down Expand Up @@ -345,21 +364,17 @@ function SolFileResults(io::IO, model::Model)
items = split(line, " ")
n_suffix = parse(Int, items[3])
suffix = _readline(io)
if !(suffix == "ipopt_zU_out" || suffix == "ipopt_zL_out")
for _ in 1:n_suffix
_ = readline(io)
end
continue
end
for i in 1:n_suffix
items = split(_readline(io), " ")
x = model.order[parse(Int, items[1])+1]
dual = parse(Float64, items[2])
if suffix == "ipopt_zU_out"
zU_out[x] = dual
if suffix in suffix_upper_bound_duals
items = split(_readline(io), " ")
x = model.order[parse(Int, items[1])+1]
zU_out[x] = parse(Float64, items[2])
elseif suffix in suffix_lower_bound_duals
items = split(_readline(io), " ")
x = model.order[parse(Int, items[1])+1]
zL_out[x] = parse(Float64, items[2])
else
@assert suffix == "ipopt_zL_out"
zL_out[x] = dual
_ = _readline(io)
end
end
end
Expand Down
31 changes: 31 additions & 0 deletions test/FileFormats/NL/data/hs071_uno.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Ipopt 3.14.4: Optimal Solution Found

Options
3
1
1
0
2
2
4
4
0.1787618002239518
0.9850008232874167
1.0999999890876724
1.5735520521364392
2.674683791567438
5.400000053551036
objno 0 0
suffix 4 4 13 0 0
upper_bound_duals
0 -6.26468441989811e-10
1 -6.909508053112211e-10
2 -9.54407535695046e-10
3 -5.582550550861189
suffix 4 4 13 0 0
lower_bound_duals
0 28.590703805487557
1 6.713713035054837e-09
2 1.8232874186951734e-09
3 6.26437836105288e-10
41 changes: 41 additions & 0 deletions test/FileFormats/NL/sol.jl
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,47 @@ function test_sol_hs071_variable_dual()
return
end

function test_sol_uno_hs071_variable_dual()
model, v = _hs071()
for (sign, sense) in [(1, MOI.MIN_SENSE), (-1, MOI.MAX_SENSE)]
MOI.set(model, MOI.ObjectiveSense(), sense)
nl_model = NL.Model()
index_map = MOI.copy_to(nl_model, model)
sol = NL.SolFileResults(
joinpath(@__DIR__, "data", "hs071_uno.sol"),
nl_model,
)
x1 = index_map[v[1]]
F = MOI.VariableIndex
dual = sign * 28.590703805487557
ci = MOI.ConstraintIndex{F,MOI.GreaterThan{Float64}}(x1.value)
@test (MOI.get(sol, MOI.ConstraintDual(), ci), dual, atol = 1e-8)
ci = MOI.ConstraintIndex{F,MOI.LessThan{Float64}}(x1.value)
@test (MOI.get(sol, MOI.ConstraintDual(), ci), 0.0, atol = 1e-8)
ci = MOI.ConstraintIndex{F,MOI.EqualTo{Float64}}(x1.value)
@test (MOI.get(sol, MOI.ConstraintDual(), ci), dual, atol = 1e-8)
ci = MOI.ConstraintIndex{F,MOI.Interval{Float64}}(x1.value)
@test (MOI.get(sol, MOI.ConstraintDual(), ci), dual, atol = 1e-8)
end
return
end

function test_sol_lower_bound_dual_args()
model, v = _hs071()
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
nl_model = NL.Model()
index_map = MOI.copy_to(nl_model, model)
sol = NL.SolFileResults(
joinpath(@__DIR__, "data", "hs071_uno.sol"),
nl_model;
suffix_lower_bound_duals = String[],
suffix_upper_bound_duals = String[],
)
@test isempty(sol.zL_out)
@test isempty(sol.zU_out)
return
end

"""
test_sol_hs071_max_sense()
Expand Down

0 comments on commit f11a616

Please sign in to comment.