Skip to content

Commit 74d7443

Browse files
authored
Merge pull request #1079 from kibaekkim/dev/sdpa-int
Feature to read/write integer constraints
2 parents 30087ba + 56bdb0f commit 74d7443

File tree

3 files changed

+73
-4
lines changed

3 files changed

+73
-4
lines changed

src/FileFormats/SDPA/SDPA.jl

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ function MOI.supports_constraint(
2626

2727
return false
2828
end
29+
function MOI.supports_constraint(
30+
::Model, ::Type{MOI.SingleVariable}, ::Type{MOI.Integer})
31+
return true
32+
end
2933

3034
function MOI.supports(
3135
::Model,
@@ -202,6 +206,16 @@ function Base.write(io::IO, model::Model{T}) where {T}
202206
for i in eachindex(psd)
203207
_print_constraint(length(nonneg) + i, true, psd[i])
204208
end
209+
210+
# Integrality constraints.
211+
# Based on the extension: http://www.opt.tu-darmstadt.de/scipsdp/downloads/data_format.txt
212+
integer_cons = model_cons(MOI.SingleVariable, MOI.Integer)
213+
if length(integer_cons) > 0
214+
println(io, "*INTEGER")
215+
for con_idx in integer_cons
216+
println(io, "*$(con_function(con_idx).variable.value)")
217+
end
218+
end
205219
return
206220
end
207221

@@ -242,6 +256,9 @@ function Base.read!(io::IO, model::Model{T}) where T
242256
end
243257
end
244258
objective_read = false
259+
integer_read = false
260+
scalar_vars = nothing
261+
intvar_idx = Int[]
245262
c = nothing
246263
funcs = nothing
247264
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
@@ -251,14 +268,28 @@ function Base.read!(io::IO, model::Model{T}) where T
251268
if startswith(line, '"')
252269
continue
253270
end
271+
# The lines starting with * should also be skipped
272+
# according to http://plato.asu.edu/ftp/sdpa_format.txt.
273+
if startswith(line, '*')
274+
# Exceptions for integer variables
275+
if startswith(line, "*INTEGER")
276+
integer_read = true
277+
elseif integer_read
278+
if !num_variables_read
279+
error("The number of variables should be given before *INTEGER section.")
280+
end
281+
push!(intvar_idx, parse(Int, strip(line[2:end])))
282+
end
283+
continue
284+
end
254285
if !num_variables_read
255286
if isempty(line)
256287
continue
257288
end
258289
num_variables_read = true
259290
# According to http://plato.asu.edu/ftp/sdpa_format.txt,
260291
# additional text after the number of variables should be ignored.
261-
MOI.add_variables(model, parse(Int, split(line)[1]))
292+
scalar_vars = MOI.add_variables(model, parse(Int, split(line)[1]))
262293
elseif num_blocks === nothing
263294
if isempty(line)
264295
continue
@@ -288,7 +319,7 @@ function Base.read!(io::IO, model::Model{T}) where T
288319
obj = zero(MOI.ScalarAffineFunction{T})
289320
for i in eachindex(c)
290321
if !iszero(c[i])
291-
push!(obj.terms, MOI.ScalarAffineTerm(c[i], MOI.VariableIndex(i)))
322+
push!(obj.terms, MOI.ScalarAffineTerm(c[i], scalar_vars[i]))
292323
end
293324
end
294325
MOI.set(model, MOI.ObjectiveFunction{typeof(obj)}(), obj)
@@ -320,14 +351,17 @@ function Base.read!(io::IO, model::Model{T}) where T
320351
else
321352
if !iszero(coef)
322353
push!(funcs[block].terms, MOI.VectorAffineTerm(k,
323-
MOI.ScalarAffineTerm(coef, MOI.VariableIndex(matrix))))
354+
MOI.ScalarAffineTerm(coef, scalar_vars[matrix])))
324355
end
325356
end
326357
end
327358
end
328359
for block in 1:num_blocks
329360
MOI.add_constraint(model, funcs[block], block_sets[block])
330361
end
362+
for var_idx in intvar_idx
363+
MOI.add_constraint(model, MOI.SingleVariable(scalar_vars[var_idx]), MOI.Integer())
364+
end
331365
return
332366
end
333367

test/FileFormats/SDPA/SDPA.jl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ function set_var_and_con_names(model::MOI.ModelLike)
1818
idx = 0
1919
constraint_names = String[]
2020
for i in Iterators.flatten((
21+
MOI.get(model, MOI.ListOfConstraintIndices{MOI.SingleVariable, MOI.Integer}()),
2122
MOI.get(model, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives}()),
2223
MOI.get(model, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.PositiveSemidefiniteConeTriangle}())))
2324
idx += 1
@@ -70,7 +71,6 @@ end
7071
MOI.Interval(1.0, 2.0),
7172
MOI.Semiinteger(1.0, 2.0),
7273
MOI.Semicontinuous(1.0, 2.0),
73-
MOI.Integer(),
7474
MOI.ZeroOne()
7575
]
7676
model_string = """
@@ -201,6 +201,16 @@ example_models = [
201201
minobjective: 1x
202202
c1: [0, 1x + -1, 0] in PositiveSemidefiniteConeTriangle(2)
203203
"""),
204+
("example_integer.sdpa", """
205+
variables: x, y, z
206+
minobjective: 1x + -2y + -1z
207+
c1: [1x, 1y, 1z] in PositiveSemidefiniteConeTriangle(2)
208+
c2: [1z, 1x, 2.1] in PositiveSemidefiniteConeTriangle(2)
209+
c3: [1x + 1y + 1z + -1, -1x + -1y + -1z + 8] in Nonnegatives(2)
210+
c4: x in Integer()
211+
c5: y in Integer()
212+
c6: z in Integer()
213+
"""),
204214
]
205215
@testset "Read and write/read $model_name" for (model_name, model_string) in example_models
206216
test_read(joinpath(SDPA_MODELS_DIR, model_name), model_string)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
* Example from http://www.opt.tu-darmstadt.de/scipsdp/downloads/data_format.txt
2+
3
3+
3
4+
2 2 -2
5+
* the next line gives the objective values in the order of the variables
6+
1 -2 -1
7+
* the remaining lines give the nonzeroes of the constraints with variable (0 meaning the constant part) block row column value
8+
1 1 1 1 1
9+
2 1 1 2 1
10+
3 1 2 2 1
11+
1 2 1 2 1
12+
3 2 1 1 1
13+
0 2 2 2 -2.1
14+
1 3 1 1 1
15+
2 3 1 1 1
16+
3 3 1 1 1
17+
0 3 1 1 1
18+
1 3 2 2 -1
19+
2 3 2 2 -1
20+
3 3 2 2 -1
21+
0 3 2 2 -8
22+
*INTEGER
23+
*1
24+
*2
25+
*3

0 commit comments

Comments
 (0)