diff --git a/Project.toml b/Project.toml index 879ce96..07652b0 100644 --- a/Project.toml +++ b/Project.toml @@ -27,9 +27,9 @@ MutableArithmetics = "1" OpenBLAS32_jll = "0.3.10" Pkg = "1" PrecompileTools = "1" -SCS_GPU_jll = "=3.2.9" -SCS_MKL_jll = "=3.2.9" -SCS_jll = "=3.2.9" +SCS_GPU_jll = "=300.200.900" +SCS_MKL_jll = "=300.200.900" +SCS_jll = "=300.200.900" SparseArrays = "1" Test = "1" julia = "1.10" diff --git a/src/MOI_wrapper/MOI_wrapper.jl b/src/MOI_wrapper/MOI_wrapper.jl index a1e7671..5930012 100644 --- a/src/MOI_wrapper/MOI_wrapper.jl +++ b/src/MOI_wrapper/MOI_wrapper.jl @@ -6,6 +6,7 @@ include("scaled_psd_cone_bridge.jl") include("hermitian_complex_psd_cone_bridge.jl") include("scaled_complex_psd_cone_bridge.jl") +include("ScaledLogDetConeTriangle.jl") MOI.Utilities.@product_of_sets( _Cones, @@ -18,6 +19,7 @@ MOI.Utilities.@product_of_sets( MOI.DualExponentialCone, MOI.PowerCone{T}, MOI.DualPowerCone{T}, + ScaledLogDetConeTriangle, ) struct _SetConstants{T} @@ -139,6 +141,7 @@ function MOI.get(::Optimizer, ::MOI.Bridges.ListOfNonstandardBridges) ScaledPSDConeBridge{Cdouble}, ScaledComplexPSDConeBridge{Cdouble}, HermitianComplexPSDConeBridge{Cdouble}, + ScaledLogDetConeTriangleBridge{Cdouble}, ] end @@ -251,6 +254,7 @@ function MOI.supports_constraint( MOI.DualExponentialCone, MOI.PowerCone{Cdouble}, MOI.DualPowerCone{Cdouble}, + ScaledLogDetConeTriangle, }, }, ) @@ -410,6 +414,12 @@ function MOI.optimize!( MOI.DualPowerCone{Cdouble}, ), ), + _map_sets(set -> set.side_dimension, T, Ab, ScaledLogDetConeTriangle), + T[], # nuc_m + T[], # nuc_n + T[], # ell1 + T[], # sl_n + T[], # nuc_k dest.sol.primal, dest.sol.dual, dest.sol.slack; diff --git a/src/MOI_wrapper/ScaledLogDetConeTriangle.jl b/src/MOI_wrapper/ScaledLogDetConeTriangle.jl new file mode 100644 index 0000000..643609f --- /dev/null +++ b/src/MOI_wrapper/ScaledLogDetConeTriangle.jl @@ -0,0 +1,104 @@ +# Copyright (c) 2014: SCS.jl contributors +# +# Use of this source code is governed by an MIT-style license that can be found +# in the LICENSE.md file or at https://opensource.org/licenses/MIT. + +""" + struct ScaledLogDetConeTriangle <: MOI.AbstractVectorSet + side_dimension::Int + end + +MOI: [t, u, x] in MOI.Scaled{MOI.LogDetConeTriangle}(n) +SCS: [-t, u, perm(x)] in SCS.ScaledLogDetConeTriangle(n) +""" +struct ScaledLogDetConeTriangle <: MOI.AbstractVectorSet + side_dimension::Int +end + +function MOI.Utilities.set_with_dimension(::Type{ScaledLogDetConeTriangle}, dim) + return ScaledLogDetConeTriangle(div(-1 + isqrt(1 + 8 * dim), 2)) +end + +function MOI.dimension(set::ScaledLogDetConeTriangle) + return MOI.dimension(MOI.LogDetConeTriangle(set.side_dimension)) +end + +struct ScaledLogDetConeTriangleBridge{T,F} <: + MOI.Bridges.Constraint.SetMapBridge{ + T, + ScaledLogDetConeTriangle, + MOI.Scaled{MOI.LogDetConeTriangle}, + F, + F, +} + constraint::MOI.ConstraintIndex{F,ScaledLogDetConeTriangle} +end + +function MOI.Bridges.Constraint.concrete_bridge_type( + ::Type{ScaledLogDetConeTriangleBridge{T}}, + ::Type{F}, + ::Type{MOI.Scaled{MOI.LogDetConeTriangle}}, +) where {T,F<:MOI.AbstractVectorFunction} + return ScaledLogDetConeTriangleBridge{T,F} +end + +function MOI.Bridges.map_set( + ::Type{<:ScaledLogDetConeTriangleBridge}, + set::MOI.Scaled{MOI.LogDetConeTriangle}, +) + return ScaledLogDetConeTriangle(set.set.side_dimension) +end + +function MOI.Bridges.inverse_map_set( + ::Type{<:ScaledLogDetConeTriangleBridge}, + set::ScaledLogDetConeTriangle, +) + return MOI.Scaled(MOI.LogDetConeTriangle(set.side_dimension)) +end + +function _transform_function( + ::Type{<:ScaledLogDetConeTriangleBridge{T}}, + func, + moi_to_scs::Bool, +) where {T} + scalars = MOI.Utilities.eachscalar(func) + upper_to_lower, lower_to_upper = + _upper_to_lower_triangular_permutation(length(scalars) - 2) + p = moi_to_scs ? lower_to_upper : upper_to_lower + return MOI.Utilities.operate( + vcat, + T, + MOI.Utilities.operate(-, T, scalars[1]), + scalars[2], + scalars[p.+2], + ) +end + +# Map ConstraintFunction from MOI -> SCS +function MOI.Bridges.map_function(b::Type{<:ScaledLogDetConeTriangleBridge}, f) + return _transform_function(b, f, true) +end + +# Used to map the ConstraintPrimal from SCS -> MOI +function MOI.Bridges.inverse_map_function( + b::Type{<:ScaledLogDetConeTriangleBridge}, + f, +) + return _transform_function(b, f, false) +end + +# Used to map the ConstraintDual from SCS -> MOI +function MOI.Bridges.adjoint_map_function( + b::Type{<:ScaledLogDetConeTriangleBridge}, + f, +) + return _transform_function(b, f, false) +end + +# Used to set ConstraintDualStart +function MOI.Bridges.inverse_adjoint_map_function( + b::Type{<:ScaledLogDetConeTriangleBridge}, + f, +) + return _transform_function(b, f, true) +end diff --git a/src/c_wrapper.jl b/src/c_wrapper.jl index 773d488..d99da78 100644 --- a/src/c_wrapper.jl +++ b/src/c_wrapper.jl @@ -62,6 +62,16 @@ mutable struct ScsCone{T} <: AbstractSCSType ed::T p::Ptr{Cdouble} psize::T + d::Ptr{T} + dsize::T + nuc_m::Ptr{T} + nuc_n::Ptr{T} + nucsize::T + ell1::Ptr{T} + ell1_size::T + sl_n::Ptr{T} + sl_k::Ptr{T} + sl_size::T end mutable struct ScsSolution <: AbstractSCSType @@ -136,6 +146,12 @@ struct _ScsDataWrapper{S,T} ep::T ed::T p::Vector{Cdouble} + d::Vector{T} + nuc_m::Vector{T} + nuc_n::Vector{T} + ell1::Vector{T} + sl_n::Vector{T} + sl_k::Vector{T} primal::Vector{Cdouble} dual::Vector{Cdouble} slack::Vector{Cdouble} @@ -194,7 +210,8 @@ subject to A * x + s = b s in K ``` where K is a product cone of -- zero cone + +- zero cone `{ 0 }` - positive orthant `{ x | x ≥ 0 }` - box cone `{ (t,x) | t*l ≤ x ≤ t*u }` - second-order cone (SOC) `{ (t,x) | ||x||_2 ≤ t }` @@ -203,10 +220,15 @@ where K is a product cone of - exponential cone `{ (x,y,z) | y e^(x/y) ≤ z, y > 0 }` - power cone `{ (x,y,z) | x^a * y^(1-a) ≥ |z|, x ≥ 0, y ≥ 0 }` - dual power cone `{ (u,v,w) | (u/a)^a * (v/(1-a))^(1-a) ≥ |w|, u ≥ 0, v ≥ 0 }` +- log determinant cone `{ (t, u, X) | t ≥ -u log(det(X / u)) }` +- nuclear norm cone `{ (t, X) | t ≥ ||X||_* }` +- L1-matrix norm cone `{ (t, X) | t ≥ Σᵢ|Xᵢⱼ| ∀j}` +- Ky-Fan norm cone `{ (t, X) | t ≥ Σᵏ λᵢ(X) }` ## Input arguments The problem data are: + - `linear_solver`: a `LinearSolver` to use - `m`: the number of affine constraints - `n`: the number of variables @@ -231,6 +253,12 @@ above by the following arguments. - `ed`: the number of dual exponential cones - `p`: the `Vector` of power cone parameters (±1, with negative values for the dual cone) +- `d`: the `Vector` of log-det cone sizes +- `nuc_m`: the `Vector` of nuclear-norm row dimensions +- `nuc_n`: the `Vector` of nuclear-norm column dimensions +- `ell1`: the `Vector` of L1-matrix-norm sizes +- `sl_n`: the `Vector` of Ky-Fan norm cone sizes +- `sl_k`: the `Vector` of Ky-Fan norm cone constants Provide a warm start to SCS by overriding: - `primal_sol = zeros(n)`: a `Vector` to warmstart the primal variables, @@ -290,6 +318,12 @@ function scs_solve( ep::Integer, ed::Integer, p::Vector{Float64}, + d::Vector{<:Integer}, + nuc_m::Vector{<:Integer}, + nuc_n::Vector{<:Integer}, + ell1::Vector{<:Integer}, + sl_n::Vector{<:Integer}, + sl_k::Vector{<:Integer}, primal_sol::Vector{Float64} = zeros(n), dual_sol::Vector{Float64} = zeros(m), slack::Vector{Float64} = zeros(m); @@ -308,6 +342,8 @@ function scs_solve( end primal_sol, dual_sol, slack = zeros(n), zeros(m), zeros(m) end + @assert length(sl_n) == length(sl_k) + @assert length(nuc_m) == length(nuc_n) T = scsint_t(linear_solver) m, n, z, l, ep, ed = T(m), T(n), T(z), T(l), T(ep), T(ed) Avalues, Arowval, Acolptr = _to_sparse(T, A) @@ -338,6 +374,12 @@ function scs_solve( ep, ed, p, + convert(Vector{T}, d), + convert(Vector{T}, nuc_m), + convert(Vector{T}, nuc_n), + convert(Vector{T}, ell1), + convert(Vector{T}, sl_n), + convert(Vector{T}, sl_k), primal_sol, dual_sol, slack, @@ -366,6 +408,16 @@ function _unsafe_scs_solve(model::_ScsDataWrapper{S,T}) where {S,T} model.ed, pointer(model.p), length(model.p), + pointer(model.d), + length(model.d), + pointer(model.nuc_m), + pointer(model.nuc_n), + length(model.nuc_m), + pointer(model.ell1), + length(model.ell1), + pointer(model.sl_k), + pointer(model.sl_n), + length(model.sl_n), ) scs_data = ScsData{T}( model.m, @@ -434,12 +486,82 @@ function scs_solve( ep::Integer, ed::Integer, p::Vector{Float64}, + # d::Vector{<:Integer}, # Skip this argument + # nuc_m::Vector{<:Integer}, # Skip this argument + # nuc_n::Vector{<:Integer}, # Skip this argument + # ell1::Vector{<:Integer}, # Skip this argument + # sl_n::Vector{<:Integer}, # Skip this argument + # sl_k::Vector{<:Integer}, # Skip this argument primal_sol::Vector{Float64} = zeros(n), dual_sol::Vector{Float64} = zeros(m), slack::Vector{Float64} = zeros(m); warm_start::Bool = false, options..., ) + T = scsint_t(linear_solver) + return scs_solve( + linear_solver, + m, + n, + A, + P, + b, + c, + z, + l, + bu, + bl, + q, + s, + T[], # Default cs argument + ep, + ed, + p, + T[], # Default d argument + T[], # Default nuc_m argument + T[], # Default nuc_n argument + T[], # Default ell1 argument + T[], # Default sl_n argument + T[], # Default sl_k argument + primal_sol, + dual_sol, + slack; + warm_start, + options..., + ) +end + +function scs_solve( + linear_solver::Type{<:LinearSolver}, + m::Integer, + n::Integer, + A, + P, + b::Vector{Float64}, + c::Vector{Float64}, + z::Integer, + l::Integer, + bu::Vector{Float64}, + bl::Vector{Float64}, + q::Vector{<:Integer}, + s::Vector{<:Integer}, + cs::Vector{<:Integer}, + ep::Integer, + ed::Integer, + p::Vector{Float64}, + # d::Vector{<:Integer}, # Skip this argument + # nuc_m::Vector{<:Integer}, # Skip this argument + # nuc_n::Vector{<:Integer}, # Skip this argument + # ell1::Vector{<:Integer}, # Skip this argument + # sl_n::Vector{<:Integer}, # Skip this argument + # sl_k::Vector{<:Integer}, # Skip this argument + primal_sol::Vector{Float64} = zeros(n), + dual_sol::Vector{Float64} = zeros(m), + slack::Vector{Float64} = zeros(m); + warm_start::Bool = false, + options..., +) + T = scsint_t(linear_solver) return scs_solve( linear_solver, m, @@ -454,10 +576,16 @@ function scs_solve( bl, q, s, - Int[], # Default cs argument + cs, ep, ed, p, + T[], # Default d argument + T[], # Default nuc_m argument + T[], # Default nuc_n argument + T[], # Default ell1 argument + T[], # Default sl_n argument + T[], # Default sl_k argument primal_sol, dual_sol, slack; diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index b478a8b..063a52a 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -421,6 +421,47 @@ function test_HermitianComplexPSDConeBridge() return end +function test_ScaledLogDetConeTriangleBridge() + set = SCS.ScaledLogDetConeTriangle(3) + @test MOI.dimension(set) == 8 + @test MOI.Utilities.set_with_dimension(SCS.ScaledLogDetConeTriangle, 8) == + set + MOI.Bridges.runtests( + SCS.ScaledLogDetConeTriangleBridge, + """ + variables: t, u, x1, x2, x3 + [1.0 * t, u, x1, x2, x3] in Scaled(LogDetConeTriangle(2)) + """, + """ + variables: t, u, x1, x2, x3 + [-1.0 * t, u, x1, x2, x3] in SCS.ScaledLogDetConeTriangle(2) + """, + ) + MOI.Bridges.runtests( + SCS.ScaledLogDetConeTriangleBridge, + """ + variables: t, u, x1, x2, x3, x4, x5, x6 + [1.0 * t, u, x1, x2, x3, x4, x5, x6] in Scaled(LogDetConeTriangle(3)) + """, + """ + variables: t, u, x1, x2, x3, x4, x5, x6 + [-1.0 * t, u, x1, x2, x4, x3, x5, x6] in SCS.ScaledLogDetConeTriangle(3) + """, + ) + MOI.Bridges.runtests( + SCS.ScaledLogDetConeTriangleBridge, + """ + variables: t, u, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10 + [1.0 * t, u, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10] in Scaled(LogDetConeTriangle(4)) + """, + """ + variables: t, u, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10 + [-1.0 * t, u, x1, x2, x4, x7, x3, x5, x8, x6, x9, x10] in SCS.ScaledLogDetConeTriangle(4) + """, + ) + return +end + end # module TestSCS.runtests()