Skip to content
Open
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: 22 additions & 10 deletions src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike)
return MOI.Utilities.default_copy_to(dest, src)
end

function MOI.optimize!(model::Optimizer)
function Base.convert(::Type{Model}, model::Optimizer)
CAent = vcat(model.Cent, model.Aent)
CArow = vcat(model.Crow, model.Arow)
CAcol = vcat(model.Ccol, model.Acol)
Expand All @@ -299,6 +299,25 @@ function MOI.optimize!(model::Optimizer)
push!(CAinfo_entptr, length(CAent))
CAinfo_type =
reduce(vcat, vcat([model.Cinfo_type], model.Ainfo_type), init = Cchar[])
return Model(;
blksz = model.blksz,
blktype = model.blktype,
b = model.b,
CAent,
CArow,
CAcol,
CAinfo_entptr,
CAinfo_type,
)
end

function MOI.write_to_file(model::Optimizer, filename::String)
write_sdplr(convert(Model, model), filename)
return
end

function MOI.optimize!(model::Optimizer)
raw_model = convert(Model, model)
params = deepcopy(model.params)
if model.silent
params.printlevel = 0
Expand All @@ -308,7 +327,7 @@ function MOI.optimize!(model::Optimizer)
model.params.maxrank,
model.blktype,
model.blksz,
CAinfo_entptr,
raw_model.CAinfo_entptr,
length(model.b),
)
model.ranks = copy(model.maxranks)
Expand All @@ -321,14 +340,7 @@ function MOI.optimize!(model::Optimizer)
end
end
_, model.R, model.lambda, model.ranks, model.pieces = solve(
model.blksz,
model.blktype,
model.b,
CAent,
CArow,
CAcol,
CAinfo_entptr,
CAinfo_type,
raw_model,
params = params,
maxranks = model.maxranks,
ranks = model.ranks,
Expand Down
153 changes: 103 additions & 50 deletions src/SDPLR.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,91 @@ end
# See `macros.h`
datablockind(data, block, numblock) = data * numblock + block

@kwdef struct Model
blksz::Vector{Cptrdiff_t}
blktype::Vector{Cchar}
b::Vector{Cdouble}
CAent::Vector{Cdouble}
CArow::Vector{Csize_t}
CAcol::Vector{Csize_t}
CAinfo_entptr::Vector{Csize_t}
CAinfo_type::Vector{Cchar}
end

function checkdims(model::Model)
numblk = length(model.blksz)
@assert length(model.blktype) == numblk
m = length(model.b)
@assert length(model.CAinfo_entptr) == (m + 1) * numblk + 1
@assert length(model.CAinfo_type) == (m + 1) * numblk
@assert length(model.CAent) == length(model.CArow) == length(model.CAcol)
@assert model.CAinfo_entptr[1] == 0
@assert model.CAinfo_entptr[end] == length(model.CArow)
k = 0
for _ in eachindex(model.b)
for blk in eachindex(model.blksz)
k += 1
@assert model.CAinfo_entptr[k] <= model.CAinfo_entptr[k+1]
for j in ((model.CAinfo_entptr[k]+1):model.CAinfo_entptr[k+1])
@assert 1 <= model.CArow[j] <= model.blksz[blk]
@assert 1 <= model.CAcol[j] <= model.blksz[blk]
if model.CAinfo_type[k] == Cchar('s')
@assert model.blktype[blk] == Cchar('s') || model.blktype[blk] == Cchar('d')
@assert model.CArow[j] <= model.CAcol[j]
elseif model.CAinfo_type[k] == Cchar('d')
@assert model.blktype[blk] == Cchar('d')
@assert model.CArow[j] == model.CAcol[j]
else
@assert model.CAinfo_type[k] == Cchar('l')
@assert model.blktype[blk] == Cchar('s')
end
end
end
end
return m, numblk
end

function write_sdplr(model::Model, filename::String)
m, numblk = checkdims(model)
open(filename, "w") do io
println(io, m)
println(io, numblk)
println(io, join(model.blksz .* map(t -> t == 'd' ? -1 : 1, model.blktype), ' '))
println(io, join(model.b, ' '))
println(io, -1) # Currently ignored
for constraint in 0:m
for blk in eachindex(model.blksz)
print(io, constraint)
print(io, ' ')
print(io, blk)
print(io, ' ')
cur = numblk * constraint + blk
t = model.CAinfo_type[cur]
range = 1 .+ (model.CAinfo_entptr[cur]:(model.CAinfo_entptr[cur + 1] - 1))
if t == 'l'
print(io, 'l')
print(io, ' ')
print(io, div(length(range), model.blksz[blk] + 1))
for i in range
println(io, model.CAent[i])
end
else
print(io, 's')
print(io, ' ')
print(io, length(range))
for i in range
print(io, model.CArow[i])
print(io, ' ')
print(io, model.CAcol[i])
print(io, ' ')
println(io, model.CAent[i])
end
end
end
end
end
end

function default_R(blktype::Vector{Cchar}, blksz, maxranks)
# See `getstorage` in `main.c`
Rsizes = map(eachindex(blktype)) do k
Expand Down Expand Up @@ -99,69 +184,37 @@ is `Cchar('d')`.
The `CA...` arguments specify the `C` and `A_i` matrices.
"""
function solve(
blksz::Vector{Cptrdiff_t},
blktype::Vector{Cchar},
b::Vector{Cdouble},
CAent::Vector{Cdouble},
CArow::Vector{Csize_t},
CAcol::Vector{Csize_t},
CAinfo_entptr::Vector{Csize_t},
CAinfo_type::Vector{Cchar};
model::Model;
params::Parameters = Parameters(),
maxranks::Vector{Csize_t} = default_maxranks(
params.maxrank,
blktype,
blksz,
CAinfo_entptr,
length(b),
model.blktype,
model.blksz,
model.CAinfo_entptr,
length(model.b),
),
ranks::Vector{Csize_t} = copy(maxranks),
R::Vector{Cdouble} = default_R(blktype, blksz, maxranks)[2],
lambda::Vector{Cdouble} = zeros(Cdouble, length(b)),
pieces::Vector{Cdouble} = default_pieces(blksz),
R::Vector{Cdouble} = default_R(model.blktype, model.blksz, maxranks)[2],
lambda::Vector{Cdouble} = zeros(Cdouble, length(model.b)),
pieces::Vector{Cdouble} = default_pieces(model.blksz),
)
numblk = length(blksz)
@assert length(blktype) == numblk
m = length(b)
@assert length(CAinfo_entptr) == (m + 1) * numblk + 1
@assert length(CAinfo_type) == (m + 1) * numblk
@assert length(CAent) == length(CArow) == length(CAcol)
m, numblk = checkdims(model)
@assert length(lambda) == m
@assert length(maxranks) == numblk
@assert length(ranks) == numblk
@assert length(pieces) == 8
@assert CAinfo_entptr[1] == 0
@assert CAinfo_entptr[end] == length(CArow)
k = 0
for _ in eachindex(b)
for blk in eachindex(blksz)
k += 1
@assert CAinfo_entptr[k] <= CAinfo_entptr[k+1]
for j in ((CAinfo_entptr[k]+1):CAinfo_entptr[k+1])
@assert blktype[blk] == CAinfo_type[k]
@assert 1 <= CArow[j] <= blksz[blk]
@assert 1 <= CAcol[j] <= blksz[blk]
if CAinfo_type[k] == Cchar('s')
@assert CArow[j] <= CAcol[j]
else
@assert CAinfo_type[k] == Cchar('d')
@assert CArow[j] == CAcol[j]
end
end
end
end
GC.@preserve blksz blktype b CAent CArow CAcol CAinfo_entptr CAinfo_type R lambda maxranks ranks pieces begin
GC.@preserve model R lambda maxranks ranks pieces begin
ret = @ccall SDPLR_jll.libsdplr.sdplrlib(
m::Csize_t,
numblk::Csize_t,
blksz::Ptr{Cptrdiff_t},
blktype::Ptr{Cchar},
b::Ptr{Cdouble},
CAent::Ptr{Cdouble},
CArow::Ptr{Csize_t},
CAcol::Ptr{Csize_t},
CAinfo_entptr::Ptr{Csize_t},
CAinfo_type::Ptr{Cchar},
model.blksz::Ptr{Cptrdiff_t},
model.blktype::Ptr{Cchar},
model.b::Ptr{Cdouble},
model.CAent::Ptr{Cdouble},
model.CArow::Ptr{Csize_t},
model.CAcol::Ptr{Csize_t},
model.CAinfo_entptr::Ptr{Csize_t},
model.CAinfo_type::Ptr{Cchar},
params.numbfgsvecs::Csize_t,
params.rho_f::Cdouble,
params.rho_c::Cdouble,
Expand Down
26 changes: 26 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,32 @@ function test_solve_vibra_with_sdplrlib()
@test ranks == Csize_t[9, 9, 1]
end

# Test of LowRankOpt's test `test_conic_PositiveSemidefinite_RankOne_polynomial` in low-level SDPLR version
function test_solve_conic_PositiveSemidefinite_RankOne_polynomial()
model = SDPLR.Model(
blksz = [2, 2],
blktype = Cchar['d', 's'],
b = Cdouble[-3, 1],
CAent = Cdouble[-1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1],
CArow = UInt64[1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 1, 2],
CAcol = UInt64[1, 2, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1],
CAinfo_entptr = UInt64[0, 2, 2, 4, 7, 9, 12],
CAinfo_type = Cchar['s', 's', 's', 'l', 's', 'l'],
)
# The `925` seed is taken from SDPLR's `main.c`
Random.seed!(925)
SDPLR.write_sdplr(model, "lowrank_RankOne_poly.sdplr")
ret, R, lambda, ranks, pieces = SDPLR.solve(
model,
params = SDPLR.Parameters(printlevel = 4),
)
@test iszero(ret)
@show R
@show lambda
@show pieces
@show ranks
end

function runtests()
for name in names(@__MODULE__; all = true)
if startswith("$(name)", "test_")
Expand Down
Loading