From 7610b5153e77f28ee36f02bb4c61992260d419c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 30 May 2019 19:36:18 +0200 Subject: [PATCH 1/9] Update to MOI v0.9 --- .gitignore | 1 + Project.toml | 28 ++++++++++++++ REQUIRE | 8 ---- src/CSDP.jl | 5 +-- src/MOI_wrapper.jl | 89 ++++++++++++++++++++++++++++++++++++--------- src/MPB_wrapper.jl | 9 +++++ src/blockmat.h.jl | 2 +- src/blockmat.jl | 14 +++---- src/options.jl | 13 +------ test/MOI_wrapper.jl | 48 +++++++++++++++++------- test/MPB_wrapper.jl | 8 +--- test/check_blas.jl | 8 ++-- test/runtests.jl | 12 +++--- 13 files changed, 166 insertions(+), 79 deletions(-) create mode 100644 Project.toml delete mode 100644 REQUIRE diff --git a/.gitignore b/.gitignore index 65c8175..427ce98 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +Manifest.toml *.jl.cov *.jl.*.cov *.jl.mem diff --git a/Project.toml b/Project.toml new file mode 100644 index 0000000..44b2bed --- /dev/null +++ b/Project.toml @@ -0,0 +1,28 @@ +name = "CSDP" +uuid = "0a46da34-8e4b-519e-b418-48813639ff34" +repo = "https://github.com/JuliaOpt/CSDP.jl.git" +version = "0.5.0" + +[deps] +BinDeps = "9e28174c-4ba2-5203-b857-d8d62c4213ee" +Glob = "c27321d9-0574-5035-807b-f59d2c89b15c" +Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" +MathProgBase = "fdba3010-5040-5b88-9595-932c9decdf73" +SemidefiniteModels = "169818f4-1a3d-53bf-95b3-11177825b1e3" +SemidefiniteOptInterface = "f0680fed-b2cd-5302-98f9-f4da282d86b5" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[compat] +MathOptInterface = "0.9" +MathProgBase = "~0.7.0" +SemidefiniteModels = "~0.1.1" +SemidefiniteOptInterface = "0.6" +julia = "1" + +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Test"] diff --git a/REQUIRE b/REQUIRE deleted file mode 100644 index effa3b9..0000000 --- a/REQUIRE +++ /dev/null @@ -1,8 +0,0 @@ -julia 0.6 -BinDeps -Glob -MathOptInterface 0.8 0.9 -SemidefiniteOptInterface 0.5 0.6 -MathProgBase 0.7 0.8 -SemidefiniteModels 0.1.1 0.2 -Compat 1.0 diff --git a/src/CSDP.jl b/src/CSDP.jl index 4bb78d3..d32a02d 100644 --- a/src/CSDP.jl +++ b/src/CSDP.jl @@ -1,8 +1,7 @@ module CSDP -using Compat -using Compat.LinearAlgebra # For Diagonal -using Compat.SparseArrays # For SparseMatrixCSC +using LinearAlgebra # For Diagonal +using SparseArrays # For SparseMatrixCSC # Try to load the binary dependency if isfile(joinpath(dirname(@__FILE__),"..","deps","deps.jl")) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 60cb75a..7c81878 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -14,15 +14,61 @@ mutable struct SDOptimizer <: SDOI.AbstractSDOptimizer status::Cint pobj::Cdouble dobj::Cdouble - options::Dict{Symbol,Any} + solve_time::Float64 + silent::Bool + options::Dict{Symbol, Any} function SDOptimizer(; kwargs...) - new(nothing, nothing, nothing, nothing, nothing, nothing, - -1, 0.0, 0.0, checkoptions(Dict{Symbol, Any}(kwargs))) + optimizer = new(nothing, nothing, nothing, nothing, nothing, nothing, + -1, NaN, NaN, NaN, false, Dict{Symbol, Any}()) + for (key, value) in kwargs + MOI.set(optimizer, MOI.RawParameter(key), value) + end + return optimizer end end Optimizer(; kws...) = SDOI.SDOIOptimizer(SDOptimizer(; kws...)) +function MOI.supports(optimizer::SDOptimizer, param::MOI.RawParameter) + return param.name in ALLOWED_OPTIONS +end +function MOI.set(optimizer::SDOptimizer, param::MOI.RawParameter, value) + if !MOI.supports(optimizer, param) + throw(MOI.UnsupportedAttribute(param)) + end + optimizer.options[param.name] = value +end +function MOI.get(optimizer::SDOptimizer, param::MOI.RawParameter) + # TODO: This gives a poor error message if the name of the parameter is invalid. + return optimizer.options[param.name] +end + +MOI.supports(::SDOptimizer, ::MOI.Silent) = true +function MOI.set(optimizer::SDOptimizer, ::MOI.Silent, value::Bool) + optimizer.silent = value +end +MOI.get(optimizer::SDOptimizer, ::MOI.Silent) = optimizer.silent + MOI.get(::SDOptimizer, ::MOI.SolverName) = "CSDP" +# See table "Return codes for easy_sdp() and CSDP" in `doc/csdpuser.pdf`. +const RAW_STATUS = [ + "Problem solved to optimality.", + "Problem is primal infeasible.", + "Problem is dual infeasible.", + "Problem solved to near optimality.", + "Maximum iterations reached.", + "Stuck at edge of primal feasibility.", + "Stuck at edge of dual feasibility.", + "Lack of progress.", + "X, Z, or O is singular.", + "NaN or Inf values encountered.", + "Program stopped by signal (SIXCPU, SIGTERM, etc.)"] + +function MOI.get(optimizer::SDOptimizer, ::MOI.RawStatusString) + return RAW_STATUS[optimizer.status + 1] +end +function MOI.get(optimizer::SDOptimizer, ::MOI.SolveTime) + return optimizer.solve_time +end function MOI.empty!(optimizer::SDOptimizer) optimizer.C = nothing @@ -67,15 +113,24 @@ function SDOI.setobjectivecoefficient!(m::SDOptimizer, coef, blk::Integer, i::In SDOI.block(m.C, blk)[i, j] = coef end -function MOI.optimize!(m::SDOptimizer) - As = map(A->A.csdp, m.As) +function MOI.optimize!(optimizer::SDOptimizer) + As = map(A->A.csdp, optimizer.As) - write_prob(m) + write_prob(optimizer) + + start_time = time() + optimizer.X, optimizer.y, optimizer.Z = initsoln(optimizer.C, optimizer.b, As) + + options = optimizer.options + if optimizer.silent + options = copy(options) + options[:printlevel] = 0 + end - m.X, m.y, m.Z = initsoln(m.C, m.b, As) - #verbose = get(m.options, :verbose, true) - #m.status, m.pobj, m.dobj = easy_sdp(m.C, m.b, As, m.X, m.y, m.Z, verbose) - m.status, m.pobj, m.dobj = sdp(m.C, m.b, m.As, m.X, m.y, m.Z, m.options) + optimizer.status, optimizer.pobj, optimizer.dobj = sdp( + optimizer.C, optimizer.b, optimizer.As, optimizer.X, optimizer.y, + optimizer.Z, options) + optimizer.solve_time = time() - start_time end function MOI.get(m::SDOptimizer, ::MOI.TerminationStatus) @@ -135,18 +190,18 @@ function MOI.get(m::SDOptimizer, ::MOI.DualStatus) end end -function SDOI.getprimalobjectivevalue(m::SDOptimizer) - m.pobj +function MOI.get(m::SDOptimizer, ::MOI.ObjectiveValue) + return m.pobj end -function SDOI.getdualobjectivevalue(m::SDOptimizer) - m.dobj +function MOI.get(m::SDOptimizer, ::MOI.DualObjectiveValue) + return m.dobj end function SDOI.getX(m::SDOptimizer) - m.X + return m.X end function SDOI.gety(m::SDOptimizer) - m.y + return m.y end function SDOI.getZ(m::SDOptimizer) - m.Z + return m.Z end diff --git a/src/MPB_wrapper.jl b/src/MPB_wrapper.jl index b0012a2..909d332 100644 --- a/src/MPB_wrapper.jl +++ b/src/MPB_wrapper.jl @@ -5,6 +5,15 @@ const SDM = SemidefiniteModels export CSDPMathProgModel, CSDPSolver +function checkoptions(d::Dict{Symbol, Any}) + for key in keys(d) + if !(key in ALLOWED_OPTIONS) + error("Option $key is not not a valid CSDP option. The valid options are $ALLOWED_OPTIONS.") + end + end + return d +end + struct CSDPSolver <: MPB.AbstractMathProgSolver options::Dict{Symbol,Any} end diff --git a/src/blockmat.h.jl b/src/blockmat.h.jl index c0799e8..415640b 100644 --- a/src/blockmat.h.jl +++ b/src/blockmat.h.jl @@ -3,7 +3,7 @@ # TODO detect size and use bitstype because if the DLL changes and gets # compiled with NOSHORTS we are screwed with the following code... -@static if Compat.Sys.iswindows() +@static if Sys.iswindows() const csdpshort = Cushort else const csdpshort = Cint diff --git a/src/blockmat.jl b/src/blockmat.jl index 19ea202..1aa85fc 100644 --- a/src/blockmat.jl +++ b/src/blockmat.jl @@ -15,16 +15,12 @@ end export fptr, ptr function mywrap(X::blockmatrix) - @compat finalizer(free_blockmatrix, X) + finalizer(free_blockmatrix, X) BlockMatrix(X) end function _unsafe_wrap(A, x, n, own::Bool) - @static if VERSION >= v"0.7-" - Base.unsafe_wrap(A, x, n, own=own) - else - Base.unsafe_wrap(A, x, n, own) - end + Base.unsafe_wrap(A, x, n, own=own) end function mywrap(x::Ptr{T}, len) where T @@ -32,7 +28,7 @@ function mywrap(x::Ptr{T}, len) where T # because the pointer it has has an offset y = _unsafe_wrap(Array, x + sizeof(T), len, false) # fptr takes care of this offset - @compat finalizer(s -> Libc.free(fptr(s)), y) + finalizer(s -> Libc.free(fptr(s)), y) y end @@ -72,7 +68,7 @@ function BlockRec(a::Vector{Cdouble}, n::Int) end BlockRec(a::Vector, n::Int) = BlockRec(Vector{Cdouble}(a), n) function BlockRec(A::Matrix) - return BlockRec(reshape(A, length(A)), Compat.LinearAlgebra.checksquare(A)) + return BlockRec(reshape(A, length(A)), LinearAlgebra.checksquare(A)) end function BlockRec(A::Diagonal) a = Vector{Cdouble}(diag(A)) @@ -205,7 +201,7 @@ end SparseBlock(A::SparseBlock) = A function SparseBlock(A::SparseMatrixCSC{Cdouble}) - n = Compat.LinearAlgebra.checksquare(A) + n = LinearAlgebra.checksquare(A) nn = nnz(A) I = csdpshort[] J = csdpshort[] diff --git a/src/options.jl b/src/options.jl index d411b0f..a20d454 100644 --- a/src/options.jl +++ b/src/options.jl @@ -1,4 +1,4 @@ -const allowed_options = [:printlevel, :axtol, :atytol, :objtol, :pinftol, :dinftol, :maxiter, :minstepfrac, :maxstepfrac, :minstepp, :minstepd, :usexzgap, :tweakgap, :affine, :perturbobj, :fastmode, :write_prob] +const ALLOWED_OPTIONS = [:printlevel, :axtol, :atytol, :objtol, :pinftol, :dinftol, :maxiter, :minstepfrac, :maxstepfrac, :minstepp, :minstepd, :usexzgap, :tweakgap, :affine, :perturbobj, :fastmode, :write_prob] # The :write_prob option is for the following function function write_prob(m) @@ -10,19 +10,10 @@ function write_prob(m) wrtf = "$wrt.$k" k += 1 end - Compat.@info "Writing problem to $(pwd())/$(wrtf)" + @info "Writing problem to $(pwd())/$(wrtf)" write_prob(wrtf, m.C, m.b, map(A->A.csdp, m.As)) end end end -function checkoptions(d::Dict{Symbol, Any}) - for key in keys(d) - if !(key in allowed_options) - error("Option $key is not not a valid CSDP option. The valid options are $allowed_options.") - end - end - d -end - options(params::Dict{Symbol}) = get(params, :printlevel, 1), paramstruc(params) diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index 6e605af..fc80dff 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -5,33 +5,55 @@ const MOIB = MOI.Bridges const MOIU = MOI.Utilities MOIU.@model(SDModelData, - (), - (MOI.EqualTo, MOI.GreaterThan, MOI.LessThan), + (), (MOI.EqualTo, MOI.GreaterThan, MOI.LessThan), (MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives, - MOI.PositiveSemidefiniteConeTriangle), - (), - (MOI.SingleVariable,), - (MOI.ScalarAffineFunction,), - (MOI.VectorOfVariables,), - (MOI.VectorAffineFunction,)) + MOI.PositiveSemidefiniteConeTriangle), (), + (), (MOI.ScalarAffineFunction,), (MOI.VectorOfVariables,), ()) -const optimizer = MOIU.CachingOptimizer(SDModelData{Float64}(), CSDP.Optimizer(printlevel=0)) +const optimizer = CSDP.Optimizer() +MOI.set(optimizer, MOI.Silent(), true) +const cache = MOIU.UniversalFallback(SDModelData{Float64}()) +const cached = MOIU.CachingOptimizer(cache, optimizer) +const bridged = MOIB.full_bridge_optimizer(cached, Float64) const config = MOIT.TestConfig(atol=1e-4, rtol=1e-4) @testset "SolverName" begin @test MOI.get(optimizer, MOI.SolverName()) == "CSDP" end +@testset "Options" begin + param = MOI.RawParameter(:bad_option) + err = MOI.UnsupportedAttribute(param) + @test_throws err CSDP.Optimizer(bad_option = 1) +end + @testset "Unit" begin - MOIT.unittest(MOIB.SplitInterval{Float64}(optimizer), config, - [# Quadratic functions are not supported + MOIT.unittest(bridged, config, + [# Multiple variable constraints on same variable + "solve_with_lowerbound", "solve_affine_interval", + "solve_with_upperbound", + # Quadratic functions are not supported "solve_qcp_edge_cases", "solve_qp_edge_cases", # Integer and ZeroOne sets are not supported "solve_integer_edge_cases", "solve_objbound_edge_cases"]) end @testset "Continuous Linear" begin - MOIT.contlineartest(MOIB.SplitInterval{Float64}(optimizer), config) + MOIT.contlineartest(bridged, config, + [ + # https://github.com/JuliaOpt/MathOptInterface.jl/issues/693 + "linear1", + # Multiple variable constraints on same variable + "linear10", "linear10b", "linear14"]) end @testset "Continuous Conic" begin - MOIT.contconictest(MOIB.RootDet{Float64}(MOIB.GeoMean{Float64}(MOIB.RSOCtoPSD{Float64}(MOIB.SOCtoPSD{Float64}(optimizer)))), config, ["psds", "rootdets", "logdet", "exp"]) + MOIT.contconictest(bridged, config, + [ + # Need to investigate + "psds0v", + # Multiple variable constraints on same variable + "rotatedsoc3", + # Missing bridges + "rootdets", + # Does not support exponential cone + "logdet", "exp"]) end diff --git a/test/MPB_wrapper.jl b/test/MPB_wrapper.jl index 06ad148..d1249f4 100644 --- a/test/MPB_wrapper.jl +++ b/test/MPB_wrapper.jl @@ -1,11 +1,7 @@ const solver = CSDP.CSDPSolver(printlevel=0) import MathProgBase -@static if VERSION >= v"0.7-" - const MPB_test_path = joinpath(dirname(pathof(MathProgBase)), "..", "test") -else - const MPB_test_path = joinpath(Pkg.dir("MathProgBase"), "test") -end +const MPB_test_path = joinpath(dirname(pathof(MathProgBase)), "..", "test") @testset "Linear tests" begin include(joinpath(MPB_test_path, "linproginterface.jl")) @@ -16,7 +12,7 @@ end include(joinpath(MPB_test_path, "conicinterface.jl")) # FIXME fails on Windows 32 bits... Maybe I should put linear vars/cons # in a diagonal matrix in SemidefiniteModels.jl instead of many 1x1 blocks - @static if !Compat.Sys.iswindows() || Sys.WORD_SIZE != 32 + @static if !Sys.iswindows() || Sys.WORD_SIZE != 32 @testset "Conic linear tests" begin coniclineartest(solver, duals=true, tol=1e-6) end diff --git a/test/check_blas.jl b/test/check_blas.jl index a94d527..27f69f5 100644 --- a/test/check_blas.jl +++ b/test/check_blas.jl @@ -13,13 +13,13 @@ const openblas = "/usr/lib/libopenblas.so.0" A = [1.0 0.0; 0.0 -1.0] lda = max(1,stride(A,2)) -infot = Ref{Compat.LinearAlgebra.BlasInt}() +infot = Ref{LinearAlgebra.BlasInt}() uplo = 'U' -ccall((:dpotrf_64_, Compat.LinearAlgebra.BLAS.libblas), Void, - (Ptr{UInt8}, Ptr{Compat.LinearAlgebra.BlasInt}, Ptr{eltype(A)}, Ptr{Compat.LinearAlgebra.BlasInt}, Ptr{Compat.LinearAlgebra.BlasInt}), +ccall((:dpotrf_64_, LinearAlgebra.BLAS.libblas), Void, + (Ptr{UInt8}, Ptr{LinearAlgebra.BlasInt}, Ptr{eltype(A)}, Ptr{LinearAlgebra.BlasInt}, Ptr{LinearAlgebra.BlasInt}), &uplo, &size(A,1), A, &lda, info) ccall((:dpotrf_, openblas), Void, - (Ptr{UInt8}, Ptr{Compat.LinearAlgebra.BlasInt}, Ptr{eltype(A)}, Ptr{Compat.LinearAlgebra.BlasInt}, Ptr{Compat.LinearAlgebra.BlasInt}), + (Ptr{UInt8}, Ptr{LinearAlgebra.BlasInt}, Ptr{eltype(A)}, Ptr{LinearAlgebra.BlasInt}, Ptr{LinearAlgebra.BlasInt}), &uplo, &size(A,1), A, &lda, infot) diff --git a/test/runtests.jl b/test/runtests.jl index f91d136..cbe8fb6 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,17 +1,16 @@ using CSDP -using Compat -using Compat.Test -using Compat.LinearAlgebra # For Diagonal +using Test +using LinearAlgebra # For Diagonal @testset "Interact with BLAS" begin vec = Cdouble[1.0, 2.0, 0.0, -1.0] l = length(vec) inc = 1 - n1 = ccall((BLAS.@blasfunc(dasum_), Compat.LinearAlgebra.BLAS.libblas), + n1 = ccall((BLAS.@blasfunc(dasum_), LinearAlgebra.BLAS.libblas), Cdouble, - (Ref{Compat.LinearAlgebra.BlasInt}, Ptr{Cdouble}, - Ref{Compat.LinearAlgebra.BlasInt}), + (Ref{LinearAlgebra.BlasInt}, Ptr{Cdouble}, + Ref{LinearAlgebra.BlasInt}), l, vec, inc) @test abs(n1 - 4) < 1e-15 end @@ -58,7 +57,6 @@ end end @testset "Options" begin - @test_throws ErrorException CSDP.Optimizer(bad_option = 1) @test CSDP.paramstruc(Dict(:axtol => 1e-7)).axtol == 1e-7 end From f9d16f10c1557ad8e1ee68528f8988e4b0fefa46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 31 May 2019 09:57:11 +0200 Subject: [PATCH 2/9] Update CI scripts --- .travis.yml | 11 +++++------ appveyor.yml | 3 +-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index a340f4b..039b487 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,9 +4,8 @@ os: - linux - osx julia: - - 0.6 - - 0.7 - 1.0 + - 1.1 matrix: allow_failures: @@ -20,8 +19,8 @@ addons: notifications: email: false +before_script: + - julia -e 'using Pkg; Pkg.add(PackageSpec(name="MathOptInterface", rev="master"))' + - julia -e 'using Pkg; Pkg.add(PackageSpec(name="SemidefiniteOptInterface", rev="bl/moiv0.9"))' after_success: - # push coverage results to Coveralls - - julia -e 'cd(Pkg.dir("CSDP")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder())' - # push coverage results to Codecov - - julia -e 'cd(Pkg.dir("CSDP")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' + - julia -e 'import Pkg; Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder()); Coveralls.submit(process_folder())' diff --git a/appveyor.yml b/appveyor.yml index ecbb923..342697c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,8 +1,7 @@ environment: matrix: - - julia_version: 0.6 - - julia_version: 0.7 - julia_version: 1 + - julia_version: 1.1 platform: - x86 # 32-bit From 36dc6524d4ac9166164958b72c93dd0495b3c53d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sun, 9 Jun 2019 23:52:30 +0200 Subject: [PATCH 3/9] Add missing using in test/MOI_wrapper --- test/MOI_wrapper.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index fc80dff..5a42557 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -1,3 +1,5 @@ +using Test + using MathOptInterface const MOI = MathOptInterface const MOIT = MOI.Test @@ -10,6 +12,7 @@ MOIU.@model(SDModelData, MOI.PositiveSemidefiniteConeTriangle), (), (), (MOI.ScalarAffineFunction,), (MOI.VectorOfVariables,), ()) +import CSDP const optimizer = CSDP.Optimizer() MOI.set(optimizer, MOI.Silent(), true) const cache = MOIU.UniversalFallback(SDModelData{Float64}()) From 17670a9dad49c973c63f2376d62bd01da7c6fb9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 12 Jul 2019 17:04:08 +0200 Subject: [PATCH 4/9] Updates --- test/MOI_wrapper.jl | 53 ++++++++++++++++++++++++--------------------- test/runtests.jl | 2 +- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index 5a42557..7209ac3 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -3,27 +3,27 @@ using Test using MathOptInterface const MOI = MathOptInterface const MOIT = MOI.Test -const MOIB = MOI.Bridges - const MOIU = MOI.Utilities -MOIU.@model(SDModelData, - (), (MOI.EqualTo, MOI.GreaterThan, MOI.LessThan), - (MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives, - MOI.PositiveSemidefiniteConeTriangle), (), - (), (MOI.ScalarAffineFunction,), (MOI.VectorOfVariables,), ()) +const MOIB = MOI.Bridges import CSDP const optimizer = CSDP.Optimizer() MOI.set(optimizer, MOI.Silent(), true) -const cache = MOIU.UniversalFallback(SDModelData{Float64}()) -const cached = MOIU.CachingOptimizer(cache, optimizer) -const bridged = MOIB.full_bridge_optimizer(cached, Float64) -const config = MOIT.TestConfig(atol=1e-4, rtol=1e-4) @testset "SolverName" begin @test MOI.get(optimizer, MOI.SolverName()) == "CSDP" end +@testset "supports_default_copy_to" begin + @test MOIU.supports_allocate_load(optimizer, false) + @test !MOIU.supports_allocate_load(optimizer, true) +end + +const cache = MOIU.UniversalFallback(MOIU.Model{Float64}()) +const cached = MOIU.CachingOptimizer(cache, optimizer) +const bridged = MOIB.full_bridge_optimizer(cached, Float64) +const config = MOIT.TestConfig(atol=1e-4, rtol=1e-4) + @testset "Options" begin param = MOI.RawParameter(:bad_option) err = MOI.UnsupportedAttribute(param) @@ -32,31 +32,34 @@ end @testset "Unit" begin MOIT.unittest(bridged, config, - [# Multiple variable constraints on same variable + [# SingleVariable objective of bridged variables, will be solved by objective bridges + "solve_time", "raw_status_string", "solve_singlevariable_obj", + # Multiple variable constraints on same variable "solve_with_lowerbound", "solve_affine_interval", "solve_with_upperbound", # Quadratic functions are not supported "solve_qcp_edge_cases", "solve_qp_edge_cases", # Integer and ZeroOne sets are not supported - "solve_integer_edge_cases", "solve_objbound_edge_cases"]) + "solve_integer_edge_cases", "solve_objbound_edge_cases", + "solve_zero_one_with_bounds_1", + "solve_zero_one_with_bounds_2", + "solve_zero_one_with_bounds_3"]) end @testset "Continuous Linear" begin MOIT.contlineartest(bridged, config, [ - # https://github.com/JuliaOpt/MathOptInterface.jl/issues/693 - "linear1", # Multiple variable constraints on same variable "linear10", "linear10b", "linear14"]) end @testset "Continuous Conic" begin - MOIT.contconictest(bridged, config, - [ - # Need to investigate - "psds0v", - # Multiple variable constraints on same variable - "rotatedsoc3", - # Missing bridges - "rootdets", - # Does not support exponential cone - "logdet", "exp"]) + MOIT.contconictest( + bridged, config, + [# See https://github.com/coin-or/Csdp/issues/11 + "rotatedsoc1v", + # Need to investigate + "psds0v", + # Missing bridges + "rootdets", + # Does not support power and exponential cone + "pow", "logdet", "exp"]) end diff --git a/test/runtests.jl b/test/runtests.jl index cbe8fb6..a05daef 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -30,7 +30,7 @@ end end @testset "Example" begin - cd("../examples/") do + cd(joinpath(dirname(dirname(pathof(CSDP))), "examples")) do include(joinpath(pwd(), "example.jl")) @test size(X) == (7, 7) @test length(y) == 2 From 2ab7486702404ce3fbe0b13dc2bc73911565038d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 12 Jul 2019 17:16:48 +0200 Subject: [PATCH 5/9] Add tests that works now with variable bridges --- test/MOI_wrapper.jl | 46 +++++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index 7209ac3..b23a2d7 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -19,6 +19,7 @@ end @test !MOIU.supports_allocate_load(optimizer, true) end +# UniversalFallback is needed for starting values, even if they are ignored by CSDP const cache = MOIU.UniversalFallback(MOIU.Model{Float64}()) const cached = MOIU.CachingOptimizer(cache, optimizer) const bridged = MOIB.full_bridge_optimizer(cached, Float64) @@ -31,35 +32,26 @@ const config = MOIT.TestConfig(atol=1e-4, rtol=1e-4) end @testset "Unit" begin - MOIT.unittest(bridged, config, - [# SingleVariable objective of bridged variables, will be solved by objective bridges - "solve_time", "raw_status_string", "solve_singlevariable_obj", - # Multiple variable constraints on same variable - "solve_with_lowerbound", "solve_affine_interval", - "solve_with_upperbound", - # Quadratic functions are not supported - "solve_qcp_edge_cases", "solve_qp_edge_cases", - # Integer and ZeroOne sets are not supported - "solve_integer_edge_cases", "solve_objbound_edge_cases", - "solve_zero_one_with_bounds_1", - "solve_zero_one_with_bounds_2", - "solve_zero_one_with_bounds_3"]) + MOIT.unittest(bridged, config, [ + # SingleVariable objective of bridged variables, will be solved by objective bridges + "solve_time", "raw_status_string", "solve_singlevariable_obj", + # Quadratic functions are not supported + "solve_qcp_edge_cases", "solve_qp_edge_cases", + # Integer and ZeroOne sets are not supported + "solve_integer_edge_cases", "solve_objbound_edge_cases", + "solve_zero_one_with_bounds_1", + "solve_zero_one_with_bounds_2", + "solve_zero_one_with_bounds_3"]) end @testset "Continuous Linear" begin - MOIT.contlineartest(bridged, config, - [ - # Multiple variable constraints on same variable - "linear10", "linear10b", "linear14"]) + MOIT.contlineartest(bridged, config) end @testset "Continuous Conic" begin - MOIT.contconictest( - bridged, config, - [# See https://github.com/coin-or/Csdp/issues/11 - "rotatedsoc1v", - # Need to investigate - "psds0v", - # Missing bridges - "rootdets", - # Does not support power and exponential cone - "pow", "logdet", "exp"]) + MOIT.contconictest(bridged, config, [ + # See https://github.com/coin-or/Csdp/issues/11 + "rotatedsoc1v", + # Missing bridges + "rootdets", + # Does not support power and exponential cone + "pow", "logdet", "exp"]) end From ef3d62f630d107971c75e9368265d9d2865b6c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 13 Aug 2019 14:24:39 +0200 Subject: [PATCH 6/9] Update to MOI master --- test/MOI_wrapper.jl | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index b23a2d7..3220b70 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -33,6 +33,8 @@ end @testset "Unit" begin MOIT.unittest(bridged, config, [ + # `TimeLimitSec` not supported. + "time_limit_sec", # SingleVariable objective of bridged variables, will be solved by objective bridges "solve_time", "raw_status_string", "solve_singlevariable_obj", # Quadratic functions are not supported @@ -44,10 +46,19 @@ end "solve_zero_one_with_bounds_3"]) end @testset "Continuous Linear" begin - MOIT.contlineartest(bridged, config) + # See explanation in `MOI/test/Bridges/lazy_bridge_optimizer.jl`. + # This is to avoid `Variable.VectorizeBridge` which does not support + # `ConstraintSet` modification. + MOIB.remove_bridge(bridged, MOIB.Constraint.ScalarSlackBridge{Float64}) + MOIT.contlineartest(bridged, config, [ + # Finds `MOI.ALMOST_OPTIMAL` instead of `MOI.OPTIMAL` + "linear10b" + ]) end @testset "Continuous Conic" begin MOIT.contconictest(bridged, config, [ + # Finds `MOI.OPTIMAL` instead of `MOI.INFEASIBLE`. + "soc3", # See https://github.com/coin-or/Csdp/issues/11 "rotatedsoc1v", # Missing bridges From 5b65db11ccb6a084a61e8681f8180beb3f8e2338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 13 Aug 2019 18:07:19 +0200 Subject: [PATCH 7/9] Get rid of SemidefiniteOptInterface --- src/CSDP.jl | 1 + src/MOI_wrapper.jl | 287 ++++++++++++++++++++++++++++++++++++--------- src/MPB_wrapper.jl | 4 +- src/blockdiag.jl | 28 +++++ src/blockmat.jl | 13 +- 5 files changed, 269 insertions(+), 64 deletions(-) create mode 100644 src/blockdiag.jl diff --git a/src/CSDP.jl b/src/CSDP.jl index d32a02d..c11ebe4 100644 --- a/src/CSDP.jl +++ b/src/CSDP.jl @@ -13,6 +13,7 @@ end export Blockmatrix include("blockmat.h.jl") +include("blockdiag.jl") include("blockmat.jl") include("declarations.h.jl") include("declarations.jl") diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 7c81878..d6f3c75 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -1,12 +1,15 @@ -using SemidefiniteOptInterface -SDOI = SemidefiniteOptInterface - using MathOptInterface -MOI = MathOptInterface +const MOI = MathOptInterface +const MOIU = MOI.Utilities +const AFFEQ = MOI.ConstraintIndex{MOI.ScalarAffineFunction{Cdouble}, MOI.EqualTo{Cdouble}} -mutable struct SDOptimizer <: SDOI.AbstractSDOptimizer +mutable struct Optimizer <: MOI.AbstractOptimizer + objconstant::Cdouble + objsign::Int + blockdims::Vector{Int} + varmap::Vector{Tuple{Int, Int, Int}} # Variable Index vi -> blk, i, j + b::Vector{Cdouble} C::Union{Nothing, BlockMatrix} - b::Union{Nothing, Vector{Cdouble}} As::Union{Nothing, Vector{ConstraintMatrix}} X::Union{Nothing, BlockMatrix} y::Union{Nothing, Vector{Cdouble}} @@ -17,8 +20,10 @@ mutable struct SDOptimizer <: SDOI.AbstractSDOptimizer solve_time::Float64 silent::Bool options::Dict{Symbol, Any} - function SDOptimizer(; kwargs...) - optimizer = new(nothing, nothing, nothing, nothing, nothing, nothing, + function Optimizer(; kwargs...) + optimizer = new( + zero(Cdouble), 1, Int[], Tuple{Int, Int, Int}[], Cdouble[], + nothing, nothing, nothing, nothing, nothing, -1, NaN, NaN, NaN, false, Dict{Symbol, Any}()) for (key, value) in kwargs MOI.set(optimizer, MOI.RawParameter(key), value) @@ -26,29 +31,30 @@ mutable struct SDOptimizer <: SDOI.AbstractSDOptimizer return optimizer end end -Optimizer(; kws...) = SDOI.SDOIOptimizer(SDOptimizer(; kws...)) -function MOI.supports(optimizer::SDOptimizer, param::MOI.RawParameter) +varmap(optimizer::Optimizer, vi::MOI.VariableIndex) = optimizer.varmap[vi.value] + +function MOI.supports(optimizer::Optimizer, param::MOI.RawParameter) return param.name in ALLOWED_OPTIONS end -function MOI.set(optimizer::SDOptimizer, param::MOI.RawParameter, value) +function MOI.set(optimizer::Optimizer, param::MOI.RawParameter, value) if !MOI.supports(optimizer, param) throw(MOI.UnsupportedAttribute(param)) end optimizer.options[param.name] = value end -function MOI.get(optimizer::SDOptimizer, param::MOI.RawParameter) +function MOI.get(optimizer::Optimizer, param::MOI.RawParameter) # TODO: This gives a poor error message if the name of the parameter is invalid. return optimizer.options[param.name] end -MOI.supports(::SDOptimizer, ::MOI.Silent) = true -function MOI.set(optimizer::SDOptimizer, ::MOI.Silent, value::Bool) +MOI.supports(::Optimizer, ::MOI.Silent) = true +function MOI.set(optimizer::Optimizer, ::MOI.Silent, value::Bool) optimizer.silent = value end -MOI.get(optimizer::SDOptimizer, ::MOI.Silent) = optimizer.silent +MOI.get(optimizer::Optimizer, ::MOI.Silent) = optimizer.silent -MOI.get(::SDOptimizer, ::MOI.SolverName) = "CSDP" +MOI.get(::Optimizer, ::MOI.SolverName) = "CSDP" # See table "Return codes for easy_sdp() and CSDP" in `doc/csdpuser.pdf`. const RAW_STATUS = [ "Problem solved to optimality.", @@ -63,16 +69,30 @@ const RAW_STATUS = [ "NaN or Inf values encountered.", "Program stopped by signal (SIXCPU, SIGTERM, etc.)"] -function MOI.get(optimizer::SDOptimizer, ::MOI.RawStatusString) +function MOI.get(optimizer::Optimizer, ::MOI.RawStatusString) return RAW_STATUS[optimizer.status + 1] end -function MOI.get(optimizer::SDOptimizer, ::MOI.SolveTime) +function MOI.get(optimizer::Optimizer, ::MOI.SolveTime) return optimizer.solve_time end -function MOI.empty!(optimizer::SDOptimizer) +function MOI.is_empty(optimizer::Optimizer) + return iszero(optimizer.objconstant) && + optimizer.objsign == 1 && + isempty(optimizer.blockdims) && + isempty(optimizer.varmap) && + isempty(optimizer.b) && + optimizer.C === nothing && + optimizer.As === nothing +end + +function MOI.empty!(optimizer::Optimizer) + optimizer.objconstant = zero(Cdouble) + optimizer.objsign = 1 + empty!(optimizer.blockdims) + empty!(optimizer.varmap) + empty!(optimizer.b) optimizer.C = nothing - optimizer.b = nothing optimizer.As = nothing optimizer.X = nothing optimizer.y = nothing @@ -82,38 +102,143 @@ function MOI.empty!(optimizer::SDOptimizer) optimizer.dobj = 0.0 end -function SDOI.init!(m::SDOptimizer, blkdims::Vector{Int}, nconstrs::Int) - @assert nconstrs >= 0 - dummy = nconstrs == 0 +function MOI.supports( + optimizer::Optimizer, + ::Union{MOI.ObjectiveSense, + MOI.ObjectiveFunction{<:Union{MOI.SingleVariable, + MOI.ScalarAffineFunction{Cdouble}}}}) + return true +end + +function MOI.supports_constraint( + ::Optimizer, ::Type{MOI.VectorOfVariables}, ::Type{MOI.Reals}) + return false +end +const SupportedSets = Union{MOI.Nonnegatives, MOI.PositiveSemidefiniteConeTriangle} +function MOI.supports_constraint( + ::Optimizer, ::Type{MOI.VectorOfVariables}, + ::Type{<:SupportedSets}) + return true +end +function MOI.supports_constraint( + ::Optimizer, ::Type{MOI.ScalarAffineFunction{Cdouble}}, + ::Type{MOI.EqualTo{Cdouble}}) + return true +end + +function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike; kws...) + return MOIU.automatic_copy_to(dest, src; kws...) +end +MOIU.supports_allocate_load(::Optimizer, copy_names::Bool) = !copy_names + +function MOIU.allocate(optimizer::Optimizer, ::MOI.ObjectiveSense, sense::MOI.OptimizationSense) + # To be sure that it is done before load(optimizer, ::ObjectiveFunction, ...), we do it in allocate + optimizer.objsign = sense == MOI.MIN_SENSE ? -1 : 1 +end +function MOIU.allocate(::Optimizer, ::MOI.ObjectiveFunction, ::Union{MOI.SingleVariable, MOI.ScalarAffineFunction}) end + +function MOIU.load(::Optimizer, ::MOI.ObjectiveSense, ::MOI.OptimizationSense) end +# Loads objective coefficient α * vi +function load_objective_term!(optimizer::Optimizer, α, vi::MOI.VariableIndex) + blk, i, j = varmap(optimizer, vi) + coef = optimizer.objsign * α + if i != j + coef /= 2 + end + # in SDP format, it is max and in MPB Conic format it is min + block(optimizer.C, blk)[i, j] = coef +end +function MOIU.load(optimizer::Optimizer, ::MOI.ObjectiveFunction, f::MOI.ScalarAffineFunction) + obj = MOIU.canonical(f) + optimizer.objconstant = f.constant + for t in obj.terms + if !iszero(t.coefficient) + load_objective_term!(optimizer, t.coefficient, t.variable_index) + end + end +end +function MOIU.load(optimizer::Optimizer, ::MOI.ObjectiveFunction, f::MOI.SingleVariable) + load_objective_term!(optimizer, one(Cdouble), f.variable) +end + +function new_block(optimizer::Optimizer, set::MOI.Nonnegatives) + push!(optimizer.blockdims, -MOI.dimension(set)) + blk = length(optimizer.blockdims) + for i in 1:MOI.dimension(set) + push!(optimizer.varmap, (blk, i, i)) + end +end + +function new_block(optimizer::Optimizer, set::MOI.PositiveSemidefiniteConeTriangle) + push!(optimizer.blockdims, set.side_dimension) + blk = length(optimizer.blockdims) + for i in 1:set.side_dimension + for j in 1:i + push!(optimizer.varmap, (blk, i, j)) + end + end +end + +function MOIU.allocate_constrained_variables(optimizer::Optimizer, + set::SupportedSets) + offset = length(optimizer.varmap) + new_block(optimizer, set) + ci = MOI.ConstraintIndex{MOI.VectorOfVariables, typeof(set)}(offset + 1) + return [MOI.VariableIndex(i) for i in offset .+ (1:MOI.dimension(set))], ci +end + +function MOIU.load_constrained_variables( + optimizer::Optimizer, vis::Vector{MOI.VariableIndex}, + ci::MOI.ConstraintIndex{MOI.VectorOfVariables}, + set::SupportedSets) +end + +function MOIU.load_variables(optimizer::Optimizer, nvars) + @assert nvars == length(optimizer.varmap) + dummy = isempty(optimizer.b) if dummy # See https://github.com/coin-or/Csdp/issues/2 - nconstrs = 1 - blkdims = [blkdims; -1] + optimizer.b = [one(Cdouble)] + optimizer.blockdims = [optimizer.blockdims; -1] end - m.C = blockmatzeros(blkdims) - m.b = zeros(Cdouble, nconstrs) - m.As = [constrmatzeros(i, blkdims) for i in 1:nconstrs] + optimizer.C = blockmatzeros(optimizer.blockdims) + optimizer.As = [constrmatzeros(i, optimizer.blockdims) for i in eachindex(optimizer.b)] if dummy # See https://github.com/coin-or/Csdp/issues/2 - m.b[1] = 1 - SDOI.block(m.As[1], length(blkdims))[1,1] = 1 + block(optimizer.As[1], length(optimizer.blockdims))[1, 1] = 1 end -end -function SDOI.setconstraintconstant!(m::SDOptimizer, val, constr::Integer) - #println("b[$constr] = $val") - m.b[constr] = val end -function SDOI.setconstraintcoefficient!(m::SDOptimizer, coef, constr::Integer, blk::Integer, i::Integer, j::Integer) - #println("A[$constr][$blk][$i, $j] = $coef") - SDOI.block(m.As[constr], blk)[i, j] = coef + +function MOIU.allocate_constraint(optimizer::Optimizer, + func::MOI.ScalarAffineFunction{Cdouble}, + set::MOI.EqualTo{Cdouble}) + push!(optimizer.b, MOI.constant(set)) + return AFFEQ(length(optimizer.b)) end -function SDOI.setobjectivecoefficient!(m::SDOptimizer, coef, blk::Integer, i::Integer, j::Integer) - #println("C[$blk][$i, $j] = $coef") - SDOI.block(m.C, blk)[i, j] = coef + +function MOIU.load_constraint(m::Optimizer, ci::AFFEQ, + f::MOI.ScalarAffineFunction, s::MOI.EqualTo) + if !iszero(MOI.constant(f)) + throw(MOI.ScalarFunctionConstantNotZero{ + Cdouble, MOI.ScalarAffineFunction{Cdouble}, MOI.EqualTo{Cdouble}}( + MOI.constant(f))) + end + f = MOIU.canonical(f) # sum terms with same variables and same outputindex + for t in f.terms + if !iszero(t.coefficient) + blk, i, j = varmap(m, t.variable_index) + coef = t.coefficient + if i != j + coef /= 2 + end + block(m.As[ci.value], blk)[i, j] = coef + end + end end -function MOI.optimize!(optimizer::SDOptimizer) + +function MOI.optimize!(optimizer::Optimizer) As = map(A->A.csdp, optimizer.As) write_prob(optimizer) @@ -133,7 +258,7 @@ function MOI.optimize!(optimizer::SDOptimizer) optimizer.solve_time = time() - start_time end -function MOI.get(m::SDOptimizer, ::MOI.TerminationStatus) +function MOI.get(m::Optimizer, ::MOI.TerminationStatus) status = m.status if status == -1 return MOI.OPTIMIZE_NOT_CALLED @@ -156,7 +281,7 @@ function MOI.get(m::SDOptimizer, ::MOI.TerminationStatus) end end -function MOI.get(m::SDOptimizer, ::MOI.PrimalStatus) +function MOI.get(m::Optimizer, ::MOI.PrimalStatus) status = m.status if status == 0 return MOI.FEASIBLE_POINT @@ -173,7 +298,7 @@ function MOI.get(m::SDOptimizer, ::MOI.PrimalStatus) end end -function MOI.get(m::SDOptimizer, ::MOI.DualStatus) +function MOI.get(m::Optimizer, ::MOI.DualStatus) status = m.status if status == 0 return MOI.FEASIBLE_POINT @@ -190,18 +315,72 @@ function MOI.get(m::SDOptimizer, ::MOI.DualStatus) end end -function MOI.get(m::SDOptimizer, ::MOI.ObjectiveValue) - return m.pobj +MOI.get(m::Optimizer, ::MOI.ResultCount) = 1 +function MOI.get(m::Optimizer, ::MOI.ObjectiveValue) + return m.objsign * m.pobj + m.objconstant end -function MOI.get(m::SDOptimizer, ::MOI.DualObjectiveValue) - return m.dobj +function MOI.get(m::Optimizer, ::MOI.DualObjectiveValue) + return m.objsign * m.dobj + m.objconstant end -function SDOI.getX(m::SDOptimizer) - return m.X +struct PrimalSolutionMatrix <: MOI.AbstractModelAttribute end +MOI.is_set_by_optimize(::PrimalSolutionMatrix) = true +MOI.get(optimizer::Optimizer, ::PrimalSolutionMatrix) = optimizer.X + +struct DualSolutionVector <: MOI.AbstractModelAttribute end +MOI.is_set_by_optimize(::DualSolutionVector) = true +MOI.get(optimizer::Optimizer, ::DualSolutionVector) = optimizer.y + +struct DualSlackMatrix <: MOI.AbstractModelAttribute end +MOI.is_set_by_optimize(::DualSlackMatrix) = true +MOI.get(optimizer::Optimizer, ::DualSlackMatrix) = optimizer.Z + +function block(optimizer::Optimizer, ci::MOI.ConstraintIndex{MOI.VectorOfVariables}) + return optimizer.varmap[ci.value][1] end -function SDOI.gety(m::SDOptimizer) - return m.y +function dimension(optimizer::Optimizer, ci::MOI.ConstraintIndex{MOI.VectorOfVariables}) + blockdim = optimizer.blockdims[block(optimizer, ci)] + if blockdim < 0 + return -blockdim + else + return MOI.dimension(MOI.PositiveSemidefiniteConeTriangle(blockdim)) + end +end +function vectorize_block(M, blk::Integer, s::Type{MOI.Nonnegatives}) + return diag(block(M, blk)) +end +function vectorize_block(M::AbstractMatrix{Cdouble}, blk::Integer, s::Type{MOI.PositiveSemidefiniteConeTriangle}) where T + B = block(M, blk) + d = LinearAlgebra.checksquare(B) + n = MOI.dimension(MOI.PositiveSemidefiniteConeTriangle(d)) + v = Vector{Cdouble}(undef, n) + k = 0 + for j in 1:d + for i in 1:j + k += 1 + v[k] = B[i, j] + end + end + @assert k == n + return v +end + +function MOI.get(optimizer::Optimizer, ::MOI.VariablePrimal, vi::MOI.VariableIndex) + blk, i, j = varmap(optimizer, vi) + return block(optimizer.X, blk)[i, j] +end + +function MOI.get(m::Optimizer, ::MOI.ConstraintPrimal, + ci::MOI.ConstraintIndex{MOI.VectorOfVariables, S}) where S<:SupportedSets + return vectorize_block(m.X, block(m, ci), S) +end +function MOI.get(m::Optimizer, ::MOI.ConstraintPrimal, ci::AFFEQ) + return m.b[ci.value] +end + +function MOI.get(optimizer::Optimizer, ::MOI.ConstraintDual, + ci::MOI.ConstraintIndex{MOI.VectorOfVariables, S}) where S<:SupportedSets + return vectorize_block(optimizer.Z, block(optimizer, ci), S) end -function SDOI.getZ(m::SDOptimizer) - return m.Z +function MOI.get(optimizer::Optimizer, ::MOI.ConstraintDual, ci::AFFEQ) + return -optimizer.y[ci.value] end diff --git a/src/MPB_wrapper.jl b/src/MPB_wrapper.jl index 909d332..64caa6a 100644 --- a/src/MPB_wrapper.jl +++ b/src/MPB_wrapper.jl @@ -65,10 +65,10 @@ function SDM.setconstrB!(m::CSDPMathProgModel, val, constr::Integer) m.b[constr] = val end function SDM.setconstrentry!(m::CSDPMathProgModel, coef, constr::Integer, blk::Integer, i::Integer, j::Integer) - SDOI.block(m.As[constr], blk)[i, j] = coef + block(m.As[constr], blk)[i, j] = coef end function SDM.setobjentry!(m::CSDPMathProgModel, coef, blk::Integer, i::Integer, j::Integer) - SDOI.block(m.C, blk)[i, j] = coef + block(m.C, blk)[i, j] = coef end function MPB.optimize!(m::CSDPMathProgModel) diff --git a/src/blockdiag.jl b/src/blockdiag.jl new file mode 100644 index 0000000..95c8cfc --- /dev/null +++ b/src/blockdiag.jl @@ -0,0 +1,28 @@ +abstract type AbstractBlockMatrix{T} <: AbstractMatrix{T} end + +function nblocks end +function block end + +function Base.size(bm::AbstractBlockMatrix) + n = mapreduce(blk -> LinearAlgebra.checksquare(block(bm, blk)), + +, 1:nblocks(bm), init=0) + return (n, n) +end +function Base.getindex(bm::AbstractBlockMatrix, i::Integer, j::Integer) + (i < 0 || j < 0) && throw(BoundsError(i, j)) + for k in 1:nblocks(bm) + blk = block(bm, k) + n = size(blk, 1) + if i <= n && j <= n + return blk[i, j] + elseif i <= n || j <= n + return 0 + else + i -= n + j -= n + end + end + i, j = (i, j) .+ size(bm) + throw(BoundsError(i, j)) +end +Base.getindex(A::AbstractBlockMatrix, I::Tuple) = getindex(A, I...) diff --git a/src/blockmat.jl b/src/blockmat.jl index 1aa85fc..e9b4ce2 100644 --- a/src/blockmat.jl +++ b/src/blockmat.jl @@ -49,9 +49,6 @@ end # * blockrec contains the blockdatarec, the category (dense or diagonal), and the block size # * blockmatrix contains the number of blocks and a vector containing the blocks (blockrec) (1-indexed) -using SemidefiniteOptInterface -const SDOI = SemidefiniteOptInterface - # blockrec mutable struct BlockRec <: AbstractMatrix{Cdouble} _blockdatarec::Vector{Cdouble} @@ -118,7 +115,7 @@ end # blockmatrix -mutable struct BlockMatrix <: SDOI.AbstractBlockMatrix{Cdouble} +mutable struct BlockMatrix <: AbstractBlockMatrix{Cdouble} jblocks::Vector{BlockRec} blocks::Vector{blockrec} csdp::blockmatrix @@ -268,7 +265,7 @@ function Base.setindex!(A::SparseBlock, v, i, j) end -mutable struct ConstraintMatrix <: SDOI.AbstractBlockMatrix{Cdouble} +mutable struct ConstraintMatrix <: AbstractBlockMatrix{Cdouble} jblocks::Vector{SparseBlock} csdp::constraintmatrix end @@ -309,13 +306,13 @@ end # Needed by MPB_wrapper function Base.getindex(A::Union{BlockMatrix, ConstraintMatrix}, i::Integer) - SDOI.block(A, i) + block(A, i) end -function SDOI.block(A::Union{BlockMatrix, ConstraintMatrix}, i::Integer) +function block(A::Union{BlockMatrix, ConstraintMatrix}, i::Integer) A.jblocks[i] end -SDOI.nblocks(A::Union{BlockMatrix, ConstraintMatrix}) = length(A.jblocks) +nblocks(A::Union{BlockMatrix, ConstraintMatrix}) = length(A.jblocks) function free_blockmatrix(m::blockmatrix) ccall((:free_mat, CSDP.csdp), Nothing, (blockmatrix,), m) From ca261af28e7b56736bd1aa987f7c20c01d812dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 13 Aug 2019 18:35:52 +0200 Subject: [PATCH 8/9] Remove SDOI from Project.toml --- Project.toml | 2 -- src/MOI_wrapper.jl | 10 +++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Project.toml b/Project.toml index 44b2bed..485fa22 100644 --- a/Project.toml +++ b/Project.toml @@ -11,14 +11,12 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" MathProgBase = "fdba3010-5040-5b88-9595-932c9decdf73" SemidefiniteModels = "169818f4-1a3d-53bf-95b3-11177825b1e3" -SemidefiniteOptInterface = "f0680fed-b2cd-5302-98f9-f4da282d86b5" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [compat] MathOptInterface = "0.9" MathProgBase = "~0.7.0" SemidefiniteModels = "~0.1.1" -SemidefiniteOptInterface = "0.6" julia = "1" [extras] diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index d6f3c75..d1efbaa 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -366,12 +366,12 @@ end function MOI.get(optimizer::Optimizer, ::MOI.VariablePrimal, vi::MOI.VariableIndex) blk, i, j = varmap(optimizer, vi) - return block(optimizer.X, blk)[i, j] + return block(MOI.get(optimizer, PrimalSolutionMatrix()), blk)[i, j] end -function MOI.get(m::Optimizer, ::MOI.ConstraintPrimal, +function MOI.get(optimizer::Optimizer, ::MOI.ConstraintPrimal, ci::MOI.ConstraintIndex{MOI.VectorOfVariables, S}) where S<:SupportedSets - return vectorize_block(m.X, block(m, ci), S) + return vectorize_block(MOI.get(optimizer, PrimalSolutionMatrix()), block(optimizer, ci), S) end function MOI.get(m::Optimizer, ::MOI.ConstraintPrimal, ci::AFFEQ) return m.b[ci.value] @@ -379,8 +379,8 @@ end function MOI.get(optimizer::Optimizer, ::MOI.ConstraintDual, ci::MOI.ConstraintIndex{MOI.VectorOfVariables, S}) where S<:SupportedSets - return vectorize_block(optimizer.Z, block(optimizer, ci), S) + return vectorize_block(MOI.get(optimizer, DualSlackMatrix()), block(optimizer, ci), S) end function MOI.get(optimizer::Optimizer, ::MOI.ConstraintDual, ci::AFFEQ) - return -optimizer.y[ci.value] + return -MOI.get(optimizer, DualSolutionVector())[ci.value] end From 4d5c1c15d43fb8e41c79eddc4c14909c033e5ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 13 Aug 2019 22:38:09 +0200 Subject: [PATCH 9/9] Update travis script --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 039b487..76696cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,5 @@ addons: notifications: email: false -before_script: - - julia -e 'using Pkg; Pkg.add(PackageSpec(name="MathOptInterface", rev="master"))' - - julia -e 'using Pkg; Pkg.add(PackageSpec(name="SemidefiniteOptInterface", rev="bl/moiv0.9"))' after_success: - julia -e 'import Pkg; Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder()); Coveralls.submit(process_folder())'