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
3 changes: 2 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ projects = ["docs", "test", "benchmark"]
[deps]
IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
OSQP = "ab2f91bb-94b4-55e3-9ba0-7f65df51de79"
ProximalCore = "dc4f5ac2-75d1-4f31-931e-60435d74994b"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
TSVD = "9449cd9e-2762-5aa3-a617-5413e99d722e"

[weakdeps]
OSQP = "ab2f91bb-94b4-55e3-9ba0-7f65df51de79"
RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd"

[extensions]
ProximalOperatorsOSQPExt = "OSQP"
RecursiveArrayToolsExt = "RecursiveArrayTools"

[compat]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,52 +1,43 @@
# IndPolyhedral: OSQP implementation
module ProximalOperatorsOSQPExt

using LinearAlgebra
using SparseArrays
using OSQP

struct IndPolyhedralOSQP{R} <: IndPolyhedral
l::AbstractVector{R}
A::AbstractMatrix{R}
u::AbstractVector{R}
mod::OSQP.Model
function IndPolyhedralOSQP{R}(
l::AbstractVector{R}, A::AbstractMatrix{R}, u::AbstractVector{R}
) where R
m, n = size(A)
mod = OSQP.Model()
if !all(l .<= u)
error("function is improper (are some bounds inverted?)")
end
OSQP.setup!(mod; P=SparseMatrixCSC{R}(I, n, n), l=l, A=sparse(A), u=u, verbose=false,
eps_abs=eps(R), eps_rel=eps(R),
eps_prim_inf=eps(R), eps_dual_inf=eps(R))
new(l, A, u, mod)
end
end

# properties

is_proximable(::Type{<:IndPolyhedralOSQP}) = false
using ProximalOperators
using ProximalOperators: IndPolyhedralOSQP
import ProximalCore: prox, prox!

# constructors

IndPolyhedralOSQP(
function ProximalOperators.IndPolyhedralOSQP(
l::AbstractVector{R}, A::AbstractMatrix{R}, u::AbstractVector{R}
) where R =
IndPolyhedralOSQP{R}(l, A, u)
) where R
m, n = size(A)
if !all(l .<= u)
error("function is improper (are some bounds inverted?)")
end
mod = OSQP.Model()
OSQP.setup!(mod; P=SparseMatrixCSC{R}(I, n, n), l=l, A=sparse(A), u=u, verbose=false,
eps_abs=eps(R), eps_rel=eps(R),
eps_prim_inf=eps(R), eps_dual_inf=eps(R))
return IndPolyhedralOSQP{R, typeof(mod)}(l, A, u, mod)
end

IndPolyhedralOSQP(
ProximalOperators.IndPolyhedralOSQP(
l::AbstractVector{R}, A::AbstractMatrix{R}, u::AbstractVector{R},
xmin::AbstractVector{R}, xmax::AbstractVector{R}
) where R =
IndPolyhedralOSQP([l; xmin], [A; I], [u; xmax])

IndPolyhedralOSQP(
ProximalOperators.IndPolyhedralOSQP(
l::AbstractVector{R}, A::AbstractMatrix{R}, args...
) where R =
IndPolyhedralOSQP(
l, SparseMatrixCSC(A), R(Inf).*ones(R, size(A, 1)), args...
)

IndPolyhedralOSQP(
ProximalOperators.IndPolyhedralOSQP(
A::AbstractMatrix{R}, u::AbstractVector{R}, args...
) where R =
IndPolyhedralOSQP(
Expand Down Expand Up @@ -81,7 +72,7 @@ end
# dual problem is: minimize_y (1/2)||-A'y||^2 - x'A'y + g*(y)
# can solve with (fast) dual proximal gradient method

function prox_naive(f::IndPolyhedralOSQP, x, gamma)
function ProximalOperators.prox_naive(f::IndPolyhedralOSQP, x, gamma)
R = eltype(x)
y = zeros(R, size(f.A, 1)) # dual vector
y1 = y
Expand All @@ -99,3 +90,5 @@ function prox_naive(f::IndPolyhedralOSQP, x, gamma)
p = -f.A'*y + x
return p, R(0)
end

end # module ProximalOperatorsOSQPExt
35 changes: 33 additions & 2 deletions src/functions/indPolyhedral.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ S = \\{ x : x_\\min \\leq x \\leq x_\\max, l \\leq Ax \\leq u \\}.
```
Matrix `A` is a mandatory argument; when any of the bounds is not provided,
it is assumed to be (plus or minus) infinity.

The default `solver=:osqp` backend is provided by a package extension: load it
with `using OSQP` before constructing the object, otherwise an error is raised.
"""
function IndPolyhedral(args...; solver=:osqp)
if solver == :osqp
Expand All @@ -23,6 +26,34 @@ function IndPolyhedral(args...; solver=:osqp)
end
end

# including concrete types
# IndPolyhedral: OSQP implementation
#
# The struct below is defined here so that the `solver=:osqp` dispatch above can
# refer to it, but everything that actually needs OSQP -- the constructors,
# `prox!`, the function evaluation and `prox_naive` -- lives in the package
# extension `ext/ProximalOperatorsOSQPExt.jl`, loaded once OSQP is available
# (`using OSQP`). Without OSQP loaded, constructing an `IndPolyhedralOSQP`
# (directly or via `IndPolyhedral(...; solver=:osqp)`) raises an informative
# error.

struct IndPolyhedralOSQP{R, M} <: IndPolyhedral
l::AbstractVector{R}
A::AbstractMatrix{R}
u::AbstractVector{R}
mod::M
# Explicit inner constructor: suppresses the auto-generated 4-positional-arg
# outer constructor, which would otherwise shadow the `(l, A, xmin, xmax)`
# constructor added by ProximalOperatorsOSQPExt.
IndPolyhedralOSQP{R, M}(l, A, u, mod) where {R, M} = new{R, M}(l, A, u, mod)
end

include("indPolyhedralOSQP.jl")
is_proximable(::Type{<:IndPolyhedralOSQP}) = false

# The real constructors are added to this function by ProximalOperatorsOSQPExt;
# this fallback only fires when OSQP is not loaded.
function IndPolyhedralOSQP(args...; kwargs...)
error(
"IndPolyhedralOSQP requires the OSQP package: run `using OSQP` before " *
"constructing IndPolyhedralOSQP(...) or IndPolyhedral(...; solver=:osqp)."
)
end
1 change: 1 addition & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
OSQP = "ab2f91bb-94b4-55e3-9ba0-7f65df51de79"
ProximalCore = "dc4f5ac2-75d1-4f31-931e-60435d74994b"
ProximalOperators = "a725b495-10eb-56fe-b38b-717eba820537"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Expand Down
1 change: 1 addition & 0 deletions test/test_indPolyhedral.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using ProximalOperators
using OSQP # loads ProximalOperatorsOSQPExt, enabling IndPolyhedral(...; solver=:osqp)
using Test

@testset "IndPolyhedral" begin
Expand Down
Loading