Skip to content

Commit 9019dae

Browse files
authored
[FileFormats.LP] fix new lines in middle of quadratic objective (#2184)
1 parent 7513db6 commit 9019dae

File tree

2 files changed

+74
-2
lines changed

2 files changed

+74
-2
lines changed

src/FileFormats/LP/LP.jl

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,9 @@ function Base.read!(io::IO, model::Model)
956956
_set_objective_sense(section, model, lower_line)
957957
continue
958958
end
959+
while _line_continues(section, peeked_line)
960+
line, peeked_line = _readline(io, string(line, ' ', peeked_line))
961+
end
959962
_parse_section(section, model, cache, line)
960963
end
961964
obj = if isempty(cache.quad_obj_terms)
@@ -971,18 +974,32 @@ function Base.read!(io::IO, model::Model)
971974
return
972975
end
973976

977+
function _line_continues(
978+
::Union{typeof(_KW_OBJECTIVE),typeof(_KW_CONSTRAINTS)},
979+
peeked_line::AbstractString,
980+
)
981+
return any(Base.Fix1(startswith, peeked_line), ('+', '-'))
982+
end
983+
984+
_line_continues(::Any, ::Any) = false
985+
974986
function _readline(io::IO, line::AbstractString)
975987
if eof(io)
976988
return line, nothing
977989
end
978990
peeked_line = _strip_comment(string(readline(io)))
979991
if isempty(line)
992+
# If the line is empty, go to the next
980993
return _readline(io, peeked_line)
981994
elseif isempty(peeked_line)
995+
# If the peeked line is empty, get another
982996
return _readline(io, line)
983-
elseif any(c -> endswith(line, c), ('+', '-', '[', '='))
997+
elseif any(Base.Fix1(endswith, line), ('+', '-', '[', '='))
998+
# If the line ends with a continuation character, read in the next line.
984999
return _readline(io, string(line, ' ', peeked_line))
985-
elseif startswith(peeked_line, ']') || startswith(peeked_line, '/')
1000+
elseif any(Base.Fix1(startswith, peeked_line), (']', '/'))
1001+
# Always read in the next line if it starts with ] or /, which are used
1002+
# in quadratic functions.
9861003
return _readline(io, string(line, ' ', peeked_line))
9871004
end
9881005
return line, peeked_line

test/FileFormats/LP/LP.jl

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,61 @@ function test_read_quadratic()
884884
"1 x + 1 y + [ 2 x * y + 2 y ^ 2 ]",
885885
"1 x + 1 y + [ 4 x * y + 4 y ^ 2 ]/2",
886886
)
887+
_test_read_quadratic(
888+
"[ 1.0 x^2 \n+ 1.0 x * y\n+ 1.0 y * y ]/2",
889+
"[ 1 x ^ 2 + 1 x * y + 1 y ^ 2 ]/2",
890+
)
891+
_test_read_quadratic(
892+
"[ 1.0 x^2 \n- 1.0 x * y\n+ 1.0 y * y ]/2",
893+
"[ 1 x ^ 2 - 1 x * y + 1 y ^ 2 ]/2",
894+
)
895+
_test_read_quadratic(
896+
"[ 1.0 x^2 -\n 1.0 x * y +\n1.0 y * y ]/2",
897+
"[ 1 x ^ 2 - 1 x * y + 1 y ^ 2 ]/2",
898+
)
899+
_test_read_quadratic(
900+
"[ 1.0 x^2 + 1.0 x * y\n+ 1.0 y * y ]/2",
901+
"[ 1 x ^ 2 + 1 x * y + 1 y ^ 2 ]/2",
902+
)
903+
return
904+
end
905+
906+
function test_read_newline_breaks()
907+
io = IOBuffer()
908+
input_text = """
909+
minimize
910+
obj: 1 x + 2 y
911+
+ 3 z
912+
subject to
913+
c: x -
914+
y
915+
+ z ==
916+
0
917+
Bounds
918+
x >= 0
919+
-1 <= y
920+
+1 <= z <= +2
921+
End
922+
"""
923+
write(io, input_text)
924+
model = MOI.FileFormats.LP.Model()
925+
seekstart(io)
926+
read!(io, model)
927+
out = IOBuffer()
928+
write(out, model)
929+
seekstart(out)
930+
output_text = """
931+
minimize
932+
obj: 1 x + 2 y + 3 z
933+
subject to
934+
c: 1 x - 1 y + 1 z = 0
935+
Bounds
936+
x >= 0
937+
y >= -1
938+
1 <= z <= 2
939+
End
940+
"""
941+
@test read(out, String) == output_text
887942
return
888943
end
889944

0 commit comments

Comments
 (0)