Skip to content

Commit 6dff51f

Browse files
committed
[FileFormats.NL] add support for defined variables
1 parent 9f3cfdb commit 6dff51f

2 files changed

Lines changed: 75 additions & 34 deletions

File tree

src/FileFormats/NL/read.jl

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mutable struct _CacheModel
1717
objective::Expr
1818
sense::MOI.OptimizationSense
1919
complements_map::Dict{Int,Int}
20+
defined_variables::Dict{Int,Expr}
2021

2122
function _CacheModel()
2223
return new(
@@ -32,6 +33,7 @@ mutable struct _CacheModel
3233
:(),
3334
MOI.FEASIBILITY_SENSE,
3435
Dict{Int,Int}(),
36+
Dict{Int,Expr}(),
3537
)
3638
end
3739
end
@@ -179,7 +181,7 @@ function _parse_expr(io::IO, model::_CacheModel)
179181
elseif char == 'v'
180182
index = _next(Int, io, model)
181183
_read_til_newline(io, model)
182-
return MOI.VariableIndex(index + 1)
184+
return _to_variable(model, index)
183185
else
184186
@assert char == 'n'
185187
ret = _next(Float64, io, model)
@@ -188,6 +190,13 @@ function _parse_expr(io::IO, model::_CacheModel)
188190
end
189191
end
190192

193+
function _to_variable(model::_CacheModel, index::Int)
194+
if index >= length(model.variable_primal)
195+
return model.defined_variables[index]
196+
end
197+
return MOI.VariableIndex(index + 1)
198+
end
199+
191200
function _to_model(data::_CacheModel; use_nlp_block::Bool)
192201
model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
193202
x = MOI.add_variables(model, length(data.variable_primal))
@@ -478,13 +487,24 @@ function _parse_section(io::IO, ::Val{'S'}, model::_CacheModel)
478487
return
479488
end
480489

481-
function _parse_section(::IO, ::Val{'V'}, ::_CacheModel)
482-
return error(
483-
"Unable to parse NL file: defined variable definitions ('V' sections)" *
484-
" are not yet supported. To request support, please open an issue at " *
485-
"https://github.com/jump-dev/MathOptInterface.jl with a reproducible " *
486-
"example.",
487-
)
490+
function _parse_section(io::IO, ::Val{'V'}, model::_CacheModel)
491+
i = _next(Int, io, model)
492+
j = _next(Int, io, model)
493+
k = _next(Int, io, model)
494+
_read_til_newline(io, model)
495+
affine_terms = Expr(:call, :+)
496+
for l in 1:j
497+
p_l = _to_variable(model, _next(Int, io, model))
498+
c_l = _next(Float64, io, model)
499+
_read_til_newline(io, model)
500+
push!(affine_terms.args, Expr(:call, :*, c_l, p_l))
501+
end
502+
expr = _parse_expr(io, model)
503+
if j > 0
504+
expr = Expr(:call, :+, affine_terms, expr)
505+
end
506+
model.defined_variables[i] = expr
507+
return
488508
end
489509

490510
function _parse_section(::IO, ::Val{'L'}, ::_CacheModel)

test/FileFormats/NL/test_read.jl

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ end
5959

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

7374
function test_parse_expr_nary()
7475
model = NL._CacheModel()
76+
NL._resize_variables(model, 4)
7577
io = IOBuffer()
7678
write(io, "o54\n4\no5\nv0\nn2\no5\nv2\nn2\no5\nv3\nn2\no5\nv1\nn2\n")
7779
seekstart(io)
@@ -84,6 +86,7 @@ end
8486

8587
function test_parse_expr_minimum()
8688
model = NL._CacheModel()
89+
NL._resize_variables(model, 3)
8790
io = IOBuffer()
8891
write(io, "o11\n3\nv0\nv1\nv2\n")
8992
seekstart(io)
@@ -95,6 +98,7 @@ end
9598

9699
function test_parse_expr_maximum()
97100
model = NL._CacheModel()
101+
NL._resize_variables(model, 3)
98102
io = IOBuffer()
99103
write(io, "o12\n3\nv0\nv1\nv2\n")
100104
seekstart(io)
@@ -119,6 +123,7 @@ end
119123

120124
function test_parse_expr_atan2()
121125
model = NL._CacheModel()
126+
NL._resize_variables(model, 2)
122127
io = IOBuffer()
123128
write(io, "o48\nv0\nv1\n")
124129
seekstart(io)
@@ -130,6 +135,7 @@ end
130135

131136
function test_parse_expr_atan()
132137
model = NL._CacheModel()
138+
NL._resize_variables(model, 1)
133139
io = IOBuffer()
134140
write(io, "o49\nv0\n")
135141
seekstart(io)
@@ -378,19 +384,20 @@ end
378384

379385
function test_parse_C_J()
380386
model = NL._CacheModel()
387+
NL._resize_variables(model, 2)
381388
NL._resize_constraints(model, 1)
382389
io = IOBuffer()
383390
write(
384391
io,
385392
"""
386-
C0
387-
o2
388-
v0
389-
v1
390-
J0 2
391-
0 1.1
392-
1 2.2
393-
""",
393+
C0
394+
o2
395+
v0
396+
v1
397+
J0 2
398+
0 1.1
399+
1 2.2
400+
""",
394401
)
395402
seekstart(io)
396403
NL._parse_section(io, model)
@@ -403,19 +410,20 @@ end
403410

404411
function test_parse_J_C()
405412
model = NL._CacheModel()
413+
NL._resize_variables(model, 2)
406414
NL._resize_constraints(model, 1)
407415
io = IOBuffer()
408416
write(
409417
io,
410418
"""
411-
J0 2
412-
0 1.1
413-
1 2.2
414-
C0
415-
o2
416-
v0
417-
v1
418-
""",
419+
J0 2
420+
0 1.1
421+
1 2.2
422+
C0
423+
o2
424+
v0
425+
v1
426+
""",
419427
)
420428
seekstart(io)
421429
NL._parse_section(io, model)
@@ -535,18 +543,31 @@ end
535543

536544
function test_parse_V()
537545
model = NL._CacheModel()
546+
NL._resize_variables(model, 9)
538547
io = IOBuffer()
539-
write(io, "V")
540-
seekstart(io)
541-
@test_throws(
542-
ErrorException(
543-
"Unable to parse NL file: defined variable definitions ('V' sections)" *
544-
" are not yet supported. To request support, please open an issue at " *
545-
"https://github.com/jump-dev/MathOptInterface.jl with a reproducible " *
546-
"example.",
547-
),
548-
NL._parse_section(io, model),
548+
write(
549+
io,
550+
"""
551+
V9 0 0 #nl(t[2])
552+
o5 #^
553+
v0 #x[2]
554+
n2
555+
V10 2 0 #t[2]
556+
3 10
557+
4 11
558+
o0 # +
559+
v9 #nl(t[2])
560+
n1
561+
""",
549562
)
563+
seekstart(io)
564+
NL._parse_section(io, model)
565+
NL._parse_section(io, model)
566+
v = MOI.VariableIndex.(1:9)
567+
t1 = :($(v[1]) ^ 2.0)
568+
@test model.defined_variables[9] == t1
569+
@test model.defined_variables[10] ==
570+
:((10.0 * $(v[4]) + 11.0 * $(v[5])) + ($t1 + 1.0))
550571
return
551572
end
552573

0 commit comments

Comments
 (0)