Skip to content

[FileFormats.SDPA] fix reading files with {} punctuation #2759

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 19, 2025
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
32 changes: 15 additions & 17 deletions src/FileFormats/SDPA/SDPA.jl
Original file line number Diff line number Diff line change
Expand Up @@ -302,18 +302,15 @@ function _dim_to_set(s::AbstractString)
end
end

function _parse_dimensions(dims::AbstractString)
isvalid(char) = isdigit(char) || char == '-'
is_delimiter(char) = isspace(char) || char == ','
start = findfirst(isvalid, dims)
if start === nothing
return Union{MOI.PositiveSemidefiniteConeTriangle,MOI.Nonnegatives}[]
end
stop = findlast(isvalid, dims)::Int
s = split(dims[start:stop], is_delimiter)
return Union{MOI.PositiveSemidefiniteConeTriangle,MOI.Nonnegatives}[
_dim_to_set(dim) for dim in filter!(!isempty, s)
]
function _split(line)
# In some variations of SDPA, there is the comment:
#
# The special characters `,`, `(`, `)`, `{`, and `}` can be used as
# punctuation and are ignored.
#
# As one example, see https://github.com/vsdp/SDPLIB
line = replace(line, r"[,{}\(\)]"=>' ')
return split(line)
end

"""
Expand Down Expand Up @@ -365,19 +362,20 @@ function Base.read!(io::IO, model::Model{T}) where {T<:Real}
num_variables_read = true
# According to http://plato.asu.edu/ftp/sdpa_format.txt,
# additional text after the number of variables should be ignored.
scalar_vars = MOI.add_variables(model, parse(Int, split(line)[1]))
scalar_vars =
MOI.add_variables(model, parse(Int, first(_split(line))))
elseif num_blocks === nothing
if isempty(line)
continue
end
# According to http://plato.asu.edu/ftp/sdpa_format.txt,
# additional text after the number of blocks should be ignored.
num_blocks = parse(Int, split(line)[1])
num_blocks = parse(Int, first(_split(line)))
elseif !block_sets_read
if isempty(line) && !iszero(num_blocks)
continue
end
block_sets = _parse_dimensions(line)
block_sets = _dim_to_set.(_split(line))
block_sets_read = true
if length(block_sets) != num_blocks
error(
Expand All @@ -399,7 +397,7 @@ function Base.read!(io::IO, model::Model{T}) where {T<:Real}
continue
end
objective_read = true
c = parse.(T, split(line))
c = parse.(T, _split(line))
if length(c) != num_vars
error(
"The number of variables ($num_vars) does not match the length of the list of coefficients for the objective function vector of coefficients ($(length(c))).",
Expand All @@ -416,7 +414,7 @@ function Base.read!(io::IO, model::Model{T}) where {T<:Real}
if isempty(line)
continue
end
values = split(line)
values = _split(line)
if length(values) != 5
error(
"Invalid line specifying entry: $line. There are $(length(values)) values instead of 5.",
Expand Down
25 changes: 9 additions & 16 deletions test/FileFormats/SDPA/SDPA.jl
Original file line number Diff line number Diff line change
Expand Up @@ -323,26 +323,19 @@ function test_examples()
end

# See https://github.com/jump-dev/MathOptInterface.jl/issues/1541
function _spacer(char)
return [" ", "$char", " $char", "$char ", " $char "]
end
_spacer(char) = [" ", "$char", " $char", "$char ", " $char "]

function test_dim_reader()
for before in _spacer('{')
for sep in _spacer(',')
for after in _spacer('}')
line = string(before, "-4", sep, "2", after)
exp = [
MOI.Nonnegatives(4),
MOI.PositiveSemidefiniteConeTriangle(2),
]
@test MOI.FileFormats.SDPA._parse_dimensions(line) == exp
line = string(before, "2", sep, "-4", after)
@test MOI.FileFormats.SDPA._parse_dimensions(line) ==
exp[2:-1:1]
end
fn(line) = SDPA._dim_to_set.(SDPA._split(line))
for (a, b) in ['{' => '}', '(' => ')']
for pre in _spacer(a), sep in _spacer(','), suf in _spacer(b)
@test fn("$(pre)-4$(sep)2$suf") ==
[MOI.Nonnegatives(4), MOI.PositiveSemidefiniteConeTriangle(2)]
@test fn("$(pre)2$(sep)-4$suf") ==
[MOI.PositiveSemidefiniteConeTriangle(2), MOI.Nonnegatives(4)]
end
end
return
end

function test_integer_before_variables()
Expand Down
2 changes: 1 addition & 1 deletion test/FileFormats/SDPA/models/issue_1541.dat-s
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
3 =mdim
2 =nblocks
{-4, 2}
-0.0 -1 -2
{-0.0,-1e+0, -2}
0 1 1 1 -1
1 1 1 1 -1
1 1 2 2 1
Expand Down
Loading