Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 29 additions & 14 deletions src/FileFormats/NL/read.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mutable struct _CacheModel
objective::Expr
sense::MOI.OptimizationSense
complements_map::Dict{Int,Int}
defined_variables::Dict{Int,Expr}

function _CacheModel()
return new(
Expand All @@ -32,6 +33,7 @@ mutable struct _CacheModel
:(),
MOI.FEASIBILITY_SENSE,
Dict{Int,Int}(),
Dict{Int,Expr}(),
)
end
end
Expand Down Expand Up @@ -179,7 +181,7 @@ function _parse_expr(io::IO, model::_CacheModel)
elseif char == 'v'
index = _next(Int, io, model)
_read_til_newline(io, model)
return MOI.VariableIndex(index + 1)
return _to_variable(model, index)
else
@assert char == 'n'
ret = _next(Float64, io, model)
Expand All @@ -188,6 +190,13 @@ function _parse_expr(io::IO, model::_CacheModel)
end
end

function _to_variable(model::_CacheModel, index::Int)
if index >= length(model.variable_primal)
return model.defined_variables[index]
end
return MOI.VariableIndex(index + 1)
end

function _to_model(data::_CacheModel; use_nlp_block::Bool)
model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
x = MOI.add_variables(model, length(data.variable_primal))
Expand Down Expand Up @@ -394,12 +403,7 @@ function _parse_header(io::IO, model::_CacheModel)
# them
_read_til_newline(io, model)
# Line 10
# We don't support reading common subexpressions
for _ in 1:5
if _next(Int, io, model) > 0
error("Unable to parse NL file : we don't support common exprs")
end
end
# We support subexpressions, but we don't need to know the details yet.
_read_til_newline(io, model)
# ==========================================================================
# Deal with the integrality of variables. This is quite complicated, so go
Expand Down Expand Up @@ -478,13 +482,24 @@ function _parse_section(io::IO, ::Val{'S'}, model::_CacheModel)
return
end

function _parse_section(::IO, ::Val{'V'}, ::_CacheModel)
return error(
"Unable to parse NL file: defined variable definitions ('V' sections)" *
" are not yet supported. To request support, please open an issue at " *
"https://github.com/jump-dev/MathOptInterface.jl with a reproducible " *
"example.",
)
function _parse_section(io::IO, ::Val{'V'}, model::_CacheModel)
i = _next(Int, io, model)
j = _next(Int, io, model)
k = _next(Int, io, model)
_read_til_newline(io, model)
affine_terms = Expr(:call, :+)
for l in 1:j
p_l = _to_variable(model, _next(Int, io, model))
c_l = _next(Float64, io, model)
_read_til_newline(io, model)
push!(affine_terms.args, Expr(:call, :*, c_l, p_l))
end
expr = _parse_expr(io, model)
if j > 0
expr = Expr(:call, :+, affine_terms, expr)
end
model.defined_variables[i] = expr
return
end

function _parse_section(::IO, ::Val{'L'}, ::_CacheModel)
Expand Down
93 changes: 47 additions & 46 deletions test/FileFormats/NL/test_read.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ end

function test_parse_expr()
model = NL._CacheModel()
NL._resize_variables(model, 4)
io = IOBuffer()
write(io, "o2\nv0\no2\nn2\no2\nv3\nv1\n")
# (* x1 (* 2 (* x4 x2)))
Expand All @@ -72,6 +73,7 @@ end

function test_parse_expr_nary()
model = NL._CacheModel()
NL._resize_variables(model, 4)
io = IOBuffer()
write(io, "o54\n4\no5\nv0\nn2\no5\nv2\nn2\no5\nv3\nn2\no5\nv1\nn2\n")
seekstart(io)
Expand All @@ -84,6 +86,7 @@ end

function test_parse_expr_minimum()
model = NL._CacheModel()
NL._resize_variables(model, 3)
io = IOBuffer()
write(io, "o11\n3\nv0\nv1\nv2\n")
seekstart(io)
Expand All @@ -95,6 +98,7 @@ end

function test_parse_expr_maximum()
model = NL._CacheModel()
NL._resize_variables(model, 3)
io = IOBuffer()
write(io, "o12\n3\nv0\nv1\nv2\n")
seekstart(io)
Expand All @@ -119,6 +123,7 @@ end

function test_parse_expr_atan2()
model = NL._CacheModel()
NL._resize_variables(model, 2)
io = IOBuffer()
write(io, "o48\nv0\nv1\n")
seekstart(io)
Expand All @@ -130,6 +135,7 @@ end

function test_parse_expr_atan()
model = NL._CacheModel()
NL._resize_variables(model, 1)
io = IOBuffer()
write(io, "o49\nv0\n")
seekstart(io)
Expand Down Expand Up @@ -160,26 +166,6 @@ function test_parse_header_assertion_errors()
return
end

function test_parse_header_common_expressions()
model = NL._CacheModel()
err = ErrorException(
"Unable to parse NL file : we don't support common exprs",
)
for header in [
"g3 1 1 0\n4 2 1 0 1 0\n2 1\n0 0\n4 0 0\n0 0 0 1\n0 0 0 2 0\n8 4\n0 0\n1 0 0 0 0\n",
"g3 1 1 0\n4 2 1 0 1 0\n2 1\n0 0\n4 0 0\n0 0 0 1\n0 0 0 2 0\n8 4\n0 0\n0 1 0 0 0\n",
"g3 1 1 0\n4 2 1 0 1 0\n2 1\n0 0\n4 0 0\n0 0 0 1\n0 0 0 2 0\n8 4\n0 0\n0 0 1 0 0\n",
"g3 1 1 0\n4 2 1 0 1 0\n2 1\n0 0\n4 0 0\n0 0 0 1\n0 0 0 2 0\n8 4\n0 0\n0 0 0 1 0\n",
"g3 1 1 0\n4 2 1 0 1 0\n2 1\n0 0\n4 0 0\n0 0 0 1\n0 0 0 2 0\n8 4\n0 0\n0 0 0 0 1\n",
]
io = IOBuffer()
write(io, header)
seekstart(io)
@test_throws(err, NL._parse_header(io, model))
end
return
end

function test_parse_y_error()
model = NL._CacheModel()
NL._resize_variables(model, 4)
Expand Down Expand Up @@ -378,19 +364,20 @@ end

function test_parse_C_J()
model = NL._CacheModel()
NL._resize_variables(model, 2)
NL._resize_constraints(model, 1)
io = IOBuffer()
write(
io,
"""
C0
o2
v0
v1
J0 2
0 1.1
1 2.2
""",
C0
o2
v0
v1
J0 2
0 1.1
1 2.2
""",
)
seekstart(io)
NL._parse_section(io, model)
Expand All @@ -403,19 +390,20 @@ end

function test_parse_J_C()
model = NL._CacheModel()
NL._resize_variables(model, 2)
NL._resize_constraints(model, 1)
io = IOBuffer()
write(
io,
"""
J0 2
0 1.1
1 2.2
C0
o2
v0
v1
""",
J0 2
0 1.1
1 2.2
C0
o2
v0
v1
""",
)
seekstart(io)
NL._parse_section(io, model)
Expand Down Expand Up @@ -535,18 +523,31 @@ end

function test_parse_V()
model = NL._CacheModel()
NL._resize_variables(model, 9)
io = IOBuffer()
write(io, "V")
seekstart(io)
@test_throws(
ErrorException(
"Unable to parse NL file: defined variable definitions ('V' sections)" *
" are not yet supported. To request support, please open an issue at " *
"https://github.com/jump-dev/MathOptInterface.jl with a reproducible " *
"example.",
),
NL._parse_section(io, model),
write(
io,
"""
V9 0 0 #nl(t[2])
o5 #^
v0 #x[2]
n2
V10 2 0 #t[2]
3 10
4 11
o0 # +
v9 #nl(t[2])
n1
""",
)
seekstart(io)
NL._parse_section(io, model)
NL._parse_section(io, model)
v = MOI.VariableIndex.(1:9)
t1 = :($(v[1]) ^ 2.0)
@test model.defined_variables[9] == t1
@test model.defined_variables[10] ==
:((10.0 * $(v[4]) + 11.0 * $(v[5])) + ($t1 + 1.0))
return
end

Expand Down
Loading